From 3384d204d2235fec1373722cf6f58c9f4f081b13 Mon Sep 17 00:00:00 2001 From: OmicronTau Date: Sat, 13 Nov 2021 23:28:06 +0300 Subject: [PATCH] Add checks for external messages --- validator/fabric.h | 4 + validator/impl/collator-impl.h | 21 +++ validator/impl/collator.cpp | 232 ++++++++++++++---------- validator/impl/external-message.cpp | 100 +++++++++- validator/impl/external-message.hpp | 21 ++- validator/impl/fabric.cpp | 9 + validator/impl/liteserver.cpp | 75 ++++++-- validator/impl/liteserver.hpp | 12 ++ validator/interfaces/external-message.h | 2 + validator/manager.cpp | 29 ++- validator/manager.hpp | 10 +- 11 files changed, 396 insertions(+), 119 deletions(-) diff --git a/validator/fabric.h b/validator/fabric.h index a10251dd..67b6ae9e 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -46,6 +46,8 @@ td::Result>> create_new_shard_bloc td::Ref create_signature_set(std::vector sig_set); +void run_check_external_message(td::BufferSlice data, td::actor::ActorId manager, td::Promise promise); + void run_accept_block_query(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::Ref signatures, td::Ref approve_signatures, bool send_broadcast, @@ -81,6 +83,8 @@ void run_collate_hardfork(ShardIdFull shard, const BlockIdExt& min_masterchain_b td::Promise promise); void run_liteserver_query(td::BufferSlice data, td::actor::ActorId manager, td::actor::ActorId cache, td::Promise promise); +void run_fetch_account_state(WorkchainId wc, StdSmcAddress addr, td::actor::ActorId manager, + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise); void run_validate_shard_block_description(td::BufferSlice data, BlockHandle masterchain_block, td::Ref masterchain_state, td::actor::ActorId manager, td::Timestamp timeout, diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index 87aa5494..2dced749 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -103,6 +103,27 @@ class Collator final : public td::actor::Actor { return 2; } + static td::Result> + impl_fetch_config_params(std::unique_ptr config, + Ref* old_mparams, + std::vector* storage_prices, + block::StoragePhaseConfig* storage_phase_cfg, + td::BitArray<256>* rand_seed, + block::ComputePhaseConfig* compute_phase_cfg, + block::ActionPhaseConfig* action_phase_cfg, + td::RefInt256* masterchain_create_fee, + td::RefInt256* basechain_create_fee, + WorkchainId wc); + + static td::Result> + impl_create_ordinary_transaction(Ref msg_root, + block::Account* acc, + UnixTime utime, LogicalTime lt, + block::StoragePhaseConfig* storage_phase_cfg, + block::ComputePhaseConfig* compute_phase_cfg, + block::ActionPhaseConfig* action_phase_cfg, + bool external, LogicalTime after_lt); + private: void start_up() override; void alarm() override; diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 9ca4a6cc..6b1c72a6 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -1556,68 +1556,92 @@ bool Collator::init_lt() { } bool Collator::fetch_config_params() { - old_mparams_ = config_->get_config_param(9); - { - auto res = config_->get_storage_prices(); - if (res.is_error()) { + auto res = impl_fetch_config_params(std::move(config_), + &old_mparams_, &storage_prices_, &storage_phase_cfg_, + &rand_seed_, &compute_phase_cfg_, &action_phase_cfg_, + &masterchain_create_fee_, &basechain_create_fee_, + workchain() + ); + if (res.is_error()) { return fatal_error(res.move_as_error()); + } + config_ = res.move_as_ok(); + return true; +} + +td::Result> + Collator::impl_fetch_config_params(std::unique_ptr config, + Ref* old_mparams, + std::vector* storage_prices, + block::StoragePhaseConfig* storage_phase_cfg, + td::BitArray<256>* rand_seed, + block::ComputePhaseConfig* compute_phase_cfg, + block::ActionPhaseConfig* action_phase_cfg, + td::RefInt256* masterchain_create_fee, + td::RefInt256* basechain_create_fee, + WorkchainId wc) { + *old_mparams = config->get_config_param(9); + { + auto res = config->get_storage_prices(); + if (res.is_error()) { + return res.move_as_error(); } - storage_prices_ = res.move_as_ok(); + *storage_prices = res.move_as_ok(); } { // generate rand seed - prng::rand_gen().strong_rand_bytes(rand_seed_.data(), 32); - LOG(DEBUG) << "block random seed set to " << rand_seed_.to_hex(); + prng::rand_gen().strong_rand_bytes(rand_seed->data(), 32); + LOG(DEBUG) << "block random seed set to " << rand_seed->to_hex(); } { // compute compute_phase_cfg / storage_phase_cfg - auto cell = config_->get_config_param(is_masterchain() ? 20 : 21); + auto cell = config->get_config_param(wc == ton::masterchainId ? 20 : 21); if (cell.is_null()) { - return fatal_error("cannot fetch current gas prices and limits from masterchain configuration"); + return td::Status::Error(-668, "cannot fetch current gas prices and limits from masterchain configuration"); } - if (!compute_phase_cfg_.parse_GasLimitsPrices(std::move(cell), storage_phase_cfg_.freeze_due_limit, - storage_phase_cfg_.delete_due_limit)) { - return fatal_error("cannot unpack current gas prices and limits from masterchain configuration"); + if (!compute_phase_cfg->parse_GasLimitsPrices(std::move(cell), storage_phase_cfg->freeze_due_limit, + storage_phase_cfg->delete_due_limit)) { + return td::Status::Error(-668, "cannot unpack current gas prices and limits from masterchain configuration"); } - compute_phase_cfg_.block_rand_seed = rand_seed_; - compute_phase_cfg_.libraries = std::make_unique(config_->get_libraries_root(), 256); - compute_phase_cfg_.global_config = config_->get_root_cell(); + compute_phase_cfg->block_rand_seed = *rand_seed; + compute_phase_cfg->libraries = std::make_unique(config->get_libraries_root(), 256); + compute_phase_cfg->global_config = config->get_root_cell(); } { // compute action_phase_cfg block::gen::MsgForwardPrices::Record rec; - auto cell = config_->get_config_param(24); + auto cell = config->get_config_param(24); if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) { - return fatal_error("cannot fetch masterchain message transfer prices from masterchain configuration"); + return td::Status::Error(-668, "cannot fetch masterchain message transfer prices from masterchain configuration"); } - action_phase_cfg_.fwd_mc = + action_phase_cfg->fwd_mc = block::MsgPrices{rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor, (unsigned)rec.first_frac, (unsigned)rec.next_frac}; - cell = config_->get_config_param(25); + cell = config->get_config_param(25); if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) { - return fatal_error("cannot fetch standard message transfer prices from masterchain configuration"); + return td::Status::Error(-668, "cannot fetch standard message transfer prices from masterchain configuration"); } - action_phase_cfg_.fwd_std = + action_phase_cfg->fwd_std = block::MsgPrices{rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor, (unsigned)rec.first_frac, (unsigned)rec.next_frac}; - action_phase_cfg_.workchains = &config_->get_workchain_list(); - action_phase_cfg_.bounce_msg_body = (config_->has_capability(ton::capBounceMsgBody) ? 256 : 0); + action_phase_cfg->workchains = &config->get_workchain_list(); + action_phase_cfg->bounce_msg_body = (config->has_capability(ton::capBounceMsgBody) ? 256 : 0); } { // fetch block_grams_created - auto cell = config_->get_config_param(14); + auto cell = config->get_config_param(14); if (cell.is_null()) { - basechain_create_fee_ = masterchain_create_fee_ = td::zero_refint(); + *basechain_create_fee = *masterchain_create_fee = td::zero_refint(); } else { block::gen::BlockCreateFees::Record create_fees; if (!(tlb::unpack_cell(cell, create_fees) && - block::tlb::t_Grams.as_integer_to(create_fees.masterchain_block_fee, masterchain_create_fee_) && - block::tlb::t_Grams.as_integer_to(create_fees.basechain_block_fee, basechain_create_fee_))) { - return fatal_error("cannot unpack BlockCreateFees from configuration parameter #14"); + block::tlb::t_Grams.as_integer_to(create_fees.masterchain_block_fee, *masterchain_create_fee) && + block::tlb::t_Grams.as_integer_to(create_fees.basechain_block_fee, *basechain_create_fee))) { + return td::Status::Error(-668, "cannot unpack BlockCreateFees from configuration parameter #14"); } } } - return true; + return std::move(config); } bool Collator::compute_minted_amount(block::CurrencyCollection& to_mint) { @@ -2218,75 +2242,25 @@ Ref Collator::create_ordinary_transaction(Ref msg_root) { } block::Account* acc = acc_res.move_as_ok(); assert(acc); - if (acc->last_trans_end_lt_ >= start_lt && acc->transactions.empty()) { - fatal_error(PSTRING() << "last transaction time in the state of account " << workchain() << ":" << addr.to_hex() - << " is too large"); - return {}; - } - auto trans_min_lt = start_lt; - if (external) { - // transactions processing external messages must have lt larger than all processed internal messages - trans_min_lt = std::max(trans_min_lt, last_proc_int_msg_.first); - } - std::unique_ptr trans = - std::make_unique(*acc, block::Transaction::tr_ord, trans_min_lt + 1, now_, msg_root); - bool ihr_delivered = false; // FIXME - if (!trans->unpack_input_msg(ihr_delivered, &action_phase_cfg_)) { - if (external) { - // inbound external message was not accepted - LOG(DEBUG) << "inbound external message rejected by account " << addr.to_hex() - << " before smart-contract execution"; + + + auto res = impl_create_ordinary_transaction(msg_root, acc, now_, start_lt, + &storage_phase_cfg_, &compute_phase_cfg_, + &action_phase_cfg_, + external, last_proc_int_msg_.first + ); + if(res.is_error()) { + auto error = res.move_as_error(); + if(error.code() == -701) { + // ignorable errors + LOG(DEBUG) << error.message(); return {}; } - fatal_error("cannot unpack input message for a new transaction"); - return {}; - } - if (trans->bounce_enabled) { - if (!trans->prepare_storage_phase(storage_phase_cfg_, true)) { - fatal_error("cannot create storage phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - if (!external && !trans->prepare_credit_phase()) { - fatal_error("cannot create credit phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - } else { - if (!external && !trans->prepare_credit_phase()) { - fatal_error("cannot create credit phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - if (!trans->prepare_storage_phase(storage_phase_cfg_, true, true)) { - fatal_error("cannot create storage phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - } - if (!trans->prepare_compute_phase(compute_phase_cfg_)) { - fatal_error("cannot create compute phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - if (!trans->compute_phase->accepted) { - if (external) { - // inbound external message was not accepted - LOG(DEBUG) << "inbound external message rejected by transaction " << addr.to_hex(); - return {}; - } else if (trans->compute_phase->skip_reason == block::ComputePhase::sk_none) { - fatal_error("new ordinary transaction for smart contract "s + addr.to_hex() + - " has not been accepted by the smart contract (?)"); - return {}; - } - } - if (trans->compute_phase->success && !trans->prepare_action_phase(action_phase_cfg_)) { - fatal_error("cannot create action phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - if (trans->bounce_enabled && !trans->compute_phase->success && !trans->prepare_bounce_phase(action_phase_cfg_)) { - fatal_error("cannot create bounce phase of a new transaction for smart contract "s + addr.to_hex()); - return {}; - } - if (!trans->serialize()) { - fatal_error("cannot serialize new transaction for smart contract "s + addr.to_hex()); + fatal_error(std::move(error)); return {}; } + std::unique_ptr trans = res.move_as_ok(); + if (!trans->update_limits(*block_limit_status_)) { fatal_error("cannot update block limit status to include the new transaction"); return {}; @@ -2296,6 +2270,7 @@ Ref Collator::create_ordinary_transaction(Ref msg_root) { fatal_error("cannot commit new transaction for smart contract "s + addr.to_hex()); return {}; } + register_new_msgs(*trans); update_max_lt(acc->last_trans_end_lt_); // temporary patch to stop producing dangerous block @@ -2305,6 +2280,75 @@ Ref Collator::create_ordinary_transaction(Ref msg_root) { return trans_root; } +// If td::status::error_code == 669 - Fatal Error block can not be produced +// if td::status::error_code == 701 - Transaction can not be included into block, but it's ok (external or too early internal) +td::Result> Collator::impl_create_ordinary_transaction(Ref msg_root, + block::Account* acc, + UnixTime utime, LogicalTime lt, + block::StoragePhaseConfig* storage_phase_cfg, + block::ComputePhaseConfig* compute_phase_cfg, + block::ActionPhaseConfig* action_phase_cfg, + bool external, LogicalTime after_lt) { + if (acc->last_trans_end_lt_ >= lt && acc->transactions.empty()) { + return td::Status::Error(-669, PSTRING() << "last transaction time in the state of account " << acc->workchain << ":" << acc->addr.to_hex() + << " is too large"); + } + auto trans_min_lt = lt; + if (external) { + // transactions processing external messages must have lt larger than all processed internal messages + trans_min_lt = std::max(trans_min_lt, after_lt); + } + + std::unique_ptr trans = + std::make_unique(*acc, block::Transaction::tr_ord, trans_min_lt + 1, utime, msg_root); + bool ihr_delivered = false; // FIXME + if (!trans->unpack_input_msg(ihr_delivered, action_phase_cfg)) { + if (external) { + // inbound external message was not accepted + return td::Status::Error(-701,"inbound external message rejected by account "s + acc->addr.to_hex() + + " before smart-contract execution"); + } + return td::Status::Error(-669,"cannot unpack input message for a new transaction"); + } + if (trans->bounce_enabled) { + if (!trans->prepare_storage_phase(*storage_phase_cfg, true)) { + return td::Status::Error(-669,"cannot create storage phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + if (!external && !trans->prepare_credit_phase()) { + return td::Status::Error(-669,"cannot create credit phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + } else { + if (!external && !trans->prepare_credit_phase()) { + return td::Status::Error(-669,"cannot create credit phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + if (!trans->prepare_storage_phase(*storage_phase_cfg, true, true)) { + return td::Status::Error(-669,"cannot create storage phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + } + if (!trans->prepare_compute_phase(*compute_phase_cfg)) { + return td::Status::Error(-669,"cannot create compute phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + if (!trans->compute_phase->accepted) { + if (external) { + // inbound external message was not accepted + return td::Status::Error(-701,"inbound external message rejected by transaction "s + acc->addr.to_hex()); + } else if (trans->compute_phase->skip_reason == block::ComputePhase::sk_none) { + return td::Status::Error(-669,"new ordinary transaction for smart contract "s + acc->addr.to_hex() + + " has not been accepted by the smart contract (?)"); + } + } + if (trans->compute_phase->success && !trans->prepare_action_phase(*action_phase_cfg)) { + return td::Status::Error(-669,"cannot create action phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + if (trans->bounce_enabled && !trans->compute_phase->success && !trans->prepare_bounce_phase(*action_phase_cfg)) { + return td::Status::Error(-669,"cannot create bounce phase of a new transaction for smart contract "s + acc->addr.to_hex()); + } + if (!trans->serialize()) { + return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + acc->addr.to_hex()); + } + return std::move(trans); +} + void Collator::update_max_lt(ton::LogicalTime lt) { CHECK(lt >= start_lt); if (lt > max_lt) { diff --git a/validator/impl/external-message.cpp b/validator/impl/external-message.cpp index 6af721b2..c5f2d097 100644 --- a/validator/impl/external-message.cpp +++ b/validator/impl/external-message.cpp @@ -16,19 +16,25 @@ Copyright 2017-2020 Telegram Systems LLP */ + #include "external-message.hpp" +#include "collator-impl.h" #include "vm/boc.h" #include "block/block-parse.h" #include "block/block-auto.h" #include "block/block-db.h" +#include "fabric.h" +#include "td/actor/actor.h" +#include "td/utils/Random.h" +#include "crypto/openssl/rand.hpp" namespace ton { namespace validator { using td::Ref; -ExtMessageQ::ExtMessageQ(td::BufferSlice data, td::Ref root, AccountIdPrefixFull addr_prefix) - : root_(std::move(root)), addr_prefix_(addr_prefix), data_(std::move(data)) { +ExtMessageQ::ExtMessageQ(td::BufferSlice data, td::Ref root, AccountIdPrefixFull addr_prefix, ton::WorkchainId wc, ton::StdSmcAddress addr) + : root_(std::move(root)), addr_prefix_(addr_prefix), data_(std::move(data)), wc_(wc), addr_(addr) { hash_ = block::compute_file_hash(data_); } @@ -70,7 +76,95 @@ td::Result> ExtMessageQ::create_ext_message(td::BufferSlice dat if (!dest_prefix.is_valid()) { return td::Status::Error("destination of an inbound external message is an invalid blockchain address"); } - return Ref{true, std::move(data), std::move(ext_msg), dest_prefix}; + ton::StdSmcAddress addr; + ton::WorkchainId wc; + if(!block::tlb::t_MsgAddressInt.extract_std_address(info.dest, wc, addr)) { + return td::Status::Error(PSLICE() << "Can't parse destination address"); + } + + return Ref{true, std::move(data), std::move(ext_msg), dest_prefix, wc, addr}; +} + +void ExtMessageQ::run_message(td::BufferSlice data, td::actor::ActorId manager, + td::Promise promise) { + auto R = create_ext_message(std::move(data)); + if (R.is_error()) { + return promise.set_error(R.move_as_error_prefix("failed to parse external message ")); + } + auto M = R.move_as_ok(); + auto root = M->root_cell(); + block::gen::CommonMsgInfo::Record_ext_in_msg_info info; + tlb::unpack_cell_inexact(root, info); // checked in create message + ton::StdSmcAddress addr = M->addr(); + ton::WorkchainId wc = M->wc(); + + run_fetch_account_state(wc, addr, manager, + [promise = std::move(promise), msg_root = root, wc = wc](td::Result,UnixTime,LogicalTime,std::unique_ptr>> res) mutable { + if (res.is_error()) { + promise.set_error(td::Status::Error(PSLICE() << "Failed to get account state")); + } else { + auto tuple = res.move_as_ok(); + block::Account acc; + auto shard_acc = std::move(std::get<0>(tuple)); + auto utime = std::get<1>(tuple); + auto lt = std::get<2>(tuple); + auto config = std::move(std::get<3>(tuple)); + if(!acc.unpack(shard_acc, {}, utime, false)) { + promise.set_error(td::Status::Error(PSLICE() << "Failed to unpack account state")); + } else { + if(run_message_on_account(wc, &acc, utime, lt + 1, msg_root, std::move(config))) { + promise.set_value(td::Unit()); + } else { + promise.set_error(td::Status::Error(PSLICE() << "External message was not accepted")); + } + } + } + } + ); +} + +bool ExtMessageQ::run_message_on_account(ton::WorkchainId wc, + block::Account* acc, + UnixTime utime, LogicalTime lt, + td::Ref msg_root, + std::unique_ptr config) { + + Ref old_mparams; + std::vector storage_prices_; + block::StoragePhaseConfig storage_phase_cfg_{&storage_prices_}; + td::BitArray<256> rand_seed_; + block::ComputePhaseConfig compute_phase_cfg_; + block::ActionPhaseConfig action_phase_cfg_; + td::RefInt256 masterchain_create_fee, basechain_create_fee; + + auto fetch_res = Collator::impl_fetch_config_params(std::move(config), &old_mparams, + &storage_prices_, &storage_phase_cfg_, + &rand_seed_, &compute_phase_cfg_, + &action_phase_cfg_, &masterchain_create_fee, + &basechain_create_fee, wc); + if(fetch_res.is_error()) { + auto error = fetch_res.move_as_error(); + LOG(DEBUG) << "Cannot fetch config params" << error.message(); + return false; + } + + auto res = Collator::impl_create_ordinary_transaction(msg_root, acc, utime, lt, + &storage_phase_cfg_, &compute_phase_cfg_, + &action_phase_cfg_, + true, lt); + if(res.is_error()) { + auto error = res.move_as_error(); + LOG(DEBUG) << "Cannot run message on account" << error.message(); + return false; + } + std::unique_ptr trans = res.move_as_ok(); + + auto trans_root = trans->commit(*acc); + if (trans_root.is_null()) { + LOG(DEBUG) << "cannot commit new transaction for smart contract "; + return false; + } + return true; } } // namespace validator diff --git a/validator/impl/external-message.hpp b/validator/impl/external-message.hpp index 5bb52b11..230258cd 100644 --- a/validator/impl/external-message.hpp +++ b/validator/impl/external-message.hpp @@ -18,9 +18,11 @@ */ #pragma once +#include "interfaces/validator-manager.h" #include "validator/interfaces/external-message.h" #include "auto/tl/ton_api.h" #include "adnl/utils.hpp" +#include "block/transaction.h" namespace ton { @@ -31,6 +33,8 @@ class ExtMessageQ : public ExtMessage { AccountIdPrefixFull addr_prefix_; td::BufferSlice data_; Hash hash_; + ton::WorkchainId wc_; + ton::StdSmcAddress addr_; public: static constexpr unsigned max_ext_msg_size = 65535; @@ -47,8 +51,23 @@ class ExtMessageQ : public ExtMessage { Hash hash() const override { return hash_; } - ExtMessageQ(td::BufferSlice data, td::Ref root, AccountIdPrefixFull shard); + ton::WorkchainId wc() const override { + return wc_; + } + + ton::StdSmcAddress addr() const override { + return addr_; + } + + ExtMessageQ(td::BufferSlice data, td::Ref root, AccountIdPrefixFull shard, ton::WorkchainId wc, ton::StdSmcAddress addr); static td::Result> create_ext_message(td::BufferSlice data); + static void run_message(td::BufferSlice data, td::actor::ActorId manager, + td::Promise promise); + static bool run_message_on_account(ton::WorkchainId wc, + block::Account* acc, + UnixTime utime, LogicalTime lt, + td::Ref msg_root, + std::unique_ptr config); }; } // namespace validator diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index ac296eb4..196595ae 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -116,6 +116,10 @@ td::Result> create_ext_message(td::BufferSlice data) { return std::move(res); } +void run_check_external_message(td::BufferSlice data, td::actor::ActorId manager, td::Promise promise) { + ExtMessageQ::run_message(std::move(data), std::move(manager), std::move(promise)); +} + td::Result> create_ihr_message(td::BufferSlice data) { TRY_RESULT(res, IhrMessageQ::create_ihr_message(std::move(data))); return std::move(res); @@ -237,6 +241,11 @@ void run_liteserver_query(td::BufferSlice data, td::actor::ActorId manager, + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise) { + LiteQuery::fetch_account_state(wc, addr, std::move(manager), std::move(promise)); +} + void run_validate_shard_block_description(td::BufferSlice data, BlockHandle masterchain_block, td::Ref masterchain_state, td::actor::ActorId manager, td::Timestamp timeout, diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index b3c0f3eb..510f4906 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -58,15 +58,28 @@ void LiteQuery::run_query(td::BufferSlice data, td::actor::ActorId("litequery", std::move(data), std::move(manager), std::move(promise)).release(); } +void LiteQuery::fetch_account_state(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId manager, + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise) { + td::actor::create_actor("litequery", wc, acc_addr, std::move(manager), std::move(promise)).release(); +} + LiteQuery::LiteQuery(td::BufferSlice data, td::actor::ActorId manager, td::Promise promise) : query_(std::move(data)), manager_(std::move(manager)), promise_(std::move(promise)) { timeout_ = td::Timestamp::in(default_timeout_msec * 0.001); } +LiteQuery::LiteQuery(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId manager, + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise) + : manager_(std::move(manager)), acc_state_promise_(std::move(promise)), acc_workchain_(wc), acc_addr_(acc_addr) { + timeout_ = td::Timestamp::in(default_timeout_msec * 0.001); +} + void LiteQuery::abort_query(td::Status reason) { LOG(INFO) << "aborted liteserver query: " << reason.to_string(); - if (promise_) { + if (acc_state_promise_) { + acc_state_promise_.set_error(std::move(reason)); + } else if (promise_) { promise_.set_error(std::move(reason)); } stop(); @@ -111,6 +124,11 @@ bool LiteQuery::finish_query(td::BufferSlice result) { void LiteQuery::start_up() { alarm_timestamp() = timeout_; + if(acc_state_promise_) { + td::actor::send_closure_later(actor_id(this),&LiteQuery::perform_fetchAccountState); + return; + } + auto F = fetch_tl_object(std::move(query_), true); if (F.is_error()) { abort_query(F.move_as_error()); @@ -205,17 +223,23 @@ 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), return_state = bool(acc_state_promise_), mode](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { auto pair = res.move_as_ok(); - td::actor::send_closure_later(Self, &LiteQuery::continue_getMasterchainInfo, std::move(pair.first), + auto func = return_state ? &LiteQuery::gotMasterchainInfoForAccountState : &LiteQuery::continue_getMasterchainInfo; + td::actor::send_closure_later(Self, func, std::move(pair.first), pair.second, mode); } }); } +void LiteQuery::gotMasterchainInfoForAccountState(Ref mc_state, BlockIdExt blkid, + int mode) { + perform_getAccountState(blkid, acc_workchain_, acc_addr_, 0x80000000); +} + void LiteQuery::continue_getMasterchainInfo(Ref mc_state, BlockIdExt blkid, int mode) { LOG(INFO) << "obtained data for getMasterchainInfo() : last block = " << blkid.to_str(); @@ -431,15 +455,25 @@ void LiteQuery::continue_getZeroState(BlockIdExt blkid, td::BufferSlice state) { void LiteQuery::perform_sendMessage(td::BufferSlice data) { LOG(INFO) << "started a sendMessage(<" << data.size() << " bytes>) liteserver query"; - auto res = ton::validator::create_ext_message(std::move(data)); - if (res.is_error()) { - abort_query(res.move_as_error()); - return; - } - LOG(INFO) << "sending an external message to validator manager"; - td::actor::send_closure_later(manager_, &ValidatorManager::send_external_message, res.move_as_ok()); - auto b = ton::create_serialize_tl_object(1); - finish_query(std::move(b)); + td::actor::send_closure_later( + manager_, &ValidatorManager::check_external_message, data.clone(), + [Self = actor_id(this), data = std::move(data), manager = manager_](td::Result res) { + if(res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, + res.move_as_error_prefix("cannot apply external message to current state : "s)); + } else { + auto crm = ton::validator::create_ext_message(data.clone()); + if (crm.is_error()) { + //UNREACHABLE, checks in check_external_message, + td::actor::send_closure(Self, &LiteQuery::abort_query, + crm.move_as_error()); + } + LOG(INFO) << "sending an external message to validator manager"; + td::actor::send_closure_later(manager, &ValidatorManager::send_external_message, crm.move_as_ok()); + auto b = ton::create_serialize_tl_object(1); + td::actor::send_closure(Self, &LiteQuery::finish_query, std::move(b)); + } + }); } bool LiteQuery::request_mc_block_data(BlockIdExt blkid) { @@ -702,6 +736,10 @@ void LiteQuery::continue_getAccountState_0(Ref request_mc_block_data(blkid); } +void LiteQuery::perform_fetchAccountState() { + perform_getMasterchainInfo(-1); +} + void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, td::int64 method_id, td::BufferSlice params) { LOG(INFO) << "started a runSmcMethod(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", " @@ -1010,6 +1048,19 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { } vm::AugmentedDictionary accounts_dict{vm::load_cell_slice_ref(sstate.accounts), 256, block::tlb::aug_ShardAccounts}; auto acc_csr = accounts_dict.lookup(acc_addr_); + if (mode_ & 0x80000000) { + auto config = block::ConfigInfo::extract_config(mc_state_->root_cell(), 0xFFFF); + if (config.is_error()) { + fatal_error(config.move_as_error()); + return; + } + auto rconfig = config.move_as_ok(); + acc_state_promise_.set_value(std::make_tuple( + std::move(acc_csr), sstate.gen_utime, sstate.gen_lt, std::move(rconfig) + )); + return; + } + Ref acc_root; if (acc_csr.not_null()) { acc_root = acc_csr->prefetch_ref(); diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index f6353c41..24392085 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -26,6 +26,8 @@ #include "block.hpp" #include "shard.hpp" #include "proof.hpp" +#include "block/block-auto.h" + namespace ton { @@ -37,6 +39,9 @@ class LiteQuery : public td::actor::Actor { td::actor::ActorId manager_; td::Timestamp timeout_; td::Promise promise_; + + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> acc_state_promise_; + int pending_{0}; int mode_{0}; WorkchainId acc_workchain_; @@ -71,9 +76,14 @@ class LiteQuery : public td::actor::Actor { }; // version 1.1; +1 = build block proof chains, +2 = masterchainInfoExt, +4 = runSmcMethod LiteQuery(td::BufferSlice data, td::actor::ActorId manager, td::Promise promise); + LiteQuery(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId manager, + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise); static void run_query(td::BufferSlice data, td::actor::ActorId manager, td::Promise promise); + static void fetch_account_state(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId manager, + td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise); + private: bool fatal_error(td::Status error); bool fatal_error(std::string err_msg, int err_code = -400); @@ -87,6 +97,7 @@ class LiteQuery : public td::actor::Actor { void perform_getVersion(); void perform_getMasterchainInfo(int mode); void continue_getMasterchainInfo(Ref mc_state, BlockIdExt blkid, int mode); + void gotMasterchainInfoForAccountState(Ref mc_state, BlockIdExt blkid, int mode); void perform_getBlock(BlockIdExt blkid); void continue_getBlock(BlockIdExt blkid, Ref block); void perform_getBlockHeader(BlockIdExt blkid, int mode); @@ -99,6 +110,7 @@ class LiteQuery : public td::actor::Actor { void continue_getAccountState_0(Ref mc_state, BlockIdExt blkid); void continue_getAccountState(); void finish_getAccountState(td::BufferSlice shard_proof); + void perform_fetchAccountState(); void perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, td::int64 method_id, td::BufferSlice params); void finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref acc_root, diff --git a/validator/interfaces/external-message.h b/validator/interfaces/external-message.h index f6d9d600..871c624a 100644 --- a/validator/interfaces/external-message.h +++ b/validator/interfaces/external-message.h @@ -35,6 +35,8 @@ class ExtMessage : public td::CntObject { virtual td::BufferSlice serialize() const = 0; virtual td::Ref root_cell() const = 0; virtual Hash hash() const = 0; + virtual ton::WorkchainId wc() const = 0; + virtual ton::StdSmcAddress addr() const = 0; }; } // namespace validator diff --git a/validator/manager.cpp b/validator/manager.cpp index 1741502d..7fcf0274 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -374,13 +374,25 @@ void ValidatorManagerImpl::new_external_message(td::BufferSlice data) { VLOG(VALIDATOR_NOTICE) << "dropping bad ihr message: " << R.move_as_error(); return; } - auto M = std::make_unique>(R.move_as_ok()); - auto id = M->ext_id(); - if (ext_messages_hashes_.count(id.hash) == 0) { - ext_messages_.emplace(id, std::move(M)); - ext_messages_hashes_.emplace(id.hash, id); + add_external_message(R.move_as_ok()); +} + +void ValidatorManagerImpl::add_external_message(td::Ref msg) { + auto message = std::make_unique>(msg); + auto id = message->ext_id(); + auto address = message->address(); + unsigned long per_address_limit = 256; + if(ext_addr_messages_.count(address) < per_address_limit) { + if (ext_messages_hashes_.count(id.hash) == 0) { + ext_messages_.emplace(id, std::move(message)); + ext_messages_hashes_.emplace(id.hash, id); + ext_addr_messages_[address].emplace(id.hash, id); + } } } +void ValidatorManagerImpl::check_external_message(td::BufferSlice data, td::Promise promise) { + run_check_external_message(std::move(data), actor_id(this), std::move(promise)); +} void ValidatorManagerImpl::new_ihr_message(td::BufferSlice data) { if (!is_validator()) { @@ -759,6 +771,7 @@ void ValidatorManagerImpl::get_external_messages(ShardIdFull shard, break; } if (it->second->expired()) { + ext_addr_messages_[it->second->address()].erase(it->first.hash); ext_messages_hashes_.erase(it->first.hash); it = ext_messages_.erase(it); continue; @@ -807,17 +820,20 @@ void ValidatorManagerImpl::complete_external_messages(std::vectorsecond]->address()].erase(it->first); CHECK(ext_messages_.erase(it->second)); ext_messages_hashes_.erase(it); } } + unsigned long soft_mempool_limit = 1024; for (auto &hash : to_delay) { auto it = ext_messages_hashes_.find(hash); if (it != ext_messages_hashes_.end()) { auto it2 = ext_messages_.find(it->second); - if (it2->second->can_postpone()) { + if ((ext_messages_.size() < soft_mempool_limit) && it2->second->can_postpone()) { it2->second->postpone(); } else { + ext_addr_messages_[it2->second->address()].erase(it2->first.hash); ext_messages_.erase(it2); ext_messages_hashes_.erase(it); } @@ -1352,6 +1368,7 @@ void ValidatorManagerImpl::send_get_next_key_blocks_request(BlockIdExt block_id, void ValidatorManagerImpl::send_external_message(td::Ref message) { callback_->send_ext_message(message->shard(), message->serialize()); + add_external_message(std::move(message)); } void ValidatorManagerImpl::send_ihr_message(td::Ref message) { diff --git a/validator/manager.hpp b/validator/manager.hpp index c2e41a30..19ee4cdd 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -74,6 +74,9 @@ class MessageExt { auto hash() const { return message_->hash(); } + auto address() const { + return std::make_pair(message_->wc(), message_->addr()); + } bool is_active() { if (!active_) { if (reactivate_at_.is_in_past()) { @@ -210,6 +213,7 @@ class ValidatorManagerImpl : public ValidatorManager { // DATA FOR COLLATOR std::map> shard_blocks_; std::map, std::unique_ptr>> ext_messages_; + std::map, std::map>> ext_addr_messages_; std::map> ext_messages_hashes_; // IHR ? std::map, std::unique_ptr>> ihr_messages_; @@ -327,9 +331,9 @@ class ValidatorManagerImpl : public ValidatorManager { //void get_block_description(BlockIdExt block_id, td::Promise promise) override; void new_external_message(td::BufferSlice data) override; - void check_external_message(td::BufferSlice data, td::Promise promise) override { - promise.set_value(td::Unit()); - } + void add_external_message(td::Ref message); + void check_external_message(td::BufferSlice data, td::Promise promise) override; + void new_ihr_message(td::BufferSlice data) override; void new_shard_block(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) override;