diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 3d248cfe..50d2661b 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -191,6 +191,14 @@ jobs: asset_name: tonlibjson.dll tag: ${{ steps.tag.outputs.TAG }} + - name: Upload Windows 2019 single artifact - libemulator + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-win-binaries/emulator.dll + asset_name: libemulator.dll + tag: ${{ steps.tag.outputs.TAG }} + - name: Upload Windows 2019 single artifact - tonlib-cli uses: svenstaro/upload-release-action@v2 with: @@ -273,6 +281,14 @@ jobs: asset_name: tonlibjson-mac-x86-64.dylib tag: ${{ steps.tag.outputs.TAG }} + - name: Upload Mac x86-64 single artifact - libemulator + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-x86_64-macos-binaries/libemulator.dylib + asset_name: libemulator-mac-x86-64.dylib + tag: ${{ steps.tag.outputs.TAG }} + - name: Upload Mac x86-64 single artifact - tonlib-cli uses: svenstaro/upload-release-action@v2 with: @@ -355,6 +371,14 @@ jobs: asset_name: tonlibjson-linux-x86_64.so tag: ${{ steps.tag.outputs.TAG }} + - name: Upload Linux x86-64 single artifact - libemulator + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-x86_64-linux-binaries/libemulator.so + asset_name: libemulator-linux-x86_64.so + tag: ${{ steps.tag.outputs.TAG }} + - name: Upload Linux x86-64 single artifact - tonlib-cli uses: svenstaro/upload-release-action@v2 with: diff --git a/assembly/cicd/jenkins/test-builds.groovy b/assembly/cicd/jenkins/test-builds.groovy index 960ac8db..7e377f0e 100644 --- a/assembly/cicd/jenkins/test-builds.groovy +++ b/assembly/cicd/jenkins/test-builds.groovy @@ -182,7 +182,7 @@ pipeline { timeout(time: 90, unit: 'MINUTES') { bat ''' copy assembly\\native\\build-windows.bat . - build-windows.bat + build-windows.bat -t ''' bat ''' cd artifacts diff --git a/assembly/native/build-windows.bat b/assembly/native/build-windows.bat index 2e3f2082..2c64e80a 100644 --- a/assembly/native/build-windows.bat +++ b/assembly/native/build-windows.bat @@ -14,24 +14,26 @@ choco feature enable -n allowEmptyChecksums echo Installing pkgconfiglite... choco install -y pkgconfiglite -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't install pkgconfiglite exit /b %errorlevel% ) echo Installing ninja... choco install -y ninja -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't install ninja exit /b %errorlevel% ) if not exist "zlib" ( git clone https://github.com/madler/zlib.git -cd zlib\contrib\vstudio\vc14 +cd zlib +git checkout v1.3.1 +cd contrib\vstudio\vc14 msbuild zlibstat.vcxproj /p:Configuration=ReleaseWithoutAsm /p:platform=x64 -p:PlatformToolset=v143 -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't install zlib exit /b %errorlevel% ) @@ -41,14 +43,20 @@ echo Using zlib... ) if not exist "secp256k1" ( -git clone https://github.com/libbitcoin/secp256k1.git -cd secp256k1\builds\msvc\vs2017 -msbuild /p:Configuration=StaticRelease -p:PlatformToolset=v143 -p:Platform=x64 -IF errorlevel 1 ( +git clone https://github.com/bitcoin-core/secp256k1.git +cd secp256k1 +git checkout v0.3.2 +cmake -G "Visual Studio 17 2022" -A x64 -S . -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DBUILD_SHARED_LIBS=OFF +IF %errorlevel% NEQ 0 ( + echo Can't configure secp256k1 + exit /b %errorlevel% +) +cmake --build build --config Release +IF %errorlevel% NEQ 0 ( echo Can't install secp256k1 exit /b %errorlevel% ) -cd ..\..\..\.. +cd .. ) else ( echo Using secp256k1... ) @@ -56,7 +64,7 @@ echo Using secp256k1... if not exist "libsodium" ( curl -Lo libsodium-1.0.18-stable-msvc.zip https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable-msvc.zip -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't download libsodium exit /b %errorlevel% ) @@ -67,7 +75,7 @@ echo Using libsodium... if not exist "openssl-3.1.4" ( curl -Lo openssl-3.1.4.zip https://github.com/neodiX42/precompiled-openssl-win64/raw/main/openssl-3.1.4.zip -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't download OpenSSL exit /b %errorlevel% ) @@ -78,7 +86,7 @@ echo Using openssl... if not exist "libmicrohttpd-0.9.77-w32-bin" ( curl -Lo libmicrohttpd-0.9.77-w32-bin.zip https://github.com/neodiX42/precompiled-openssl-win64/raw/main/libmicrohttpd-0.9.77-w32-bin.zip -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't download libmicrohttpd exit /b %errorlevel% ) @@ -89,7 +97,7 @@ echo Using libmicrohttpd... if not exist "readline-5.0-1-lib" ( curl -Lo readline-5.0-1-lib.zip https://github.com/neodiX42/precompiled-openssl-win64/raw/main/readline-5.0-1-lib.zip -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't download readline exit /b %errorlevel% ) @@ -110,7 +118,7 @@ cmake -GNinja -DCMAKE_BUILD_TYPE=Release ^ -DSODIUM_USE_STATIC_LIBS=1 ^ -DSECP256K1_FOUND=1 ^ -DSECP256K1_INCLUDE_DIR=%root%\secp256k1\include ^ --DSECP256K1_LIBRARY=%root%\secp256k1\bin\x64\Release\v143\static\secp256k1.lib ^ +-DSECP256K1_LIBRARY=%root%\secp256k1\build\src\Release\libsecp256k1.lib ^ -DMHD_FOUND=1 ^ -DMHD_LIBRARY=%root%\libmicrohttpd-0.9.77-w32-bin\x86_64\VS2019\Release-static\libmicrohttpd.lib ^ -DMHD_INCLUDE_DIR=%root%\libmicrohttpd-0.9.77-w32-bin\x86_64\VS2019\Release-static ^ @@ -118,10 +126,12 @@ cmake -GNinja -DCMAKE_BUILD_TYPE=Release ^ -DZLIB_INCLUDE_DIR=%root%\zlib ^ -DZLIB_LIBRARIES=%root%\zlib\contrib\vstudio\vc14\x64\ZlibStatReleaseWithoutAsm\zlibstat.lib ^ -DOPENSSL_FOUND=1 ^ --DOPENSSL_INCLUDE_DIR=%root%/openssl-3.1.4/x64/include ^ --DOPENSSL_CRYPTO_LIBRARY=%root%/openssl-3.1.4/x64/lib/libcrypto_static.lib ^ +-DOPENSSL_INCLUDE_DIR=%root%\openssl-3.1.4\x64\include ^ +-DOPENSSL_CRYPTO_LIBRARY=%root%\openssl-3.1.4\x64\lib\libcrypto_static.lib ^ +-DREADLINE_INCLUDE_DIR=%root%\readline-5.0-1-lib\include ^ +-DREADLINE_LIBRARY=%root%\readline-5.0-1-lib\lib\readline.lib ^ -DCMAKE_CXX_FLAGS="/DTD_WINDOWS=1 /EHsc /bigobj" .. -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't configure TON exit /b %errorlevel% ) @@ -133,7 +143,7 @@ json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-ha test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont test-net ^ test-tdactor test-tdutils test-tonlib-offline test-adnl test-dht test-rldp test-rldp2 test-catchain ^ test-fec test-tddb test-db test-validator-session-state -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't compile TON exit /b %errorlevel% ) @@ -141,14 +151,14 @@ IF errorlevel 1 ( ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson ^ tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id ^ json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo Can't compile TON exit /b %errorlevel% ) ) copy validator-engine\validator-engine.exe test -IF errorlevel 1 ( +IF %errorlevel% NEQ 0 ( echo validator-engine.exe does not exist exit /b %errorlevel% ) @@ -156,8 +166,8 @@ IF errorlevel 1 ( IF "%1"=="-t" ( echo Running tests... REM ctest -C Release --output-on-failure -E "test-catchain|test-actors|test-validator-session-state" - ctest -C Release --output-on-failure --timeout 1800 - IF errorlevel 1 ( + ctest -C Release --output-on-failure -E "test-bigint" --timeout 1800 + IF %errorlevel% NEQ 0 ( echo Some tests failed exit /b %errorlevel% ) diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index 13e5c70b..ce3beba9 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -174,7 +174,9 @@ td::Ref prepare_vm_c7(SmartContract::Args args, td::Ref cod if (args.config && args.config.value()->get_global_version() >= 6) { tuple.push_back(args.config.value()->get_unpacked_config_tuple(now)); // unpacked_config_tuple tuple.push_back(td::zero_refint()); // due_payment - tuple.push_back(vm::StackEntry()); // precompiled_gas_usage:Integer + // precomiled_gas_usage:(Maybe Integer) + auto precompiled = args.config.value()->get_precompiled_contracts_config().get_contract(code->get_hash().bits()); + tuple.push_back(precompiled ? td::make_refint(precompiled.value().gas_usage) : vm::StackEntry()); } auto tuple_ref = td::make_cnt_ref>(std::move(tuple)); //LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); diff --git a/crypto/smc-envelope/WalletInterface.cpp b/crypto/smc-envelope/WalletInterface.cpp index c76ac4a2..e02759b2 100644 --- a/crypto/smc-envelope/WalletInterface.cpp +++ b/crypto/smc-envelope/WalletInterface.cpp @@ -71,7 +71,7 @@ void WalletInterface::store_gift_message(vm::CellBuilder &cb, const Gift &gift) cb.store_zeroes(1); if (gift.is_encrypted) { - cb.store_long(0x2167da4b, 32); + cb.store_long(EncryptedCommentOp, 32); } else { cb.store_long(0, 32); } diff --git a/crypto/smc-envelope/WalletInterface.h b/crypto/smc-envelope/WalletInterface.h index b7f80630..c4e1f270 100644 --- a/crypto/smc-envelope/WalletInterface.h +++ b/crypto/smc-envelope/WalletInterface.h @@ -33,6 +33,7 @@ namespace ton { class WalletInterface : public SmartContract { public: + static constexpr uint32_t EncryptedCommentOp = 0x2167da4b; struct Gift { block::StdAddress destination; td::int64 gramms; diff --git a/emulator/emulator-extern.cpp b/emulator/emulator-extern.cpp index f8e2f724..6d38ae59 100644 --- a/emulator/emulator-extern.cpp +++ b/emulator/emulator-extern.cpp @@ -543,6 +543,66 @@ const char *tvm_emulator_run_get_method(void *tvm_emulator, int method_id, const return strdup(jb.string_builder().as_cslice().c_str()); } +const char *tvm_emulator_emulate_run_method(uint32_t len, const char *params_boc, int64_t gas_limit) { + auto params_cell = vm::std_boc_deserialize(td::Slice(params_boc, len)); + if (params_cell.is_error()) { + return nullptr; + } + auto params_cs = vm::load_cell_slice(params_cell.move_as_ok()); + auto code = params_cs.fetch_ref(); + auto data = params_cs.fetch_ref(); + + auto stack_cs = vm::load_cell_slice(params_cs.fetch_ref()); + auto params = vm::load_cell_slice(params_cs.fetch_ref()); + auto c7_cs = vm::load_cell_slice(params.fetch_ref()); + auto libs = vm::Dictionary(params.fetch_ref(), 256); + + auto method_id = params_cs.fetch_long(32); + + td::Ref stack; + if (!vm::Stack::deserialize_to(stack_cs, stack)) { + return nullptr; + } + + td::Ref c7; + if (!vm::Stack::deserialize_to(c7_cs, c7)) { + return nullptr; + } + + auto emulator = new emulator::TvmEmulator(code, data); + emulator->set_vm_verbosity_level(0); + emulator->set_gas_limit(gas_limit); + emulator->set_c7_raw(c7->fetch(0).as_tuple()); + if (libs.is_empty()) { + emulator->set_libraries(std::move(libs)); + } + auto result = emulator->run_get_method(int(method_id), stack); + delete emulator; + + vm::CellBuilder stack_cb; + if (!result.stack->serialize(stack_cb)) { + return nullptr; + } + + vm::CellBuilder cb; + cb.store_long(result.code, 32); + cb.store_long(result.gas_used, 64); + cb.store_ref(stack_cb.finalize()); + + auto ser = vm::std_boc_serialize(cb.finalize()); + if (!ser.is_ok()) { + return nullptr; + } + auto sok = ser.move_as_ok(); + + auto sz = uint32_t(sok.size()); + char* rn = (char*)malloc(sz + 4); + memcpy(rn, &sz, 4); + memcpy(rn+4, sok.data(), sz); + + return rn; +} + const char *tvm_emulator_send_external_message(void *tvm_emulator, const char *message_body_boc) { auto message_body_cell = boc_b64_to_cell(message_body_boc); if (message_body_cell.is_error()) { diff --git a/emulator/emulator-extern.h b/emulator/emulator-extern.h index ce920f98..b418e5b0 100644 --- a/emulator/emulator-extern.h +++ b/emulator/emulator-extern.h @@ -213,6 +213,16 @@ EMULATOR_EXPORT bool tvm_emulator_set_debug_enabled(void *tvm_emulator, bool deb */ EMULATOR_EXPORT const char *tvm_emulator_run_get_method(void *tvm_emulator, int method_id, const char *stack_boc); +/** + * @brief Optimized version of "run get method" with all passed parameters in a single call + * @param len Length of params_boc buffer + * @param params_boc BoC serialized parameters, scheme: request$_ code:^Cell data:^Cell stack:^VmStack params:^[c7:^VmStack libs:^Cell] method_id:(## 32) + * @param gas_limit Gas limit + * @return Char* with first 4 bytes defining length, and the rest BoC serialized result + * Scheme: result$_ exit_code:(## 32) gas_used:(## 32) stack:^VmStack + */ +EMULATOR_EXPORT const char *tvm_emulator_emulate_run_method(uint32_t len, const char *params_boc, int64_t gas_limit); + /** * @brief Send external message * @param tvm_emulator Pointer to TVM emulator diff --git a/emulator/emulator_export_list b/emulator/emulator_export_list index e70166e7..93f5dbac 100644 --- a/emulator/emulator_export_list +++ b/emulator/emulator_export_list @@ -21,3 +21,4 @@ _tvm_emulator_run_get_method _tvm_emulator_send_external_message _tvm_emulator_send_internal_message _tvm_emulator_destroy +_tvm_emulator_emulate_run_method diff --git a/emulator/tvm-emulator.hpp b/emulator/tvm-emulator.hpp index dafa2a5f..a9f248b7 100644 --- a/emulator/tvm-emulator.hpp +++ b/emulator/tvm-emulator.hpp @@ -33,6 +33,10 @@ public: } } + void set_c7_raw(td::Ref c7) { + args_.set_c7(std::move(c7)); + } + void set_prev_blocks_info(td::Ref tuple) { args_.set_prev_blocks_info(std::move(tuple)); } diff --git a/tddb/td/db/RocksDb.cpp b/tddb/td/db/RocksDb.cpp index 500985e2..a84a804b 100644 --- a/tddb/td/db/RocksDb.cpp +++ b/tddb/td/db/RocksDb.cpp @@ -59,9 +59,8 @@ RocksDb RocksDb::clone() const { return RocksDb{db_, statistics_}; } -Result RocksDb::open(std::string path) { +Result RocksDb::open(std::string path, std::shared_ptr statistics) { rocksdb::OptimisticTransactionDB *db; - auto statistics = rocksdb::CreateDBStatistics(); { rocksdb::Options options; @@ -94,6 +93,18 @@ Result RocksDb::open(std::string path) { return RocksDb(std::shared_ptr(db), std::move(statistics)); } +std::shared_ptr RocksDb::create_statistics() { + return rocksdb::CreateDBStatistics(); +} + +std::string RocksDb::statistics_to_string(const std::shared_ptr statistics) { + return statistics->ToString(); +} + +void RocksDb::reset_statistics(const std::shared_ptr statistics) { + statistics->Reset(); +} + std::unique_ptr RocksDb::snapshot() { auto res = std::make_unique(clone()); res->begin_snapshot().ensure(); diff --git a/tddb/td/db/RocksDb.h b/tddb/td/db/RocksDb.h index b8bfaf9d..1afba4cc 100644 --- a/tddb/td/db/RocksDb.h +++ b/tddb/td/db/RocksDb.h @@ -38,7 +38,7 @@ class RocksDb : public KeyValue { public: static Status destroy(Slice path); RocksDb clone() const; - static Result open(std::string path); + static Result open(std::string path, std::shared_ptr statistics = nullptr); Result get(Slice key, std::string &value) override; Status set(Slice key, Slice value) override; @@ -60,6 +60,10 @@ class RocksDb : public KeyValue { std::unique_ptr snapshot() override; std::string stats() const override; + static std::shared_ptr create_statistics(); + static std::string statistics_to_string(const std::shared_ptr statistics); + static void reset_statistics(const std::shared_ptr statistics); + RocksDb(RocksDb &&); RocksDb &operator=(RocksDb &&); ~RocksDb(); diff --git a/tdutils/td/utils/misc.cpp b/tdutils/td/utils/misc.cpp index d592593a..caff44e3 100644 --- a/tdutils/td/utils/misc.cpp +++ b/tdutils/td/utils/misc.cpp @@ -153,9 +153,9 @@ string buffer_to_hex(Slice buffer) { const char *hex = "0123456789ABCDEF"; string res(2 * buffer.size(), '\0'); for (std::size_t i = 0; i < buffer.size(); i++) { - auto c = buffer.ubegin()[i]; - res[2 * i] = hex[c & 15]; - res[2 * i + 1] = hex[c >> 4]; + unsigned char c = buffer[i]; + res[2 * i] = hex[c >> 4]; + res[2 * i + 1] = hex[c & 15]; } return res; } diff --git a/test/regression-tests.ans b/test/regression-tests.ans index 96117ee9..bfea7089 100644 --- a/test/regression-tests.ans +++ b/test/regression-tests.ans @@ -1,7 +1,7 @@ abce Test_Bigint_main_default 76f38492ec19464a1d0eac51d389023a31ce10396b3894061361d159567ce8cd Test_Bitstrings_main_default a8b08af3116923c4c2a14e138d168375abd0c059f2f780d3267b294929a1110e -Test_Cells_simple_default 832502642fe4fe5db70de82681aedb7d54d7f3530e0069861fff405fe6f6cf23 +Test_Cells_simple_default 414f68da0a2f6fa09b2bdb99c453cdf919db48b5b4ca1c6ac1dfcd837e2d8170 Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e3063a20ea60b0 Test_Fift_bug_newlize_default e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 Test_Fift_bug_ufits_default 51bf5a9f1ed7633a193f6fdd17a7a3af8e032dfe72a9669c85e8639aa8a7c195 diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index ce4d2731..e1e28b9f 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -53,8 +53,10 @@ liteServer.partialBlockProof complete:Bool from:tonNode.blockIdExt to:tonNode.bl liteServer.configInfo mode:# id:tonNode.blockIdExt state_proof:bytes config_proof:bytes = liteServer.ConfigInfo; liteServer.validatorStats mode:# id:tonNode.blockIdExt count:int complete:Bool state_proof:bytes data_proof:bytes = liteServer.ValidatorStats; liteServer.libraryResult result:(vector liteServer.libraryEntry) = liteServer.LibraryResult; +liteServer.libraryResultWithProof id:tonNode.blockIdExt mode:# result:(vector liteServer.libraryEntry) state_proof:bytes data_proof:bytes = liteServer.LibraryResultWithProof; liteServer.shardBlockLink id:tonNode.blockIdExt proof:bytes = liteServer.ShardBlockLink; liteServer.shardBlockProof masterchain_id:tonNode.blockIdExt links:(vector liteServer.shardBlockLink) = liteServer.ShardBlockProof; +liteServer.lookupBlockResult id:tonNode.blockIdExt mode:# mc_block_id:tonNode.blockIdExt client_mc_state_proof:bytes mc_block_proof:bytes shard_links:(vector liteServer.shardBlockLink) header:bytes prev_header:bytes = liteServer.LookupBlockResult; liteServer.debug.verbosity value:int = liteServer.debug.Verbosity; @@ -76,6 +78,7 @@ liteServer.getAllShardsInfo id:tonNode.blockIdExt = liteServer.AllShardsInfo; liteServer.getOneTransaction id:tonNode.blockIdExt account:liteServer.accountId lt:long = liteServer.TransactionInfo; liteServer.getTransactions count:# account:liteServer.accountId lt:long hash:int256 = liteServer.TransactionList; liteServer.lookupBlock mode:# id:tonNode.blockId lt:mode.1?long utime:mode.2?int = liteServer.BlockHeader; +liteServer.lookupBlockWithProof mode:# id:tonNode.blockId mc_block_id:tonNode.blockIdExt lt:mode.1?long utime:mode.2?int = liteServer.LookupBlockResult; liteServer.listBlockTransactions id:tonNode.blockIdExt mode:# count:# after:mode.7?liteServer.transactionId3 reverse_order:mode.6?true want_proof:mode.5?true = liteServer.BlockTransactions; liteServer.listBlockTransactionsExt id:tonNode.blockIdExt mode:# count:# after:mode.7?liteServer.transactionId3 reverse_order:mode.6?true want_proof:mode.5?true = liteServer.BlockTransactionsExt; liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; @@ -83,6 +86,7 @@ liteServer.getConfigAll mode:# id:tonNode.blockIdExt = liteServer.ConfigInfo; liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo; liteServer.getValidatorStats#091a58bc mode:# id:tonNode.blockIdExt limit:int start_after:mode.0?int256 modified_after:mode.2?int = liteServer.ValidatorStats; liteServer.getLibraries library_list:(vector int256) = liteServer.LibraryResult; +liteServer.getLibrariesWithProof id:tonNode.blockIdExt mode:# library_list:(vector int256) = liteServer.LibraryResultWithProof; liteServer.getShardBlockProof id:tonNode.blockIdExt = liteServer.ShardBlockProof; liteServer.queryPrefix = Object; diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index da64ac53..06c08258 100644 Binary files a/tl/generate/scheme/lite_api.tlo and b/tl/generate/scheme/lite_api.tlo differ diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 7b4342dd..b7423853 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -83,7 +83,7 @@ using namespace tonlib; TEST(Tonlib, PublicKey) { block::PublicKey::parse("pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").ensure_error(); auto key = block::PublicKey::parse("Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").move_as_ok(); - CHECK(td::buffer_to_hex(key.key) == "3EE9DC0A7A0B6CA01770CE34698792BD8ECB53A6949BFD6C81B6E3CA475B74D7"); + CHECK(td::buffer_to_hex(key.key) == "E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D"); CHECK(key.serialize() == "Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2"); } diff --git a/tonlib/tonlib/CellString.cpp b/tonlib/tonlib/CellString.cpp deleted file mode 100644 index ad2cbf5f..00000000 --- a/tonlib/tonlib/CellString.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "CellString.h" -#include "td/utils/misc.h" - -#include "vm/cells/CellSlice.h" - -namespace vm { -td::Status CellString::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) { - td::uint32 size = td::narrow_cast(slice.size() * 8); - return store(cb, td::BitSlice(slice.ubegin(), size), top_bits); -} - -td::Status CellString::store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits) { - if (slice.size() > max_bytes * 8) { - return td::Status::Error("String is too long (1)"); - } - unsigned int head = td::min(slice.size(), td::min(cb.remaining_bits(), top_bits)) / 8 * 8; - auto max_bits = vm::Cell::max_bits / 8 * 8; - auto depth = 1 + (slice.size() - head + max_bits - 1) / max_bits; - if (depth > max_chain_length) { - return td::Status::Error("String is too long (2)"); - } - cb.append_bitslice(slice.subslice(0, head)); - slice.advance(head); - if (slice.size() == 0) { - return td::Status::OK(); - } - CellBuilder child_cb; - store(child_cb, std::move(slice)); - cb.store_ref(child_cb.finalize()); - return td::Status::OK(); -} - -template -void CellString::for_each(F &&f, CellSlice &cs, unsigned int top_bits) { - unsigned int head = td::min(cs.size(), top_bits); - f(cs.prefetch_bits(head)); - if (!cs.have_refs()) { - return; - } - auto ref = cs.prefetch_ref(); - while (true) { - auto cs = vm::load_cell_slice(ref); - f(cs.prefetch_bits(cs.size())); - if (!cs.have_refs()) { - return; - } - ref = cs.prefetch_ref(); - } -} - -td::Result CellString::load(CellSlice &cs, unsigned int top_bits) { - unsigned int size = 0; - for_each([&](auto slice) { size += slice.size(); }, cs, top_bits); - if (size % 8 != 0) { - return td::Status::Error("Size is not divisible by 8"); - } - std::string res(size / 8, 0); - - td::BitPtr to(td::MutableSlice(res).ubegin()); - for_each([&](auto slice) { to.concat(slice); }, cs, top_bits); - CHECK(to.offs == (int)size); - return res; -} -} // namespace vm diff --git a/tonlib/tonlib/CellString.h b/tonlib/tonlib/CellString.h deleted file mode 100644 index 89c933d8..00000000 --- a/tonlib/tonlib/CellString.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "td/utils/Status.h" - -#include "vm/cells/CellBuilder.h" - -namespace vm { -class CellString { - public: - static constexpr unsigned int max_bytes = 1024; - static constexpr unsigned int max_chain_length = 16; - - static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits); - static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits); - static td::Result load(CellSlice &cs, unsigned int top_bits = Cell::max_bits); - - private: - template - static void for_each(F &&f, CellSlice &cs, unsigned int top_bits = Cell::max_bits); -}; - -} // namespace vm diff --git a/tonlib/tonlib/ClientActor.cpp b/tonlib/tonlib/ClientActor.cpp deleted file mode 100644 index ab2bc125..00000000 --- a/tonlib/tonlib/ClientActor.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2020 Telegram Systems LLP -*/ diff --git a/tonlib/tonlib/ClientActor.h b/tonlib/tonlib/ClientActor.h deleted file mode 100644 index 592589cd..00000000 --- a/tonlib/tonlib/ClientActor.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2020 Telegram Systems LLP -*/ -#pragma once - -#include "tonlib/TonlibCallback.h" -#include "tonlib/TonlibClient.h" -#include "auto/tl/tonlib_api.h" - -namespace tonlinb { -class ClientActor : public td::actor::Actor { - public: - explicit ClientActor(td::unique_ptr callback); - void request(td::uint64 id, tonlib_api::object_ptr request); - static tonlib_api::object_ptr execute(tonlib_api::object_ptr request); - ~ClientActor(); - ClientActor(ClientActor&& other); - ClientActor& operator=(ClientActor&& other); - - ClientActor(const ClientActor& other) = delete; - ClientActor& operator=(const ClientActor& other) = delete; - - private: - td::actor::ActorOwn tonlib_; -}; -} // namespace tonlinb diff --git a/tonlib/tonlib/GenericAccount.cpp b/tonlib/tonlib/GenericAccount.cpp deleted file mode 100644 index 9f44ede2..00000000 --- a/tonlib/tonlib/GenericAccount.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "tonlib/GenericAccount.h" -#include "tonlib/utils.h" -#include "block/block-auto.h" -namespace tonlib { -td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref data) noexcept { - return vm::CellBuilder() - .append_cellslice(binary_bitstring_to_cellslice("b{00110}").move_as_ok()) - .store_ref(std::move(code)) - .store_ref(std::move(data)) - .finalize(); -} -block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, - const td::Ref& init_state) noexcept { - return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/); -} -td::Ref GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, - td::Ref body) noexcept { - block::gen::Message::Record message; - /*info*/ { - block::gen::CommonMsgInfo::Record_ext_in_msg_info info; - /* src */ - tlb::csr_pack(info.src, block::gen::MsgAddressExt::Record_addr_none{}); - /* dest */ { - block::gen::MsgAddressInt::Record_addr_std dest; - dest.anycast = vm::CellBuilder().store_zeroes(1).as_cellslice_ref(); - dest.workchain_id = address.workchain; - dest.address = address.addr; - - tlb::csr_pack(info.dest, dest); - } - /* import_fee */ { - vm::CellBuilder cb; - block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(0)); - info.import_fee = cb.as_cellslice_ref(); - } - - tlb::csr_pack(message.info, info); - } - /* init */ { - if (new_state.not_null()) { - // Just(Left(new_state)) - message.init = vm::CellBuilder() - .store_ones(1) - .store_zeroes(1) - .append_cellslice(vm::load_cell_slice(new_state)) - .as_cellslice_ref(); - } else { - message.init = vm::CellBuilder().store_zeroes(1).as_cellslice_ref(); - CHECK(message.init.not_null()); - } - } - /* body */ { - message.body = vm::CellBuilder().store_zeroes(1).append_cellslice(vm::load_cell_slice_ref(body)).as_cellslice_ref(); - } - - td::Ref res; - tlb::type_pack_cell(res, block::gen::t_Message_Any, message); - CHECK(res.not_null()); - - return res; -} -} // namespace tonlib diff --git a/tonlib/tonlib/GenericAccount.h b/tonlib/tonlib/GenericAccount.h deleted file mode 100644 index 4a36d78a..00000000 --- a/tonlib/tonlib/GenericAccount.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once -#include "vm/cells.h" -#include "block/block.h" -namespace tonlib { -class GenericAccount { - public: - static td::Ref get_init_state(td::Ref code, td::Ref data) noexcept; - static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) noexcept; - static td::Ref create_ext_message(const block::StdAddress& address, td::Ref new_state, - td::Ref body) noexcept; -}; -} // namespace tonlib diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index cdb0f5ee..b62ce7c0 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -22,6 +22,7 @@ #include "td/utils/port/path.h" #include "td/utils/PathView.h" +#include #include #include @@ -42,7 +43,11 @@ class KeyValueDir : public KeyValue { } td::Status add(td::Slice key, td::Slice value) override { - auto path = to_file_path(key.str()); + auto key_str = key.str(); + if (!is_valid_key(key_str)) { + return td::Status::Error("Invalid key"); + } + auto path = to_file_path(key_str); if (td::stat(path).is_ok()) { return td::Status::Error(PSLICE() << "File " << path << "already exists"); } @@ -50,15 +55,27 @@ class KeyValueDir : public KeyValue { } td::Status set(td::Slice key, td::Slice value) override { - return td::atomic_write_file(to_file_path(key.str()), value); + auto key_str = key.str(); + if (!is_valid_key(key_str)) { + return td::Status::Error("Invalid key"); + } + return td::atomic_write_file(to_file_path(key_str), value); } td::Result get(td::Slice key) override { - return td::read_file_secure(to_file_path(key.str())); + auto key_str = key.str(); + if (!is_valid_key(key_str)) { + return td::Status::Error("Invalid key"); + } + return td::read_file_secure(to_file_path(key_str)); } td::Status erase(td::Slice key) override { - return td::unlink(to_file_path(key.str())); + auto key_str = key.str(); + if (!is_valid_key(key_str)) { + return td::Status::Error("Invalid key"); + } + return td::unlink(to_file_path(key_str)); } void foreach_key(std::function f) override { @@ -83,6 +100,20 @@ class KeyValueDir : public KeyValue { std::string to_file_path(std::string key) { return directory_ + TD_DIR_SLASH + key; } + + bool is_valid_key(const std::string& key) { + if (key.empty()) { + return false; + } + + if (key.find(TD_DIR_SLASH) != std::string::npos || key.find("..") != std::string::npos) { + return false; + } + + return std::all_of(key.begin(), key.end(), [](char c) { + return std::isalnum(c) || c == '_' || c == '-' || c == '.'; + }); + } }; class KeyValueInmemory : public KeyValue { diff --git a/tonlib/tonlib/LastBlockStorage.cpp b/tonlib/tonlib/LastBlockStorage.cpp index 53c4456e..6e5b3f0f 100644 --- a/tonlib/tonlib/LastBlockStorage.cpp +++ b/tonlib/tonlib/LastBlockStorage.cpp @@ -32,13 +32,43 @@ void LastBlockStorage::set_key_value(std::shared_ptr kv) { } namespace { +std::string buffer_to_hex_nibbles_reversed(td::Slice buffer) { + const char *hex = "0123456789ABCDEF"; + std::string res(2 * buffer.size(), '\0'); + for (std::size_t i = 0; i < buffer.size(); i++) { + unsigned char c = buffer[i]; + res[2 * i + 1] = hex[c >> 4]; + res[2 * i] = hex[c & 15]; + } + return res; +} + +std::string get_file_name_depr(td::Slice name) { + return buffer_to_hex_nibbles_reversed(name) + ".blkstate"; +} + std::string get_file_name(td::Slice name) { return td::buffer_to_hex(name) + ".blkstate"; } } // namespace td::Result LastBlockStorage::get_state(td::Slice name) { - TRY_RESULT(data, kv_->get(get_file_name(name))); + // This migration addresses an issue in the old version of Tonlib, where the td::buffer_to_hex + // incorrectly reversed the order of nibbles in hex representation. + auto data_r = kv_->get(get_file_name(name)); + if (data_r.is_error()) { + auto key_depr = get_file_name_depr(name); + auto data_depr = kv_->get(key_depr); + if (data_depr.is_ok()) { + kv_->set(get_file_name(name), data_depr.move_as_ok()); + kv_->erase(key_depr); + data_r = std::move(data_depr); + } else { + return td::Status::Error("not found"); + } + } + + auto data = data_r.move_as_ok(); if (data.size() < 8) { return td::Status::Error("too short"); } diff --git a/tonlib/tonlib/TestGiver.cpp b/tonlib/tonlib/TestGiver.cpp deleted file mode 100644 index b906193d..00000000 --- a/tonlib/tonlib/TestGiver.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "tonlib/TestGiver.h" -#include "tonlib/utils.h" - -#include "td/utils/base64.h" - -namespace tonlib { -const block::StdAddress& TestGiver::address() noexcept { - static block::StdAddress res = - block::StdAddress::parse("kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny").move_as_ok(); - return res; -} - -vm::CellHash TestGiver::get_init_code_hash() noexcept { - return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok()); -} - -td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept { - td::BigInt256 dest_addr; - dest_addr.import_bits(dest_address.addr.as_bitslice()); - vm::CellBuilder cb; - cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) - .store_long(dest_address.bounceable, 1) - .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) - .store_long(dest_address.workchain, 8) - .store_int256(dest_addr, 256); - block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - - cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); - vm::CellString::store(cb, message, 35 * 8).ensure(); - auto message_inner = cb.finalize(); - return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); -} -} // namespace tonlib diff --git a/tonlib/tonlib/TestGiver.h b/tonlib/tonlib/TestGiver.h deleted file mode 100644 index f8b62599..00000000 --- a/tonlib/tonlib/TestGiver.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once -#include "block/block.h" -#include "CellString.h" -namespace tonlib { -class TestGiver { - public: - static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static const block::StdAddress& address() noexcept; - static vm::CellHash get_init_code_hash() noexcept; - static td::Ref make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept; -}; -} // namespace tonlib diff --git a/tonlib/tonlib/TestWallet.cpp b/tonlib/tonlib/TestWallet.cpp deleted file mode 100644 index 8bdf78c9..00000000 --- a/tonlib/tonlib/TestWallet.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "tonlib/TestWallet.h" -#include "tonlib/GenericAccount.h" -#include "tonlib/utils.h" - -#include "vm/boc.h" -#include "td/utils/base64.h" - -namespace tonlib { -td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { - auto code = get_init_code(); - auto data = get_init_data(public_key); - return GenericAccount::get_init_state(std::move(code), std::move(data)); -} - -td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { - std::string seq_no(4, 0); - auto signature = - private_key.sign(vm::CellBuilder().store_bytes(seq_no).finalize()->get_hash().as_slice()).move_as_ok(); - return vm::CellBuilder().store_bytes(signature).store_bytes(seq_no).finalize(); -} - -td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept { - td::BigInt256 dest_addr; - dest_addr.import_bits(dest_address.addr.as_bitslice()); - vm::CellBuilder cb; - cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) - .store_long(dest_address.bounceable, 1) - .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) - .store_long(dest_address.workchain, 8) - .store_int256(dest_addr, 256); - td::int32 send_mode = 3; - if (gramms == -1) { - gramms = 0; - send_mode += 128; - } - block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); - vm::CellString::store(cb, message, 35 * 8).ensure(); - auto message_inner = cb.finalize(); - auto message_outer = - vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize(); - auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok(); - return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize(); -} - -td::Ref TestWallet::get_init_code() noexcept { - static auto res = [] { - auto serialized_code = td::base64_decode( - "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" - "0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==") - .move_as_ok(); - return vm::std_boc_deserialize(serialized_code).move_as_ok(); - }(); - return res; -} - -vm::CellHash TestWallet::get_init_code_hash() noexcept { - return get_init_code()->get_hash(); -} - -td::Ref TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { - return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); -} -} // namespace tonlib diff --git a/tonlib/tonlib/TestWallet.h b/tonlib/tonlib/TestWallet.h deleted file mode 100644 index ef726b55..00000000 --- a/tonlib/tonlib/TestWallet.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once - -#include "vm/cells.h" -#include "Ed25519.h" -#include "block/block.h" -#include "CellString.h" - -namespace tonlib { -class TestWallet { - public: - static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; - static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept; - - static td::Ref get_init_code() noexcept; - static vm::CellHash get_init_code_hash() noexcept; - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; -}; -} // namespace tonlib diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index f19c93d8..58bc817c 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -1053,9 +1053,8 @@ td::Result to_balance(td::Ref balance_ref) { class GetTransactionHistory : public td::actor::Actor { public: - GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash, - td::int32 count, td::actor::ActorShared<> parent, - td::Promise promise) + GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash, td::int32 count, + td::actor::ActorShared<> parent, td::Promise promise) : address_(std::move(address)) , lt_(std::move(lt)) , hash_(std::move(hash)) @@ -1227,8 +1226,7 @@ class RemoteRunSmcMethod : public td::actor::Actor { td::Status do_with_last_block(td::Result r_last_block) { TRY_RESULT(last_block, std::move(r_last_block)); query_.block_id = std::move(last_block.last_block_id); - with_block_id(); - return td::Status::OK(); + return with_block_id(); } void start_up() override { @@ -1729,6 +1727,9 @@ class GetShardBlockProof : public td::actor::Actor { auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result>; auto to_tonlib_api(const ton::lite_api::liteServer_transactionId& txid) -> tonlib_api_ptr; +td::Status check_block_transactions_proof(lite_api_ptr& bTxes, int32_t mode, + ton::LogicalTime start_lt, td::Bits256 start_addr, td::Bits256 root_hash, int req_count); + class RunEmulator : public TonlibQueryActor { public: RunEmulator(ExtClientRef ext_client_ref, int_api::GetAccountStateByTransaction request, @@ -1778,7 +1779,6 @@ class RunEmulator : public TonlibQueryActor { return td::Status::Error("block header proof is not a valid Merkle proof"); } - ton::RootHash vhash{virt_root->get_hash().bits()}; if (ton::RootHash{virt_root->get_hash().bits()} != block_id.root_hash) { return td::Status::Error("block header has incorrect root hash"); } @@ -1837,14 +1837,18 @@ class RunEmulator : public TonlibQueryActor { td::Status get_transactions(std::int64_t lt) { TRY_RESULT(lite_block, to_lite_api(*to_tonlib_api(block_id_.id))); auto after = ton::lite_api::make_object(request_.address.addr, lt); - auto query = ton::lite_api::liteServer_listBlockTransactions(std::move(lite_block), 0b10100111, 256, std::move(after), false, false); + auto mode = 0b10100111; + constexpr int req_count = 256; + auto query = ton::lite_api::liteServer_listBlockTransactions(std::move(lite_block), mode, req_count, std::move(after), false, true); - client_.send_query(std::move(query), [self = this](lite_api_ptr&& bTxes) { + client_.send_query(std::move(query), [self = this, mode, lt, root_hash = block_id_.id.root_hash, req_count](lite_api_ptr&& bTxes) { if (!bTxes) { self->check(td::Status::Error("liteServer.blockTransactions is null")); return; } + self->check(check_block_transactions_proof(bTxes, mode, lt, self->request_.address.addr, root_hash, req_count)); + std::int64_t last_lt = 0; for (auto& id : bTxes->ids_) { last_lt = id->lt_; @@ -2833,7 +2837,8 @@ td::Result TonlibClient::validate_config(tonlib_api::o state.vert_seqno = vert_seqno; bool user_defined_init_block = false; - if (new_config.init_block_id.is_valid() && state.last_key_block_id.id.seqno < new_config.init_block_id.id.seqno) { + if (new_config.init_block_id.is_valid() && + state.last_key_block_id.id.seqno < new_config.init_block_id.id.seqno) { state.last_key_block_id = new_config.init_block_id; user_defined_init_block = true; LOG(INFO) << "Use init block from USER config: " << new_config.init_block_id.to_str(); @@ -2929,7 +2934,8 @@ td::Result to_std_address(td::Ref cs) { } struct ToRawTransactions { explicit ToRawTransactions(td::optional private_key, bool try_decode_messages = true) - : private_key_(std::move(private_key)), try_decode_messages_(try_decode_messages) { + : private_key_(std::move(private_key)) + , try_decode_messages_(try_decode_messages) { } td::optional private_key_; @@ -2966,7 +2972,7 @@ struct ToRawTransactions { tonlib_api::object_ptr data; if (try_decode_messages_ && body->size() >= 32) { auto type = static_cast(body.write().fetch_long(32)); - if (type == 0 || type == 0x2167da4b) { + if (type == 0 || type == ton::WalletInterface::EncryptedCommentOp) { td::Status status; auto r_body_message = TRY_VM(vm::CellString::load(body.write())); @@ -2994,8 +3000,7 @@ struct ToRawTransactions { } } if (!data) { - data = tonlib_api::make_object(to_bytes(std::move(body_cell)), - to_bytes(std::move(init_state_cell))); + data = tonlib_api::make_object(to_bytes(std::move(body_cell)), to_bytes(std::move(init_state_cell))); } return data; }; @@ -3043,8 +3048,7 @@ struct ToRawTransactions { auto created_lt = static_cast(msg_info.created_lt); return tonlib_api::make_object( tonlib_api::make_object(src), - tonlib_api::make_object(), 0, 0, 0, created_lt, std::move(body_hash), - get_data(src)); + tonlib_api::make_object(), 0, 0, 0, created_lt, std::move(body_hash), get_data(src)); } } @@ -3221,9 +3225,10 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessageReturnHash& td::Promise>&& promise) { TRY_RESULT_PREFIX(body, vm::std_boc_deserialize(request.body_), TonlibError::InvalidBagOfCells("body")); auto hash = body->get_hash().as_slice().str(); - make_request(int_api::SendMessage{std::move(body)}, promise.wrap([hash = std::move(hash)](auto res) { - return tonlib_api::make_object(std::move(hash)); - })); + make_request(int_api::SendMessage{std::move(body)}, + promise.wrap([hash = std::move(hash)](auto res) { + return tonlib_api::make_object(std::move(hash)); + })); return td::Status::OK(); } @@ -3334,7 +3339,7 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request, } td::Status TonlibClient::do_request(tonlib_api::raw_getTransactionsV2& request, - td::Promise>&& promise) { + td::Promise>&& promise) { if (!request.account_address_) { return TonlibError::EmptyField("account_address"); } @@ -3370,10 +3375,9 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactionsV2& request, auto actor_id = actor_id_++; actors_[actor_id] = td::actor::create_actor( "GetTransactionHistory", client_.get_client(), account_address, lt, hash, count, actor_shared(this, actor_id), - promise.wrap( - [private_key = std::move(private_key), try_decode_messages = request.try_decode_messages_](auto&& x) mutable { - return ToRawTransactions(std::move(private_key), try_decode_messages).to_raw_transactions(std::move(x)); - })); + promise.wrap([private_key = std::move(private_key), try_decode_messages = request.try_decode_messages_](auto&& x) mutable { + return ToRawTransactions(std::move(private_key), try_decode_messages).to_raw_transactions(std::move(x)); + })); return td::Status::OK(); } @@ -3593,25 +3597,26 @@ class GenericCreateSendGrams : public TonlibQueryActor { td::Result to_dns_action(tonlib_api::dns_Action& action) { using R = td::Result; - return downcast_call2( - action, td::overloaded( - [&](tonlib_api::dns_actionDeleteAll& del_all) -> R { - return ton::ManualDns::Action{"", td::Bits256::zero(), {}}; - }, - [&](tonlib_api::dns_actionDelete& del) -> R { - return ton::ManualDns::Action{del.name_, del.category_, {}}; - }, - [&](tonlib_api::dns_actionSet& set) -> R { - if (!set.entry_) { - return TonlibError::EmptyField("entry"); - } - if (!set.entry_->entry_) { - return TonlibError::EmptyField("entry.entry"); - } - TRY_RESULT(entry_data, to_dns_entry_data(*set.entry_->entry_)); - TRY_RESULT(data_cell, entry_data.as_cell()); - return ton::ManualDns::Action{set.entry_->name_, set.entry_->category_, std::move(data_cell)}; - })); + return downcast_call2(action, + td::overloaded( + [&](tonlib_api::dns_actionDeleteAll& del_all) -> R { + return ton::ManualDns::Action{"", td::Bits256::zero(), {}}; + }, + [&](tonlib_api::dns_actionDelete& del) -> R { + return ton::ManualDns::Action{del.name_, del.category_, {}}; + }, + [&](tonlib_api::dns_actionSet& set) -> R { + if (!set.entry_) { + return TonlibError::EmptyField("entry"); + } + if (!set.entry_->entry_) { + return TonlibError::EmptyField("entry.entry"); + } + TRY_RESULT(entry_data, to_dns_entry_data(*set.entry_->entry_)); + TRY_RESULT(data_cell, entry_data.as_cell()); + return ton::ManualDns::Action{set.entry_->name_, set.entry_->category_, + std::move(data_cell)}; + })); } td::Status parse_action(tonlib_api::Action& action) { @@ -4237,8 +4242,7 @@ td::Status TonlibClient::do_request(const tonlib_api::query_send& request, td::Status TonlibClient::do_request(tonlib_api::query_forget& request, td::Promise>&& promise) { - auto it = queries_.find(request.id_); - if (it == queries_.end()) { + if (queries_.erase(request.id_) == 0) { return TonlibError::InvalidQueryId(); } promise.set_value(tonlib_api::make_object()); @@ -4486,10 +4490,31 @@ void deep_library_search(std::set& set, std::set& v td::Status TonlibClient::do_request(const tonlib_api::smc_getLibraries& request, td::Promise>&& promise) { + if (request.library_list_.size() > 16) { + promise.set_error(TonlibError::InvalidField("library_list", ": too many libraries requested, 16 maximum")); + } + if (query_context_.block_id) { + get_libraries(query_context_.block_id.value(), request.library_list_, std::move(promise)); + } else { + client_.with_last_block([this, promise = std::move(promise), library_list = request.library_list_](td::Result r_last_block) mutable { + if (r_last_block.is_error()) { + promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed "))); + } else { + this->get_libraries(r_last_block.move_as_ok().last_block_id, library_list, std::move(promise)); + } + }); + } + return td::Status::OK(); +} + +void TonlibClient::get_libraries(ton::BlockIdExt blkid, std::vector library_list, td::Promise>&& promise) { + sort(library_list.begin(), library_list.end()); + library_list.erase(unique(library_list.begin(), library_list.end()), library_list.end()); + std::vector> result_entries; - result_entries.reserve(request.library_list_.size()); + result_entries.reserve(library_list.size()); std::vector not_cached_hashes; - for (auto& library_hash : request.library_list_) { + for (auto& library_hash : library_list) { if (libraries.key_exists(library_hash)) { auto library_content = vm::std_boc_serialize(libraries.lookup_ref(library_hash)).move_as_ok().as_slice().str(); result_entries.push_back(tonlib_api::make_object(library_hash, library_content)); @@ -4500,42 +4525,80 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_getLibraries& request, if (not_cached_hashes.empty()) { promise.set_value(tonlib_api::make_object(std::move(result_entries))); - return td::Status::OK(); + return; } - client_.send_query( - ton::lite_api::liteServer_getLibraries(std::move(not_cached_hashes)), - promise.wrap( - [self = this, result_entries = std::move(result_entries)]( - td::Result> r_libraries) mutable { - if (r_libraries.is_error()) { - LOG(WARNING) << "cannot obtain found libraries: " << r_libraries.move_as_error().to_string(); - } else { - auto libraries = r_libraries.move_as_ok(); - bool updated = false; - for (auto& lr : libraries->result_) { - auto contents = vm::std_boc_deserialize(lr->data_); - if (contents.is_ok() && contents.ok().not_null()) { - if (contents.ok()->get_hash().bits().compare(lr->hash_.cbits(), 256)) { - LOG(WARNING) << "hash mismatch for library " << lr->hash_.to_hex(); - continue; - } - result_entries.push_back( - tonlib_api::make_object(lr->hash_, lr->data_.as_slice().str())); - self->libraries.set_ref(lr->hash_, contents.move_as_ok()); - updated = true; - LOG(DEBUG) << "registered library " << lr->hash_.to_hex(); - } else { - LOG(WARNING) << "failed to deserialize library: " << lr->hash_.to_hex(); - } - if (updated) { - self->store_libs_to_disk(); - } - } - } - return tonlib_api::make_object(std::move(result_entries)); - })); - return td::Status::OK(); + client_.send_query(ton::lite_api::liteServer_getLibrariesWithProof(ton::create_tl_lite_block_id(blkid), 1, std::move(not_cached_hashes)), + promise.wrap([self=this, blkid, result_entries = std::move(result_entries), not_cached_hashes] + (td::Result> r_libraries) mutable + -> td::Result> { + if (r_libraries.is_error()) { + LOG(WARNING) << "cannot obtain found libraries: " << r_libraries.move_as_error().to_string(); + return r_libraries.move_as_error(); + } + + auto libraries = r_libraries.move_as_ok(); + auto state = block::check_extract_state_proof(blkid, libraries->state_proof_.as_slice(), + libraries->data_proof_.as_slice()); + if (state.is_error()) { + LOG(WARNING) << "cannot check state proof: " << state.move_as_error().to_string(); + return state.move_as_error(); + } + auto state_root = state.move_as_ok(); + + try { + block::gen::ShardStateUnsplit::Record state_record; + if (!tlb::unpack_cell(state_root, state_record)) { + return td::Status::Error("cannot unpack shardchain state"); + } + auto libraries_dict = vm::Dictionary(state_record.r1.libraries->prefetch_ref(), 256); + + for (auto& hash : not_cached_hashes) { + auto csr = libraries_dict.lookup(hash.bits(), 256); + if (csr.is_null()) { + LOG(WARNING) << "library " << hash.to_hex() << " not found in config"; + if (std::any_of(libraries->result_.begin(), libraries->result_.end(), + [&hash](const auto& lib) { return lib->hash_.bits().equals(hash.cbits(), 256); })) { + return TonlibError::Internal("library is included in response but it's not found in proof"); + } + continue; + } + block::gen::LibDescr::Record libdescr; + if (!tlb::csr_unpack(csr, libdescr)) { + return TonlibError::Internal("cannot unpack LibDescr record"); + } + + auto lib_it = std::find_if(libraries->result_.begin(), libraries->result_.end(), + [&hash](const auto& lib) { return lib->hash_.bits().equals(hash.cbits(), 256); }); + if (lib_it == libraries->result_.end()) { + return TonlibError::Internal("library is found in proof but not in response"); + } + auto& lib = *lib_it; + auto contents = vm::std_boc_deserialize(lib->data_); + if (!contents.is_ok() || contents.ok().is_null()) { + return TonlibError::Internal(PSLICE() << "cannot deserialize library cell " << lib->hash_.to_hex()); + } + + if (!contents.ok()->get_hash().bits().equals(hash.cbits(), 256)) { + return TonlibError::Internal(PSLICE() << "library hash mismatch data " << contents.ok()->get_hash().to_hex() << " != requested " << hash.to_hex()); + } + + if (contents.ok()->get_hash() != libdescr.lib->get_hash()) { + return TonlibError::Internal(PSLICE() << "library hash mismatch data " << lib->hash_.to_hex() << " != proof " << libdescr.lib->get_hash().to_hex()); + } + + result_entries.push_back(tonlib_api::make_object(lib->hash_, lib->data_.as_slice().str())); + self->libraries.set_ref(lib->hash_, contents.move_as_ok()); + LOG(DEBUG) << "registered library " << lib->hash_.to_hex(); + } + self->store_libs_to_disk(); + return tonlib_api::make_object(std::move(result_entries)); + } catch (vm::VmError& err) { + return TonlibError::Internal(PSLICE() << "error while checking getLibrariesWithProof proof: " << err.get_msg()); + } catch (vm::VmVirtError& err) { + return TonlibError::Internal(PSLICE() << "virtualization error while checking getLibrariesWithProof proof: " << err.get_msg()); + } + })); } td::Status TonlibClient::do_request(const tonlib_api::smc_getLibrariesExt& request, @@ -4627,8 +4690,8 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request, args.set_now(it->second->get_sync_time()); args.set_address(it->second->get_address()); - client_.with_last_config([self = this, smc = std::move(smc), args = std::move(args), - promise = std::move(promise)](td::Result r_state) mutable { + client_.with_last_config([self = this, smc = std::move(smc), args = std::move(args), promise = std::move(promise) + ](td::Result r_state) mutable { TRY_RESULT_PROMISE(promise, state, std::move(r_state)); args.set_config(state.config); args.set_prev_blocks_info(state.prev_blocks_info); @@ -4688,6 +4751,7 @@ void TonlibClient::process_new_libraries( void TonlibClient::perform_smc_execution(td::Ref smc, ton::SmartContract::Args args, td::Promise>&& promise) { + args.set_libraries(libraries); auto res = smc->run_get_method(args); @@ -4818,8 +4882,9 @@ void TonlibClient::do_dns_request(std::string name, td::Bits256 category, td::in td::optional block_id, block::StdAddress address, td::Promise>&& promise) { auto block_id_copy = block_id.copy(); - td::Promise new_promise = promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, - category, ttl, std::move(block_id), address); + td::Promise new_promise = + promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, category, ttl, std::move(block_id), + address); if (0) { make_request(int_api::GetAccountState{address, std::move(block_id_copy), {}}, @@ -4857,7 +4922,8 @@ td::Status TonlibClient::do_request(const tonlib_api::dns_resolve& request, name += '.'; } TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); - do_dns_request(name, request.category_, request.ttl_, std::move(block_id), account_address, std::move(promise)); + do_dns_request(name, request.category_, request.ttl_, std::move(block_id), account_address, + std::move(promise)); return td::Status::OK(); } @@ -5308,15 +5374,15 @@ auto to_tonlib_api(const ton::lite_api::tonNode_blockIdExt& blk) -> tonlib_api_p auto to_tonlib_api(const ton::lite_api::tonNode_zeroStateIdExt& zeroStateId) -> tonlib_api_ptr { - return tonlib_api::make_object( //TODO check wether shard indeed 0??? + return tonlib_api::make_object( //TODO check wether shard indeed 0??? zeroStateId.workchain_, 0, 0, zeroStateId.root_hash_.as_slice().str(), zeroStateId.file_hash_.as_slice().str()); } auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result> { TRY_RESULT(root_hash, to_bits256(blk.root_hash_, "blk.root_hash")) TRY_RESULT(file_hash, to_bits256(blk.file_hash_, "blk.file_hash")) - return ton::lite_api::make_object(blk.workchain_, blk.shard_, blk.seqno_, - root_hash, file_hash); + return ton::lite_api::make_object( + blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash); } td::Result to_block_id(const tonlib_api::ton_blockIdExt& blk) { @@ -5328,7 +5394,7 @@ td::Result to_block_id(const tonlib_api::ton_blockIdExt& blk) { void TonlibClient::get_config_param(int32_t param, int32_t mode, ton::BlockIdExt block, td::Promise>&& promise) { std::vector params = { param }; client_.send_query(ton::lite_api::liteServer_getConfigParams(mode, ton::create_tl_lite_block_id(block), std::move(params)), - promise.wrap([param, block](auto r_config) -> td::Result> { + promise.wrap([param, block](auto r_config) -> td::Result> { auto state = block::check_extract_state_proof(block, r_config->state_proof_.as_slice(), r_config->config_proof_.as_slice()); if (state.is_error()) { @@ -5349,7 +5415,7 @@ td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request, if (query_context_.block_id) { get_config_param(request.param_, request.mode_, query_context_.block_id.value(), std::move(promise)); } else { - client_.with_last_block([this, promise = std::move(promise), param = request.param_, mode = request.mode_](td::Result r_last_block) mutable { + client_.with_last_block([this, promise = std::move(promise), param = request.param_, mode = request.mode_](td::Result r_last_block) mutable { if (r_last_block.is_error()) { promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed "))); } else { @@ -5362,7 +5428,7 @@ td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request, void TonlibClient::get_config_all(int32_t mode, ton::BlockIdExt block, td::Promise>&& promise) { client_.send_query(ton::lite_api::liteServer_getConfigAll(mode, ton::create_tl_lite_block_id(block)), - promise.wrap([block](auto r_config) -> td::Result> { + promise.wrap([block](auto r_config) -> td::Result> { auto state = block::check_extract_state_proof(block, r_config->state_proof_.as_slice(), r_config->config_proof_.as_slice()); if (state.is_error()) { @@ -5383,7 +5449,7 @@ td::Status TonlibClient::do_request(const tonlib_api::getConfigAll& request, if (query_context_.block_id) { get_config_all(request.mode_, query_context_.block_id.value(), std::move(promise)); } else { - client_.with_last_block([this, promise = std::move(promise), mode = request.mode_](td::Result r_last_block) mutable { + client_.with_last_block([this, promise = std::move(promise), mode = request.mode_](td::Result r_last_block) mutable { if (r_last_block.is_error()) { promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed "))); } else { @@ -5395,7 +5461,7 @@ td::Status TonlibClient::do_request(const tonlib_api::getConfigAll& request, } td::Status TonlibClient::do_request(const tonlib_api::blocks_getMasterchainInfo& masterchain_info, - td::Promise>&& promise) { + td::Promise>&& promise) { client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(), promise.wrap([](lite_api_ptr&& masterchain_info) { return tonlib_api::make_object( @@ -5408,68 +5474,345 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getMasterchainInfo& td::Status TonlibClient::do_request(const tonlib_api::blocks_getShards& request, td::Promise>&& promise) { TRY_RESULT(block, to_lite_api(*request.id_)) + TRY_RESULT(req_blk_id, to_block_id(*request.id_)); client_.send_query(ton::lite_api::liteServer_getAllShardsInfo(std::move(block)), - promise.wrap([](lite_api_ptr&& all_shards_info) + promise.wrap([req_blk_id](lite_api_ptr&& all_shards_info) -> td::Result> { - td::BufferSlice proof = std::move((*all_shards_info).proof_); - td::BufferSlice data = std::move((*all_shards_info).data_); - if (data.empty()) { - return td::Status::Error("shard configuration is empty"); - } else { - auto R = vm::std_boc_deserialize(data.clone()); - if (R.is_error()) { - return R.move_as_error_prefix("cannot deserialize shard configuration: "); - } - auto root = R.move_as_ok(); - block::ShardConfig sh_conf; - if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) { - return td::Status::Error("cannot extract shard block list from shard configuration"); - } else { - auto ids = sh_conf.get_shard_hash_ids(true); - tonlib_api::blocks_shards shards; - for (auto id : ids) { - auto ref = sh_conf.get_shard_hash(ton::ShardIdFull(id)); - if (ref.not_null()) { - shards.shards_.push_back(to_tonlib_api(ref->top_block_id())); - } - } - return tonlib_api::make_object(std::move(shards)); - } - } + auto blk_id = ton::create_block_id(all_shards_info->id_); + if (blk_id != req_blk_id) { + return td::Status::Error("Liteserver responded with wrong block"); + } + td::BufferSlice proof = std::move((*all_shards_info).proof_); + td::BufferSlice data = std::move((*all_shards_info).data_); + if (data.empty() || proof.empty()) { + return td::Status::Error("Shard configuration or proof is empty"); + } + auto proof_cell = vm::std_boc_deserialize(std::move(proof)); + if (proof_cell.is_error()) { + return proof_cell.move_as_error_prefix("Couldn't deserialize shards proof: "); + } + auto data_cell = vm::std_boc_deserialize(std::move(data)); + if (data_cell.is_error()) { + return data_cell.move_as_error_prefix("Couldn't deserialize shards data: "); + } + try { + auto virt_root = vm::MerkleProof::virtualize(proof_cell.move_as_ok(), 1); + if (virt_root.is_null()) { + return td::Status::Error("Virt root is null"); + } + if (ton::RootHash{virt_root->get_hash().bits()} != blk_id.root_hash) { + return td::Status::Error("Block shards merkle proof has incorrect root hash"); + } + + block::gen::Block::Record blk; + block::gen::BlockExtra::Record extra; + block::gen::McBlockExtra::Record mc_extra; + if (!tlb::unpack_cell(virt_root, blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() || + !tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) { + return td::Status::Error("cannot unpack block extra of block " + blk_id.to_str()); + } + auto data_csr = vm::load_cell_slice_ref(data_cell.move_as_ok()); + if (data_csr->prefetch_ref()->get_hash() != mc_extra.shard_hashes->prefetch_ref()->get_hash()) { + return td::Status::Error("Block shards data and proof hashes don't match"); + } + + block::ShardConfig sh_conf; + if (!sh_conf.unpack(mc_extra.shard_hashes)) { + return td::Status::Error("cannot extract shard block list from shard configuration"); + } + auto ids = sh_conf.get_shard_hash_ids(true); + tonlib_api::blocks_shards shards; + for (auto& id : ids) { + auto ref = sh_conf.get_shard_hash(ton::ShardIdFull(id)); + if (ref.not_null()) { + shards.shards_.push_back(to_tonlib_api(ref->top_block_id())); + } + } + return tonlib_api::make_object(std::move(shards)); + } catch (vm::VmError& err) { + return err.as_status("Couldn't verify proof: "); + } catch (vm::VmVirtError& err) { + return err.as_status("Couldn't verify proof: "); + } catch (...) { + return td::Status::Error("Unknown exception raised while verifying proof"); + } })); return td::Status::OK(); } +td::Status check_lookup_block_proof(lite_api_ptr& result, int mode, ton::BlockId blkid, ton::BlockIdExt client_mc_blkid, td::uint64 lt, td::uint32 utime); + td::Status TonlibClient::do_request(const tonlib_api::blocks_lookupBlock& request, - td::Promise>&& promise) { - client_.send_query(ton::lite_api::liteServer_lookupBlock( - request.mode_, - ton::lite_api::make_object( - (*request.id_).workchain_, (*request.id_).shard_, (*request.id_).seqno_), - (td::uint64)(request.lt_), (td::uint32)(request.utime_)), - promise.wrap([](lite_api_ptr&& header) { - const auto& id = header->id_; - return to_tonlib_api(*id); - //tonlib_api::make_object( - // ton::tonlib_api::ton_blockIdExt(id->workchain_, id->) - //); - })); + td::Promise>&& promise) { + auto lite_block = ton::lite_api::make_object((*request.id_).workchain_, (*request.id_).shard_, (*request.id_).seqno_); + auto blkid = ton::BlockId(request.id_->workchain_, request.id_->shard_, request.id_->seqno_); + client_.with_last_block( + [self = this, blkid, lite_block = std::move(lite_block), mode = request.mode_, lt = (td::uint64)request.lt_, + utime = (td::uint32)request.utime_, promise = std::move(promise)](td::Result r_last_block) mutable { + self->client_.send_query(ton::lite_api::liteServer_lookupBlockWithProof(mode, std::move(lite_block), ton::create_tl_lite_block_id(r_last_block.ok().last_block_id), lt, utime), + promise.wrap([blkid, mode, utime, lt, last_block = r_last_block.ok().last_block_id](lite_api_ptr&& result) + -> td::Result> { + TRY_STATUS(check_lookup_block_proof(result, mode, blkid, last_block, lt, utime)); + return to_tonlib_api(*result->id_); + }) + ); + }); + return td::Status::OK(); +} + +td::Status check_lookup_block_proof(lite_api_ptr& result, int mode, ton::BlockId blkid, ton::BlockIdExt client_mc_blkid, td::uint64 lt, td::uint32 utime) { + try { + ton::BlockIdExt cur_id = ton::create_block_id(result->mc_block_id_); + try { + for (auto& link : result->shard_links_) { + ton::BlockIdExt prev_id = create_block_id(link->id_); + td::BufferSlice proof = std::move(link->proof_); + auto R = vm::std_boc_deserialize(proof); + if (R.is_error()) { + return TonlibError::InvalidBagOfCells("proof"); + } + auto block_root = vm::MerkleProof::virtualize(R.move_as_ok(), 1); + if (cur_id.root_hash != block_root->get_hash().bits()) { + return td::Status::Error("invalid block hash in proof"); + } + if (cur_id.is_masterchain()) { + if (client_mc_blkid != cur_id) { + auto state = block::check_extract_state_proof(client_mc_blkid, result->client_mc_state_proof_.as_slice(), + result->mc_block_proof_.as_slice()); + if (state.is_error()) { + LOG(WARNING) << "cannot check state proof: " << state.move_as_error().to_string(); + return state.move_as_error(); + } + auto state_root = state.move_as_ok(); + auto prev_blocks_dict = block::get_prev_blocks_dict(state_root); + if (!prev_blocks_dict) { + return td::Status::Error("cannot extract prev blocks dict from state"); + } + + if (!block::check_old_mc_block_id(*prev_blocks_dict, cur_id)) { + return td::Status::Error("couldn't check old mc block id"); + } + } + block::gen::Block::Record blk; + block::gen::BlockExtra::Record extra; + block::gen::McBlockExtra::Record mc_extra; + if (!tlb::unpack_cell(block_root, blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() || + !tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) { + return td::Status::Error("cannot unpack block header"); + } + block::ShardConfig shards(mc_extra.shard_hashes->prefetch_ref()); + td::Ref shard_hash = shards.get_shard_hash(prev_id.shard_full(), true); + if (shard_hash.is_null() || shard_hash->top_block_id() != prev_id) { + return td::Status::Error("invalid proof chain: prev block is not in mc shard list"); + } + } else { + std::vector prev; + ton::BlockIdExt mc_blkid; + bool after_split; + td::Status S = block::unpack_block_prev_blk_try(block_root, cur_id, prev, mc_blkid, after_split); + if (S.is_error()) { + return S; + } + CHECK(prev.size() == 1 || prev.size() == 2); + bool found = prev_id == prev[0] || (prev.size() == 2 && prev_id == prev[1]); + if (!found) { + return td::Status::Error("invalid proof chain: prev block is not in prev blocks list"); + } + } + cur_id = prev_id; + } + } catch (vm::VmVirtError& err) { + return err.as_status(); + } + if (cur_id.id.workchain != blkid.workchain || !ton::shard_contains(cur_id.id.shard, blkid.shard)) { + return td::Status::Error("response block has incorrect workchain/shard"); + } + + auto header_r = vm::std_boc_deserialize(std::move(result->header_)); + if (header_r.is_error()) { + return TonlibError::InvalidBagOfCells("header"); + } + auto header_root = vm::MerkleProof::virtualize(header_r.move_as_ok(), 1); + if (header_root.is_null()) { + return td::Status::Error("header_root is null"); + } + if (cur_id.root_hash != header_root->get_hash().bits()) { + return td::Status::Error("invalid header hash in proof"); + } + + std::vector prev; + ton::BlockIdExt mc_blkid; + bool after_split; + auto R = block::unpack_block_prev_blk_try(header_root, cur_id, prev, mc_blkid, after_split); + if (R.is_error()) { + return R; + } + if (cur_id != ton::create_block_id(result->id_)) { + return td::Status::Error("response blkid doesn't match header"); + } + + block::gen::Block::Record blk; + block::gen::BlockInfo::Record info; + if (!(tlb::unpack_cell(header_root, blk) && tlb::unpack_cell(blk.info, info))) { + return td::Status::Error("block header unpack failed"); + } + + if (mode & 1) { + if (cur_id.seqno() != blkid.seqno) { + return td::Status::Error("invalid seqno in proof"); + } + } else if (mode & 6) { + auto prev_header_r = vm::std_boc_deserialize(std::move(result->prev_header_)); + if (prev_header_r.is_error()) { + return TonlibError::InvalidBagOfCells("prev_headers"); + } + auto prev_header = prev_header_r.move_as_ok(); + auto prev_root = vm::MerkleProof::virtualize(prev_header, 1); + if (prev_root.is_null()) { + return td::Status::Error("prev_root is null"); + } + + bool prev_valid = false; + int prev_idx = -1; + for (size_t i = 0; i < prev.size(); i++) { + if (prev[i].root_hash == prev_root->get_hash().bits()) { + prev_valid = true; + prev_idx = i; + } + } + if (!prev_valid) { + return td::Status::Error("invalid prev header hash in proof"); + } + if (!ton::shard_contains(prev[prev_idx].id.shard, blkid.shard)) { + return td::Status::Error("invalid prev header shard in proof"); + } + + block::gen::Block::Record prev_blk; + block::gen::BlockInfo::Record prev_info; + if (!(tlb::unpack_cell(prev_root, prev_blk) && tlb::unpack_cell(prev_blk.info, prev_info))) { + return td::Status::Error("prev header unpack failed"); + } + + if (mode & 2) { + if (prev_info.end_lt > lt) { + return td::Status::Error("prev header end_lt > lt"); + } + if (info.end_lt < lt) { + return td::Status::Error("header end_lt < lt"); + } + } else if (mode & 4) { + if (prev_info.gen_utime > utime) { + return td::Status::Error("prev header end_lt > lt"); + } + if (info.gen_utime < utime) { + return td::Status::Error("header end_lt < lt"); + } + } + } + } catch (vm::VmError& err) { + return td::Status::Error(PSLICE() << "error while checking lookupBlock proof: " << err.get_msg()); + } catch (vm::VmVirtError& err) { + return td::Status::Error(PSLICE() << "virtualization error while checking lookupBlock proof: " << err.get_msg()); + } + return td::Status::OK(); } auto to_tonlib_api(const ton::lite_api::liteServer_transactionId& txid) -> tonlib_api_ptr { - return tonlib_api::make_object(txid.mode_, txid.account_.as_slice().str(), txid.lt_, - txid.hash_.as_slice().str()); + return tonlib_api::make_object( + txid.mode_, txid.account_.as_slice().str(), txid.lt_, txid.hash_.as_slice().str()); +} + +td::Status check_block_transactions_proof(lite_api_ptr& bTxes, int32_t mode, + ton::LogicalTime start_lt, td::Bits256 start_addr, td::Bits256 root_hash, int req_count) { + if (mode & ton::lite_api::liteServer_listBlockTransactions::WANT_PROOF_MASK == 0) { + return td::Status::OK(); + } + constexpr int max_answer_transactions = 256; + bool reverse_mode = mode & ton::lite_api::liteServer_listBlockTransactions::REVERSE_ORDER_MASK; + + try { + TRY_RESULT(proof_cell, vm::std_boc_deserialize(std::move(bTxes->proof_))); + auto virt_root = vm::MerkleProof::virtualize(proof_cell, 1); + + if (root_hash != virt_root->get_hash().bits()) { + return td::Status::Error("Invalid block proof root hash"); + } + block::gen::Block::Record blk; + block::gen::BlockExtra::Record extra; + if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) { + return td::Status::Error("Error unpacking proof cell"); + } + vm::AugmentedDictionary acc_dict{vm::load_cell_slice_ref(extra.account_blocks), 256, + block::tlb::aug_ShardAccountBlocks}; + + bool eof = false; + ton::LogicalTime reverse = reverse_mode ? ~0ULL : 0; + ton::LogicalTime trans_lt = static_cast(start_lt); + td::Bits256 cur_addr = start_addr; + bool allow_same = true; + int count = 0; + while (!eof && count < req_count && count < max_answer_transactions) { + auto value = acc_dict.extract_value( + acc_dict.vm::DictionaryFixed::lookup_nearest_key(cur_addr.bits(), 256, !reverse, allow_same)); + if (value.is_null()) { + eof = true; + break; + } + allow_same = false; + if (cur_addr != start_addr) { + trans_lt = reverse; + } + + block::gen::AccountBlock::Record acc_blk; + if (!tlb::csr_unpack(std::move(value), acc_blk) || acc_blk.account_addr != cur_addr) { + return td::Status::Error("Error unpacking proof account block"); + } + vm::AugmentedDictionary trans_dict{vm::DictNonEmpty(), std::move(acc_blk.transactions), 64, + block::tlb::aug_AccountTransactions}; + td::BitArray<64> cur_trans{(long long)trans_lt}; + while (count < req_count && count < max_answer_transactions) { + auto tvalue = trans_dict.extract_value_ref( + trans_dict.vm::DictionaryFixed::lookup_nearest_key(cur_trans.bits(), 64, !reverse)); + if (tvalue.is_null()) { + trans_lt = reverse; + break; + } + if (static_cast(count) < bTxes->ids_.size()) { + if (mode & 4 && !tvalue->get_hash().bits().equals(bTxes->ids_[count]->hash_.bits(), 256)) { + return td::Status::Error("Couldn't verify proof (hash)"); + } + if (mode & 2 && cur_trans != td::BitArray<64>(bTxes->ids_[count]->lt_)) { + return td::Status::Error("Couldn't verify proof (lt)"); + } + if (mode & 1 && cur_addr != bTxes->ids_[count]->account_) { + return td::Status::Error("Couldn't verify proof (account)"); + } + } + count++; + } + } + if (static_cast(count) != bTxes->ids_.size()) { + return td::Status::Error(PSLICE() << "Txs count mismatch in proof (" << count << ") and response (" << bTxes->ids_.size() << ")"); + } + } catch (vm::VmError& err) { + return err.as_status("Couldn't verify proof: "); + } catch (vm::VmVirtError& err) { + return err.as_status("Couldn't verify proof: "); + } catch (...) { + return td::Status::Error("Unknown exception raised while verifying proof"); + } + return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& request, - td::Promise>&& promise) { + td::Promise>&& promise) { TRY_RESULT(block, to_lite_api(*request.id_)) auto root_hash = block->root_hash_; - bool check_proof = request.mode_ & 32; - bool reverse_mode = request.mode_ & 64; - bool has_starting_tx = request.mode_ & 128; + bool check_proof = request.mode_ & ton::lite_api::liteServer_listBlockTransactions::WANT_PROOF_MASK; + bool reverse_mode = request.mode_ & ton::lite_api::liteServer_listBlockTransactions::REVERSE_ORDER_MASK; + bool has_starting_tx = request.mode_ & ton::lite_api::liteServer_listBlockTransactions::AFTER_MASK; td::Bits256 start_addr; ton::LogicalTime start_lt; @@ -5496,81 +5839,8 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& re check_proof), promise.wrap([check_proof, reverse_mode, root_hash, req_count = request.count_, start_addr, start_lt, mode = request.mode_] (lite_api_ptr&& bTxes) -> td::Result> { - if (check_proof) { - try { - constexpr int max_answer_transactions = 256; - TRY_RESULT(proof_cell, vm::std_boc_deserialize(std::move(bTxes->proof_))); - auto virt_root = vm::MerkleProof::virtualize(proof_cell, 1); - - if (root_hash != virt_root->get_hash().bits()) { - return td::Status::Error("Invalid block proof root hash"); - } - block::gen::Block::Record blk; - block::gen::BlockExtra::Record extra; - if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) { - return td::Status::Error("Error unpacking proof cell"); - } - vm::AugmentedDictionary acc_dict{vm::load_cell_slice_ref(extra.account_blocks), 256, - block::tlb::aug_ShardAccountBlocks}; - - bool eof = false; - ton::LogicalTime reverse = reverse_mode ? ~0ULL : 0; - ton::LogicalTime trans_lt = static_cast(start_lt); - td::Bits256 cur_addr = start_addr; - bool allow_same = true; - int count = 0; - while (!eof && count < req_count && count < max_answer_transactions) { - auto value = acc_dict.extract_value( - acc_dict.vm::DictionaryFixed::lookup_nearest_key(cur_addr.bits(), 256, !reverse, allow_same)); - if (value.is_null()) { - eof = true; - break; - } - allow_same = false; - if (cur_addr != start_addr) { - trans_lt = reverse; - } - - block::gen::AccountBlock::Record acc_blk; - if (!tlb::csr_unpack(std::move(value), acc_blk) || acc_blk.account_addr != cur_addr) { - return td::Status::Error("Error unpacking proof account block"); - } - vm::AugmentedDictionary trans_dict{vm::DictNonEmpty(), std::move(acc_blk.transactions), 64, - block::tlb::aug_AccountTransactions}; - td::BitArray<64> cur_trans{(long long)trans_lt}; - while (count < req_count && count < max_answer_transactions) { - auto tvalue = trans_dict.extract_value_ref( - trans_dict.vm::DictionaryFixed::lookup_nearest_key(cur_trans.bits(), 64, !reverse)); - if (tvalue.is_null()) { - trans_lt = reverse; - break; - } - if (static_cast(count) < bTxes->ids_.size()) { - if (mode & 4 && !tvalue->get_hash().bits().equals(bTxes->ids_[count]->hash_.bits(), 256)) { - return td::Status::Error("Couldn't verify proof (hash)"); - } - if (mode & 2 && cur_trans != td::BitArray<64>(bTxes->ids_[count]->lt_)) { - return td::Status::Error("Couldn't verify proof (lt)"); - } - if (mode & 1 && cur_addr != bTxes->ids_[count]->account_) { - return td::Status::Error("Couldn't verify proof (account)"); - } - } - count++; - } - } - if (static_cast(count) != bTxes->ids_.size()) { - return td::Status::Error(PSLICE() << "Txs count mismatch in proof (" << count << ") and response (" << bTxes->ids_.size() << ")"); - } - } catch (vm::VmError& err) { - return err.as_status("Couldn't verify proof: "); - } catch (vm::VmVirtError& err) { - return err.as_status("Couldn't verify proof: "); - } catch (...) { - return td::Status::Error("Unknown exception raised while verifying proof"); - } - } - + TRY_STATUS(check_block_transactions_proof(bTxes, mode, start_lt, start_addr, root_hash, req_count)); + tonlib_api::blocks_transactions r; r.id_ = to_tonlib_api(*bTxes->id_); r.req_count_ = bTxes->req_count_; @@ -5586,10 +5856,10 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& re td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactionsExt& request, td::Promise>&& promise) { TRY_RESULT(block, to_lite_api(*request.id_)) - bool check_proof = request.mode_ & 32; - bool reverse_mode = request.mode_ & 64; - bool has_starting_tx = request.mode_ & 128; - + bool check_proof = request.mode_ & ton::lite_api::liteServer_listBlockTransactionsExt::WANT_PROOF_MASK; + bool reverse_mode = request.mode_ & ton::lite_api::liteServer_listBlockTransactionsExt::REVERSE_ORDER_MASK; + bool has_starting_tx = request.mode_ & ton::lite_api::liteServer_listBlockTransactionsExt::AFTER_MASK; + td::Bits256 start_addr; ton::LogicalTime start_lt; ton::lite_api::object_ptr after; @@ -5597,7 +5867,7 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactionsExt& if (!request.after_) { return td::Status::Error("Missing field `after`"); } - TRY_RESULT_ASSIGN(start_addr, to_bits256(request.after_->account_, "account")); + TRY_RESULT_ASSIGN(start_addr, to_bits256(request.after_->account_, "account")); start_lt = request.after_->lt_; after = ton::lite_api::make_object(start_addr, start_lt); } else { @@ -5618,7 +5888,7 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactionsExt& if (block_id != create_block_id(bTxes->id_)) { return td::Status::Error("Liteserver responded with wrong block"); } - + block::BlockTransactionList list; list.blkid = block_id; list.transactions_boc = std::move(bTxes->transactions_); @@ -5648,78 +5918,80 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactionsExt& } td::Status TonlibClient::do_request(const tonlib_api::blocks_getBlockHeader& request, - td::Promise>&& promise) { - TRY_RESULT(block, to_lite_api(*request.id_)) - client_.send_query( - ton::lite_api::liteServer_getBlockHeader(std::move(block), 0xffff), - promise.wrap([](lite_api_ptr&& hdr) { - auto blk_id = ton::create_block_id(hdr->id_); - auto R = vm::std_boc_deserialize(std::move(hdr->header_proof_)); - tonlib_api::blocks_header header; - if (R.is_error()) { - LOG(WARNING) << "R.is_error() "; - } else { - auto root = R.move_as_ok(); - try { - ton::RootHash vhash{root->get_hash().bits()}; - auto virt_root = vm::MerkleProof::virtualize(root, 1); - if (virt_root.is_null()) { - LOG(WARNING) << "virt root is null"; - } else { - std::vector prev; - ton::BlockIdExt mc_blkid; - bool after_split; - auto res = block::unpack_block_prev_blk_ext(virt_root, blk_id, prev, mc_blkid, after_split); - if (res.is_error()) { - LOG(WARNING) << "res.is_error() "; - } else { - block::gen::Block::Record blk; - block::gen::BlockInfo::Record info; - if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(blk.info, info))) { - LOG(WARNING) << "unpack failed"; - } else { - header.id_ = to_tonlib_api(blk_id); - header.global_id_ = blk.global_id; - header.version_ = info.version; - header.flags_ = info.flags; - header.after_merge_ = info.after_merge; - header.after_split_ = info.after_split; - header.before_split_ = info.before_split; - header.want_merge_ = info.want_merge; - header.want_split_ = info.want_split; - header.validator_list_hash_short_ = info.gen_validator_list_hash_short; - header.catchain_seqno_ = info.gen_catchain_seqno; - header.min_ref_mc_seqno_ = info.min_ref_mc_seqno; - header.start_lt_ = info.start_lt; - header.end_lt_ = info.end_lt; - header.gen_utime_ = info.gen_utime; - header.is_key_block_ = info.key_block; - header.vert_seqno_ = info.vert_seq_no; - if (!info.not_master) { - header.prev_key_block_seqno_ = info.prev_key_block_seqno; - } - for (auto id : prev) { - header.prev_blocks_.push_back(to_tonlib_api(id)); - } - //if(info.before_split) { - //} else { - //} - return tonlib_api::make_object(std::move(header)); - } - } - } - } catch (vm::VmError& err) { - auto E = err.as_status(PSLICE() << "error processing header for " << blk_id.to_str() << " :"); - LOG(ERROR) << std::move(E); - } catch (vm::VmVirtError& err) { - auto E = err.as_status(PSLICE() << "error processing header for " << blk_id.to_str() << " :"); - LOG(ERROR) << std::move(E); - } catch (...) { - LOG(WARNING) << "exception catched "; - } - } - return tonlib_api::make_object(std::move(header)); - })); + td::Promise>&& promise) { + TRY_RESULT(lite_block, to_lite_api(*request.id_)) + TRY_RESULT(req_blk_id, to_block_id(*request.id_)); + client_.send_query(ton::lite_api::liteServer_getBlockHeader( + std::move(lite_block), + 0xffff), + promise.wrap([req_blk_id](lite_api_ptr&& hdr) -> td::Result> { + auto blk_id = ton::create_block_id(hdr->id_); + if (blk_id != req_blk_id) { + return td::Status::Error("Liteserver responded with wrong block"); + } + auto R = vm::std_boc_deserialize(std::move(hdr->header_proof_)); + if (R.is_error()) { + return R.move_as_error_prefix("Couldn't deserialize header proof: "); + } else { + auto root = R.move_as_ok(); + try { + auto virt_root = vm::MerkleProof::virtualize(root, 1); + if (virt_root.is_null()) { + return td::Status::Error("Virt root is null"); + } else { + if (ton::RootHash{virt_root->get_hash().bits()} != blk_id.root_hash) { + return td::Status::Error("Block header merkle proof has incorrect root hash"); + } + std::vector prev; + ton::BlockIdExt mc_blkid; + bool after_split; + auto res = + block::unpack_block_prev_blk_ext(virt_root, blk_id, prev, mc_blkid, after_split); + if (res.is_error()) { + return td::Status::Error("Unpack failed"); + } else { + block::gen::Block::Record blk; + block::gen::BlockInfo::Record info; + if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(blk.info, info))) { + return td::Status::Error("Unpack failed"); + } else { + tonlib_api::blocks_header header; + header.id_ = to_tonlib_api(blk_id); + header.global_id_ = blk.global_id; + header.version_ = info.version; + header.flags_ = info.flags; + header.after_merge_ = info.after_merge; + header.after_split_ = info.after_split; + header.before_split_ = info.before_split; + header.want_merge_ = info.want_merge; + header.want_split_ = info.want_split; + header.validator_list_hash_short_ = info.gen_validator_list_hash_short; + header.catchain_seqno_ = info.gen_catchain_seqno; + header.min_ref_mc_seqno_ = info.min_ref_mc_seqno; + header.start_lt_ = info.start_lt; + header.end_lt_ = info.end_lt; + header.gen_utime_ = info.gen_utime; + header.is_key_block_ = info.key_block; + header.vert_seqno_ = info.vert_seq_no; + if (!info.not_master) { + header.prev_key_block_seqno_ = info.prev_key_block_seqno; + } + for (auto& id : prev) { + header.prev_blocks_.push_back(to_tonlib_api(id)); + } + return tonlib_api::make_object(std::move(header)); + } + } + } + } catch (vm::VmError& err) { + return err.as_status(PSLICE() << "error processing header for " << blk_id.to_str() << " :"); + } catch (vm::VmVirtError& err) { + return err.as_status(PSLICE() << "error processing header for " << blk_id.to_str() << " :"); + } catch (...) { + return td::Status::Error("Unhandled exception catched while processing header"); + } + } + })); return td::Status::OK(); } @@ -5755,18 +6027,16 @@ void TonlibClient::load_libs_from_disk() { if (r_dict.is_error()) { return; } - libraries = vm::Dictionary( - vm::load_cell_slice(vm::CellBuilder().append_cellslice(vm::load_cell_slice(r_dict.move_as_ok())).finalize()), - 256); - // int n = 0; for (auto&& lr : libraries) n++; + libraries = vm::Dictionary(vm::load_cell_slice(vm::CellBuilder().append_cellslice(vm::load_cell_slice( + r_dict.move_as_ok())).finalize()), 256); + LOG(DEBUG) << "loaded libraries from disk cache"; } void TonlibClient::store_libs_to_disk() { // NB: Dictionary.get_root_cell does not compute_root, and it is protected - kv_->set("tonlib.libcache", vm::std_boc_serialize(vm::CellBuilder().append_cellslice(libraries.get_root()).finalize()) - .move_as_ok() - .as_slice()); - // int n = 0; for (auto&& lr : libraries) n++; + kv_->set("tonlib.libcache", vm::std_boc_serialize(vm::CellBuilder().append_cellslice(libraries.get_root()) + .finalize()).move_as_ok().as_slice()); + LOG(DEBUG) << "stored libraries to disk cache"; } diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index aae96577..32731e4e 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -331,6 +331,7 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::smc_getLibraries& request, td::Promise>&& promise); + void get_libraries(ton::BlockIdExt blkid, std::vector library_list_, td::Promise>&& promise); td::Status do_request(const tonlib_api::smc_getLibrariesExt& request, td::Promise>&& promise); diff --git a/tonlib/tonlib/Wallet.cpp b/tonlib/tonlib/Wallet.cpp deleted file mode 100644 index b4682823..00000000 --- a/tonlib/tonlib/Wallet.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "tonlib/Wallet.h" -#include "tonlib/CellString.h" -#include "tonlib/GenericAccount.h" -#include "tonlib/utils.h" - -#include "vm/boc.h" -#include "td/utils/base64.h" - -#include - -namespace tonlib { -td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { - auto code = get_init_code(); - auto data = get_init_data(public_key); - return GenericAccount::get_init_state(std::move(code), std::move(data)); -} - -td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { - td::uint32 seqno = 0; - td::uint32 valid_until = std::numeric_limits::max(); - auto signature = - private_key - .sign(vm::CellBuilder().store_long(seqno, 32).store_long(valid_until, 32).finalize()->get_hash().as_slice()) - .move_as_ok(); - return vm::CellBuilder().store_bytes(signature).store_long(seqno, 32).store_long(valid_until, 32).finalize(); -} - -td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - td::uint32 valid_until, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept { - td::BigInt256 dest_addr; - dest_addr.import_bits(dest_address.addr.as_bitslice()); - vm::CellBuilder cb; - cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) - .store_long(dest_address.bounceable, 1) - .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) - .store_long(dest_address.workchain, 8) - .store_int256(dest_addr, 256); - td::int32 send_mode = 3; - if (gramms == -1) { - gramms = 0; - send_mode += 128; - } - block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); - vm::CellString::store(cb, message, 35 * 8).ensure(); - auto message_inner = cb.finalize(); - auto message_outer = vm::CellBuilder() - .store_long(seqno, 32) - .store_long(valid_until, 32) - .store_long(send_mode, 8) - .store_ref(message_inner) - .finalize(); - std::string seq_no(4, 0); - auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok(); - return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize(); -} - -td::Ref Wallet::get_init_code() noexcept { - static auto res = [] { - auto serialized_code = td::base64_decode( - "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" - "0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=") - .move_as_ok(); - return vm::std_boc_deserialize(serialized_code).move_as_ok(); - }(); - return res; -} - -vm::CellHash Wallet::get_init_code_hash() noexcept { - return get_init_code()->get_hash(); -} - -td::Ref Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { - return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); -} -} // namespace tonlib diff --git a/tonlib/tonlib/Wallet.h b/tonlib/tonlib/Wallet.h deleted file mode 100644 index dd114cce..00000000 --- a/tonlib/tonlib/Wallet.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of TON Blockchain Library. - - TON Blockchain Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - TON Blockchain Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with TON Blockchain Library. If not, see . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once - -#include "vm/cells.h" -#include "Ed25519.h" -#include "block/block.h" -#include "CellString.h" - -namespace tonlib { -class Wallet { - public: - static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; - static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - td::uint32 valid_until, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept; - - static td::Ref get_init_code() noexcept; - static vm::CellHash get_init_code_hash() noexcept; - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; -}; -} // namespace tonlib diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 43be90a4..400e6de2 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -1427,6 +1427,7 @@ td::Status ValidatorEngine::load_global_config() { validator_options_.write().set_celldb_compress_depth(celldb_compress_depth_); validator_options_.write().set_max_open_archive_files(max_open_archive_files_); validator_options_.write().set_archive_preload_period(archive_preload_period_); + validator_options_.write().set_disable_rocksdb_stats(disable_rocksdb_stats_); std::vector h; for (auto &x : conf.validator_->hardforks_) { @@ -4127,6 +4128,9 @@ int main(int argc, char *argv[]) { p.add_option('\0', "enable-precompiled-smc", "enable exectuion of precompiled contracts (experimental, disabled by default)", []() { block::precompiled::set_precompiled_execution_enabled(true); }); + p.add_option('\0', "disable-rocksdb-stats", "disable gathering rocksdb statistics (enabled by default)", [&]() { + acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_disable_rocksdb_stats, true); }); + }); auto S = p.run(argc, argv); if (S.is_error()) { LOG(ERROR) << "failed to parse options: " << S.move_as_error(); diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 0edf01c9..14a35bee 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -220,6 +220,7 @@ class ValidatorEngine : public td::actor::Actor { td::uint32 celldb_compress_depth_ = 0; size_t max_open_archive_files_ = 0; double archive_preload_period_ = 0.0; + bool disable_rocksdb_stats_ = false; bool read_config_ = false; bool started_keyring_ = false; bool started_ = false; @@ -289,6 +290,9 @@ class ValidatorEngine : public td::actor::Actor { void set_archive_preload_period(double value) { archive_preload_period_ = value; } + void set_disable_rocksdb_stats(bool value) { + disable_rocksdb_stats_ = value; + } void set_not_all_shards() { not_all_shards_ = true; } diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 2c0c82e5..d4d9e76c 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -601,7 +601,7 @@ void ArchiveManager::load_package(PackageId id) { } desc.file = - td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, archive_lru_.get()); + td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, archive_lru_.get(), statistics_); m.emplace(id, std::move(desc)); update_permanent_slices(); @@ -636,7 +636,7 @@ const ArchiveManager::FileDescription *ArchiveManager::add_file_desc(ShardIdFull td::mkdir(db_root_ + id.path()).ensure(); std::string prefix = PSTRING() << db_root_ << id.path() << id.name(); new_desc.file = - td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, archive_lru_.get()); + td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, archive_lru_.get(), statistics_); const FileDescription &desc = f.emplace(id, std::move(new_desc)); if (!id.temp) { update_desc(f, desc, shard, seqno, ts, lt); @@ -829,7 +829,10 @@ void ArchiveManager::start_up() { if (opts_->get_max_open_archive_files() > 0) { archive_lru_ = td::actor::create_actor("archive_lru", opts_->get_max_open_archive_files()); } - index_ = std::make_shared(td::RocksDb::open(db_root_ + "/files/globalindex").move_as_ok()); + if (!opts_->get_disable_rocksdb_stats()) { + statistics_ = td::RocksDb::create_statistics(); + } + index_ = std::make_shared(td::RocksDb::open(db_root_ + "/files/globalindex", statistics_).move_as_ok()); std::string value; auto v = index_->get(create_serialize_tl_object().as_slice(), value); v.ensure(); @@ -903,6 +906,28 @@ void ArchiveManager::start_up() { break; } } + + if (!opts_->get_disable_rocksdb_stats()) { + alarm_timestamp() = td::Timestamp::in(60.0); + } +} + +void ArchiveManager::alarm() { + alarm_timestamp() = td::Timestamp::in(60.0); + auto stats = td::RocksDb::statistics_to_string(statistics_); + auto to_file_r = td::FileFd::open(db_root_ + "/db_stats.txt", td::FileFd::Truncate | td::FileFd::Create | td::FileFd::Write, 0644); + if (to_file_r.is_error()) { + LOG(ERROR) << "Failed to open db_stats.txt: " << to_file_r.move_as_error(); + return; + } + auto to_file = to_file_r.move_as_ok(); + auto res = to_file.write(stats); + to_file.close(); + if (res.is_error()) { + LOG(ERROR) << "Failed to write to db_stats.txt: " << res.move_as_error(); + return; + } + td::RocksDb::reset_statistics(statistics_); } void ArchiveManager::run_gc(UnixTime mc_ts, UnixTime gc_ts, UnixTime archive_ttl) { diff --git a/validator/db/archive-manager.hpp b/validator/db/archive-manager.hpp index 1c5deaf8..aff76544 100644 --- a/validator/db/archive-manager.hpp +++ b/validator/db/archive-manager.hpp @@ -70,6 +70,7 @@ class ArchiveManager : public td::actor::Actor { td::Promise promise); void start_up() override; + void alarm() override; void commit_transaction(); void set_async_mode(bool mode, td::Promise promise); @@ -173,6 +174,8 @@ class ArchiveManager : public td::actor::Actor { bool huge_transaction_started_ = false; td::uint32 huge_transaction_size_ = 0; + std::shared_ptr statistics_; + FileMap &get_file_map(const PackageId &p) { return p.key ? key_files_ : p.temp ? temp_files_ : files_; } diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp index 52abc008..b38fbb7f 100644 --- a/validator/db/archive-slice.cpp +++ b/validator/db/archive-slice.cpp @@ -465,7 +465,7 @@ void ArchiveSlice::get_archive_id(BlockSeqno masterchain_seqno, td::Promise(td::RocksDb::open(db_path_).move_as_ok()); + kv_ = std::make_unique(td::RocksDb::open(db_path_, statistics_).move_as_ok()); std::string value; auto R2 = kv_->get("status", value); R2.ensure(); @@ -604,14 +604,15 @@ void ArchiveSlice::set_async_mode(bool mode, td::Promise promise) { } ArchiveSlice::ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, std::string db_root, - td::actor::ActorId archive_lru) + td::actor::ActorId archive_lru, std::shared_ptr statistics) : archive_id_(archive_id) , key_blocks_only_(key_blocks_only) , temp_(temp) , finalized_(finalized) , p_id_(archive_id_, key_blocks_only_, temp_) , db_root_(std::move(db_root)) - , archive_lru_(std::move(archive_lru)) { + , archive_lru_(std::move(archive_lru)) + , statistics_(statistics) { db_path_ = PSTRING() << db_root_ << p_id_.path() << p_id_.name() << ".index"; } diff --git a/validator/db/archive-slice.hpp b/validator/db/archive-slice.hpp index f178a9b8..a58df32b 100644 --- a/validator/db/archive-slice.hpp +++ b/validator/db/archive-slice.hpp @@ -23,6 +23,10 @@ #include "fileref.hpp" #include +namespace rocksdb { +class Statistics; +} + namespace ton { namespace validator { @@ -81,7 +85,7 @@ class ArchiveLru; class ArchiveSlice : public td::actor::Actor { public: ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, std::string db_root, - td::actor::ActorId archive_lru); + td::actor::ActorId archive_lru, std::shared_ptr statistics = nullptr); void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise); @@ -151,6 +155,7 @@ class ArchiveSlice : public td::actor::Actor { std::string db_root_; td::actor::ActorId archive_lru_; + std::shared_ptr statistics_; std::unique_ptr kv_; struct PackageInfo { diff --git a/validator/db/celldb.cpp b/validator/db/celldb.cpp index d29126ce..d9803cbe 100644 --- a/validator/db/celldb.cpp +++ b/validator/db/celldb.cpp @@ -20,6 +20,7 @@ #include "rootdb.hpp" #include "td/db/RocksDb.h" +#include "td/utils/filesystem.h" #include "ton/ton-tl.hpp" #include "ton/ton-io.hpp" @@ -83,7 +84,12 @@ void CellDbIn::start_up() { }; CellDbBase::start_up(); - cell_db_ = std::make_shared(td::RocksDb::open(path_).move_as_ok()); + if (!opts_->get_disable_rocksdb_stats()) { + statistics_ = td::RocksDb::create_statistics(); + statistics_flush_at_ = td::Timestamp::in(60.0); + } + cell_db_ = std::make_shared(td::RocksDb::open(path_, statistics_).move_as_ok()); + boc_ = vm::DynamicBagOfCellsDb::create(); boc_->set_celldb_compress_depth(opts_->get_celldb_compress_depth()); @@ -155,7 +161,29 @@ void CellDbIn::get_cell_db_reader(td::Promise> promise.set_result(boc_->get_cell_db_reader()); } +void CellDbIn::flush_db_stats() { + auto stats = td::RocksDb::statistics_to_string(statistics_); + auto to_file_r = td::FileFd::open(path_ + "/db_stats.txt", td::FileFd::Truncate | td::FileFd::Create | td::FileFd::Write, 0644); + if (to_file_r.is_error()) { + LOG(ERROR) << "Failed to open db_stats.txt: " << to_file_r.move_as_error(); + return; + } + auto to_file = to_file_r.move_as_ok(); + auto res = to_file.write(stats); + to_file.close(); + if (res.is_error()) { + LOG(ERROR) << "Failed to write to db_stats.txt: " << res.move_as_error(); + return; + } + td::RocksDb::reset_statistics(statistics_); +} + void CellDbIn::alarm() { + if (statistics_flush_at_ && statistics_flush_at_.is_in_past()) { + statistics_flush_at_ = td::Timestamp::in(60.0); + flush_db_stats(); + } + if (migrate_after_ && migrate_after_.is_in_past()) { migrate_cells(); } diff --git a/validator/db/celldb.hpp b/validator/db/celldb.hpp index a2a84ab4..573d4b99 100644 --- a/validator/db/celldb.hpp +++ b/validator/db/celldb.hpp @@ -27,6 +27,10 @@ #include "auto/tl/ton_api.h" #include "validator.h" +namespace rocksdb { +class Statistics; +} + namespace ton { namespace validator { @@ -56,6 +60,8 @@ class CellDbIn : public CellDbBase { void migrate_cell(td::Bits256 hash); + void flush_db_stats(); + CellDbIn(td::actor::ActorId root_db, td::actor::ActorId parent, std::string path, td::Ref opts); @@ -103,6 +109,8 @@ class CellDbIn : public CellDbBase { std::unique_ptr boc_; std::shared_ptr cell_db_; + std::shared_ptr statistics_; + td::Timestamp statistics_flush_at_ = td::Timestamp::never(); std::function on_load_callback_; std::set cells_to_migrate_; diff --git a/validator/impl/liteserver-cache.hpp b/validator/impl/liteserver-cache.hpp index 5318371f..20beca91 100644 --- a/validator/impl/liteserver-cache.hpp +++ b/validator/impl/liteserver-cache.hpp @@ -110,7 +110,7 @@ class LiteServerCacheImpl : public LiteServerCache { std::set send_message_cache_; size_t send_message_error_cnt_ = 0; - static const size_t MAX_CACHE_SIZE = 64 << 20; + static constexpr size_t MAX_CACHE_SIZE = 64 << 20; }; } // namespace ton::validator diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 9b8cb300..5d40b8c2 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -233,6 +233,9 @@ void LiteQuery::perform() { [&](lite_api::liteServer_lookupBlock& q) { this->perform_lookupBlock(ton::create_block_id_simple(q.id_), q.mode_, q.lt_, q.utime_); }, + [&](lite_api::liteServer_lookupBlockWithProof& q) { + this->perform_lookupBlockWithProof(ton::create_block_id_simple(q.id_), ton::create_block_id(q.mc_block_id_), q.mode_, q.lt_, q.utime_); + }, [&](lite_api::liteServer_listBlockTransactions& q) { this->perform_listBlockTransactions(ton::create_block_id(q.id_), q.mode_, q.count_, (q.mode_ & 128) ? q.after_->account_ : td::Bits256::zero(), @@ -266,6 +269,9 @@ void LiteQuery::perform() { [&](lite_api::liteServer_getLibraries& q) { this->perform_getLibraries(q.library_list_); }, + [&](lite_api::liteServer_getLibrariesWithProof& q) { + this->perform_getLibrariesWithProof(ton::create_block_id(q.id_), q.mode_, q.library_list_); + }, [&](lite_api::liteServer_getShardBlockProof& q) { this->perform_getShardBlockProof(create_block_id(q.id_)); }, @@ -964,6 +970,100 @@ void LiteQuery::continue_getLibraries(Ref mc_s finish_query(std::move(b)); } +void LiteQuery::perform_getLibrariesWithProof(BlockIdExt blkid, int mode, std::vector library_list) { + LOG(INFO) << "started a getLibrariesWithProof() liteserver query"; + if (library_list.size() > 16) { + LOG(INFO) << "too many libraries requested, returning only first 16"; + library_list.resize(16); + } + sort( library_list.begin(), library_list.end() ); + library_list.erase( unique( library_list.begin(), library_list.end() ), library_list.end() ); + + set_continuation([this, library_list, mode]() -> void { continue_getLibrariesWithProof(library_list, mode); }); + request_mc_block_data_state(blkid); +} + +void LiteQuery::continue_getLibrariesWithProof(std::vector library_list, int mode) { + LOG(INFO) << "obtained masterchain block = " << base_blk_id_.to_str(); + CHECK(mc_state_.not_null()); + + Ref state_proof, data_proof; + if (!make_mc_state_root_proof(state_proof)) { + return; + } + + vm::MerkleProofBuilder pb{mc_state_->root_cell()}; + block::gen::ShardStateUnsplit::Record state; + if (!tlb::unpack_cell(pb.root(), state)) { + fatal_error("cannot unpack header of shardchain state "s + base_blk_id_.to_str()); + } + auto libraries_dict = vm::Dictionary(state.r1.libraries->prefetch_ref(), 256); + + std::vector> result; + std::vector result_hashes; + for (const auto& hash : library_list) { + LOG(INFO) << "looking for library " << hash.to_hex(); + + auto csr = libraries_dict.lookup(hash.bits(), 256); + if (csr.is_null() || csr->prefetch_ulong(2) != 0 || !csr->have_refs()) { // shared_lib_descr$00 lib:^Cell + continue; + } + block::gen::LibDescr::Record libdescr; + if (!tlb::csr_unpack(csr, libdescr)) { + fatal_error("cannot unpack LibDescr record "s + hash.to_hex()); + return; + } + if (mode & 1) { + // include first 16 publishers in the proof + auto publishers_dict = vm::Dictionary{vm::DictNonEmpty(), libdescr.publishers, 256}; + auto iter = publishers_dict.begin(); + constexpr int max_publishers = 15; // set to 15 because publishers_dict.begin() counts as the first visit + for (int i = 0; i < max_publishers && iter != publishers_dict.end(); ++i, ++iter) {} + } + + result_hashes.push_back(hash); + } + + auto data_proof_boc = pb.extract_proof_boc(); + if (data_proof_boc.is_error()) { + fatal_error(data_proof_boc.move_as_error()); + return; + } + auto state_proof_boc = vm::std_boc_serialize(std::move(state_proof)); + if (state_proof_boc.is_error()) { + fatal_error(state_proof_boc.move_as_error()); + return; + } + + for (const auto& hash : result_hashes) { + auto csr = libraries_dict.lookup(hash.bits(), 256); + block::gen::LibDescr::Record libdescr; + if (!tlb::csr_unpack(csr, libdescr)) { + fatal_error("cannot unpack LibDescr record "s + hash.to_hex()); + return; + } + if (!libdescr.lib->get_hash().bits().equals(hash.bits(), 256)) { + LOG(ERROR) << "public library hash mismatch: expected " << hash.to_hex() << " , found " + << libdescr.lib->get_hash().to_hex(); + continue; + } + td::BufferSlice libdata; + if (!(mode & 2)) { + auto data = vm::std_boc_serialize(libdescr.lib); + if (data.is_error()) { + LOG(WARNING) << "library serialization failed: " << data.move_as_error().to_string(); + continue; + } + libdata = data.move_as_ok(); + } + result.push_back(ton::create_tl_object(hash, std::move(libdata))); + } + + auto b = ton::create_serialize_tl_object(ton::create_tl_lite_block_id(base_blk_id_), mode, std::move(result), + state_proof_boc.move_as_ok(), data_proof_boc.move_as_ok()); + finish_query(std::move(b)); +} + void LiteQuery::perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt) { LOG(INFO) << "started a getOneTransaction(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << "," << lt << ") liteserver query"; @@ -1333,8 +1433,8 @@ static td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::make_refint(lt), // trans_lt:Integer std::move(rand_seed_int), // rand_seed:Integer balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] - my_addr, // myself:MsgAddressInt - config ? config->get_root_cell() : vm::StackEntry() // global_config:(Maybe Cell) ] = SmartContractInfo; + my_addr, // myself:MsgAddressInt + config ? config->get_root_cell() : vm::StackEntry() // global_config:(Maybe Cell) ] = SmartContractInfo; }; if (config && config->get_global_version() >= 4) { tuple.push_back(my_code); // code:Cell @@ -1350,6 +1450,9 @@ static td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, if (config && config->get_global_version() >= 6) { tuple.push_back(config->get_unpacked_config_tuple(now)); // unpacked_config_tuple:[...] tuple.push_back(due_payment); // due_payment:Integer + // precomiled_gas_usage:(Maybe Integer) + auto precompiled = config->get_precompiled_contracts_config().get_contract(my_code->get_hash().bits()); + tuple.push_back(precompiled ? td::make_refint(precompiled.value().gas_usage) : vm::StackEntry()); } auto tuple_ref = td::make_cnt_ref>(std::move(tuple)); LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string(); @@ -1430,6 +1533,7 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice auto c7 = prepare_vm_c7(gen_utime, gen_lt, td::make_ref(acc.addr->clone()), balance, config.get(), std::move(code), due_payment); vm.set_c7(c7); // tuple with SmartContractInfo + vm.set_global_version(config->get_global_version()); // vm.incr_stack_trace(1); // enable stack dump after each step LOG(INFO) << "starting VM to run GET-method of smart contract " << acc_workchain_ << ":" << acc_addr_.to_hex(); // **** RUN VM **** @@ -1849,7 +1953,7 @@ void LiteQuery::continue_getConfigParams(int mode, std::vector param_list) void LiteQuery::perform_getAllShardsInfo(BlockIdExt blkid) { LOG(INFO) << "started a getAllShardsInfo(" << blkid.to_str() << ") liteserver query"; set_continuation([&]() -> void { continue_getAllShardsInfo(); }); - request_mc_block_data_state(blkid); + request_mc_block_data(blkid); } void LiteQuery::continue_getShardInfo(ShardIdFull shard, bool exact) { @@ -1896,30 +2000,30 @@ void LiteQuery::continue_getShardInfo(ShardIdFull shard, bool exact) { void LiteQuery::continue_getAllShardsInfo() { LOG(INFO) << "completing getAllShardsInfo() query"; - Ref proof1, proof2; - if (!make_mc_state_root_proof(proof1)) { + vm::MerkleProofBuilder mpb{mc_block_->root_cell()}; + block::gen::Block::Record blk; + block::gen::BlockExtra::Record extra; + block::gen::McBlockExtra::Record mc_extra; + if (!tlb::unpack_cell(mpb.root(), blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() || + !tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) { + fatal_error("cannot unpack header of block "s + mc_block_->block_id().to_str()); return; } - vm::MerkleProofBuilder mpb{mc_state_->root_cell()}; - auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(mpb.root()); - if (!shards_dict) { - fatal_error("cannot extract ShardHashes from last mc state"); - return; - } - if (!mpb.extract_proof_to(proof2)) { + vm::Dictionary shards_dict(std::move(mc_extra.shard_hashes), 32); + Ref proof; + if (!mpb.extract_proof_to(proof)) { fatal_error("cannot construct Merkle proof for all shards dictionary"); return; } - shards_dict = block::ShardConfig::extract_shard_hashes_dict(mc_state_->root_cell()); - vm::CellBuilder cb; - Ref cell; - if (!(std::move(shards_dict)->append_dict_to_bool(cb) && cb.finalize_to(cell))) { - fatal_error("cannot store ShardHashes from last mc state into a new cell"); + auto proof_boc = vm::std_boc_serialize(std::move(proof)); + if (proof_boc.is_error()) { + fatal_error(proof_boc.move_as_error()); return; } - auto proof = vm::std_boc_serialize_multi({std::move(proof1), std::move(proof2)}); - if (proof.is_error()) { - fatal_error(proof.move_as_error()); + vm::CellBuilder cb; + Ref cell; + if (!(shards_dict.append_dict_to_bool(cb) && cb.finalize_to(cell))) { + fatal_error("cannot store ShardHashes from last mc block into a new cell"); return; } auto data = vm::std_boc_serialize(std::move(cell)); @@ -1929,10 +2033,307 @@ void LiteQuery::continue_getAllShardsInfo() { } LOG(INFO) << "getAllShardInfo() query completed"; auto b = ton::create_serialize_tl_object( - ton::create_tl_lite_block_id(base_blk_id_), proof.move_as_ok(), data.move_as_ok()); + ton::create_tl_lite_block_id(base_blk_id_), proof_boc.move_as_ok(), data.move_as_ok()); finish_query(std::move(b)); } +void LiteQuery::perform_lookupBlockWithProof(BlockId blkid, BlockIdExt mc_blkid, int mode, LogicalTime lt, UnixTime utime) { + if (!((1 << (mode & 7)) & 0x16)) { + fatal_error("exactly one of mode.0, mode.1 and mode.2 bits must be set"); + return; + } + if (!mc_blkid.is_masterchain_ext()) { + fatal_error("masterchain block id must be specified"); + return; + } + if (!(mode & 1)) { + blkid.seqno = 0; + } + if (!(mode & 2)) { + lt = 0; + } + if (!(mode & 4)) { + utime = 0; + } + mode_ = mode; + base_blk_id_ = mc_blkid; + LOG(INFO) << "started a lookupBlockWithProof(" << blkid.to_str() << ", " << mc_blkid.to_str() << ", " << mode << ", " + << lt << ", " << utime << ") liteserver query"; + + ton::AccountIdPrefixFull pfx{blkid.workchain, blkid.shard}; + auto P = td::PromiseCreator::lambda( + [Self = actor_id(this), mc_blkid, manager = manager_, mode, pfx](td::Result res) { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + return; + } + auto handle = res.move_as_ok(); + if (!handle->inited_masterchain_ref_block()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, td::Status::Error("block doesn't have masterchain ref")); + return; + } + if (handle->masterchain_ref_block() > mc_blkid.seqno()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, td::Status::Error("specified mc block is older than block's masterchain ref")); + return; + } + LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); + td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, + [Self, mc_ref_blkid = handle->masterchain_ref_block(), mc_blkid, pfx, mode](td::Result> res) { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + } else { + td::actor::send_closure_later(Self, &LiteQuery::continue_lookupBlockWithProof_getHeaderProof, res.move_as_ok(), pfx, mc_ref_blkid); + } + }); + }); + + if (mode & 2) { + td::actor::send_closure_later(manager_, &ValidatorManager::get_block_by_lt_from_db_for_litequery, pfx, lt, std::move(P)); + } else if (mode & 4) { + td::actor::send_closure_later(manager_, &ValidatorManager::get_block_by_unix_time_from_db_for_litequery, pfx, utime, std::move(P)); + } else { + td::actor::send_closure_later(manager_, &ValidatorManager::get_block_by_seqno_from_db_for_litequery, pfx, blkid.seqno, std::move(P)); + } +} + +void LiteQuery::continue_lookupBlockWithProof_getHeaderProof(Ref block, AccountIdPrefixFull req_prefix, BlockSeqno masterchain_ref_seqno) { + blk_id_ = block->block_id(); + LOG(INFO) << "obtained data for getBlockHeader(" << blk_id_.to_str() << ", " << mode_ << ")"; + CHECK(block.not_null()); + auto block_root = block->root_cell(); + if (block_root.is_null()) { + fatal_error("block has no valid root cell"); + return; + } + + vm::MerkleProofBuilder mpb{block_root}; + std::vector prev; + BlockIdExt mc_blkid; + bool after_split; + td::Status S = block::unpack_block_prev_blk_try(mpb.root(), blk_id_, prev, mc_blkid, after_split); + if (S.is_error()) { + fatal_error(std::move(S)); + return; + } + auto proof_data = mpb.extract_proof_boc(); + if (proof_data.is_error()) { + fatal_error(proof_data.move_as_error()); + return; + } + lookup_header_proof_ = proof_data.move_as_ok(); + + bool include_prev = mode_ & 6; + if (include_prev) { + BlockIdExt prev_blkid; + for (auto& p : prev) { + if (ton::shard_contains(p.shard_full(), req_prefix)) { + prev_blkid = p; + } + } + CHECK(prev_blkid.is_valid()); + get_block_handle_checked(prev_blkid, [Self = actor_id(this), masterchain_ref_seqno, manager = manager_](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error()); + return; + } + td::actor::send_closure(manager, &ValidatorManager::get_block_data_from_db, R.move_as_ok(), + [Self, masterchain_ref_seqno](td::Result> res) mutable { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + return; + } + td::actor::send_closure(Self, &LiteQuery::continue_lookupBlockWithProof_gotPrevBlockData, res.move_as_ok(), masterchain_ref_seqno); + }); + }); + } else { + continue_lookupBlockWithProof_gotPrevBlockData(Ref(), masterchain_ref_seqno); + } +} + +void LiteQuery::continue_lookupBlockWithProof_gotPrevBlockData(Ref prev_block, BlockSeqno masterchain_ref_seqno) { + if (prev_block.not_null()) { + CHECK(prev_block.not_null()); + if (prev_block->root_cell().is_null()) { + fatal_error("block has no valid root cell"); + return; + } + vm::MerkleProofBuilder mpb{prev_block->root_cell()}; + block::gen::Block::Record blk; + block::gen::BlockInfo::Record info; + if (!(tlb::unpack_cell(mpb.root(), blk) && tlb::unpack_cell(blk.info, info))) { + fatal_error(td::Status::Error("cannot unpack prev block header")); + return; + } + auto proof_data = mpb.extract_proof_boc(); + if (proof_data.is_error()) { + fatal_error(proof_data.move_as_error()); + return; + } + lookup_prev_header_proof_ = proof_data.move_as_ok(); + } + + if (!blk_id_.is_masterchain()) { + ton::AccountIdPrefixFull pfx{ton::masterchainId, ton::shardIdAll}; + td::actor::send_closure_later(manager_, &ValidatorManager::get_block_by_seqno_from_db, pfx, masterchain_ref_seqno, + [manager = manager_, Self = actor_id(this)](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error()); + return; + } + td::actor::send_closure(manager, &ValidatorManager::get_block_data_from_db, R.move_as_ok(), + [Self](td::Result> res) mutable { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + return; + } + td::actor::send_closure(Self, &LiteQuery::continue_lookupBlockWithProof_buildProofLinks, res.move_as_ok(), std::vector>>()); + }); + }); + } else { + base_blk_id_alt_ = blk_id_; + td::actor::send_closure(actor_id(this), &LiteQuery::continue_lookupBlockWithProof_getClientMcBlockDataState, std::vector>>()); + } +} + +void LiteQuery::continue_lookupBlockWithProof_buildProofLinks(td::Ref cur_block, + std::vector>> result) { + BlockIdExt cur_id = cur_block->block_id(); + BlockIdExt prev_id; + vm::MerkleProofBuilder mpb{cur_block->root_cell()}; + if (cur_id.is_masterchain()) { + base_blk_id_alt_ = cur_id; + block::gen::Block::Record blk; + block::gen::BlockExtra::Record extra; + block::gen::McBlockExtra::Record mc_extra; + if (!tlb::unpack_cell(mpb.root(), blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() || + !tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) { + fatal_error("cannot unpack header of block "s + cur_id.to_str()); + return; + } + block::ShardConfig shards(mc_extra.shard_hashes->prefetch_ref()); + ShardIdFull shard_id = blk_id_.shard_full(); + shard_id.shard = (shard_id.shard & ~(1 << (63 - shard_id.pfx_len()))) | 1; + Ref shard_hash = shards.get_shard_hash(shard_id, false); + if (shard_hash.is_null()) { + fatal_error("shard not found"); + return; + } + prev_id = shard_hash->top_block_id(); + } else { + std::vector prev; + BlockIdExt mc_blkid; + bool after_split; + td::Status S = block::unpack_block_prev_blk_try(mpb.root(), cur_id, prev, mc_blkid, after_split); + if (S.is_error()) { + fatal_error(std::move(S)); + return; + } + bool found = false; + for (const BlockIdExt& id : prev) { + if (shard_intersects(id.shard_full(), blk_id_.shard_full())) { + found = true; + prev_id = id; + break; + } + } + if (!found) { + fatal_error("failed to find block chain"); + return; + } + } + auto proof = mpb.extract_proof(); + if (proof.is_error()) { + fatal_error(proof.move_as_error_prefix("cannot serialize Merkle proof : ")); + return; + } + result.emplace_back(prev_id, proof.move_as_ok()); + + if (prev_id == blk_id_) { + CHECK(base_blk_id_alt_.is_masterchain()); + if (base_blk_id_alt_ != base_blk_id_) { + continue_lookupBlockWithProof_getClientMcBlockDataState(std::move(result)); + } else { + continue_lookupBlockWithProof_getMcBlockPrev(std::move(result)); + } + return; + } + if (result.size() == 8) { + // Chains of shardblocks between masterchain blocks can't be longer than 8 (see collator.cpp:991) + fatal_error("proof chain is too long"); + return; + } + + td::actor::send_closure_later( + manager_, &ValidatorManager::get_block_data_from_db_short, prev_id, + [Self = actor_id(this), result = std::move(result)](td::Result> R) mutable { + if (R.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error()); + } else { + td::actor::send_closure_later(Self, &LiteQuery::continue_lookupBlockWithProof_buildProofLinks, R.move_as_ok(), + std::move(result)); + } + }); +} + +void LiteQuery::continue_lookupBlockWithProof_getClientMcBlockDataState(std::vector>> links) { + set_continuation([this, links = std::move(links)]() -> void { + continue_lookupBlockWithProof_getMcBlockPrev(std::move(links)); + }); + request_mc_block_data_state(base_blk_id_); +} + +void LiteQuery::continue_lookupBlockWithProof_getMcBlockPrev(std::vector>> links) { + td::BufferSlice mc_state_proof_buf, client_mc_blk_proof_buf; + + if (base_blk_id_alt_ != base_blk_id_) { + vm::MerkleProofBuilder mpb{mc_state_->root_cell()}; + auto prev_blocks_dict = block::get_prev_blocks_dict(mpb.root()); + if (!prev_blocks_dict) { + fatal_error(td::Status::Error("cannot extract prev_blocks from mc state")); + return; + } + if (!block::check_old_mc_block_id(*prev_blocks_dict, base_blk_id_alt_)) { + fatal_error(td::Status::Error("client mc blkid is not in prev_blocks")); + return; + } + auto client_mc_blk_proof = mpb.extract_proof_boc(); + if (client_mc_blk_proof.is_error()) { + fatal_error(client_mc_blk_proof.move_as_error()); + return; + } + client_mc_blk_proof_buf = client_mc_blk_proof.move_as_ok(); + + Ref mc_state_proof; + if (!make_mc_state_root_proof(mc_state_proof)) { + fatal_error(td::Status::Error("cannot create Merkle proof for mc state")); + return; + } + auto mc_state_proof_boc = vm::std_boc_serialize(std::move(mc_state_proof)); + if (mc_state_proof_boc.is_error()) { + fatal_error(mc_state_proof_boc.move_as_error()); + return; + } + mc_state_proof_buf = mc_state_proof_boc.move_as_ok(); + } + + std::vector> links_res; + for (auto& p : links) { + auto prev_block_proof = vm::std_boc_serialize(std::move(p.second)); + if (prev_block_proof.is_error()) { + fatal_error(prev_block_proof.move_as_error()); + return; + } + links_res.push_back( + create_tl_object(create_tl_lite_block_id(p.first), prev_block_proof.move_as_ok())); + } + + auto b = ton::create_serialize_tl_object(ton::create_tl_lite_block_id(blk_id_), + mode_, ton::create_tl_lite_block_id(base_blk_id_alt_), std::move(mc_state_proof_buf), std::move(client_mc_blk_proof_buf), + std::move(links_res), std::move(lookup_header_proof_), std::move(lookup_prev_header_proof_)); + finish_query(std::move(b)); +} + + void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, UnixTime utime) { if (!((1 << (mode & 7)) & 0x16)) { fatal_error("exactly one of mode.0, mode.1 and mode.2 bits must be set"); diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 145fff3d..931dd6f6 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -69,6 +69,9 @@ class LiteQuery : public td::actor::Actor { std::unique_ptr chain_; Ref stack_; + td::BufferSlice lookup_header_proof_; + td::BufferSlice lookup_prev_header_proof_; + public: enum { default_timeout_msec = 4500, // 4.5 seconds @@ -124,6 +127,8 @@ class LiteQuery : public td::actor::Actor { UnixTime gen_utime, LogicalTime gen_lt); void perform_getLibraries(std::vector library_list); void continue_getLibraries(Ref mc_state, BlockIdExt blkid, std::vector library_list); + void perform_getLibrariesWithProof(BlockIdExt blkid, int mode, std::vector library_list); + void continue_getLibrariesWithProof(std::vector library_list, int mode); void perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt); void continue_getOneTransaction(); void perform_getTransactions(WorkchainId workchain, StdSmcAddress addr, LogicalTime lt, Bits256 hash, unsigned count); @@ -138,6 +143,12 @@ class LiteQuery : public td::actor::Actor { void perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list = {}); void continue_getConfigParams(int mode, std::vector param_list); void perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, UnixTime utime); + void perform_lookupBlockWithProof(BlockId blkid, BlockIdExt client_mc_blkid, int mode, LogicalTime lt, UnixTime utime); + void continue_lookupBlockWithProof_getHeaderProof(Ref block, AccountIdPrefixFull req_prefix, BlockSeqno masterchain_ref_seqno); + void continue_lookupBlockWithProof_gotPrevBlockData(Ref prev_block, BlockSeqno masterchain_ref_seqno); + void continue_lookupBlockWithProof_buildProofLinks(td::Ref cur_block, std::vector>> result); + void continue_lookupBlockWithProof_getClientMcBlockDataState(std::vector>> links); + void continue_lookupBlockWithProof_getMcBlockPrev(std::vector>> links); void perform_listBlockTransactions(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt); void finish_listBlockTransactions(int mode, int count); void perform_listBlockTransactionsExt(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt); diff --git a/validator/state-serializer.cpp b/validator/state-serializer.cpp index 9ac2cf24..2c239bf9 100644 --- a/validator/state-serializer.cpp +++ b/validator/state-serializer.cpp @@ -314,7 +314,8 @@ bool AsyncStateSerializer::need_serialize(BlockHandle handle) { if (handle->id().id.seqno == 0 || !handle->is_key_block()) { return false; } - return ValidatorManager::is_persistent_state(handle->unix_time(), last_key_block_ts_); + return ValidatorManager::is_persistent_state(handle->unix_time(), last_key_block_ts_) && + ValidatorManager::persistent_state_ttl(handle->unix_time()) > (UnixTime)td::Clocks::system(); } } // namespace validator diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index a2a18baa..df4bb525 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -121,6 +121,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { double get_archive_preload_period() const override { return archive_preload_period_; } + bool get_disable_rocksdb_stats() const override { + return disable_rocksdb_stats_; + } ValidatorMode validator_mode() const override { return validator_mode_; } @@ -186,6 +189,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { void set_archive_preload_period(double value) override { archive_preload_period_ = value; } + void set_disable_rocksdb_stats(bool value) override { + disable_rocksdb_stats_ = value; + } void set_validator_mode(ValidatorMode value) override { validator_mode_ = value; } @@ -232,6 +238,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { td::uint32 celldb_compress_depth_{0}; size_t max_open_archive_files_ = 0; double archive_preload_period_ = 0.0; + bool disable_rocksdb_stats_; ValidatorMode validator_mode_ = validator_normal; }; diff --git a/validator/validator.h b/validator/validator.h index 49c5d65b..5c6de83d 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -84,6 +84,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual td::uint32 get_celldb_compress_depth() const = 0; virtual size_t get_max_open_archive_files() const = 0; virtual double get_archive_preload_period() const = 0; + virtual bool get_disable_rocksdb_stats() const = 0; virtual ValidatorMode validator_mode() const = 0; virtual void set_zero_block_id(BlockIdExt block_id) = 0; @@ -106,6 +107,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual void set_celldb_compress_depth(td::uint32 value) = 0; virtual void set_max_open_archive_files(size_t value) = 0; virtual void set_archive_preload_period(double value) = 0; + virtual void set_disable_rocksdb_stats(bool value) = 0; virtual void set_validator_mode(ValidatorMode value) = 0; static td::Ref create(