diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 8b8f147a..22c05fba 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -350,6 +350,7 @@ if (NOT CMAKE_CROSSCOMPILING) GenFif(DEST smartcont/auto/config-code SOURCE smartcont/config-code.fc NAME config) GenFif(DEST smartcont/auto/wallet-code SOURCE smartcont/wallet-code.fc NAME wallet) + GenFif(DEST smartcont/auto/wallet3-code SOURCE smartcont/wallet3-code.fc NAME wallet3) GenFif(DEST smartcont/auto/simple-wallet-code SOURCE smartcont/simple-wallet-code.fc NAME simple-wallet) GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet) GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2) @@ -364,7 +365,9 @@ endif() add_library(smc-envelope ${SMC_ENVELOPE_SOURCE}) target_include_directories(smc-envelope PUBLIC $) target_link_libraries(smc-envelope PUBLIC ton_crypto PRIVATE tdutils ton_block) -add_dependencies(smc-envelope gen_fif) +if (NOT CMAKE_CROSSCOMPILING) + add_dependencies(smc-envelope gen_fif) +endif() add_executable(create-state block/create-state.cpp) target_include_directories(create-state PUBLIC $ diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 80e399ee..b5959f75 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -534,19 +534,16 @@ td::Result> Config::get_storage_prices() const { return std::move(res); } -td::Result Config::get_gas_limits_prices(bool is_masterchain) const { +td::Result Config::do_get_gas_limits_prices(td::Ref cell, int id) { GasLimitsPrices res; - auto id = is_masterchain ? 20 : 21; - auto cell = get_config_param(id); - if (cell.is_null()) { - return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent"); - } - auto cs = vm::load_cell_slice(std::move(cell)); + auto cs = vm::load_cell_slice(cell); block::gen::GasLimitsPrices::Record_gas_flat_pfx flat; if (tlb::unpack(cs, flat)) { cs = *flat.other; res.flat_gas_limit = flat.flat_gas_limit; res.flat_gas_price = flat.flat_gas_price; + } else { + cs = vm::load_cell_slice(cell); } auto f = [&](const auto& r, td::uint64 spec_limit) { res.gas_limit = r.gas_limit; @@ -570,6 +567,14 @@ td::Result Config::get_gas_limits_prices(bool is_masterchain) c } return res; } +td::Result Config::get_gas_limits_prices(bool is_masterchain) const { + auto id = is_masterchain ? 20 : 21; + auto cell = get_config_param(id); + if (cell.is_null()) { + return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent"); + } + return do_get_gas_limits_prices(std::move(cell), id); +} td::Result Config::get_msg_prices(bool is_masterchain) const { auto id = is_masterchain ? 24 : 25; diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 018a47ad..241ae88c 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -540,6 +540,7 @@ class Config { static td::Result> unpack_validator_set(Ref valset_root); td::Result> get_storage_prices() const; td::Result get_gas_limits_prices(bool is_masterchain = false) const; + static td::Result do_get_gas_limits_prices(td::Ref cell, int id); td::Result get_msg_prices(bool is_masterchain = false) const; static CatchainValidatorsConfig unpack_catchain_validators_config(Ref cell); CatchainValidatorsConfig get_catchain_validators_config() const; diff --git a/crypto/parser/symtable.cpp b/crypto/parser/symtable.cpp index 157b4a2b..939bb29c 100644 --- a/crypto/parser/symtable.cpp +++ b/crypto/parser/symtable.cpp @@ -68,7 +68,7 @@ sym_idx_t SymTableBase::gen_lookup(std::string str, int mode, sym_idx_t idx) { if (!(mode & 1)) { return not_found; } - if (def_sym >= ((long)p * 3) / 4) { + if (def_sym >= ((long long)p * 3) / 4) { throw SymTableOverflow{def_sym}; } sym_table[h1] = std::make_unique(str, idx <= 0 ? sym_idx_t(h1) : -idx); diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index a49ad137..3a897d4c 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -163,6 +163,14 @@ slice create_new_entry(cs) inline { return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse(); } +(cell, int, int, slice) unpack_suggestion(slice cs) inline { + return (cs~load_dict(), cs~load_uint(64), cs~load_uint(256), cs); +} + +builder pack_suggestion(cell voters, int sum_weight, int vset_id, slice body) inline { + return begin_cell().store_dict(voters).store_uint(sum_weight, 64).store_uint(vset_id, 256).store_slice(body); +} + cell register_vote(vote_dict, action, cs, idx, weight) { int hash = 0; var entry = null(); diff --git a/crypto/smartcont/new-wallet-v3.fif b/crypto/smartcont/new-wallet-v3.fif new file mode 100644 index 00000000..187519c9 --- /dev/null +++ b/crypto/smartcont/new-wallet-v3.fif @@ -0,0 +1,63 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new advanced wallet with unique 32-bit identifier in specified workchain, with private key saved to or loaded from .pk" cr + ."('new-wallet.pk' by default)" cr 1 halt +} : usage +$# 2- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +$2 parse-int =: subwallet-id +def? $3 { @' $3 } { "new-wallet" } cond constant file-base + +."Creating new advanced v3 wallet in workchain " wc . cr +."with unique wallet id " subwallet-id . cr + +// Create new advanced wallet; code adapted from `auto/wallet3-code.fif` +<{ SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method + DROP c4 PUSHCTR CTOS 32 PLDU // cnt + }> + INC 32 THROWIF // fail unless recv_external + 9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU 32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs + NOW s1 s3 XCHG LEQ 35 THROWIF // signature in_msg subwallet_id cs msg_seqno + c4 PUSH CTOS 32 LDU 32 LDU 256 LDU ENDS // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key + s3 s2 XCPU EQUAL 33 THROWIFNOT // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet + s4 s4 XCPU EQUAL 34 THROWIFNOT // signature in_msg stored_subwallet cs public_key stored_seqno + s0 s4 XCHG HASHSU // signature stored_seqno stored_subwallet cs public_key msg_hash + s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs msg_hash signature public_key + CHKSIGNU 35 THROWIFNOT // public_key stored_seqno stored_subwallet cs + ACCEPT + WHILE:<{ + DUP SREFS // public_key stored_seqno stored_subwallet cs _51 + }>DO<{ // public_key stored_seqno stored_subwallet cs + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno stored_subwallet cs _56 mode + SENDRAWMSG + }> // public_key stored_seqno stored_subwallet cs + ENDS SWAP INC // public_key stored_subwallet seqno' + NEWC 32 STU 32 STU 256 STU ENDC c4 POP +}>c // >libref +// code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/wallet3-code.fc b/crypto/smartcont/wallet3-code.fc new file mode 100644 index 00000000..a52cb1ec --- /dev/null +++ b/crypto/smartcont/wallet3-code.fc @@ -0,0 +1,35 @@ +;; Simple wallet smart contract + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); + throw_if(35, valid_until <= now()); + var ds = get_data().begin_parse(); + var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); + ds.end_parse(); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); + cs~touch(); + while (cs.slice_refs()) { + var mode = cs~load_uint(8); + send_raw_message(cs~load_ref(), mode); + } + set_data(begin_cell() + .store_uint(stored_seqno + 1, 32) + .store_uint(stored_subwallet, 32) + .store_uint(public_key, 256) + .end_cell()); +} + +;; Get methods + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index 5ab0620c..bbf3adf7 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -1021,9 +1021,9 @@ void CppTypeCode::generate_tag_pfx_selector(std::ostream& os, std::string nl, co } os << "};" << nl << "return ctab[1 + "; if (simple) { - os << "(long)cs.prefetch_ulong(" << d << ")];"; + os << "(long long)cs.prefetch_ulong(" << d << ")];"; } else { - os << "(long)cs.bselect" << (d >= min_size ? "(" : "_ext(") << d << ", " << HexConstWriter{mask} << ")];"; + os << "(long long)cs.bselect" << (d >= min_size ? "(" : "_ext(") << d << ", " << HexConstWriter{mask} << ")];"; } } @@ -1153,7 +1153,7 @@ void CppTypeCode::generate_get_tag_body(std::ostream& os, std::string nl) { os << ")) {"; for (int i = 0; i < l; i++) { if (A[i] != 0) { - if ((long)A[i] > 0) { + if ((long long)A[i] > 0) { int j; for (j = 0; j < i; j++) { if (A[j] == A[i]) { @@ -1165,7 +1165,7 @@ void CppTypeCode::generate_get_tag_body(std::ostream& os, std::string nl) { } } os << nl << "case " << i << ":"; - if ((long)A[i] > 0) { + if ((long long)A[i] > 0) { int j; for (j = i + 1; j < l; j++) { if (A[j] == A[i]) { diff --git a/crypto/tl/tlbc.cpp b/crypto/tl/tlbc.cpp index 5d3d0fe4..ff2e19e6 100644 --- a/crypto/tl/tlbc.cpp +++ b/crypto/tl/tlbc.cpp @@ -687,7 +687,7 @@ unsigned long long BinTrie::build_submap(int depth, unsigned long long A[]) cons } else { std::memset(A + n, 0, n * 8); } - if (A[n] != A[n - 1] || (long)A[n] < 0) { + if (A[n] != A[n - 1] || (long long)A[n] < 0) { r2 |= 1; } else { r2 &= ~1; diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index c87bd4d9..0b16f59c 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -33,6 +33,8 @@ #include "vm/cellslice.h" #include "vm/excno.hpp" +#include "td/utils/Span.h" + namespace td { extern template class td::Cnt; extern template class td::Ref>; @@ -156,7 +158,7 @@ class StackEntry { private: template - Ref dynamic_as() const & { + Ref dynamic_as() const& { return tp == tag ? static_cast>(ref) : td::Ref{}; } template @@ -168,7 +170,7 @@ class StackEntry { return tp == tag ? static_cast>(std::move(ref)) : td::Ref{}; } template - Ref as() const & { + Ref as() const& { return tp == tag ? Ref{td::static_cast_ref(), ref} : td::Ref{}; } template @@ -189,31 +191,31 @@ class StackEntry { return ref; } } - td::RefInt256 as_int() const & { + td::RefInt256 as_int() const& { return as(); } td::RefInt256 as_int() && { return move_as(); } - Ref as_cell() const & { + Ref as_cell() const& { return as(); } Ref as_cell() && { return move_as(); } - Ref as_builder() const & { + Ref as_builder() const& { return as(); } Ref as_builder() && { return move_as(); } - Ref as_slice() const & { + Ref as_slice() const& { return as(); } Ref as_slice() && { return move_as(); } - Ref as_cont() const &; + Ref as_cont() const&; Ref as_cont() &&; Ref> as_string_ref() const { return as, t_string>(); @@ -228,16 +230,16 @@ class StackEntry { std::string as_bytes() const { return tp == t_bytes ? *as_bytes_ref() : ""; } - Ref as_box() const &; + Ref as_box() const&; Ref as_box() &&; - Ref as_tuple() const &; + Ref as_tuple() const&; Ref as_tuple() &&; - Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const &; + Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const&; Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) &&; - Ref as_atom() const &; + Ref as_atom() const&; Ref as_atom() &&; template - Ref as_object() const & { + Ref as_object() const& { return dynamic_as(); } template @@ -361,6 +363,9 @@ class Stack : public td::CntObject { std::vector::const_iterator from_top(int offs) const { return stack.cend() - offs; } + td::Span as_span() const { + return stack; + } bool at_least(int req) const { return depth() >= req; } diff --git a/tdutils/td/utils/JsonBuilder.h b/tdutils/td/utils/JsonBuilder.h index f2df4e60..be089ca6 100644 --- a/tdutils/td/utils/JsonBuilder.h +++ b/tdutils/td/utils/JsonBuilder.h @@ -193,7 +193,7 @@ class JsonObjectScope; class JsonBuilder { public: - explicit JsonBuilder(StringBuilder &&sb, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) { + explicit JsonBuilder(StringBuilder &&sb = {}, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) { } StringBuilder &string_builder() { return sb_; diff --git a/tdutils/td/utils/StringBuilder.h b/tdutils/td/utils/StringBuilder.h index 7415aa2a..229b967e 100644 --- a/tdutils/td/utils/StringBuilder.h +++ b/tdutils/td/utils/StringBuilder.h @@ -31,6 +31,8 @@ namespace td { class StringBuilder { public: explicit StringBuilder(MutableSlice slice, bool use_buffer = false); + StringBuilder() : StringBuilder({}, true) { + } void clear() { current_ptr_ = begin_ptr_; diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 2a3f0f52..01ab5c62 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -73,8 +73,22 @@ fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53= Fees; query.fees source_fees:fees destination_fees:fees = query.Fees; query.info id:int53 valid_until:int53 body_hash:bytes = query.Info; +tvm.slice bytes:string = tvm.Slice; +tvm.cell bytes:string = tvm.Cell; +tvm.numberDecimal number:string = tvm.Number; + +tvm.stackEntrySlice slice:tvm.slice = tvm.StackEntry; +tvm.stackEntryCell cell:tvm.cell = tvm.StackEntry; +tvm.stackEntryNumber number:tvm.Number = tvm.StackEntry; +tvm.stackEntryUnsupported = tvm.StackEntry; + smc.info id:int53 = smc.Info; +smc.methodIdNumber number:int32 = smc.MethodId; +smc.methodIdName name:string = smc.MethodId; + +smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; + updateSendLiteServerQuery id:int64 data:bytes = Update; updateSyncState sync_state:SyncState = Update; @@ -98,8 +112,6 @@ logTags tags:vector = LogTags; data bytes:secureBytes = Data; -tvm.cell bytes:string = tvm.Cell; - liteServer.info now:int53 version:int32 capabilities:int64 = liteServer.Info; ---functions--- @@ -108,6 +120,7 @@ init options:options = Ok; close = Ok; options.setConfig config:config = Ok; +options.validateConfig config:config = Ok; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; deleteKey key:key = Ok; @@ -126,7 +139,6 @@ kdf password:secureBytes salt:secureBytes iterations:int32 = Data; unpackAccountAddress account_address:string = UnpackedAccountAddress; packAccountAddress account_address:unpackedAccountAddress = AccountAddress; - getBip39Hints prefix:string = Bip39Hints; //raw.init initial_account_state:raw.initialAccountState = Ok; @@ -168,6 +180,7 @@ smc.load account_address:accountAddress = smc.Info; smc.getCode id:int53 = tvm.Cell; smc.getData id:int53 = tvm.Cell; smc.getState id:int53 = tvm.Cell; +smc.runGetMethod id:int53 method:smc.MethodId stack:vector = smc.RunResult; onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryError id:int64 error:error = Ok; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index d7c0e049..0e727228 100644 Binary files a/tl/generate/scheme/tonlib_api.tlo and b/tl/generate/scheme/tonlib_api.tlo differ diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 53d08bdb..c615397e 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -19,6 +19,7 @@ #include "block/block.h" #include "block/block-auto.h" +#include "block/mc-config.h" #include "vm/cells.h" #include "vm/boc.h" @@ -246,6 +247,17 @@ TEST(Tonlib, ParseAddres) { ASSERT_EQ("Uf9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfdyS", addr_str2->account_address_); } +TEST(Tonlib, ConfigParseBug) { + td::Slice literal = + "D1000000000000006400000000000186A0DE0000000003E8000000000000000F424000000000000F42400000000000002710000000000098" + "96800000000005F5E100000000003B9ACA00"; + unsigned char buff[128]; + int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), literal.begin(), literal.end()); + CHECK(bits >= 0); + auto slice = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize(); + block::Config::do_get_gas_limits_prices(std::move(slice), 21).ensure(); +} + TEST(Tonlib, EncryptionApi) { using tonlib_api::make_object; Client client; diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 08a3c36a..c96fc75e 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -462,29 +462,31 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { transfer_grams(client, giver_wallet, address, 1 * Gramm).ensure(); auto init_state = ms->get_init_state(); - // Just transfer all (some) money back in one query - vm::CellBuilder icb; - ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), - 5 * Gramm / 10); - icb.store_bytes("\0\0\0\0", 4); - vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); - ton::MultisigWallet::QueryBuilder qb(-1, icb.finalize()); - for (int i = 0; i < k - 1; i++) { - qb.sign(i, private_keys[i]); + for (int i = 0; i < 2; i++) { + // Just transfer all (some) money back in one query + vm::CellBuilder icb; + ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), 1); + icb.store_bytes("\0\0\0\0", 4); + vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); + ton::MultisigWallet::QueryBuilder qb(-1 - i, icb.finalize()); + for (int i = 0; i < k - 1; i++) { + qb.sign(i, private_keys[i]); + } + + auto query_id = + create_raw_query(client, address, + i == 0 ? vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str() : "", + i == 0 ? vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str() : "", + vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str()) + .move_as_ok(); + auto fees = query_estimate_fees(client, query_id); + + LOG(INFO) << "Expected src fees: " << fees.first; + LOG(INFO) << "Expected dst fees: " << fees.second; + auto a_state = get_account_state(client, address); + query_send(client, query_id); + auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok(); } - - auto query_id = - create_raw_query(client, address, vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str(), - vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str(), - vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str()) - .move_as_ok(); - auto fees = query_estimate_fees(client, query_id); - - LOG(INFO) << "Expected src fees: " << fees.first; - LOG(INFO) << "Expected dst fees: " << fees.second; - auto a_state = get_account_state(client, address); - query_send(client, query_id); - auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok(); } int main(int argc, char* argv[]) { @@ -515,8 +517,8 @@ int main(int argc, char* argv[]) { if (reset_keystore_dir) { td::rmrf(keystore_dir).ignore(); - td::mkdir(keystore_dir).ensure(); } + td::mkdir(keystore_dir).ensure(); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO)); static_send(make_object("tonlib_query", 4)).ensure(); diff --git a/tonlib/tonlib/ExtClientOutbound.cpp b/tonlib/tonlib/ExtClientOutbound.cpp index f1746af5..ee6ddc4f 100644 --- a/tonlib/tonlib/ExtClientOutbound.cpp +++ b/tonlib/tonlib/ExtClientOutbound.cpp @@ -42,6 +42,7 @@ class ExtClientOutboundImp : public ExtClientOutbound { auto it = queries_.find(id); if (it == queries_.end()) { promise.set_error(TonlibError::Internal("Unknown query id")); + return; } it->second.set_result(std::move(r_data)); queries_.erase(it); diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index 234af997..a2f94787 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -25,6 +25,8 @@ #include "lite-client/lite-client-common.h" +#include "td/utils/JsonBuilder.h" + namespace tonlib { // init_state <-> last_key_block @@ -313,6 +315,18 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) { if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) { state_.last_key_block_id = mc_key_block_id; VLOG(last_block) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); + if (true) { + td::JsonBuilder jb; + auto jo = jb.enter_object(); + jo("workchain", state_.last_key_block_id.id.workchain); + jo("shard", static_cast(state_.last_key_block_id.id.shard)); + jo("seqno", static_cast(state_.last_key_block_id.id.seqno)); + jo("root_hash", td::base64_encode(as_slice(state_.last_key_block_id.root_hash))); + jo("file_hash", td::base64_encode(as_slice(state_.last_key_block_id.file_hash))); + jo.leave(); + LOG(INFO) << jb.string_builder().as_cslice(); + } + //LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " " //<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " " //<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice()); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index edd8d6e7..74483fa3 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -798,18 +798,22 @@ void TonlibClient::init_ext_client() { if (use_callbacks_for_network_) { class Callback : public ExtClientOutbound::Callback { public: - explicit Callback(td::actor::ActorShared parent) : parent_(std::move(parent)) { + explicit Callback(td::actor::ActorShared parent, td::uint32 config_generation) + : parent_(std::move(parent)), config_generation_(config_generation) { } void request(td::int64 id, std::string data) override { - send_closure(parent_, &TonlibClient::proxy_request, id, std::move(data)); + send_closure(parent_, &TonlibClient::proxy_request, (id << 16) | (config_generation_ & 0xffff), + std::move(data)); } private: td::actor::ActorShared parent_; + td::uint32 config_generation_; }; ref_cnt_++; - auto client = ExtClientOutbound::create(td::make_unique(td::actor::actor_shared(this))); + auto client = + ExtClientOutbound::create(td::make_unique(td::actor::actor_shared(this), config_generation_)); ext_client_outbound_ = client.get(); raw_client_ = std::move(client); } else { @@ -836,6 +840,7 @@ void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 conf if (config_generation != config_generation_) { return; } + last_block_storage_.save_state(blockchain_name_, state); } @@ -858,7 +863,7 @@ void TonlibClient::update_sync_state(LastBlockSyncState state, td::uint32 config } } -void TonlibClient::init_last_block() { +void TonlibClient::init_last_block(td::optional o_master_config) { ref_cnt_++; class Callback : public LastBlock::Callback { public: @@ -893,6 +898,15 @@ void TonlibClient::init_last_block() { state = r_state.move_as_ok(); } + if (o_master_config) { + auto master_config = o_master_config.unwrap(); + if (master_config.init_block_id.is_valid() && + state.last_key_block_id.id.seqno < master_config.init_block_id.id.seqno) { + state.last_key_block_id = master_config.init_block_id; + LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str(); + } + } + raw_last_block_ = td::actor::create_actor( td::actor::ActorOptions().with_name("LastBlock").with_poll(false), get_client_ref(), std::move(state), config_, source_.get_cancellation_token(), td::make_unique(td::actor::actor_shared(this), config_generation_)); @@ -988,6 +1002,7 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::testGiver_getAccountAddress::ID: case tonlib_api::packAccountAddress::ID: case tonlib_api::unpackAccountAddress::ID: + case tonlib_api::options_validateConfig::ID: case tonlib_api::getBip39Hints::ID: case tonlib_api::setLogStream::ID: case tonlib_api::getLogStream::ID: @@ -1142,37 +1157,112 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, key_storage_.set_key_value(kv_); last_block_storage_.set_key_value(kv_); if (request.options_->config_) { - TRY_STATUS(set_config(std::move(request.options_->config_))); + TRY_RESULT(full_config, validate_config(std::move(request.options_->config_))); + set_config(std::move(full_config)); } state_ = State::Running; promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } -td::Status TonlibClient::set_config(object_ptr config) { - CHECK(config); +class MasterConfig { + public: + void add_config(std::string name, std::string json) { + auto config = std::make_shared(Config::parse(json).move_as_ok()); + if (!name.empty()) { + by_name_[name] = config; + } + by_root_hash_[config->zero_state_id.root_hash] = config; + } + td::optional by_name(std::string name) const { + auto it = by_name_.find(name); + if (it == by_name_.end()) { + return {}; + } + return *it->second; + } + + td::optional by_root_hash(const ton::RootHash& root_hash) const { + auto it = by_root_hash_.find(root_hash); + if (it == by_root_hash_.end()) { + return {}; + } + return *it->second; + } + + private: + size_t next_id_{0}; + std::map> by_name_; + std::map> by_root_hash_; +}; + +const MasterConfig& get_default_master_config() { + static MasterConfig config = [] { + MasterConfig res; + res.add_config("testnet", R"abc({ + "liteservers": [ + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", + "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + }, + "init_block": +{"workchain":-1,"shard":-9223372036854775808,"seqno":870721,"root_hash":"jYKhSQ1xeSPprzgjqiUOnAWwc2yqs7nCVAU21k922s4=","file_hash":"kHidF02CZpaz2ia9jtXUJLp0AiWMWwfzprTUIsddHSo="} + } +})abc"); + return res; + }(); + return config; +} + +td::Result TonlibClient::validate_config(tonlib_api::object_ptr config) { + if (!config) { + return TonlibError::EmptyField("config"); + } if (config->config_.empty()) { return TonlibError::InvalidConfig("config is empty"); } TRY_RESULT_PREFIX(new_config, Config::parse(std::move(config->config_)), TonlibError::InvalidConfig("can't parse config")); + if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { return TonlibError::InvalidConfig("no lite clients"); } - config_ = std::move(new_config); - config_generation_++; + + td::optional o_master_config; if (config->blockchain_name_.empty()) { - blockchain_name_ = td::sha256(config_.zero_state_id.to_str()).substr(0, 16); + o_master_config = get_default_master_config().by_root_hash(new_config.zero_state_id.root_hash); } else { - blockchain_name_ = config->blockchain_name_; + o_master_config = get_default_master_config().by_name(config->blockchain_name_); } - use_callbacks_for_network_ = config->use_callbacks_for_network_; - ignore_cache_ = config->ignore_cache_; + + if (o_master_config && o_master_config.value().zero_state_id != new_config.zero_state_id) { + return TonlibError::InvalidConfig("zero_state differs from embedded zero_state"); + } + FullConfig res; + res.config = std::move(new_config); + res.o_master_config = std::move(o_master_config); + res.ignore_cache = config->ignore_cache_; + res.use_callbacks_for_network = config->use_callbacks_for_network_; + return std::move(res); +} + +void TonlibClient::set_config(FullConfig full_config) { + config_ = std::move(full_config.config); + config_generation_++; + blockchain_name_ = config_.zero_state_id.root_hash.as_slice().str(); + + use_callbacks_for_network_ = full_config.use_callbacks_for_network; + ignore_cache_ = full_config.ignore_cache; init_ext_client(); - init_last_block(); + init_last_block(std::move(full_config.o_master_config)); init_last_config(); client_.set_client(get_client_ref()); - return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::close& request, @@ -1184,12 +1274,22 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, return td::Status::OK(); } +tonlib_api::object_ptr TonlibClient::do_static_request( + tonlib_api::options_validateConfig& request) { + auto r_config = validate_config(std::move(request.config_)); + if (r_config.is_error()) { + return status_to_tonlib_api(r_config.move_as_error()); + } + return tonlib_api::make_object(); +} + td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise) { if (!request.config_) { return TonlibError::EmptyField("config"); } - TRY_STATUS(set_config(std::move(request.config_))); + TRY_RESULT(config, validate_config(std::move(request.config_))); + set_config(std::move(config)); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -2020,6 +2120,84 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_getState& request, return td::Status::OK(); } +td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request, + td::Promise>&& promise) { + auto it = smcs_.find(request.id_); + if (it == smcs_.end()) { + return TonlibError::InvalidSmcId(); + } + + td::Ref smc(true, it->second->get_smc_state()); + ton::SmartContract::Args args; + downcast_call(*request.method_, + td::overloaded([&](tonlib_api::smc_methodIdNumber& number) { args.set_method_id(number.number_); }, + [&](tonlib_api::smc_methodIdName& name) { args.set_method_id(name.name_); })); + td::Ref stack(true); + td::Status status; + // TODO: error codes + // downcast_call + for (auto& entry : request.stack_) { + downcast_call(*entry, td::overloaded( + [&](tonlib_api::tvm_stackEntryUnsupported& cell) { + status = td::Status::Error("Unsuppored stack entry"); + }, + [&](tonlib_api::tvm_stackEntrySlice& cell) { + auto r_cell = vm::std_boc_deserialize(cell.slice_->bytes_); + if (r_cell.is_error()) { + status = r_cell.move_as_error(); + return; + } + stack.write().push_cell(r_cell.move_as_ok()); + }, + [&](tonlib_api::tvm_stackEntryCell& cell) { + auto r_cell = vm::std_boc_deserialize(cell.cell_->bytes_); + if (r_cell.is_error()) { + status = r_cell.move_as_error(); + return; + } + stack.write().push_cell(r_cell.move_as_ok()); + }, + [&](tonlib_api::tvm_stackEntryNumber& number) { + [&](tonlib_api::tvm_numberDecimal& dec) { + auto num = td::dec_string_to_int256(dec.number_); + if (num.is_null()) { + status = td::Status::Error("Failed to parse dec string to int256"); + return; + } + stack.write().push_int(std::move(num)); + }(*number.number_); + })); + } + TRY_STATUS(std::move(status)); + args.set_stack(std::move(stack)); + auto res = smc->run_get_method(std::move(args)); + + // smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; + std::vector> res_stack; + for (auto& entry : res.stack->as_span()) { + switch (entry.type()) { + case vm::StackEntry::Type::t_int: + res_stack.push_back(tonlib_api::make_object( + tonlib_api::make_object(dec_string(entry.as_int())))); + break; + case vm::StackEntry::Type::t_slice: + res_stack.push_back( + tonlib_api::make_object(tonlib_api::make_object( + to_bytes(vm::CellBuilder().append_cellslice(entry.as_slice()).finalize())))); + break; + case vm::StackEntry::Type::t_cell: + res_stack.push_back(tonlib_api::make_object( + tonlib_api::make_object(to_bytes(entry.as_cell())))); + break; + default: + res_stack.push_back(tonlib_api::make_object()); + break; + } + } + promise.set_value(tonlib_api::make_object(res.gas_used, std::move(res_stack), res.code)); + return td::Status::OK(); +} + td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Promise>&& promise) { client_.with_last_block(to_any_promise(std::move(promise))); return td::Status::OK(); @@ -2146,13 +2324,25 @@ td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& reque td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryResult& request, td::Promise>&& promise) { - send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, td::BufferSlice(request.bytes_), - to_any_promise(std::move(promise))); + if (ext_client_outbound_.empty()) { + return TonlibError::InvalidQueryId(); + } + if (((request.id_ ^ config_generation_) & 0xffff) != 0) { + return TonlibError::InvalidQueryId(); + } + send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_ >> 16, + td::BufferSlice(request.bytes_), to_any_promise(std::move(promise))); return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& request, td::Promise>&& promise) { - send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, + if (ext_client_outbound_.empty()) { + return TonlibError::InvalidQueryId(); + } + if (((request.id_ ^ config_generation_) & 0xffff) != 0) { + return TonlibError::InvalidQueryId(); + } + send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_ >> 16, td::Status::Error(request.error_->code_, request.error_->message_) .move_as_error_prefix(TonlibError::LiteServerNetwork()), to_any_promise(std::move(promise))); @@ -2306,6 +2496,11 @@ td::Status TonlibClient::do_request(const tonlib_api::unpackAccountAddress& requ return TonlibError::Internal(); } template +td::Status TonlibClient::do_request(const tonlib_api::options_validateConfig& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template td::Status TonlibClient::do_request(tonlib_api::getBip39Hints& request, P&&) { UNREACHABLE(); return TonlibError::Internal(); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 9a844fa3..fe006620 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -30,6 +30,7 @@ #include "td/actor/actor.h" #include "td/utils/CancellationToken.h" +#include "td/utils/optional.h" #include @@ -87,7 +88,7 @@ class TonlibClient : public td::actor::Actor { ExtClientRef get_client_ref(); void init_ext_client(); - void init_last_block(); + void init_last_block(td::optional o_master_config); void init_last_config(); bool is_closing_{false}; @@ -125,6 +126,7 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::testGiver_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::packAccountAddress& request); static object_ptr do_static_request(const tonlib_api::unpackAccountAddress& request); + static object_ptr do_static_request(tonlib_api::options_validateConfig& request); static object_ptr do_static_request(tonlib_api::getBip39Hints& request); static object_ptr do_static_request(tonlib_api::setLogStream& request); @@ -155,6 +157,8 @@ class TonlibClient : public td::actor::Actor { template td::Status do_request(const tonlib_api::unpackAccountAddress& request, P&&); template + td::Status do_request(const tonlib_api::options_validateConfig& request, P&&); + template td::Status do_request(tonlib_api::getBip39Hints& request, P&&); template @@ -189,7 +193,14 @@ class TonlibClient : public td::actor::Actor { } } - td::Status set_config(object_ptr config); + struct FullConfig { + Config config; + td::optional o_master_config; + bool use_callbacks_for_network; + bool ignore_cache; + }; + static td::Result validate_config(tonlib_api::object_ptr config); + void set_config(FullConfig config); td::Status do_request(const tonlib_api::init& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::close& request, td::Promise>&& promise); td::Status do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise); @@ -288,6 +299,9 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::smc_getState& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::smc_runGetMethod& request, + td::Promise>&& promise); + td::Status do_request(int_api::GetAccountState request, td::Promise>&&); td::Status do_request(int_api::GetPrivateKey request, td::Promise&&); td::Status do_request(int_api::SendMessage request, td::Promise&& promise); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index b401a027..8e1e74d3 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -29,20 +29,21 @@ // "help []\tThis help\n" // TODO: support [] // "quit\tExit\n"; // "sendfile \tLoad a serialized message from and send it to server\n" -// // "saveaccount[code|data] []\tSaves into specified file the most recent state " +// "(StateInit) or just the code or data of specified account; is in " +// "[:] format\n" +// // "runmethod ...\tRuns GET method of account " // "with specified parameters\n" // +// "getaccount []\tLoads the most recent state of specified account; is in " +// "[:] format\n" +// // WONTSUPPORT // // UNSUPPORTED //"last\tGet last block and state info from server\n" //"status\tShow connection and local database status\n" -//"getaccount []\tLoads the most recent state of specified account; is in " -//"[:] format\n" -//"(StateInit) or just the code or data of specified account; is in " -//"[:] format\n" //"allshards []\tShows shard configuration from the most recent masterchain " //"state or from masterchain state corresponding to \n" //"getconfig [...]\tShows specified or all configuration parameters from the latest masterchain state\n" @@ -266,6 +267,8 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "time\tGet server time\n"; td::TerminalIO::out() << "remote-version\tShows server time, version and capabilities\n"; td::TerminalIO::out() << "sendfile \tLoad a serialized message from and send it to server\n"; + td::TerminalIO::out() << "setconfig|validateconfig [] [] [] - set or validate " + "lite server config\n"; td::TerminalIO::out() << "exit\tExit\n"; td::TerminalIO::out() << "quit\tExit\n"; td::TerminalIO::out() @@ -279,7 +282,6 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "exportkey [] - export key\n"; td::TerminalIO::out() << "exportkeypem [] - export key\n"; - td::TerminalIO::out() << "setconfig [] [] [] - set lite server config\n"; td::TerminalIO::out() << "getstate - get state of simple wallet with requested key\n"; td::TerminalIO::out() << "gethistory - get history fo simple wallet with requested key (last 10 transactions)\n"; @@ -305,12 +307,6 @@ class TonlibCli : public td::actor::Actor { export_key(cmd.str(), parser.read_word()); } else if (cmd == "importkey") { import_key(parser.read_all()); - } else if (cmd == "setconfig") { - auto config = parser.read_word(); - auto name = parser.read_word(); - auto use_callback = parser.read_word(); - auto force = parser.read_word(); - set_config(config, name, to_bool(use_callback), to_bool(force)); } else if (cmd == "getstate") { get_state(parser.read_word()); } else if (cmd == "gethistory") { @@ -346,6 +342,14 @@ class TonlibCli : public td::actor::Actor { auto path = parser.read_word(); auto address = parser.read_word(); save_account(cmd, path, address, std::move(cmd_promise)); + } else if (cmd == "runmethod") { + run_method(parser, std::move(cmd_promise)); + } else if (cmd == "setconfig" || cmd == "validateconfig") { + auto config = parser.read_word(); + auto name = parser.read_word(); + auto use_callback = parser.read_word(); + auto force = parser.read_word(); + set_validate_config(cmd, config, name, to_bool(use_callback), to_bool(force), std::move(cmd_promise)); } else { cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`")); } @@ -419,6 +423,107 @@ class TonlibCli : public td::actor::Actor { return td::Unit(); })); } + td::Result> parse_stack_entry(td::Slice str) { + if (str.empty() || str.size() > 65535) { + return td::Status::Error("String is or empty or too big"); + } + int l = (int)str.size(); + if (str[0] == '"') { + vm::CellBuilder cb; + if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) { + return td::Status::Error("Failed to parse slice"); + } + return tonlib_api::make_object( + tonlib_api::make_object(vm::std_boc_serialize(cb.finalize()).ok().as_slice().str())); + } + if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') { + unsigned char buff[128]; + int bits = + (str[0] == 'x') + ? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1) + : (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1); + if (bits < 0) { + return td::Status::Error("Failed to parse slice"); + } + return tonlib_api::make_object(tonlib_api::make_object( + vm::std_boc_serialize(vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()) + .ok() + .as_slice() + .str())); + } + auto num = td::RefInt256{true}; + auto& x = num.unique_write(); + if (l >= 3 && str[0] == '0' && str[1] == 'x') { + if (x.parse_hex(str.data() + 2, l - 2) != l - 2) { + return td::Status::Error("Failed to parse a number"); + } + } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') { + if (x.parse_hex(str.data() + 3, l - 3) != l - 3) { + return td::Status::Error("Failed to parse a number"); + } + x.negate().normalize(); + } else if (!l || x.parse_dec(str.data(), l) != l) { + return td::Status::Error("Failed to parse a number"); + } + return tonlib_api::make_object( + tonlib_api::make_object(dec_string(num))); + } + + void run_method(td::ConstParser& parser, td::Promise promise) { + TRY_RESULT_PROMISE(promise, addr, to_account_address(parser.read_word(), false)); + + auto method_str = parser.read_word(); + tonlib_api::object_ptr method; + if (std::all_of(method_str.begin(), method_str.end(), [](auto c) { return c >= '0' && c <= '9'; })) { + method = tonlib_api::make_object(td::to_integer(method_str.str())); + } else { + method = tonlib_api::make_object(method_str.str()); + } + std::vector> stack; + while (true) { + auto word = parser.read_word(); + if (word.empty()) { + break; + } + TRY_RESULT_PROMISE(promise, stack_entry, parse_stack_entry(word)); + stack.push_back(std::move(stack_entry)); + } + auto to_run = + tonlib_api::make_object(0 /*fixme*/, std::move(method), std::move(stack)); + + send_query(tonlib_api::make_object(std::move(addr.address)), + promise.send_closure(actor_id(this), &TonlibCli::run_method_2, std::move(to_run))); + } + + void run_method_2(tonlib_api::object_ptr to_run, + tonlib_api::object_ptr info, td::Promise promise) { + to_run->id_ = info->id_; + send_query(std::move(to_run), promise.send_closure(actor_id(this), &TonlibCli::run_method_3)); + } + + void run_method_3(tonlib_api::object_ptr info, td::Promise promise) { + td::TerminalIO::out() << "Got smc result " << to_string(info); + promise.set_value({}); + } + + void set_validate_config(td::Slice cmd, td::Slice path, td::Slice name, bool use_callback, bool ignore_cache, + td::Promise promise) { + TRY_RESULT_PROMISE(promise, data, td::read_file_str(path.str())); + using tonlib_api::make_object; + + auto config = make_object(std::move(data), name.str(), use_callback, ignore_cache); + if (cmd == "setconfig") { + send_query(make_object(std::move(config)), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Config is set\n"; + return td::Unit(); + })); + } else { + send_query(make_object(std::move(config)), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Config is valid\n"; + return td::Unit(); + })); + } + } void dump_netstats() { td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n"; @@ -437,6 +542,8 @@ class TonlibCli : public td::actor::Actor { } } + td::Timestamp sync_started_; + void on_tonlib_result(std::uint64_t id, tonlib_api::object_ptr result) { if (id == 0) { switch (result->get_id()) { @@ -455,10 +562,15 @@ class TonlibCli : public td::actor::Actor { auto update = tonlib_api::move_object_as(std::move(result)); switch (update->sync_state_->get_id()) { case tonlib_api::syncStateDone::ID: { - td::TerminalIO::out() << "synchronization: DONE\n"; + td::TerminalIO::out() << "synchronization: DONE in " + << td::format::as_time(td::Time::now() - sync_started_.at()) << "\n"; + sync_started_ = {}; break; } case tonlib_api::syncStateInProgress::ID: { + if (!sync_started_) { + sync_started_ = td::Timestamp::now(); + } auto progress = tonlib_api::move_object_as(update->sync_state_); auto from = progress->from_seqno_; auto to = progress->to_seqno_; @@ -857,26 +969,6 @@ class TonlibCli : public td::actor::Actor { }); } - void set_config(td::Slice path, td::Slice name, bool use_callback, bool ignore_cache) { - auto r_data = td::read_file_str(path.str()); - if (r_data.is_error()) { - td::TerminalIO::out() << "Can't read file [" << path << "] : " << r_data.error() << "\n"; - return; - } - - auto data = r_data.move_as_ok(); - using tonlib_api::make_object; - send_query(make_object( - make_object(std::move(data), name.str(), use_callback, ignore_cache)), - [](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n"; - return; - } - td::TerminalIO::out() << to_string(r_res.ok()); - }); - } - void get_state(td::Slice key) { if (key.empty()) { dump_keys(); diff --git a/validator/manager.cpp b/validator/manager.cpp index fc1edd98..2198d42a 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1843,7 +1843,9 @@ void ValidatorManagerImpl::allow_block_state_gc(BlockIdExt block_id, td::Promise } void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise promise) { - auto P = + promise.set_result(false); + return; + /*auto P = td::PromiseCreator::lambda([db = db_.get(), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_result(false); @@ -1860,7 +1862,7 @@ void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise< } } }); - get_block_handle(block_id, false, std::move(P)); + get_block_handle(block_id, false, std::move(P));*/ } void ValidatorManagerImpl::got_next_gc_masterchain_handle(BlockHandle handle) {