diff --git a/CMakeLists.txt b/CMakeLists.txt index 8146bef9..4d59081b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -441,7 +441,7 @@ if (USE_LIBRAPTORQ) endif() add_executable(test-hello-world test/test-hello-world.cpp ) -target_link_libraries(test-hello-world tl_api crypto ton_crypto) +target_link_libraries(test-hello-world tl_api ton_crypto) add_executable(test-adnl test/test-adnl.cpp) target_link_libraries(test-adnl adnl adnltest dht tl_api) @@ -510,6 +510,12 @@ add_test(test-tonlib-offline test-tonlib-offline) #BEGIN internal if (NOT TON_ONLY_TONLIB) +add_test(test-adnl test-adnl) +add_test(test-dht test-dht) +add_test(test-rldp test-rldp) +add_test(test-validator-session-state test-validator-session-state) +add_test(test-catchain test-catchain) + add_test(test-fec test-fec) add_test(test-tddb test-tddb ${TEST_OPTIONS}) add_test(test-db test-db ${TEST_OPTIONS}) diff --git a/blockchain-explorer/blockchain-explorer-http.cpp b/blockchain-explorer/blockchain-explorer-http.cpp index 43d63d91..d5d41a15 100644 --- a/blockchain-explorer/blockchain-explorer-http.cpp +++ b/blockchain-explorer/blockchain-explorer-http.cpp @@ -379,6 +379,23 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { return *this; } + *this << "
" + << "
" + << "

Run get method

" + << "

" + << "" + << "
\n" + << "
" + << "
" + << "" + << "" + << "" + << "" + << "" + << "" + << "
" + << "
\n"; + *this << "
\n" << "\n"; *this << "
block" << block_id.id.to_str() @@ -480,10 +497,13 @@ HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) { return *this; } - return *this << "

download block" - << "view block\n" - << "

"; + *this << "

download block" + << "view block\n"; + if (block_id.is_masterchain()) { + *this << "view config\n"; + } + return *this << "

"; } HttpAnswer& HttpAnswer::operator<<(BlockShardsCell shards_c) { @@ -571,6 +591,12 @@ HttpAnswer& HttpAnswer::operator<<(BlockViewLink block) { return *this; } +HttpAnswer& HttpAnswer::operator<<(ConfigViewLink block) { + *this << prefix_ << "config?"; + block_id_link(block.block_id); + return *this; +} + HttpAnswer& HttpAnswer::operator<<(BlockDownloadLink block) { *this << prefix_ << "download?"; block_id_link(block.block_id); @@ -606,10 +632,26 @@ HttpAnswer& HttpAnswer::operator<<(TransactionList trans) { return *this << "
"; } +HttpAnswer& HttpAnswer::operator<<(ConfigParam conf) { + std::ostringstream os; + *this << "

param " << conf.idx << "

"; + if (conf.idx >= 0) { + *this << RawData{conf.root, conf.idx}; + } else { + *this << RawData{conf.root}; + } + *this << "
\n"; + return *this; +} + HttpAnswer& HttpAnswer::operator<<(Error error) { return *this << "
" << error.error.to_string() << "
"; } +HttpAnswer& HttpAnswer::operator<<(Notification n) { + return *this << "
" << n.text << "
"; +} + void HttpAnswer::block_id_link(ton::BlockIdExt block_id) { *this << "workchain=" << block_id.id.workchain << "&shard=" << ton::shard_to_str(block_id.id.shard) << "&seqno=" << block_id.id.seqno << "&roothash=" << block_id.root_hash << "&filehash=" << block_id.file_hash; diff --git a/blockchain-explorer/blockchain-explorer-http.hpp b/blockchain-explorer/blockchain-explorer-http.hpp index dadd7e17..4fbb42c9 100644 --- a/blockchain-explorer/blockchain-explorer-http.hpp +++ b/blockchain-explorer/blockchain-explorer-http.hpp @@ -84,6 +84,9 @@ class HttpAnswer { struct BlockViewLink { ton::BlockIdExt block_id; }; + struct ConfigViewLink { + ton::BlockIdExt block_id; + }; struct BlockDownloadLink { ton::BlockIdExt block_id; }; @@ -116,9 +119,16 @@ class HttpAnswer { struct CodeBlock { std::string data; }; + struct ConfigParam { + td::int32 idx; + td::Ref root; + }; struct Error { td::Status error; }; + struct Notification { + std::string text; + }; template struct RawData { td::Ref root; @@ -189,14 +199,17 @@ class HttpAnswer { HttpAnswer &operator<<(TransactionLinkShort trans); HttpAnswer &operator<<(BlockLink block); HttpAnswer &operator<<(BlockViewLink block); + HttpAnswer &operator<<(ConfigViewLink block); HttpAnswer &operator<<(BlockDownloadLink block); HttpAnswer &operator<<(Error error); + HttpAnswer &operator<<(Notification notification); HttpAnswer &operator<<(TransactionList trans); HttpAnswer &operator<<(CodeBlock block) { return *this << "
" << block.data << "
"; } + HttpAnswer &operator<<(ConfigParam conf); template HttpAnswer &operator<<(RawData data) { @@ -220,3 +233,16 @@ class HttpAnswer { std::unique_ptr sb_; td::BufferSlice buf_; }; + +template <> +struct HttpAnswer::RawData { + td::Ref root; + RawData(td::Ref root) : root(std::move(root)) { + } +}; +template <> +inline HttpAnswer &HttpAnswer::operator<<(RawData data) { + std::ostringstream outp; + vm::load_cell_slice(data.root).print_rec(outp); + return *this << CodeBlock{outp.str()}; +} diff --git a/blockchain-explorer/blockchain-explorer-query.cpp b/blockchain-explorer/blockchain-explorer-query.cpp index 061aace6..db731f14 100644 --- a/blockchain-explorer/blockchain-explorer-query.cpp +++ b/blockchain-explorer/blockchain-explorer-query.cpp @@ -28,6 +28,8 @@ */ #include "blockchain-explorer-query.hpp" #include "blockchain-explorer-http.hpp" +#include "block/mc-config.h" +#include "crypto/block/check-proof.h" #include "auto/tl/lite_api.h" @@ -39,6 +41,40 @@ #include "common/errorcode.h" #include "block/block-auto.h" +#include "crypto/vm/utils.h" +#include "td/utils/crypto.h" + +#include "vm/boc.h" +#include "vm/cellops.h" +#include "vm/cells/MerkleProof.h" +#include "vm/continuation.h" +#include "vm/cp0.h" + +namespace { + +td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref my_addr, + const block::CurrencyCollection &balance) { + td::BitArray<256> rand_seed; + td::RefInt256 rand_seed_int{true}; + td::Random::secure_bytes(rand_seed.as_slice()); + if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) { + return {}; + } + auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea + td::make_refint(0), // actions:Integer + td::make_refint(0), // msgs_sent:Integer + td::make_refint(now), // unixtime:Integer + td::make_refint(lt), // block_lt:Integer + 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 + vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo; + LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); + return vm::make_tuple_ref(std::move(tuple)); +} + +} // namespace td::Result parse_block_id(std::map &opts, bool allow_empty) { if (allow_empty) { @@ -990,6 +1026,419 @@ void HttpQueryViewLastBlock::finish_query() { stop(); } +HttpQueryConfig::HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector params, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)), block_id_(block_id), params_(std::move(params)) { +} + +HttpQueryConfig::HttpQueryConfig(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { + auto R = parse_block_id(opts, true); + if (R.is_error()) { + error_ = R.move_as_error(); + return; + } + block_id_ = R.move_as_ok(); + + auto it = opts.find("param"); + if (it != opts.end()) { + auto R2 = td::to_integer_safe(it->second); + if (R2.is_error()) { + error_ = R2.move_as_error(); + return; + } + params_.push_back(R2.move_as_ok()); + } +} + +void HttpQueryConfig::start_up() { + if (error_.is_error()) { + abort_query(std::move(error_)); + return; + } + if (block_id_.is_valid()) { + send_main_query(); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &HttpQueryConfig::got_block, R.move_as_ok()); + } + }); + + auto query = ton::serialize_tl_object(ton::create_tl_object(), true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); + } +} + +void HttpQueryConfig::got_block(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + return; + } + auto f = F.move_as_ok(); + block_id_ = ton::create_block_id(f->last_); + + send_main_query(); +} + +void HttpQueryConfig::send_main_query() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &HttpQueryConfig::got_result, R.move_as_ok()); + } + }); + auto query = + params_.size() > 0 + ? ton::serialize_tl_object(ton::create_tl_object( + 0, ton::create_tl_lite_block_id(block_id_), std::vector(params_)), + true) + : ton::serialize_tl_object(ton::create_tl_object( + 0, ton::create_tl_lite_block_id(block_id_)), + true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); +} + +void HttpQueryConfig::got_result(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + return; + } + auto f = F.move_as_ok(); + + state_proof_ = std::move(f->state_proof_); + config_proof_ = std::move(f->config_proof_); + + finish_query(); +} + +void HttpQueryConfig::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"config", prefix_}; + A.set_block_id(block_id_); + auto R = block::check_extract_state_proof(block_id_, state_proof_.as_slice(), config_proof_.as_slice()); + if (R.is_error()) { + A.abort(PSTRING() << "masterchain state proof for " << block_id_.to_str() + << " is invalid : " << R.move_as_error()); + return A.finish(); + } + try { + auto res = block::Config::extract_from_state(R.move_as_ok(), 0); + if (res.is_error()) { + A.abort(PSTRING() << "cannot unpack configuration: " << res.move_as_error()); + return A.finish(); + } + auto config = res.move_as_ok(); + if (params_.size() > 0) { + A << "

params: "; + for (int i : params_) { + auto value = config->get_config_param(i); + if (value.not_null()) { + A << "" << i << " "; + } + } + A << "

"; + for (int i : params_) { + auto value = config->get_config_param(i); + if (value.not_null()) { + A << HttpAnswer::ConfigParam{i, value}; + } else { + A << HttpAnswer::Error{td::Status::Error(404, PSTRING() << "empty param " << i)}; + } + } + } else { + A << "

params: "; + config->foreach_config_param([&](int i, td::Ref value) { + if (value.not_null()) { + A << "" << i << " "; + } + return true; + }); + A << "

