From e43e235143d2f151db1ce2da390384274518f640 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 1 Aug 2022 17:48:22 +0300 Subject: [PATCH] Get neighbors' msg queues from other nodes --- tl/generate/scheme/ton_api.tl | 4 + tl/generate/scheme/ton_api.tlo | Bin 70468 -> 70924 bytes validator/CMakeLists.txt | 1 + validator/full-node-shard.cpp | 53 +++++ validator/full-node-shard.h | 2 + validator/full-node-shard.hpp | 4 + validator/full-node.cpp | 17 ++ validator/full-node.hpp | 2 + validator/impl/CMakeLists.txt | 5 +- validator/impl/collator-impl.h | 5 +- validator/impl/collator.cpp | 72 +++--- validator/impl/out-msg-queue-proof.cpp | 245 +++++++++++++++++++++ validator/impl/out-msg-queue-proof.hpp | 109 +++++++++ validator/impl/proof.cpp | 35 +++ validator/interfaces/out-msg-queue-proof.h | 42 ++++ validator/interfaces/proof.h | 3 + validator/interfaces/validator-manager.h | 3 + validator/manager-disk.hpp | 8 + validator/manager-hardfork.hpp | 8 + validator/manager.cpp | 94 ++++++-- validator/manager.hpp | 7 + validator/validator.h | 7 + 22 files changed, 658 insertions(+), 68 deletions(-) create mode 100644 validator/impl/out-msg-queue-proof.cpp create mode 100644 validator/impl/out-msg-queue-proof.hpp create mode 100644 validator/interfaces/out-msg-queue-proof.h diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 9f1072ce..f3ac4ee9 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -410,6 +410,9 @@ tonNode.success = tonNode.Success; tonNode.archiveNotFound = tonNode.ArchiveInfo; tonNode.archiveInfo id:long = tonNode.ArchiveInfo; +tonNode.outMsgQueueProof queue_proof:bytes block_state_proof:bytes = tonNode.OutMsgQueueProof; +tonNode.outMsgQueueProofEmpty = tonNode.OutMsgQueueProof; + ---functions--- tonNode.getNextBlockDescription prev_block:tonNode.blockIdExt = tonNode.BlockDescription; @@ -441,6 +444,7 @@ tonNode.downloadBlockProofLinks blocks:(vector tonNode.blockIdExt) = tonNode.Dat tonNode.downloadKeyBlockProofLinks blocks:(vector tonNode.blockIdExt) = tonNode.DataList; tonNode.getArchiveInfo masterchain_seqno:int = tonNode.ArchiveInfo; tonNode.getArchiveSlice archive_id:long offset:long max_size:int = tonNode.Data; +tonNode.getOutMsgQueueProof block_id:tonNode.blockIdExt dst_workchain:int dst_shard:long = tonNode.OutMsgQueueProof; tonNode.getCapabilities = tonNode.Capabilities; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 95a2144a69edb475430ee6965b8a84f5b497eddb..2960fa72c13d97c0b874d959604f413a38afe08c 100644 GIT binary patch delta 326 zcmX@IjHPE43-6=Z`c@23aBd^-X+_y2t+*)?CHZ-N`6;P-{-q_p#p!{isimm_Mfv$@ zlO2VmH{Vda!@}sfd8=CUESmbFCBiC@ zz9MHf9S+;JgCKJ@>l{pwXH41L`@mmElZAIxjx@}m^wbh`55nBbJo#dx`g8|AMpl-T a;*$8u509uo7>wD promise) { + BlockIdExt block_id = create_block_id(query.block_id_); + ShardIdFull dst_shard(query.dst_workchain_, query.dst_shard_); + if (!block_id.is_valid_ext()) { + promise.set_error(td::Status::Error("invalid block_id")); + return; + } + if (!dst_shard.is_valid_ext()) { + promise.set_error(td::Status::Error("invalid shard")); + return; + } + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_result(create_serialize_tl_object()); + } else { + promise.set_result(serialize_tl_object(R.move_as_ok(), true)); + } + }); + td::actor::create_actor("buildqueueproof", block_id, dst_shard, validator_manager_, + std::move(P)) + .release(); +} + void FullNodeShardImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise) { if (!active_) { @@ -791,6 +818,32 @@ void FullNodeShardImpl::download_archive(BlockSeqno masterchain_seqno, std::stri .release(); } +void FullNodeShardImpl::download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise) { + // TODO: maybe more complex download (like other requests here) + // TODO: estimate max size + auto &b = choose_neighbour(); + auto P = td::PromiseCreator::lambda( + [=, promise = create_neighbour_promise(b, std::move(promise))](td::Result R) mutable { + if (R.is_error()) { + promise.set_result(R.move_as_error()); + return; + } + TRY_RESULT_PROMISE(promise, f, fetch_tl_object(R.move_as_ok(), true)); + ton_api::downcast_call(*f, td::overloaded( + [&](ton_api::tonNode_outMsgQueueProofEmpty &x) { + promise.set_error(td::Status::Error("node doesn't have this block")); + }, + [&](ton_api::tonNode_outMsgQueueProof &x) { + promise.set_result(OutMsgQueueProof::fetch(block_id, dst_shard, x)); + })); + }); + td::BufferSlice query = create_serialize_tl_object( + create_tl_block_id(block_id), dst_shard.workchain, dst_shard.shard); + td::actor::send_closure(overlays_, &overlay::Overlays::send_query_via, b.adnl_id, adnl_id_, overlay_id_, + "get_msg_queue", std::move(P), timeout, std::move(query), 1 << 20, rldp_); +} + void FullNodeShardImpl::set_handle(BlockHandle handle, td::Promise promise) { CHECK(!handle_); handle_ = std::move(handle); diff --git a/validator/full-node-shard.h b/validator/full-node-shard.h index 2b2098a6..7da46af1 100644 --- a/validator/full-node-shard.h +++ b/validator/full-node-shard.h @@ -62,6 +62,8 @@ class FullNodeShard : public td::actor::Actor { td::Promise> promise) = 0; virtual void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, td::Promise promise) = 0; + virtual void download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise) = 0; virtual void set_handle(BlockHandle handle, td::Promise promise) = 0; diff --git a/validator/full-node-shard.hpp b/validator/full-node-shard.hpp index 9a022cbf..ead8cafd 100644 --- a/validator/full-node-shard.hpp +++ b/validator/full-node-shard.hpp @@ -133,6 +133,8 @@ class FullNodeShardImpl : public FullNodeShard { td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getOutMsgQueueProof &query, + td::Promise promise); // void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_prepareNextKeyBlockProof &query, // td::Promise promise); void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise); @@ -164,6 +166,8 @@ class FullNodeShardImpl : public FullNodeShard { td::Promise> promise) override; void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, td::Promise promise) override; + void download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise) override; void set_handle(BlockHandle handle, td::Promise promise) override; diff --git a/validator/full-node.cpp b/validator/full-node.cpp index 9c26cf3c..9194b9a4 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -356,6 +356,18 @@ void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, std::string tm std::move(promise)); } +void FullNodeImpl::download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise) { + auto shard = get_shard(block_id.shard_full()); + if (shard.empty()) { + VLOG(FULL_NODE_WARNING) << "dropping download msg queue query to unknown shard"; + promise.set_error(td::Status::Error(ErrorCode::notready, "shard not ready")); + return; + } + td::actor::send_closure(shard, &FullNodeShard::download_out_msg_queue_proof, block_id, dst_shard, timeout, + std::move(promise)); +} + td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard, bool exact) { if (!exact) { ShardIdFull s = shard; @@ -546,6 +558,11 @@ void FullNodeImpl::start_up() { td::actor::send_closure(id_, &FullNodeImpl::download_archive, masterchain_seqno, std::move(tmp_dir), timeout, std::move(promise)); } + void download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise) override { + td::actor::send_closure(id_, &FullNodeImpl::download_out_msg_queue_proof, block_id, dst_shard, timeout, + std::move(promise)); + } void new_key_block(BlockHandle handle) override { td::actor::send_closure(id_, &FullNodeImpl::new_key_block, std::move(handle)); diff --git a/validator/full-node.hpp b/validator/full-node.hpp index 47cca0f5..49dad3ba 100644 --- a/validator/full-node.hpp +++ b/validator/full-node.hpp @@ -73,6 +73,8 @@ class FullNodeImpl : public FullNode { void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise); void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, td::Promise promise); + void download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise); void got_key_block_proof(td::Ref proof); void got_zero_block_state(td::Ref state); diff --git a/validator/impl/CMakeLists.txt b/validator/impl/CMakeLists.txt index 224599f8..4e3fd04f 100644 --- a/validator/impl/CMakeLists.txt +++ b/validator/impl/CMakeLists.txt @@ -15,6 +15,7 @@ set(TON_VALIDATOR_SOURCE ihr-message.cpp liteserver.cpp message-queue.cpp + out-msg-queue-proof.cpp proof.cpp shard.cpp signature-set.cpp @@ -32,13 +33,13 @@ set(TON_VALIDATOR_SOURCE ihr-message.hpp liteserver.hpp message-queue.hpp + out-msg-queue-proof.hpp proof.hpp shard.hpp signature-set.hpp top-shard-descr.hpp validate-query.hpp - validator-set.hpp -) + validator-set.hpp) add_library(ton_validator STATIC ${TON_VALIDATOR_SOURCE}) diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index 8a17e953..c19199c8 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -205,7 +205,7 @@ class Collator final : public td::actor::Actor { std::vector bad_ext_msgs_, delay_ext_msgs_; Ref shard_account_blocks_; // ShardAccountBlocks - std::map> blocks_with_state_proofs_; + std::map> block_state_proofs_; std::vector neighbor_proof_builders_; std::vector> collated_roots_; @@ -238,8 +238,7 @@ class Collator final : public td::actor::Actor { void after_get_aux_shard_state(ton::BlockIdExt blkid, td::Result> res); bool fix_one_processed_upto(block::MsgProcessedUpto& proc, const ton::ShardIdFull& owner); bool fix_processed_upto(block::MsgProcessedUptoCollection& upto); - void got_neighbor_block_data(td::Result> res); - void got_neighbor_block_state(int i, td::Result> res); + void got_neighbor_msg_queue(unsigned i, td::Result> R); bool adjust_shard_config(); bool store_shard_fees(ShardIdFull shard, const block::CurrencyCollection& fees, const block::CurrencyCollection& created); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 49c94596..e116a935 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -501,7 +501,13 @@ void Collator::after_get_block_data(int idx, td::Result> res) { prev_mc_block = prev_block_data[0]; mc_block_root = prev_mc_block->root_cell(); } - blocks_with_state_proofs_[prev_block_data[idx]->root_cell()->get_hash().bits()] = prev_block_data[idx]; + Ref root = prev_block_data[idx]->root_cell(); + auto proof = create_block_state_proof(root); + if (proof.is_error()) { + fatal_error(proof.move_as_error()); + return; + } + block_state_proofs_.emplace(root->get_hash().bits(), proof.move_as_ok()); } check_pending(); } @@ -613,54 +619,39 @@ bool Collator::request_neighbor_msg_queues() { } neighbors_.emplace_back(*shard_ptr); } - int i = 0; + unsigned i = 0; neighbor_proof_builders_.resize(neighbors_.size()); for (block::McShardDescr& descr : neighbors_) { LOG(DEBUG) << "neighbor #" << i << " : " << descr.blk_.to_str(); - if (descr.blk_.seqno() != 0) { - ++pending; - send_closure_later(manager, &ValidatorManager::wait_block_data_short, descr.blk_, priority(), timeout, - [self = get_self(), i](td::Result> res) { - LOG(DEBUG) << "got answer to wait_block_data for neighbor #" << i; - send_closure_later(std::move(self), &Collator::got_neighbor_block_data, std::move(res)); - }); - } ++pending; - send_closure_later(manager, &ValidatorManager::wait_block_state_short, descr.blk_, priority(), timeout, - [self = get_self(), i](td::Result> res) { - LOG(DEBUG) << "got answer to wait_block_state for neighbor #" << i; - send_closure_later(std::move(self), &Collator::got_neighbor_block_state, i, std::move(res)); + send_closure_later(manager, &ValidatorManager::wait_out_msg_queue_proof, descr.blk_, shard_, priority(), timeout, + [self = get_self(), i](td::Result> res) { + LOG(DEBUG) << "got msg queue for neighbor #" << i; + send_closure_later(std::move(self), &Collator::got_neighbor_msg_queue, i, std::move(res)); }); ++i; } return true; } -void Collator::got_neighbor_block_data(td::Result> res) { +void Collator::got_neighbor_msg_queue(unsigned i, td::Result> R) { --pending; - if (res.is_error()) { - fatal_error(res.move_as_error()); + if (R.is_error()) { + fatal_error(R.move_as_error()); return; } - auto block_data = res.move_as_ok(); - blocks_with_state_proofs_[block_data->root_cell()->get_hash().bits()] = block_data; - check_pending(); -} - -void Collator::got_neighbor_block_state(int i, td::Result> res) { - --pending; - if (res.is_error()) { - fatal_error(res.move_as_error()); + auto res = R.move_as_ok(); + BlockIdExt block_id = neighbors_.at(i).blk_; + if (res->block_state_proof_.not_null()) { + block_state_proofs_.emplace(block_id.root_hash, res->block_state_proof_); + } + neighbor_proof_builders_.at(i) = vm::MerkleProofBuilder{res->state_root_}; + auto state = ShardStateQ::fetch(block_id, {}, neighbor_proof_builders_.at(i).root()); + if (state.is_error()) { + fatal_error(state.move_as_error()); return; } - Ref state = res.move_as_ok(); - neighbor_proof_builders_.at(i) = vm::MerkleProofBuilder{state->root_cell()}; - auto new_state = ShardStateQ::fetch(state->get_block_id(), {}, neighbor_proof_builders_.at(i).root()); - if (new_state.is_error()) { - fatal_error(new_state.move_as_error()); - return; - } - auto outq_descr_res = new_state.move_as_ok()->message_queue(); + auto outq_descr_res = state.move_as_ok()->message_queue(); if (outq_descr_res.is_error()) { fatal_error(outq_descr_res.move_as_error()); return; @@ -3992,17 +3983,8 @@ bool Collator::create_collated_data() { collated_roots_.push_back(std::move(cell)); } // 2. Proofs for hashes of states: previous states + neighbors - for (const auto& p : blocks_with_state_proofs_) { - vm::MerkleProofBuilder mpb{p.second->root_cell()}; - block::gen::Block::Record block; - if (!tlb::unpack_cell(mpb.root(), block) || block.state_update->load_cell().is_error()) { - return fatal_error("cannot generate Merkle proof for previous block"); - } - Ref proof = mpb.extract_proof(); - if (proof.is_null()) { - return fatal_error("cannot generate Merkle proof for previous block"); - } - collated_roots_.push_back(std::move(proof)); + for (const auto& p : block_state_proofs_) { + collated_roots_.push_back(p.second); } // 3. Previous state proof (only shadchains) std::map> proofs; diff --git a/validator/impl/out-msg-queue-proof.cpp b/validator/impl/out-msg-queue-proof.cpp new file mode 100644 index 00000000..0c3c993c --- /dev/null +++ b/validator/impl/out-msg-queue-proof.cpp @@ -0,0 +1,245 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . +*/ +#include "out-msg-queue-proof.hpp" +#include "interfaces/proof.h" +#include "shard.hpp" +#include "vm/cells/MerkleProof.h" +#include "common/delay.h" +#include "interfaces/validator-manager.h" + +namespace ton { + +namespace validator { + +td::Result> OutMsgQueueProof::fetch(BlockIdExt block_id, ShardIdFull dst_shard, + const ton_api::tonNode_outMsgQueueProof &f) { + Ref block_state_proof; + td::Bits256 state_root_hash; + if (block_id.seqno() == 0) { + if (!f.block_state_proof_.empty()) { + return td::Status::Error("expected empty block state proof"); + } + state_root_hash = block_id.root_hash; + } else { + TRY_RESULT_ASSIGN(block_state_proof, vm::std_boc_deserialize(f.block_state_proof_.as_slice())); + TRY_RESULT_ASSIGN(state_root_hash, unpack_block_state_proof(block_id, block_state_proof)); + } + + TRY_RESULT(queue_proof, vm::std_boc_deserialize(f.queue_proof_.as_slice())); + auto state_root = vm::MerkleProof::virtualize(queue_proof, 1); + if (state_root.is_null()) { + return td::Status::Error("invalid queue proof"); + } + if (state_root->get_hash().as_slice() != state_root_hash.as_slice()) { + return td::Status::Error("state root hash mismatch"); + } + + // TODO: validate + return Ref(true, std::move(state_root), std::move(block_state_proof)); +} + +td::Result> OutMsgQueueProof::serialize( + BlockIdExt block_id, ShardIdFull dst_shard, Ref state_root, Ref block_root) { + vm::MerkleProofBuilder mpb{std::move(state_root)}; + TRY_RESULT(state, ShardStateQ::fetch(block_id, {}, mpb.root())); + TRY_RESULT(outq_descr, state->message_queue()); + + // TODO: add only required part of msg queue + td::HashSet visited; + std::function)> dfs = [&](Ref cell) { + if (!visited.insert(cell->get_hash()).second) { + return; + } + vm::CellSlice cs(vm::NoVm(), cell); + for (unsigned i = 0; i < cs.size_refs(); i++) { + dfs(cs.prefetch_ref(i)); + } + }; + dfs(outq_descr->root_cell()); + + TRY_RESULT(queue_proof, vm::std_boc_serialize(mpb.extract_proof())); + + td::BufferSlice block_state_proof; + if (block_id.seqno() != 0) { + TRY_RESULT(proof, create_block_state_proof(std::move(block_root))); + TRY_RESULT_ASSIGN(block_state_proof, vm::std_boc_serialize(std::move(proof), 31)); + } + + return create_tl_object(std::move(queue_proof), std::move(block_state_proof)); +} + +void WaitOutMsgQueueProof::alarm() { + abort_query(td::Status::Error(ErrorCode::timeout, "timeout")); +} + +void WaitOutMsgQueueProof::abort_query(td::Status reason) { + if (promise_) { + if (priority_ > 0 || (reason.code() != ErrorCode::timeout && reason.code() != ErrorCode::notready)) { + LOG(WARNING) << "aborting wait msg queue query for " << block_id_.to_str() << " priority=" << priority_ << ": " + << reason; + } else { + LOG(DEBUG) << "aborting wait msg queue query for " << block_id_.to_str() << " priority=" << priority_ << ": " + << reason; + } + promise_.set_error( + reason.move_as_error_prefix(PSTRING() << "failed to get msg queue for " << block_id_.to_str() << ": ")); + } + stop(); +} + +void WaitOutMsgQueueProof::finish_query(Ref result) { + promise_.set_result(std::move(result)); + stop(); +} + +void WaitOutMsgQueueProof::start_up() { + alarm_timestamp() = timeout_; + if (local_) { + run_local(); + } else { + run_net(); + } +} + +void WaitOutMsgQueueProof::run_local() { + ++pending; + td::actor::send_closure(manager_, &ValidatorManager::wait_block_state_short, block_id_, priority_, timeout_, + [SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &WaitOutMsgQueueProof::abort_query, + R.move_as_error_prefix("failed to get shard state")); + } else { + td::actor::send_closure(SelfId, &WaitOutMsgQueueProof::got_state_root, + R.move_as_ok()->root_cell()); + } + }); + if (block_id_.seqno() != 0) { + ++pending; + td::actor::send_closure(manager_, &ValidatorManager::wait_block_data_short, block_id_, priority_, timeout_, + [SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &WaitOutMsgQueueProof::abort_query, + R.move_as_error_prefix("failed to get block data")); + } else { + td::actor::send_closure(SelfId, &WaitOutMsgQueueProof::got_block_root, + R.move_as_ok()->root_cell()); + } + }); + } +} + +void WaitOutMsgQueueProof::got_state_root(Ref root) { + state_root_ = std::move(root); + if (--pending == 0) { + run_local_cont(); + } +} + +void WaitOutMsgQueueProof::got_block_root(Ref root) { + block_root_ = std::move(root); + if (--pending == 0) { + run_local_cont(); + } +} + +void WaitOutMsgQueueProof::run_local_cont() { + Ref block_state_proof; + if (block_id_.seqno() != 0) { + auto R = create_block_state_proof(std::move(block_root_)); + if (R.is_error()) { + abort_query(R.move_as_error_prefix("failed to create block state proof")); + return; + } + block_state_proof = R.move_as_ok(); + } + finish_query(td::Ref(true, std::move(state_root_), std::move(block_state_proof))); +} + +void WaitOutMsgQueueProof::run_net() { + auto P = + td::PromiseCreator::lambda([SelfId = actor_id(this), block_id = block_id_](td::Result> R) { + if (R.is_error()) { + LOG(DEBUG) << "failed to get msg queue for " << block_id.to_str() << " from net: " << R.move_as_error(); + delay_action([SelfId]() mutable { td::actor::send_closure(SelfId, &WaitOutMsgQueueProof::run_net); }, + td::Timestamp::in(0.1)); + } else { + td::actor::send_closure(SelfId, &WaitOutMsgQueueProof::finish_query, R.move_as_ok()); + } + }); + + td::actor::send_closure(manager_, &ValidatorManager::send_get_out_msg_queue_proof_request, block_id_, dst_shard_, + priority_, std::move(P)); +} + +void BuildOutMsgQueueProof::abort_query(td::Status reason) { + if (promise_) { + LOG(WARNING) << "failed to build msg queue proof for " << block_id_.to_str() << ": " << reason; + promise_.set_error( + reason.move_as_error_prefix(PSTRING() << "failed to build msg queue proof for " << block_id_.to_str() << ": ")); + } + stop(); +} + +void BuildOutMsgQueueProof::start_up() { + ++pending; + td::actor::send_closure(manager_, &ValidatorManagerInterface::get_shard_state_from_db_short, block_id_, + [SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &BuildOutMsgQueueProof::abort_query, + R.move_as_error_prefix("failed to get shard state")); + } else { + td::actor::send_closure(SelfId, &BuildOutMsgQueueProof::got_state_root, + R.move_as_ok()->root_cell()); + } + }); + if (block_id_.seqno() != 0) { + ++pending; + td::actor::send_closure(manager_, &ValidatorManagerInterface::get_block_data_from_db_short, block_id_, + [SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &BuildOutMsgQueueProof::abort_query, + R.move_as_error_prefix("failed to get block data")); + } else { + td::actor::send_closure(SelfId, &BuildOutMsgQueueProof::got_block_root, + R.move_as_ok()->root_cell()); + } + }); + } +} + +void BuildOutMsgQueueProof::got_state_root(Ref root) { + state_root_ = std::move(root); + if (--pending == 0) { + build_proof(); + } +} + +void BuildOutMsgQueueProof::got_block_root(Ref root) { + block_root_ = std::move(root); + if (--pending == 0) { + build_proof(); + } +} + +void BuildOutMsgQueueProof::build_proof() { + promise_.set_result( + OutMsgQueueProof::serialize(block_id_, dst_shard_, std::move(state_root_), std::move(block_root_))); + stop(); +} + +} // namespace validator +} // namespace ton diff --git a/validator/impl/out-msg-queue-proof.hpp b/validator/impl/out-msg-queue-proof.hpp new file mode 100644 index 00000000..6a18ca6c --- /dev/null +++ b/validator/impl/out-msg-queue-proof.hpp @@ -0,0 +1,109 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . +*/ +#pragma once +#include "vm/cells.h" +#include "ton/ton-types.h" +#include "auto/tl/ton_api.h" +#include "interfaces/out-msg-queue-proof.h" +#include "td/actor/actor.h" + +namespace ton { + +namespace validator { +using td::Ref; + +class ValidatorManager; +class ValidatorManagerInterface; + +class WaitOutMsgQueueProof : public td::actor::Actor { + public: + WaitOutMsgQueueProof(BlockIdExt block_id, ShardIdFull dst_shard, bool local, td::uint32 priority, + td::actor::ActorId manager, td::Timestamp timeout, + td::Promise> promise) + : block_id_(std::move(block_id)) + , dst_shard_(dst_shard) + , local_(local) + , priority_(priority) + , manager_(manager) + , timeout_(timeout) + , promise_(std::move(promise)) { + } + + void update_timeout(td::Timestamp timeout, td::uint32 priority) { + timeout_ = timeout; + alarm_timestamp() = timeout_; + priority_ = priority; + } + + void abort_query(td::Status reason); + void finish_query(Ref result); + void alarm() override; + + void start_up() override; + + void run_local(); + void got_state_root(Ref root); + void got_block_root(Ref root); + void run_local_cont(); + + void run_net(); + + + private: + BlockIdExt block_id_; + ShardIdFull dst_shard_; + bool local_; + td::uint32 priority_; + + td::actor::ActorId manager_; + td::Timestamp timeout_; + td::Promise> promise_; + + Ref state_root_, block_root_; + unsigned pending = 0; +}; + +class BuildOutMsgQueueProof : public td::actor::Actor { + public: + BuildOutMsgQueueProof(BlockIdExt block_id, ShardIdFull dst_shard, + td::actor::ActorId manager, + td::Promise> promise) + : block_id_(std::move(block_id)) + , dst_shard_(dst_shard) + , manager_(manager) + , promise_(std::move(promise)) { + } + + void abort_query(td::Status reason); + void start_up() override; + void got_state_root(Ref root); + void got_block_root(Ref root); + void build_proof(); + + private: + BlockIdExt block_id_; + ShardIdFull dst_shard_; + + td::actor::ActorId manager_; + td::Promise> promise_; + + Ref state_root_, block_root_; + unsigned pending = 0; +}; + +} // namespace validator +} // namespace ton diff --git a/validator/impl/proof.cpp b/validator/impl/proof.cpp index 033a1ab1..2297b48c 100644 --- a/validator/impl/proof.cpp +++ b/validator/impl/proof.cpp @@ -162,5 +162,40 @@ td::Result> ProofQ::get_signatures_root() const { return proof.signatures->prefetch_ref(); } +td::Result> create_block_state_proof(td::Ref root) { + if (root.is_null()) { + return td::Status::Error("root is null"); + } + vm::MerkleProofBuilder mpb{std::move(root)}; + block::gen::Block::Record block; + if (!tlb::unpack_cell(mpb.root(), block) || block.state_update->load_cell().is_error()) { + return td::Status::Error("invalid block"); + } + Ref proof = mpb.extract_proof(); + if (proof.is_null()) { + return td::Status::Error("failed to create proof"); + } + return proof; +} + +td::Result unpack_block_state_proof(BlockIdExt block_id, td::Ref proof) { + auto virt_root = vm::MerkleProof::virtualize(proof, 1); + if (virt_root.is_null()) { + return td::Status::Error("invalid Merkle proof"); + } + if (virt_root->get_hash().as_slice() != block_id.root_hash.as_slice()) { + return td::Status::Error("hash mismatch"); + } + block::gen::Block::Record block; + if (!tlb::unpack_cell(virt_root, block)) { + return td::Status::Error("invalid block"); + } + vm::CellSlice upd_cs{vm::NoVmSpec(), block.state_update}; + if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 && upd_cs.size_ext() == 0x20228)) { + return td::Status::Error("invalid Merkle update"); + } + return upd_cs.prefetch_ref(1)->get_hash(0).bits(); +} + } // namespace validator } // namespace ton diff --git a/validator/interfaces/out-msg-queue-proof.h b/validator/interfaces/out-msg-queue-proof.h new file mode 100644 index 00000000..1fece66a --- /dev/null +++ b/validator/interfaces/out-msg-queue-proof.h @@ -0,0 +1,42 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . +*/ +#pragma once +#include "vm/cells.h" +#include "ton/ton-types.h" +#include "auto/tl/ton_api.h" + +namespace ton { + +namespace validator { +using td::Ref; + +struct OutMsgQueueProof : public td::CntObject { + OutMsgQueueProof(Ref state_root, Ref block_state_proof) + : state_root_(std::move(state_root)), block_state_proof_(std::move(block_state_proof)) { + } + + Ref state_root_; + Ref block_state_proof_; + + static td::Result> fetch(BlockIdExt block_id, ShardIdFull dst_shard, + const ton_api::tonNode_outMsgQueueProof &f); + static td::Result> serialize( + BlockIdExt block_id, ShardIdFull dst_shard, Ref state_root, Ref block_root); +}; + +} // namespace validator +} // namespace ton diff --git a/validator/interfaces/proof.h b/validator/interfaces/proof.h index 99471a1f..6665ad08 100644 --- a/validator/interfaces/proof.h +++ b/validator/interfaces/proof.h @@ -48,6 +48,9 @@ class Proof : virtual public ProofLink { virtual td::Result> export_as_proof_link() const = 0; }; +td::Result> create_block_state_proof(td::Ref root); +td::Result unpack_block_state_proof(BlockIdExt block_id, td::Ref proof); + } // namespace validator } // namespace ton diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index 209f871f..362c259f 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -29,6 +29,7 @@ #include "liteserver.h" #include "crypto/vm/db/DynamicBagOfCellsDb.h" #include "validator-session/validator-session-types.h" +#include "impl/out-msg-queue-proof.hpp" namespace ton { @@ -128,6 +129,8 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void send_ihr_message(td::Ref message) = 0; virtual void send_top_shard_block_description(td::Ref desc) = 0; virtual void send_block_broadcast(BlockBroadcast broadcast) = 0; + virtual void send_get_out_msg_queue_proof_request(BlockIdExt id, ShardIdFull dst_shard, td::uint32 priority, + td::Promise> promise) = 0; virtual void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) = 0; virtual void get_shard_client_state(bool from_db, td::Promise promise) = 0; diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index 501ee078..d8f75eef 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -152,6 +152,10 @@ class ValidatorManagerImpl : public ValidatorManager { td::Promise> promise) override; void wait_block_state_short(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; + void wait_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::uint32 priority, td::Timestamp timeout, + td::Promise> promise) override { + UNREACHABLE(); + } void set_block_data(BlockHandle handle, td::Ref data, td::Promise promise) override; void wait_block_data(BlockHandle handle, td::uint32 priority, td::Timestamp, @@ -250,6 +254,10 @@ class ValidatorManagerImpl : public ValidatorManager { void send_top_shard_block_description(td::Ref desc) override; void send_block_broadcast(BlockBroadcast broadcast) override { } + void send_get_out_msg_queue_proof_request(BlockIdExt id, ShardIdFull dst_shard, td::uint32 priority, + td::Promise> promise) override { + UNREACHABLE(); + } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; void get_shard_client_state(bool from_db, td::Promise promise) override; diff --git a/validator/manager-hardfork.hpp b/validator/manager-hardfork.hpp index 67782771..87ec8c2b 100644 --- a/validator/manager-hardfork.hpp +++ b/validator/manager-hardfork.hpp @@ -182,6 +182,10 @@ class ValidatorManagerImpl : public ValidatorManager { td::Promise> promise) override; void wait_block_state_short(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; + void wait_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::uint32 priority, td::Timestamp timeout, + td::Promise> promise) override { + UNREACHABLE(); + } void set_block_data(BlockHandle handle, td::Ref data, td::Promise promise) override { UNREACHABLE(); @@ -314,6 +318,10 @@ class ValidatorManagerImpl : public ValidatorManager { } void send_block_broadcast(BlockBroadcast broadcast) override { } + void send_get_out_msg_queue_proof_request(BlockIdExt id, ShardIdFull dst_shard, td::uint32 priority, + td::Promise> promise) override { + UNREACHABLE(); + } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override { UNREACHABLE(); diff --git a/validator/manager.cpp b/validator/manager.cpp index 7c5e7d3f..64ee3273 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -453,24 +453,18 @@ void ValidatorManagerImpl::add_shard_block_description(td::Refblock_id().shard_full(), desc->catchain_seqno()}] = desc; VLOG(VALIDATOR_DEBUG) << "new shard block descr for " << desc->block_id(); - if (opts_->need_monitor(desc->block_id().shard_full()) && last_masterchain_block_handle_ && - last_masterchain_seqno_ > 0 && desc->generated_at() < last_masterchain_block_handle_->unix_time() + 60) { - delay_action( - [SelfId = actor_id(this), desc]() { - auto P = td::PromiseCreator::lambda([](td::Result> R) { - if (R.is_error()) { - auto S = R.move_as_error(); - if (S.code() != ErrorCode::timeout && S.code() != ErrorCode::notready) { - VLOG(VALIDATOR_NOTICE) << "failed to get shard state: " << S; - } else { - VLOG(VALIDATOR_DEBUG) << "failed to get shard state: " << S; - } - } - }); - td::actor::send_closure(SelfId, &ValidatorManager::wait_block_state_short, desc->block_id(), 0, - td::Timestamp::in(60.0), std::move(P)); - }, - td::Timestamp::in(1.0)); + if (opts_->need_monitor(desc->block_id().shard_full())) { + auto P = td::PromiseCreator::lambda([](td::Result> R) { + if (R.is_error()) { + auto S = R.move_as_error(); + if (S.code() != ErrorCode::timeout && S.code() != ErrorCode::notready) { + VLOG(VALIDATOR_NOTICE) << "failed to get shard state: " << S; + } else { + VLOG(VALIDATOR_DEBUG) << "failed to get shard state: " << S; + } + } + }); + wait_block_state_short(desc->block_id(), 0, td::Timestamp::in(60.0), std::move(P)); } } } @@ -606,6 +600,30 @@ void ValidatorManagerImpl::wait_block_state_short(BlockIdExt block_id, td::uint3 get_block_handle(block_id, true, std::move(P)); } +void ValidatorManagerImpl::wait_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::uint32 priority, + td::Timestamp timeout, + td::Promise> promise) { + auto key = std::make_pair(block_id, dst_shard); + auto it = wait_out_msg_queue_proof_.find(key); + if (it == wait_out_msg_queue_proof_.end()) { + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), block_id, dst_shard](td::Result> R) { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::finished_wait_msg_queue, block_id, dst_shard, + std::move(R)); + }); + auto id = td::actor::create_actor("waitmsgqueue", block_id, dst_shard, + opts_->need_monitor(block_id.shard_full()), priority, + actor_id(this), td::Timestamp::in(10.0), std::move(P)) + .release(); + wait_out_msg_queue_proof_[key].actor_ = id; + it = wait_out_msg_queue_proof_.find(key); + } + + it->second.waiting_.emplace_back(timeout, priority, std::move(promise)); + auto X = it->second.get_timeout(); + td::actor::send_closure(it->second.actor_, &WaitOutMsgQueueProof::update_timeout, X.first, X.second); +} + void ValidatorManagerImpl::wait_block_data(BlockHandle handle, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) { auto it = wait_block_data_.find(handle->id()); @@ -1037,6 +1055,40 @@ void ValidatorManagerImpl::finished_wait_data(BlockHandle handle, td::Result> R) { + auto it = wait_out_msg_queue_proof_.find({block_id, dst_shard}); + if (it != wait_out_msg_queue_proof_.end()) { + if (R.is_error()) { + auto S = R.move_as_error(); + if (S.code() != ErrorCode::timeout) { + for (auto &X : it->second.waiting_) { + X.promise.set_error(S.clone()); + } + } else { + auto X = it->second.get_timeout(); + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), block_id, dst_shard](td::Result> R) { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::finished_wait_msg_queue, block_id, dst_shard, + std::move(R)); + }); + auto id = td::actor::create_actor("waitmsgqueue", block_id, dst_shard, + opts_->need_monitor(block_id.shard_full()), X.second, + actor_id(this), X.first, std::move(P)) + .release(); + it->second.actor_ = id; + return; + } + } else { + auto r = R.move_as_ok(); + for (auto &X : it->second.waiting_) { + X.promise.set_result(r); + } + } + wait_out_msg_queue_proof_.erase(it); + } +} + void ValidatorManagerImpl::set_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) { auto P = td::PromiseCreator::lambda( @@ -1410,6 +1462,12 @@ void ValidatorManagerImpl::send_block_broadcast(BlockBroadcast broadcast) { callback_->send_broadcast(std::move(broadcast)); } +void ValidatorManagerImpl::send_get_out_msg_queue_proof_request(BlockIdExt id, ShardIdFull dst_shard, + td::uint32 priority, + td::Promise> promise) { + callback_->download_out_msg_queue_proof(id, dst_shard, td::Timestamp::in(10.0), std::move(promise)); +} + void ValidatorManagerImpl::start_up() { db_ = create_db_actor(actor_id(this), db_root_); lite_server_cache_ = create_liteserver_cache_actor(actor_id(this), db_root_); diff --git a/validator/manager.hpp b/validator/manager.hpp index 1105b2b6..327aa3c5 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -183,6 +183,8 @@ class ValidatorManagerImpl : public ValidatorManager { }; std::map>> wait_state_; std::map>> wait_block_data_; + std::map, WaitList>> + wait_out_msg_queue_proof_; struct WaitBlockHandle { std::vector> waiting_; @@ -357,6 +359,8 @@ class ValidatorManagerImpl : public ValidatorManager { td::Promise> promise) override; void wait_block_state_short(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; + void wait_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::uint32 priority, td::Timestamp timeout, + td::Promise> promise) override; void set_block_data(BlockHandle handle, td::Ref data, td::Promise promise) override; void wait_block_data(BlockHandle handle, td::uint32 priority, td::Timestamp, @@ -444,6 +448,8 @@ class ValidatorManagerImpl : public ValidatorManager { void send_ihr_message(td::Ref message) override; void send_top_shard_block_description(td::Ref desc) override; void send_block_broadcast(BlockBroadcast broadcast) override; + void send_get_out_msg_queue_proof_request(BlockIdExt id, ShardIdFull dst_shard, td::uint32 priority, + td::Promise> promise) override; void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; void get_shard_client_state(bool from_db, td::Promise promise) override; @@ -478,6 +484,7 @@ class ValidatorManagerImpl : public ValidatorManager { void finished_wait_state(BlockHandle handle, td::Result> R); void finished_wait_data(BlockHandle handle, td::Result> R); + void finished_wait_msg_queue(BlockIdExt block_id, ShardIdFull dst_shard, td::Result> R); void start_up() override; void started(ValidatorManagerInitResult result); diff --git a/validator/validator.h b/validator/validator.h index ab013cb0..6804ae80 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -34,6 +34,7 @@ #include "interfaces/proof.h" #include "interfaces/shard.h" #include "catchain/catchain-types.h" +#include "interfaces/out-msg-queue-proof.h" namespace ton { @@ -131,6 +132,8 @@ class ValidatorManagerInterface : public td::actor::Actor { td::Promise> promise) = 0; virtual void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, td::Promise promise) = 0; + virtual void download_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::Timestamp timeout, + td::Promise> promise) = 0; virtual void new_key_block(BlockHandle handle) = 0; }; @@ -212,6 +215,10 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void wait_block_state_short(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) = 0; + virtual void wait_out_msg_queue_proof(BlockIdExt block_id, ShardIdFull dst_shard, td::uint32 priority, + td::Timestamp timeout, + 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, td::Promise promise) = 0;