diff --git a/config/static_config.yaml b/config/static_config.yaml index 4d589f6..3e9f5a1 100644 --- a/config/static_config.yaml +++ b/config/static_config.yaml @@ -248,6 +248,13 @@ components_manager: cache_size: 1024 cache_ways: 16 cache_ttl: 300s + handler-GetBlock: + path: /api/v2/getBlock + method: GET,POST + cache_enabled: true + cache_size: 1024 + cache_ways: 16 + cache_ttl: 300s handler-GetBlockHeader: path: /api/v2/getBlockHeader method: GET,POST diff --git a/ton-http-api/schemas/v2.yaml b/ton-http-api/schemas/v2.yaml index 5a541fe..71b85cc 100644 --- a/ton-http-api/schemas/v2.yaml +++ b/ton-http-api/schemas/v2.yaml @@ -898,6 +898,56 @@ paths: security: - APIKeyHeader: [ ] - APIKeyQuery: [ ] + /api/v2/getBlock: + get: + tags: + - blocks + summary: Get Block + description: Get raw block data as a base64-encoded BOC + operationId: getBlock_get + parameters: + - $ref: '#/components/parameters/workchain' + - $ref: '#/components/parameters/shard' + - $ref: '#/components/parameters/seqno' + - $ref: '#/components/parameters/rootHashOptional' + - $ref: '#/components/parameters/fileHashOptional' + - $ref: '#/components/parameters/archivalOptional' + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TonlibResponse' + default: + $ref: '#/components/responses/default' + security: + - APIKeyHeader: [ ] + - APIKeyQuery: [ ] + post: + tags: + - rpc + summary: Get Block + description: Get raw block data as a base64-encoded BOC + operationId: getBlock_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BlockDataRequest' + required: true + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TonlibResponse' + default: + $ref: '#/components/responses/default' + security: + - APIKeyHeader: [ ] + - APIKeyQuery: [ ] /api/v2/getBlockHeader: get: tags: @@ -2012,6 +2062,44 @@ components: x-usrv-cpp-type: 'std::int32_t' ShardsRequest: $ref: '#/components/schemas/SeqnoRequest' + BlockDataRequest: + type: object + additionalProperties: false + required: + - workchain + - shard + - seqno + properties: + workchain: + oneOf: + - type: string + - type: integer + format: int32 + x-usrv-cpp-type: 'std::int32_t' + shard: + oneOf: + - type: string + - type: integer + format: int64 + x-usrv-cpp-type: 'std::int64_t' + x-usrv-cpp-type: 'std::int64_t' + seqno: + oneOf: + - type: string + - type: integer + format: int32 + x-usrv-cpp-type: 'std::int32_t' + root_hash: + $ref: '#/components/schemas/TonHash' + file_hash: + $ref: '#/components/schemas/TonHash' + archival: + oneOf: + - type: string + - type: integer + format: int32 + - type: boolean + x-usrv-cpp-type: 'bool' BlockHeaderRequest: type: object additionalProperties: false @@ -3553,6 +3641,7 @@ components: - $ref: '#/components/schemas/ConsensusBlock' - $ref: '#/components/schemas/TonBlockIdExt' - $ref: '#/components/schemas/Shards' + - $ref: '#/components/schemas/BlockData' - $ref: '#/components/schemas/BlockHeader' - $ref: '#/components/schemas/OutMsgQueueSizes' - $ref: '#/components/schemas/BlockTransactions' @@ -3587,6 +3676,7 @@ components: 'ext.blocks.consensusBlock': '#/components/schemas/ConsensusBlock' 'ton.blockIdExt': '#/components/schemas/TonBlockIdExt' 'blocks.shards': '#/components/schemas/Shards' + 'blocks.blockData': '#/components/schemas/BlockData' 'blocks.header': '#/components/schemas/BlockHeader' 'blocks.outMsgQueueSizes': '#/components/schemas/OutMsgQueueSizes' 'blocks.transactions': '#/components/schemas/BlockTransactions' @@ -4018,6 +4108,26 @@ components: required: - '@type' - shards + BlockData: + type: object + additionalProperties: false + description: Raw block data returned by a LiteServer. + properties: + '@type': + type: string + enum: + - blocks.blockData + default: blocks.blockData + id: + $ref: '#/components/schemas/TonBlockIdExt' + description: Extended identifier of the returned block. + data: + $ref: '#/components/schemas/Bytes' + description: Full block BOC encoded as base64. + required: + - '@type' + - id + - data BlockHeader: type: object additionalProperties: true diff --git a/ton-http-api/src/converters/blocks.hpp b/ton-http-api/src/converters/blocks.hpp index 1ef1a1d..23e6f9f 100644 --- a/ton-http-api/src/converters/blocks.hpp +++ b/ton-http-api/src/converters/blocks.hpp @@ -124,6 +124,13 @@ inline schemas::v2::BlockHeader Convert(const tonlib_api::blocks_getBlockHeader: return result; } +inline schemas::v2::BlockData Convert(const tonlib_api::blocks_getBlock::ReturnType& value) { + schemas::v2::BlockData result; + result.id = Convert(value->id_); + result.data = types::bytes{value->data_}; + return result; +} + inline schemas::v2::OutMsgQueueSize Convert(const tonlib_api::object_ptr& value) { schemas::v2::OutMsgQueueSize result; result.id = Convert(value->id_); diff --git a/ton-http-api/src/core/tonlib_worker.h b/ton-http-api/src/core/tonlib_worker.h index f2b018e..f47a632 100644 --- a/ton-http-api/src/core/tonlib_worker.h +++ b/ton-http-api/src/core/tonlib_worker.h @@ -89,6 +89,15 @@ class TonlibWorker { const std::string& file_hash = "", multiclient::SessionPtr session = nullptr ) const; + [[nodiscard]] td::Result getBlock( + const std::int32_t& workchain, + const std::int64_t& shard, + const std::int32_t& seqno, + const std::string& root_hash = "", + const std::string& file_hash = "", + std::optional archival = std::nullopt, + multiclient::SessionPtr session = nullptr + ) const; [[nodiscard]] td::Result getOutMsgQueueSizes( multiclient::SessionPtr session = nullptr ) const; diff --git a/ton-http-api/src/core/tonlib_worker_blocks.cpp b/ton-http-api/src/core/tonlib_worker_blocks.cpp index ce890b8..db7e378 100644 --- a/ton-http-api/src/core/tonlib_worker_blocks.cpp +++ b/ton-http-api/src/core/tonlib_worker_blocks.cpp @@ -168,6 +168,44 @@ td::Result TonlibWorker::getBlock }; return send_request_function(std::move(request), true); } +td::Result TonlibWorker::getBlock( + const std::int32_t& workchain, + const std::int64_t& shard, + const std::int32_t& seqno, + const std::string& root_hash, + const std::string& file_hash, + std::optional archival, + multiclient::SessionPtr session +) const { + if (session == nullptr) { + auto options = multiclient::RequestParameters{.mode = multiclient::RequestMode::Single, .archival = archival}; + TRY_RESULT_PREFIX_ASSIGN(session, tonlib_.get_session(options, nullptr), "failed to get session: "); + } else if (!session->is_valid()) { + auto options = multiclient::RequestParameters{.mode = multiclient::RequestMode::Single, .archival = archival}; + TRY_RESULT_PREFIX_ASSIGN(session, tonlib_.get_session(options, std::move(session)), "failed to get session: "); + } + tonlib_api::object_ptr blk_id = nullptr; + if (!root_hash.empty() && !file_hash.empty()) { + blk_id = tonlib_api::make_object(workchain, shard, seqno, root_hash, file_hash); + } else { + TRY_RESULT_ASSIGN(blk_id, lookupBlock(workchain, shard, seqno, std::nullopt, std::nullopt, session)); + } + auto request = multiclient::RequestFunction{ + .parameters = {.mode = multiclient::RequestMode::Single, .archival = archival}, + .request_creator = + [w = blk_id->workchain_, + s = blk_id->shard_, + ss = blk_id->seqno_, + r = blk_id->root_hash_, + f = blk_id->file_hash_] { + return tonlib_api::make_object( + tonlib_api::make_object(w, s, ss, r, f) + ); + }, + .session = session + }; + return send_request_function(std::move(request), !archival.has_value()); +} td::Result TonlibWorker::getOutMsgQueueSizes( multiclient::SessionPtr session ) const { diff --git a/ton-http-api/src/handlers/JsonRpcHandler.cpp b/ton-http-api/src/handlers/JsonRpcHandler.cpp index 17f4eb0..84a607f 100644 --- a/ton-http-api/src/handlers/JsonRpcHandler.cpp +++ b/ton-http-api/src/handlers/JsonRpcHandler.cpp @@ -12,7 +12,7 @@ namespace ton_http::handlers { namespace { -constexpr std::array kAllowedMethods = { +constexpr std::array kAllowedMethods = { "detectAddress", "detectHash", "packAddress", @@ -32,6 +32,7 @@ constexpr std::array kAllowedMethods = { "lookupBlock", "getShards", "shards", + "getBlock", "getBlockHeader", "getOutMsgQueueSize", "getBlockTransactions", diff --git a/ton-http-api/src/handlers/blocks/GetBlockHandler.cpp b/ton-http-api/src/handlers/blocks/GetBlockHandler.cpp new file mode 100644 index 0000000..fcbed7e --- /dev/null +++ b/ton-http-api/src/handlers/blocks/GetBlockHandler.cpp @@ -0,0 +1,87 @@ +#include "GetBlockHandler.h" + +#include + +#include "converters/convert.hpp" +#include "utils/common.hpp" + +ton_http::handlers::GetBlockHandler::GetBlockHandler( + const userver::components::ComponentConfig& config, const userver::components::ComponentContext& context +) : + TonlibRequestHandler(config, context) { +} + +ton_http::schemas::v2::BlockDataRequest ton_http::handlers::GetBlockHandler::ParseTonlibGetRequest( + const HttpRequest& request, RequestContext& +) const { + schemas::v2::BlockDataRequest req; + try { + req.workchain = boost::lexical_cast(request.GetArg("workchain")); + } catch (std::exception& exc) { + throw utils::TonlibException("failed to parse workchain", 422); + } + try { + req.shard = boost::lexical_cast(request.GetArg("shard")); + } catch (std::exception& exc) { + throw utils::TonlibException("failed to parse shard", 422); + } + try { + req.seqno = boost::lexical_cast(request.GetArg("seqno")); + } catch (std::exception& exc) { + throw utils::TonlibException("failed to parse seqno", 422); + } + if (request.HasArg("root_hash")) { + try { + req.root_hash = userver::chaotic::convert::Convert( + request.GetArg("root_hash"), userver::chaotic::convert::To{} + ); + } catch (std::exception& exc) { + throw utils::TonlibException("failed to parse root_hash", 422); + } + } + if (request.HasArg("file_hash")) { + try { + req.file_hash = userver::chaotic::convert::Convert( + request.GetArg("file_hash"), userver::chaotic::convert::To{} + ); + } catch (std::exception& exc) { + throw utils::TonlibException("failed to parse file_hash", 422); + } + } + if (request.HasArg("archival")) { + try { + req.archival = utils::stringToBool(request.GetArg("archival")); + } catch (std::exception& exc) { + throw utils::TonlibException("failed to parse archival", 422); + } + } + return req; +} +td::Status ton_http::handlers::GetBlockHandler::ValidateRequest( + const schemas::v2::BlockDataRequest& request +) const { + if (request.seqno <= 0) { + return td::Status::Error(422, "seqno should be positive"); + } + return td::Status::OK(); +} +td::Result ton_http::handlers::GetBlockHandler::HandleRequestTonlibThrow( + schemas::v2::BlockDataRequest& request, multiclient::SessionPtr& session +) const { + auto root_hash = request.root_hash.has_value() ? request.root_hash.value().GetUnderlying() : ""; + auto file_hash = request.file_hash.has_value() ? request.file_hash.value().GetUnderlying() : ""; + TRY_RESULT( + result, + tonlib_component_.DoRequest( + &core::TonlibWorker::getBlock, + request.workchain, + request.shard, + request.seqno, + root_hash, + file_hash, + request.archival, + session + ) + ); + return converters::Convert(result); +} diff --git a/ton-http-api/src/handlers/blocks/GetBlockHandler.h b/ton-http-api/src/handlers/blocks/GetBlockHandler.h new file mode 100644 index 0000000..5c4e7d4 --- /dev/null +++ b/ton-http-api/src/handlers/blocks/GetBlockHandler.h @@ -0,0 +1,25 @@ +#pragma once + +#include "handlers/TonlibRequestHandler.h" +#include "schemas/v2.hpp" + +namespace ton_http::handlers { + +class GetBlockHandler : public TonlibRequestHandler { +public: + static constexpr std::string_view kName = "handler-GetBlock"; + + GetBlockHandler( + const userver::components::ComponentConfig& config, const userver::components::ComponentContext& context + ); + + td::Status ValidateRequest(const schemas::v2::BlockDataRequest& request) const override; + schemas::v2::BlockDataRequest ParseTonlibGetRequest( + const HttpRequest& request, RequestContext& context + ) const override; + td::Result HandleRequestTonlibThrow( + schemas::v2::BlockDataRequest& request, multiclient::SessionPtr& session + ) const override; +}; + +} // namespace ton_http::handlers diff --git a/ton-http-api/src/main.cpp b/ton-http-api/src/main.cpp index c98fcc7..929dce3 100644 --- a/ton-http-api/src/main.cpp +++ b/ton-http-api/src/main.cpp @@ -20,6 +20,7 @@ #include "handlers/accounts/GetShardAccountCellHandler.h" #include "handlers/accounts/GetTokenDataHandler.h" #include "handlers/accounts/GetWalletInformationHandler.h" +#include "handlers/blocks/GetBlockHandler.h" #include "handlers/blocks/GetBlockHeaderHandler.h" #include "handlers/blocks/GetConsensusBlockHandler.h" #include "handlers/blocks/GetMasterchainBlockSignaturesHandler.h" @@ -96,6 +97,7 @@ int main(int argc, char* argv[]) { component_list.Append(); component_list.Append(); component_list.Append("handler-Shards"); + component_list.Append(); component_list.Append(); component_list.Append();