"; + config->foreach_config_param([&](int i, td::Ref value) { + if (value.not_null()) { + A << HttpAnswer::ConfigParam{i, value}; + } + return true; + }); + } + } catch (vm::VmError &err) { + A.abort(PSTRING() << "error while traversing configuration: " << err.get_msg()); + } catch (vm::VmVirtError &err) { + A.abort(PSTRING() << "virtualization error while traversing configuration: " << err.get_msg()); + } + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} + +HttpQuerySendForm::HttpQuerySendForm(std::string prefix, td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { +} + +HttpQuerySendForm::HttpQuerySendForm(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { +} + +void HttpQuerySendForm::start_up() { + finish_query(); +} + +void HttpQuerySendForm::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"send", prefix_}; + A << "
" + << "" + << "" + << "" + << "
"; + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} + +HttpQuerySend::HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)), data_(std::move(data)) { +} + +HttpQuerySend::HttpQuerySend(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { + auto it = opts.find("filedata"); + if (it != opts.end()) { + data_ = td::BufferSlice{it->second}; + } else { + error_ = td::Status::Error("no file data"); + return; + } +} + +void HttpQuerySend::start_up() { + if (error_.is_error()) { + abort_query(std::move(error_)); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQuerySend::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &HttpQuerySend::got_result, R.move_as_ok()); + } + }); + auto query = + ton::serialize_tl_object(ton::create_tl_object(std::move(data_)), true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); +} + +void HttpQuerySend::got_result(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + } else { + status_ = F.move_as_ok()->status_; + } + finish_query(); +} + +void HttpQuerySend::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"send", prefix_}; + if (status_ >= 0) { + A << HttpAnswer::Notification{"success"}; + } else { + A << HttpAnswer::Error{td::Status::Error(status_, "failed")}; + } + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} + +HttpQueryRunMethod::HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name, + std::vector params, std::string prefix, + td::Promise promise) + : HttpQueryCommon(std::move(prefix), std::move(promise)) + , block_id_(block_id) + , addr_(addr) + , method_name_(std::move(method_name)) + , params_(std::move(params)) { +} + +HttpQueryRunMethod::HttpQueryRunMethod(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(std::move(prefix), std::move(promise)) { + auto R = parse_block_id(opts, true); + if (R.is_ok()) { + block_id_ = R.move_as_ok(); + if (!block_id_.is_valid()) { + block_id_.id.workchain = ton::masterchainId; + block_id_.id.shard = ton::shardIdAll; + block_id_.id.seqno = static_cast(0xffffffff); + block_id_.root_hash.set_zero(); + block_id_.file_hash.set_zero(); + } + } else { + error_ = R.move_as_error(); + return; + } + auto R2 = parse_account_addr(opts); + if (R2.is_ok()) { + addr_ = R2.move_as_ok(); + } else { + error_ = R2.move_as_error(); + return; + } + auto it = opts.find("method"); + if (it == opts.end()) { + error_ = td::Status::Error("no method"); + return; + } else { + method_name_ = it->second; + } + it = opts.find("params"); + if (it != opts.end()) { + auto R3 = vm::parse_stack_entries(it->second); + if (R3.is_error()) { + error_ = R3.move_as_error(); + return; + } + params_ = R3.move_as_ok(); + } +} + +void HttpQueryRunMethod::start_up_query() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQueryRunMethod::abort_query, R.move_as_error_prefix("litequery failed: ")); + } else { + td::actor::send_closure(SelfId, &HttpQueryRunMethod::got_account, R.move_as_ok()); + } + }); + auto a = ton::create_tl_object(addr_.workchain, addr_.addr); + auto query = ton::serialize_tl_object(ton::create_tl_object( + ton::create_tl_lite_block_id(block_id_), std::move(a)), + true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); +} + +void HttpQueryRunMethod::got_account(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + return; + } + + auto f = F.move_as_ok(); + data_ = std::move(f->state_); + proof_ = std::move(f->proof_); + shard_proof_ = std::move(f->shard_proof_); + block_id_ = ton::create_block_id(f->id_); + res_block_id_ = ton::create_block_id(f->shardblk_); + + finish_query(); +} + +void HttpQueryRunMethod::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"account", prefix_}; + A.set_account_id(addr_); + A.set_block_id(res_block_id_); + + block::AccountState account_state; + account_state.blk = block_id_; + account_state.shard_blk = res_block_id_; + account_state.shard_proof = std::move(shard_proof_); + account_state.proof = std::move(proof_); + account_state.state = std::move(data_); + auto r_info = account_state.validate(block_id_, addr_); + if (r_info.is_error()) { + A.abort(r_info.move_as_error()); + return A.finish(); + } + auto info = r_info.move_as_ok(); + if (info.root.is_null()) { + A.abort(PSTRING() << "account state of " << addr_ << " is empty (cannot run method `" << method_name_ << "`)"); + return A.finish(); + } + block::gen::Account::Record_account acc; + block::gen::AccountStorage::Record store; + block::CurrencyCollection balance; + if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) && + balance.validate_unpack(store.balance))) { + A.abort("error unpacking account state"); + return A.finish(); + } + int tag = block::gen::t_AccountState.get_tag(*store.state); + switch (tag) { + case block::gen::AccountState::account_uninit: + A.abort(PSTRING() << "account " << addr_ << " not initialized yet (cannot run any methods)"); + return A.finish(); + case block::gen::AccountState::account_frozen: + A.abort(PSTRING() << "account " << addr_ << " frozen (cannot run any methods)"); + return A.finish(); + } + + CHECK(store.state.write().fetch_ulong(1) == 1); // account_init$1 _:StateInit = AccountState; + block::gen::StateInit::Record state_init; + CHECK(tlb::csr_unpack(store.state, state_init)); + auto code = state_init.code->prefetch_ref(); + auto data = state_init.data->prefetch_ref(); + auto stack = td::make_ref(std::move(params_)); + td::int64 method_id = (td::crc16(td::Slice{method_name_}) & 0xffff) | 0x10000; + stack.write().push_smallint(method_id); + long long gas_limit = vm::GasLimits::infty; + // OstreamLogger ostream_logger(ctx.error_stream); + // auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); + vm::GasLimits gas{gas_limit}; + LOG(DEBUG) << "creating VM"; + vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()}; + vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo + // vm.incr_stack_trace(1); // enable stack dump after each step + int exit_code = ~vm.run(); + if (exit_code != 0) { + A.abort(PSTRING() << "VM terminated with error code " << exit_code); + return A.finish(); + } + stack = vm.get_stack_ref(); + { + std::ostringstream os; + os << "result: "; + stack->dump(os, 3); + + A << HttpAnswer::CodeBlock{os.str()}; + } + + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} HttpQueryStatus::HttpQueryStatus(std::string prefix, td::Promise promise) : HttpQueryCommon(std::move(prefix), std::move(promise)) { } diff --git a/blockchain-explorer/blockchain-explorer-query.hpp b/blockchain-explorer/blockchain-explorer-query.hpp index 2acf9efb..ce7a713f 100644 --- a/blockchain-explorer/blockchain-explorer-query.hpp +++ b/blockchain-explorer/blockchain-explorer-query.hpp @@ -258,6 +258,77 @@ class HttpQueryViewLastBlock : public HttpQueryCommon { ton::BlockIdExt res_block_id_; }; +class HttpQueryConfig : public HttpQueryCommon { + public: + HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector params, + td::Promise promise); + HttpQueryConfig(std::map opts, std::string prefix, td::Promise promise); + + void finish_query(); + + void start_up() override; + void got_block(td::BufferSlice result); + void send_main_query(); + void got_result(td::BufferSlice result); + + private: + ton::BlockIdExt block_id_; + std::vector params_; + + td::BufferSlice state_proof_; + td::BufferSlice config_proof_; +}; + +class HttpQuerySendForm : public HttpQueryCommon { + public: + HttpQuerySendForm(std::string prefix, td::Promise promise); + HttpQuerySendForm(std::map opts, std::string prefix, td::Promise promise); + + void start_up() override; + void finish_query(); + + private: +}; + +class HttpQuerySend : public HttpQueryCommon { + public: + HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise promise); + HttpQuerySend(std::map opts, std::string prefix, td::Promise promise); + + void finish_query(); + + void start_up() override; + void got_result(td::BufferSlice result); + + private: + td::BufferSlice data_; + td::int32 status_; +}; + +class HttpQueryRunMethod : public HttpQueryCommon { + public: + HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name, + std::vector params, std::string prefix, td::Promise promise); + HttpQueryRunMethod(std::map opts, std::string prefix, td::Promise promise); + + void finish_query(); + + void start_up_query() override; + void got_account(td::BufferSlice result); + + private: + ton::BlockIdExt block_id_; + block::StdAddress addr_; + + std::string method_name_; + std::vector params_; + + td::BufferSlice data_; + td::BufferSlice proof_; + td::BufferSlice shard_proof_; + ton::BlockIdExt res_block_id_; +}; + class HttpQueryStatus : public HttpQueryCommon { public: HttpQueryStatus(std::string prefix, td::Promise promise); diff --git a/blockchain-explorer/blockchain-explorer.cpp b/blockchain-explorer/blockchain-explorer.cpp index 2fa1bf75..b3776e3e 100644 --- a/blockchain-explorer/blockchain-explorer.cpp +++ b/blockchain-explorer/blockchain-explorer.cpp @@ -49,6 +49,12 @@ #include "blockchain-explorer-http.hpp" #include "blockchain-explorer-query.hpp" +#include "vm/boc.h" +#include "vm/cellops.h" +#include "vm/cells/MerkleProof.h" +#include "vm/continuation.h" +#include "vm/cp0.h" + #include "auto/tl/lite_api.h" #include "ton/lite-tl.hpp" #include "tl-utils/lite-utils.hpp" @@ -263,22 +269,80 @@ class CoreActor : public CoreActorInterface { return MHD_YES; } + struct HttpRequestExtra { + HttpRequestExtra(MHD_Connection* connection, bool is_post) { + if (is_post) { + postprocessor = MHD_create_post_processor(connection, 1 << 14, iterate_post, static_cast(this)); + } + } + ~HttpRequestExtra() { + MHD_destroy_post_processor(postprocessor); + } + static int iterate_post(void* coninfo_cls, enum MHD_ValueKind kind, const char* key, const char* filename, + const char* content_type, const char* transfer_encoding, const char* data, uint64_t off, + size_t size) { + auto ptr = static_cast(coninfo_cls); + ptr->total_size += strlen(key) + size; + if (ptr->total_size > MAX_POST_SIZE) { + return MHD_NO; + } + std::string k = key; + if (ptr->opts[k].size() < off + size) { + ptr->opts[k].resize(off + size); + } + td::MutableSlice(ptr->opts[k]).remove_prefix(off).copy_from(td::Slice(data, size)); + return MHD_YES; + } + MHD_PostProcessor* postprocessor; + std::map opts; + td::uint64 total_size = 0; + }; + + static void request_completed(void* cls, struct MHD_Connection* connection, void** ptr, + enum MHD_RequestTerminationCode toe) { + auto e = static_cast(*ptr); + if (e) { + delete e; + } + } + static int process_http_request(void* cls, struct MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** ptr) { - static int dummy; struct MHD_Response* response = nullptr; int ret; - if (0 != std::strcmp(method, "GET")) + bool is_post = false; + if (std::strcmp(method, "GET") == 0) { + is_post = false; + } else if (std::strcmp(method, "POST") == 0) { + is_post = true; + } else { return MHD_NO; /* unexpected method */ - if (&dummy != *ptr) { - /* The first time only the headers are valid, - do not respond in the first round... */ - *ptr = &dummy; - return MHD_YES; } - if (0 != *upload_data_size) - return MHD_NO; /* upload data in a GET!? */ + std::map opts; + if (!is_post) { + if (!*ptr) { + *ptr = static_cast(new HttpRequestExtra{connection, false}); + return MHD_YES; + } + if (0 != *upload_data_size) + return MHD_NO; /* upload data in a GET!? */ + } else { + if (!*ptr) { + *ptr = static_cast(new HttpRequestExtra{connection, true}); + return MHD_YES; + } + auto e = static_cast(*ptr); + if (0 != *upload_data_size) { + CHECK(e->postprocessor); + MHD_post_process(e->postprocessor, upload_data, *upload_data_size); + *upload_data_size = 0; + return MHD_YES; + } + for (auto& o : e->opts) { + opts[o.first] = std::move(o.second); + } + } std::string url_s = url; @@ -295,7 +359,6 @@ class CoreActor : public CoreActorInterface { command = url_s.substr(pos + 1); } - std::map opts; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, get_arg_iterate, static_cast(&opts)); if (command == "status") { @@ -352,6 +415,26 @@ class CoreActor : public CoreActorInterface { .release(); }}; response = g.wait(); + } else if (command == "config") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("getconfig", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); + } else if (command == "send") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("send", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); + } else if (command == "sendform") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("sendform", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); + } else if (command == "runmethod") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("runmethod", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); } else { ret = MHD_NO; } @@ -394,7 +477,8 @@ class CoreActor : public CoreActorInterface { remote_addr_, make_callback(0))); } daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(http_port_), nullptr, nullptr, - &process_http_request, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END); + &process_http_request, nullptr, MHD_OPTION_NOTIFY_COMPLETED, request_completed, nullptr, + MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END); CHECK(daemon_ != nullptr); } }; @@ -567,6 +651,8 @@ int main(int argc, char* argv[]) { }); #endif + vm::init_op_cp0(); + td::actor::Scheduler scheduler({2}); scheduler_ptr = &scheduler; scheduler.run_in_context([&] { x = td::actor::create_actor("testnode"); }); diff --git a/blockchain-explorer/blockchain-explorer.hpp b/blockchain-explorer/blockchain-explorer.hpp index 84b86658..59e42cce 100644 --- a/blockchain-explorer/blockchain-explorer.hpp +++ b/blockchain-explorer/blockchain-explorer.hpp @@ -32,6 +32,8 @@ #include "ton/ton-types.h" #include "td/utils/port/IPAddress.h" +#define MAX_POST_SIZE (64 << 10) + class CoreActorInterface : public td::actor::Actor { public: struct RemoteNodeStatus { diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 38f65500..ff7d9227 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -35,6 +35,7 @@ set(TON_CRYPTO_SOURCE vm/debugops.cpp vm/tonops.cpp vm/boc.cpp + vm/utils.cpp tl/tlblib.cpp Ed25519.h @@ -80,6 +81,7 @@ set(TON_CRYPTO_SOURCE vm/tupleops.h vm/tonops.h vm/vmstate.h + vm/utils.h vm/cells.h vm/cellslice.h diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index bdbf08a6..b2cad142 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -789,10 +789,11 @@ td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref prev_st return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid global_balance"); } if (extra.r1.flags & 1) { - if (extra.r1.block_create_stats->prefetch_ulong(8) != 0x17) { + if (extra.r1.block_create_stats->prefetch_ulong(8) == 0x17) { + block_create_stats_ = std::make_unique(extra.r1.block_create_stats->prefetch_ref(), 256); + } else { return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid BlockCreateStats"); } - block_create_stats_ = std::make_unique(extra.r1.block_create_stats->prefetch_ref(), 256); } else { block_create_stats_ = std::make_unique(256); } @@ -1846,6 +1847,18 @@ td::Status check_block_header(Ref block_root, const ton::BlockIdExt& i return td::Status::OK(); } +std::unique_ptr get_block_create_stats_dict(Ref state_root) { + block::gen::ShardStateUnsplit::Record info; + block::gen::McStateExtra::Record extra; + block::gen::BlockCreateStats::Record_block_create_stats cstats; + if (!(::tlb::unpack_cell(std::move(state_root), info) && info.custom->size_refs() && + ::tlb::unpack_cell(info.custom->prefetch_ref(), extra) && (extra.r1.flags & 1) && + ::tlb::csr_unpack(std::move(extra.r1.block_create_stats), cstats))) { + return {}; + } + return std::make_unique(std::move(cstats.counters), 256); +} + std::unique_ptr get_prev_blocks_dict(Ref state_root) { block::gen::ShardStateUnsplit::Record info; block::gen::McStateExtra::Record extra_info; diff --git a/crypto/block/block.h b/crypto/block/block.h index fd9a724f..0cfbe24a 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -163,12 +163,12 @@ struct MsgProcessedUpto { MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash) : shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) { } - bool operator<(const MsgProcessedUpto& other) const& { + bool operator<(const MsgProcessedUpto& other) const & { return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno); } - bool contains(const MsgProcessedUpto& other) const&; + bool contains(const MsgProcessedUpto& other) const &; bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash, - ton::BlockSeqno other_mc_seqno) const&; + ton::BlockSeqno other_mc_seqno) const &; // NB: this is for checking whether we have already imported an internal message bool already_processed(const EnqueuedMsgDescr& msg) const; }; @@ -514,6 +514,9 @@ struct DiscountedCounter { return last_updated == other.last_updated && total == other.total && cnt2048 <= other.cnt2048 + 1 && other.cnt2048 <= cnt2048 + 1 && cnt65536 <= other.cnt65536 + 1 && other.cnt65536 <= cnt65536 + 1; } + bool modified_since(ton::UnixTime utime) const { + return last_updated >= utime; + } bool validate(); bool increase_by(unsigned count, ton::UnixTime now); bool fetch(vm::CellSlice& cs); @@ -629,6 +632,8 @@ td::Status unpack_block_prev_blk_try(Ref block_root, const ton::BlockI td::Status check_block_header(Ref block_root, const ton::BlockIdExt& id, ton::Bits256* store_shard_hash_to = nullptr); +std::unique_ptr get_block_create_stats_dict(Ref state_root); + std::unique_ptr get_prev_blocks_dict(Ref state_root); bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr); diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index ec91ef57..074a5d4b 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -366,6 +366,10 @@ action_send_msg#0ec3c86d mode:(## 8) action_set_code#ad4de08e new_code:^Cell = OutAction; action_reserve_currency#36e6b809 mode:(## 8) currency:CurrencyCollection = OutAction; +libref_hash$0 lib_hash:bits256 = LibRef; +libref_ref$1 library:^Cell = LibRef; +action_change_library#26fa1dd4 mode:(## 7) { mode <= 2 } + libref:LibRef = OutAction; out_list_node$_ prev:^Cell action:OutAction = OutListNode; // @@ -505,6 +509,7 @@ _ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo; counters#_ last_updated:uint32 total:uint64 cnt2048:uint64 cnt65536:uint64 = Counters; creator_info#4 mc_blocks:Counters shard_blocks:Counters = CreatorStats; block_create_stats#17 counters:(HashmapE 256 CreatorStats) = BlockCreateStats; +block_create_stats_ext#34 counters:(HashmapAugE 256 CreatorStats uint32) = BlockCreateStats; masterchain_state_extra#cc26 shard_hashes:ShardHashes diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index b5959f75..c686e200 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -1710,6 +1710,30 @@ std::vector Config::compute_total_validator_set(int next) c return res.move_as_ok()->export_validator_set(); } +td::Result> Config::unpack_validator_set_start_stop(Ref vset_root) { + if (vset_root.is_null()) { + return td::Status::Error("validator set absent"); + } + gen::ValidatorSet::Record_validators_ext rec; + if (tlb::unpack_cell(vset_root, rec)) { + return std::pair(rec.utime_since, rec.utime_until); + } + gen::ValidatorSet::Record_validators rec0; + if (tlb::unpack_cell(std::move(vset_root), rec0)) { + return std::pair(rec0.utime_since, rec0.utime_until); + } + return td::Status::Error("validator set is invalid"); +} + +std::pair Config::get_validator_set_start_stop(int next) const { + auto res = unpack_validator_set_start_stop(get_config_param(next < 0 ? 32 : (next ? 36 : 34))); + if (res.is_error()) { + return {0, 0}; + } else { + return res.move_as_ok(); + } +} + bool WorkchainInfo::unpack(ton::WorkchainId wc, vm::CellSlice& cs) { workchain = ton::workchainInvalid; if (wc == ton::workchainInvalid) { diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 241ae88c..55bf1122 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -50,7 +50,7 @@ struct ValidatorDescr { : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) { adnl_addr.set_zero(); } - bool operator<(td::uint64 wt_pos) const& { + bool operator<(td::uint64 wt_pos) const & { return cum_weight < wt_pos; } }; @@ -558,6 +558,7 @@ class Config { const ValidatorSet* get_cur_validator_set() const { return cur_validators_.get(); } + std::pair get_validator_set_start_stop(int next = 0) const; ton::ValidatorSessionConfig get_consensus_config() const; bool foreach_config_param(std::function)> scan_func) const; Ref get_workchain_info(ton::WorkchainId workchain_id) const; @@ -577,6 +578,7 @@ class Config { static td::Result> unpack_config(Ref config_csr, int mode = 0); static td::Result> extract_from_state(Ref mc_state_root, int mode = 0); static td::Result> extract_from_key_block(Ref key_block_root, int mode = 0); + static td::Result> unpack_validator_set_start_stop(Ref root); protected: Config(int _mode) : mode(_mode) { diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 6667cf87..df4cf883 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1098,6 +1098,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { case block::gen::OutAction::action_reserve_currency: err_code = try_action_reserve_currency(cs, ap, cfg); break; + case block::gen::OutAction::action_change_library: + err_code = try_action_change_library(cs, ap, cfg); + break; } if (err_code) { ap.result_code = (err_code == -1 ? 34 : err_code); @@ -1148,6 +1151,56 @@ int Transaction::try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const A return 0; } +int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) { + block::gen::OutAction::Record_action_change_library rec; + if (!tlb::unpack_exact(cs, rec)) { + return -1; + } + // mode: +0 = remove library, +1 = add private library, +2 = add public library + Ref lib_ref = rec.libref->prefetch_ref(); + ton::Bits256 hash; + if (lib_ref.not_null()) { + hash = lib_ref->get_hash().bits(); + } else { + CHECK(rec.libref.write().fetch_ulong(1) == 0 && rec.libref.write().fetch_bits_to(hash)); + } + try { + vm::Dictionary dict{new_library, 256}; + if (!rec.mode) { + // remove library + dict.lookup_delete(hash); + LOG(DEBUG) << "removed " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex(); + } else { + auto val = dict.lookup(hash); + if (val.not_null()) { + bool is_public = val->prefetch_ulong(1); + auto ref = val->prefetch_ref(); + if (hash == ref->get_hash().bits()) { + lib_ref = ref; + if (is_public == (rec.mode >> 1)) { + // library already in required state + ap.spec_actions++; + return 0; + } + } + } + if (lib_ref.is_null()) { + // library code not found + return 41; + } + vm::CellBuilder cb; + CHECK(cb.store_bool_bool(rec.mode >> 1) && cb.store_ref_bool(std::move(lib_ref))); + CHECK(dict.set_builder(hash, cb)); + LOG(DEBUG) << "added " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex(); + } + new_library = std::move(dict).extract_root_cell(); + } catch (vm::VmError& vme) { + return 42; + } + ap.spec_actions++; + return 0; +} + // msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms // ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms // bits in the root cell of a message are not included in msg.bits (lump_price pays for them) diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 25f80595..10150f1c 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -65,10 +65,10 @@ struct NewOutMsg { NewOutMsg(ton::LogicalTime _lt, Ref _msg, Ref _trans) : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) { } - bool operator<(const NewOutMsg& other) const& { + bool operator<(const NewOutMsg& other) const & { return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash()); } - bool operator>(const NewOutMsg& other) const& { + bool operator>(const NewOutMsg& other) const & { return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash()); } }; @@ -371,6 +371,7 @@ struct Transaction { Ref prepare_vm_c7(const ComputePhaseConfig& cfg) const; bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const; int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); + int try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0); int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); bool check_replace_src_addr(Ref& src_addr) const; diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index eb9034ec..730ef78a 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1013,6 +1013,8 @@ x{FB00} @Defop SENDRAWMSG x{FB02} @Defop RAWRESERVE x{FB03} @Defop RAWRESERVEX x{FB04} @Defop SETCODE +x{FB06} @Defop SETLIBCODE +x{FB07} @Defop CHANGELIB // // debug primitives diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index 5430c359..724ef812 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -111,3 +111,4 @@ variable base { true (atom) drop } : atom { bl word atom 1 'nop } ::_ ` { hole dup 1 { @ execute } does create } : recursive +{ 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n diff --git a/crypto/fift/lib/GetOpt.fif b/crypto/fift/lib/GetOpt.fif new file mode 100644 index 00000000..ee8a59af --- /dev/null +++ b/crypto/fift/lib/GetOpt.fif @@ -0,0 +1,92 @@ +library GetOpt // Simple command-line options parser +"Lists.fif" include + +// May be used as follows: +// begin-options +// "h" { ."Help Message" 0 halt } short-option +// "v" { parse-int =: verbosity } short-option-arg +// "i" "--interactive" { true =: interactive } short-long-option +// parse-options + +// ( l -- l') computes tail of list l if non-empty; else () +{ dup null? ' cdr ifnot } : safe-cdr +// ( l c -- l') deletes first c elements from list l +{ ' safe-cdr swap times } : list-delete-first +// ( l n c -- l' ) deletes c elements starting from n-th in list l +recursive list-delete-range { + dup 0<= { 2drop } { + over 0<= { nip list-delete-first } { + swap 1- swap rot uncons 2swap list-delete-range cons + } cond } cond +} swap ! +// ( n c -- ) deletes $n .. $(n+c-1) from the argument list $* +{ swap 1- $* @ swap rot list-delete-range $* ! } : $*del.. +// ( s s' -- ? ) checks whether s' is a prefix of s +{ tuck $len over $len over >= { $| drop $= } { 2drop drop false } cond +} : $pfx? +// ( s -- ? ) checks whether s is an option (a string beginning with '-') +{ dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt? +// ( l -- s i or 0 ) finds first string in l beginning with '-' +{ 0 { 1+ over null? { 2drop 0 true } { + swap uncons over is-opt? { drop swap true } { nip swap false } cond + } cond } until +} : list-find-opt +// ( -- s i or 0 ) finds first option in cmdline args +{ $* @ list-find-opt } : first-opt +// ( s t -- ? ) checks whether short/long option s matches description t +{ third $= } : short-option-matches +' second : get-opt-flags +' first : get-opt-exec +{ dup get-opt-flags 4 and 0= 3 + [] $= +} : long-option-matches +// ( s l -- t -1 or 0 ) finds short/long option s in list l +{ swap 1 { swap short-option-matches } does assoc-gen +} : lookup-short-option +{ swap 1 { swap long-option-matches } does assoc-gen +} : lookup-long-option +// ( s -- s' null or s' s'' ) Splits long option --opt=arg at '=' +{ dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond +} : split-longopt +// ( l -- i or 0 ) +// parses command line arguments according to option description list l +// and returns index i of first incorrect option +{ { first-opt dup 0= { true } { + swap dup "--" $pfx? { // l i s + dup $len 2 = { drop dup 1 $*del.. 0 true } { + split-longopt swap 3 pick + lookup-long-option not { drop true } { // l i s' t f + dup get-opt-exec swap get-opt-flags 3 and // l i s' e f' + 2 pick null? { dup 1 = } { dup 0= negate } cond // l i s' e f' f'' + dup 1 = { 2drop 2drop true } { + { drop nip over 1+ $() swap execute 2 $*del.. false } { + ' nip ifnot execute 1 $*del.. false + } cond } cond } cond } cond } { // l i s + 1 $| nip { + dup $len 0= { drop 1 $*del.. false true } { + 1 $| swap 3 pick // l i s' s l + lookup-short-option not { drop true true } { // l i s' t + dup get-opt-exec swap get-opt-flags 3 and // l i s' e f' + ?dup 0= { execute false } { + 2 pick $len { drop execute "" false } { + 2 = { nip null swap execute "" false } { // l i e + nip over 1+ $() swap execute 2 $*del.. false true + } cond } cond } cond } cond } cond } until + } cond + } cond } until nip +} : getopt +// ( l -- ) Parses options and throws an error on failure +{ getopt ?dup { $() "cannot parse command line options near `" swap $+ +"`" abort } if +} : run-getopt + +anon constant opt-list-marker +' opt-list-marker : begin-options +{ opt-list-marker list-until-marker } : end-options +{ end-options run-getopt } : parse-options +// ( s e -- o ) Creates short/long option s with execution token e +{ 0 rot triple } dup : short-option : long-option +// ( s s' e -- o ) Creates a combined short option s and long option s' with execution token e +{ 4 2swap 4 tuple } : short-long-option +{ 1 rot triple } dup : short-option-arg : long-option-arg +{ 2 rot triple } dup : short-option-?arg : long-option-?arg +{ 5 2swap 4 tuple } : short-long-option-arg +{ 6 2swap 4 tuple } : short-long-option-?arg diff --git a/crypto/fift/utils.cpp b/crypto/fift/utils.cpp index fdbd0c7e..3ed11c31 100644 --- a/crypto/fift/utils.cpp +++ b/crypto/fift/utils.cpp @@ -46,6 +46,9 @@ td::Result load_Lists_fif(std::string dir = "") { td::Result load_Lisp_fif(std::string dir = "") { return load_source("Lisp.fif", dir); } +td::Result load_GetOpt_fif(std::string dir = "") { + return load_source("GetOpt.fif", dir); +} class MemoryFileLoader : public fift::FileLoader { public: @@ -115,6 +118,10 @@ td::Result create_source_lookup(std::string main, bool need_ TRY_RESULT(f, load_TonUtil_fif(dir)); loader->add_file("/TonUtil.fif", std::move(f)); } + { + TRY_RESULT(f, load_GetOpt_fif(dir)); + loader->add_file("/GetOpt.fif", std::move(f)); + } } if (need_lisp) { TRY_RESULT(f, load_Lisp_fif(dir)); diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index ed703b12..7459656b 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -604,6 +604,12 @@ void interpret_str_split(vm::Stack& stack) { stack.push_string(std::string{str, sz}); } +void interpret_str_pos(vm::Stack& stack) { + auto s2 = stack.pop_string(), s1 = stack.pop_string(); + auto pos = s1.find(s2); + stack.push_smallint(pos == std::string::npos ? -1 : pos); +} + void interpret_str_reverse(vm::Stack& stack) { std::string s = stack.pop_string(); auto it = s.begin(); @@ -659,6 +665,20 @@ void interpret_utf8_str_split(vm::Stack& stack) { } } +void interpret_utf8_str_pos(vm::Stack& stack) { + auto s2 = stack.pop_string(), s1 = stack.pop_string(); + auto pos = s1.find(s2); + if (pos == std::string::npos) { + stack.push_smallint(-1); + return; + } + int cnt = 0; + for (std::size_t i = 0; i < pos; i++) { + cnt += ((s1[i] & 0xc0) != 0x80); + } + stack.push_smallint(cnt); +} + void interpret_str_remove_trailing_int(vm::Stack& stack, int arg) { char x = (char)(arg ? arg : stack.pop_long_range(127)); std::string s = stack.pop_string(); @@ -2336,12 +2356,38 @@ void interpret_db_run_vm_parallel(IntCtx& ctx) { do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, threads_n, tasks_n); } +Ref cmdline_args{true}; + +void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) { + if (!n) { + return; + } + auto v = cmdline_args->get(); + while (true) { + if (v.empty()) { + stack.push(vm::StackEntry{}); + return; + } + auto t = v.as_tuple_range(2, 2); + if (t.is_null()) { + throw IntError{"invalid cmdline arg list"}; + } + if (!--n) { + stack.push(t->at(0)); + return; + } + v = t->at(1); + } +} + // n -- executes $n void interpret_get_cmdline_arg(IntCtx& ctx) { int n = ctx.stack.pop_smallint_range(999999); - char buffer[14]; - sprintf(buffer, "$%d ", n); - auto entry = ctx.dictionary->lookup(std::string{buffer}); + if (n) { + interpret_get_fixed_cmdline_arg(ctx.stack, n); + return; + } + auto entry = ctx.dictionary->lookup("$0 "); if (!entry) { throw IntError{"-?"}; } else { @@ -2349,6 +2395,19 @@ void interpret_get_cmdline_arg(IntCtx& ctx) { } } +void interpret_get_cmdline_arg_count(vm::Stack& stack) { + auto v = cmdline_args->get(); + int cnt; + for (cnt = 0; !v.empty(); cnt++) { + auto t = v.as_tuple_range(2, 2); + if (t.is_null()) { + throw IntError{"invalid cmdline arg list"}; + } + v = t->at(1); + } + stack.push_smallint(cnt); +} + void interpret_getenv(vm::Stack& stack) { auto str = stack.pop_string(); auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr; @@ -2568,6 +2627,7 @@ void init_words_common(Dictionary& d) { d.def_stack_word("$= ", interpret_str_equal); d.def_stack_word("$cmp ", interpret_str_cmp); d.def_stack_word("$reverse ", interpret_str_reverse); + d.def_stack_word("$pos ", interpret_str_pos); d.def_stack_word("(-trailing) ", std::bind(interpret_str_remove_trailing_int, _1, 0)); d.def_stack_word("-trailing ", std::bind(interpret_str_remove_trailing_int, _1, ' ')); d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0')); @@ -2575,6 +2635,7 @@ void init_words_common(Dictionary& d) { d.def_stack_word("Blen ", interpret_bytes_len); d.def_stack_word("$Len ", interpret_utf8_str_len); d.def_stack_word("$Split ", interpret_utf8_str_split); + d.def_stack_word("$Pos ", interpret_utf8_str_pos); d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true)); d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true)); d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false)); @@ -2766,6 +2827,10 @@ void init_words_common(Dictionary& d) { d.def_ctx_word("quit ", interpret_quit); d.def_ctx_word("bye ", interpret_bye); d.def_stack_word("halt ", interpret_halt); + // cmdline args + d.def_stack_word("$* ", std::bind(interpret_literal, _1, vm::StackEntry{cmdline_args})); + d.def_stack_word("$# ", interpret_get_cmdline_arg_count); + d.def_ctx_word("$() ", interpret_get_cmdline_arg); } void init_words_ton(Dictionary& d) { @@ -2799,13 +2864,16 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con using namespace std::placeholders; LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")"; d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0})); - for (int i = 0; i < n; i++) { - char buffer[14]; - sprintf(buffer, "$%d ", i + 1); - d.def_stack_word(buffer, std::bind(interpret_literal, _1, vm::StackEntry{argv[i]})); + vm::StackEntry list; + for (int i = n - 1; i >= 0; i--) { + list = vm::StackEntry::cons(vm::StackEntry{argv[i]}, list); + } + cmdline_args->set(std::move(list)); + for (int i = 1; i <= n; i++) { + char buffer[14]; + sprintf(buffer, "$%d ", i); + d.def_stack_word(buffer, std::bind(interpret_get_fixed_cmdline_arg, _1, i)); } - d.def_stack_word("$# ", std::bind(interpret_const, _1, n)); - d.def_ctx_word("$() ", interpret_get_cmdline_arg); } std::pair numeric_value_ext(std::string s, bool allow_frac = true) { diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index c7058f50..8c453cde 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -9,10 +9,9 @@ (cell, int, int, cell) load_data() inline { var cs = get_data().begin_parse(); - var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256)); - var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict(); + var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict()); cs.end_parse(); - return (cfg_dict, stored_seqno, public_key, vote_dict); + return res; } () store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline { diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index 37257d1b..563aa7bc 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -796,7 +796,7 @@ _ participant_list() method_id { _ participant_list_extended() method_id { var elect = get_data().begin_parse().preload_dict(); if (elect.null?()) { - return nil; + return (0, 0, 0, 0, nil, 0, 0); } var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect(); var l = nil; @@ -809,7 +809,7 @@ _ participant_list_extended() method_id { l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l); } } until (~ f); - return l; + return (elect_at, elect_close, min_stake, total_stake, l, failed, finished); } ;; computes the return stake diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 4277445f..5c4de564 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -160,7 +160,7 @@ Masterchain swap // 9 4 1 config.validator_num! 1000 100 13 config.validator_num! // min-stake max-stake min-total-stake max-factor -GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits! +GR$10000 GR$10000000 GR$500000 sg~10 config.validator_stake_limits! // elected-for elect-start-before elect-end-before stakes-frozen-for // 400000 200000 4000 400000 config.election_params! // 4000 2000 500 1000 config.election_params! // DEBUG @@ -219,7 +219,7 @@ now dup orig_vset_valid_for + 0 config.validators! 0 32 u, // seqno "config-master" +suffix +".pk" load-generate-keypair drop B, - newdict dict, // vote dict + dictnew dict, // vote dict b> // data empty_cell // libraries GR$10 // balance @@ -237,8 +237,8 @@ Masterchain swap */ // pubkey amount `create-wallet1` or pubkey amount `create-wallet2` -PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100 create-wallet1 -PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$170 create-wallet0 +PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$1000000000 create-wallet1 +PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0 /* * diff --git a/crypto/smartcont/multisig-code.fc b/crypto/smartcont/multisig-code.fc index cbe2d30f..36e21a7b 100644 --- a/crypto/smartcont/multisig-code.fc +++ b/crypto/smartcont/multisig-code.fc @@ -2,21 +2,32 @@ _ unpack_state() inline_ref { var ds = begin_parse(get_data()); - var res = (ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict()); + var res = (ds~load_uint(32), ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict()); ds.end_parse(); return res; } -_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref { +_ pack_state(cell pending_queries, cell owner_infos, int last_cleaned, int k, int n, int wallet_id) inline_ref { return begin_cell() + .store_uint(wallet_id, 32) .store_uint(n, 8) .store_uint(k, 8) .store_uint(last_cleaned, 64) - .store_dict(public_keys) + .store_dict(owner_infos) .store_dict(pending_queries) .end_cell(); } +_ pack_owner_info(int public_key, int flood) inline_ref { + return begin_cell() + .store_uint(public_key, 256) + .store_uint(flood, 8); +} + +_ unpack_owner_info(slice cs) inline_ref { + return (cs~load_uint(256), cs~load_uint(8)); +} + (int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref { int cnt = 0; @@ -41,44 +52,60 @@ _ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, in return (cnt, cnt_bits); } - () recv_internal(slice in_msg) impure { ;; do nothing for internal messages } -(int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?) inline_ref { +(int, int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?, int root_i) inline_ref { if (found?) { throw_unless(35, query~load_int(1)); - (int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(n), query); + (int creator_i, int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(8), query~load_uint(n), query); throw_unless(36, slice_hash(msg) == slice_hash(in_msg)); - return (cnt, cnt_bits, msg); + return (creator_i, cnt, cnt_bits, msg); } - return (0, 0, in_msg); + return (root_i, 0, 0, in_msg); } () try_init() impure inline_ref { ;; first query without signatures is always accepted - (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state(); + (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state(); throw_if(37, last_cleaned); accept_message(); - set_data(pack_state(pending_queries, public_keys, 1, k, n)); + set_data(pack_state(pending_queries, owner_infos, 1, k, n, wallet_id)); } -cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref { +(cell, cell) update_pending_queries(cell pending_queries, cell owner_infos, slice msg, int query_id, int creator_i, int cnt, int cnt_bits, int n, int k) impure inline_ref { if (cnt >= k) { + accept_message(); while (msg.slice_refs()) { var mode = msg~load_uint(8); send_raw_message(msg~load_ref(), mode); } pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1)); + + (slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i); + (int public_key, int flood) = unpack_owner_info(owner_info); + owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1)); } else { pending_queries~udict_set_builder(64, query_id, begin_cell() .store_uint(1, 1) + .store_uint(creator_i, 8) .store_uint(cnt, 8) .store_uint(cnt_bits, n) .store_slice(msg)); } - return pending_queries; + return (pending_queries, owner_infos); +} + +(int, int) calc_boc_size(int cells, int bits, slice root) { + cells += 1; + bits += root.slice_bits(); + + while (root.slice_refs()) { + (cells, bits) = calc_boc_size(cells, bits, root~load_ref().begin_parse()); + } + + return (cells, bits); } () recv_external(slice in_msg) impure { @@ -92,44 +119,62 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c int root_hash = slice_hash(in_msg); int root_i = in_msg~load_uint(8); - (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state(); + (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state(); last_cleaned -= last_cleaned == 0; - (slice public_key, var found?) = public_keys.udict_get?(8, root_i); + (slice owner_info, var found?) = owner_infos.udict_get?(8, root_i); + (int public_key, int flood) = unpack_owner_info(owner_info); throw_unless(31, found?); - throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256))); + throw_unless(32, check_signature(root_hash, root_signature, public_key)); cell signatures = in_msg~load_dict(); var hash = slice_hash(in_msg); + int query_wallet_id = in_msg~load_uint(32); + throw_unless(42, query_wallet_id == wallet_id); + int query_id = in_msg~load_uint(64); + (int cnt, int bits) = calc_boc_size(0, 0, in_msg); + throw_if(40, (cnt > 8) | (bits > 2048)); + + (slice query, var found?) = pending_queries.udict_get?(64, query_id); + + ifnot (found?) { + flood += 1; + throw_if(39, flood > 10); + } + var bound = (now() << 32); throw_if(33, query_id < bound); - (slice query, var found?) = pending_queries.udict_get?(64, query_id); - (int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?); + (int creator_i, int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?, root_i); int mask = 1 << root_i; throw_if(34, cnt_bits & mask); cnt_bits |= mask; cnt += 1; - ;; TODO: reserve some gas or FAIL - accept_message(); + set_gas_limit(100000); - pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k); - set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n)); + ifnot (found?) { + owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood)); + throw_if(41, (cnt < k) & (bound + ((60 * 60) << 32) > query_id)); + } + + (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k); + set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id)); commit(); int need_save = 0; ifnot (cell_null?(signatures) | (cnt >= k)) { - (int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits); + (int new_cnt, cnt_bits) = check_signatures(owner_infos, signatures, hash, cnt_bits); cnt += new_cnt; - pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k); + (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k); need_save = -1; } + accept_message(); bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago int old_last_cleaned = last_cleaned; do { @@ -146,18 +191,18 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c } until (~ f); if (need_save) { - set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n)); + set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id)); } } ;; Get methods ;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten) (int, int) get_query_state(int query_id) method_id { - (int n, _, int last_cleaned, _, cell pending_queries) = unpack_state(); + (_, int n, _, int last_cleaned, _, cell pending_queries) = unpack_state(); (slice cs, var found) = pending_queries.udict_get?(64, query_id); if (found) { if (cs~load_int(1)) { - cs~load_uint(8); + cs~load_uint(8 + 8); return (0, cs~load_uint(n)); } else { return (-1, 0); @@ -172,8 +217,8 @@ int processed?(int query_id) method_id { return x; } -cell create_init_state(int n, int k, cell public_keys) method_id { - return pack_state(new_dict(), public_keys, 0, k, n); +cell create_init_state(int wallet_id, int n, int k, cell owners_info) method_id { + return pack_state(new_dict(), owners_info, 0, k, n, wallet_id); } cell merge_list(cell a, cell b) { @@ -196,7 +241,7 @@ cell merge_list(cell a, cell b) { } cell get_public_keys() method_id { - (_, _, _, cell public_keys, _) = unpack_state(); + (_, _, _, _, cell public_keys, _) = unpack_state(); return public_keys; } @@ -222,14 +267,14 @@ cell get_public_keys() method_id { } cell messages_by_mask(int mask) method_id { - (int n, _, _, _, cell pending_queries) = unpack_state(); + (_, int n, _, _, _, cell pending_queries) = unpack_state(); int i = -1; cell a = new_dict(); do { (i, var cs, var f) = pending_queries.udict_get_next?(64, i); if (f) { if (cs~load_int(1)) { - int cnt_bits = cs.skip_bits(8).preload_uint(n); + int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n); if (cnt_bits & mask) { a~udict_set_builder(64, i, begin_cell().store_slice(cs)); } @@ -248,7 +293,7 @@ cell get_messages_unsigned() method_id { } (int, int) get_n_k() method_id { - (int n, int k, _, _, _) = unpack_state(); + (_, int n, int k, _, _, _) = unpack_state(); return (n, k); } diff --git a/crypto/smartcont/show-addr.fif b/crypto/smartcont/show-addr.fif index 5d309464..51b2df8c 100755 --- a/crypto/smartcont/show-addr.fif +++ b/crypto/smartcont/show-addr.fif @@ -1,12 +1,13 @@ #!/usr/bin/fift -s "TonUtil.fif" include -{ ."usage: " @' $0 type ." " cr +{ ."usage: " $0 type ." " cr ."Shows the address of a simple wallet created by new-wallet.fif, with address in .addr " ."and private key in file .pk" cr 1 halt } : usage -def? $# { @' $# 1 > ' usage if } if -def? $1 { @' $1 } { "new-wallet" } cond constant file-base +$# 1 > ' usage if +1 :$1..n +$1 dup null? { drop "new-wallet" } if =: file-base file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B| dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index ba941670..12889a13 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -40,6 +40,7 @@ cont get_c3() impure asm "c3 PUSH"; cont bless(slice s) impure asm "BLESS"; () accept_message() impure asm "ACCEPT"; +() set_gas_limit(int limit) impure asm "SETGASLIMIT"; () commit() impure asm "COMMIT"; int min(int x, int y) asm "MIN"; diff --git a/crypto/smartcont/wallet.fif b/crypto/smartcont/wallet.fif index bdd03399..e5cf6ad4 100755 --- a/crypto/smartcont/wallet.fif +++ b/crypto/smartcont/wallet.fif @@ -1,35 +1,32 @@ #!/usr/bin/fift -s "TonUtil.fif" include +"GetOpt.fif" include { ."usage: " @' $0 type ." [-n] [-B ] [-C ] []" cr ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt } : usage + "" =: comment // comment for simple transfers true =: allow-bounce -def? $5 { @' $5 "-n" $= { false =: allow-bounce [forget] $5 - def? $6 { @' $6 =: $5 [forget] $6 } if - def? $7 { @' $7 =: $6 [forget] $7 } if - @' $# 1- =: $# - } if -} if - -def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or - { @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6 - def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond - @' $# 2- =: $# - } if -} if +3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors + +begin-options + "n" "--no-bounce" { false =: allow-bounce } short-long-option + "B" "--body" { =: body-boc-file } short-long-option-arg + "C" "--comment" { =: comment } short-long-option-arg + "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "h" "--help" { usage } short-long-option +parse-options + $# dup 4 < swap 5 > or ' usage if - -true constant bounce - +5 :$1..n +true =: bounce $1 =: file-base $2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount -def? $5 { @' $5 } { "wallet-query" } cond constant savefile -3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +$5 dup null? { drop "wallet-query" } if =: savefile // "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment file-base +".addr" load-address diff --git a/crypto/smc-envelope/MultisigWallet.cpp b/crypto/smc-envelope/MultisigWallet.cpp index 36830179..0c8b56d7 100644 --- a/crypto/smc-envelope/MultisigWallet.cpp +++ b/crypto/smc-envelope/MultisigWallet.cpp @@ -8,8 +8,13 @@ namespace ton { -MultisigWallet::QueryBuilder::QueryBuilder(td::int64 query_id, td::Ref msg, int mode) { - msg_ = vm::CellBuilder().store_long(query_id, 64).store_long(mode, 8).store_ref(std::move(msg)).finalize(); +MultisigWallet::QueryBuilder::QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref msg, int mode) { + msg_ = vm::CellBuilder() + .store_long(wallet_id, 32) + .store_long(query_id, 64) + .store_long(mode, 8) + .store_ref(std::move(msg)) + .finalize(); } void MultisigWallet::QueryBuilder::sign(td::int32 id, td::Ed25519::PrivateKey& pk) { CHECK(id < td::narrow_cast(mask_.size())); @@ -87,26 +92,29 @@ std::vector MultisigWallet::get_public_keys() const { return res; } -td::Ref MultisigWallet::create_init_data(std::vector public_keys, int k) const { +td::Ref MultisigWallet::create_init_data(td::uint32 wallet_id, std::vector public_keys, + int k) const { vm::Dictionary pk(8); for (size_t i = 0; i < public_keys.size(); i++) { auto key = pk.integer_key(td::make_refint(i), 8, false); - pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice())); + pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8)); } - auto res = run_get_method("create_init_state", - {td::make_refint(public_keys.size()), td::make_refint(k), pk.get_root_cell()}); + auto res = run_get_method("create_init_state", {td::make_refint(wallet_id), td::make_refint(public_keys.size()), + td::make_refint(k), pk.get_root_cell()}); CHECK(res.code == 0); return res.stack.write().pop_cell(); } -td::Ref MultisigWallet::create_init_data_fast(std::vector public_keys, int k) { +td::Ref MultisigWallet::create_init_data_fast(td::uint32 wallet_id, std::vector public_keys, + int k) { vm::Dictionary pk(8); for (size_t i = 0; i < public_keys.size(); i++) { auto key = pk.integer_key(td::make_refint(i), 8, false); - pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice())); + pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8)); } vm::CellBuilder cb; + cb.store_long(wallet_id, 32); cb.store_long(public_keys.size(), 8).store_long(k, 8).store_long(0, 64); cb.ensure_throw(cb.store_maybe_ref(pk.get_root_cell())); cb.ensure_throw(cb.store_maybe_ref({})); @@ -156,7 +164,7 @@ std::vector MultisigWallet::get_unsigned_messaged(int i vm::Dictionary dict(std::move(cell), 64); std::vector res; dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) { - cs.write().skip_first(8); + cs.write().skip_first(8 + 8); Message message; td::BigInt256 query_id; query_id.import_bits(ptr, ptr_bits, false); diff --git a/crypto/smc-envelope/MultisigWallet.h b/crypto/smc-envelope/MultisigWallet.h index 10c6d076..17395e47 100644 --- a/crypto/smc-envelope/MultisigWallet.h +++ b/crypto/smc-envelope/MultisigWallet.h @@ -20,7 +20,7 @@ class MultisigWallet : public ton::SmartContract { class QueryBuilder { public: - QueryBuilder(td::int64 query_id, td::Ref msg, int mode = 3); + QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref msg, int mode = 3); void sign(td::int32 id, td::Ed25519::PrivateKey& pk); td::Ref create_inner() const; @@ -42,8 +42,9 @@ class MultisigWallet : public ton::SmartContract { // creation static td::Ref create(td::Ref data = {}); - td::Ref create_init_data(std::vector public_keys, int k) const; - static td::Ref create_init_data_fast(std::vector public_keys, int k); + td::Ref create_init_data(td::uint32 wallet_id, std::vector public_keys, int k) const; + static td::Ref create_init_data_fast(td::uint32 wallet_id, std::vector public_keys, + int k); // get methods int processed(td::uint64 query_id) const; diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index aec7bed4..02964a7c 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -30,10 +30,6 @@ namespace ton { namespace { -td::int32 get_method_id(td::Slice method_name) { - unsigned crc = td::crc16(method_name); - return (crc & 0xffff) | 0x10000; -} td::Ref prepare_vm_stack(td::Ref body) { td::Ref stack_ref{true}; td::RefInt256 acc_addr{true}; diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index 4e17139b..e61cab24 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -27,11 +27,7 @@ namespace ton { namespace { const auto& get_map() { static auto map = [] { - class Cmp : public std::less<> { - public: - using is_transparent = void; - }; - std::map, Cmp> map; + std::map, std::less<>> map; auto with_tvm_code = [&](auto name, td::Slice code_str) { map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok(); }; diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index 750ed94b..0ec52f58 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -455,16 +455,17 @@ TEST(Smartcon, Multisig) { int n = 100; int k = 99; + td::uint32 wallet_id = std::numeric_limits::max() - 3; std::vector keys; for (int i = 0; i < n; i++) { keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); } auto init_state = ms_lib->create_init_data( - td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k); + wallet_id, td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k); auto ms = ton::MultisigWallet::create(init_state); - td::uint64 query_id = 123; - ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize()); + td::uint64 query_id = 123 | ((100 * 60ull) << 32); + ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize()); // first empty query (init) CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0); // first empty query @@ -491,7 +492,7 @@ TEST(Smartcon, Multisig) { ASSERT_EQ(0, ms->processed(query_id)); { - ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize()); + ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize()); for (int i = 50; i + 1 < 100; i++) { qb.sign(i, keys[i]); } @@ -507,6 +508,7 @@ TEST(Smartcon, Multisig) { TEST(Smartcont, MultisigStress) { int n = 10; int k = 5; + td::uint32 wallet_id = std::numeric_limits::max() - 3; std::vector keys; for (int i = 0; i < n; i++) { @@ -515,13 +517,14 @@ TEST(Smartcont, MultisigStress) { auto public_keys = td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }); auto ms_lib = ton::MultisigWallet::create(); auto init_state_old = - ms_lib->create_init_data_fast(td::transform(public_keys, [](auto& key) { return key.copy(); }), k); - auto init_state = ms_lib->create_init_data(td::transform(public_keys, [](auto& key) { return key.copy(); }), k); + ms_lib->create_init_data_fast(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k); + auto init_state = + ms_lib->create_init_data(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k); CHECK(init_state_old->get_hash() == init_state->get_hash()); auto ms = ton::MultisigWallet::create(init_state); CHECK(ms->get_public_keys() == public_keys); - td::int32 now = 0; + td::int32 now = 100 * 60; td::int32 qid = 1; using Mask = std::bitset<128>; struct Query { @@ -566,7 +569,7 @@ TEST(Smartcont, MultisigStress) { }; auto sign_query = [&](Query& query, Mask mask) { - auto qb = ton::MultisigWallet::QueryBuilder(query.id, query.message); + auto qb = ton::MultisigWallet::QueryBuilder(wallet_id, query.id, query.message); int first_i = -1; for (int i = 0; i < (int)mask.size(); i++) { if (mask.test(i)) { diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 5273e159..83ba6e10 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -708,6 +708,9 @@ int VmState::step() { } int VmState::run() { + if (code.is_null()) { + throw VmError{Excno::fatal, "cannot run an uninitialized VM"}; + } int res; Guard guard(this); do { diff --git a/crypto/vm/dict.h b/crypto/vm/dict.h index 11234277..9bb11be3 100644 --- a/crypto/vm/dict.h +++ b/crypto/vm/dict.h @@ -237,6 +237,11 @@ class DictionaryFixed : public DictionaryBase { Ref get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) { return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first); } + template + Ref lookup_nearest_key(T& key_buffer, bool fetch_next = false, bool allow_eq = false, + bool invert_first = false) { + return lookup_nearest_key(key_buffer.bits(), key_buffer.size(), fetch_next, allow_eq, invert_first); + } protected: virtual int label_mode() const { diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 1c7fb55c..507f98df 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -57,6 +57,11 @@ class Atom; using Tuple = td::Cnt>; +template +Ref make_tuple_ref(Args&&... args) { + return td::make_cnt_ref>(std::vector{std::forward(args)...}); +} + struct from_object_t {}; constexpr from_object_t from_object{}; @@ -192,6 +197,10 @@ class StackEntry { public: static StackEntry make_list(std::vector&& elems); static StackEntry make_list(const std::vector& elems); + template + static StackEntry cons(T1&& x, T2&& y) { + return StackEntry{make_tuple_ref(std::forward(x), std::forward(y))}; + } template static StackEntry maybe(Ref ref) { if (ref.is_null()) { @@ -268,11 +277,6 @@ inline void swap(StackEntry& se1, StackEntry& se2) { se1.swap(se2); } -template -Ref make_tuple_ref(Args&&... args) { - return td::make_cnt_ref>(std::vector{std::forward(args)...}); -} - const StackEntry& tuple_index(const Tuple& tup, unsigned idx); StackEntry tuple_extend_index(const Ref& tup, unsigned idx); unsigned tuple_extend_set_index(Ref& tup, unsigned idx, StackEntry&& value, bool force = false); diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index e2142f07..8697ddcc 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -662,12 +662,49 @@ int exec_set_code(VmState* st) { return install_output_action(st, cb.finalize()); } +int exec_set_lib_code(VmState* st) { + VM_LOG(st) << "execute SETLIBCODE"; + Stack& stack = st->get_stack(); + stack.check_underflow(2); + int mode = stack.pop_smallint_range(2); + auto code = stack.pop_cell(); + CellBuilder cb; + if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n) + && cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4 + && cb.store_long_bool(mode * 2 + 1, 8) // mode:(## 7) { mode <= 2 } + && cb.store_ref_bool(std::move(code)))) { // libref:LibRef = OutAction; + throw VmError{Excno::cell_ov, "cannot serialize new library code into an output action cell"}; + } + return install_output_action(st, cb.finalize()); +} + +int exec_change_lib(VmState* st) { + VM_LOG(st) << "execute CHANGELIB"; + Stack& stack = st->get_stack(); + stack.check_underflow(2); + int mode = stack.pop_smallint_range(2); + auto hash = stack.pop_int_finite(); + if (!hash->unsigned_fits_bits(256)) { + throw VmError{Excno::range_chk, "library hash must be non-negative"}; + } + CellBuilder cb; + if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n) + && cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4 + && cb.store_long_bool(mode * 2, 8) // mode:(## 7) { mode <= 2 } + && cb.store_int256_bool(hash, 256, false))) { // libref:LibRef = OutAction; + throw VmError{Excno::cell_ov, "cannot serialize library hash into an output action cell"}; + } + return install_output_action(st, cb.finalize()); +} + void register_ton_message_ops(OpcodeTable& cp0) { using namespace std::placeholders; cp0.insert(OpcodeInstr::mksimple(0xfb00, 16, "SENDRAWMSG", exec_send_raw_message)) .insert(OpcodeInstr::mksimple(0xfb02, 16, "RESERVERAW", std::bind(exec_reserve_raw, _1, 0))) .insert(OpcodeInstr::mksimple(0xfb03, 16, "RESERVERAWX", std::bind(exec_reserve_raw, _1, 1))) - .insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code)); + .insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code)) + .insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code)) + .insert(OpcodeInstr::mksimple(0xfb07, 16, "CHANGELIB", exec_change_lib)); } void register_ton_ops(OpcodeTable& cp0) { diff --git a/crypto/vm/utils.cpp b/crypto/vm/utils.cpp new file mode 100644 index 00000000..25e01f21 --- /dev/null +++ b/crypto/vm/utils.cpp @@ -0,0 +1,134 @@ +#include "utils.h" + +namespace vm { + +td::Result convert_stack_entry(td::Slice word); +td::Result> parse_stack_entries_in(td::Slice& str, bool prefix_only = false); +td::Result parse_stack_entry_in(td::Slice& str, bool prefix_only = false); + +namespace { + +td::Slice& skip_spaces(td::Slice& str, const char* delims) { + while (str.size() > 0 && strchr(delims, str[0])) { + str.remove_prefix(1); + } + return str; +} + +td::Slice get_word(td::Slice& str, const char* delims, const char* specials) { + skip_spaces(str, delims); + + size_t p = 0; + while (p < str.size() && !strchr(delims, str[p])) { + if (specials && strchr(specials, str[p])) { + if (!p) { + p++; + } + break; + } + p++; + } + + td::Slice ret = str.copy().truncate(p); + str.remove_prefix(p); + return ret; +} + +} // namespace + +td::Result parse_stack_entry_in(td::Slice& str, bool prefix_only) { + auto word = get_word(str, " \t", "[()]"); + if (word.empty()) { + return td::Status::Error("stack value expected instead of end-of-line"); + } + if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) { + int expected = (word[0] == '(' ? ')' : ']'); + TRY_RESULT(values, parse_stack_entries_in(str, true)); + word = get_word(str, " \t", "[()]"); + if (word.size() != 1 || word[0] != expected) { + return td::Status::Error("closing bracket expected"); + } + vm::StackEntry value; + if (expected == ']') { + value = vm::StackEntry{std::move(values)}; + } else { + value = vm::StackEntry::make_list(std::move(values)); + } + if (prefix_only || (skip_spaces(str, " \t").size() == 0)) { + return value; + } else { + return td::Status::Error("extra data at the end"); + } + } else { + return convert_stack_entry(word); + } +} + +td::Result convert_stack_entry(td::Slice str) { + if (str.empty() || str.size() > 65535) { + return td::Status::Error("too long string"); + } + 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("incomplete (or too long) string"); + } + return vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())}; + } + 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 raw b{...}/x{...} number"); + } + return vm::StackEntry{ + Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}}; + } + 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 0x... hex 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 -0x... hex number"); + } + x.negate().normalize(); + } else if (!l || x.parse_dec(str.data(), l) != l) { + return td::Status::Error("failed to parse dec number"); + } + return vm::StackEntry{std::move(num)}; +} + +td::Result> parse_stack_entries_in(td::Slice& str, bool prefix_only) { + std::vector ret; + while (!skip_spaces(str, " \t").empty()) { + auto c = str.copy(); + auto word = get_word(c, " \t", "[()]"); + if (word == "]" || word == ")") { + if (prefix_only) { + return ret; + } else { + return td::Status::Error("not paired closing bracket"); + } + } + TRY_RESULT(value, parse_stack_entry_in(str, true)); + ret.push_back(std::move(value)); + } + return ret; +} + +td::Result> parse_stack_entries(td::Slice str, bool prefix_only) { + return parse_stack_entries_in(str, prefix_only); +} + +td::Result parse_stack_entry(td::Slice str, bool prefix_only) { + return parse_stack_entry_in(str, prefix_only); +} + +} // namespace vm diff --git a/crypto/vm/utils.h b/crypto/vm/utils.h new file mode 100644 index 00000000..e1afe60c --- /dev/null +++ b/crypto/vm/utils.h @@ -0,0 +1,11 @@ +#pragma once +#include "stack.hpp" + +#include + +namespace vm { + +td::Result> parse_stack_entries(td::Slice str, bool prefix_only = false); +td::Result parse_stack_entry(td::Slice str, bool prefix_only = false); + +} // namespace vm diff --git a/doc/fiftbase.tex b/doc/fiftbase.tex index a175f3a4..d39bf3fc 100644 --- a/doc/fiftbase.tex +++ b/doc/fiftbase.tex @@ -1848,6 +1848,7 @@ For example, the active prefix word {\tt B\{}, used for defining {\em Bytes\/} l \item {\tt \$@?+} ($s$ $x$ -- $S$ $s'$ $-1$ or $s$ $0$), similar to {\tt \$@+}, but uses a flag to indicate failure instead of throwing an exception, cf.~\ptref{p:slice.ops}. \item {\tt \$cmp} ($S$ $S'$ -- $x$), returns $0$ if strings $S$ and $S'$ are equal, $-1$ if $S$ is lexicographically less than $S'$, and $1$ if $S$ is lexicographically greater than $S'$, cf.~\ptref{p:string.cmp.ops}. \item {\tt \$len} ($S$ -- $x$), computes the byte length (not the UTF-8 character length!) of a string, cf.~\ptref{p:string.ops}. +\item {\tt \$pos} ($S$ $S'$ -- $x$ or $-1$), returns the position (byte offset)~$x$ of the first occurence of substring $S'$ in string~$S$ or $-1$. \item {\tt \$reverse} ($S$ -- $S'$), reverses the order of UTF-8 characters in {\em String\/}~$S$. If $S$ is not a valid UTF-8 string, the return value is undefined and may be also invalid. \item {\tt \%1<{<}} ($x$ $y$ -- $z$), computes $z:=x\bmod 2^y=x\&(2^y-1)$ for two {\em Integer\/}s $x$ and $0\leq y\leq 256$. \item {\tt \underline{'} $\langle\textit{word-name}\rangle$} ( -- $e$), returns the execution token equal to the current (compile-time) definition of $\langle\textit{word-name}\rangle$, cf.~\ptref{p:blocks}. If the specified word is not found, throws an exception. diff --git a/doc/tvm.tex b/doc/tvm.tex index 26f7df24..73a2145b 100644 --- a/doc/tvm.tex +++ b/doc/tvm.tex @@ -2267,7 +2267,9 @@ The following primitives, which use the above conventions, are defined: \item {\tt FB02} --- {\tt RAWRESERVE} ($x$ $y$ -- ), creates an output action which would reserve exactly $x$ nanograms (if $y=0$), at most $x$ nanograms (if $y=2$), or all but $x$ nanograms (if $y=1$ or $y=3$), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying $x$ nanograms (or $b-x$ nanograms, where $b$ is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit $+2$ in $y$ means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Currently $x$ must be a non-negative integer, and $y$ must be in the range $0\ldots 3$. \item {\tt FB03} --- {\tt RAWRESERVEX} ($s$ $y$ -- ), similar to {\tt RAWRESERVE}, but accepts a {\em Slice $s$} with a {\em CurrencyCollection\/} as an argument. In this way currencies other than Grams can be reserved. \item {\tt FB04} --- {\tt SETCODE} ($c$ -- ), creates an output action that would change this smart contract code to that given by {\em Cell\/}~$c$. Notice that this change will take effect only after the successful termination of the current run of the smart contract. -\item {\tt FB05}--{\tt FB3F} --- Reserved for output action primitives. +\item {\tt FB06} --- {\tt SETLIBCODE} ($c$ $x$ -- ), creates an output action that would modify the collection of this smart contract libraries by adding or removing library with code given in {\em Cell\/}~$c$. If $x=0$, the library is actually removed if it was previously present in the collection (if not, this action does nothing). If $x=1$, the library is added as a private library, and if $x=2$, the library is added as a public library (and becomes available to all smart contracts if the current smart contract resides in the masterchain); if the library was present in the collection before, its public/private status is changed according to $x$. Values of $x$ other than $0\ldots 2$ are invalid. +\item {\tt FB07} --- {\tt CHANGELIB} ($h$ $x$ -- ), creates an output action similarly to {\tt SETLIBCODE}, but instead of the library code accepts its hash as an unsigned 256-bit integer $h$. If $x\neq0$ and the library with hash $h$ is absent from the library collection of this smart contract, this output action will fail. +\item {\tt FB08}--{\tt FB3F} --- Reserved for output action primitives. \end{itemize} \mysubsection{Debug primitives}\label{p:prim.debug} diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index a4d3af51..d3041193 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -59,6 +59,7 @@ #include "vm/cp0.h" #include "ton/ton-shard.h" #include "openssl/rand.hpp" +#include "crypto/vm/utils.h" #if TD_DARWIN || TD_LINUX #include @@ -756,91 +757,6 @@ bool TestNode::parse_shard_id(ton::ShardIdFull& shard) { return convert_shard_id(get_word(), shard) || set_error("cannot parse full shard identifier or prefix"); } -bool TestNode::parse_stack_value(td::Slice str, vm::StackEntry& value) { - if (str.empty() || str.size() > 65535) { - return false; - } - 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 false; - } - value = vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())}; - return true; - } - 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 false; - } - value = - vm::StackEntry{Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}}; - return true; - } - 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 false; - } - } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') { - if (x.parse_hex(str.data() + 3, l - 3) != l - 3) { - return false; - } - x.negate().normalize(); - } else if (!l || x.parse_dec(str.data(), l) != l) { - return false; - } - value = vm::StackEntry{std::move(num)}; - return true; -} - -bool TestNode::parse_stack_value(vm::StackEntry& value) { - auto word = get_word_ext(" \t", "[()]"); - if (word.empty()) { - return set_error("stack value expected instead of end-of-line"); - } - if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) { - int expected = (word[0] == '(' ? ')' : ']'); - std::vector values; - if (!parse_stack_values(values)) { - return false; - } - word = get_word_ext(" \t", "[()]"); - if (word.size() != 1 || word[0] != expected) { - return set_error("closing bracket expected"); - } - if (expected == ']') { - value = vm::StackEntry{std::move(values)}; - } else { - value = vm::StackEntry::make_list(std::move(values)); - } - return true; - } else { - return parse_stack_value(word, value) || set_error("invalid vm stack value"); - } -} - -bool TestNode::parse_stack_values(std::vector& values) { - values.clear(); - while (!seekeoln()) { - if (cur() == ']' || cur() == ')') { - break; - } - values.emplace_back(); - if (!parse_stack_value(values.back())) { - values.pop_back(); - return false; - } - } - return true; -} - bool TestNode::set_error(std::string err_msg) { return set_error(td::Status::Error(-1, err_msg)); } @@ -912,6 +828,11 @@ bool TestNode::show_help(std::string command) { "header\n" "byutime \tLooks up a block by workchain, shard and creation time, and " "shows its header\n" + "creatorstats [ []]\tLists block creator statistics by validator public " + "key\n" + "recentcreatorstats [ []]\tLists block creator statistics " + "updated after by validator public " + "key\n" "known\tShows the list of all known block ids\n" "privkey \tLoads a private key from file\n" "help []\tThis help\n" @@ -999,6 +920,12 @@ bool TestNode::do_parse_line() { return parse_shard_id(shard) && parse_uint32(utime) && seekeoln() && lookup_block(shard, 4, utime); } else if (word == "bylt") { return parse_shard_id(shard) && parse_lt(lt) && seekeoln() && lookup_block(shard, 2, lt); + } else if (word == "creatorstats" || word == "recentcreatorstats") { + count = 1000; + int mode = (word == "recentcreatorstats" ? 4 : 0); + return parse_block_id_ext(blkid) && (!mode || parse_uint32(utime)) && (seekeoln() || parse_uint32(count)) && + (seekeoln() || (parse_hash(hash) && (mode |= 1))) && seekeoln() && + get_creator_stats(blkid, mode, count, hash, utime); } else if (word == "known") { return eoln() && show_new_blkids(true); } else if (word == "quit" && eoln()) { @@ -1090,13 +1017,12 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid, std::string method_name) { - std::vector params; - if (!parse_stack_values(params)) { - return set_error("cannot parse list of TVM stack values"); - } - if (!seekeoln()) { - return set_error("extra characters after a list of TVM stack values"); + auto R = vm::parse_stack_entries(td::Slice(parse_ptr_, parse_end_)); + if (R.is_error()) { + return set_error(R.move_as_error().to_string()); } + parse_ptr_ = parse_end_; + auto params = R.move_as_ok(); if (!ref_blkid.is_valid()) { return set_error("must obtain last block information before making other queries"); } @@ -2236,6 +2162,107 @@ void TestNode::got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mod show_new_blkids(); } +bool TestNode::get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after, + ton::UnixTime min_utime) { + if (!(ready_ && !client_.empty())) { + return set_error("server connection not ready"); + } + if (!blkid.is_masterchain_ext()) { + return set_error("only masterchain blocks contain block creator statistics"); + } + if (!(mode & 1)) { + start_after.set_zero(); + } + auto b = ton::serialize_tl_object(ton::create_tl_object( + mode, ton::create_tl_lite_block_id(blkid), req_count, start_after, min_utime), + true); + LOG(INFO) << "requesting up to " << req_count << " block creator stats records with respect to masterchain block " + << blkid.to_str() << " starting from validator public key " << start_after.to_hex() << " created after " + << min_utime << " (mode=" << mode << ")"; + return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, blkid, req_count, start_after, + min_utime ](td::Result R) { + if (R.is_error()) { + return; + } + auto F = ton::fetch_tl_object(R.move_as_ok(), true); + if (F.is_error()) { + LOG(ERROR) << "cannot parse answer to liteServer.getValidatorStats"; + } else { + auto f = F.move_as_ok(); + td::actor::send_closure_later(Self, &TestNode::got_creator_stats, blkid, ton::create_block_id(f->id_), mode, + f->mode_, start_after, min_utime, std::move(f->state_proof_), + std::move(f->data_proof_), f->count_, req_count, f->complete_); + } + }); +} + +void TestNode::got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode, + td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof, + td::BufferSlice data_proof, int count, int req_count, bool complete) { + LOG(INFO) << "got answer to getValidatorStats query: " << count << " records out of " << req_count << ", " + << (complete ? "complete" : "incomplete"); + if (!blkid.is_masterchain_ext()) { + LOG(ERROR) << "reference block " << blkid.to_str() + << " for block creator statistics is not a valid masterchain block"; + return; + } + if (count > req_count) { + LOG(ERROR) << "obtained " << count << " answers to getValidatorStats query, but only " << req_count + << " were requested"; + return; + } + if (blkid != req_blkid) { + LOG(ERROR) << "answer to getValidatorStats refers to masterchain block " << blkid.to_str() + << " different from requested " << req_blkid.to_str(); + return; + } + auto R = block::check_extract_state_proof(blkid, state_proof.as_slice(), data_proof.as_slice()); + if (R.is_error()) { + LOG(ERROR) << "masterchain state proof for " << blkid.to_str() << " is invalid : " << R.move_as_error().to_string(); + return; + } + bool allow_eq = (mode & 3) != 1; + ton::Bits256 key{start_after}; + std::ostringstream os; + try { + auto dict = block::get_block_create_stats_dict(R.move_as_ok()); + if (!dict) { + LOG(ERROR) << "cannot extract BlockCreateStats from mc state"; + return; + } + for (int i = 0; i < count + (int)complete; i++) { + auto v = dict->lookup_nearest_key(key, true, allow_eq); + if (v.is_null()) { + if (i != count) { + LOG(ERROR) << "could fetch only " << i << " CreatorStats entries out of " << count + << " declared in answer to getValidatorStats"; + return; + } + break; + } + block::DiscountedCounter mc_cnt, shard_cnt; + if (!block::unpack_CreatorStats(std::move(v), mc_cnt, shard_cnt)) { + LOG(ERROR) << "invalid CreatorStats record with key " << key.to_hex(); + return; + } + if (mc_cnt.modified_since(min_utime) || shard_cnt.modified_since(min_utime)) { + os << key.to_hex() << " mc_cnt:" << mc_cnt << " shard_cnt:" << shard_cnt << std::endl; + } + allow_eq = false; + } + if (complete) { + os << "(complete)" << std::endl; + } else { + os << "(incomplete, repeat query from " << key.to_hex() << " )" << std::endl; + } + td::TerminalIO::out() << os.str(); + } catch (vm::VmError& err) { + LOG(ERROR) << "error while traversing block creator stats: " << err.get_msg(); + } catch (vm::VmVirtError& err) { + LOG(ERROR) << "virtualization error while traversing block creator stats: " << err.get_msg(); + } +} + int main(int argc, char* argv[]) { SET_VERBOSITY_LEVEL(verbosity_INFO); td::set_default_failure_signal_handler(); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index bb4f31df..f016c96e 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -153,6 +153,12 @@ class TestNode : public td::actor::Actor { std::vector trans, td::BufferSlice proof); bool get_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode); void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res); + bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after, + ton::UnixTime min_utime); + void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode, + td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof, + td::BufferSlice data_proof, int count, int req_count, bool complete); + // parser bool do_parse_line(); bool show_help(std::string command); td::Slice get_word(char delim = ' '); diff --git a/tddb/td/db/MemoryKeyValue.h b/tddb/td/db/MemoryKeyValue.h index f2c261fe..f2c04713 100644 --- a/tddb/td/db/MemoryKeyValue.h +++ b/tddb/td/db/MemoryKeyValue.h @@ -38,11 +38,7 @@ class MemoryKeyValue : public KeyValue { std::string stats() const override; private: - class Cmp : public std::less<> { - public: - using is_transparent = void; - }; - std::map map_; + std::map> map_; int64 get_count_{0}; }; } // namespace td diff --git a/test/test-adnl.cpp b/test/test-adnl.cpp index 88758fc7..1b415644 100644 --- a/test/test-adnl.cpp +++ b/test/test-adnl.cpp @@ -42,6 +42,7 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-catchain.cpp b/test/test-catchain.cpp index 49a1c196..0e9bd207 100644 --- a/test/test-catchain.cpp +++ b/test/test-catchain.cpp @@ -217,6 +217,7 @@ int main(int argc, char *argv[]) { td::set_default_failure_signal_handler().ensure(); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-dht.cpp b/test/test-dht.cpp index 8bbb8d18..61a49fe6 100644 --- a/test/test-dht.cpp +++ b/test/test-dht.cpp @@ -42,6 +42,7 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-rldp.cpp b/test/test-rldp.cpp index 90d7ba04..0d1730d3 100644 --- a/test/test-rldp.cpp +++ b/test/test-rldp.cpp @@ -41,6 +41,7 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-validator-session-state.cpp b/test/test-validator-session-state.cpp index 877347bb..5c28380e 100644 --- a/test/test-validator-session-state.cpp +++ b/test/test-validator-session-state.cpp @@ -318,8 +318,8 @@ int main() { CHECK(!found); auto vec = s->choose_blocks_to_approve(desc, i); CHECK(vec.size() == 1); - CHECK(vec[1] == nullptr); - CHECK(ton::validatorsession::SentBlock::get_block_id(vec[1]) == ton::validatorsession::skip_round_candidate_id()); + CHECK(vec[0] == nullptr); + CHECK(ton::validatorsession::SentBlock::get_block_id(vec[0]) == ton::validatorsession::skip_round_candidate_id()); } for (td::uint32 i = 0; i < total_nodes; i++) { diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index 9604624a..0dd3fa22 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -48,6 +48,7 @@ liteServer.blockLinkBack to_key_block:Bool from:tonNode.blockIdExt to:tonNode.bl liteServer.blockLinkForward to_key_block:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt dest_proof:bytes config_proof:bytes signatures:liteServer.SignatureSet = liteServer.BlockLink; liteServer.partialBlockProof complete:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt steps:(vector liteServer.BlockLink) = liteServer.PartialBlockProof; 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.debug.verbosity value:int = liteServer.debug.Verbosity; @@ -71,6 +72,7 @@ liteServer.listBlockTransactions id:tonNode.blockIdExt mode:# count:# after:mode liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; 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.queryPrefix = Object; liteServer.query data:bytes = Object; diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 9b01c0d8..02b507f4 100644 Binary files a/tl/generate/scheme/lite_api.tlo and b/tl/generate/scheme/lite_api.tlo differ diff --git a/ton/ton-types.h b/ton/ton-types.h index d4c7e33c..4743253a 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -25,6 +25,8 @@ #include "td/utils/UInt.h" #include "td/utils/misc.h" +#include + namespace ton { using WorkchainId = td::int32; @@ -141,7 +143,7 @@ struct AccountIdPrefixFull { std::string to_str() const { char buffer[64]; return std::string{ - buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, (unsigned long long)account_id_prefix)}; + buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, static_cast(account_id_prefix))}; } }; @@ -204,8 +206,8 @@ struct BlockId { } std::string to_str() const { char buffer[64]; - return std::string{buffer, - (unsigned)snprintf(buffer, 63, "(%d,%016llx,%u)", workchain, (unsigned long long)shard, seqno)}; + return std::string{buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx,%u)", workchain, + static_cast(shard), seqno)}; } }; @@ -282,7 +284,7 @@ struct BlockIdExt { BlockIdExt v; char rh[65]; char fh[65]; - auto r = sscanf(s.begin(), "(%d,%lx,%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); + auto r = sscanf(s.begin(), "(%d,%" SCNu64 ",%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); if (r < 5) { return td::Status::Error("failed to parse block id"); } diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 65c6f6f8..0e25dd66 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -451,6 +451,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { int n = 16; int k = 10; + td::uint32 wallet_id = 7; std::vector private_keys; for (int i = 0; i < n; i++) { private_keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); @@ -458,6 +459,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { auto ms = ton::MultisigWallet::create(); auto init_data = ms->create_init_data( + wallet_id, td::transform(private_keys, [](const auto& pk) { return pk.get_public_key().move_as_ok().as_octet_string(); }), k); ms = ton::MultisigWallet::create(init_data); @@ -472,7 +474,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { 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()); + ton::MultisigWallet::QueryBuilder qb(wallet_id, -1 - i, icb.finalize()); for (int i = 0; i < k - 1; i++) { qb.sign(i, private_keys[i]); } diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index ccf9a383..5943ea47 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -106,11 +106,7 @@ class KeyValueInmemory : public KeyValue { } private: - class Cmp : public std::less<> { - public: - using is_transparent = void; - }; - std::map map_; + std::map> map_; }; } // namespace detail diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 542ecf00..c110abbe 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -648,12 +648,13 @@ td::Result Config::config_del_gc(ton::PublicKeyHash key) { class ValidatorElectionBidCreator : public td::actor::Actor { public: ValidatorElectionBidCreator(td::uint32 date, std::string addr, std::string wallet, std::string dir, - td::actor::ActorId engine, + std::vector old_keys, td::actor::ActorId engine, td::actor::ActorId keyring, td::Promise promise) : date_(date) , addr_(addr) , wallet_(wallet) , dir_(dir) + , old_keys_(std::move(old_keys)) , engine_(engine) , keyring_(keyring) , promise_(std::move(promise)) { @@ -661,6 +662,22 @@ class ValidatorElectionBidCreator : public td::actor::Actor { } void start_up() override { + if (old_keys_.size() > 0) { + CHECK(old_keys_.size() == 3); + + adnl_addr_ = ton::adnl::AdnlNodeIdShort{old_keys_[2]}; + perm_key_ = old_keys_[0]; + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::got_perm_public_key, R.move_as_ok()); + } + }); + td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, perm_key_, std::move(P)); + return; + } auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; perm_key_full_ = pk1.compute_public_key(); perm_key_ = perm_key_full_.compute_short_id(); @@ -712,6 +729,11 @@ class ValidatorElectionBidCreator : public td::actor::Actor { ttl_, ig.get_promise()); } + void got_perm_public_key(ton::PublicKey pub) { + perm_key_full_ = pub; + updated_config(); + } + void updated_config() { auto codeR = td::read_file_str(dir_ + "/validator-elect-req.fif"); if (codeR.is_error()) { @@ -792,6 +814,7 @@ class ValidatorElectionBidCreator : public td::actor::Actor { std::string addr_; std::string wallet_; std::string dir_; + std::vector old_keys_; td::actor::ActorId engine_; td::actor::ActorId keyring_; @@ -899,28 +922,24 @@ void ValidatorEngine::alarm() { if (state_.not_null()) { bool need_write = false; + auto configR = state_->get_config_holder(); + configR.ensure(); + auto config = configR.move_as_ok(); + auto cur_t = config->get_validator_set_start_stop(0); + CHECK(cur_t.first > 0); + LOG(ERROR) << "curt: " << cur_t.first << " " << cur_t.second; + auto val_set = state_->get_total_validator_set(0); auto e = val_set->export_vector(); - std::set adnl_ids; - for (auto &el : e) { - adnl_ids.insert(ton::PublicKeyHash{el.addr}); - } std::set to_del; for (auto &val : config_.validators) { - if (val.second.expire_at < state_->get_unix_time() && - !val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) { + bool is_validator = false; + if (val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) { + is_validator = true; + } + if (!is_validator && val.second.election_date < cur_t.first && cur_t.first + 600 < state_->get_unix_time()) { to_del.insert(val.first); - } else { - std::set to_del_2; - for (auto &x : val.second.adnl_ids) { - if (x.second < state_->get_unix_time() && !adnl_ids.count(x.first)) { - to_del_2.insert(x.first); - } - } - for (auto &x : to_del_2) { - config_.config_del_validator_temp_key(val.first, x); - need_write = true; - } + continue; } } for (auto &x : to_del) { @@ -2699,9 +2718,23 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_createEle return; } + std::vector v; + for (auto &x : config_.validators) { + if (x.second.election_date == static_cast(query.election_date_)) { + if (x.second.temp_keys.size() == 0 || x.second.adnl_ids.size() == 0) { + promise.set_value( + create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "prev bid is partial"))); + return; + } + v.push_back(x.first); + v.push_back(x.second.temp_keys.begin()->first); + v.push_back(x.second.adnl_ids.begin()->first); + } + } + td::actor::create_actor("bidcreate", query.election_date_, query.election_addr_, - query.wallet_, fift_dir_, actor_id(this), keyring_.get(), - std::move(promise)) + query.wallet_, fift_dir_, std::move(v), actor_id(this), + keyring_.get(), std::move(promise)) .release(); } diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 7eef7bbc..fe666313 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -45,9 +45,11 @@ void ArchiveManager::add_handle(BlockHandle handle, td::Promise promis update_handle(std::move(handle), std::move(promise)); return; } - auto p = get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(), - handle->unix_time(), handle->logical_time(), - handle->inited_is_key_block() && handle->is_key_block()); + auto p = handle->id().is_masterchain() + ? get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(), + handle->unix_time(), handle->logical_time(), + handle->inited_is_key_block() && handle->is_key_block()) + : get_package_id(handle->masterchain_ref_block()); auto f = get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_handle, std::move(handle), std::move(promise)); @@ -248,7 +250,7 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(P)); } -void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise promise) { +void ArchiveManager::get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise promise) { if (handle->moved_to_archive()) { auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false); if (f) { @@ -368,27 +370,53 @@ void ArchiveManager::check_persistent_state(BlockIdExt block_id, BlockIdExt mast } void ArchiveManager::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, - td::Promise promise) { + td::Promise promise) { auto f = get_file_desc_by_unix_time(account_id, ts, false); if (f) { - td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts, - std::move(promise)); + auto n = get_next_file_desc(f); + td::actor::ActorId aid; + if (n) { + aid = n->file_actor_id(); + } + auto P = td::PromiseCreator::lambda( + [aid, account_id, ts, promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok() || R.error().code() != ErrorCode::notready || aid.empty()) { + promise.set_result(std::move(R)); + } else { + td::actor::send_closure(aid, &ArchiveSlice::get_block_by_unix_time, account_id, ts, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts, std::move(P)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "ts not in db")); } } -void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { +void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, + td::Promise promise) { auto f = get_file_desc_by_lt(account_id, lt, false); if (f) { - td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise)); + auto n = get_next_file_desc(f); + td::actor::ActorId aid; + if (n) { + aid = n->file_actor_id(); + } + auto P = td::PromiseCreator::lambda( + [aid, account_id, lt, promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok() || R.error().code() != ErrorCode::notready || aid.empty()) { + promise.set_result(std::move(R)); + } else { + td::actor::send_closure(aid, &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(P)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "lt not in db")); } } void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { auto f = get_file_desc_by_seqno(account_id, seqno, false); if (f) { td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_seqno, account_id, seqno, @@ -398,7 +426,7 @@ void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeq } } -void ArchiveManager::delete_package(PackageId id) { +void ArchiveManager::delete_package(PackageId id, td::Promise promise) { auto key = create_serialize_tl_object(id.id, id.key, id.temp); std::string value; @@ -411,24 +439,27 @@ void ArchiveManager::delete_package(PackageId id) { auto x = R.move_as_ok(); if (x->deleted_) { + promise.set_value(td::Unit()); return; } auto &m = get_file_map(id); auto it = m.find(id); if (it == m.end() || it->second.deleted) { + promise.set_value(td::Unit()); return; } it->second.deleted = true; - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result R) { - R.ensure(); - td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id); - }); + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), id, promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id, std::move(promise)); + }); td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::destroy, std::move(P)); } -void ArchiveManager::deleted_package(PackageId id) { +void ArchiveManager::deleted_package(PackageId id, td::Promise promise) { auto key = create_serialize_tl_object(id.id, id.key, id.temp); std::string value; @@ -441,6 +472,7 @@ void ArchiveManager::deleted_package(PackageId id) { auto x = R.move_as_ok(); if (x->deleted_) { + promise.set_value(td::Unit()); return; } x->deleted_ = true; @@ -453,6 +485,7 @@ void ArchiveManager::deleted_package(PackageId id) { CHECK(it != m.end()); CHECK(it->second.deleted); it->second.clear_actor_id(); + promise.set_value(td::Unit()); } void ArchiveManager::load_package(PackageId id) { @@ -690,6 +723,18 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(AccountIdPr return nullptr; } +ArchiveManager::FileDescription *ArchiveManager::get_next_file_desc(FileDescription *f) { + auto &m = get_file_map(f->id); + auto it = m.find(f->id); + CHECK(it != m.end()); + it++; + if (it == m.end()) { + return nullptr; + } else { + return &it->second; + } +} + ArchiveManager::FileDescription *ArchiveManager::get_temp_file_desc_by_idx(PackageId idx) { auto it = temp_files_.find(idx); if (it != temp_files_.end()) { @@ -797,7 +842,7 @@ void ArchiveManager::run_gc(UnixTime ts) { vec.resize(vec.size() - 1, PackageId::empty(false, true)); for (auto &x : vec) { - delete_package(x); + delete_package(x, [](td::Unit) {}); } } @@ -841,7 +886,7 @@ void ArchiveManager::persistent_state_gc(FileHash last) { return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, nullptr, hash); } else { @@ -852,7 +897,7 @@ void ArchiveManager::persistent_state_gc(FileHash last) { get_block_by_seqno(AccountIdPrefixFull{masterchainId, 0}, seqno, std::move(P)); } -void ArchiveManager::got_gc_masterchain_handle(BlockHandle handle, FileHash hash) { +void ArchiveManager::got_gc_masterchain_handle(ConstBlockHandle handle, FileHash hash) { bool to_del = false; if (!handle || !handle->inited_unix_time() || !handle->unix_time()) { to_del = true; @@ -881,7 +926,7 @@ PackageId ArchiveManager::get_temp_package_id_by_unixtime(UnixTime ts) const { } PackageId ArchiveManager::get_key_package_id(BlockSeqno seqno) const { - return PackageId{seqno - seqno % 200000, true, false}; + return PackageId{seqno - seqno % key_archive_size(), true, false}; } PackageId ArchiveManager::get_package_id(BlockSeqno seqno) const { @@ -896,7 +941,7 @@ PackageId ArchiveManager::get_package_id_force(BlockSeqno masterchain_seqno, Sha PackageId p = PackageId::empty(false, false); if (!is_key) { auto it = files_.upper_bound(PackageId{masterchain_seqno, false, false}); - p = PackageId{masterchain_seqno - (masterchain_seqno % 20000), false, false}; + p = PackageId{masterchain_seqno - (masterchain_seqno % archive_size()), false, false}; if (it != files_.begin()) { it--; if (p < it->first) { @@ -980,6 +1025,7 @@ void ArchiveManager::set_async_mode(bool mode, td::Promise promise) { } } } + } // namespace validator } // namespace ton diff --git a/validator/db/archive-manager.hpp b/validator/db/archive-manager.hpp index fc073b6b..2f6e4752 100644 --- a/validator/db/archive-manager.hpp +++ b/validator/db/archive-manager.hpp @@ -48,7 +48,7 @@ class ArchiveManager : public td::actor::Actor { void get_key_block_proof(FileReference ref_id, td::Promise promise); void get_temp_file_short(FileReference ref_id, td::Promise promise); void get_file_short(FileReference ref_id, td::Promise promise); - void get_file(BlockHandle handle, FileReference ref_id, td::Promise promise); + void get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise promise); void add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); void add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data, @@ -60,12 +60,15 @@ class ArchiveManager : public td::actor::Actor { void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); void check_zero_state(BlockIdExt block_id, td::Promise promise); + //void truncate(BlockSeqno masterchain_seqno, td::Promise promise); + //void truncate_continue(BlockSeqno masterchain_seqno, td::Promise promise); + void run_gc(UnixTime ts); /* from LTDB */ - void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); - void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); - void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); + void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); + void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise); void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, @@ -77,6 +80,13 @@ class ArchiveManager : public td::actor::Actor { void commit_transaction(); void set_async_mode(bool mode, td::Promise promise); + static constexpr td::uint32 archive_size() { + return 20000; + } + static constexpr td::uint32 key_archive_size() { + return 200000; + } + private: struct FileDescription { struct Desc { @@ -113,8 +123,8 @@ class ArchiveManager : public td::actor::Actor { std::map perm_states_; void load_package(PackageId seqno); - void delete_package(PackageId seqno); - void deleted_package(PackageId seqno); + void delete_package(PackageId seqno, td::Promise promise); + void deleted_package(PackageId seqno, td::Promise promise); void get_handle_cont(BlockIdExt block_id, PackageId id, td::Promise promise); void get_handle_finish(BlockHandle handle, td::Promise promise); void get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise); @@ -129,6 +139,7 @@ class ArchiveManager : public td::actor::Actor { FileDescription *get_file_desc_by_seqno(AccountIdPrefixFull shard, BlockSeqno seqno, bool key_block); FileDescription *get_file_desc_by_lt(AccountIdPrefixFull shard, LogicalTime lt, bool key_block); FileDescription *get_file_desc_by_unix_time(AccountIdPrefixFull shard, UnixTime ts, bool key_block); + FileDescription *get_next_file_desc(FileDescription *f); FileDescription *get_temp_file_desc_by_idx(PackageId idx); PackageId get_max_temp_file_desc_idx(); PackageId get_prev_temp_file_desc_idx(PackageId id); @@ -136,7 +147,7 @@ class ArchiveManager : public td::actor::Actor { void written_perm_state(FileReferenceShort id); void persistent_state_gc(FileHash last); - void got_gc_masterchain_handle(BlockHandle handle, FileHash hash); + void got_gc_masterchain_handle(ConstBlockHandle handle, FileHash hash); std::string db_root_; diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp index b623415a..ceccfc4c 100644 --- a/validator/db/archive-slice.cpp +++ b/validator/db/archive-slice.cpp @@ -204,6 +204,28 @@ void ArchiveSlice::get_handle(BlockIdExt block_id, td::Promise prom promise.set_value(std::move(handle)); } +void ArchiveSlice::get_temp_handle(BlockIdExt block_id, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + CHECK(!key_blocks_only_); + std::string value; + auto R = kv_->get(get_db_key_block_info(block_id), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_error(td::Status::Error(ErrorCode::notready, "handle not in archive slice")); + return; + } + auto E = create_block_handle(td::BufferSlice{value}); + E.ensure(); + auto handle = E.move_as_ok(); + if (!temp_) { + handle->set_handle_moved_to_archive(); + } + promise.set_value(std::move(handle)); +} + void ArchiveSlice::get_file(FileReference ref_id, td::Promise promise) { if (destroyed_) { promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); @@ -231,7 +253,7 @@ void ArchiveSlice::get_file(FileReference ref_id, td::Promise p void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, std::function compare_desc, std::function compare, bool exact, - td::Promise promise) { + td::Promise promise) { if (destroyed_) { promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); return; @@ -281,7 +303,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, lseq = create_block_id(e->id_); l = x; } else { - get_handle(create_block_id(e->id_), std::move(promise)); + get_temp_handle(create_block_id(e->id_), std::move(promise)); return; } } @@ -299,7 +321,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, } if (block_id.is_valid() && ls + 1 == block_id.id.seqno) { if (!exact) { - get_handle(block_id, std::move(promise)); + get_temp_handle(block_id, std::move(promise)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); } @@ -307,13 +329,14 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, } } if (!exact && block_id.is_valid()) { - get_handle(block_id, std::move(promise)); + get_temp_handle(block_id, std::move(promise)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); } } -void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { +void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, + td::Promise promise) { return get_block_common( account_id, [lt](ton_api::db_lt_desc_value &w) { @@ -326,7 +349,7 @@ void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime l } void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { return get_block_common( account_id, [seqno](ton_api::db_lt_desc_value &w) { @@ -343,7 +366,7 @@ void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno } void ArchiveSlice::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, - td::Promise promise) { + td::Promise promise) { return get_block_common( account_id, [ts](ton_api::db_lt_desc_value &w) { diff --git a/validator/db/archive-slice.hpp b/validator/db/archive-slice.hpp index b4e03e01..45a40a12 100644 --- a/validator/db/archive-slice.hpp +++ b/validator/db/archive-slice.hpp @@ -35,16 +35,17 @@ class ArchiveSlice : public td::actor::Actor { void update_handle(BlockHandle handle, td::Promise promise); void add_file(FileReference ref_id, td::BufferSlice data, td::Promise promise); void get_handle(BlockIdExt block_id, td::Promise promise); + void get_temp_handle(BlockIdExt block_id, td::Promise promise); void get_file(FileReference ref_id, td::Promise promise); /* from LTDB */ - void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); - void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); - void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); + void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); + void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); void get_block_common(AccountIdPrefixFull account_id, std::function compare_desc, std::function compare, bool exact, - td::Promise promise); + td::Promise promise); void get_slice(td::uint64 offset, td::uint32 limit, td::Promise promise); diff --git a/validator/db/package.cpp b/validator/db/package.cpp index e15767df..e41c3688 100644 --- a/validator/db/package.cpp +++ b/validator/db/package.cpp @@ -170,4 +170,8 @@ void Package::iterate(std::function open(std::string path, bool read_only = false, bool create = false); Package(td::FileFd fd); + Package(Package &&p) = default; + ~Package(); td::Status truncate(td::uint64 size); diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index 21cc196d..6dfb971e 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -51,7 +51,7 @@ void RootDb::store_block_data(BlockHandle handle, td::Ref block, td:: std::move(P)); } -void RootDb::get_block_data(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_data(ConstBlockHandle handle, td::Promise> promise) { if (!handle->received()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -88,7 +88,7 @@ void RootDb::store_block_signatures(BlockHandle handle, td::Refserialize(), std::move(P)); } -void RootDb::get_block_signatures(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_signatures(ConstBlockHandle handle, td::Promise> promise) { if (!handle->inited_signatures() || handle->moved_to_archive()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -124,7 +124,7 @@ void RootDb::store_block_proof(BlockHandle handle, td::Ref proof, td::Pro std::move(P)); } -void RootDb::get_block_proof(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_proof(ConstBlockHandle handle, td::Promise> promise) { if (!handle->inited_proof()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -159,7 +159,7 @@ void RootDb::store_block_proof_link(BlockHandle handle, td::Ref proof proof->data(), std::move(P)); } -void RootDb::get_block_proof_link(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_proof_link(ConstBlockHandle handle, td::Promise> promise) { if (!handle->inited_proof_link()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -248,7 +248,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref state, } } -void RootDb::get_block_state(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_state(ConstBlockHandle handle, td::Promise> promise) { if (handle->inited_state_boc()) { if (handle->deleted_state_boc()) { promise.set_error(td::Status::Error(ErrorCode::error, "state already gc'd")); @@ -323,15 +323,15 @@ void RootDb::apply_block(BlockHandle handle, td::Promise promise) { .release(); } -void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { +void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_lt, account, lt, std::move(promise)); } -void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) { +void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_unix_time, account, ts, std::move(promise)); } -void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) { +void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_seqno, account, seqno, std::move(promise)); } diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index 373941e7..7c4b7fa9 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -41,17 +41,17 @@ class RootDb : public Db { void start_up() override; void store_block_data(BlockHandle handle, td::Ref block, td::Promise promise) override; - void get_block_data(BlockHandle handle, td::Promise> promise) override; + void get_block_data(ConstBlockHandle handle, td::Promise> promise) override; void store_block_signatures(BlockHandle handle, td::Ref data, td::Promise promise) override; - void get_block_signatures(BlockHandle handle, td::Promise> promise) override; + void get_block_signatures(ConstBlockHandle handle, td::Promise> promise) override; void store_block_proof(BlockHandle handle, td::Ref proof, td::Promise promise) override; - void get_block_proof(BlockHandle handle, td::Promise> promise) override; + void get_block_proof(ConstBlockHandle handle, td::Promise> promise) override; void store_block_proof_link(BlockHandle handle, td::Ref proof, td::Promise promise) override; - void get_block_proof_link(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_link(ConstBlockHandle handle, td::Promise> promise) override; void store_block_candidate(BlockCandidate candidate, td::Promise promise) override; void get_block_candidate(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, @@ -59,7 +59,7 @@ class RootDb : public Db { void store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) override; - void get_block_state(BlockHandle handle, td::Promise> promise) override; + void get_block_state(ConstBlockHandle handle, td::Promise> promise) override; void store_block_handle(BlockHandle handle, td::Promise promise) override; void get_block_handle(BlockIdExt id, td::Promise promise) override; @@ -82,9 +82,9 @@ class RootDb : public Db { void try_get_static_file(FileHash file_hash, td::Promise promise) override; void apply_block(BlockHandle handle, td::Promise promise) override; - void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; - void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; - void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) override; + void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; + void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) override; void update_init_masterchain_block(BlockIdExt block, td::Promise promise) override; void get_init_masterchain_block(td::Promise promise) override; diff --git a/validator/fabric.h b/validator/fabric.h index 41df565b..55f4bfd3 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -38,6 +38,7 @@ td::Result> create_signature_set(td::BufferSlice sig_ td::Result> create_shard_state(BlockIdExt block_id, td::BufferSlice data); td::Result> create_shard_state(BlockIdExt block_id, td::Ref root_cell); td::Result create_block_handle(td::BufferSlice data); +td::Result create_temp_block_handle(td::BufferSlice data); BlockHandle create_empty_block_handle(BlockIdExt id); td::Result> create_ext_message(td::BufferSlice data); td::Result> create_ihr_message(td::BufferSlice data); diff --git a/validator/impl/config.cpp b/validator/impl/config.cpp index e8f1c83c..8f496caa 100644 --- a/validator/impl/config.cpp +++ b/validator/impl/config.cpp @@ -53,6 +53,14 @@ td::Ref ConfigHolderQ::get_validator_set(ShardIdFull shard, UnixTi return Ref{true, cc_seqno, shard, std::move(nodes)}; } +std::pair ConfigHolderQ::get_validator_set_start_stop(int next) const { + if (!config_) { + LOG(ERROR) << "MasterchainStateQ::get_validator_set_start_stop() : no config"; + return {}; + } + return config_->get_validator_set_start_stop(next); +} + } // namespace validator } // namespace ton diff --git a/validator/impl/config.hpp b/validator/impl/config.hpp index 4fe82ee3..68982ed9 100644 --- a/validator/impl/config.hpp +++ b/validator/impl/config.hpp @@ -43,6 +43,7 @@ class ConfigHolderQ : public ConfigHolder { // if necessary, add more public methods providing interface to config_->...() td::Ref get_total_validator_set(int next) const override; // next = -1 -> prev, next = 0 -> cur td::Ref get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const override; + std::pair get_validator_set_start_stop(int next) const override; }; } // namespace validator diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index 1163089e..42737480 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -96,6 +96,10 @@ td::Result create_block_handle(td::BufferSlice data) { return ton::validator::BlockHandleImpl::create(std::move(data)); } +td::Result create_temp_block_handle(td::BufferSlice data) { + return ton::validator::BlockHandleImpl::create(std::move(data)); +} + BlockHandle create_empty_block_handle(BlockIdExt id) { return ton::validator::BlockHandleImpl::create_empty(id); } diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 196397a9..b9f89f0c 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -167,6 +167,11 @@ void LiteQuery::start_up() { q.mode_ & 1 ? ton::create_block_id(q.target_block_) : ton::BlockIdExt{}, q.mode_); }, + [&](lite_api::liteServer_getValidatorStats& q) { + this->perform_getValidatorStats(ton::create_block_id(q.id_), q.mode_, q.limit_, + q.mode_ & 1 ? q.start_after_ : td::Bits256::zero(), + q.mode_ & 4 ? q.modified_after_ : 0); + }, [&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); })); } @@ -192,7 +197,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) { } td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -230,7 +235,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -256,7 +261,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid, mode](td::Result> res) { + [ Self = actor_id(this), blkid, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -371,7 +376,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { } if (blkid.id.seqno) { td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -381,7 +386,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { }); } else { td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid, - [Self = actor_id(this), blkid](td::Result res) { + [ Self = actor_id(this), blkid ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -440,7 +445,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -466,7 +471,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_from_db_short, blkid, - [Self = actor_id(this), blkid, mode](td::Result> res) { + [ Self = actor_id(this), blkid, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : ")); @@ -488,7 +493,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -519,7 +524,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -541,7 +546,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -563,7 +568,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : ")); @@ -588,7 +593,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_zero_state, blkid, - [Self = actor_id(this), blkid](td::Result res) { + [ Self = actor_id(this), blkid ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : ")); @@ -632,7 +637,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, LOG(INFO) << "sending a get_top_masterchain_state_block query to manager"; td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this)](td::Result, BlockIdExt>> res) -> void { + [Self = actor_id(this)](td::Result, BlockIdExt>> res)->void { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1067,14 +1072,14 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) { << " " << trans_lt_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_), - trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result res) { + trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{}); } else { auto handle = res.move_as_ok(); LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, - [Self, blkid = handle->id(), remaining](td::Result> res) { + [ Self, blkid = handle->id(), remaining ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), blkid); @@ -1141,7 +1146,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list) { LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", ) liteserver query"; - set_continuation([this, mode, param_list = std::move(param_list)]() mutable { + set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable { continue_getConfigParams(mode, std::move(param_list)); }); request_mc_block_data_state(blkid); @@ -1294,14 +1299,14 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime << ") query"; auto P = td::PromiseCreator::lambda( - [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result res) { + [ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { auto handle = res.move_as_ok(); LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, - [Self, blkid = handle->id(), mode](td::Result> res) { + [ Self, blkid = handle->id(), mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1449,7 +1454,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, if (mode & 0x1000) { BlockIdExt bblk = (from.seqno() > to.seqno()) ? from : to; td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, bblk, - [Self = actor_id(this), from, to, bblk, mode](td::Result> res) { + [ Self = actor_id(this), from, to, bblk, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1461,7 +1466,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } else { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), from, to, mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), from, to, mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1474,7 +1479,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } else if (mode & 2) { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), from, mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), from, mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1485,7 +1490,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, }); } else { td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false, - [Self = actor_id(this), from, mode](td::Result res) { + [ Self = actor_id(this), from, mode ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1873,5 +1878,72 @@ bool LiteQuery::finish_proof_chain(ton::BlockIdExt id) { } } +void LiteQuery::perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after, + UnixTime min_utime) { + LOG(INFO) << "started a getValidatorStats(" << blkid.to_str() << ", " << mode << ", " << count << ", " + << start_after.to_hex() << ", " << min_utime << ") liteserver query"; + if (count <= 0) { + fatal_error("requested entry count limit must be positive"); + return; + } + if ((mode & ~7) != 0) { + fatal_error("unknown flags set in mode"); + return; + } + set_continuation([this, mode, count, min_utime, start_after]() { + continue_getValidatorStats(mode, count, start_after, min_utime); + }); + request_mc_block_data_state(blkid); +} + +void LiteQuery::continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime) { + LOG(INFO) << "completing getValidatorStats(" << base_blk_id_.to_str() << ", " << mode << ", " << limit << ", " + << start_after.to_hex() << ", " << min_utime << ") liteserver query"; + Ref proof1; + if (!make_mc_state_root_proof(proof1)) { + return; + } + vm::MerkleProofBuilder mpb{mc_state_->root_cell()}; + int count; + bool complete = false, allow_eq = (mode & 3) != 1; + limit = std::min(limit, 1000); + try { + auto dict = block::get_block_create_stats_dict(mpb.root()); + if (!dict) { + fatal_error("cannot extract block create stats from mc state"); + return; + } + for (count = 0; count < limit; count++) { + auto v = dict->lookup_nearest_key(start_after, true, allow_eq); + if (v.is_null()) { + complete = true; + break; + } + if (!block::gen::t_CreatorStats.validate_csr(std::move(v))) { + fatal_error("invalid CreatorStats record with key "s + start_after.to_hex()); + return; + } + allow_eq = false; + } + } catch (vm::VmError& err) { + fatal_error("error while traversing required block create stats records: "s + err.get_msg()); + return; + } + auto res1 = vm::std_boc_serialize(std::move(proof1)); + if (res1.is_error()) { + fatal_error("cannot serialize Merkle proof : "s + res1.move_as_error().to_string()); + return; + } + auto res2 = mpb.extract_proof_boc(); + if (res2.is_error()) { + fatal_error("cannot serialize Merkle proof : "s + res2.move_as_error().to_string()); + return; + } + LOG(INFO) << "getValidatorStats() query completed"; + auto b = ton::create_serialize_tl_object( + mode & 0xff, ton::create_tl_lite_block_id(base_blk_id_), count, complete, res1.move_as_ok(), res2.move_as_ok()); + finish_query(std::move(b)); +} + } // namespace validator } // namespace ton diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 9118ec0a..36fc9a80 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -114,6 +114,8 @@ class LiteQuery : public td::actor::Actor { void perform_getBlockProof(BlockIdExt from, BlockIdExt to, int mode); void continue_getBlockProof(BlockIdExt from, BlockIdExt to, int mode, BlockIdExt baseblk, Ref state); + void perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after, UnixTime min_utime); + void continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime); bool construct_proof_chain(BlockIdExt id); bool construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next); bool construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next); diff --git a/validator/impl/shard.hpp b/validator/impl/shard.hpp index b067da14..2e4b368a 100644 --- a/validator/impl/shard.hpp +++ b/validator/impl/shard.hpp @@ -139,7 +139,7 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { std::shared_ptr get_config() const { return config_; } - td::Result> get_key_block_config() const override { + td::Result> get_config_holder() const override { if (!config_) { return td::Status::Error(ErrorCode::notready, "config not found"); } else { diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 0f16a3b9..b8737873 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -5086,7 +5086,7 @@ bool ValidateQuery::check_mc_state_extra() { } // block_create_stats:(flags . 0)?BlockCreateStats if (new_extra.r1.flags & 1) { - block::gen::BlockCreateStats::Record rec; + block::gen::BlockCreateStats::Record_block_create_stats rec; if (!tlb::csr_unpack(new_extra.r1.block_create_stats, rec)) { return reject_query("cannot unpack BlockCreateStats in the new masterchain state"); } diff --git a/validator/interfaces/block-handle.h b/validator/interfaces/block-handle.h index 00ecd8ab..a0ed5f2a 100644 --- a/validator/interfaces/block-handle.h +++ b/validator/interfaces/block-handle.h @@ -107,6 +107,7 @@ struct BlockHandleInterface { }; using BlockHandle = std::shared_ptr; +using ConstBlockHandle = std::shared_ptr; } // namespace validator diff --git a/validator/interfaces/config.h b/validator/interfaces/config.h index fba20249..06368e4b 100644 --- a/validator/interfaces/config.h +++ b/validator/interfaces/config.h @@ -35,6 +35,7 @@ class ConfigHolder : public td::CntObject { virtual td::Ref get_total_validator_set(int next) const = 0; // next = -1 -> prev, next = 0 -> cur virtual td::Ref get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const = 0; + virtual std::pair get_validator_set_start_stop(int next) const = 0; }; } // namespace validator diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index 632b0a9c..704272e8 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -31,17 +31,17 @@ class Db : public td::actor::Actor { virtual ~Db() = default; virtual void store_block_data(BlockHandle handle, td::Ref data, td::Promise promise) = 0; - virtual void get_block_data(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_data(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_signatures(BlockHandle handle, td::Ref data, td::Promise promise) = 0; - virtual void get_block_signatures(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_signatures(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_proof(BlockHandle handle, td::Ref proof, td::Promise promise) = 0; - virtual void get_block_proof(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_proof_link(BlockHandle handle, td::Ref proof, td::Promise promise) = 0; - virtual void get_block_proof_link(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof_link(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_candidate(BlockCandidate candidate, td::Promise promise) = 0; virtual void get_block_candidate(ton::PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, @@ -49,7 +49,7 @@ class Db : public td::actor::Actor { virtual void store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) = 0; - virtual void get_block_state(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_state(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state, td::Promise promise) = 0; @@ -72,9 +72,11 @@ class Db : public td::actor::Actor { virtual void get_block_handle(BlockIdExt id, td::Promise promise) = 0; virtual void apply_block(BlockHandle handle, td::Promise promise) = 0; - virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; - virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) = 0; - virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) = 0; + virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; + virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, + td::Promise promise) = 0; + virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, + td::Promise promise) = 0; virtual void update_init_masterchain_block(BlockIdExt block, td::Promise promise) = 0; virtual void get_init_masterchain_block(td::Promise promise) = 0; diff --git a/validator/interfaces/shard.h b/validator/interfaces/shard.h index 33b98c1e..eab36399 100644 --- a/validator/interfaces/shard.h +++ b/validator/interfaces/shard.h @@ -79,7 +79,7 @@ class MasterchainState : virtual public ShardState { virtual bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const = 0; virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0; - virtual td::Result> get_key_block_config() const = 0; + virtual td::Result> get_config_holder() const = 0; virtual td::Status prepare() { return td::Status::OK(); } diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index 7c9e2e6a..564dc6b8 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -541,7 +541,7 @@ void ValidatorManagerImpl::complete_ihr_messages(std::vector t std::vector to_delete) { } -void ValidatorManagerImpl::get_block_data_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_data, handle, std::move(promise)); } @@ -558,7 +558,7 @@ void ValidatorManagerImpl::get_block_data_from_db_short(BlockIdExt block_id, td: get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_shard_state_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_state, handle, std::move(promise)); } @@ -582,7 +582,7 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise)); } -void ValidatorManagerImpl::get_block_proof_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } @@ -599,7 +599,8 @@ void ValidatorManagerImpl::get_block_proof_from_db_short(BlockIdExt block_id, td get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_link_from_db(ConstBlockHandle handle, + td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(promise)); } @@ -618,17 +619,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i } void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise)); } void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise)); } void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise)); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index dbb85b2c..f65617f4 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -191,22 +191,23 @@ class ValidatorManagerImpl : public ValidatorManager { //void set_first_block(ZeroStateIdExt state, BlockIdExt block, td::Promise promise) override; void set_next_block(BlockIdExt prev, BlockIdExt next, td::Promise promise) override; - void get_block_data_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) override; - void get_shard_state_from_db(BlockHandle handle, td::Promise> promise) override; + void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; - void get_block_proof_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, + td::Promise promise) override; void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) override; + td::Promise promise) override; void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) override; + td::Promise promise) override; // get block handle declared in parent class void write_handle(BlockHandle handle, td::Promise promise) override; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 80366875..fce82265 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -461,9 +461,9 @@ void ValidatorManagerMasterchainStarter::got_hardforks(std::vector v return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), db = db_](td::Result R) { R.ensure(); - td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_handle, R.move_as_ok()); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_id, R.move_as_ok()->id()); }); td::actor::send_closure(db_, &Db::get_block_by_seqno, AccountIdPrefixFull{masterchainId, 0}, b.seqno() - 1, std::move(P)); diff --git a/validator/manager.cpp b/validator/manager.cpp index 17bf449a..286baca4 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -846,7 +846,7 @@ void ValidatorManagerImpl::complete_ihr_messages(std::vector t } } -void ValidatorManagerImpl::get_block_data_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_data, std::move(handle), std::move(promise)); } @@ -863,7 +863,7 @@ void ValidatorManagerImpl::get_block_data_from_db_short(BlockIdExt block_id, td: get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_shard_state_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_state, handle, std::move(promise)); } @@ -887,7 +887,7 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise)); } -void ValidatorManagerImpl::get_block_proof_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } @@ -904,7 +904,8 @@ void ValidatorManagerImpl::get_block_proof_from_db_short(BlockIdExt block_id, td get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_link_from_db(ConstBlockHandle handle, + td::Promise> promise) { if (handle->inited_proof_link()) { td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(promise)); } else if (handle->inited_proof()) { @@ -937,17 +938,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i } void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise)); } void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise)); } void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise)); } @@ -1455,6 +1456,8 @@ bool ValidatorManagerImpl::out_of_sync() { if (validator_groups_.size() > 0 && last_known_key_block_handle_->id().seqno() <= last_masterchain_seqno_) { return false; } + LOG(INFO) << "groups=" << validator_groups_.size() << " seqno=" << last_known_key_block_handle_->id().seqno() + << " our_seqno=" << last_masterchain_seqno_; return true; } @@ -1475,7 +1478,8 @@ void ValidatorManagerImpl::download_next_archive() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { LOG(INFO) << "failed to download archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); }, + td::Timestamp::in(2.0)); } else { td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); } @@ -1489,7 +1493,8 @@ void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); }, + td::Timestamp::in(2.0)); } else { td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); } @@ -1504,9 +1509,14 @@ void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) CHECK(seqno.size() == 2); LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; CHECK(seqno[0] <= last_masterchain_seqno_); + CHECK(seqno[1] <= seqno[0]); BlockIdExt b; - CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); + if (seqno[1] < last_masterchain_seqno_) { + CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); + } else { + b = last_masterchain_block_id_; + } auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), db = db_.get(), client = shard_client_.get()](td::Result R) { diff --git a/validator/manager.hpp b/validator/manager.hpp index b0c64c96..e085240b 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -385,22 +385,23 @@ class ValidatorManagerImpl : public ValidatorManager { void set_next_block(BlockIdExt prev, BlockIdExt next, td::Promise promise) override; - void get_block_data_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) override; - void get_shard_state_from_db(BlockHandle handle, td::Promise> promise) override; + void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; - void get_block_proof_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, + td::Promise promise) override; void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) override; + td::Promise promise) override; void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) override; + td::Promise promise) override; // get block handle declared in parent class void write_handle(BlockHandle handle, td::Promise promise) override; diff --git a/validator/validate-broadcast.cpp b/validator/validate-broadcast.cpp index 2d0fbdab..1aa025e3 100644 --- a/validator/validate-broadcast.cpp +++ b/validator/validate-broadcast.cpp @@ -110,7 +110,7 @@ void ValidateBroadcast::start_up() { } else if (key_block_seqno == last_masterchain_state_->get_seqno()) { got_key_block_handle(last_masterchain_block_handle_); } else { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error_prefix("cannot find reference key block id: ")); @@ -138,7 +138,7 @@ void ValidateBroadcast::got_key_block_id(BlockIdExt block_id) { td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id, false, std::move(P)); } -void ValidateBroadcast::got_key_block_handle(BlockHandle handle) { +void ValidateBroadcast::got_key_block_handle(ConstBlockHandle handle) { if (handle->id().seqno() == 0) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { @@ -183,7 +183,7 @@ void ValidateBroadcast::got_key_block_proof_link(td::Ref key_proof_li void ValidateBroadcast::got_zero_state(td::Ref state) { zero_state_ = state; - auto confR = state->get_key_block_config(); + auto confR = state->get_config_holder(); if (confR.is_error()) { abort_query(confR.move_as_error_prefix("failed to extract config from zero state: ")); return; diff --git a/validator/validate-broadcast.hpp b/validator/validate-broadcast.hpp index 4cb8ba49..ff42f635 100644 --- a/validator/validate-broadcast.hpp +++ b/validator/validate-broadcast.hpp @@ -65,7 +65,7 @@ class ValidateBroadcast : public td::actor::Actor { void start_up() override; void got_key_block_id(BlockIdExt block_id); - void got_key_block_handle(BlockHandle block_handle); + void got_key_block_handle(ConstBlockHandle block_handle); void got_key_block_proof_link(td::Ref proof_link); void got_zero_state(td::Ref state); void check_signatures_common(td::Ref conf); diff --git a/validator/validator.h b/validator/validator.h index 5f6a377f..f9f37611 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -176,23 +176,23 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_download_token(size_t download_size, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) = 0; - virtual void get_block_data_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) = 0; virtual void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) = 0; - virtual void get_shard_state_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) = 0; - virtual void get_block_proof_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) = 0; - virtual void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) = 0; virtual void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) = 0; virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,