From c26855a2e827bee8d774c189599d4f0a8633aad5 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 29 Sep 2025 15:14:40 +0300 Subject: [PATCH 01/13] Deduplicate cells in collated data during one validator session --- create-hardfork/create-hardfork.cpp | 8 +- crypto/block/block.tlb | 8 +- crypto/block/mc-config.cpp | 1 + crypto/common/refcnt.hpp | 6 +- crypto/vm/boc-compression.cpp | 2 +- crypto/vm/boc.cpp | 94 ++++-- crypto/vm/boc.h | 13 +- crypto/vm/cells/ExtCell.h | 12 + overlay/overlay-peers.cpp | 8 +- tdutils/td/utils/LRUCache.h | 11 + tdutils/td/utils/optional.h | 7 + test/test-ton-collator.cpp | 8 +- tl/generate/scheme/ton_api.tl | 18 +- tl/generate/scheme/ton_api.tlo | Bin 118988 -> 119332 bytes ton/ton-types.h | 1 + validator-session/candidate-serializer.cpp | 2 +- validator-session/validator-session-types.h | 1 + validator-session/validator-session.cpp | 143 ++++++++- validator-session/validator-session.h | 4 + validator-session/validator-session.hpp | 9 + validator/collation-manager.cpp | 146 ++++----- validator/collation-manager.hpp | 20 +- .../collator-node/collator-node-session.cpp | 195 ++++++++++-- .../collator-node/collator-node-session.hpp | 17 ++ validator/collator-node/collator-node.cpp | 34 ++- validator/collator-node/collator-node.hpp | 2 + validator/fabric.h | 7 +- validator/full-node-fast-sync-overlays.cpp | 125 ++++++-- validator/full-node-fast-sync-overlays.hpp | 24 +- validator/full-node-private-overlay.cpp | 60 ++-- validator/full-node-private-overlay.hpp | 20 +- validator/full-node-serializer.cpp | 194 +++++++++--- validator/full-node-serializer.hpp | 11 +- validator/full-node-shard.cpp | 27 +- validator/full-node-shard.h | 5 +- validator/full-node-shard.hpp | 9 +- validator/full-node.cpp | 71 +++-- validator/full-node.h | 5 +- validator/full-node.hpp | 13 +- validator/impl/CMakeLists.txt | 2 + validator/impl/collated-data-merger.cpp | 192 ++++++++++++ validator/impl/collated-data-merger.h | 62 ++++ validator/impl/collator-impl.h | 10 +- validator/impl/collator.cpp | 280 +++++++++++------- validator/impl/validate-query.cpp | 173 +++++++++-- validator/impl/validate-query.hpp | 14 +- validator/interfaces/validator-manager.h | 10 +- validator/manager-disk.cpp | 12 +- validator/manager-disk.hpp | 9 +- validator/manager-hardfork.cpp | 4 + validator/manager-hardfork.hpp | 13 +- validator/manager.cpp | 134 ++++++--- validator/manager.hpp | 24 +- validator/validator-group.cpp | 211 ++++++++++--- validator/validator-group.hpp | 27 +- validator/validator.h | 12 +- 56 files changed, 1907 insertions(+), 623 deletions(-) create mode 100644 validator/impl/collated-data-merger.cpp create mode 100644 validator/impl/collated-data-merger.h diff --git a/create-hardfork/create-hardfork.cpp b/create-hardfork/create-hardfork.cpp index aadf877ca..a3ed7c36c 100644 --- a/create-hardfork/create-hardfork.cpp +++ b/create-hardfork/create-hardfork.cpp @@ -246,8 +246,9 @@ class HardforkCreator : public td::actor::Actor { } void send_shard_block_info(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::BufferSlice data) override { } - void send_block_candidate(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) override { + void send_block_candidate_broadcast(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data, int mode) override { } void send_broadcast(ton::BlockBroadcast broadcast, int mode) override { } @@ -277,6 +278,9 @@ class HardforkCreator : public td::actor::Actor { ton::ShardIdFull dst_shard, std::vector blocks, block::ImportedMsgQueueLimits limits, td::Timestamp timeout, td::Promise>> promise) override { } + void download_block_candidate(ton::BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) override { + } void new_key_block(ton::validator::BlockHandle handle) override { } diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 03b92cd2b..30905045b 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -451,7 +451,7 @@ block_info#9bc7a987 version:uint32 after_split:(## 1) want_split:Bool want_merge:Bool key_block:Bool vert_seqno_incr:(## 1) - flags:(## 8) { flags <= 1 } + flags:(## 8) { flags <= 3 } seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr } { prev_seq_no:# } { ~prev_seq_no + 1 = seq_no } shard:ShardIdent gen_utime:uint32 @@ -464,6 +464,7 @@ block_info#9bc7a987 version:uint32 master_ref:not_master?^BlkMasterInfo prev_ref:^(BlkPrevInfo after_merge) prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0) + collated_data_hash:flags . 1?bits256 = BlockInfo; prev_blk_info$_ prev:ExtBlkRef = BlkPrevInfo 0; @@ -772,7 +773,7 @@ consensus_config_v3#d8 flags:(## 7) { flags = 0 } new_catchain_ids:Bool max_block_bytes:uint32 max_collated_bytes:uint32 proto_version:uint16 = ConsensusConfig; -consensus_config_v4#d9 flags:(## 7) { flags = 0 } new_catchain_ids:Bool +consensus_config_v4#d9 flags:(## 6) { flags = 0 } merge_collated_data:Bool new_catchain_ids:Bool round_candidates:(## 8) { round_candidates >= 1 } next_candidate_delay_ms:uint32 consensus_timeout_ms:uint32 fast_attempts:uint32 attempt_duration:uint32 catchain_max_deps:uint32 @@ -857,6 +858,9 @@ top_block_descr#d5 proof_for:BlockIdExt signatures:(Maybe ^BlockSignatures) // top_block_descr_set#4ac789f3 collection:(HashmapE 96 ^TopBlockDescr) = TopBlockDescrSet; account_storage_dict_proof#37c1e3fc proof:^Cell = AccountStorageDictProof; +collated_data_root_state#4b2f36ec hash:bits256 = CollatedDataRootState; +collated_data_root_storage_dict#796eaeb6 hash:bits256 = CollatedDataRootStorageDict; +collated_data_separator#fa8b2b92 = CollatedDataSeparator; // // VALIDATOR MISBEHAVIOR COMPLAINTS diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index d9a67b0be..e401847c9 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -336,6 +336,7 @@ ton::ValidatorSessionConfig Config::get_consensus_config() const { td::uint64 catchain_lifetime = std::max(catchain_config.mc_cc_lifetime, catchain_config.shard_cc_lifetime); c.catchain_opts.max_block_height_coeff = catchain_lifetime * max_blocks_coeff; } + c.merge_collated_data = r.merge_collated_data; }; if (cc.not_null()) { block::gen::ConsensusConfig::Record_consensus_config_v4 r4; diff --git a/crypto/common/refcnt.hpp b/crypto/common/refcnt.hpp index 953cc7797..0456ce958 100644 --- a/crypto/common/refcnt.hpp +++ b/crypto/common/refcnt.hpp @@ -285,14 +285,14 @@ class Ref { Ref& operator=(Ref&& r); const typename RefValue::Type* operator->() const { if (!ptr) { - CHECK(ptr && "deferencing null Ref"); + LOG_CHECK(ptr) << "dereferencing null Ref<" << typeid(T).name() << ">"; throw NullRef{}; } return RefValue::make_const_ptr(ptr); } const typename RefValue::Type& operator*() const { if (!ptr) { - CHECK(ptr && "deferencing null Ref"); + LOG_CHECK(ptr) << "dereferencing null Ref<" << typeid(T).name() << ">"; throw NullRef{}; } return RefValue::make_const_ref(ptr); @@ -308,7 +308,7 @@ class Ref { } bool is_unique() const { if (!ptr) { - CHECK(ptr && "defererencing null Ref"); + LOG_CHECK(ptr) << "dereferencing null Ref<" << typeid(T).name() << ">"; throw NullRef{}; } return ptr->is_unique(); diff --git a/crypto/vm/boc-compression.cpp b/crypto/vm/boc-compression.cpp index d37e0380a..2559c0faf 100644 --- a/crypto/vm/boc-compression.cpp +++ b/crypto/vm/boc-compression.cpp @@ -57,7 +57,7 @@ td::Result>> boc_decompress_baseline_lz4(td::Slice } TRY_RESULT(decompressed, td::lz4_decompress(compressed, decompressed_size)); - TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed)); + TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); return roots; } diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index 93fc0a89d..4263bcfb6 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -20,6 +20,9 @@ #include #include #include "vm/boc.h" + +#include "cells/MerkleProof.h" +#include "cells/PrunnedCell.h" #include "vm/boc-writers.h" #include "vm/cells.h" #include "vm/cellslice.h" @@ -973,7 +976,7 @@ td::Result> std_boc_deserialize(td::Slice data, bool can_be_empty, boo return std::move(root); } -td::Result>> std_boc_deserialize_multi(td::Slice data, int max_roots) { +td::Result>> std_boc_deserialize_multi(td::Slice data, int max_roots, bool allow_nonzero_level) { if (data.empty()) { return std::vector>{}; } @@ -989,7 +992,7 @@ td::Result>> std_boc_deserialize_multi(td::Slice data, int if (root.is_null()) { return td::Status::Error("bag of cells has a null root cell (?)"); } - if (root->get_level() != 0) { + if (!allow_nonzero_level && root->get_level() != 0) { return td::Status::Error("bag of cells has a root with non-zero level"); } roots.emplace_back(std::move(root)); @@ -1265,46 +1268,95 @@ bool VmStorageStat::add_storage(const CellSlice& cs) { } void ProofStorageStat::add_loaded_cell(const Ref& cell, td::uint8 max_level) { - max_level = std::min(max_level, Cell::max_level); - auto& [status, size] = cells_[cell->get_hash(max_level)]; - if (status == c_loaded) { + max_level = std::min(max_level, Cell::max_level); + auto& info = cells_[cell->get_hash(max_level)]; + if (info.status == c_loaded) { return; } - proof_size_ -= size; - status = c_loaded; - proof_size_ += size = estimate_serialized_size(cell); + proof_size_ -= info.serialized_size; + info.status = c_loaded; + info.cell = cell; + info.cell_max_level = max_level; + proof_size_ += info.serialized_size = estimate_serialized_size(cell); max_level += (cell->special_type() == CellTraits::SpecialType::MerkleProof || cell->special_type() == CellTraits::SpecialType::MerkleUpdate); for (unsigned i = 0; i < cell->size_refs(); ++i) { - auto& [child_status, child_size] = cells_[cell->get_ref(i)->get_hash(max_level)]; - if (child_status == c_none) { - child_status = c_prunned; - proof_size_ += child_size = estimate_prunned_size(); + auto& child = cells_[cell->get_ref(i)->get_hash(max_level)]; + if (child.status == c_none) { + child.status = c_prunned; + proof_size_ += child.serialized_size = estimate_prunned_size(); } } } void ProofStorageStat::add_loaded_cells(const ProofStorageStat& other) { - for (const auto& [hash, x] : other.cells_) { - const auto& [new_status, new_size] = x; - auto& [old_status, old_size] = cells_[hash]; - if (old_status >= new_status) { + for (const auto& [hash, new_info] : other.cells_) { + auto& old_info = cells_[hash]; + if (old_info.status >= new_info.status) { continue; } - proof_size_ -= old_size; - old_status = new_status; - proof_size_ += old_size = new_size; + proof_size_ -= old_info.serialized_size; + old_info = new_info; + proof_size_ += old_info.serialized_size; } } - td::uint64 ProofStorageStat::estimate_proof_size() const { return proof_size_; } ProofStorageStat::CellStatus ProofStorageStat::get_cell_status(const Cell::Hash& hash) const { auto it = cells_.find(hash); - return it == cells_.end() ? c_none : it->second.first; + return it == cells_.end() ? c_none : it->second.status; +} + +std::vector> ProofStorageStat::build_collated_data() const { + struct Cache { + Ref result; + bool is_root = true; + }; + std::map cache; + std::function dfs = [&](const CellInfo& info) -> Cache& { + Cell::Hash hash = info.cell->get_hash(info.cell_max_level); + Cache& entry = cache[hash]; + if (entry.result.not_null()) { + return entry; + } + CellBuilder cb; + cb.store_bits(info.cell->get_data(), info.cell->size()); + td::uint8 child_max_level = info.cell_max_level; + if (info.cell->special_type() == CellTraits::SpecialType::MerkleProof || + info.cell->special_type() == CellTraits::SpecialType::MerkleUpdate) { + ++child_max_level; + } + for (unsigned i = 0; i < info.cell->size_refs(); ++i) { + Ref child = info.cell->get_ref(i); + Cell::Hash child_hash = child->get_hash(child_max_level); + auto it = cells_.find(child_hash); + if (it == cells_.end() || it->second.status != c_loaded) { + cb.store_ref(CellBuilder::create_pruned_branch(child, Cell::max_level, child_max_level)); + } else { + Cache& child_result = dfs(it->second); + child_result.is_root = false; + cb.store_ref(child_result.result); + } + } + Cache& entry2 = cache[hash]; + entry2.result = cb.finalize(info.cell->is_special()); + return entry2; + }; + for (auto& [_, info] : cells_) { + if (info.status == c_loaded) { + dfs(info); + } + } + std::vector> result; + for (auto& [_, entry] : cache) { + if (entry.is_root) { + result.push_back(std::move(entry.result)); + } + } + return result; } td::uint64 ProofStorageStat::estimate_prunned_size() { diff --git a/crypto/vm/boc.h b/crypto/vm/boc.h index 5bf45c922..e13875241 100644 --- a/crypto/vm/boc.h +++ b/crypto/vm/boc.h @@ -177,11 +177,19 @@ class ProofStorageStat { return get_cell_status(hash) == c_loaded; } + std::vector> build_collated_data() const; + static td::uint64 estimate_prunned_size(); static td::uint64 estimate_serialized_size(const Ref& cell); private: - td::HashMap> cells_; + struct CellInfo { + Ref cell; // only for c_loaded + td::uint8 cell_max_level = Cell::max_level; // only for c_loaded + CellStatus status = c_none; + td::uint64 serialized_size = 0; + }; + td::HashMap cells_; td::uint64 proof_size_ = 0; }; @@ -400,7 +408,8 @@ td::Result> std_boc_deserialize(td::Slice data, bool can_be_empty = fa td::Result std_boc_serialize(Ref root, int mode = 0); td::Result>> std_boc_deserialize_multi(td::Slice data, - int max_roots = BagOfCells::default_max_roots); + int max_roots = BagOfCells::default_max_roots, + bool allow_nonzero_level = false); td::Result std_boc_serialize_multi(std::vector> root, int mode = 0); td::Status std_boc_serialize_to_file(Ref root, td::FileFd& fd, int mode = 0, diff --git a/crypto/vm/cells/ExtCell.h b/crypto/vm/cells/ExtCell.h index dbbd8575b..846ceac8f 100644 --- a/crypto/vm/cells/ExtCell.h +++ b/crypto/vm/cells/ExtCell.h @@ -68,6 +68,18 @@ class ExtCell : public Cell { Ref> get_prunned_cell() const { return prunned_cell_.load(); } + td::Status set_inner_cell(Ref new_cell) const { + auto prunned_cell = prunned_cell_.load(); + if (prunned_cell.is_null()) { + return td::Status::OK(); + } + TRY_STATUS(prunned_cell->check_equals_unloaded(new_cell)); + if (data_cell_.store_if_empty(new_cell)) { + prunned_cell_.store({}); + get_thread_safe_counter_unloaded().add(-1); + } + return td::Status::OK(); + } private: mutable td::AtomicRef data_cell_; diff --git a/overlay/overlay-peers.cpp b/overlay/overlay-peers.cpp index 52cc40cf6..4964a6cd9 100644 --- a/overlay/overlay-peers.cpp +++ b/overlay/overlay-peers.cpp @@ -508,12 +508,16 @@ void OverlayImpl::get_overlay_random_peers(td::uint32 max_peers, td::Promise> promise) { std::vector v; auto t = td::Clocks::system(); - while (v.size() < max_peers && v.size() < peer_list_.peers_.size() - peer_list_.bad_peers_.size()) { + td::uint32 iters = 0; + while (v.size() < max_peers && v.size() < peer_list_.peers_.size() - peer_list_.bad_peers_.size() && + iters <= peer_list_.peers_.size() * 2) { + ++iters; auto P = peer_list_.peers_.get_random(); if (!P->is_permanent_member() && (P->get_version() + 3600 < t || P->certificate()->is_expired(t))) { VLOG(OVERLAY_INFO) << this << ": deleting outdated peer " << P->get_id(); del_peer(P->get_id()); - } else if (P->is_alive()) { + } else if (P->is_alive() && P->get_id() != local_id_ && + !(P->get_node()->flags() & OverlayMemberFlags::DoNotReceiveBroadcasts)) { bool dup = false; for (auto &n : v) { if (n == P->get_id()) { diff --git a/tdutils/td/utils/LRUCache.h b/tdutils/td/utils/LRUCache.h index d8f51525b..eeeb2a372 100644 --- a/tdutils/td/utils/LRUCache.h +++ b/tdutils/td/utils/LRUCache.h @@ -86,6 +86,17 @@ class LRUCache { return result; } + void remove(const K& key) { + auto it = cache_.find(key); + if (it == cache_.end()) { + return; + } + total_weight_ -= (*it)->weight; + (*it)->remove(); + cache_.erase(it); + } + + private: struct Entry : ListNode { Entry(K key, uint64 weight) : key(std::move(key)), weight(weight) { diff --git a/tdutils/td/utils/optional.h b/tdutils/td/utils/optional.h index 7723d2c31..62178cd1c 100644 --- a/tdutils/td/utils/optional.h +++ b/tdutils/td/utils/optional.h @@ -89,6 +89,13 @@ class optional { return {}; } + td::optional clone() const { + if (*this) { + return value().clone(); + } + return {}; + } + template void emplace(ArgsT &&... args) { impl_.emplace(std::forward(args)...); diff --git a/test/test-ton-collator.cpp b/test/test-ton-collator.cpp index 3ee723ec8..a7714200a 100644 --- a/test/test-ton-collator.cpp +++ b/test/test-ton-collator.cpp @@ -347,8 +347,9 @@ class TestNode : public td::actor::Actor { } } } - void send_block_candidate(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) override { + void send_block_candidate_broadcast(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data, int mode) override { } void send_broadcast(ton::BlockBroadcast broadcast, int mode) override { } @@ -378,6 +379,9 @@ class TestNode : public td::actor::Actor { ton::ShardIdFull dst_shard, std::vector blocks, block::ImportedMsgQueueLimits limits, td::Timestamp timeout, td::Promise>> promise) override { } + void download_block_candidate(ton::BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) override { + } void new_key_block(ton::validator::BlockHandle handle) override { } diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index d737dea5a..7df428389 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -437,13 +437,12 @@ tonNode.blockBroadcastCompressedV2 id:tonNode.blockIdExt catchain_seqno:int vali tonNode.ihrMessageBroadcast message:tonNode.ihrMessage = tonNode.Broadcast; tonNode.externalMessageBroadcast message:tonNode.externalMessage = tonNode.Broadcast; tonNode.newShardBlockBroadcast block:tonNode.newShardBlock = tonNode.Broadcast; -// signature may be empty, at least for now -tonNode.newBlockCandidateBroadcast id:tonNode.blockIdExt catchain_seqno:int validator_set_hash:int - collator_signature:tonNode.blockSignature data:bytes = tonNode.Broadcast; -tonNode.newBlockCandidateBroadcastCompressed id:tonNode.blockIdExt catchain_seqno:int validator_set_hash:int - collator_signature:tonNode.blockSignature flags:# compressed:bytes = tonNode.Broadcast; -tonNode.newBlockCandidateBroadcastCompressedV2 id:tonNode.blockIdExt catchain_seqno:int validator_set_hash:int - collator_signature:tonNode.blockSignature flags:# compressed:bytes = tonNode.Broadcast; + +// flags & 1 - with collated data +tonNode.blockCandidateBroadcastCompressed#0cd86894 id:tonNode.blockIdExt catchain_seqno:int validator_set_hash:int unused:tonNode.blockSignature + flags:# compressed:bytes = tonNode.Broadcast; +tonNode.blockCandidateBroadcastCompressedV2#9f0e7f35 id:tonNode.blockIdExt catchain_seqno:int validator_set_hash:int unused:tonNode.blockSignature + flags:# compressed:bytes = tonNode.Broadcast; // optimistic broadcast of response to tonNode.getOutMsgQueueProof with dst_shard, block and limits arguments tonNode.outMsgQueueProofBroadcast dst_shard:tonNode.shardId block:tonNode.blockIdExt @@ -465,6 +464,10 @@ tonNode.dataFullCompressed id:tonNode.blockIdExt flags:# compressed:bytes is_lin tonNode.dataFullCompressedV2 id:tonNode.blockIdExt flags:# compressed:bytes is_link:Bool = tonNode.DataFull; tonNode.dataFullEmpty = tonNode.DataFull; +tonNode.blockCandidateData block_id:tonNode.blockIdExt data:bytes collated_data:bytes = tonNode.BlockCandidateData; +tonNode.blockCandidateDataCompressedV2 block_id:tonNode.blockIdExt flags:# compressed:bytes = tonNode.BlockCandidateData; +tonNode.blockCandidateDataEmpty = tonNode.BlockCandidateData; + tonNode.capabilities#f5bf60c0 version_major:int version_minor:int flags:# = tonNode.Capabilities; tonNode.success = tonNode.Success; @@ -506,6 +509,7 @@ tonNode.getShardArchiveInfo masterchain_seqno:int shard_prefix:tonNode.shardId = tonNode.getArchiveSlice archive_id:long offset:long max_size:int = tonNode.Data; tonNode.getOutMsgQueueProof dst_shard:tonNode.shardId blocks:(vector tonNode.blockIdExt) limits:tonNode.importedMsgQueueLimits = tonNode.OutMsgQueueProof; +tonNode.downloadBlockCandidate block:tonNode.blockIdExt only_collated_data:Bool = tonNode.BlockCandidateData; tonNode.downloadPersistentState block:tonNode.blockIdExt masterchain_block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadPersistentStateSlice block:tonNode.blockIdExt masterchain_block:tonNode.blockIdExt offset:long max_size:long = tonNode.Data; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index bf531b7af9deb192d7628c25692399ab8c7691e8..a61f6a5466d27d5b8e2b6a8f821665fd3815db67 100644 GIT binary patch delta 699 zcmX@JkbTJ#cHT#`^{p77z+)rtZc{~;ADiDumE`C7<)@_TIpyRhXFDh6rDUcgmZZ8Q zmLxJTZ{BDs&M4XRDDQ|MTv<_mVoGviaR~zh=jMlIzquJ7Y`*LC{IRA<)%~k*!;&x! z16g+Sn2-JMv*g{4u@^qL6F$?9cqj@%q$?EPfyTc+&cZP z7NawStD?Ofc<_!woT zZ_s5FfpAagLKRQbV>EyWnZQ_+1Lnz0Z_#5EkpsB{Ii#j!+~CoKdu{qbeMTjSUZ9ON zJZz5a!3wI{!E z6PR|GQE>WFBWUn!e`CZL;la3gyK6P0y$j34N4$E|0}e2X@xfCTIO?V!ILoLbER>&@ gQyHI}pOXVjaVhb@beA~U{*WvSI0h>AGlmEP0GlWUHvj+t delta 478 zcmZ3og#FAycHT#`^{p77z>FtdP^PxsMd+&cZF4x=-KE1=700%0YrV3e4ipv$-c z!WGejN-PKJV1$ST=rig-SPlA&1`yU3eWR$xATUI58wClL2S%`Pl`v+U c0v6l8!I&|^gK^Pz&KgF0m+f~BFnS0A0B#zkkN^Mx diff --git a/ton/ton-types.h b/ton/ton-types.h index 87947d027..c05ac10d4 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -566,6 +566,7 @@ struct ValidatorSessionConfig { td::uint32 max_collated_data_size = (4 << 20); bool new_catchain_ids = false; + bool merge_collated_data = false; static const td::uint32 BLOCK_HASH_COVERS_DATA_FROM_VERSION = 2; }; diff --git a/validator-session/candidate-serializer.cpp b/validator-session/candidate-serializer.cpp index 9aa376e2a..f4b1a066b 100644 --- a/validator-session/candidate-serializer.cpp +++ b/validator-session/candidate-serializer.cpp @@ -102,7 +102,7 @@ td::Result> decompress_candidate_dat if (decompressed.size() != (size_t)decompressed_size) { return td::Status::Error("decompressed size mismatch"); } - TRY_RESULT_ASSIGN(roots, vm::std_boc_deserialize_multi(decompressed)); + TRY_RESULT_ASSIGN(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); } else { TRY_RESULT_ASSIGN(roots, vm::boc_decompress(compressed, max_decompressed_size)); } diff --git a/validator-session/validator-session-types.h b/validator-session/validator-session-types.h index 982d2642b..b04251206 100644 --- a/validator-session/validator-session-types.h +++ b/validator-session/validator-session-types.h @@ -59,6 +59,7 @@ struct ValidatorSessionOptions { td::uint32 max_collated_data_size = 4 << 20; bool new_catchain_ids = false; + bool merge_collated_data = false; td::uint32 proto_version = 0; diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index 27c105929..feac80c0d 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -19,6 +19,7 @@ #include "validator-session.hpp" #include "td/utils/Random.h" #include "candidate-serializer.h" +#include "delay.h" #include "td/utils/overloaded.h" #include "ton/ton-tl.hpp" @@ -948,6 +949,105 @@ void ValidatorSessionImpl::check_all() { alarm_timestamp().relax(description().attempt_start_at(att + 1)); } +void ValidatorSessionImpl::download_accepted_candidate(td::uint32 round, const SentBlock *candidate, bool try_local) { + CHECK(candidate); + ValidatorSessionCandidateId candidate_id = SentBlock::get_block_id(candidate); + if (blocks_.contains(candidate_id)) { + return; + } + if (try_local) { + PublicKey pubkey = description().get_source_public_key(candidate->get_src_idx()); + callback_->get_approved_candidate( + pubkey, candidate->get_root_hash(), candidate->get_file_hash(), candidate->get_collated_data_file_hash(), + [=, pubkey_hash = pubkey.compute_short_id(), SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidatorSessionImpl::download_accepted_candidate, round, candidate, + false); + } else { + BlockCandidate c = R.move_as_ok(); + td::actor::send_closure( + SelfId, &ValidatorSessionImpl::store_block_candidate, candidate_id, + create_tl_object( + pubkey_hash.bits256_value(), round, c.id.root_hash, std::move(c.data), std::move(c.collated_data))); + } + }); + return; + } + VLOG(VALIDATOR_SESSION_NOTICE) << this << ": downloading accepted block candidate " << candidate_id + << " round=" << round; + auto v = virtual_state_->get_block_approvers(description(), candidate_id); + std::erase_if(v, [&](td::uint32 x) { return x == local_idx(); }); + if (v.empty()) { + VLOG(VALIDATOR_SESSION_WARNING) << this << ": failed to download accepted candidate " << candidate_id + << " : no nodes"; + return; + } + auto node = description().get_source_id(v[td::Random::fast(0, static_cast(v.size() - 1))]); + get_broadcast_p2p( + node, candidate->get_file_hash(), candidate->get_collated_data_file_hash(), + description().get_source_id(candidate->get_src_idx()), round, candidate->get_root_hash(), + [=, print_id = print_id(), SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + VLOG(VALIDATOR_SESSION_WARNING) << print_id << ": failed to download accepted candidate " << candidate_id + << " from " << node << ": " << R.move_as_error(); + delay_action( + [=]() { + td::actor::send_closure(SelfId, &ValidatorSessionImpl::download_accepted_candidate, round, candidate, + false); + }, + td::Timestamp::in(0.1)); + } else { + td::actor::send_closure(SelfId, &ValidatorSessionImpl::downloaded_accepted_candidate, round, candidate, + R.move_as_ok()); + } + }, + td::Timestamp::in(15.0)); +} + +void ValidatorSessionImpl::downloaded_accepted_candidate(td::uint32 round, const SentBlock *candidate, + td::BufferSlice result) { + ValidatorSessionCandidateId candidate_id = SentBlock::get_block_id(candidate); + if (blocks_.contains(candidate_id)) { + return; + } + auto R = + deserialize_candidate(result, compress_block_candidates_, + description().opts().max_block_size + description().opts().max_collated_data_size + 1024, + description().opts().proto_version); + if (R.is_error()) { + VLOG(VALIDATOR_SESSION_WARNING) << this << ": failed to download accepted candidate " << candidate_id << ": " + << R.move_as_error(); + download_accepted_candidate(round, candidate, false); + return; + } + auto f = R.move_as_ok(); + + if (f->root_hash_ != candidate->get_root_hash() || + f->src_ != description().get_source_id(candidate->get_src_idx()).tl() || + td::sha256_bits256(f->data_) != candidate->get_file_hash() || + td::sha256_bits256(f->collated_data_) != candidate->get_collated_data_file_hash()) { + VLOG(VALIDATOR_SESSION_WARNING) << this << ": failed to download accepted candidate " << candidate_id + << ": wrong candidate data"; + download_accepted_candidate(round, candidate, false); + return; + } + VLOG(VALIDATOR_SESSION_NOTICE) << this << ": downloaded accepted block candidate " << candidate_id + << " round=" << round; + store_block_candidate(candidate_id, std::move(f)); +} + +void ValidatorSessionImpl::store_block_candidate(ValidatorSessionCandidateId candidate_id, + tl_object_ptr f) { + blocks_[candidate_id] = std::move(f); + if (auto it = block_waiters_.find(candidate_id); it != block_waiters_.end()) { + for (auto &promise : it->second) { + promise.set_result(td::Unit()); + } + block_waiters_.erase(it); + } +} + + void ValidatorSessionImpl::request_new_block(bool now) { if (requested_new_block_now_) { return; @@ -1032,11 +1132,13 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) { } } - auto it = blocks_.find(SentBlock::get_block_id(block)); + ValidatorSessionCandidateId candidate_id = SentBlock::get_block_id(block); + auto it = blocks_.find(candidate_id); bool have_block = (bool)block; if (!have_block) { callback_->on_block_skipped(cur_round_); } else { + accepted_block_candidates_.insert(candidate_id); cur_stats_.success = true; cur_stats_.timestamp = td::Clocks::system(); cur_stats_.signatures = (td::uint32)export_sigs.size(); @@ -1059,12 +1161,14 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) { description().get_node_priority(block->get_src_idx(), cur_round_)}}; if (it == blocks_.end()) { callback_->on_block_committed(std::move(source_info), block->get_root_hash(), block->get_file_hash(), - td::BufferSlice(), std::move(export_sigs), std::move(export_approve_sigs), - std::move(stats)); + td::BufferSlice(), td::BufferSlice(), block->get_collated_data_file_hash(), + std::move(export_sigs), std::move(export_approve_sigs), std::move(stats)); + download_accepted_candidate(cur_round_, block, true); } else { callback_->on_block_committed(std::move(source_info), block->get_root_hash(), block->get_file_hash(), - it->second->data_.clone(), std::move(export_sigs), std::move(export_approve_sigs), - std::move(stats)); + it->second->data_.clone(), it->second->collated_data_.clone(), + block->get_collated_data_file_hash(), std::move(export_sigs), + std::move(export_approve_sigs), std::move(stats)); } first_block_round_ = cur_round_ + 1; } @@ -1080,7 +1184,8 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) { } auto it2 = blocks_.begin(); while (it2 != blocks_.end()) { - if (it2->second->round_ < (td::int32)cur_round_ - MAX_PAST_ROUND_BLOCK) { + if (it2->second->round_ < (td::int32)cur_round_ - MAX_PAST_ROUND_BLOCK && + !accepted_block_candidates_.contains(it->first)) { it2 = blocks_.erase(it2); } else { ++it2; @@ -1253,6 +1358,31 @@ void ValidatorSessionImpl::get_validator_group_info_for_litequery( promise.set_result(std::move(result)); } +void ValidatorSessionImpl::get_accepted_candidate(PublicKey source, BlockIdExt block_id, + ValidatorSessionCollatedDataFileHash collated_data_file_hash, + td::Promise promise) { + ValidatorSessionCandidateId candidate_id = + description().candidate_id(description().get_source_idx(source.compute_short_id()), block_id.root_hash, + block_id.file_hash, collated_data_file_hash); + if (!accepted_block_candidates_.contains(candidate_id)) { + promise.set_error(td::Status::Error("not an accepted candidate")); + return; + } + td::Promise P = [=, this, promise = std::move(promise)](td::Result R) mutable { + TRY_STATUS_PROMISE(promise, R.move_as_status()); + auto it = blocks_.find(candidate_id); + CHECK(it != blocks_.end()); // accepted candidates are not deleted + const auto &c = it->second; + promise.set_value(BlockCandidate(Ed25519_PublicKey{source.ed25519_value().raw()}, block_id, collated_data_file_hash, + c->data_.clone(), c->collated_data_.clone())); + }; + if (blocks_.contains(candidate_id)) { + P.set_value(td::Unit()); + } else { + block_waiters_[candidate_id].push_back(std::move(P)); + } +} + void ValidatorSessionImpl::start_up() { CHECK(!rldp_.empty()); cur_round_ = 0; @@ -1518,6 +1648,7 @@ ValidatorSessionOptions::ValidatorSessionOptions(const ValidatorSessionConfig &c round_attempt_duration = conf.round_attempt_duration; round_candidates = conf.round_candidates; new_catchain_ids = conf.new_catchain_ids; + merge_collated_data = conf.merge_collated_data; } } // namespace validatorsession diff --git a/validator-session/validator-session.h b/validator-session/validator-session.h index f8e2d62e7..c81cbe2f5 100644 --- a/validator-session/validator-session.h +++ b/validator-session/validator-session.h @@ -85,6 +85,7 @@ class ValidatorSession : public td::actor::Actor { virtual void on_generate_slot(BlockSourceInfo source_info, td::Promise promise) = 0; virtual void on_block_committed(BlockSourceInfo source_info, ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash, td::BufferSlice data, + td::BufferSlice collated_data, FileHash collated_data_hash, std::vector> signatures, std::vector> approve_signatures, ValidatorSessionStats stats) = 0; @@ -113,6 +114,9 @@ class ValidatorSession : public td::actor::Actor { td::uint32 cur_round, td::Promise>> promise) = 0; virtual void set_catchain_max_block_delay(double delay, double delay_slow) = 0; + virtual void get_accepted_candidate(PublicKey source, BlockIdExt block_id, + ValidatorSessionCollatedDataFileHash collated_data_file_hash, + td::Promise promise) = 0; static td::actor::ActorOwn create( catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id, diff --git a/validator-session/validator-session.hpp b/validator-session/validator-session.hpp index 4861c52e1..5239a54fa 100644 --- a/validator-session/validator-session.hpp +++ b/validator-session/validator-session.hpp @@ -77,6 +77,7 @@ class ValidatorSessionImpl : public ValidatorSession { // src_round_candidate_[src_id][round] -> candidate id std::vector> src_round_candidate_; std::map>> block_waiters_; + std::set accepted_block_candidates_; catchain::CatChainSessionId unique_hash_; @@ -103,6 +104,11 @@ class ValidatorSessionImpl : public ValidatorSession { void check_action(td::uint32 att); void check_all(); + void download_accepted_candidate(td::uint32 round, const SentBlock *candidate, bool try_local); + void downloaded_accepted_candidate(td::uint32 round, const SentBlock *candidate, td::BufferSlice result); + void store_block_candidate(ValidatorSessionCandidateId candidate_id, + tl_object_ptr f); + std::unique_ptr make_catchain_callback() { class cb : public catchain::CatChain::Callback { public: @@ -228,6 +234,9 @@ class ValidatorSessionImpl : public ValidatorSession { catchain_max_block_delay_ = delay; catchain_max_block_delay_slow_ = delay_slow; } + void get_accepted_candidate(PublicKey source, BlockIdExt block_id, + ValidatorSessionCollatedDataFileHash collated_data_file_hash, + td::Promise promise) override; void process_blocks(std::vector blocks); void finished_processing(); diff --git a/validator/collation-manager.cpp b/validator/collation-manager.cpp index 7d694af98..026c26a56 100644 --- a/validator/collation-manager.cpp +++ b/validator/collation-manager.cpp @@ -55,83 +55,31 @@ void CollationManager::tear_down() { adnl::Adnl::int_to_bytestring(ton_api::collatorNode_requestBlockCallback::ID)); } -void CollationManager::collate_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, - std::vector prev, Ed25519_PublicKey creator, - BlockCandidatePriority priority, td::Ref validator_set, - td::uint64 max_answer_size, td::CancellationToken cancellation_token, - td::Promise promise, int proto_version) { - if (shard.is_masterchain()) { - run_collate_query( - CollateParams{.shard = shard, - .min_masterchain_block_id = min_masterchain_block_id, - .prev = std::move(prev), - .creator = creator, - .validator_set = std::move(validator_set), - .collator_opts = opts_->get_collator_options()}, - manager_, td::Timestamp::in(10.0), std::move(cancellation_token), promise.wrap([](BlockCandidate&& candidate) { - return GeneratedCandidate{.candidate = std::move(candidate), .is_cached = false, .self_collated = true}; - })); +void CollationManager::collate_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, + td::CancellationToken cancellation_token, td::Promise promise, + int proto_version) { + if (params.shard.is_masterchain()) { + run_collate_query(std::move(params), manager_, td::Timestamp::in(10.0), std::move(cancellation_token), + promise.wrap([](BlockCandidate&& candidate) { + return GeneratedCandidate{ + .candidate = std::move(candidate), .is_cached = false, .self_collated = true}; + })); return; } - collate_shard_block(shard, min_masterchain_block_id, std::move(prev), creator, priority, std::move(validator_set), - max_answer_size, std::move(cancellation_token), std::move(promise), td::Timestamp::in(10.0), - proto_version); + collate_shard_block(std::move(params), priority, max_answer_size, std::move(cancellation_token), std::move(promise), + td::Timestamp::in(10.0), proto_version); } -void CollationManager::collate_block_optimistic(ShardIdFull shard, BlockIdExt min_masterchain_block_id, - BlockIdExt prev_block_id, td::BufferSlice prev_block, - Ed25519_PublicKey creator, BlockCandidatePriority priority, - td::Ref validator_set, td::uint64 max_answer_size, - td::CancellationToken cancellation_token, - td::Promise promise, int proto_version) { - if (shard.is_masterchain()) { - TRY_RESULT_PROMISE(promise, prev_block_data, create_block(prev_block_id, std::move(prev_block))); - run_collate_query( - CollateParams{.shard = shard, - .min_masterchain_block_id = min_masterchain_block_id, - .prev = {prev_block_id}, - .creator = creator, - .validator_set = std::move(validator_set), - .collator_opts = opts_->get_collator_options(), - .optimistic_prev_block = std::move(prev_block_data)}, - manager_, td::Timestamp::in(10.0), std::move(cancellation_token), promise.wrap([](BlockCandidate&& candidate) { - return GeneratedCandidate{.candidate = std::move(candidate), .is_cached = false, .self_collated = true}; - })); - return; - } - - auto& entry = optimistic_prev_cache_[prev_block_id]; - entry.block_data = std::move(prev_block); - ++entry.refcnt; - promise = [this, SelfId = actor_id(this), prev_block_id, - promise = std::move(promise)](td::Result R) mutable { - promise.set_result(std::move(R)); - td::actor::send_lambda_later(SelfId, [=, this]() { - auto it = optimistic_prev_cache_.find(prev_block_id); - CHECK(it != optimistic_prev_cache_.end()); - CHECK(it->second.refcnt > 0); - if (--it->second.refcnt == 0) { - optimistic_prev_cache_.erase(it); - } - }); - }; - - collate_shard_block(shard, min_masterchain_block_id, {prev_block_id}, creator, priority, std::move(validator_set), - max_answer_size, std::move(cancellation_token), std::move(promise), td::Timestamp::in(10.0), - proto_version, true); -} - -void CollationManager::collate_shard_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, - std::vector prev, Ed25519_PublicKey creator, - BlockCandidatePriority priority, td::Ref validator_set, +void CollationManager::collate_shard_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, td::CancellationToken cancellation_token, td::Promise promise, td::Timestamp timeout, - int proto_version, bool is_optimistic) { + int proto_version) { + bool is_optimistic = params.optimistic_prev_block.not_null(); TRY_STATUS_PROMISE(promise, cancellation_token.check()); - ShardInfo* s = select_shard_info(shard); + ShardInfo* s = select_shard_info(params.shard); if (s == nullptr) { promise.set_error( - td::Status::Error(PSTRING() << "shard " << shard.to_str() << " is not configured in collators list")); + td::Status::Error(PSTRING() << "shard " << params.shard.to_str() << " is not configured in collators list")); return; } @@ -197,29 +145,17 @@ void CollationManager::collate_shard_block(ShardIdFull shard, BlockIdExt min_mas } if (selected_collator.is_zero() && s->self_collate) { - td::Ref optimistic_prev_block; - if (is_optimistic) { - CHECK(prev.size() == 1); - TRY_RESULT_PROMISE_ASSIGN(promise, optimistic_prev_block, - create_block(prev[0], optimistic_prev_cache_.at(prev[0]).block_data.clone())); - } - run_collate_query( - CollateParams{.shard = shard, - .min_masterchain_block_id = min_masterchain_block_id, - .prev = std::move(prev), - .creator = creator, - .validator_set = std::move(validator_set), - .collator_opts = opts_->get_collator_options(), - .optimistic_prev_block = std::move(optimistic_prev_block)}, - manager_, td::Timestamp::in(10.0), std::move(cancellation_token), promise.wrap([](BlockCandidate&& candidate) { - return GeneratedCandidate{.candidate = std::move(candidate), .is_cached = false, .self_collated = true}; - })); + run_collate_query(std::move(params), manager_, td::Timestamp::in(10.0), std::move(cancellation_token), + promise.wrap([](BlockCandidate&& candidate) { + return GeneratedCandidate{ + .candidate = std::move(candidate), .is_cached = false, .self_collated = true}; + })); return; } std::vector> prev_blocks; - BlockId next_block_id{shard, 0}; - for (const BlockIdExt& p : prev) { + BlockId next_block_id{params.shard, 0}; + for (const BlockIdExt& p : params.prev) { prev_blocks.push_back(create_tl_block_id(p)); next_block_id.seqno = std::max(next_block_id.seqno, p.seqno() + 1); } @@ -244,27 +180,26 @@ void CollationManager::collate_shard_block(ShardIdFull shard, BlockIdExt min_mas } delay_action( [=, promise = std::move(promise)]() mutable { - td::actor::send_closure(SelfId, &CollationManager::collate_shard_block, shard, min_masterchain_block_id, prev, - creator, priority, validator_set, max_answer_size, cancellation_token, - std::move(promise), timeout, proto_version, is_optimistic); + td::actor::send_closure(SelfId, &CollationManager::collate_shard_block, params, priority, max_answer_size, + cancellation_token, std::move(promise), timeout, proto_version); }, retry_at); }; if (selected_collator.is_zero()) { - P.set_error(td::Status::Error(PSTRING() << "shard " << shard.to_str() << " has no suitable collator node")); + P.set_error(td::Status::Error(PSTRING() << "shard " << params.shard.to_str() << " has no suitable collator node")); return; } td::BufferSlice query; if (is_optimistic) { query = create_serialize_tl_object( - create_tl_shard_id(shard), validator_set->get_catchain_seqno(), std::move(prev_blocks), creator.as_bits256(), - priority.round, priority.first_block_round, priority.priority); + create_tl_shard_id(params.shard), params.validator_set->get_catchain_seqno(), std::move(prev_blocks), + params.creator.as_bits256(), priority.round, priority.first_block_round, priority.priority); } else { query = create_serialize_tl_object( - create_tl_shard_id(shard), validator_set->get_catchain_seqno(), std::move(prev_blocks), creator.as_bits256(), - priority.round, priority.first_block_round, priority.priority); + create_tl_shard_id(params.shard), params.validator_set->get_catchain_seqno(), std::move(prev_blocks), + params.creator.as_bits256(), priority.round, priority.first_block_round, priority.priority); } LOG(INFO) << "sending collate query for " << next_block_id.to_str() << ": send to #" << selected_idx << "(" << selected_collator << ")"; @@ -281,7 +216,7 @@ void CollationManager::collate_shard_block(ShardIdFull shard, BlockIdExt min_mas TRY_RESULT_PROMISE(P, f, fetch_tl_object(data, true)); TRY_RESULT_PROMISE(P, candidate, deserialize_candidate(std::move(f), td::narrow_cast(max_answer_size), proto_version)); - if (candidate.pubkey.as_bits256() != creator.as_bits256()) { + if (candidate.pubkey.as_bits256() != params.creator.as_bits256()) { P.set_error(td::Status::Error("collate query: block candidate source mismatch")); return; } @@ -293,6 +228,25 @@ void CollationManager::collate_shard_block(ShardIdFull shard, BlockIdExt min_mas << selected_collator << ") in " << timer.elapsed() << "s"; P.set_result(std::move(candidate)); }; + + if (is_optimistic) { + BlockIdExt prev_block_id = params.optimistic_prev_block->block_id(); + auto& entry = optimistic_prev_cache_[prev_block_id]; + entry.block_data = params.optimistic_prev_block->data().clone(); + ++entry.refcnt; + P2 = [this, SelfId = actor_id(this), prev_block_id, P2 = std::move(P2)](td::Result R) mutable { + P2.set_result(std::move(R)); + td::actor::send_lambda_later(SelfId, [=, this]() { + auto it = optimistic_prev_cache_.find(prev_block_id); + CHECK(it != optimistic_prev_cache_.end()); + CHECK(it->second.refcnt > 0); + if (--it->second.refcnt == 0) { + optimistic_prev_cache_.erase(it); + } + }); + }; + } + td::actor::send_closure(rldp_, &rldp2::Rldp::send_query_ex, local_id_, selected_collator, "collatequery", std::move(P2), timeout, std::move(query), max_answer_size); } diff --git a/validator/collation-manager.hpp b/validator/collation-manager.hpp index 6691a387e..7f001124d 100644 --- a/validator/collation-manager.hpp +++ b/validator/collation-manager.hpp @@ -16,6 +16,7 @@ */ #pragma once +#include "fabric.h" #include "interfaces/validator-manager.h" #include "rldp2/rldp.h" #include @@ -36,16 +37,9 @@ class CollationManager : public td::actor::Actor { void tear_down() override; void alarm() override; - void collate_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, - Ed25519_PublicKey creator, BlockCandidatePriority priority, td::Ref validator_set, - td::uint64 max_answer_size, td::CancellationToken cancellation_token, - td::Promise promise, int proto_version); - - void collate_block_optimistic(ShardIdFull shard, BlockIdExt min_masterchain_block_id, BlockIdExt prev_block_id, - td::BufferSlice prev_block, Ed25519_PublicKey creator, BlockCandidatePriority priority, - td::Ref validator_set, td::uint64 max_answer_size, - td::CancellationToken cancellation_token, td::Promise promise, - int proto_version); + void collate_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, + td::CancellationToken cancellation_token, td::Promise promise, + int proto_version); void update_options(td::Ref opts); @@ -63,11 +57,9 @@ class CollationManager : public td::actor::Actor { td::actor::ActorId adnl_; td::actor::ActorId rldp_; - void collate_shard_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, - Ed25519_PublicKey creator, BlockCandidatePriority priority, - td::Ref validator_set, td::uint64 max_answer_size, + void collate_shard_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, td::CancellationToken cancellation_token, td::Promise promise, - td::Timestamp timeout, int proto_version, bool is_optimistic = false); + td::Timestamp timeout, int proto_version); void update_collators_list(const CollatorsList& collators_list); diff --git a/validator/collator-node/collator-node-session.cpp b/validator/collator-node/collator-node-session.cpp index 8e131f3c0..92bbad303 100644 --- a/validator/collator-node/collator-node-session.cpp +++ b/validator/collator-node/collator-node-session.cpp @@ -17,6 +17,8 @@ #include "collator-node-session.hpp" +#include "block-auto.h" +#include "checksum.h" #include "collator-node.hpp" #include "fabric.h" #include "utils.hpp" @@ -48,13 +50,16 @@ CollatorNodeSession::CollatorNodeSession(ShardIdFull shard, std::vectorget_catchain_seqno() << ", next block seqno " << next_block_seqno_; - + if (merge_collated_data_enabled_) { + collated_data_deduplicator_ = std::make_shared(); + } if (can_generate_) { generate_block(prev_, {}, {}, td::Timestamp::in(10.0), [](td::Result) {}); } @@ -74,9 +79,11 @@ void CollatorNodeSession::new_shard_block_accepted(BlockIdExt block_id, bool can if (next_block_seqno_ > block_id.seqno()) { return; } + LOG(INFO) << "New shard block #" << block_id.seqno(); LOG(DEBUG) << "New shard block " << block_id.to_str(); next_block_seqno_ = block_id.seqno() + 1; prev_ = {block_id}; + accepted_blocks_[block_id.seqno()] = block_id; while (!cache_.empty()) { auto& [cache_prev, entry] = *cache_.begin(); @@ -103,15 +110,33 @@ void CollatorNodeSession::new_shard_block_accepted(BlockIdExt block_id, bool can cache_.erase(cache_.begin()); } + try_merge_collated_data(block_id); + if (can_generate_) { generate_block(prev_, {}, {}, td::Timestamp::in(10.0), [](td::Result) {}); } } +void CollatorNodeSession::on_block_candidate_broadcast(BlockCandidate candidate) { + BlockIdExt id = candidate.id; + if (id.shard_full() != shard_) { + LOG(DEBUG) << "Dropping block candidate broadcast " << id.to_str() << " - wrong shard"; + return; + } + auto it = accepted_blocks_.find(id.seqno()); + if (it != accepted_blocks_.end() && it->second == id) { + if (merge_collated_data_enabled_ && !collated_data_merged_.contains(id.seqno())) { + LOG(INFO) << "Merge collated data #" << id.seqno() << ": using candidate broadcast"; + try_merge_collated_data_finish(std::move(candidate), false); + } + } +} + void CollatorNodeSession::update_masterchain_config(td::Ref state) { ValidatorSessionConfig config = state->get_consensus_config(); proto_version_ = config.proto_version; max_candidate_size_ = config.max_block_size + config.max_collated_data_size + 1024; + merge_collated_data_enabled_ = config.merge_collated_data; } void CollatorNodeSession::generate_block(std::vector prev_blocks, @@ -207,23 +232,32 @@ void CollatorNodeSession::generate_block(std::vector prev_blocks, }; cache_entry->started = true; cache_entry->block_seqno = block_seqno; - run_collate_query(CollateParams{.shard = shard_, - .min_masterchain_block_id = min_masterchain_block_id_, - .prev = std::move(prev_blocks), - .validator_set = validator_set_, - .collator_opts = opts_->get_collator_options(), - .collator_node_id = local_id_, - .skip_store_candidate = true, - .optimistic_prev_block = o_optimistic_prev_block}, - manager_, timeout, cache_entry->cancellation_token_source.get_cancellation_token(), - [=, shard = shard_, cc_seqno = validator_set_->get_catchain_seqno(), SelfId = actor_id(this), - timer = td::Timer{}](td::Result R) mutable { - FLOG(INFO) { - prefix_inner(sb, shard, cc_seqno, block_seqno, o_priority, is_optimistic); - sb << ": " << (R.is_ok() ? "OK" : R.error().to_string()) << " time=" << timer.elapsed(); - }; - td::actor::send_closure(SelfId, &CollatorNodeSession::process_result, cache_entry, std::move(R)); - }); + CollateParams params{.shard = shard_, + .min_masterchain_block_id = min_masterchain_block_id_, + .prev = std::move(prev_blocks), + .validator_set = validator_set_, + .collator_opts = opts_->get_collator_options(), + .collator_node_id = local_id_, + .skip_store_candidate = true, + .optimistic_prev_block = o_optimistic_prev_block, + .collated_data_deduplicator = collated_data_deduplicator_}; + auto token = cache_entry->cancellation_token_source.get_cancellation_token(); + wait_collated_data_merged( + block_seqno - (is_optimistic ? 1 : 0), [=, this, params = std::move(params)](td::Result R) mutable { + if (R.is_error()) { + return; + } + run_collate_query(std::move(params), manager_, timeout, token, + [=, shard = shard_, cc_seqno = validator_set_->get_catchain_seqno(), SelfId = actor_id(this), + timer = td::Timer{}](td::Result R) mutable { + FLOG(INFO) { + prefix_inner(sb, shard, cc_seqno, block_seqno, o_priority, is_optimistic); + sb << ": " << (R.is_ok() ? "OK" : R.error().to_string()) << " time=" << timer.elapsed(); + }; + td::actor::send_closure(SelfId, &CollatorNodeSession::process_result, cache_entry, + std::move(R)); + }); + }); } void CollatorNodeSession::process_result(std::shared_ptr cache_entry, td::Result R) { @@ -311,4 +345,129 @@ void CollatorNodeSession::CacheEntry::cancel(td::Status reason) { cancellation_token_source.cancel(); } +void CollatorNodeSession::wait_collated_data_merged(BlockSeqno seqno, td::Promise promise) { + if (!merge_collated_data_enabled_ || collated_data_merged_upto_ >= seqno) { + promise.set_value(td::Unit{}); + } else { + collated_data_merged_waiters_[seqno].push_back(std::move(promise)); + } +} + +void CollatorNodeSession::try_merge_collated_data(BlockIdExt block_id) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(block_id.seqno())) { + return; + } + td::actor::send_closure( + manager_, &ValidatorManager::get_block_candidate_by_block_id_from_db, block_id, + [SelfId = actor_id(this), block_id](td::Result R) mutable { + if (R.is_error()) { + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": no candidate in DB, downloading"; + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net, block_id); + } else { + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": got candidate from disk"; + BlockCandidate c = R.move_as_ok(); + CHECK(c.id == block_id); + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_finish, std::move(c), true); + } + }); +} + +void CollatorNodeSession::try_merge_collated_data_from_net(BlockIdExt block_id) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(block_id.seqno())) { + return; + } + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": wait block data"; + td::actor::send_closure( + manager_, &ValidatorManager::wait_block_data_short, block_id, 0, td::Timestamp::in(30.0), + [SelfId = actor_id(this), block_id](td::Result> R) mutable { + if (R.is_error()) { + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": wait block data failed - " << R.error(); + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net, block_id); + } else { + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": got block data, downloading collated data"; + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net_cont, block_id, + R.move_as_ok()); + } + }); +} + +void CollatorNodeSession::try_merge_collated_data_from_net_cont(BlockIdExt block_id, Ref block_data) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(block_id.seqno())) { + return; + } + LOG(DEBUG) << "Merge collated data #" << block_id.seqno() << ": download collated data"; + td::actor::send_closure( + manager_, &ValidatorManager::send_get_block_candidate_request, block_id, /* only_collated_data = */ true, + td::Timestamp::in(10.0), + [SelfId = actor_id(this), block_id, block_data = std::move(block_data), + retry_at = td::Timestamp::in(5.0)](td::Result> R) mutable { + if (R.is_error()) { + LOG(DEBUG) << "Merge collated data #" << block_id.seqno() << ": request failed - " << R.error(); + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net_cont, block_id, + std::move(block_data)); + } else { + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net_cont2, block_id, + std::move(block_data), std::move(R.ok_ref().second)); + } + }); +} + +void CollatorNodeSession::try_merge_collated_data_from_net_cont2(BlockIdExt block_id, Ref block_data, + td::BufferSlice collated_data) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(block_id.seqno())) { + return; + } + block::gen::Block::Record rec; + block::gen::BlockInfo::Record info; + block::gen::BlockExtra::Record extra; + if (!block::gen::unpack_cell(block_data->root_cell(), rec) || !block::gen::unpack_cell(rec.info, info) || + !block::gen::unpack_cell(rec.extra, extra)) { + LOG(ERROR) << "Merge collated data #" << block_id.seqno() << ": failed to unpack block"; + return; + } + FileHash collated_data_hash = td::sha256_bits256(collated_data); + if (info.collated_data_hash->size() == 256) { + FileHash expected_collated_data_hash; + info.collated_data_hash->prefetch_bits_to(expected_collated_data_hash); + if (expected_collated_data_hash != collated_data_hash) { + LOG(DEBUG) << "Merge collated data #" << block_id.seqno() << ": request failed - collated data hash mismatch"; + try_merge_collated_data_from_net_cont(block_id, std::move(block_data)); + return; + } + } + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": got collated data from net"; + try_merge_collated_data_finish(BlockCandidate(Ed25519_PublicKey{extra.created_by}, block_id, collated_data_hash, + block_data->data(), std::move(collated_data)), + false); +} + +void CollatorNodeSession::try_merge_collated_data_finish(BlockCandidate candidate, bool from_disk) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(candidate.id.seqno())) { + return; + } + td::Status S = + collated_data_deduplicator_->add_block_candidate(candidate.id.seqno(), candidate.data, candidate.collated_data); + if (S.is_error()) { + LOG(ERROR) << "Merge collated data #" << candidate.id.seqno() << ": " << S; + } + collated_data_merged_.insert(candidate.id.seqno()); + while (collated_data_merged_.contains(collated_data_merged_upto_)) { + ++collated_data_merged_upto_; + } + for (auto it = collated_data_merged_waiters_.begin(); it != collated_data_merged_waiters_.end();) { + if (it->first > collated_data_merged_upto_) { + break; + } + for (auto& promise : it->second) { + promise.set_value(td::Unit{}); + } + it = collated_data_merged_waiters_.erase(it); + } + LOG(INFO) << "Merge collated data #" << candidate.id.seqno() << ": done, merged_upto=" << collated_data_merged_upto_; + if (!from_disk) { + td::actor::send_closure(manager_, &ValidatorManager::set_block_candidate, std::move(candidate), + [](td::Result) {}); + } +} + } // namespace ton::validator diff --git a/validator/collator-node/collator-node-session.hpp b/validator/collator-node/collator-node-session.hpp index 0f9f03d30..5f3a4c4f6 100644 --- a/validator/collator-node/collator-node-session.hpp +++ b/validator/collator-node/collator-node-session.hpp @@ -16,6 +16,7 @@ */ #pragma once +#include "impl/collated-data-merger.h" #include "interfaces/validator-manager.h" #include "rldp/rldp.h" #include "rldp2/rldp.h" @@ -42,6 +43,7 @@ class CollatorNodeSession : public td::actor::Actor { } void new_shard_block_accepted(BlockIdExt block_id, bool can_generate); + void on_block_candidate_broadcast(BlockCandidate candidate); void process_request(adnl::AdnlNodeIdShort src, std::vector prev_blocks, BlockCandidatePriority priority, bool is_optimistic, td::Timestamp timeout, td::Promise promise); @@ -90,6 +92,21 @@ class CollatorNodeSession : public td::actor::Actor { void process_request_optimistic_cont2(BlockIdExt prev_block_id, BlockCandidatePriority priority, td::Timestamp timeout, td::Promise promise, td::Result R); + + std::map accepted_blocks_; + bool merge_collated_data_enabled_ = false; + std::shared_ptr collated_data_deduplicator_; + std::set collated_data_merged_; + BlockSeqno collated_data_merged_upto_ = 0; + std::map>> collated_data_merged_waiters_; + + void wait_collated_data_merged(BlockSeqno seqno, td::Promise promise); + void try_merge_collated_data(BlockIdExt block_id); + void try_merge_collated_data_from_net(BlockIdExt block_id); + void try_merge_collated_data_from_net_cont(BlockIdExt block_id, Ref block_data); + void try_merge_collated_data_from_net_cont2(BlockIdExt block_id, Ref block_data, + td::BufferSlice collated_data); + void try_merge_collated_data_finish(BlockCandidate candidate, bool from_disk); }; } // namespace ton::validator diff --git a/validator/collator-node/collator-node.cpp b/validator/collator-node/collator-node.cpp index 9b03d9dc0..8ba8d60e1 100644 --- a/validator/collator-node/collator-node.cpp +++ b/validator/collator-node/collator-node.cpp @@ -138,11 +138,6 @@ void CollatorNode::new_masterchain_block_notification(td::Ref } } } - for (auto& [_, group] : validator_groups_) { - if (!group.actor.empty()) { - td::actor::send_closure(group.actor, &CollatorNodeSession::update_masterchain_config, state); - } - } } std::map> new_shards; @@ -197,6 +192,9 @@ void CollatorNode::new_masterchain_block_notification(td::Ref can_generate()); } } + for (BlockCandidate& candidate : future_group.pending_candidate_broadcasts) { + td::actor::send_closure(it->second.actor, &CollatorNodeSession::on_block_candidate_broadcast, std::move(candidate)); + } for (auto& promise : future_group.promises) { promise.set_value(td::Unit()); } @@ -242,6 +240,29 @@ void CollatorNode::new_shard_block_accepted(BlockIdExt block_id, CatchainSeqno c } } +void CollatorNode::on_block_candidate_broadcast(BlockCandidate candidate, CatchainSeqno cc_seqno) { + ShardIdFull shard = candidate.id.shard_full(); + if (!can_collate_shard(shard)) { + return; + } + auto it = validator_groups_.find(shard); + if (it == validator_groups_.end() || it->second.cc_seqno != cc_seqno) { + auto future_group = get_future_validator_group(shard, cc_seqno); + if (future_group.is_error()) { + LOG(DEBUG) << "Dropping block candidate broadcast " << candidate.id.to_str() << " cc_seqno=" << cc_seqno << " : " + << future_group.error(); + } else { + LOG(DEBUG) << "New block candidate broadcast in future validator group " << candidate.id.to_str() + << " cc_seqno=" << cc_seqno; + future_group.ok()->pending_candidate_broadcasts.push_back(std::move(candidate)); + } + return; + } + if (!it->second.actor.empty()) { + td::actor::send_closure(it->second.actor, &CollatorNodeSession::on_block_candidate_broadcast, std::move(candidate)); + } +} + td::Result CollatorNode::get_future_validator_group(ShardIdFull shard, CatchainSeqno cc_seqno) { auto it = validator_groups_.find(shard); @@ -398,8 +419,7 @@ void CollatorNode::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data td::Promise P = new_promise.wrap([block = block.clone()](td::Unit&&) mutable -> BlockCandidate { return std::move(block); }); - td::actor::send_closure(manager, &ValidatorManager::set_block_candidate, block.id, std::move(block), cc_seqno, - val_set_hash, std::move(P)); + td::actor::send_closure(manager, &ValidatorManager::set_block_candidate, std::move(block), std::move(P)); }; if (!shard.is_valid_ext()) { new_promise.set_error(td::Status::Error(PSTRING() << "invalid shard " << shard.to_str())); diff --git a/validator/collator-node/collator-node.hpp b/validator/collator-node/collator-node.hpp index cb9cec47c..41e02c1b4 100644 --- a/validator/collator-node/collator-node.hpp +++ b/validator/collator-node/collator-node.hpp @@ -42,6 +42,7 @@ class CollatorNode : public td::actor::Actor { void new_masterchain_block_notification(td::Ref state); void update_shard_client_handle(BlockHandle shard_client_handle); void new_shard_block_accepted(BlockIdExt block_id, CatchainSeqno cc_seqno); + void on_block_candidate_broadcast(BlockCandidate candidate, CatchainSeqno cc_seqno); private: void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise); @@ -67,6 +68,7 @@ class CollatorNode : public td::actor::Actor { }; struct FutureValidatorGroup { std::vector pending_blocks; + std::vector pending_candidate_broadcasts; std::vector> promises; }; std::map validator_groups_; diff --git a/validator/fabric.h b/validator/fabric.h index 8a3f6193f..dc1abfced 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -18,6 +18,7 @@ */ #pragma once +#include "impl/collated-data-merger.h" #include "interfaces/validator-manager.h" #include "interfaces/db.h" #include "validator.h" @@ -40,6 +41,8 @@ struct CollateParams { // Optional - used for optimistic collation Ref optimistic_prev_block = {}; + + std::shared_ptr collated_data_deduplicator = {}; }; struct ValidateParams { @@ -47,11 +50,13 @@ struct ValidateParams { BlockIdExt min_masterchain_block_id; std::vector prev; td::Ref validator_set = {}; - PublicKeyHash local_validator_id = PublicKeyHash::zero();; + PublicKeyHash local_validator_id = PublicKeyHash::zero(); bool is_fake = false; // Optional - used for validation of optimistic candidates Ref optimistic_prev_block = {}; + + td::actor::ActorId collated_data_merger = {}; }; td::actor::ActorOwn create_db_actor(td::actor::ActorId manager, std::string db_root_, diff --git a/validator/full-node-fast-sync-overlays.cpp b/validator/full-node-fast-sync-overlays.cpp index e9927518a..406fe1305 100644 --- a/validator/full-node-fast-sync-overlays.cpp +++ b/validator/full-node-fast-sync-overlays.cpp @@ -89,17 +89,13 @@ void FullNodeFastSyncOverlay::process_broadcast(PublicKeyHash src, ton_api::tonN block_id, query.block_->cc_seqno_, std::move(query.block_->data_)); } -void FullNodeFastSyncOverlay::process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast &query) { - process_block_candidate_broadcast(src, query); -} - void FullNodeFastSyncOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressed &query) { + ton_api::tonNode_blockCandidateBroadcastCompressed &query) { process_block_candidate_broadcast(src, query); } void FullNodeFastSyncOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query) { + ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query) { process_block_candidate_broadcast(src, query); } @@ -108,7 +104,8 @@ void FullNodeFastSyncOverlay::process_block_candidate_broadcast(PublicKeyHash sr CatchainSeqno cc_seqno; td::uint32 validator_set_hash; td::BufferSlice data; - auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, + td::optional collated_data; + auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, collated_data, overlay::Overlays::max_fec_broadcast_size()); if (S.is_error()) { LOG(DEBUG) << "dropped broadcast: " << S; @@ -122,9 +119,10 @@ void FullNodeFastSyncOverlay::process_block_candidate_broadcast(PublicKeyHash sr VLOG(FULL_NODE_WARNING) << "received block candidate with incorrect file hash from " << src; return; } - VLOG(FULL_NODE_DEBUG) << "Received newBlockCandidate in fast sync overlay from " << src << ": " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Received block candidate broadcast (" << (collated_data ? "with" : "no") + << " cdata) in fast sync overlay from " << src << ": " << block_id.to_str(); td::actor::send_closure(full_node_, &FullNode::process_block_candidate_broadcast, block_id, cc_seqno, - validator_set_hash, std::move(data)); + validator_set_hash, std::move(data), std::move(collated_data)); } void FullNodeFastSyncOverlay::process_telemetry_broadcast( @@ -169,6 +167,34 @@ void FullNodeFastSyncOverlay::receive_broadcast(PublicKeyHash src, td::BufferSli ton_api::downcast_call(*B.move_as_ok(), [src, Self = this](auto &obj) { Self->process_broadcast(src, obj); }); } +void FullNodeFastSyncOverlay::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_downloadBlockCandidate &query, + td::Promise promise) { + auto P = td::PromiseCreator::lambda([only_collated_data = query.only_collated_data_, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_serialize_tl_object()); + return; + } + auto candidate = R.move_as_ok(); + promise.set_result(serialize_block_candidate_data(std::move(candidate), only_collated_data, true)); + }); + BlockIdExt block_id = create_block_id(query.block_); + VLOG(FULL_NODE_DEBUG) << "Got query downloadBlockCandidate " << block_id.to_str() << " " + << (query.only_collated_data_ ? "(only cdata)" : "(full)") << " from " << src; + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_block_candidate_by_block_id_from_db, + block_id, std::move(P)); +} + +void FullNodeFastSyncOverlay::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, + td::Promise promise) { + auto B = fetch_tl_object(std::move(query), true); + if (B.is_error()) { + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "cannot parse tonnode query")); + return; + } + ton_api::downcast_call(*B.move_as_ok().get(), [&](auto &obj) { this->process_query(src, obj, std::move(promise)); }); +} + void FullNodeFastSyncOverlay::send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) { if (!inited_) { return; @@ -200,18 +226,20 @@ void FullNodeFastSyncOverlay::send_broadcast(BlockBroadcast broadcast) { local_id_.pubkey_hash(), overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } -void FullNodeFastSyncOverlay::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::BufferSlice data) { +void FullNodeFastSyncOverlay::send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) { if (!inited_) { return; } - auto B = - serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, true); // compression enabled + auto B = serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, + collated_data ? collated_data.value() : td::optional{}); if (B.is_error()) { VLOG(FULL_NODE_WARNING) << "failed to serialize block candidate broadcast: " << B.move_as_error(); return; } - VLOG(FULL_NODE_DEBUG) << "Sending newBlockCandidate in fast sync overlay (with compression): " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Sending blockDataBroadcast in fast sync overlay (" << (collated_data ? "with" : "no") + << " cdata): " << block_id.to_str(); td::actor::send_closure(overlays_, &overlay::Overlays::send_broadcast_fec_ex, local_id_, overlay_id_, local_id_.pubkey_hash(), overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } @@ -230,6 +258,53 @@ void FullNodeFastSyncOverlay::send_validator_telemetry(tl_object_ptr> promise) { + if (!inited_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "not inited")); + return; + } + td::actor::send_closure( + overlays_, &overlay::Overlays::get_overlay_random_peers, local_id_, overlay_id_, 1, + [=, overlays = overlays_, rldp2 = rldp2_, local_id = local_id_, overlay_id = overlay_id_, + promise = std::move(promise)](td::Result> R) mutable { + TRY_RESULT_PROMISE(promise, peers, std::move(R)); + if (peers.empty()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "no nodes")); + return; + } + td::BufferSlice query = create_serialize_tl_object( + create_tl_block_id(block_id), only_collated_data); + + td::Promise P = [=, promise = std::move(promise)](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, response, std::move(R)); + delay_action( + [=, promise = std::move(promise), response = std::move(response)]() mutable { + TRY_RESULT_PROMISE(promise, obj, fetch_tl_object(response, true)); + if (obj->get_id() == ton_api::tonNode_blockCandidateDataEmpty::ID) { + promise.set_error(td::Status::Error(ErrorCode::notready, "node doesn't have this block")); + return; + } + BlockIdExt response_block_id; + std::pair result; + TRY_STATUS_PROMISE( + promise, deserialize_block_candidate_data(*obj, only_collated_data, response_block_id, result.first, + result.second, FullNode::max_block_size() * 2 + 128)); + if (response_block_id != block_id) { + promise.set_error(td::Status::Error(ErrorCode::notready, "wrong block id in response")); + return; + } + promise.set_value(std::move(result)); + }, + td::Timestamp::now()); + }; + td::actor::send_closure(overlays, &overlay::Overlays::send_query_via, peers[0], local_id, overlay_id, + "get_block_candidate", std::move(P), timeout, std::move(query), + FullNode::max_block_size() * 2 + 128, rldp2); + }); +} + void FullNodeFastSyncOverlay::collect_validator_telemetry(std::string filename) { if (collect_telemetry_) { telemetry_file_.close(); @@ -290,6 +365,7 @@ void FullNodeFastSyncOverlay::init() { } void receive_query(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id, td::BufferSlice data, td::Promise promise) override { + td::actor::send_closure(node_, &FullNodeFastSyncOverlay::receive_query, src, std::move(data), std::move(promise)); } void receive_broadcast(PublicKeyHash src, overlay::OverlayIdShort overlay_id, td::BufferSlice data) override { td::actor::send_closure(node_, &FullNodeFastSyncOverlay::receive_broadcast, src, std::move(data)); @@ -323,6 +399,7 @@ void FullNodeFastSyncOverlay::init() { td::actor::send_closure(overlays_, &overlay::Overlays::create_semiprivate_overlay, local_id_, overlay_id_full_.clone(), current_validators_adnl_, root_public_keys_, member_certificate_, std::make_unique(actor_id(this)), rules, std::move(scope), options); + td::actor::send_closure(rldp2_, &rldp2::Rldp::add_id, local_id_); inited_ = true; if (shard_.is_masterchain()) { @@ -425,15 +502,13 @@ td::actor::ActorId FullNodeFastSyncOverlays::get_master return it2->second.get(); } -void FullNodeFastSyncOverlays::update_overlays(td::Ref state, - std::set my_adnl_ids, - std::set monitoring_shards, - const FileHash &zero_state_file_hash, - const td::actor::ActorId &keyring, - const td::actor::ActorId &adnl, - const td::actor::ActorId &overlays, - const td::actor::ActorId &validator_manager, - const td::actor::ActorId &full_node) { +void FullNodeFastSyncOverlays::update_overlays( + td::Ref state, std::set my_adnl_ids, + std::set monitoring_shards, const FileHash &zero_state_file_hash, + const td::actor::ActorId &keyring, const td::actor::ActorId &adnl, + const td::actor::ActorId &rldp2, const td::actor::ActorId &overlays, + const td::actor::ActorId &validator_manager, + const td::actor::ActorId &full_node) { monitoring_shards.insert(ShardIdFull{masterchainId}); std::set all_shards; all_shards.insert(ShardIdFull{masterchainId}); @@ -555,8 +630,8 @@ void FullNodeFastSyncOverlays::update_overlays(td::Ref state, if (overlay.empty()) { overlay = td::actor::create_actor( PSTRING() << "FastSyncOv" << shard.to_str(), local_id, shard, zero_state_file_hash, root_public_keys_, - current_validators_adnl_, overlays_info.current_certificate_, receive_broadcasts, keyring, adnl, overlays, - validator_manager, full_node); + current_validators_adnl_, overlays_info.current_certificate_, receive_broadcasts, keyring, adnl, rldp2, + overlays, validator_manager, full_node); } else { td::actor::send_closure(overlay, &FullNodeFastSyncOverlay::set_receive_broadcasts, receive_broadcasts); if (changed_certificate) { diff --git a/validator/full-node-fast-sync-overlays.hpp b/validator/full-node-fast-sync-overlays.hpp index 3505ae5eb..bc6f27633 100644 --- a/validator/full-node-fast-sync-overlays.hpp +++ b/validator/full-node-fast-sync-overlays.hpp @@ -33,9 +33,8 @@ class FullNodeFastSyncOverlay : public td::actor::Actor { void process_broadcast(PublicKeyHash src, ton_api::tonNode_newShardBlockBroadcast& query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast& query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressed& query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressedV2& query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressed& query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressedV2& query); void process_block_candidate_broadcast(PublicKeyHash src, ton_api::tonNode_Broadcast& query); void process_telemetry_broadcast(adnl::AdnlNodeIdShort src, @@ -47,12 +46,22 @@ class FullNodeFastSyncOverlay : public td::actor::Actor { } void receive_broadcast(PublicKeyHash src, td::BufferSlice query); + template + void process_query(adnl::AdnlNodeIdShort src, T& query, td::Promise promise) { + promise.set_error(td::Status::Error(ErrorCode::error, "unknown query")); + } + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_downloadBlockCandidate& query, + td::Promise promise); + void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise); + void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data); void send_broadcast(BlockBroadcast broadcast); - void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data); + void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data, td::optional collated_data); void send_out_msg_queue_proof_broadcast(td::Ref broadcast); void send_validator_telemetry(tl_object_ptr telemetry); + void download_block_candidate(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise); void collect_validator_telemetry(std::string filename); @@ -69,7 +78,7 @@ class FullNodeFastSyncOverlay : public td::actor::Actor { std::vector current_validators_adnl, overlay::OverlayMemberCertificate member_certificate, bool receive_broadcasts, td::actor::ActorId keyring, td::actor::ActorId adnl, - td::actor::ActorId overlays, + td::actor::ActorId rldp2, td::actor::ActorId overlays, td::actor::ActorId validator_manager, td::actor::ActorId full_node) : local_id_(local_id) @@ -81,6 +90,7 @@ class FullNodeFastSyncOverlay : public td::actor::Actor { , zero_state_file_hash_(zero_state_file_hash) , keyring_(keyring) , adnl_(adnl) + , rldp2_(rldp2) , overlays_(overlays) , validator_manager_(validator_manager) , full_node_(full_node) { @@ -97,6 +107,7 @@ class FullNodeFastSyncOverlay : public td::actor::Actor { td::actor::ActorId keyring_; td::actor::ActorId adnl_; + td::actor::ActorId rldp2_; td::actor::ActorId overlays_; td::actor::ActorId validator_manager_; td::actor::ActorId full_node_; @@ -122,6 +133,7 @@ class FullNodeFastSyncOverlays { void update_overlays(td::Ref state, std::set my_adnl_ids, std::set monitoring_shards, const FileHash& zero_state_file_hash, const td::actor::ActorId& keyring, const td::actor::ActorId& adnl, + const td::actor::ActorId& rldp2, const td::actor::ActorId& overlays, const td::actor::ActorId& validator_manager, const td::actor::ActorId& full_node); diff --git a/validator/full-node-private-overlay.cpp b/validator/full-node-private-overlay.cpp index 7ea813a46..5b31fad6c 100644 --- a/validator/full-node-private-overlay.cpp +++ b/validator/full-node-private-overlay.cpp @@ -59,17 +59,12 @@ void FullNodePrivateBlockOverlay::process_broadcast(PublicKeyHash src, ton_api:: } void FullNodePrivateBlockOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcast &query) { + ton_api::tonNode_blockCandidateBroadcastCompressed &query) { process_block_candidate_broadcast(src, query); } void FullNodePrivateBlockOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressed &query) { - process_block_candidate_broadcast(src, query); -} - -void FullNodePrivateBlockOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query) { + ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query) { process_block_candidate_broadcast(src, query); } @@ -79,7 +74,8 @@ void FullNodePrivateBlockOverlay::process_block_candidate_broadcast(PublicKeyHas CatchainSeqno cc_seqno; td::uint32 validator_set_hash; td::BufferSlice data; - auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, + td::optional collated_data; + auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, collated_data, overlay::Overlays::max_fec_broadcast_size()); if (S.is_error()) { LOG(DEBUG) << "dropped broadcast: " << S; @@ -93,9 +89,10 @@ void FullNodePrivateBlockOverlay::process_block_candidate_broadcast(PublicKeyHas VLOG(FULL_NODE_WARNING) << "received block candidate with incorrect file hash from " << src; return; } - VLOG(FULL_NODE_DEBUG) << "Received newBlockCandidate in private overlay from " << src << ": " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Received block candidate broadcast (" << (collated_data ? "with" : "no") + << " cdata) in private overlay from " << src << ": " << block_id.to_str(); td::actor::send_closure(full_node_, &FullNode::process_block_candidate_broadcast, block_id, cc_seqno, - validator_set_hash, std::move(data)); + validator_set_hash, std::move(data), std::move(collated_data)); } void FullNodePrivateBlockOverlay::process_telemetry_broadcast( @@ -159,18 +156,20 @@ void FullNodePrivateBlockOverlay::send_shard_block_info(BlockIdExt block_id, Cat } } -void FullNodePrivateBlockOverlay::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::BufferSlice data) { +void FullNodePrivateBlockOverlay::send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) { if (!inited_) { return; } - auto B = - serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, true); // compression enabled + auto B = serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, + collated_data ? collated_data.value() : td::optional{}); if (B.is_error()) { VLOG(FULL_NODE_WARNING) << "failed to serialize block candidate broadcast: " << B.move_as_error(); return; } - VLOG(FULL_NODE_DEBUG) << "Sending newBlockCandidate in private overlay: " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Sending blockDataBroadcast in private overlay (" << (collated_data ? "with" : "no") + << " cdata): " << block_id.to_str(); td::actor::send_closure(overlays_, &overlay::Overlays::send_broadcast_fec_ex, local_id_, overlay_id_, local_id_.pubkey_hash(), overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } @@ -345,22 +344,18 @@ void FullNodeCustomOverlay::process_broadcast(PublicKeyHash src, ton_api::tonNod std::move(query.message_->data_), it->second); } -void FullNodeCustomOverlay::process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast &query) { - process_block_candidate_broadcast(src, query); -} - void FullNodeCustomOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressed &query) { + ton_api::tonNode_blockCandidateBroadcastCompressed &query) { process_block_candidate_broadcast(src, query); } void FullNodeCustomOverlay::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query) { + ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query) { process_block_candidate_broadcast(src, query); } void FullNodeCustomOverlay::process_block_candidate_broadcast(PublicKeyHash src, ton_api::tonNode_Broadcast &query) { - if (!block_senders_.count(adnl::AdnlNodeIdShort(src))) { + if (!block_senders_.contains(adnl::AdnlNodeIdShort(src))) { VLOG(FULL_NODE_DEBUG) << "Dropping block candidate broadcast in private overlay \"" << name_ << "\" from unauthorized sender " << src; return; @@ -369,7 +364,8 @@ void FullNodeCustomOverlay::process_block_candidate_broadcast(PublicKeyHash src, CatchainSeqno cc_seqno; td::uint32 validator_set_hash; td::BufferSlice data; - auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, + td::optional collated_data; + auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, collated_data, overlay::Overlays::max_fec_broadcast_size()); if (S.is_error()) { LOG(DEBUG) << "dropped broadcast: " << S; @@ -383,10 +379,10 @@ void FullNodeCustomOverlay::process_block_candidate_broadcast(PublicKeyHash src, VLOG(FULL_NODE_WARNING) << "received block candidate with incorrect file hash from " << src; return; } - VLOG(FULL_NODE_DEBUG) << "Received newBlockCandidate in custom overlay \"" << name_ << "\" from " << src << ": " - << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Received block candidate broadcast (" << (collated_data ? "with" : "no") + << " cdata) in custom overlay \"" << name_ << "\" from " << src << ": " << block_id.to_str(); td::actor::send_closure(full_node_, &FullNode::process_block_candidate_broadcast, block_id, cc_seqno, - validator_set_hash, std::move(data)); + validator_set_hash, std::move(data), std::move(collated_data)); } void FullNodeCustomOverlay::receive_broadcast(PublicKeyHash src, td::BufferSlice broadcast) { @@ -431,18 +427,20 @@ void FullNodeCustomOverlay::send_broadcast(BlockBroadcast broadcast) { local_id_.pubkey_hash(), overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } -void FullNodeCustomOverlay::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::BufferSlice data) { +void FullNodeCustomOverlay::send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) { if (!inited_) { return; } - auto B = - serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, true); // compression enabled + auto B = serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, + collated_data ? collated_data.value() : td::optional{}); if (B.is_error()) { VLOG(FULL_NODE_WARNING) << "failed to serialize block candidate broadcast: " << B.move_as_error(); return; } - VLOG(FULL_NODE_DEBUG) << "Sending newBlockCandidate in custom overlay \"" << name_ << "\": " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Sending blockDataBroadcast in custom overlay \"" << name_ << "\" (" + << (collated_data ? "with" : "no") << " cdata): " << block_id.to_str(); td::actor::send_closure(overlays_, &overlay::Overlays::send_broadcast_fec_ex, local_id_, overlay_id_, local_id_.pubkey_hash(), overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } diff --git a/validator/full-node-private-overlay.hpp b/validator/full-node-private-overlay.hpp index 2cbe25f68..d6369a877 100644 --- a/validator/full-node-private-overlay.hpp +++ b/validator/full-node-private-overlay.hpp @@ -32,12 +32,11 @@ class FullNodePrivateBlockOverlay : public td::actor::Actor { void process_broadcast(PublicKeyHash src, ton_api::tonNode_newShardBlockBroadcast &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressed &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressed &query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query); void process_block_candidate_broadcast(PublicKeyHash src, ton_api::tonNode_Broadcast &query); - void process_telemetry_broadcast(PublicKeyHash src, const tl_object_ptr& telemetry); + void process_telemetry_broadcast(PublicKeyHash src, const tl_object_ptr &telemetry); template void process_broadcast(PublicKeyHash, T &) { @@ -46,8 +45,8 @@ class FullNodePrivateBlockOverlay : public td::actor::Actor { void receive_broadcast(PublicKeyHash src, td::BufferSlice query); void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data); - void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data); + void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data, td::optional collated_data); void send_broadcast(BlockBroadcast broadcast); void send_validator_telemetry(tl_object_ptr telemetry); @@ -116,9 +115,8 @@ class FullNodeCustomOverlay : public td::actor::Actor { void process_broadcast(PublicKeyHash src, ton_api::tonNode_externalMessageBroadcast &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressed &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressed &query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query); void process_block_candidate_broadcast(PublicKeyHash src, ton_api::tonNode_Broadcast &query); template @@ -129,8 +127,8 @@ class FullNodeCustomOverlay : public td::actor::Actor { void send_external_message(td::BufferSlice data); void send_broadcast(BlockBroadcast broadcast); - void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data); + void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data, td::optional collated_data); void set_config(FullNodeConfig config) { opts_.config_ = std::move(config); diff --git a/validator/full-node-serializer.cpp b/validator/full-node-serializer.cpp index 5665cc233..38b8a85e8 100644 --- a/validator/full-node-serializer.cpp +++ b/validator/full-node-serializer.cpp @@ -153,7 +153,7 @@ static td::Status deserialize_block_full(ton_api::tonNode_dataFull& f, BlockIdEx static td::Status deserialize_block_full(ton_api::tonNode_dataFullCompressed& f, BlockIdExt& id, td::BufferSlice& proof, td::BufferSlice& data, bool& is_proof_link, int max_decompressed_size) { TRY_RESULT(decompressed, td::lz4_decompress(f.compressed_, max_decompressed_size)); - TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 2)); + TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); if (roots.size() != 2) { return td::Status::Error("expected 2 roots in boc"); } @@ -165,15 +165,17 @@ static td::Status deserialize_block_full(ton_api::tonNode_dataFullCompressed& f, return td::Status::OK(); } -static td::Status deserialize_block_full(ton_api::tonNode_dataFullCompressedV2& f, BlockIdExt& id, td::BufferSlice& proof, - td::BufferSlice& data, bool& is_proof_link, int max_decompressed_size) { +static td::Status deserialize_block_full(ton_api::tonNode_dataFullCompressedV2& f, BlockIdExt& id, + td::BufferSlice& proof, td::BufferSlice& data, bool& is_proof_link, + int max_decompressed_size) { TRY_RESULT(roots, vm::boc_decompress(f.compressed_, max_decompressed_size)); if (roots.size() != 2) { return td::Status::Error("expected 2 roots in boc"); } TRY_RESULT_ASSIGN(proof, vm::std_boc_serialize(roots[0], 0)); TRY_RESULT_ASSIGN(data, vm::std_boc_serialize(roots[1], 31)); - VLOG(FULL_NODE_DEBUG) << "Decompressing block full V2: " << f.compressed_.size() << " -> " << data.size() + proof.size(); + VLOG(FULL_NODE_DEBUG) << "Decompressing block full V2: " << f.compressed_.size() << " -> " + << data.size() + proof.size(); id = create_block_id(f.id_); is_proof_link = f.is_link_; return td::Status::OK(); @@ -197,80 +199,180 @@ td::Status deserialize_block_full(ton_api::tonNode_DataFull& obj, BlockIdExt& id td::Result serialize_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::Slice data, - bool compression_enabled) { - if (!compression_enabled) { - return create_serialize_tl_object( - create_tl_block_id(block_id), cc_seqno, validator_set_hash, - create_tl_object(Bits256::zero(), td::BufferSlice()), td::BufferSlice(data)); - } + td::optional collated_data) { TRY_RESULT(root, vm::std_boc_deserialize(data)); - TRY_RESULT(data_new, vm::std_boc_serialize(root, 2)); + std::vector> roots = {root}; + if (collated_data) { + TRY_RESULT(collated_data_roots, vm::std_boc_deserialize_multi(data, 1000000, true)); + roots.insert(roots.end(), collated_data_roots.begin(), collated_data_roots.end()); + } + TRY_RESULT(data_new, vm::std_boc_serialize_multi(std::move(roots), 2)); td::BufferSlice compressed = td::lz4_compress(data_new); - VLOG(FULL_NODE_DEBUG) << "Compressing block candidate broadcast: " << data.size() << " -> " << compressed.size(); - return create_serialize_tl_object( + VLOG(FULL_NODE_DEBUG) << "Compressing block candidate broadcast " << (collated_data ? "(with cdata)" : "(no cdata)") + << ": " << data.size() + (collated_data ? collated_data.value().size() : 0) << " -> " + << compressed.size(); + bool with_collated_data = (bool)collated_data; + return create_serialize_tl_object( create_tl_block_id(block_id), cc_seqno, validator_set_hash, - create_tl_object(Bits256::zero(), td::BufferSlice()), 0, std::move(compressed)); -} - -static td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_newBlockCandidateBroadcast& obj, - BlockIdExt& block_id, CatchainSeqno& cc_seqno, - td::uint32& validator_set_hash, td::BufferSlice& data) { - block_id = create_block_id(obj.id_); - cc_seqno = obj.catchain_seqno_; - validator_set_hash = obj.validator_set_hash_; - data = std::move(obj.data_); - return td::Status::OK(); + create_tl_object(Bits256::zero(), td::BufferSlice()), with_collated_data ? 1 : 0, + std::move(compressed)); } -static td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_newBlockCandidateBroadcastCompressed& obj, +static td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_blockCandidateBroadcastCompressed& obj, BlockIdExt& block_id, CatchainSeqno& cc_seqno, td::uint32& validator_set_hash, td::BufferSlice& data, + td::optional& collated_data, int max_decompressed_data_size) { block_id = create_block_id(obj.id_); cc_seqno = obj.catchain_seqno_; validator_set_hash = obj.validator_set_hash_; TRY_RESULT(decompressed, td::lz4_decompress(obj.compressed_, max_decompressed_data_size)); - TRY_RESULT(root, vm::std_boc_deserialize(decompressed)); - TRY_RESULT_ASSIGN(data, vm::std_boc_serialize(root, 31)); - VLOG(FULL_NODE_DEBUG) << "Decompressing block candidate broadcast: " << obj.compressed_.size() << " -> " - << data.size(); + TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); + if (roots.empty()) { + return td::Status::Error("expected at least 1 root in boc"); + } + if (!(obj.flags_ & 1) && roots.size() != 1) { + return td::Status::Error("expected 1 root in boc"); + } + TRY_RESULT_ASSIGN(data, vm::std_boc_serialize(roots[0], 31)); + if (obj.flags_ & 1) { + roots.erase(roots.begin()); + TRY_RESULT_ASSIGN(collated_data, vm::std_boc_serialize_multi(std::move(roots), 2)); + } else { + collated_data = {}; + } + VLOG(FULL_NODE_DEBUG) << "Decompressing block candidate broadcast " << (collated_data ? "(with cdata)" : "(no cdata)") + << ": " << obj.compressed_.size() << " -> " + << data.size() + (collated_data ? collated_data.value().size() : 0); return td::Status::OK(); } -static td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_newBlockCandidateBroadcastCompressedV2& obj, +static td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_blockCandidateBroadcastCompressedV2& obj, BlockIdExt& block_id, CatchainSeqno& cc_seqno, td::uint32& validator_set_hash, td::BufferSlice& data, + td::optional& collated_data, int max_decompressed_data_size) { block_id = create_block_id(obj.id_); cc_seqno = obj.catchain_seqno_; validator_set_hash = obj.validator_set_hash_; TRY_RESULT(roots, vm::boc_decompress(obj.compressed_, max_decompressed_data_size)); - if (roots.size() != 1) { + if (roots.empty()) { + return td::Status::Error("expected at least 1 root in boc"); + } + if (!(obj.flags_ & 1) && roots.size() != 1) { return td::Status::Error("expected 1 root in boc"); } - auto root = std::move(roots[0]); - TRY_RESULT_ASSIGN(data, vm::std_boc_serialize(root, 31)); - VLOG(FULL_NODE_DEBUG) << "Decompressing block candidate broadcast V2: " << obj.compressed_.size() << " -> " - << data.size(); + TRY_RESULT_ASSIGN(data, vm::std_boc_serialize(roots[0], 31)); + if (obj.flags_ & 1) { + roots.erase(roots.begin()); + TRY_RESULT_ASSIGN(collated_data, vm::std_boc_serialize_multi(std::move(roots), 2)); + } else { + collated_data = {}; + } + VLOG(FULL_NODE_DEBUG) << "Decompressing block candidate broadcast V2 " + << (collated_data ? "(with cdata)" : "(no cdata)") << ": " << obj.compressed_.size() << " -> " + << data.size() + (collated_data ? collated_data.value().size() : 0); return td::Status::OK(); } td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_Broadcast& obj, BlockIdExt& block_id, CatchainSeqno& cc_seqno, td::uint32& validator_set_hash, - td::BufferSlice& data, int max_decompressed_data_size) { + td::BufferSlice& data, td::optional& collated_data, + int max_decompressed_data_size) { + td::Status S; + ton_api::downcast_call( + obj, td::overloaded( + [&](ton_api::tonNode_blockCandidateBroadcastCompressed& f) { + S = deserialize_block_candidate_broadcast(f, block_id, cc_seqno, validator_set_hash, data, + collated_data, max_decompressed_data_size); + }, + [&](ton_api::tonNode_blockCandidateBroadcastCompressedV2& f) { + S = deserialize_block_candidate_broadcast(f, block_id, cc_seqno, validator_set_hash, data, + collated_data, max_decompressed_data_size); + }, + [&](auto&) { S = td::Status::Error("unknown data type"); })); + return S; +} + +td::Result serialize_block_candidate_data(BlockCandidate candidate, bool only_collated_data, + bool compression_enabled) { + if (!compression_enabled) { + return create_serialize_tl_object( + create_tl_block_id(candidate.id), only_collated_data ? td::BufferSlice{} : std::move(candidate.data), + std::move(candidate.collated_data)); + } + + std::vector> roots; + vm::BagOfCells boc1, boc2; + if (!only_collated_data) { + TRY_STATUS(boc1.deserialize(candidate.data)); + if (boc1.get_root_count() != 1) { + return td::Status::Error("block should have exactly one root"); + } + roots.push_back(boc1.get_root_cell()); + } + TRY_STATUS(boc2.deserialize(candidate.collated_data)); + for (int i = 0; i < boc2.get_root_count(); ++i) { + roots.push_back(boc2.get_root_cell(i)); + } + td::BufferSlice compressed; + if (!roots.empty()) { + TRY_RESULT_ASSIGN(compressed, vm::boc_compress(roots, vm::CompressionAlgorithm::ImprovedStructureLZ4)); + } + if (only_collated_data) { + LOG(DEBUG) << "Compressing block candidate (only cdata): " << candidate.collated_data.size() << " -> " + << compressed.size(); + } else { + LOG(DEBUG) << "Compressing block candidate (full): " << candidate.data.size() + candidate.collated_data.size() + << " -> " << compressed.size(); + } + return create_serialize_tl_object(create_tl_block_id(candidate.id), + 0, std::move(compressed)); +} + +td::Status deserialize_block_candidate_data(ton_api::tonNode_blockCandidateData& obj, bool only_collated_data, + BlockIdExt& id, td::BufferSlice& data, td::BufferSlice& collated_data) { + id = create_block_id(obj.block_id_); + data = only_collated_data ? td::BufferSlice{} : std::move(obj.data_); + collated_data = std::move(obj.collated_data_); + return td::Status::OK(); +} + +td::Status deserialize_block_candidate_data(ton_api::tonNode_blockCandidateDataCompressedV2& obj, + bool only_collated_data, BlockIdExt& id, td::BufferSlice& data, + td::BufferSlice& collated_data, int max_decompressed_size) { + std::vector> roots; + if (!obj.compressed_.empty()) { + TRY_RESULT_ASSIGN(roots, vm::boc_decompress(obj.compressed_, max_decompressed_size)); + } + if (only_collated_data) { + data = td::BufferSlice{}; + } else { + if (roots.empty()) { + return td::Status::Error("expected at least one root"); + } + TRY_RESULT_ASSIGN(data, vm::std_boc_serialize(roots[0], 31)); + roots.erase(roots.begin()); + } + TRY_RESULT_ASSIGN(collated_data, vm::std_boc_serialize_multi(roots, 2)); + VLOG(FULL_NODE_DEBUG) << "Decompressing block candidate V2 " << (only_collated_data ? "(only cdata)" : "(full)") + << ": " << obj.compressed_.size() << " -> " << data.size() + collated_data.size(); + id = create_block_id(obj.block_id_); + return td::Status::OK(); +} + +td::Status deserialize_block_candidate_data(ton_api::tonNode_BlockCandidateData& obj, bool only_collated_data, + BlockIdExt& id, td::BufferSlice& data, td::BufferSlice& collated_data, + int max_decompressed_data_size) { td::Status S; ton_api::downcast_call(obj, td::overloaded( - [&](ton_api::tonNode_newBlockCandidateBroadcast& f) { - S = deserialize_block_candidate_broadcast(f, block_id, cc_seqno, validator_set_hash, - data); - }, - [&](ton_api::tonNode_newBlockCandidateBroadcastCompressed& f) { - S = deserialize_block_candidate_broadcast(f, block_id, cc_seqno, validator_set_hash, - data, max_decompressed_data_size); + [&](ton_api::tonNode_blockCandidateData& f) { + S = deserialize_block_candidate_data(f, only_collated_data, id, data, + collated_data); }, - [&](ton_api::tonNode_newBlockCandidateBroadcastCompressedV2& f) { - S = deserialize_block_candidate_broadcast(f, block_id, cc_seqno, validator_set_hash, - data, max_decompressed_data_size); + [&](ton_api::tonNode_blockCandidateDataCompressedV2& f) { + S = deserialize_block_candidate_data(f, only_collated_data, id, data, collated_data, + max_decompressed_data_size); }, [&](auto&) { S = td::Status::Error("unknown data type"); })); return S; diff --git a/validator/full-node-serializer.hpp b/validator/full-node-serializer.hpp index f6751689c..a457ebc60 100644 --- a/validator/full-node-serializer.hpp +++ b/validator/full-node-serializer.hpp @@ -30,9 +30,16 @@ td::Status deserialize_block_full(ton_api::tonNode_DataFull& obj, BlockIdExt& id td::Result serialize_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::Slice data, - bool compression_enabled); + td::optional collated_data); td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_Broadcast& obj, BlockIdExt& block_id, CatchainSeqno& cc_seqno, td::uint32& validator_set_hash, - td::BufferSlice& data, int max_decompressed_data_size); + td::BufferSlice& data, td::optional& collated_data, + int max_decompressed_data_size); + +td::Result serialize_block_candidate_data(BlockCandidate candidate, bool only_collated_data, + bool compression_enabled); +td::Status deserialize_block_candidate_data(ton_api::tonNode_BlockCandidateData& obj, bool only_collated_data, + BlockIdExt& id, td::BufferSlice& data, td::BufferSlice& collated_data, + int max_decompressed_data_size); } // namespace ton::validator::fullnode diff --git a/validator/full-node-shard.cpp b/validator/full-node-shard.cpp index 771b28be3..cac4d4026 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -795,17 +795,13 @@ void FullNodeShardImpl::process_broadcast(PublicKeyHash src, ton_api::tonNode_ne block_id, query.block_->cc_seqno_, std::move(query.block_->data_)); } -void FullNodeShardImpl::process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast &query) { - process_block_candidate_broadcast(src, query); -} - void FullNodeShardImpl::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressed &query) { + ton_api::tonNode_blockCandidateBroadcastCompressed &query) { process_block_candidate_broadcast(src, query); } void FullNodeShardImpl::process_broadcast(PublicKeyHash src, - ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query) { + ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query) { process_block_candidate_broadcast(src, query); } @@ -814,7 +810,8 @@ void FullNodeShardImpl::process_block_candidate_broadcast(PublicKeyHash src, ton CatchainSeqno cc_seqno; td::uint32 validator_set_hash; td::BufferSlice data; - auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, + td::optional collated_data; + auto S = deserialize_block_candidate_broadcast(query, block_id, cc_seqno, validator_set_hash, data, collated_data, overlay::Overlays::max_fec_broadcast_size()); if (data.size() > FullNode::max_block_size()) { VLOG(FULL_NODE_WARNING) << "received block candidate with too big size from " << src; @@ -824,9 +821,10 @@ void FullNodeShardImpl::process_block_candidate_broadcast(PublicKeyHash src, ton VLOG(FULL_NODE_WARNING) << "received block candidate with incorrect file hash from " << src; return; } - VLOG(FULL_NODE_DEBUG) << "Received newBlockCandidate from " << src << ": " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Received block candidate broadcast (" << (collated_data ? "with" : "no") << " cdata) from " + << src << ": " << block_id.to_str(); td::actor::send_closure(full_node_, &FullNode::process_block_candidate_broadcast, block_id, cc_seqno, - validator_set_hash, std::move(data)); + validator_set_hash, std::move(data), std::move(collated_data)); } void FullNodeShardImpl::process_broadcast(PublicKeyHash src, ton_api::tonNode_blockBroadcast &query) { @@ -933,19 +931,20 @@ void FullNodeShardImpl::send_shard_block_info(BlockIdExt block_id, CatchainSeqno } } -void FullNodeShardImpl::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data) { +void FullNodeShardImpl::send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) { if (!client_.empty()) { UNREACHABLE(); return; } - auto B = - serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, true); // compression enabled + auto B = serialize_block_candidate_broadcast(block_id, cc_seqno, validator_set_hash, data, + collated_data ? collated_data.value() : td::optional{}); if (B.is_error()) { VLOG(FULL_NODE_WARNING) << "failed to serialize block candidate broadcast: " << B.move_as_error(); return; } - VLOG(FULL_NODE_DEBUG) << "Sending newBlockCandidate: " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Sending blockDataBroadcast (" << (collated_data ? "with" : "no") << " cdata): " << block_id.to_str(); td::actor::send_closure(overlays_, &overlay::Overlays::send_broadcast_fec_ex, adnl_id_, overlay_id_, local_id_, overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } diff --git a/validator/full-node-shard.h b/validator/full-node-shard.h index 0c6aa441b..d9cd2d2c5 100644 --- a/validator/full-node-shard.h +++ b/validator/full-node-shard.h @@ -42,8 +42,9 @@ class FullNodeShard : public td::actor::Actor { virtual void send_ihr_message(td::BufferSlice data) = 0; virtual void send_external_message(td::BufferSlice data) = 0; virtual void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) = 0; - virtual void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data) = 0; + virtual void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) = 0; virtual void send_broadcast(BlockBroadcast broadcast) = 0; virtual void sign_overlay_certificate(PublicKeyHash signed_key, td::uint32 expiry_at, td::uint32 max_size, diff --git a/validator/full-node-shard.hpp b/validator/full-node-shard.hpp index a90460adb..86e61f631 100644 --- a/validator/full-node-shard.hpp +++ b/validator/full-node-shard.hpp @@ -162,9 +162,8 @@ class FullNodeShardImpl : public FullNodeShard { LOG(ERROR) << "Ignore outMsgQueueProofBroadcast"; } - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcast &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressed &query); - void process_broadcast(PublicKeyHash src, ton_api::tonNode_newBlockCandidateBroadcastCompressedV2 &query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressed &query); + void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockCandidateBroadcastCompressedV2 &query); void process_block_candidate_broadcast(PublicKeyHash src, ton_api::tonNode_Broadcast &query); void receive_broadcast(PublicKeyHash src, td::BufferSlice query); @@ -175,8 +174,8 @@ class FullNodeShardImpl : public FullNodeShard { void send_ihr_message(td::BufferSlice data) override; void send_external_message(td::BufferSlice data) override; void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) override; - void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data) override; + void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data, td::optional collated_data) override; void send_broadcast(BlockBroadcast broadcast) override; void download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, diff --git a/validator/full-node.cpp b/validator/full-node.cpp index d648fc821..3c6597b0f 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -240,7 +240,7 @@ void FullNodeImpl::on_new_masterchain_block(td::Ref state, std } } - for (auto it = shards_.begin(); it != shards_.end(); ) { + for (auto it = shards_.begin(); it != shards_.end();) { if (all_shards.contains(it->first)) { ++it; } else { @@ -283,7 +283,7 @@ void FullNodeImpl::on_new_masterchain_block(td::Ref state, std monitoring_shards.insert(cut_shard(shard)); } fast_sync_overlays_.update_overlays(state, std::move(my_adnl_ids), std::move(monitoring_shards), - zero_state_file_hash_, keyring_, adnl_, overlays_, validator_manager_, + zero_state_file_hash_, keyring_, adnl_, rldp2_, overlays_, validator_manager_, actor_id(this)); update_validator_telemetry_collector(); } @@ -360,20 +360,22 @@ void FullNodeImpl::send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_s td::actor::send_closure(shard, &FullNodeShard::send_shard_block_info, block_id, cc_seqno, std::move(data)); } -void FullNodeImpl::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) { +void FullNodeImpl::send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data, int mode) { if (mode & broadcast_mode_custom) { - send_block_candidate_broadcast_to_custom_overlays(block_id, cc_seqno, validator_set_hash, data); + send_block_candidate_broadcast_to_custom_overlays(block_id, cc_seqno, validator_set_hash, data, collated_data); } - if (mode & broadcast_mode_private_block) { + if (mode & broadcast_mode_fast_sync) { if (!private_block_overlays_.empty()) { - td::actor::send_closure(private_block_overlays_.begin()->second, &FullNodePrivateBlockOverlay::send_block_candidate, - block_id, cc_seqno, validator_set_hash, data.clone()); + td::actor::send_closure(private_block_overlays_.begin()->second, + &FullNodePrivateBlockOverlay::send_block_candidate_broadcast, block_id, cc_seqno, + validator_set_hash, data.clone(), collated_data.clone()); } auto fast_sync_overlay = fast_sync_overlays_.choose_overlay(block_id.shard_full()).first; if (!fast_sync_overlay.empty()) { - td::actor::send_closure(fast_sync_overlay, &FullNodeFastSyncOverlay::send_block_candidate, block_id, cc_seqno, - validator_set_hash, data.clone()); + td::actor::send_closure(fast_sync_overlay, &FullNodeFastSyncOverlay::send_block_candidate_broadcast, block_id, + cc_seqno, validator_set_hash, data.clone(), collated_data.clone()); } } if (mode & broadcast_mode_public) { @@ -382,8 +384,8 @@ void FullNodeImpl::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_se VLOG(FULL_NODE_WARNING) << "dropping OUT shard block info message to unknown shard"; return; } - td::actor::send_closure(shard, &FullNodeShard::send_block_candidate, block_id, cc_seqno, validator_set_hash, - std::move(data)); + td::actor::send_closure(shard, &FullNodeShard::send_block_candidate_broadcast, block_id, cc_seqno, + validator_set_hash, std::move(data), std::move(collated_data)); } } @@ -399,7 +401,7 @@ void FullNodeImpl::send_broadcast(BlockBroadcast broadcast, int mode) { if (mode & broadcast_mode_custom) { send_block_broadcast_to_custom_overlays(broadcast); } - if (mode & broadcast_mode_private_block) { + if (mode & broadcast_mode_fast_sync) { if (!private_block_overlays_.empty()) { td::actor::send_closure(private_block_overlays_.begin()->second, &FullNodePrivateBlockOverlay::send_broadcast, broadcast.clone()); @@ -519,6 +521,17 @@ void FullNodeImpl::download_out_msg_queue_proof(ShardIdFull dst_shard, std::vect timeout, std::move(promise)); } +void FullNodeImpl::download_block_candidate(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) { + auto fast_sync_overlay = fast_sync_overlays_.choose_overlay(block_id.shard_full()).first; + if (fast_sync_overlay.empty()) { + promise.set_error(td::Status::Error("no fast-sync overlays for download block candidate query")); + return; + } + td::actor::send_closure(fast_sync_overlay, &FullNodeFastSyncOverlay::download_block_candidate, block_id, + only_collated_data, timeout, std::move(promise)); +} + td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard, bool historical) { if (shard.is_masterchain()) { return shards_[ShardIdFull{masterchainId}].actor.get(); @@ -640,11 +653,12 @@ void FullNodeImpl::process_block_broadcast(BlockBroadcast broadcast) { } void FullNodeImpl::process_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::BufferSlice data) { - send_block_candidate_broadcast_to_custom_overlays(block_id, cc_seqno, validator_set_hash, data); + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) { + send_block_candidate_broadcast_to_custom_overlays(block_id, cc_seqno, validator_set_hash, data, collated_data); // ignore cc_seqno and validator_hash for now td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::new_block_candidate_broadcast, block_id, - std::move(data)); + std::move(data), std::move(collated_data)); } void FullNodeImpl::get_out_msg_queue_query_token(td::Promise> promise) { @@ -716,10 +730,11 @@ void FullNodeImpl::start_up() { void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) override { td::actor::send_closure(id_, &FullNodeImpl::send_shard_block_info, block_id, cc_seqno, std::move(data)); } - void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) override { - td::actor::send_closure(id_, &FullNodeImpl::send_block_candidate, block_id, cc_seqno, validator_set_hash, - std::move(data), mode); + void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data, td::optional collated_data, + int mode) override { + td::actor::send_closure(id_, &FullNodeImpl::send_block_candidate_broadcast, block_id, cc_seqno, + validator_set_hash, std::move(data), std::move(collated_data), mode); } void send_out_msg_queue_proof_broadcast(td::Ref broadcast) override { td::actor::send_closure(id_, &FullNodeImpl::send_out_msg_queue_proof_broadcast, std::move(broadcast)); @@ -766,6 +781,11 @@ void FullNodeImpl::start_up() { td::actor::send_closure(id_, &FullNodeImpl::download_out_msg_queue_proof, dst_shard, std::move(blocks), limits, timeout, std::move(promise)); } + void download_block_candidate(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) override { + td::actor::send_closure(id_, &FullNodeImpl::download_block_candidate, block_id, only_collated_data, timeout, + std::move(promise)); + } void new_key_block(BlockHandle handle) override { td::actor::send_closure(id_, &FullNodeImpl::new_key_block, std::move(handle)); @@ -860,9 +880,9 @@ void FullNodeImpl::send_block_broadcast_to_custom_overlays(const BlockBroadcast } } -void FullNodeImpl::send_block_candidate_broadcast_to_custom_overlays(const BlockIdExt &block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, - const td::BufferSlice &data) { +void FullNodeImpl::send_block_candidate_broadcast_to_custom_overlays( + const BlockIdExt &block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, const td::BufferSlice &data, + const td::optional &collated_data) { // Same cache of sent broadcasts as in send_block_broadcast_to_custom_overlays if (!custom_overlays_sent_broadcasts_.insert(block_id).second) { return; @@ -876,8 +896,9 @@ void FullNodeImpl::send_block_candidate_broadcast_to_custom_overlays(const Block if (private_overlay.params_.send_shard(block_id.shard_full())) { for (auto &[local_id, actor] : private_overlay.actors_) { if (private_overlay.params_.block_senders_.contains(local_id)) { - td::actor::send_closure(actor, &FullNodeCustomOverlay::send_block_candidate, block_id, cc_seqno, - validator_set_hash, data.clone()); + // Don't send collated data here + td::actor::send_closure(actor, &FullNodeCustomOverlay::send_block_candidate_broadcast, block_id, cc_seqno, + validator_set_hash, data.clone(), td::optional{}); } } } diff --git a/validator/full-node.h b/validator/full-node.h index 4c11c1fbd..a815fc10a 100644 --- a/validator/full-node.h +++ b/validator/full-node.h @@ -99,7 +99,8 @@ class FullNode : public td::actor::Actor { virtual void process_block_broadcast(BlockBroadcast broadcast) = 0; virtual void process_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::BufferSlice data) = 0; + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data) = 0; virtual void get_out_msg_queue_query_token(td::Promise> promise) = 0; virtual void set_validator_telemetry_filename(std::string value) = 0; @@ -116,7 +117,7 @@ class FullNode : public td::actor::Actor { static constexpr td::uint64 max_state_size() { return 4ull << 30; } - enum { broadcast_mode_public = 1, broadcast_mode_private_block = 2, broadcast_mode_custom = 4 }; + enum { broadcast_mode_public = 1, broadcast_mode_fast_sync = 2, broadcast_mode_custom = 4 }; static constexpr td::int32 MAX_FAST_SYNC_OVERLAY_CLIENTS = 5; diff --git a/validator/full-node.hpp b/validator/full-node.hpp index 482a916bb..974082971 100644 --- a/validator/full-node.hpp +++ b/validator/full-node.hpp @@ -68,10 +68,10 @@ class FullNodeImpl : public FullNode { void send_ihr_message(AccountIdPrefixFull dst, td::BufferSlice data); void send_ext_message(AccountIdPrefixFull dst, td::BufferSlice data); void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqnp, td::BufferSlice data); - void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode); + void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data, td::optional collated_data, int mode); void send_broadcast(BlockBroadcast broadcast, int mode); - void send_out_msg_queue_proof_broadcast(td::Ref broadcats); + void send_out_msg_queue_proof_broadcast(td::Ref broadcasts); void download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); void download_zero_state(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); @@ -87,13 +87,15 @@ class FullNodeImpl : public FullNode { void download_out_msg_queue_proof(ShardIdFull dst_shard, std::vector blocks, block::ImportedMsgQueueLimits limits, td::Timestamp timeout, td::Promise>> promise); + void download_block_candidate(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise); void got_key_block_config(td::Ref config); void new_key_block(BlockHandle handle); void process_block_broadcast(BlockBroadcast broadcast) override; void process_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data) override; + td::BufferSlice data, td::optional collated_data) override; void get_out_msg_queue_query_token(td::Promise> promise) override; void set_validator_telemetry_filename(std::string value) override; @@ -176,7 +178,8 @@ class FullNodeImpl : public FullNode { void update_custom_overlay(CustomOverlayInfo& overlay); void send_block_broadcast_to_custom_overlays(const BlockBroadcast& broadcast); void send_block_candidate_broadcast_to_custom_overlays(const BlockIdExt& block_id, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, const td::BufferSlice& data); + td::uint32 validator_set_hash, const td::BufferSlice& data, + const td::optional& collated_data); std::string validator_telemetry_filename_; PublicKeyHash validator_telemetry_collector_key_ = PublicKeyHash::zero(); diff --git a/validator/impl/CMakeLists.txt b/validator/impl/CMakeLists.txt index 9ab32c47b..487a46a60 100644 --- a/validator/impl/CMakeLists.txt +++ b/validator/impl/CMakeLists.txt @@ -8,6 +8,7 @@ set(TON_VALIDATOR_SOURCE candidates-buffer.cpp check-proof.cpp collator.cpp + collated-data-merger.cpp config.cpp external-message.cpp fabric.cpp @@ -28,6 +29,7 @@ set(TON_VALIDATOR_SOURCE check-proof.hpp collator-impl.h collator.h + collated-data-merger.h config.hpp external-message.hpp ihr-message.hpp diff --git a/validator/impl/collated-data-merger.cpp b/validator/impl/collated-data-merger.cpp new file mode 100644 index 000000000..c811e2580 --- /dev/null +++ b/validator/impl/collated-data-merger.cpp @@ -0,0 +1,192 @@ +/* +This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . +*/ +#include "collated-data-merger.h" + +#include "vm/cells/ExtCell.h" + +namespace ton::validator { + +class CollatedDataMergerExtCellLoader { + public: + static td::Result> load_data_cell(const vm::Cell &cell, const td::Unit &extra) { + throw vm::VmVirtError{}; + } +}; + +using CollatedDataMergerExtCell = vm::ExtCell; + +void CollatedDataMerger::get_cells(std::vector hashes, td::Promise>> promise) { + td::HashMap> result; + for (const vm::CellHash &hash : hashes) { + auto it = cells_.find(hash); + if (it != cells_.end() && it->second.cell.not_null()) { + result[hash] = it->second.cell; + } + } + promise.set_value(std::move(result)); +} + +void CollatedDataMerger::add_cells(Ref cell) { + auto &info = cells_[cell->get_hash()]; + if (info.visited) { + return; + } + info.visited = true; + auto loaded_cell = cell->load_cell().move_as_ok(); + CHECK(loaded_cell.virt.get_virtualization() == 0); + auto data_cell = std::move(loaded_cell.data_cell); + info.set_cell(data_cell); + + for (unsigned i = 0; i < data_cell->size_refs(); ++i) { + add_cells(data_cell->get_ref(i)); + } + bool is_prunned_branch = data_cell->special_type() == vm::CellTraits::SpecialType::PrunnedBranch; + for (unsigned level = 0; level < cell->get_level(); ++level) { + vm::CellHash hash = cell->get_hash(level); + auto &info2 = cells_[hash]; + if (info2.cell.not_null() && (is_prunned_branch || !info2.prunned)) { + continue; + } + if (is_prunned_branch) { + vm::detail::LevelMask level_mask = data_cell->get_level_mask().apply(level); + td::uint8 hashes[(1 + vm::Cell::max_level) * 32]; + td::uint8 depths[(1 + vm::Cell::max_level) * 2]; + size_t n = 0; + for (td::uint32 i = 0; i <= level; ++i) { + if (level_mask.is_significant(i)) { + memcpy(hashes + n * 32, data_cell->get_hash(i).as_slice().data(), 32); + vm::DataCell::store_depth(depths + n * 2, data_cell->get_depth(i)); + ++n; + } + } + info2.cell = CollatedDataMergerExtCell::create(vm::PrunnedCellInfo{.level_mask = level_mask, + .hash = td::Slice(hashes, n * 32), + .depth = td::Slice(depths, n * 2)}, + td::Unit{}) + .move_as_ok(); + info2.prunned = true; + CHECK(info2.cell->get_hash() == hash) + } else { + vm::CellBuilder cb; + cb.store_bits(data_cell->get_data(), data_cell->size()); + unsigned child_level = level + (data_cell->special_type() == vm::CellTraits::SpecialType::MerkleProof || + data_cell->special_type() == vm::CellTraits::SpecialType::MerkleUpdate + ? 1 + : 0); + for (unsigned i = 0; i < data_cell->size_refs(); ++i) { + Ref child = data_cell->get_ref(i); + vm::CellHash child_hash = child->get_hash(child_level); + auto it = cells_.find(child_hash); + CHECK(it != cells_.end()); + cb.store_ref(it->second.cell); + } + info2.set_cell(cb.finalize(data_cell->is_special())); + CHECK(info2.cell->get_hash() == hash) + } + } +} + +void CollatedDataMerger::add_block_candidate(BlockIdExt block_id, Ref root, + std::vector> collated_roots) { + if (!blocks_.insert(block_id).second) { + return; + } + td::Timer timer; + add_cells(std::move(root)); + for (auto &c : collated_roots) { + add_cells(std::move(c)); + } + LOG(INFO) << "Added block " << block_id.to_str() << " in " << timer.elapsed() + << " s, total cells = " << cells_.size(); +} + +void CollatedDataMerger::add_block_candidate_data(BlockIdExt block_id, td::BufferSlice data, + td::BufferSlice collated_data) { + if (!blocks_.insert(block_id).second) { + return; + } + td::Timer timer; + auto r_root = vm::std_boc_deserialize(data); + if (r_root.is_error()) { + LOG(WARNING) << "Failed to deserialize block data for " << block_id.to_str() << " : " << r_root.error(); + return; + } + auto r_collated_roots = vm::std_boc_deserialize_multi(collated_data, 1000000, true); + if (r_collated_roots.is_error()) { + LOG(WARNING) << "Failed to deserialize collated data for " << block_id.to_str() << " : " + << r_collated_roots.error(); + return; + } + add_cells(r_root.move_as_ok()); + for (auto &c : r_collated_roots.move_as_ok()) { + add_cells(std::move(c)); + } + LOG(INFO) << "Added block " << block_id.to_str() << " in " << timer.elapsed() << " s"; +} + +void CollatedDataMerger::CellInfo::set_cell(const Ref &new_cell) { + if (cell.is_null()) { + cell = new_cell; + prunned = false; + return; + } + if (prunned) { + auto ext_cell = dynamic_cast(cell.get()); + CHECK(ext_cell != nullptr); + ext_cell->set_inner_cell(new_cell).ensure(); + prunned = false; + } +} + +td::Status CollatedDataDeduplicator::add_block_candidate(BlockSeqno seqno, td::Slice block_data, + td::Slice collated_data) { + td::Timer timer; + TRY_RESULT(root, vm::std_boc_deserialize(block_data)); + TRY_RESULT(collated_roots, vm::std_boc_deserialize_multi(collated_data, 1000000, true)); + std::lock_guard lock{mutex_}; + td::HashSet visited; + std::function &)> dfs = [&](const Ref &cell) { + if (!visited.insert(cell->get_hash()).second) { + return; + } + vm::CellSlice cs{vm::NoVm{}, cell}; + for (unsigned i = cs.special_type() == vm::CellTraits::SpecialType::PrunnedBranch ? cell->get_level() : 0; + i <= cell->get_level(); ++i) { + auto [it, inserted] = cells_.emplace(cell->get_hash(i), seqno); + if (!inserted) { + it->second = std::min(it->second, seqno); + } + } + for (unsigned i = 0; i < cs.size_refs(); ++i) { + dfs(cs.prefetch_ref(i)); + } + }; + dfs(root); + for (const Ref &c : collated_roots) { + dfs(c); + } + LOG(INFO) << "Added block " << seqno << " in " << timer.elapsed() << " s, total cells = " << cells_.size(); + return td::Status::OK(); +} + +bool CollatedDataDeduplicator::cell_exists(const vm::CellHash &hash, BlockSeqno seqno) { + std::lock_guard lock{mutex_}; + auto it = cells_.find(hash); + return it != cells_.end() && it->second < seqno; +} + +} // namespace ton::validator diff --git a/validator/impl/collated-data-merger.h b/validator/impl/collated-data-merger.h new file mode 100644 index 000000000..64ad59310 --- /dev/null +++ b/validator/impl/collated-data-merger.h @@ -0,0 +1,62 @@ +/* + This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . +*/ +#pragma once +#include "common/refcnt.hpp" +#include "vm/dict.h" +#include "ton/ton-types.h" +#include "ton/ton-shard.h" +#include "common/bitstring.h" +#include "../../crypto/block/block.h" +#include "td/actor/common.h" +#include "td/actor/core/Actor.h" +#include "vm/db/CellHashTable.h" + +#include + +namespace ton::validator { +using td::Ref; + +class CollatedDataMerger : public td::actor::Actor { + public: + void get_cells(std::vector hashes, td::Promise>> promise); + void add_cells(Ref cell); + void add_block_candidate(BlockIdExt block_id, Ref root, std::vector> collated_roots); + void add_block_candidate_data(BlockIdExt block_id, td::BufferSlice data, td::BufferSlice collated_data); + + private: + struct CellInfo { + Ref cell; + bool prunned = true; + bool visited = false; + + void set_cell(const Ref& new_cell); + }; + td::HashMap cells_; + std::set blocks_; +}; + +class CollatedDataDeduplicator { + public: + td::Status add_block_candidate(BlockSeqno seqno, td::Slice block_data, td::Slice collated_data); + bool cell_exists(const vm::CellHash& hash, BlockSeqno seqno); + + private: + td::HashMap cells_; + std::mutex mutex_; // TODO: other way to synchronize? +}; + +} // namespace ton::validator \ No newline at end of file diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index 83ae4370a..b40137362 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -17,6 +17,7 @@ Copyright 2017-2020 Telegram Systems LLP */ #pragma once +#include "collated-data-merger.h" #include "block-parse.h" #include "interfaces/validator-manager.h" #include "shard.hpp" @@ -47,8 +48,8 @@ class Collator final : public td::actor::Actor { return SUPPORTED_VERSION; } static constexpr long long supported_capabilities() { - return ton::capCreateStatsEnabled | ton::capBounceMsgBody | ton::capReportVersion | ton::capShortDequeue | - ton::capStoreOutMsgQueueSize | ton::capMsgMetadata | ton::capDeferMessages | ton::capFullCollatedData; + return capCreateStatsEnabled | capBounceMsgBody | capReportVersion | capShortDequeue | capStoreOutMsgQueueSize | + capMsgMetadata | capDeferMessages | capFullCollatedData; } private: @@ -84,6 +85,7 @@ class Collator final : public td::actor::Actor { adnl::AdnlNodeIdShort collator_node_id_ = adnl::AdnlNodeIdShort::zero(); bool skip_store_candidate_ = false; Ref optimistic_prev_block_; + std::shared_ptr collated_data_deduplicator_; int attempt_idx_; bool allow_repeat_collation_ = false; ton::BlockSeqno last_block_seqno{0}; @@ -128,6 +130,7 @@ class Collator final : public td::actor::Actor { int verbosity{3 * 0}; int verify{1}; bool full_collated_data_ = false; + bool merge_collated_data_enabled_ = false; ton::LogicalTime start_lt, max_lt; ton::UnixTime now_; ton::UnixTime prev_now_; @@ -225,6 +228,8 @@ class Collator final : public td::actor::Actor { std::map> block_state_proofs_; std::vector neighbor_proof_builders_; std::vector> collated_roots_; + td::BufferSlice collated_data_; + FileHash collated_data_hash_; struct AccountStorageDict { bool inited = false; @@ -424,6 +429,7 @@ class Collator final : public td::actor::Actor { void finalize_stats(); AccountStorageDict* current_tx_storage_dict_ = nullptr; + bool stop_cell_load_processing_ = false; void on_cell_loaded(const vm::LoadedCell& cell); void set_current_tx_storage_dict(const block::Account& account); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 14c2d9aed..f92fb6ec1 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -83,6 +83,7 @@ Collator::Collator(CollateParams params, td::actor::ActorId ma , collator_node_id_(params.collator_node_id) , skip_store_candidate_(params.skip_store_candidate) , optimistic_prev_block_(std::move(params.optimistic_prev_block)) + , collated_data_deduplicator_(std::move(params.collated_data_deduplicator)) , attempt_idx_(params.attempt_idx) , perf_timer_("collate", 0.1, [manager](double duration) { @@ -445,7 +446,8 @@ bool Collator::fatal_error(td::Status error) { .collator_node_id = collator_node_id_, .skip_store_candidate = skip_store_candidate_, .attempt_idx = attempt_idx_ + 1, - .optimistic_prev_block = optimistic_prev_block_}, + .optimistic_prev_block = optimistic_prev_block_, + .collated_data_deduplicator = collated_data_deduplicator_}, manager, td::Timestamp::in(10.0), std::move(cancellation_token_), std::move(main_promise)); } else { LOG(INFO) << "collation failed in " << perf_timer_.elapsed() << " s " << error; @@ -878,7 +880,9 @@ bool Collator::unpack_last_mc_state() { msg_metadata_enabled_ = config_->has_capability(ton::capMsgMetadata); deferring_messages_enabled_ = config_->has_capability(ton::capDeferMessages); full_collated_data_ = config_->has_capability(capFullCollatedData) || collator_opts_->force_full_collated_data; + merge_collated_data_enabled_ = config_->get_consensus_config().merge_collated_data && !is_masterchain(); LOG(DEBUG) << "full_collated_data is " << full_collated_data_; + LOG(DEBUG) << "merge_collated_data is " << merge_collated_data_enabled_; shard_conf_ = std::make_unique(*config_); prev_key_block_exists_ = config_->get_last_key_block(prev_key_block_, prev_key_block_lt_); if (prev_key_block_exists_) { @@ -2494,15 +2498,15 @@ bool Collator::do_collate() { if (!create_shard_state()) { return fatal_error("cannot create new ShardState"); } - // D. serialize Block + // D. create collated data + if (!create_collated_data()) { + return fatal_error("cannot create collated data for new Block candidate"); + } + // E. serialize Block LOG(DEBUG) << "serialize Block"; if (!create_block()) { return fatal_error("cannot create new Block"); } - // E. create collated data - if (!create_collated_data()) { - return fatal_error("cannot create collated data for new Block candidate"); - } // F. create a block candidate LOG(DEBUG) << "create a Block candidate"; if (!create_block_candidate()) { @@ -5986,23 +5990,24 @@ bool Collator::create_block_info(Ref& block_info) { bool mc = is_masterchain(); td::uint32 val_hash = is_hardfork_ ? 0 : validator_set_->get_validator_set_hash(); CatchainSeqno cc_seqno = is_hardfork_ ? 0 : validator_set_->get_catchain_seqno(); - return cb.store_long_bool(0x9bc7a987, 32) // block_info#9bc7a987 - && cb.store_long_bool(0, 32) // version:uint32 - && cb.store_bool_bool(!mc) // not_master:(## 1) - && cb.store_bool_bool(after_merge_) // after_merge:(## 1) - && cb.store_bool_bool(before_split_) // before_split:Bool - && cb.store_bool_bool(after_split_) // after_split:Bool - && cb.store_bool_bool(want_split_) // want_split:Bool - && cb.store_bool_bool(want_merge_) // want_merge:Bool - && cb.store_bool_bool(is_key_block_) // key_block:Bool - && cb.store_bool_bool(is_hardfork_) // vert_seqno_incr:(## 1) - && cb.store_long_bool((int)report_version_, 8) // flags:(## 8) - && cb.store_long_bool(new_block_seqno, 32) // seq_no:# - && cb.store_long_bool(vert_seqno_, 32) // vert_seq_no:# - && block::ShardId{shard_}.serialize(cb) // shard:ShardIdent - && cb.store_long_bool(now_, 32) // gen_utime:uint32 - && cb.store_long_bool(start_lt, 64) // start_lt:uint64 - && cb.store_long_bool(max_lt, 64) // end_lt:uint64 + bool store_collated_data_hash = config_->get_consensus_config().merge_collated_data; + return cb.store_long_bool(0x9bc7a987, 32) // block_info#9bc7a987 + && cb.store_long_bool(0, 32) // version:uint32 + && cb.store_bool_bool(!mc) // not_master:(## 1) + && cb.store_bool_bool(after_merge_) // after_merge:(## 1) + && cb.store_bool_bool(before_split_) // before_split:Bool + && cb.store_bool_bool(after_split_) // after_split:Bool + && cb.store_bool_bool(want_split_) // want_split:Bool + && cb.store_bool_bool(want_merge_) // want_merge:Bool + && cb.store_bool_bool(is_key_block_) // key_block:Bool + && cb.store_bool_bool(is_hardfork_) // vert_seqno_incr:(## 1) + && cb.store_long_bool((int)report_version_ + (store_collated_data_hash ? 2 : 0), 8) // flags:(## 8) + && cb.store_long_bool(new_block_seqno, 32) // seq_no:# + && cb.store_long_bool(vert_seqno_, 32) // vert_seq_no:# + && block::ShardId{shard_}.serialize(cb) // shard:ShardIdent + && cb.store_long_bool(now_, 32) // gen_utime:uint32 + && cb.store_long_bool(start_lt, 64) // start_lt:uint64 + && cb.store_long_bool(max_lt, 64) // end_lt:uint64 && cb.store_long_bool(val_hash, 32) // gen_validator_list_hash_short:uint32 && cb.store_long_bool(cc_seqno, 32) // gen_catchain_seqno:uint32 && cb.store_long_bool(min_ref_mc_seqno_, 32) // min_ref_mc_seqno:uint32 @@ -6015,6 +6020,8 @@ bool Collator::create_block_info(Ref& block_info) { && (!is_hardfork_ || // prev_vert_ref:vert_seqno_incr?.. (store_master_ref(cb2) // && cb.store_builder_ref_bool(std::move(cb2)))) // .. ^(BlkPrevInfo 0) + && + (!store_collated_data_hash || cb.store_bits_bool(collated_data_hash_)) // collated_data_hash:flags . 1?bits256 && cb.finalize_to(block_info); } @@ -6296,84 +6303,140 @@ bool Collator::create_collated_data() { // 1. store the set of used shard block descriptions if (!used_shard_block_descr_.empty()) { auto cell = collate_shard_block_descr_set(); - if (cell.is_null()) { - return true; - // return fatal_error("cannot collate the collection of used shard block descriptions"); + if (!cell.is_null()) { + collated_roots_.push_back(std::move(cell)); } - collated_roots_.push_back(std::move(cell)); - } - if (!full_collated_data_) { - return true; } - // 2. Proofs for hashes of states: previous states + neighbors - for (const auto& p : block_state_proofs_) { - collated_roots_.push_back(p.second); - } - // 3. Previous state proof (only shadchains) - std::map> proofs; - if (!is_masterchain()) { - if (!prepare_msg_queue_proof()) { - return fatal_error("cannot prepare message queue proof"); + if (full_collated_data_) { + // 2. Proofs for hashes of states: previous states + neighbors + for (const auto& p : block_state_proofs_) { + collated_roots_.push_back(p.second); } - - state_usage_tree_->set_use_mark_for_is_loaded(false); - Ref state_proof = vm::MerkleProof::generate(prev_state_root_, [&](const Ref& c) { - return !collated_data_stat.is_loaded(c->get_hash()); - }); - if (state_proof.is_null()) { - return fatal_error("cannot generate Merkle proof for previous state"); + // 3. Prepare msg queue proof (only shardchains) + if (!is_masterchain()) { + if (!prepare_msg_queue_proof()) { + return fatal_error("cannot prepare message queue proof"); + } } - if (after_merge_) { - bool special; - auto cs = vm::load_cell_slice_special(state_proof, special); - CHECK(cs.special_type() == vm::CellTraits::SpecialType::MerkleProof); - cs = vm::load_cell_slice(cs.prefetch_ref(0)); - CHECK(cs.size_refs() == 2); - CHECK(cs.size() == 32); - CHECK(cs.prefetch_ulong(32) == 0x5f327da5U); - proofs[cs.prefetch_ref(0)->get_hash(0).bits()] = vm::CellBuilder::create_merkle_proof(cs.prefetch_ref(0)); - proofs[cs.prefetch_ref(1)->get_hash(0).bits()] = vm::CellBuilder::create_merkle_proof(cs.prefetch_ref(1)); + + if (merge_collated_data_enabled_) { + // 4. Previous states proofs (only shardchains) + if (!is_masterchain()) { + for (auto& prev_state : prev_states) { + collated_roots_.push_back(vm::CellBuilder() + .store_long(block::gen::CollatedDataRootState::cons_tag[0], 32) + .store_bits(prev_state->root_cell()->get_hash().as_bitslice()) + .finalize_novm()); + } + } + stop_cell_load_processing_ = true; + + // 5. Proofs for message queues + for (vm::MerkleProofBuilder& mpb : neighbor_proof_builders_) { + collated_roots_.push_back(vm::CellBuilder() + .store_long(block::gen::CollatedDataRootState::cons_tag[0], 32) + .store_bits(mpb.original_root()->get_hash().as_bitslice()) + .finalize_novm()); + } + + // 6. Proofs for account storage dicts + for (auto& [hash, dict] : account_storage_dicts_) { + if (!dict.add_to_collated_data) { + continue; + } + // collated_data_root_storage_dict#b70eedd2 hash:bits256 = CollatedDataRootStorageDict; + collated_roots_.push_back(vm::CellBuilder() + .store_long(block::gen::CollatedDataRootStorageDict::cons_tag[0], 32) + .store_bits(hash.as_bitslice()) + .finalize_novm()); + } + // 7. Collated data proofs + collated_roots_.push_back( + vm::CellBuilder().store_long(block::gen::CollatedDataSeparator::cons_tag[0], 32).finalize_novm()); + std::vector> proofs = collated_data_stat.build_collated_data(); + collated_roots_.insert(collated_roots_.end(), proofs.begin(), proofs.end()); } else { - proofs[prev_state_root_->get_hash().bits()] = std::move(state_proof); - } - } - // 4. Proofs for message queues - for (vm::MerkleProofBuilder &mpb : neighbor_proof_builders_) { - Ref proof = vm::MerkleProof::generate(mpb.original_root(), [&](const Ref& c) { - return !collated_data_stat.is_loaded(c->get_hash()); - }); - if (proof.is_null()) { - return fatal_error("cannot generate Merkle proof for neighbor"); - } - auto it = proofs.emplace(mpb.root()->get_hash().bits(), proof); - if (!it.second) { - it.first->second = vm::MerkleProof::combine(it.first->second, std::move(proof)); - if (it.first->second.is_null()) { - return fatal_error("cannot combine merkle proofs"); + std::map> proofs; + // 4. Previous states proofs (only shardchains) + if (!is_masterchain()) { + state_usage_tree_->set_use_mark_for_is_loaded(false); + Ref state_proof = vm::MerkleProof::generate( + prev_state_root_, [&](const Ref& c) { return !collated_data_stat.is_loaded(c->get_hash()); }); + if (state_proof.is_null()) { + return fatal_error("cannot generate Merkle proof for previous state"); + } + if (after_merge_) { + bool special; + auto cs = vm::load_cell_slice_special(state_proof, special); + CHECK(cs.special_type() == vm::CellTraits::SpecialType::MerkleProof); + cs = vm::load_cell_slice(cs.prefetch_ref(0)); + CHECK(cs.size_refs() == 2); + CHECK(cs.size() == 32); + CHECK(cs.prefetch_ulong(32) == 0x5f327da5U); + proofs[cs.prefetch_ref(0)->get_hash(0).bits()] = vm::CellBuilder::create_merkle_proof(cs.prefetch_ref(0)); + proofs[cs.prefetch_ref(1)->get_hash(0).bits()] = vm::CellBuilder::create_merkle_proof(cs.prefetch_ref(1)); + } else { + proofs[prev_state_root_->get_hash().bits()] = std::move(state_proof); + } + } + // 5. Proofs for message queues + for (vm::MerkleProofBuilder& mpb : neighbor_proof_builders_) { + Ref proof = vm::MerkleProof::generate( + mpb.original_root(), [&](const Ref& c) { return !collated_data_stat.is_loaded(c->get_hash()); }); + if (proof.is_null()) { + return fatal_error("cannot generate Merkle proof for neighbor"); + } + auto it = proofs.emplace(mpb.root()->get_hash().bits(), proof); + if (!it.second) { + it.first->second = vm::MerkleProof::combine(it.first->second, std::move(proof)); + if (it.first->second.is_null()) { + return fatal_error("cannot combine merkle proofs"); + } + } + } + + for (auto& p : proofs) { + collated_roots_.push_back(std::move(p.second)); } - } - } - for (auto& p : proofs) { - collated_roots_.push_back(std::move(p.second)); + // 6. Proofs for account storage dicts + for (auto& [_, dict] : account_storage_dicts_) { + if (!dict.add_to_collated_data) { + continue; + } + Ref proof = vm::MerkleProof::generate(dict.mpb.original_root(), [&](const Ref& c) { + return !collated_data_stat.is_loaded(c->get_hash()); + }); + if (proof.is_null()) { + return fatal_error("cannot generate Merkle proof for neighbor"); + } + // account_storage_dict_proof#37c1e3fc proof:^Cell = AccountStorageDictProof; + collated_roots_.push_back(vm::CellBuilder() + .store_long(block::gen::AccountStorageDictProof::cons_tag[0], 32) + .store_ref(proof) + .finalize_novm()); + } + } } - // 5. Proofs for account storage dicts - for (auto& [_, dict] : account_storage_dicts_) { - if (!dict.add_to_collated_data) { - continue; + // 8. serialize collated data + if (collated_roots_.empty()) { + collated_data_ = td::BufferSlice{0}; + } else { + vm::BagOfCells boc_collated; + boc_collated.set_roots(collated_roots_); + td::Status S = boc_collated.import_cells(); + if (S.is_error()) { + return fatal_error(S.move_as_error()); } - Ref proof = vm::MerkleProof::generate( - dict.mpb.original_root(), [&](const Ref& c) { return !collated_data_stat.is_loaded(c->get_hash()); }); - if (proof.is_null()) { - return fatal_error("cannot generate Merkle proof for neighbor"); + auto cdata_res = boc_collated.serialize_to_slice(2); + if (cdata_res.is_error()) { + LOG(ERROR) << "cannot serialize collated data"; + return fatal_error(cdata_res.move_as_error()); } - // account_storage_dict_proof#37c1e3fc proof:^Cell = AccountStorageDictProof; - collated_roots_.push_back(vm::CellBuilder() - .store_long(block::gen::AccountStorageDictProof::cons_tag[0], 32) - .store_ref(proof) - .finalize_novm()); + collated_data_ = cdata_res.move_as_ok(); } + collated_data_hash_ = block::compute_file_hash(collated_data_.as_slice()); return true; } @@ -6409,39 +6472,20 @@ bool Collator::create_block_candidate() { return fatal_error(blk_res.move_as_error()); } auto blk_slice = blk_res.move_as_ok(); - // 2. serialize collated data - td::BufferSlice cdata_slice; - if (collated_roots_.empty()) { - cdata_slice = td::BufferSlice{0}; - } else { - vm::BagOfCells boc_collated; - boc_collated.set_roots(collated_roots_); - res = boc_collated.import_cells(); - if (res.is_error()) { - return fatal_error(res.move_as_error()); - } - int cdata_serialize_mode = consensus_config.proto_version >= 5 ? 2 : 31; - auto cdata_res = boc_collated.serialize_to_slice(cdata_serialize_mode); - if (cdata_res.is_error()) { - LOG(ERROR) << "cannot serialize collated data"; - return fatal_error(cdata_res.move_as_error()); - } - cdata_slice = cdata_res.move_as_ok(); - } + // 2. collated data is already serialized in create_collated_data LOG(INFO) << "serialized block size " << blk_slice.size() << " bytes (preliminary estimate was " << block_size_estimate_ << ")"; auto st = block_limit_status_->st_stat.get_total_stat(); LOG(INFO) << "size regression stats: " << blk_slice.size() << " " << st.cells << " " << st.bits << " " << st.internal_refs << " " << st.external_refs << " " << block_limit_status_->accounts << " " << block_limit_status_->transactions; - LOG(INFO) << "serialized collated data size " << cdata_slice.size() << " bytes (preliminary estimate was " + LOG(INFO) << "serialized collated data size " << collated_data_.size() << " bytes (preliminary estimate was " << block_limit_status_->collated_data_size_estimate << ")"; auto new_block_id_ext = ton::BlockIdExt{ton::BlockId{shard_, new_block_seqno}, new_block->get_hash().bits(), block::compute_file_hash(blk_slice.as_slice())}; // 3. create a BlockCandidate - block_candidate = - std::make_unique(created_by_, new_block_id_ext, block::compute_file_hash(cdata_slice.as_slice()), - blk_slice.clone(), cdata_slice.clone()); + block_candidate = std::make_unique(created_by_, new_block_id_ext, collated_data_hash_, + blk_slice.clone(), collated_data_.clone()); const bool need_out_msg_queue_broadcasts = !is_masterchain(); if (need_out_msg_queue_broadcasts) { // we can't generate two proofs at the same time for the same root (it is not currently supported by cells) @@ -6497,9 +6541,7 @@ bool Collator::create_block_candidate() { } else { LOG(INFO) << "saving new BlockCandidate"; auto token = perf_log_.start_action("set_block_candidate"); - td::actor::send_closure_later(manager, &ValidatorManager::set_block_candidate, block_candidate->id, - block_candidate->clone(), validator_set_->get_catchain_seqno(), - validator_set_->get_validator_set_hash(), + td::actor::send_closure_later(manager, &ValidatorManager::set_block_candidate, block_candidate->clone(), [self = get_self(), token = std::move(token)](td::Result saved) mutable { LOG(DEBUG) << "got answer to set_block_candidate"; td::actor::send_closure_later(std::move(self), &Collator::return_block_candidate, @@ -6724,6 +6766,14 @@ void Collator::finalize_stats() { * @param loaded_cell Loaded cell */ void Collator::on_cell_loaded(const vm::LoadedCell& loaded_cell) { + if (stop_cell_load_processing_) { + return; + } + if (merge_collated_data_enabled_ && collated_data_deduplicator_ && + collated_data_deduplicator_->cell_exists(loaded_cell.data_cell->get_hash(loaded_cell.virt.get_level()), + new_block_seqno)) { + return; + } auto context = block::StorageStatCalculationContext::get(); vm::ProofStorageStat* stat = (context && context->calculating_storage_stat() && current_tx_storage_dict_ ? ¤t_tx_storage_dict_->proof_stat diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index a1ab5026f..c6fb76131 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -82,6 +82,8 @@ ValidateQuery::ValidateQuery(BlockCandidate candidate, ValidateParams params, , shard_pfx_(shard_.shard) , shard_pfx_len_(ton::shard_prefix_length(shard_)) , optimistic_prev_block_(std::move(params.optimistic_prev_block)) + , collated_data_merger_(std::move(params.collated_data_merger)) + , merge_collated_data_enabled_(!collated_data_merger_.empty()) , perf_timer_("validateblock", 0.1, [manager](double duration) { send_closure(manager, &ValidatorManager::add_perf_timer_stat, "validateblock", duration); }) { @@ -361,7 +363,18 @@ void ValidateQuery::start_up() { } LOG(WARNING) << "Optimistic prev block id = " << optimistic_prev_block_->block_id().to_str(); } - // 2. learn latest masterchain state and block id + // 2. unpack block candidate + if (!unpack_block_candidate()) { + reject_query("error unpacking block candidate"); + return; + } + if (pending == 0) { + start_up_cont(); + } +} + +void ValidateQuery::start_up_cont() { + // 3. learn latest masterchain state and block id LOG(DEBUG) << "sending get_top_masterchain_state_block() to Manager"; ++pending; td::actor::send_closure_later(manager, &ValidatorManager::get_top_masterchain_state_block, @@ -372,11 +385,6 @@ void ValidateQuery::start_up() { &ValidateQuery::after_get_latest_mc_state, std::move(res), std::move(token)); }); - // 3. unpack block candidate (while necessary data is being loaded) - if (!unpack_block_candidate()) { - reject_query("error unpacking block candidate"); - return; - } // 4. load state(s) corresponding to previous block(s) (not full-collated-data or masterchain) prev_states.resize(prev_blocks.size()); if (is_masterchain() || !full_collated_data_) { @@ -630,6 +638,11 @@ bool ValidateQuery::init_parse() { want_split_ = info.want_split; is_key_block_ = info.key_block; prev_key_seqno_ = info.prev_key_block_seqno; + if (info.flags & 2) { + CHECK(info.collated_data_hash->prefetch_bits_to(stored_collated_data_hash_.value_force())); + } else { + stored_collated_data_hash_ = {}; + } CHECK(after_split_ == info.after_split); if (is_key_block_) { LOG(INFO) << "validating key block " << id_.to_str(); @@ -704,10 +717,12 @@ bool ValidateQuery::init_parse() { * * @param croot The root cell containing the collated data. * @param idx The index of the root. + * @param end Set to true if the datum is collated_data_separator * * @returns True if the extraction is successful, false otherwise. */ -bool ValidateQuery::extract_collated_data_from(Ref croot, int idx) { +bool ValidateQuery::extract_collated_data_from(Ref croot, int idx, bool& end) { + end = false; bool is_special = false; auto cs = vm::load_cell_slice_special(croot, is_special); if (!cs.is_valid()) { @@ -741,6 +756,11 @@ bool ValidateQuery::extract_collated_data_from(Ref croot, int idx) { top_shard_descr_dict_ = std::make_unique(cs.prefetch_ref(), 96); return true; } + if (block::gen::t_CollatedDataSeparator.has_valid_tag(cs)) { + LOG(DEBUG) << "collated datum # " << idx << " is a CollatedDataSeparator"; + end = true; + return true; + } if (block::gen::t_AccountStorageDictProof.has_valid_tag(cs)) { if (!block::gen::t_AccountStorageDictProof.validate_upto(10000, cs)) { return reject_query("invalid AccountStorageDictProof"); @@ -759,6 +779,30 @@ bool ValidateQuery::extract_collated_data_from(Ref croot, int idx) { full_collated_data_ = true; return true; } + if (block::gen::t_CollatedDataRootState.has_valid_tag(cs)) { + if (!block::gen::t_CollatedDataRootState.validate_upto(10000, cs)) { + return reject_query("invalid CollatedDataRootState"); + } + cs.advance(32); + vm::CellHash hash; + cs.fetch_bytes(hash.as_slice()); + LOG(DEBUG) << "collated datum # " << idx << " is an CollatedDataRootState with hash " << hash.to_hex(); + collated_data_root_state_hashes_.push_back(hash); + full_collated_data_ = true; + return true; + } + if (block::gen::t_CollatedDataRootStorageDict.has_valid_tag(cs)) { + if (!block::gen::t_CollatedDataRootStorageDict.validate_upto(10000, cs)) { + return reject_query("invalid CollatedDataRootStorageDict"); + } + cs.advance(32); + vm::CellHash hash; + cs.fetch_bytes(hash.as_slice()); + LOG(DEBUG) << "collated datum # " << idx << " is an CollatedDataRootStorageDict with hash " << hash.to_hex(); + collated_data_root_dict_hashes_.push_back(hash); + full_collated_data_ = true; + return true; + } LOG(WARNING) << "collated datum # " << idx << " has unknown type (magic " << cs.prefetch_ulong(32) << "), ignoring"; return true; } @@ -774,9 +818,13 @@ bool ValidateQuery::extract_collated_data() { ++i; auto guard = error_ctx_add_guard(PSTRING() << "collated datum #" << i); try { - if (!extract_collated_data_from(croot, i)) { + bool end = false; + if (!extract_collated_data_from(croot, i, end)) { return reject_query("cannot unpack collated datum"); } + if (end) { + break; + } } catch (vm::VmError& err) { return reject_query(PSTRING() << "vm error " << err.get_msg()); } catch (vm::VmVirtError& err) { @@ -786,9 +834,60 @@ bool ValidateQuery::extract_collated_data() { if (full_collated_data_) { LOG(INFO) << "full_collated_data = true"; } + if (merge_collated_data_enabled_) { + LOG(INFO) << "merge_collated_data = true"; + LOG(DEBUG) << "sending add_block_candidate() to CollatedDataMerger"; + td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate, id_, block_root_, + collated_roots_); + std::vector hashes = collated_data_root_state_hashes_; + hashes.insert(hashes.end(), collated_data_root_dict_hashes_.begin(), collated_data_root_dict_hashes_.end()); + if (!hashes.empty()) { + LOG(DEBUG) << "sending get_cells to CollatedDataMerger"; + ++pending; + td::actor::send_closure_later( + collated_data_merger_, &CollatedDataMerger::get_cells, std::move(hashes), + [self = get_self(), token = perf_log_.start_action("CollatedDataMerger::get_cells")]( + td::Result>> res) mutable { + LOG(DEBUG) << "got answer to CollatedDataMerger::get_cells"; + td::actor::send_closure_later(std::move(self), &ValidateQuery::process_merged_collated_roots, + std::move(res), std::move(token)); + }); + } + } return true; } +/** + * Process roots returned from CollatedDataMerger + * + * @param res List of returned roots + */ +void ValidateQuery::process_merged_collated_roots(td::Result>> res, + td::PerfLogAction token) { + token.finish(res); + --pending; + if (res.is_error()) { + fatal_error(res.move_as_error()); + return; + } + auto roots = res.move_as_ok(); + for (const vm::CellHash& hash : collated_data_root_state_hashes_) { + auto it = roots.find(hash); + if (it != roots.end()) { + virt_roots_[td::Bits256{hash.bits()}] = it->second; + } + } + for (const vm::CellHash& hash : collated_data_root_dict_hashes_) { + auto it = roots.find(hash); + if (it != roots.end()) { + virt_account_storage_dicts_[td::Bits256{hash.bits()}] = it->second; + } + } + if (pending == 0) { + start_up_cont(); + } +} + /** * Send get_top_masterchain_state_block to manager, call after_get_latest_mc_state afterwards */ @@ -1139,8 +1238,8 @@ bool ValidateQuery::fetch_config_params() { if (compute_phase_cfg_.global_version >= 4) { auto prev_blocks_info = config_->get_prev_blocks_info(); if (prev_blocks_info.is_error()) { - return fatal_error(prev_blocks_info.move_as_error_prefix( - "cannot fetch prev blocks info from masterchain configuration: ")); + return fatal_error( + prev_blocks_info.move_as_error_prefix("cannot fetch prev blocks info from masterchain configuration: ")); } compute_phase_cfg_.prev_blocks_info = prev_blocks_info.move_as_ok(); } @@ -2858,7 +2957,26 @@ bool ValidateQuery::unpack_block_data() { if (!account_blocks_dict_->validate_all()) { return reject_query("ShardAccountBlocks dictionary is invalid"); } - return unpack_precheck_value_flow(std::move(blk.value_flow)); + if (!unpack_precheck_value_flow(std::move(blk.value_flow))) { + return reject_query("failed to precheck value flow"); + } + + if (!is_fake_) { + if (config_->get_consensus_config().merge_collated_data) { + if (!stored_collated_data_hash_) { + return reject_query("no collated data hash in BlockInfo"); + } + if (stored_collated_data_hash_.value() != block_candidate.collated_file_hash) { + return reject_query(PSTRING() << "collated data hash is " << block_candidate.collated_file_hash.to_hex() + << ", but BlockInfo has " << stored_collated_data_hash_.value().to_hex()); + } + } else { + if (stored_collated_data_hash_) { + return reject_query("unexpected collated data hash in BlockInfo"); + } + } + } + return true; } /** @@ -3062,14 +3180,14 @@ bool ValidateQuery::precheck_one_account_update(td::ConstBitPtr acc_id, Ref= 3 * 0) { FLOG(INFO) { sb << "state of account " << workchain() << ":" << acc_id.to_hex(256) - << " in the old shardchain state:" << "\n"; + << " in the old shardchain state:" << "\n"; if (old_value.not_null()) { block::gen::t_ShardAccount.print(sb, old_value); } else { sb << "" << "\n"; } sb << "state of account " << workchain() << ":" << acc_id.to_hex(256) - << " in the new shardchain state:" << "\n"; + << " in the new shardchain state:" << "\n"; if (new_value.not_null()) { block::gen::t_ShardAccount.print(sb, new_value); } else { @@ -3452,7 +3570,8 @@ bool ValidateQuery::precheck_one_message_queue_update(td::ConstBitPtr out_msg_id } // mode for msg_export_{ext,new,imm,tr,deq_imm,???,deq/deq_short,tr_req,new_defer,deferred_tr} static const int tag_mode[10] = {0, 2, 0, 2, 1, 0, 1, 3, 0, 2}; - static const char* tag_str[10] = {"ext", "new", "imm", "tr", "deq_imm", "???", "deq", "tr_req", "new_defer", "deferred_tr"}; + static const char* tag_str[10] = {"ext", "new", "imm", "tr", "deq_imm", + "???", "deq", "tr_req", "new_defer", "deferred_tr"}; if (tag < 0 || tag >= 10 || !(tag_mode[tag] & mode)) { return reject_query(PSTRING() << "OutMsgDescr corresponding to " << m_str[mode] << "queued message with key " << out_msg_id.to_hex(352) << " has invalid tag " << tag << "(" << tag_str[tag & 7] @@ -4077,9 +4196,8 @@ bool ValidateQuery::check_in_msg(td::ConstBitPtr key, Ref in_msg) dest_prefix.to_str() + "... yet"); } if (from_dispatch_queue && next_prefix != cur_prefix) { - return reject_query( - "next hop address "s + next_prefix.to_str() + "... of deferred internal message with hash " + key.to_hex(256) + - " must coincide with its current prefix "s + cur_prefix.to_str() + "..."s); + return reject_query("next hop address "s + next_prefix.to_str() + "... of deferred internal message with hash " + + key.to_hex(256) + " must coincide with its current prefix "s + cur_prefix.to_str() + "..."s); } // if a message is processed by a transaction, it must have destination inside the current shard if (transaction.not_null() && !ton::shard_contains(shard_, dest_prefix)) { @@ -4275,8 +4393,8 @@ bool ValidateQuery::check_in_msg(td::ConstBitPtr key, Ref in_msg) } if (cur_prefix != next_prefix && tag == block::gen::InMsg::msg_import_deferred_tr) { return reject_query("internal message from DispatchQueue with hash "s + key.to_hex(256) + - " is a msg_import_deferred_tr$00101, but its current address " + - cur_prefix.to_str() + " is not equal to next address"); + " is a msg_import_deferred_tr$00101, but its current address " + cur_prefix.to_str() + + " is not equal to next address"); } CHECK(transaction.is_null()); auto out_msg_cs = out_msg_dict_->lookup(key, 256); @@ -4657,8 +4775,9 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms } // check that next hop is nearer to the destination than the current address if (count_matching_bits(dest_prefix, next_prefix) < count_matching_bits(dest_prefix, cur_prefix)) { - return reject_query("next hop address "s + next_prefix.to_str() + "... of outbound internal message with hash " + - key.to_hex(256) + " is further from its destination " + dest_prefix.to_str() + + return reject_query("next hop address "s + next_prefix.to_str() + + "... of outbound internal message with hash " + key.to_hex(256) + + " is further from its destination " + dest_prefix.to_str() + "... than its current address " + cur_prefix.to_str() + "..."); } // current address must belong to this shard (otherwise we should never had exported this message) @@ -5525,7 +5644,7 @@ std::unique_ptr ValidateQuery::unpack_account(td::ConstBitPtr ad * * @returns IHR fee */ -static td::RefInt256 get_ihr_fee(const block::gen::CommonMsgInfo::Record_int_msg_info &info, int global_version) { +static td::RefInt256 get_ihr_fee(const block::gen::CommonMsgInfo::Record_int_msg_info& info, int global_version) { // Legacy: extra_flags was previously ihr_fee return global_version >= 12 ? td::zero_refint() : block::tlb::t_Grams.as_integer(std::move(info.extra_flags)); } @@ -7153,13 +7272,12 @@ bool ValidateQuery::postcheck_value_flow() { << import_fees_ << ", the total transaction fees are " << transaction_fees_.to_str() << ", creation fee for this block is " << value_flow_.created.to_str() << ", the total imported fees from shards are " << value_flow_.fees_imported.to_str() - << " and the burned fees are " << fees_burned_.to_str() - << " with a total of " << expected_fees.to_str()); + << " and the burned fees are " << fees_burned_.to_str() << " with a total of " + << expected_fees.to_str()); } if (total_burned_ != value_flow_.burned) { return reject_query(PSTRING() << "invalid burned in value flow: " << id_.to_str() << " declared " - << value_flow_.burned.to_str() << ", correct value is " - << total_burned_.to_str()); + << value_flow_.burned.to_str() << ", correct value is " << total_burned_.to_str()); } return true; } @@ -7323,8 +7441,7 @@ bool ValidateQuery::save_candidate() { } }); - td::actor::send_closure(manager, &ValidatorManager::set_block_candidate, id_, block_candidate.clone(), - validator_set_->get_catchain_seqno(), validator_set_->get_validator_set_hash(), std::move(P)); + td::actor::send_closure(manager, &ValidatorManager::set_block_candidate, block_candidate.clone(), std::move(P)); if (!storage_stat_cache_update_.empty()) { td::actor::send_closure(manager, &ValidatorManager::update_storage_stat_cache, std::move(storage_stat_cache_update_)); diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index f338f975c..517e90c2f 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -18,7 +18,7 @@ */ #pragma once - +#include "collated-data-merger.h" #include "block-parse.h" #include "fabric.h" #include "interfaces/validator-manager.h" @@ -150,6 +150,7 @@ class ValidateQuery : public td::actor::Actor { bool debug_checks_{false}; bool outq_cleanup_partial_{false}; BlockSeqno prev_key_seqno_{~0u}; + td::optional stored_collated_data_hash_; int stage_{0}; td::BitArray<64> shard_pfx_; int shard_pfx_len_; @@ -174,6 +175,11 @@ class ValidateQuery : public td::actor::Actor { std::unique_ptr top_shard_descr_dict_; std::map> virt_account_storage_dicts_; + td::actor::ActorId collated_data_merger_; + std::vector collated_data_root_state_hashes_; + std::vector collated_data_root_dict_hashes_; + bool merge_collated_data_enabled_{false}; + Ref shard_hashes_; // from McBlockExtra Ref blk_config_params_; // from McBlockExtra Ref prev_signatures_; // from McBlockExtra (UNCHECKED) @@ -274,6 +280,7 @@ class ValidateQuery : public td::actor::Actor { bool soft_reject_query(std::string error, td::BufferSlice reason = {}); void alarm() override; void start_up() override; + void start_up_cont(); void load_prev_states(); bool process_optimistic_prev_block(); @@ -326,9 +333,12 @@ class ValidateQuery : public td::actor::Actor { bool check_prev_block_exact(const BlockIdExt& listed, const BlockIdExt& prev); bool check_this_shard_mc_info(); bool init_parse(); + bool unpack_block_candidate(); - bool extract_collated_data_from(Ref croot, int idx); + bool extract_collated_data_from(Ref croot, int idx, bool& end); bool extract_collated_data(); + void process_merged_collated_roots(td::Result>> res, td::PerfLogAction token); + bool try_validate(); bool compute_prev_state(); bool compute_next_state(); diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index 90e524756..823866df5 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -266,10 +266,10 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void wait_block_signatures_short(BlockIdExt id, td::Timestamp timeout, td::Promise> promise) = 0; - virtual void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::Promise promise) = 0; + virtual void set_block_candidate(BlockCandidate candidate, td::Promise promise) = 0; virtual void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) = 0; + td::BufferSlice data, td::optional collated_data, + int mode) = 0; virtual void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) = 0; @@ -316,6 +316,10 @@ class ValidatorManager : public ValidatorManagerInterface { td::Promise>> promise) = 0; virtual void send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, std::string tmp_dir, td::Timestamp timeout, td::Promise promise) = 0; + virtual void send_get_block_candidate_request(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) { + promise.set_error(td::Status::Error("not supported")); + } virtual void get_block_proof_link_from_import(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index eb06dd304..a893b5946 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -606,6 +606,10 @@ void ValidatorManagerImpl::get_candidate_data_by_block_id_from_db(BlockIdExt id, promise.wrap([](BlockCandidate &&b) { return std::move(b.data); })); } +void ValidatorManagerImpl::get_block_candidate_by_block_id_from_db(BlockIdExt id, td::Promise promise) { + td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, std::move(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)); } @@ -809,15 +813,15 @@ void ValidatorManagerImpl::set_next_block(BlockIdExt block_id, BlockIdExt next, get_block_handle(block_id, true, std::move(P)); } -void ValidatorManagerImpl::set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::Promise promise) { +void ValidatorManagerImpl::set_block_candidate(BlockCandidate candidate, td::Promise promise) { td::actor::send_closure(db_, &Db::store_block_candidate, std::move(candidate), std::move(promise)); } void ValidatorManagerImpl::send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::BufferSlice data, - int mode) { - callback_->send_block_candidate(id, cc_seqno, validator_set_hash, std::move(data), mode); + td::optional collated_data, int mode) { + callback_->send_block_candidate_broadcast(id, cc_seqno, validator_set_hash, std::move(data), std::move(collated_data), + mode); } void ValidatorManagerImpl::write_handle(BlockHandle handle, td::Promise promise) { diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index 1c4d00d72..e6f33e1cb 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -136,8 +136,6 @@ class ValidatorManagerImpl : public ValidatorManager { void new_ihr_message(td::BufferSlice data) override; void new_shard_block_description_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) override; - void new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data) override { - } void add_ext_server_id(adnl::AdnlNodeIdShort id) override { UNREACHABLE(); @@ -195,10 +193,10 @@ class ValidatorManagerImpl : public ValidatorManager { void wait_block_signatures_short(BlockIdExt id, td::Timestamp timeout, td::Promise> promise) override; - void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::Promise promise) override; + void set_block_candidate(BlockCandidate candidate, td::Promise promise) override; void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) override; + td::BufferSlice data, td::optional collated_data, + int mode) override; void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; @@ -228,6 +226,7 @@ class ValidatorManagerImpl : public ValidatorManager { void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) override; + void get_block_candidate_by_block_id_from_db(BlockIdExt id, 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(ConstBlockHandle handle, td::Promise> promise) override; diff --git a/validator/manager-hardfork.cpp b/validator/manager-hardfork.cpp index 717ab3aa5..78aa0369f 100644 --- a/validator/manager-hardfork.cpp +++ b/validator/manager-hardfork.cpp @@ -423,6 +423,10 @@ void ValidatorManagerImpl::get_candidate_data_by_block_id_from_db(BlockIdExt id, promise.wrap([](BlockCandidate &&b) { return std::move(b.data); })); } +void ValidatorManagerImpl::get_block_candidate_by_block_id_from_db(BlockIdExt id, td::Promise promise) { + td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, std::move(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)); } diff --git a/validator/manager-hardfork.hpp b/validator/manager-hardfork.hpp index 37b5d6dd3..aa0d6e537 100644 --- a/validator/manager-hardfork.hpp +++ b/validator/manager-hardfork.hpp @@ -159,9 +159,6 @@ class ValidatorManagerImpl : public ValidatorManager { td::BufferSlice data) override { UNREACHABLE(); } - void new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data) override { - UNREACHABLE(); - } void add_ext_server_id(adnl::AdnlNodeIdShort id) override { UNREACHABLE(); @@ -242,13 +239,14 @@ class ValidatorManagerImpl : public ValidatorManager { void wait_block_signatures_short(BlockIdExt id, td::Timestamp timeout, td::Promise> promise) override; - void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::Promise promise) override { + void set_block_candidate(BlockCandidate candidate, td::Promise promise) override { promise.set_value(td::Unit()); } void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) override { - callback_->send_block_candidate(id, cc_seqno, validator_set_hash, std::move(data), mode); + td::BufferSlice data, td::optional collated_data, + int mode) override { + callback_->send_block_candidate_broadcast(id, cc_seqno, validator_set_hash, std::move(data), + std::move(collated_data), mode); } void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, @@ -283,6 +281,7 @@ class ValidatorManagerImpl : public ValidatorManager { void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) override; + void get_block_candidate_by_block_id_from_db(BlockIdExt id, 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(ConstBlockHandle handle, td::Promise> promise) override; diff --git a/validator/manager.cpp b/validator/manager.cpp index a5315788d..d5edd644b 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -25,6 +25,8 @@ #include "downloaders/wait-block-data.hpp" #include "fabric.h" #include "manager.h" + +#include "block-auto.h" #include "validate-broadcast.hpp" #include "ton/ton-tl.hpp" #include "ton/ton-io.hpp" @@ -526,16 +528,17 @@ void ValidatorManagerImpl::new_shard_block_description_broadcast(BlockIdExt bloc actor_id(this), td::Timestamp::in(2.0), std::move(P)); } -void ValidatorManagerImpl::new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data) { +void ValidatorManagerImpl::new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data, + td::optional collated_data) { if (!last_masterchain_block_handle_ || !started_) { - VLOG(VALIDATOR_DEBUG) << "dropping top shard block broadcast: not inited"; + VLOG(VALIDATOR_DEBUG) << "dropping block candidate broadcast: not inited"; return; } if (!need_monitor(block_id.shard_full())) { VLOG(VALIDATOR_DEBUG) << "dropping block candidate broadcast: not monitoring shard"; return; } - add_cached_block_data(block_id, std::move(data)); + add_cached_block_data(block_id, std::move(data), std::move(collated_data), true); } void ValidatorManagerImpl::add_shard_block_description(td::Ref desc) { @@ -658,41 +661,80 @@ void ValidatorManagerImpl::set_shard_block_description_ready(td::Ref collated_data, bool is_broadcast) { if (block_id.is_masterchain()) { return; } - td::BufferSlice& block_data = cached_block_data_.get(block_id); - if (!block_data.empty()) { - return; - } - block_data = std::move(data); - { - auto it = wait_block_data_.find(block_id); - if (it != wait_block_data_.end()) { - auto r_block = create_block(ReceivedBlock{block_id, block_data.clone()}); - if (r_block.is_ok()) { - td::actor::send_closure(it->second.actor_, &WaitBlockData::loaded_block_data, r_block.move_as_ok()); - } else { - LOG(WARNING) << "Failed to parse cached block " << block_id.to_str() << " : " << r_block.move_as_error(); + auto &entry = cached_block_data_.get(block_id); + if (entry.data.empty()) { + // sha256 hash of data is equal to block_id.file_hash (this is checked elsewhere) + auto r_block = create_block(block_id, data.clone()); + if (r_block.is_error()) { + VLOG(VALIDATOR_WARNING) << "dropping invalid block candidate broadcast: " << r_block.error(); + cached_block_data_.remove(block_id); + return; + } + auto block = r_block.move_as_ok(); + block::gen::Block::Record rec; + block::gen::BlockInfo::Record info; + block::gen::BlockExtra::Record extra; + if (!block::gen::unpack_cell(block->root_cell(), rec) || !block::gen::unpack_cell(rec.info, info) || + !block::gen::unpack_cell(rec.extra, extra)) { + VLOG(VALIDATOR_WARNING) << "dropping invalid block candidate broadcast: cannot unpack block"; + cached_block_data_.remove(block_id); + return; + } + entry.data = std::move(data); + entry.creator = Ed25519_PublicKey{extra.created_by}; + if (info.flags & 2) { + info.collated_data_hash->prefetch_bits_to(entry.collated_data_hash.value_force()); + } + entry.cc_seqno = info.gen_catchain_seqno; + + { + auto it = wait_block_data_.find(block_id); + if (it != wait_block_data_.end()) { + td::actor::send_closure(it->second.actor_, &WaitBlockData::loaded_block_data, block); } } - } - { - auto it = wait_state_.find(block_id); - if (it != wait_state_.end()) { - // Proof link is not ready at this point, but this will force WaitBlockState to redo send_get_proof_link_request - td::actor::send_closure(it->second.actor_, &WaitBlockState::after_get_proof_link); + { + auto it = wait_state_.find(block_id); + if (it != wait_state_.end()) { + // Proof link is not ready at this point, but this will force WaitBlockState to redo send_get_proof_link_request + td::actor::send_closure(it->second.actor_, &WaitBlockState::after_get_proof_link); + } + } + + if (cached_checked_shard_block_descriptions_.contains(block_id)) { + wait_block_data_short(block_id, 0, td::Timestamp::in(60.0), [id = block_id](td::Result> R) { + if (R.is_error()) { + LOG(WARNING) << "Failed to store block data from cache for checked shard block description " << id.to_str() + << " : " << R.move_as_error(); + } + }); } } - if (cached_checked_shard_block_descriptions_.contains(block_id)) { - wait_block_data_short(block_id, 0, td::Timestamp::in(60.0), [id = block_id](td::Result> R) { - if (R.is_error()) { - LOG(WARNING) << "Failed to store block data from cache for checked shard block description " << id.to_str() - << " : " << R.move_as_error(); + if (!entry.collated_data && collated_data) { + FileHash collated_data_hash = td::sha256_bits256(collated_data.value()); + if (entry.collated_data_hash && collated_data_hash != entry.collated_data_hash.value()) { + VLOG(VALIDATOR_WARNING) << "invalid block candidate broadcast: collated data hash mismatch"; + return; + } + entry.collated_data_hash = collated_data_hash; + entry.collated_data = std::move(collated_data); + + if (is_broadcast) { + for (auto &[_, collator] : collator_nodes_) { + if (collator.can_collate_shard(block_id.shard_full())) { + td::actor::send_closure( + collator.actor, &CollatorNode::on_block_candidate_broadcast, + BlockCandidate(Ed25519_PublicKey(entry.creator), block_id, entry.collated_data_hash.value(), + entry.data.clone(), entry.collated_data.value().clone()), entry.cc_seqno); + } } - }); + } } } @@ -1246,13 +1288,22 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId void ValidatorManagerImpl::get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) { if (auto cached = cached_block_data_.get_if_exists(id, false)) { - promise.set_result(cached->clone()); + promise.set_result(cached->data.clone()); return; } td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, promise.wrap([](BlockCandidate &&b) { return std::move(b.data); })); } +void ValidatorManagerImpl::get_block_candidate_by_block_id_from_db(BlockIdExt id, td::Promise promise) { + if (auto cached = cached_block_data_.get_if_exists(id, false); cached && cached->collated_data) { + promise.set_result(BlockCandidate(cached->creator, id, cached->collated_data_hash.value(), cached->data.clone(), + cached->collated_data.value().clone())); + return; + } + td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, std::move(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)); } @@ -1516,14 +1567,14 @@ void ValidatorManagerImpl::set_next_block(BlockIdExt block_id, BlockIdExt next, get_block_handle(block_id, true, std::move(P)); } -void ValidatorManagerImpl::set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::Promise promise) { +void ValidatorManagerImpl::set_block_candidate(BlockCandidate candidate, td::Promise promise) { + BlockIdExt id = candidate.id; if (!candidates_buffer_.empty()) { td::actor::send_closure(candidates_buffer_, &CandidatesBuffer::add_new_candidate, id, PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}, candidate.collated_file_hash); } if (!id.is_masterchain()) { - add_cached_block_data(id, candidate.data.clone()); + add_cached_block_data(id, candidate.data.clone(), candidate.collated_data.clone(), false); } LOG(INFO) << "Got candidate " << id.to_str() << " with " << candidate.out_msg_queue_proof_broadcasts.size() << " out msg queue proof broadcasts"; @@ -1535,8 +1586,9 @@ void ValidatorManagerImpl::set_block_candidate(BlockIdExt id, BlockCandidate can void ValidatorManagerImpl::send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::BufferSlice data, - int mode) { - callback_->send_block_candidate(id, cc_seqno, validator_set_hash, std::move(data), mode); + td::optional collated_data, int mode) { + callback_->send_block_candidate_broadcast(id, cc_seqno, validator_set_hash, std::move(data), std::move(collated_data), + mode); } void ValidatorManagerImpl::write_handle(BlockHandle handle, td::Promise promise) { @@ -1794,7 +1846,7 @@ void ValidatorManagerImpl::send_get_block_request(BlockIdExt id, td::uint32 prio td::Promise promise) { if (auto cached = cached_block_data_.get_if_exists(id, false)) { LOG(DEBUG) << "send_get_block_request: got result from block data cache for " << id.to_str(); - return promise.set_value(ReceivedBlock{id, cached->clone()}); + return promise.set_value(ReceivedBlock{id, cached->data.clone()}); } callback_->download_block(id, priority, td::Timestamp::in(10.0), std::move(promise)); } @@ -1823,9 +1875,9 @@ void ValidatorManagerImpl::send_get_block_proof_link_request(BlockIdExt block_id // Proof link can be created from the cached block data LOG(DEBUG) << "send_get_block_proof_link_request: creating proof link from cached block data for " << block_id.to_str(); - TRY_RESULT_PROMISE_PREFIX(promise, block_root, vm::std_boc_deserialize(*cached), + TRY_RESULT_PROMISE_PREFIX(promise, block, create_block(block_id, cached->data.clone()), "failed to create proof link: "); - TRY_RESULT_PROMISE_PREFIX(promise, proof_link, WaitBlockData::generate_proof_link(block_id, block_root), + TRY_RESULT_PROMISE_PREFIX(promise, proof_link, WaitBlockData::generate_proof_link(block_id, block->root_cell()), "failed to create proof link: "); promise.set_result(std::move(proof_link)); return; @@ -1880,6 +1932,12 @@ void ValidatorManagerImpl::send_download_archive_request(BlockSeqno mc_seqno, Sh callback_->download_archive(mc_seqno, shard_prefix, std::move(tmp_dir), timeout, std::move(promise)); } +void ValidatorManagerImpl::send_get_block_candidate_request( + BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) { + callback_->download_block_candidate(block_id, only_collated_data, timeout, std::move(promise)); +} + void ValidatorManagerImpl::get_block_proof_link_from_import(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { auto it = to_import_all_.upper_bound(masterchain_block_id.seqno() + 1); diff --git a/validator/manager.hpp b/validator/manager.hpp index b3fdbc344..b9618deef 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -268,7 +268,14 @@ class ValidatorManagerImpl : public ValidatorManager { std::map shard_blocks_; std::map> cached_msg_queue_to_masterchain_; - td::LRUCache cached_block_data_{/* max_size = */ 128}; + struct CachedBlockData { + td::BufferSlice data; + CatchainSeqno cc_seqno; + Ed25519_PublicKey creator; + td::optional collated_data; + td::optional collated_data_hash; + }; + td::LRUCache cached_block_data_{/* max_size = */ 128}; td::LRUCache cached_checked_shard_block_descriptions_{/* max_size = */ 1024}; struct ExtMessages { @@ -434,7 +441,8 @@ class ValidatorManagerImpl : public ValidatorManager { void new_ihr_message(td::BufferSlice data) override; void new_shard_block_description_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) override; - void new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data) override; + void new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data, + td::optional collated_data) override; void add_ext_server_id(adnl::AdnlNodeIdShort id) override; void add_ext_server_port(td::uint16 port) override; @@ -486,10 +494,10 @@ class ValidatorManagerImpl : public ValidatorManager { void wait_block_signatures_short(BlockIdExt id, td::Timestamp timeout, td::Promise> promise) override; - void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, - td::uint32 validator_set_hash, td::Promise promise) override; + void set_block_candidate(BlockCandidate candidate, td::Promise promise) override; void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) override; + td::BufferSlice data, td::optional collated_data, + int mode) override; void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; @@ -518,6 +526,7 @@ class ValidatorManagerImpl : public ValidatorManager { void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) override; + void get_block_candidate_by_block_id_from_db(BlockIdExt id, 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(ConstBlockHandle handle, td::Promise> promise) override; @@ -560,6 +569,8 @@ class ValidatorManagerImpl : public ValidatorManager { td::Promise>> promise) override; void send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, std::string tmp_dir, td::Timestamp timeout, td::Promise promise) override; + void send_get_block_candidate_request(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) override; void get_block_proof_link_from_import(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) override; @@ -591,7 +602,8 @@ class ValidatorManagerImpl : public ValidatorManager { } void add_shard_block_description(td::Ref desc); - void add_cached_block_data(BlockIdExt block_id, td::BufferSlice data); + void add_cached_block_data(BlockIdExt block_id, td::BufferSlice data, td::optional collated_data, + bool is_broadcast); void preload_msg_queue_to_masterchain(td::Ref desc, td::Promise promise); void loaded_msg_queue_to_masterchain(td::Ref desc, td::Ref res, td::Promise promise); diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index c79dbda4a..6d5983920 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -17,6 +17,8 @@ Copyright 2017-2020 Telegram Systems LLP */ #include "validator-group.hpp" + +#include "block-auto.h" #include "fabric.h" #include "full-node-master.hpp" #include "ton/ton-io.hpp" @@ -30,7 +32,8 @@ namespace ton { namespace validator { -static bool need_send_candidate_broadcast(const validatorsession::BlockSourceInfo &source_info, bool is_masterchain) { +static bool need_send_block_candidate_broadcast(const validatorsession::BlockSourceInfo &source_info, + bool is_masterchain) { return source_info.priority.first_block_round == source_info.priority.round && source_info.priority.priority == 0 && !is_masterchain; } @@ -95,10 +98,22 @@ void ValidatorGroup::generate_block_candidate_cont(validatorsession::BlockSource td::CancellationToken cancellation_token) { TRY_STATUS_PROMISE(promise, cancellation_token.check()); td::uint64 max_answer_size = config_.max_block_size + config_.max_collated_data_size + 1024; - td::actor::send_closure(collation_manager_, &CollationManager::collate_block, shard_, min_masterchain_block_id_, - prev_block_ids_, Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, - source_info.priority, validator_set_, max_answer_size, std::move(cancellation_token), - std::move(promise), config_.proto_version); + CollateParams params{.shard = shard_, + .min_masterchain_block_id = min_masterchain_block_id_, + .prev = prev_block_ids_, + .creator = Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, + .validator_set = validator_set_, + .collator_opts = opts_->get_collator_options(), + .collated_data_deduplicator = collated_data_deduplicator_}; + wait_collated_data_merged( + create_next_block_id_simple().seqno, + [=, this, params = std::move(params), promise = std::move(promise)](td::Result R) mutable { + TRY_STATUS_PROMISE(promise, cancellation_token.check()); + TRY_STATUS_PROMISE(promise, R.move_as_status()); + td::actor::send_closure(collation_manager_, &CollationManager::collate_block, params, source_info.priority, + max_answer_size, std::move(cancellation_token), std::move(promise), + config_.proto_version); + }); } void ValidatorGroup::generated_block_candidate(validatorsession::BlockSourceInfo source_info, @@ -114,8 +129,8 @@ void ValidatorGroup::generated_block_candidate(validatorsession::BlockSourceInfo } else { auto c = R.move_as_ok(); add_available_block_candidate(c.candidate.pubkey.as_bits256(), c.candidate.id, c.candidate.collated_file_hash); - if (need_send_candidate_broadcast(source_info, shard_.is_masterchain())) { - send_block_candidate_broadcast(c.candidate.id, c.candidate.data.clone()); + if (need_send_block_candidate_broadcast(source_info, shard_.is_masterchain())) { + send_block_candidate_broadcast(c.candidate.id, c.candidate.data.clone(), c.candidate.collated_data.clone()); } if (!c.self_collated) { block_collator_node_id_[c.candidate.id] = adnl::AdnlNodeIdShort{c.collator_node_id}; @@ -213,9 +228,9 @@ void ValidatorGroup::validate_block_candidate(validatorsession::BlockSourceInfo td::actor::send_closure(SelfId, &ValidatorGroup::update_approve_cache, block_to_cache_key(block), ts); td::actor::send_closure(SelfId, &ValidatorGroup::add_available_block_candidate, block.pubkey.as_bits256(), block.id, block.collated_file_hash); - if (need_send_candidate_broadcast(source_info, block.id.is_masterchain())) { + if (need_send_block_candidate_broadcast(source_info, block.id.is_masterchain())) { td::actor::send_closure(SelfId, &ValidatorGroup::send_block_candidate_broadcast, block.id, - block.data.clone()); + block.data.clone(), block.collated_data.clone()); } promise.set_value({ts, false}); }, @@ -234,21 +249,27 @@ void ValidatorGroup::validate_block_candidate(validatorsession::BlockSourceInfo return; } VLOG(VALIDATOR_DEBUG) << "validating block candidate " << next_block_id; - td::Ref optimistic_prev_block_data; + Ref optimistic_prev_block_data; if (is_optimistic) { TRY_RESULT_PROMISE_PREFIX_ASSIGN( P, optimistic_prev_block_data, create_block(optimistic_prev_block.value().id, std::move(optimistic_prev_block.value().data)), "failed to parse optimistic prev block: "); } - run_validate_query(std::move(block), - ValidateParams{.shard = shard_, - .min_masterchain_block_id = min_masterchain_block_id_, - .prev = std::move(prev), - .validator_set = validator_set_, - .local_validator_id = local_id_, - .optimistic_prev_block = optimistic_prev_block_data}, - manager_, td::Timestamp::in(15.0), std::move(P)); + + ValidateParams params{.shard = shard_, + .min_masterchain_block_id = min_masterchain_block_id_, + .prev = std::move(prev), + .validator_set = validator_set_, + .local_validator_id = local_id_, + .optimistic_prev_block = optimistic_prev_block_data, + .collated_data_merger = collated_data_merger_.get()}; + wait_collated_data_merged( + next_block_id.seqno(), [block = std::move(block), params = std::move(params), manager = manager_, + P = std::move(P)](td::Result R) mutable { + TRY_STATUS_PROMISE(P, R.move_as_status()); + run_validate_query(std::move(block), std::move(params), manager, td::Timestamp::in(15.0), std::move(P)); + }); } void ValidatorGroup::update_approve_cache(CacheKey key, UnixTime value) { @@ -256,8 +277,8 @@ void ValidatorGroup::update_approve_cache(CacheKey key, UnixTime value) { } void ValidatorGroup::accept_block_candidate(validatorsession::BlockSourceInfo source_info, td::BufferSlice block_data, - RootHash root_hash, FileHash file_hash, - std::vector signatures, + RootHash root_hash, FileHash file_hash, td::BufferSlice collated_data, + FileHash collated_data_hash, std::vector signatures, std::vector approve_signatures, validatorsession::ValidatorSessionStats stats, td::Promise promise) { @@ -270,12 +291,15 @@ void ValidatorGroup::accept_block_candidate(validatorsession::BlockSourceInfo so validator_set_->check_approve_signatures(root_hash, file_hash, approve_sig_set).ensure(); if (!started_) { - postponed_accept_.push_back(PostponedAccept{root_hash, file_hash, std::move(block_data), std::move(sig_set), + postponed_accept_.push_back(PostponedAccept{source_info.source, root_hash, file_hash, std::move(block_data), + std::move(collated_data), collated_data_hash, std::move(sig_set), std::move(approve_sig_set), std::move(stats), std::move(promise)}); return; } auto next_block_id = create_next_block_id(root_hash, file_hash); LOG(WARNING) << "Accepted block " << next_block_id.to_str(); + approved_blocks_ids_[next_block_id.root_hash] = next_block_id; + merge_collated_data(source_info.source, next_block_id, collated_data_hash, block_data.clone(), collated_data.clone()); stats.block_id = next_block_id; td::actor::send_closure(manager_, &ValidatorManager::log_validator_session_stats, std::move(stats)); auto block = @@ -283,33 +307,36 @@ void ValidatorGroup::accept_block_candidate(validatorsession::BlockSourceInfo so // OLD BROADCAST BEHAVIOR: // Creator of the block sends broadcast to public overlays - // Creator of the block sends broadcast to private block overlay unless candidate broadcast was sent - // Any node sends broadcast to custom overlays unless candidate broadcast was sent + // Creator of the block sends broadcast to private block overlay unless block candidate broadcast was sent earlier + // Any node sends broadcast to custom overlays unless block candidate broadcast was sent earlier + if (block.not_null() && !shard_.is_masterchain()) { + send_block_candidate_broadcast(next_block_id, block->data().clone(), collated_data.clone()); + } int send_broadcast_mode = 0; - bool sent_candidate = sent_candidate_broadcasts_.contains(next_block_id); + bool sent_block_data = sent_block_candidate_broadcasts_.contains(next_block_id); if (source_info.source.compute_short_id() == local_id_) { send_broadcast_mode |= fullnode::FullNode::broadcast_mode_public; - if (!sent_candidate) { - send_broadcast_mode |= fullnode::FullNode::broadcast_mode_private_block; + if (!sent_block_data) { + send_broadcast_mode |= fullnode::FullNode::broadcast_mode_fast_sync; } } - if (!sent_candidate) { + if (!sent_block_data) { send_broadcast_mode |= fullnode::FullNode::broadcast_mode_custom; } // NEW BROADCAST BEHAVIOR (activate later): - // Masterchain block are broadcasted as Block Broadcast (with signatures). Shard blocks are broadcasted as Block Candidate Broadcast (only block data). + // Masterchain block are broadcasted as Block Broadcast (with signatures). Shard blocks are broadcasted as block candidate broadcast (only block data, no signatures). // Public and private overlays: creator sends masterchain blocks, all validators send shard blocks. // Custom overlays: all nodes send all blocks. - // If the block was broadcasted earlier as a candidate (to private and custom overlays), the broadcast is not repeated. + // If the block was broadcasted earlier as block data (to private and custom overlays), the broadcast is not repeated. /*int send_broadcast_mode = 0; - bool sent_candidate = sent_candidate_broadcasts_.contains(next_block_id); + bool sent_block_data = sent_block_candidate_broadcasts_.contains(next_block_id); if (!shard_.is_masterchain() || source_info.source.compute_short_id() == local_id_) { send_broadcast_mode |= fullnode::FullNode::broadcast_mode_public; - if (!sent_candidate) { + if (!sent_block_data) { send_broadcast_mode |= fullnode::FullNode::broadcast_mode_private_block; } } - if (!sent_candidate) { + if (!sent_block_data) { send_broadcast_mode |= fullnode::FullNode::broadcast_mode_custom; }*/ accept_block_query(next_block_id, std::move(block), std::move(prev_block_ids_), std::move(sig_set), @@ -352,8 +379,8 @@ void ValidatorGroup::skip_round(td::uint32 round_id) { void ValidatorGroup::get_approved_candidate(PublicKey source, RootHash root_hash, FileHash file_hash, FileHash collated_data_file_hash, td::Promise promise) { - BlockIdExt id = create_next_block_id(root_hash, file_hash); - + auto it = approved_blocks_ids_.find(root_hash); + BlockIdExt id = it != approved_blocks_ids_.end() ? it->second : create_next_block_id(root_hash, file_hash); td::actor::send_closure(manager_, &ValidatorManager::get_block_candidate_from_db, source, id, collated_data_file_hash, std::move(promise)); } @@ -388,11 +415,24 @@ void ValidatorGroup::generate_block_optimistic(validatorsession::BlockSourceInfo }; LOG(WARNING) << "Optimistically generating next block after " << block_id.to_str(); td::uint64 max_answer_size = config_.max_block_size + config_.max_collated_data_size + 1024; - td::actor::send_closure(collation_manager_, &CollationManager::collate_block_optimistic, shard_, min_masterchain_block_id_, - block_id, std::move(prev_block), Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, - source_info.priority, validator_set_, max_answer_size, - optimistic_generation_->cancellation_token_source.get_cancellation_token(), std::move(P), - config_.proto_version); + TRY_RESULT_PROMISE(promise, prev_block_data, create_block(block_id, std::move(prev_block))); + CollateParams params{.shard = shard_, + .min_masterchain_block_id = min_masterchain_block_id_, + .prev = {block_id}, + .creator = Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, + .validator_set = validator_set_, + .collator_opts = opts_->get_collator_options(), + .optimistic_prev_block = prev_block_data, + .collated_data_deduplicator = collated_data_deduplicator_}; + auto token = optimistic_generation_->cancellation_token_source.get_cancellation_token(); + wait_collated_data_merged(block_id.seqno(), [=, this, params = std::move(params), token = std::move(token), + promise = std::move(promise)](td::Result R) mutable { + TRY_STATUS_PROMISE(promise, token.check()); + TRY_STATUS_PROMISE(promise, R.move_as_status()); + td::actor::send_closure(collation_manager_, &CollationManager::collate_block, std::move(params), + source_info.priority, max_answer_size, std::move(token), std::move(promise), + config_.proto_version); + }); } void ValidatorGroup::generated_block_optimistic(validatorsession::BlockSourceInfo source_info, @@ -480,6 +520,7 @@ std::unique_ptr ValidatorGroup::ma void on_block_committed(validatorsession::BlockSourceInfo source_info, validatorsession::ValidatorSessionRootHash root_hash, validatorsession::ValidatorSessionFileHash file_hash, td::BufferSlice data, + td::BufferSlice collated_data, FileHash collated_data_hash, std::vector> signatures, std::vector> approve_signatures, validatorsession::ValidatorSessionStats stats) override { @@ -493,8 +534,8 @@ std::unique_ptr ValidatorGroup::ma } auto P = td::PromiseCreator::lambda([](td::Result) {}); td::actor::send_closure(id_, &ValidatorGroup::accept_block_candidate, std::move(source_info), std::move(data), - root_hash, file_hash, std::move(sigs), std::move(approve_sigs), std::move(stats), - std::move(P)); + root_hash, file_hash, std::move(collated_data), collated_data_hash, std::move(sigs), + std::move(approve_sigs), std::move(stats), std::move(P)); } void on_block_skipped(td::uint32 round) override { td::actor::send_closure(id_, &ValidatorGroup::skip_round, round); @@ -565,6 +606,12 @@ void ValidatorGroup::create_session() { td::actor::send_closure(rldp_, &rldp::Rldp::add_id, local_adnl_id_); td::actor::send_closure(rldp2_, &rldp2::Rldp::add_id, local_adnl_id_); + merge_collated_data_enabled_ = config_.merge_collated_data && !shard_.is_masterchain(); + if (merge_collated_data_enabled_) { + collated_data_deduplicator_ = std::make_shared(); + collated_data_merger_ = td::actor::create_actor(PSTRING() << "cdata-merger" << shard_.to_str()); + } + config_.catchain_opts.broadcast_speed_multiplier = opts_->get_catchain_broadcast_speed_multiplier(); if (!config_.new_catchain_ids) { session_ = validatorsession::ValidatorSession::create(session_id_, config_, local_id_, std::move(vec), @@ -594,6 +641,7 @@ void ValidatorGroup::start(std::vector prev, BlockIdExt min_masterch min_masterchain_block_id_ = min_masterchain_block_id; cached_collated_block_ = nullptr; started_ = true; + collated_data_merged_upto_ = create_next_block_id_simple().seqno; if (init_) { td::actor::send_closure(session_, &validatorsession::ValidatorSession::start); @@ -601,8 +649,10 @@ void ValidatorGroup::start(std::vector prev, BlockIdExt min_masterch for (auto &p : postponed_accept_) { auto next_block_id = create_next_block_id(p.root_hash, p.file_hash); + approved_blocks_ids_[next_block_id.root_hash] = next_block_id; p.stats.block_id = next_block_id; td::actor::send_closure(manager_, &ValidatorManager::log_validator_session_stats, std::move(p.stats)); + merge_collated_data(p.source, next_block_id, p.collated_data_hash, p.block.clone(), p.collated_data.clone()); auto block = p.block.size() > 0 ? create_block(next_block_id, std::move(p.block)).move_as_ok() : td::Ref{}; @@ -723,12 +773,81 @@ void ValidatorGroup::get_validator_group_info_for_litequery_cont( promise.set_result(std::move(result)); } -void ValidatorGroup::send_block_candidate_broadcast(BlockIdExt id, td::BufferSlice data) { - if (sent_candidate_broadcasts_.insert(id).second) { +void ValidatorGroup::send_block_candidate_broadcast(BlockIdExt id, td::BufferSlice data, + td::BufferSlice collated_data) { + if (sent_block_candidate_broadcasts_.insert(id).second) { + td::actor::send_closure(manager_, &ValidatorManager::send_block_candidate_broadcast, id, + validator_set_->get_catchain_seqno(), validator_set_->get_validator_set_hash(), + std::move(data), std::move(collated_data), + fullnode::FullNode::broadcast_mode_fast_sync | fullnode::FullNode::broadcast_mode_custom); + } +} + +void ValidatorGroup::merge_collated_data(PublicKey source, BlockIdExt block_id, FileHash collated_data_hash, + td::BufferSlice o_block_data, td::BufferSlice o_collated_data, bool try_disk) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(block_id.seqno())) { + return; + } + if (!o_block_data.empty()) { + td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate_data, block_id, + o_block_data.clone(), o_collated_data.clone()); + collated_data_deduplicator_->add_block_candidate(block_id.seqno(), o_block_data.clone(), o_collated_data.clone()) + .ensure(); + collated_data_merged_.insert(block_id.seqno()); + while (collated_data_merged_.contains(collated_data_merged_upto_)) { + ++collated_data_merged_upto_; + } + for (auto it = collated_data_merged_waiters_.begin(); it != collated_data_merged_waiters_.end();) { + if (it->first > collated_data_merged_upto_) { + break; + } + for (auto &promise : it->second) { + promise.set_value(td::Unit{}); + } + it = collated_data_merged_waiters_.erase(it); + } + LOG(INFO) << "Merge collated data " << block_id.id.to_str() << ", merged_upto=" << collated_data_merged_upto_; + return; + } + + if (try_disk) { td::actor::send_closure( - manager_, &ValidatorManager::send_block_candidate_broadcast, id, validator_set_->get_catchain_seqno(), - validator_set_->get_validator_set_hash(), std::move(data), - fullnode::FullNode::broadcast_mode_private_block | fullnode::FullNode::broadcast_mode_custom); + manager_, &ValidatorManager::get_block_candidate_from_db, source, block_id, collated_data_hash, + [=, SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + LOG(INFO) << "Failed to get block candidate " << block_id.id.to_str() << " from disk: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorGroup::merge_collated_data, source, block_id, collated_data_hash, + td::BufferSlice{}, td::BufferSlice{}, false); + } else { + BlockCandidate c = R.move_as_ok(); + td::actor::send_closure(SelfId, &ValidatorGroup::merge_collated_data, source, block_id, collated_data_hash, + std::move(c.data), std::move(c.collated_data), false); + } + }); + return; + } + + td::actor::send_closure(session_, &validatorsession::ValidatorSession::get_accepted_candidate, source, block_id, + collated_data_hash, [=, SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + LOG(WARNING) << "Failed to get block candidate " << block_id.id.to_str() + << " from validator session: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorGroup::merge_collated_data, source, block_id, + collated_data_hash, td::BufferSlice{}, td::BufferSlice{}, false); + } else { + BlockCandidate c = R.move_as_ok(); + td::actor::send_closure(SelfId, &ValidatorGroup::merge_collated_data, source, block_id, + collated_data_hash, std::move(c.data), std::move(c.collated_data), + false); + } + }); +} + +void ValidatorGroup::wait_collated_data_merged(BlockSeqno seqno, td::Promise promise) { + if (!merge_collated_data_enabled_ || collated_data_merged_upto_ >= seqno) { + promise.set_value(td::Unit{}); + } else { + collated_data_merged_waiters_[seqno].push_back(std::move(promise)); } } diff --git a/validator/validator-group.hpp b/validator/validator-group.hpp index 3df456efa..f9eb7228f 100644 --- a/validator/validator-group.hpp +++ b/validator/validator-group.hpp @@ -18,6 +18,7 @@ */ #pragma once +#include "impl/collated-data-merger.h" #include "collation-manager.hpp" #include "interfaces/validator-manager.h" @@ -27,6 +28,7 @@ #include "rldp2/rldp.h" #include +#include namespace ton { @@ -43,8 +45,8 @@ class ValidatorGroup : public td::actor::Actor { td::Promise> promise, td::optional optimistic_prev_block); void accept_block_candidate(validatorsession::BlockSourceInfo source_info, td::BufferSlice block, RootHash root_hash, - FileHash file_hash, std::vector signatures, - std::vector approve_signatures, + FileHash file_hash, td::BufferSlice collated_data, FileHash collated_data_hash, + std::vector signatures, std::vector approve_signatures, validatorsession::ValidatorSessionStats stats, td::Promise promise); void skip_round(td::uint32 round); void accept_block_query(BlockIdExt block_id, td::Ref block, std::vector prev, @@ -114,9 +116,11 @@ class ValidatorGroup : public td::actor::Actor { void destroy_cont(); struct PostponedAccept { + PublicKey source; RootHash root_hash; FileHash file_hash; - td::BufferSlice block; + td::BufferSlice block, collated_data; + FileHash collated_data_hash; td::Ref sigs; td::Ref approve_sigs; validatorsession::ValidatorSessionStats stats; @@ -185,12 +189,13 @@ class ValidatorGroup : public td::actor::Actor { void add_available_block_candidate(td::Bits256 source, BlockIdExt id, FileHash collated_data_hash) { available_block_candidates_.emplace(source, id, collated_data_hash); + approved_blocks_ids_[id.root_hash] = id; } - std::set sent_candidate_broadcasts_; + std::set sent_block_candidate_broadcasts_; std::map block_collator_node_id_; - void send_block_candidate_broadcast(BlockIdExt id, td::BufferSlice data); + void send_block_candidate_broadcast(BlockIdExt id, td::BufferSlice data, td::BufferSlice collated_data); struct OptimisticGeneration { td::uint32 round = 0; @@ -206,6 +211,18 @@ class ValidatorGroup : public td::actor::Actor { } }; std::unique_ptr optimistic_generation_; + std::map approved_blocks_ids_; + + bool merge_collated_data_enabled_ = false; + td::actor::ActorOwn collated_data_merger_; + std::shared_ptr collated_data_deduplicator_; + std::set collated_data_merged_; + BlockSeqno collated_data_merged_upto_ = 0; + std::map>> collated_data_merged_waiters_; + + void merge_collated_data(PublicKey source, BlockIdExt block_id, FileHash collated_data_hash, + td::BufferSlice o_block_data, td::BufferSlice o_collated_data, bool try_disk = true); + void wait_collated_data_merged(BlockSeqno seqno, td::Promise promise); }; } // namespace validator diff --git a/validator/validator.h b/validator/validator.h index 826fc8f8f..e8dae0e54 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -216,8 +216,9 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void send_ihr_message(AccountIdPrefixFull dst, td::BufferSlice data) = 0; virtual void send_ext_message(AccountIdPrefixFull dst, td::BufferSlice data) = 0; virtual void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) = 0; - virtual void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, int mode) = 0; + virtual void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data, + td::optional collated_data, int mode) = 0; virtual void send_broadcast(BlockBroadcast broadcast, int mode) = 0; virtual void send_out_msg_queue_proof_broadcast(td::Ref broadcats) { LOG(ERROR) << "Unimplemented send_out_msg_queue_proof_broadcast - ignore broadcast"; @@ -240,6 +241,8 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void download_out_msg_queue_proof(ShardIdFull dst_shard, std::vector blocks, block::ImportedMsgQueueLimits limits, td::Timestamp timeout, td::Promise>> promise) = 0; + virtual void download_block_candidate(BlockIdExt block_id, bool only_collated_data, td::Timestamp timeout, + td::Promise> promise) = 0; virtual void new_key_block(BlockHandle handle) = 0; }; @@ -297,7 +300,9 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void new_ihr_message(td::BufferSlice data) = 0; virtual void new_shard_block_description_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) = 0; - virtual void new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data) = 0; + virtual void new_block_candidate_broadcast(BlockIdExt block_id, td::BufferSlice data, + td::optional collated_data) { + } virtual void add_ext_server_id(adnl::AdnlNodeIdShort id) = 0; virtual void add_ext_server_port(td::uint16 port) = 0; @@ -310,6 +315,7 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) = 0; virtual void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) = 0; + virtual void get_block_candidate_by_block_id_from_db(BlockIdExt id, 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(ConstBlockHandle handle, td::Promise> promise) = 0; From c07bffa808ba4b2c446f621489a59133d39dbe1e Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 30 Sep 2025 14:10:10 +0300 Subject: [PATCH 02/13] Remove cells of current block data from current collated data --- crypto/vm/boc.cpp | 36 +++++++++++++++++++++++++++++++----- crypto/vm/boc.h | 2 +- validator/impl/collator.cpp | 9 ++++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index 4263bcfb6..3c1f29cff 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -1310,12 +1310,38 @@ ProofStorageStat::CellStatus ProofStorageStat::get_cell_status(const Cell::Hash& return it == cells_.end() ? c_none : it->second.status; } -std::vector> ProofStorageStat::build_collated_data() const { +std::vector> ProofStorageStat::build_collated_data(std::vector> skip_roots) const { struct Cache { Ref result; bool is_root = true; + bool skip = false; + bool skip_visited = false; }; std::map cache; + + std::function&)> dfs_skip = [&](const Ref& cell) { + Cell::Hash hash = cell->get_hash(); + Cache& entry = cache[hash]; + if (entry.skip_visited) { + return; + } + entry.skip_visited = entry.skip = true; + CellSlice cs{NoVm{}, cell}; + if (cs.special_type() != CellTraits::SpecialType::PrunnedBranch) { + for (unsigned i = 0; i < cell->get_level(); ++i) { + cache[cell->get_hash(i)].skip = true; + } + } + for (unsigned i = 0; i < cs.size_refs(); ++i) { + dfs_skip(cs.prefetch_ref(i)); + } + }; + for (const auto& cell : skip_roots) { + if (cell.not_null()) { + dfs_skip(cell); + } + } + std::function dfs = [&](const CellInfo& info) -> Cache& { Cell::Hash hash = info.cell->get_hash(info.cell_max_level); Cache& entry = cache[hash]; @@ -1333,7 +1359,7 @@ std::vector> ProofStorageStat::build_collated_data() const { Ref child = info.cell->get_ref(i); Cell::Hash child_hash = child->get_hash(child_max_level); auto it = cells_.find(child_hash); - if (it == cells_.end() || it->second.status != c_loaded) { + if (it == cells_.end() || it->second.status != c_loaded || cache[child_hash].skip) { cb.store_ref(CellBuilder::create_pruned_branch(child, Cell::max_level, child_max_level)); } else { Cache& child_result = dfs(it->second); @@ -1345,14 +1371,14 @@ std::vector> ProofStorageStat::build_collated_data() const { entry2.result = cb.finalize(info.cell->is_special()); return entry2; }; - for (auto& [_, info] : cells_) { - if (info.status == c_loaded) { + for (auto& [hash, info] : cells_) { + if (info.status == c_loaded && !cache[hash].skip) { dfs(info); } } std::vector> result; for (auto& [_, entry] : cache) { - if (entry.is_root) { + if (entry.result.not_null() && entry.is_root) { result.push_back(std::move(entry.result)); } } diff --git a/crypto/vm/boc.h b/crypto/vm/boc.h index e13875241..b2698fb58 100644 --- a/crypto/vm/boc.h +++ b/crypto/vm/boc.h @@ -177,7 +177,7 @@ class ProofStorageStat { return get_cell_status(hash) == c_loaded; } - std::vector> build_collated_data() const; + std::vector> build_collated_data(std::vector> skip_roots = {}) const; static td::uint64 estimate_prunned_size(); static td::uint64 estimate_serialized_size(const Ref& cell); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index f92fb6ec1..c60ae5f0c 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -6353,7 +6353,14 @@ bool Collator::create_collated_data() { // 7. Collated data proofs collated_roots_.push_back( vm::CellBuilder().store_long(block::gen::CollatedDataSeparator::cons_tag[0], 32).finalize_novm()); - std::vector> proofs = collated_data_stat.build_collated_data(); + std::vector> block_data_parts = { + shard_account_blocks_, + in_msg_dict->get_root_cell(), + out_msg_dict->get_root_cell(), + state_update + }; + std::vector> proofs = + collated_data_stat.build_collated_data(/* skip_roots = */ std::move(block_data_parts)); collated_roots_.insert(collated_roots_.end(), proofs.begin(), proofs.end()); } else { std::map> proofs; From 959be8f0daf8c65565b5265928657856152786b8 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 30 Sep 2025 16:42:09 +0300 Subject: [PATCH 03/13] Fix serializing candidate broadcast; allow collating before receiving all collated data; cleanup code --- validator-session/candidate-serializer.cpp | 63 ++++++++-------- validator-session/candidate-serializer.h | 6 +- validator-session/validator-session.cpp | 6 +- validator/collation-manager.cpp | 14 ++-- validator/collation-manager.hpp | 5 +- .../collator-node/collator-node-session.cpp | 75 +++++++++++++++---- .../collator-node/collator-node-session.hpp | 9 ++- validator/collator-node/utils.cpp | 8 +- validator/collator-node/utils.hpp | 2 +- validator/full-node-serializer.cpp | 2 +- validator/full-node.cpp | 3 +- validator/full-node.h | 1 + validator/manager.cpp | 4 +- validator/validator-group.cpp | 12 +-- 14 files changed, 126 insertions(+), 84 deletions(-) diff --git a/validator-session/candidate-serializer.cpp b/validator-session/candidate-serializer.cpp index f4b1a066b..4336001d4 100644 --- a/validator-session/candidate-serializer.cpp +++ b/validator-session/candidate-serializer.cpp @@ -36,39 +36,38 @@ td::Result serialize_candidate(const tl_object_ptr> deserialize_candidate(td::Slice data, bool compression_enabled, - int max_decompressed_data_size, - int proto_version) { + int max_decompressed_data_size) { if (!compression_enabled) { return fetch_tl_object(data, true); } TRY_RESULT(f, fetch_tl_object(data, true)); td::Result> res; - ton_api::downcast_call(*f, td::overloaded( - [&](ton_api::validatorSession_candidate& c) { - res = td::Status::Error("Received decompressed tl object, while compression_enabled=true"); - }, - [&](ton_api::validatorSession_compressedCandidate& c) { - res = [&]() -> td::Result> { - if (c.decompressed_size_ > max_decompressed_data_size) { - return td::Status::Error("decompressed size is too big"); - } - TRY_RESULT(p, decompress_candidate_data(c.data_, false, c.decompressed_size_, - max_decompressed_data_size, proto_version)); - return create_tl_object(c.src_, c.round_, c.root_hash_, std::move(p.first), - std::move(p.second)); - }(); - }, - [&](ton_api::validatorSession_compressedCandidateV2& c) { - res = [&]() -> td::Result> { - if (c.data_.size() > max_decompressed_data_size) { - return td::Status::Error("Compressed data is too big"); - } - TRY_RESULT(p, decompress_candidate_data(c.data_, true, 0, - max_decompressed_data_size, proto_version)); - return create_tl_object(c.src_, c.round_, c.root_hash_, std::move(p.first), - std::move(p.second)); - }(); - })); + ton_api::downcast_call( + *f, td::overloaded( + [&](ton_api::validatorSession_candidate& c) { + res = td::Status::Error("Received decompressed tl object, while compression_enabled=true"); + }, + [&](ton_api::validatorSession_compressedCandidate& c) { + res = [&]() -> td::Result> { + if (c.decompressed_size_ > max_decompressed_data_size) { + return td::Status::Error("decompressed size is too big"); + } + TRY_RESULT( + p, decompress_candidate_data(c.data_, false, c.decompressed_size_, max_decompressed_data_size)); + return create_tl_object(c.src_, c.round_, c.root_hash_, + std::move(p.first), std::move(p.second)); + }(); + }, + [&](ton_api::validatorSession_compressedCandidateV2& c) { + res = [&]() -> td::Result> { + if (c.data_.size() > max_decompressed_data_size) { + return td::Status::Error("Compressed data is too big"); + } + TRY_RESULT(p, decompress_candidate_data(c.data_, true, 0, max_decompressed_data_size)); + return create_tl_object(c.src_, c.round_, c.root_hash_, + std::move(p.first), std::move(p.second)); + }(); + })); return res; } @@ -80,7 +79,7 @@ td::Result compress_candidate_data(td::Slice block, td::Slice c return td::Status::Error("block candidate should have exactly one root"); } std::vector> roots = {boc1.get_root_cell()}; - TRY_STATUS(boc2.deserialize(collated_data)); + TRY_STATUS(boc2.deserialize(collated_data, 1000000)); for (int i = 0; i < boc2.get_root_count(); ++i) { roots.push_back(boc2.get_root_cell(i)); } @@ -94,8 +93,7 @@ td::Result compress_candidate_data(td::Slice block, td::Slice c td::Result> decompress_candidate_data(td::Slice compressed, bool improved_compression, int decompressed_size, - int max_decompressed_size, - int proto_version) { + int max_decompressed_size) { std::vector> roots; if (!improved_compression) { TRY_RESULT(decompressed, td::lz4_decompress(compressed, decompressed_size)); @@ -111,8 +109,7 @@ td::Result> decompress_candidate_dat } TRY_RESULT(block_data, vm::std_boc_serialize(roots[0], 31)); roots.erase(roots.begin()); - int collated_data_mode = proto_version >= 5 ? 2 : 31; - TRY_RESULT(collated_data, vm::std_boc_serialize_multi(std::move(roots), collated_data_mode)); + TRY_RESULT(collated_data, vm::std_boc_serialize_multi(std::move(roots), 2)); LOG(DEBUG) << "Decompressing block candidate " << (improved_compression ? "V2:" : ":") << compressed.size() << " -> " << block_data.size() + collated_data.size(); return std::make_pair(std::move(block_data), std::move(collated_data)); diff --git a/validator-session/candidate-serializer.h b/validator-session/candidate-serializer.h index 9ab53cc89..d9c6a17c8 100644 --- a/validator-session/candidate-serializer.h +++ b/validator-session/candidate-serializer.h @@ -24,15 +24,13 @@ td::Result serialize_candidate(const tl_object_ptr> deserialize_candidate(td::Slice data, bool compression_enabled, - int max_decompressed_data_size, - int proto_version); + int max_decompressed_data_size); td::Result compress_candidate_data(td::Slice block, td::Slice collated_data, size_t& decompressed_size); td::Result> decompress_candidate_data(td::Slice compressed, bool improved_compression, int decompressed_size, - int max_decompressed_size, - int proto_version); + int max_decompressed_size); } // namespace ton::validatorsession diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index feac80c0d..deb6d3b23 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -242,8 +242,7 @@ void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice td::Timer deserialize_timer; auto R = deserialize_candidate(data, compress_block_candidates_, - description().opts().max_block_size + description().opts().max_collated_data_size + 1024, - description().opts().proto_version); + description().opts().max_block_size + description().opts().max_collated_data_size + 1024); double deserialize_time = deserialize_timer.elapsed(); if (R.is_error()) { VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << src << "][broadcast " << sha256_bits256(data.as_slice()) @@ -1012,8 +1011,7 @@ void ValidatorSessionImpl::downloaded_accepted_candidate(td::uint32 round, const } auto R = deserialize_candidate(result, compress_block_candidates_, - description().opts().max_block_size + description().opts().max_collated_data_size + 1024, - description().opts().proto_version); + description().opts().max_block_size + description().opts().max_collated_data_size + 1024); if (R.is_error()) { VLOG(VALIDATOR_SESSION_WARNING) << this << ": failed to download accepted candidate " << candidate_id << ": " << R.move_as_error(); diff --git a/validator/collation-manager.cpp b/validator/collation-manager.cpp index 026c26a56..fcdecffea 100644 --- a/validator/collation-manager.cpp +++ b/validator/collation-manager.cpp @@ -56,8 +56,8 @@ void CollationManager::tear_down() { } void CollationManager::collate_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, - td::CancellationToken cancellation_token, td::Promise promise, - int proto_version) { + td::CancellationToken cancellation_token, + td::Promise promise) { if (params.shard.is_masterchain()) { run_collate_query(std::move(params), manager_, td::Timestamp::in(10.0), std::move(cancellation_token), promise.wrap([](BlockCandidate&& candidate) { @@ -67,13 +67,12 @@ void CollationManager::collate_block(CollateParams params, BlockCandidatePriorit return; } collate_shard_block(std::move(params), priority, max_answer_size, std::move(cancellation_token), std::move(promise), - td::Timestamp::in(10.0), proto_version); + td::Timestamp::in(10.0)); } void CollationManager::collate_shard_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, td::CancellationToken cancellation_token, - td::Promise promise, td::Timestamp timeout, - int proto_version) { + td::Promise promise, td::Timestamp timeout) { bool is_optimistic = params.optimistic_prev_block.not_null(); TRY_STATUS_PROMISE(promise, cancellation_token.check()); ShardInfo* s = select_shard_info(params.shard); @@ -181,7 +180,7 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP delay_action( [=, promise = std::move(promise)]() mutable { td::actor::send_closure(SelfId, &CollationManager::collate_shard_block, params, priority, max_answer_size, - cancellation_token, std::move(promise), timeout, proto_version); + cancellation_token, std::move(promise), timeout); }, retry_at); }; @@ -214,8 +213,7 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP return; } TRY_RESULT_PROMISE(P, f, fetch_tl_object(data, true)); - TRY_RESULT_PROMISE(P, candidate, - deserialize_candidate(std::move(f), td::narrow_cast(max_answer_size), proto_version)); + TRY_RESULT_PROMISE(P, candidate, deserialize_candidate(std::move(f), td::narrow_cast(max_answer_size))); if (candidate.pubkey.as_bits256() != params.creator.as_bits256()) { P.set_error(td::Status::Error("collate query: block candidate source mismatch")); return; diff --git a/validator/collation-manager.hpp b/validator/collation-manager.hpp index 7f001124d..67faf5fe4 100644 --- a/validator/collation-manager.hpp +++ b/validator/collation-manager.hpp @@ -38,8 +38,7 @@ class CollationManager : public td::actor::Actor { void alarm() override; void collate_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, - td::CancellationToken cancellation_token, td::Promise promise, - int proto_version); + td::CancellationToken cancellation_token, td::Promise promise); void update_options(td::Ref opts); @@ -59,7 +58,7 @@ class CollationManager : public td::actor::Actor { void collate_shard_block(CollateParams params, BlockCandidatePriority priority, td::uint64 max_answer_size, td::CancellationToken cancellation_token, td::Promise promise, - td::Timestamp timeout, int proto_version); + td::Timestamp timeout); void update_collators_list(const CollatorsList& collators_list); diff --git a/validator/collator-node/collator-node-session.cpp b/validator/collator-node/collator-node-session.cpp index 92bbad303..460a3fef5 100644 --- a/validator/collator-node/collator-node-session.cpp +++ b/validator/collator-node/collator-node-session.cpp @@ -21,6 +21,7 @@ #include "checksum.h" #include "collator-node.hpp" #include "fabric.h" +#include "full-node.h" #include "utils.hpp" namespace ton::validator { @@ -134,9 +135,10 @@ void CollatorNodeSession::on_block_candidate_broadcast(BlockCandidate candidate) void CollatorNodeSession::update_masterchain_config(td::Ref state) { ValidatorSessionConfig config = state->get_consensus_config(); - proto_version_ = config.proto_version; max_candidate_size_ = config.max_block_size + config.max_collated_data_size + 1024; merge_collated_data_enabled_ = config.merge_collated_data; + LOG(INFO) << "Config: max_candidate_size=" << max_candidate_size_ + << " merge_collated_data=" << merge_collated_data_enabled_; } void CollatorNodeSession::generate_block(std::vector prev_blocks, @@ -243,9 +245,14 @@ void CollatorNodeSession::generate_block(std::vector prev_blocks, .collated_data_deduplicator = collated_data_deduplicator_}; auto token = cache_entry->cancellation_token_source.get_cancellation_token(); wait_collated_data_merged( - block_seqno - (is_optimistic ? 1 : 0), [=, this, params = std::move(params)](td::Result R) mutable { + block_seqno - (is_optimistic ? 1 : 0), td::Timestamp::in(0.5), + [=, this, params = std::move(params)](td::Result R) mutable { if (R.is_error()) { - return; + if (R.error().code() != ErrorCode::timeout) { + return; + } + LOG(WARNING) << "Merge collated data takes too long: seqno=" << block_seqno - (is_optimistic ? 1 : 0) + << ", merged_upto=" << collated_data_merged_upto_ << ", proceeding without merge"; } run_collate_query(std::move(params), manager_, timeout, token, [=, shard = shard_, cc_seqno = validator_set_->get_catchain_seqno(), SelfId = actor_id(this), @@ -329,7 +336,7 @@ void CollatorNodeSession::process_request_optimistic_cont2(BlockIdExt prev_block TRY_RESULT_PROMISE_PREFIX(promise, f, fetch_tl_object(response, true), "failed to download prev block data for optimistic collation: "); TRY_RESULT_PROMISE_PREFIX(promise, candidate, - deserialize_candidate(std::move(f), max_candidate_size_, proto_version_), + deserialize_candidate(std::move(f), max_candidate_size_), "failed to download prev block data for optimistic collation: "); TRY_RESULT_PROMISE_PREFIX(promise, prev_block, create_block(prev_block_id, std::move(candidate.data)), "invalid prev block data from validator: "); @@ -345,11 +352,31 @@ void CollatorNodeSession::CacheEntry::cancel(td::Status reason) { cancellation_token_source.cancel(); } -void CollatorNodeSession::wait_collated_data_merged(BlockSeqno seqno, td::Promise promise) { +void CollatorNodeSession::alarm() { + for (auto it = collated_data_merged_waiters_.begin(); it != collated_data_merged_waiters_.end();) { + std::erase_if(it->second, [&](std::pair, td::Timestamp>& p) { + if (p.second && p.second.is_in_past()) { + p.first.set_error(td::Status::Error(ErrorCode::timeout)); + return true; + } + alarm_timestamp().relax(p.second); + return false; + }); + if (it->second.empty()) { + it = collated_data_merged_waiters_.erase(it); + } else { + ++it; + } + } +} + +void CollatorNodeSession::wait_collated_data_merged(BlockSeqno seqno, td::Timestamp timeout, + td::Promise promise) { if (!merge_collated_data_enabled_ || collated_data_merged_upto_ >= seqno) { promise.set_value(td::Unit{}); } else { - collated_data_merged_waiters_[seqno].push_back(std::move(promise)); + collated_data_merged_waiters_[seqno].emplace_back(std::move(promise), timeout); + alarm_timestamp().relax(timeout); } } @@ -401,13 +428,16 @@ void CollatorNodeSession::try_merge_collated_data_from_net_cont(BlockIdExt block td::Timestamp::in(10.0), [SelfId = actor_id(this), block_id, block_data = std::move(block_data), retry_at = td::Timestamp::in(5.0)](td::Result> R) mutable { - if (R.is_error()) { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net_cont2, block_id, + std::move(block_data), std::move(R.ok_ref().second)); + } else if (R.error().code() == fullnode::FullNode::errorcode_not_in_fast_sync_overlay) { + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": not in fast sync overlay, don't merge"; + td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_ignore, block_id); + } else { LOG(DEBUG) << "Merge collated data #" << block_id.seqno() << ": request failed - " << R.error(); td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net_cont, block_id, std::move(block_data)); - } else { - td::actor::send_closure(SelfId, &CollatorNodeSession::try_merge_collated_data_from_net_cont2, block_id, - std::move(block_data), std::move(R.ok_ref().second)); } }); } @@ -451,6 +481,24 @@ void CollatorNodeSession::try_merge_collated_data_finish(BlockCandidate candidat LOG(ERROR) << "Merge collated data #" << candidate.id.seqno() << ": " << S; } collated_data_merged_.insert(candidate.id.seqno()); + process_collated_data_merged_upto(); + LOG(INFO) << "Merge collated data #" << candidate.id.seqno() << ": done, merged_upto=" << collated_data_merged_upto_; + if (!from_disk) { + td::actor::send_closure(manager_, &ValidatorManager::set_block_candidate, std::move(candidate), + [](td::Result) {}); + } +} + +void CollatorNodeSession::try_merge_collated_data_ignore(BlockIdExt block_id) { + if (!merge_collated_data_enabled_ || collated_data_merged_.contains(block_id.seqno())) { + return; + } + collated_data_merged_.insert(block_id.seqno()); + process_collated_data_merged_upto(); + LOG(INFO) << "Merge collated data #" << block_id.seqno() << ": IGNORED, merged_upto=" << collated_data_merged_upto_; +} + +void CollatorNodeSession::process_collated_data_merged_upto() { while (collated_data_merged_.contains(collated_data_merged_upto_)) { ++collated_data_merged_upto_; } @@ -458,16 +506,11 @@ void CollatorNodeSession::try_merge_collated_data_finish(BlockCandidate candidat if (it->first > collated_data_merged_upto_) { break; } - for (auto& promise : it->second) { + for (auto& [promise, _] : it->second) { promise.set_value(td::Unit{}); } it = collated_data_merged_waiters_.erase(it); } - LOG(INFO) << "Merge collated data #" << candidate.id.seqno() << ": done, merged_upto=" << collated_data_merged_upto_; - if (!from_disk) { - td::actor::send_closure(manager_, &ValidatorManager::set_block_candidate, std::move(candidate), - [](td::Result) {}); - } } } // namespace ton::validator diff --git a/validator/collator-node/collator-node-session.hpp b/validator/collator-node/collator-node-session.hpp index 5f3a4c4f6..c5c055fd8 100644 --- a/validator/collator-node/collator-node-session.hpp +++ b/validator/collator-node/collator-node-session.hpp @@ -49,6 +49,8 @@ class CollatorNodeSession : public td::actor::Actor { bool is_optimistic, td::Timestamp timeout, td::Promise promise); void update_masterchain_config(td::Ref state); + void alarm() override; + private: ShardIdFull shard_; std::vector prev_; @@ -77,7 +79,6 @@ class CollatorNodeSession : public td::actor::Actor { BlockSeqno next_block_seqno_; std::map, std::shared_ptr> cache_; - td::uint32 proto_version_ = 0; td::uint32 max_candidate_size_ = 0; void generate_block(std::vector prev_blocks, td::optional o_priority, @@ -98,15 +99,17 @@ class CollatorNodeSession : public td::actor::Actor { std::shared_ptr collated_data_deduplicator_; std::set collated_data_merged_; BlockSeqno collated_data_merged_upto_ = 0; - std::map>> collated_data_merged_waiters_; + std::map, td::Timestamp>>> collated_data_merged_waiters_; - void wait_collated_data_merged(BlockSeqno seqno, td::Promise promise); + void wait_collated_data_merged(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise); void try_merge_collated_data(BlockIdExt block_id); void try_merge_collated_data_from_net(BlockIdExt block_id); void try_merge_collated_data_from_net_cont(BlockIdExt block_id, Ref block_data); void try_merge_collated_data_from_net_cont2(BlockIdExt block_id, Ref block_data, td::BufferSlice collated_data); void try_merge_collated_data_finish(BlockCandidate candidate, bool from_disk); + void try_merge_collated_data_ignore(BlockIdExt block_id); + void process_collated_data_merged_upto(); }; } // namespace ton::validator diff --git a/validator/collator-node/utils.cpp b/validator/collator-node/utils.cpp index 23e2ec40f..e606bc4d3 100644 --- a/validator/collator-node/utils.cpp +++ b/validator/collator-node/utils.cpp @@ -37,7 +37,7 @@ tl_object_ptr serialize_candidate(const BlockCa } td::Result deserialize_candidate(tl_object_ptr f, - int max_decompressed_data_size, int proto_version) { + int max_decompressed_data_size) { td::Result res; ton_api::downcast_call( *f, td::overloaded( @@ -62,7 +62,7 @@ td::Result deserialize_candidate(tl_object_ptr deserialize_candidate(tl_object_ptr td::Result { - TRY_RESULT(p, validatorsession::decompress_candidate_data(c.data_, true, 0, - max_decompressed_data_size, proto_version)); + TRY_RESULT(p, + validatorsession::decompress_candidate_data(c.data_, true, 0, max_decompressed_data_size)); auto collated_data_hash = td::sha256_bits256(p.second); auto key = PublicKey{c.source_}; if (!key.is_ed25519()) { diff --git a/validator/collator-node/utils.hpp b/validator/collator-node/utils.hpp index 26b67d5a9..f92393971 100644 --- a/validator/collator-node/utils.hpp +++ b/validator/collator-node/utils.hpp @@ -23,5 +23,5 @@ namespace ton::validator { tl_object_ptr serialize_candidate(const BlockCandidate& block, bool compress); td::Result deserialize_candidate(tl_object_ptr f, - int max_decompressed_data_size, int proto_version); + int max_decompressed_data_size); } // namespace ton::validator diff --git a/validator/full-node-serializer.cpp b/validator/full-node-serializer.cpp index 38b8a85e8..674f1c989 100644 --- a/validator/full-node-serializer.cpp +++ b/validator/full-node-serializer.cpp @@ -203,7 +203,7 @@ td::Result serialize_block_candidate_broadcast(BlockIdExt block TRY_RESULT(root, vm::std_boc_deserialize(data)); std::vector> roots = {root}; if (collated_data) { - TRY_RESULT(collated_data_roots, vm::std_boc_deserialize_multi(data, 1000000, true)); + TRY_RESULT(collated_data_roots, vm::std_boc_deserialize_multi(collated_data.value(), 1000000, true)); roots.insert(roots.end(), collated_data_roots.begin(), collated_data_roots.end()); } TRY_RESULT(data_new, vm::std_boc_serialize_multi(std::move(roots), 2)); diff --git a/validator/full-node.cpp b/validator/full-node.cpp index 3c6597b0f..fee0f8b5b 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -525,7 +525,8 @@ void FullNodeImpl::download_block_candidate(BlockIdExt block_id, bool only_colla td::Promise> promise) { auto fast_sync_overlay = fast_sync_overlays_.choose_overlay(block_id.shard_full()).first; if (fast_sync_overlay.empty()) { - promise.set_error(td::Status::Error("no fast-sync overlays for download block candidate query")); + promise.set_error(td::Status::Error(errorcode_not_in_fast_sync_overlay, + "no fast-sync overlays for download block candidate query")); return; } td::actor::send_closure(fast_sync_overlay, &FullNodeFastSyncOverlay::download_block_candidate, block_id, diff --git a/validator/full-node.h b/validator/full-node.h index a815fc10a..f49ba6d3d 100644 --- a/validator/full-node.h +++ b/validator/full-node.h @@ -118,6 +118,7 @@ class FullNode : public td::actor::Actor { return 4ull << 30; } enum { broadcast_mode_public = 1, broadcast_mode_fast_sync = 2, broadcast_mode_custom = 4 }; + static constexpr int errorcode_not_in_fast_sync_overlay = 1002; static constexpr td::int32 MAX_FAST_SYNC_OVERLAY_CLIENTS = 5; diff --git a/validator/manager.cpp b/validator/manager.cpp index d5edd644b..e439700be 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -719,7 +719,9 @@ void ValidatorManagerImpl::add_cached_block_data(BlockIdExt block_id, td::Buffer if (!entry.collated_data && collated_data) { FileHash collated_data_hash = td::sha256_bits256(collated_data.value()); if (entry.collated_data_hash && collated_data_hash != entry.collated_data_hash.value()) { - VLOG(VALIDATOR_WARNING) << "invalid block candidate broadcast: collated data hash mismatch"; + VLOG(VALIDATOR_WARNING) << "invalid block candidate broadcast: collated data hash mismatch for block " + << block_id.to_str() << " - expected " << entry.collated_data_hash.value().to_hex() + << ", got " << collated_data_hash.to_hex(); return; } entry.collated_data_hash = collated_data_hash; diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index 6d5983920..2567d39b8 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -111,8 +111,7 @@ void ValidatorGroup::generate_block_candidate_cont(validatorsession::BlockSource TRY_STATUS_PROMISE(promise, cancellation_token.check()); TRY_STATUS_PROMISE(promise, R.move_as_status()); td::actor::send_closure(collation_manager_, &CollationManager::collate_block, params, source_info.priority, - max_answer_size, std::move(cancellation_token), std::move(promise), - config_.proto_version); + max_answer_size, std::move(cancellation_token), std::move(promise)); }); } @@ -430,8 +429,7 @@ void ValidatorGroup::generate_block_optimistic(validatorsession::BlockSourceInfo TRY_STATUS_PROMISE(promise, token.check()); TRY_STATUS_PROMISE(promise, R.move_as_status()); td::actor::send_closure(collation_manager_, &CollationManager::collate_block, std::move(params), - source_info.priority, max_answer_size, std::move(token), std::move(promise), - config_.proto_version); + source_info.priority, max_answer_size, std::move(token), std::move(promise)); }); } @@ -776,9 +774,13 @@ void ValidatorGroup::get_validator_group_info_for_litequery_cont( void ValidatorGroup::send_block_candidate_broadcast(BlockIdExt id, td::BufferSlice data, td::BufferSlice collated_data) { if (sent_block_candidate_broadcasts_.insert(id).second) { + td::optional o_collated_data; + if (merge_collated_data_enabled_) { + o_collated_data = std::move(collated_data); + } td::actor::send_closure(manager_, &ValidatorManager::send_block_candidate_broadcast, id, validator_set_->get_catchain_seqno(), validator_set_->get_validator_set_hash(), - std::move(data), std::move(collated_data), + std::move(data), std::move(o_collated_data), fullnode::FullNode::broadcast_mode_fast_sync | fullnode::FullNode::broadcast_mode_custom); } } From 655d7e6d1b80e4de59628c11c33ec6a8cfdd9798 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 30 Sep 2025 17:42:20 +0300 Subject: [PATCH 04/13] Fix description of del-fast-sync-overlay-client query --- validator-engine-console/validator-engine-console-query.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator-engine-console/validator-engine-console-query.h b/validator-engine-console/validator-engine-console-query.h index c06924d9a..4e91ca9da 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -1738,7 +1738,7 @@ class DelFastSyncOverlayClientQuery : public Query { return "del-fast-sync-overlay-client"; } static std::string get_help() { - return "del-fast-sync-overlay-client \tstops issuing member certificates to (hex)"; + return "del-fast-sync-overlay-client \tstops issuing member certificates to (hex)"; } std::string name() const override { return get_name(); From 6308fb7fad541ab599631aaf8e5ea04de5e1d0c3 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 30 Sep 2025 23:21:31 +0300 Subject: [PATCH 05/13] Support optimistic collation/validation --- tdutils/td/utils/Status.h | 4 +- validator-session/validator-session.cpp | 24 +++---- validator-session/validator-session.h | 4 +- validator/collation-manager.cpp | 31 +++++---- validator/collation-manager.hpp | 1 + .../collator-node/collator-node-session.cpp | 67 +++++++++++++----- .../collator-node/collator-node-session.hpp | 6 +- validator/collator-node/collator-node.cpp | 3 +- validator/fabric.h | 19 ++++++ validator/impl/collator-impl.h | 2 + validator/impl/collator.cpp | 19 ++++-- validator/impl/validate-query.cpp | 8 ++- validator/impl/validate-query.hpp | 1 + validator/validator-group.cpp | 68 +++++++++++-------- validator/validator-group.hpp | 3 +- 15 files changed, 173 insertions(+), 87 deletions(-) diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index f75de466a..e22cece4e 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -74,7 +74,7 @@ #define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(r_response, __LINE__), name, result) #define TRY_RESULT_PROMISE_ASSIGN(promise_name, name, result) \ - TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result) + TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(r_response, __LINE__), name, result) #define TRY_RESULT_PREFIX(name, result, prefix) \ TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix) @@ -86,7 +86,7 @@ TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix) #define TRY_RESULT_PROMISE_PREFIX_ASSIGN(promise_name, name, result, prefix) \ - TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix) + TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(r_response, __LINE__), name, result, prefix) #define TRY_RESULT_IMPL(r_name, name, result) \ auto r_name = (result); \ diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index deb6d3b23..15cdcb1be 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -1578,18 +1578,18 @@ void ValidatorSessionImpl::generate_block_optimistic(td::uint32 cur_round, if (!stat) { return; } - callback_->generate_block_optimistic(BlockSourceInfo{description().get_source_public_key(local_idx()), - BlockCandidatePriority{cur_round + 1, cur_round + 1, 0}}, - block->data_.clone(), block->root_hash_, stat->block_id.file_hash, - [=, SelfId = actor_id(this)](td::Result R) { - if (R.is_error()) { - LOG(DEBUG) << "Optimistic generation error: " << R.move_as_error(); - return; - } - td::actor::send_closure(SelfId, - &ValidatorSessionImpl::generated_optimistic_candidate, - cur_round + 1, R.move_as_ok(), prev_candidate_id); - }); + callback_->generate_block_optimistic( + BlockSourceInfo{description().get_source_public_key(local_idx()), + BlockCandidatePriority{cur_round + 1, cur_round + 1, 0}}, + block->data_.clone(), block->collated_data_.clone(), block->root_hash_, stat->block_id.file_hash, + [=, SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + LOG(DEBUG) << "Optimistic generation error: " << R.move_as_error(); + return; + } + td::actor::send_closure(SelfId, &ValidatorSessionImpl::generated_optimistic_candidate, cur_round + 1, + R.move_as_ok(), prev_candidate_id); + }); } void ValidatorSessionImpl::generated_optimistic_candidate(td::uint32 round, GeneratedCandidate candidate, diff --git a/validator-session/validator-session.h b/validator-session/validator-session.h index c81cbe2f5..80f614719 100644 --- a/validator-session/validator-session.h +++ b/validator-session/validator-session.h @@ -95,8 +95,8 @@ class ValidatorSession : public td::actor::Actor { ValidatorSessionCollatedDataFileHash collated_data_file_hash, td::Promise promise) = 0; virtual void generate_block_optimistic(BlockSourceInfo source_info, td::BufferSlice prev_block, - RootHash prev_root_hash, FileHash prev_file_hash, - td::Promise promise) { + td::BufferSlice prev_collated_data, RootHash prev_root_hash, + FileHash prev_file_hash, td::Promise promise) { } virtual void on_optimistic_candidate(BlockSourceInfo source_info, ValidatorSessionRootHash root_hash, td::BufferSlice data, td::BufferSlice collated_data, PublicKey prev_source, diff --git a/validator/collation-manager.cpp b/validator/collation-manager.cpp index fcdecffea..74a912671 100644 --- a/validator/collation-manager.cpp +++ b/validator/collation-manager.cpp @@ -159,7 +159,7 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP next_block_id.seqno = std::max(next_block_id.seqno, p.seqno() + 1); } - td::Promise P = [=, SelfId = actor_id(this), promise = std::move(promise), + td::Promise P = [=, SelfId = actor_id(this), promise = std::move(promise), params = params.clone(), retry_at = td::Timestamp::in(0.5)](td::Result R) mutable { if (R.is_ok()) { promise.set_value(GeneratedCandidate{.candidate = R.move_as_ok(), @@ -178,9 +178,9 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP return; } delay_action( - [=, promise = std::move(promise)]() mutable { - td::actor::send_closure(SelfId, &CollationManager::collate_shard_block, params, priority, max_answer_size, - cancellation_token, std::move(promise), timeout); + [=, promise = std::move(promise), params = std::move(params)]() mutable { + td::actor::send_closure(SelfId, &CollationManager::collate_shard_block, std::move(params), priority, + max_answer_size, cancellation_token, std::move(promise), timeout); }, retry_at); }; @@ -203,7 +203,7 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP LOG(INFO) << "sending collate query for " << next_block_id.to_str() << ": send to #" << selected_idx << "(" << selected_collator << ")"; - td::Promise P2 = [=, SelfId = actor_id(this), P = std::move(P), + td::Promise P2 = [=, SelfId = actor_id(this), P = std::move(P), creator = params.creator, timer = td::Timer()](td::Result R) mutable { TRY_RESULT_PROMISE_PREFIX(P, data, std::move(R), "rldp query failed: "); auto r_error = fetch_tl_object(data, true); @@ -214,7 +214,7 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP } TRY_RESULT_PROMISE(P, f, fetch_tl_object(data, true)); TRY_RESULT_PROMISE(P, candidate, deserialize_candidate(std::move(f), td::narrow_cast(max_answer_size))); - if (candidate.pubkey.as_bits256() != params.creator.as_bits256()) { + if (candidate.pubkey.as_bits256() != creator.as_bits256()) { P.set_error(td::Status::Error("collate query: block candidate source mismatch")); return; } @@ -231,6 +231,7 @@ void CollationManager::collate_shard_block(CollateParams params, BlockCandidateP BlockIdExt prev_block_id = params.optimistic_prev_block->block_id(); auto& entry = optimistic_prev_cache_[prev_block_id]; entry.block_data = params.optimistic_prev_block->data().clone(); + entry.collated_data = params.optimistic_prev_collated_data.clone(); ++entry.refcnt; P2 = [this, SelfId = actor_id(this), prev_block_id, P2 = std::move(P2)](td::Result R) mutable { P2.set_result(std::move(R)); @@ -479,18 +480,22 @@ void CollationManager::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice } TRY_RESULT_PROMISE(promise, query, fetch_tl_object(data, true)); BlockIdExt block_id = create_block_id(query->block_id_); + bool with_collated_data = query->flags_ & 1; auto it = optimistic_prev_cache_.find(block_id); if (it == optimistic_prev_cache_.end()) { - LOG(INFO) << "collatorNode.requestBlockCallback from " << src << " block " << block_id.to_str() << " : not found"; + LOG(INFO) << "collatorNode.requestBlockCallback from " << src << " block " << block_id.to_str() << " (" + << (with_collated_data ? "with" : "no") << " cdata) : not found"; promise.set_error(td::Status::Error("block not found")); return; } - LOG(INFO) << "collatorNode.requestBlockCallback from " << src << " block " << block_id.to_str() << " : OK"; - promise.set_value( - serialize_tl_object(serialize_candidate(BlockCandidate(Ed25519_PublicKey{td::Bits256::zero()}, block_id, - td::Bits256::zero(), it->second.block_data.clone(), {}), - true), - true)); + LOG(INFO) << "collatorNode.requestBlockCallback from " << src << " block " << block_id.to_str() << " (" + << (with_collated_data ? "with" : "no") << " cdata) : OK"; + promise.set_value(serialize_tl_object( + serialize_candidate(BlockCandidate(Ed25519_PublicKey{td::Bits256::zero()}, block_id, td::Bits256::zero(), + it->second.block_data.clone(), + with_collated_data ? it->second.collated_data.clone() : td::BufferSlice{}), + true), + true)); } } // namespace ton::validator diff --git a/validator/collation-manager.hpp b/validator/collation-manager.hpp index 67faf5fe4..446c11f60 100644 --- a/validator/collation-manager.hpp +++ b/validator/collation-manager.hpp @@ -96,6 +96,7 @@ class CollationManager : public td::actor::Actor { struct OptimisticPrevCache { td::BufferSlice block_data; + td::BufferSlice collated_data; size_t refcnt = 0; }; std::map optimistic_prev_cache_; diff --git a/validator/collator-node/collator-node-session.cpp b/validator/collator-node/collator-node-session.cpp index 460a3fef5..c892dd01a 100644 --- a/validator/collator-node/collator-node-session.cpp +++ b/validator/collator-node/collator-node-session.cpp @@ -62,7 +62,7 @@ void CollatorNodeSession::start_up() { collated_data_deduplicator_ = std::make_shared(); } if (can_generate_) { - generate_block(prev_, {}, {}, td::Timestamp::in(10.0), [](td::Result) {}); + generate_block(prev_, {}, {}, {}, td::Timestamp::in(10.0), [](td::Result) {}); } } @@ -72,6 +72,11 @@ void CollatorNodeSession::tear_down() { for (auto& [_, entry] : cache_) { entry->cancel(td::Status::Error("validator session finished")); } + for (auto& [_, vec] : collated_data_merged_waiters_) { + for (auto& [promise, _] : vec) { + promise.set_error(td::Status::Error("validator session finished")); + } + } } void CollatorNodeSession::new_shard_block_accepted(BlockIdExt block_id, bool can_generate) { @@ -114,7 +119,7 @@ void CollatorNodeSession::new_shard_block_accepted(BlockIdExt block_id, bool can try_merge_collated_data(block_id); if (can_generate_) { - generate_block(prev_, {}, {}, td::Timestamp::in(10.0), [](td::Result) {}); + generate_block(prev_, {}, {}, {}, td::Timestamp::in(10.0), [](td::Result) {}); } } @@ -143,7 +148,8 @@ void CollatorNodeSession::update_masterchain_config(td::Ref st void CollatorNodeSession::generate_block(std::vector prev_blocks, td::optional o_priority, - td::Ref o_optimistic_prev_block, td::Timestamp timeout, + td::Ref o_optimistic_prev_block, + td::BufferSlice o_optimistic_prev_collated_data, td::Timestamp timeout, td::Promise promise) { bool is_external = !o_priority; bool is_optimistic = o_optimistic_prev_block.not_null(); @@ -242,6 +248,7 @@ void CollatorNodeSession::generate_block(std::vector prev_blocks, .collator_node_id = local_id_, .skip_store_candidate = true, .optimistic_prev_block = o_optimistic_prev_block, + .optimistic_prev_collated_data = std::move(o_optimistic_prev_collated_data), .collated_data_deduplicator = collated_data_deduplicator_}; auto token = cache_entry->cancellation_token_source.get_cancellation_token(); wait_collated_data_merged( @@ -295,26 +302,33 @@ void CollatorNodeSession::process_request(adnl::AdnlNodeIdShort src, std::vector if (it == cache_.end() || it->second->started) { BlockIdExt prev_block = prev_blocks[0]; td::actor::send_closure( - manager_, &ValidatorManager::get_candidate_data_by_block_id_from_db, prev_block, - [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + manager_, &ValidatorManager::get_block_candidate_by_block_id_from_db, prev_block, + [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + td::Result> res; + if (R.is_error()) { + res = R.move_as_error(); + } else { + BlockCandidate c = R.move_as_ok(); + res = std::make_pair(std::move(c.data), std::move(c.collated_data)); + } td::actor::send_closure(SelfId, &CollatorNodeSession::process_request_optimistic_cont, src, prev_block, - priority, timeout, std::move(promise), std::move(R)); + priority, timeout, std::move(promise), std::move(res)); }); return; } } - generate_block(std::move(prev_blocks), priority, {}, timeout, std::move(promise)); + generate_block(std::move(prev_blocks), priority, {}, {}, timeout, std::move(promise)); } -void CollatorNodeSession::process_request_optimistic_cont(adnl::AdnlNodeIdShort src, BlockIdExt prev_block_id, - BlockCandidatePriority priority, td::Timestamp timeout, - td::Promise promise, - td::Result prev_block_data) { - if (prev_block_data.is_ok()) { - TRY_RESULT_PROMISE_PREFIX(promise, prev_block, create_block(prev_block_id, prev_block_data.move_as_ok()), +void CollatorNodeSession::process_request_optimistic_cont( + adnl::AdnlNodeIdShort src, BlockIdExt prev_block_id, BlockCandidatePriority priority, td::Timestamp timeout, + td::Promise promise, td::Result> prev_candidate) { + if (prev_candidate.is_ok()) { + auto [prev_block_data, prev_collated_data] = prev_candidate.move_as_ok(); + TRY_RESULT_PROMISE_PREFIX(promise, prev_block, create_block(prev_block_id, std::move(prev_block_data)), "invalid prev block data in db: "); LOG(INFO) << "got prev block from db for optimistic collation: " << prev_block_id.to_str(); - generate_block({prev_block_id}, priority, prev_block, timeout, std::move(promise)); + generate_block({prev_block_id}, priority, prev_block, std::move(prev_collated_data), timeout, std::move(promise)); return; } td::actor::send_closure( @@ -324,7 +338,8 @@ void CollatorNodeSession::process_request_optimistic_cont(adnl::AdnlNodeIdShort timeout, std::move(promise), std::move(R)); }, timeout, - create_serialize_tl_object(0, create_tl_block_id(prev_block_id)), + create_serialize_tl_object(merge_collated_data_enabled_ ? 1 : 0, + create_tl_block_id(prev_block_id)), max_candidate_size_); } @@ -335,13 +350,29 @@ void CollatorNodeSession::process_request_optimistic_cont2(BlockIdExt prev_block "failed to download prev block data for optimistic collation: "); TRY_RESULT_PROMISE_PREFIX(promise, f, fetch_tl_object(response, true), "failed to download prev block data for optimistic collation: "); - TRY_RESULT_PROMISE_PREFIX(promise, candidate, - deserialize_candidate(std::move(f), max_candidate_size_), + TRY_RESULT_PROMISE_PREFIX(promise, candidate, deserialize_candidate(std::move(f), max_candidate_size_), "failed to download prev block data for optimistic collation: "); TRY_RESULT_PROMISE_PREFIX(promise, prev_block, create_block(prev_block_id, std::move(candidate.data)), "invalid prev block data from validator: "); + if (merge_collated_data_enabled_) { + block::gen::Block::Record rec; + block::gen::BlockInfo::Record info; + if (!block::gen::unpack_cell(prev_block->root_cell(), rec) || !block::gen::unpack_cell(rec.info, info)) { + promise.set_error(td::Status::Error("failed to unpack prev block header")); + return; + } + if (info.flags & 2) { + FileHash stored_collated_data_hash; + info.collated_data_hash->prefetch_bits_to(stored_collated_data_hash); + if (stored_collated_data_hash != candidate.collated_file_hash) { + promise.set_error(td::Status::Error("collated data hash mismatch")); + return; + } + } + } LOG(INFO) << "got prev block from validator for optimistic collation: " << prev_block_id.to_str(); - generate_block({prev_block_id}, priority, prev_block, timeout, std::move(promise)); + generate_block({prev_block_id}, priority, prev_block, std::move(candidate.collated_data), timeout, + std::move(promise)); } void CollatorNodeSession::CacheEntry::cancel(td::Status reason) { diff --git a/validator/collator-node/collator-node-session.hpp b/validator/collator-node/collator-node-session.hpp index c5c055fd8..ca00e86d6 100644 --- a/validator/collator-node/collator-node-session.hpp +++ b/validator/collator-node/collator-node-session.hpp @@ -82,14 +82,14 @@ class CollatorNodeSession : public td::actor::Actor { td::uint32 max_candidate_size_ = 0; void generate_block(std::vector prev_blocks, td::optional o_priority, - td::Ref o_optimistic_prev_block, td::Timestamp timeout, - td::Promise promise); + td::Ref o_optimistic_prev_block, td::BufferSlice o_optimistic_prev_collated_data, + td::Timestamp timeout, td::Promise promise); void process_result(std::shared_ptr cache_entry, td::Result R); void process_request_optimistic_cont(adnl::AdnlNodeIdShort src, BlockIdExt prev_block_id, BlockCandidatePriority priority, td::Timestamp timeout, td::Promise promise, - td::Result prev_block_data); + td::Result> prev_candidate); void process_request_optimistic_cont2(BlockIdExt prev_block_id, BlockCandidatePriority priority, td::Timestamp timeout, td::Promise promise, td::Result R); diff --git a/validator/collator-node/collator-node.cpp b/validator/collator-node/collator-node.cpp index 8ba8d60e1..0cca7224b 100644 --- a/validator/collator-node/collator-node.cpp +++ b/validator/collator-node/collator-node.cpp @@ -193,7 +193,8 @@ void CollatorNode::new_masterchain_block_notification(td::Ref } } for (BlockCandidate& candidate : future_group.pending_candidate_broadcasts) { - td::actor::send_closure(it->second.actor, &CollatorNodeSession::on_block_candidate_broadcast, std::move(candidate)); + td::actor::send_closure(it->second.actor, &CollatorNodeSession::on_block_candidate_broadcast, + std::move(candidate)); } for (auto& promise : future_group.promises) { promise.set_value(td::Unit()); diff --git a/validator/fabric.h b/validator/fabric.h index dc1abfced..712add4b5 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -22,6 +22,7 @@ #include "interfaces/validator-manager.h" #include "interfaces/db.h" #include "validator.h" +#include "vm/fmt.hpp" namespace ton { @@ -41,8 +42,25 @@ struct CollateParams { // Optional - used for optimistic collation Ref optimistic_prev_block = {}; + td::BufferSlice optimistic_prev_collated_data = {}; std::shared_ptr collated_data_deduplicator = {}; + + CollateParams clone() const { + return {.shard = shard, + .min_masterchain_block_id = min_masterchain_block_id, + .prev = prev, + .is_hardfork = is_hardfork, + .creator = creator, + .validator_set = validator_set, + .collator_opts = collator_opts, + .collator_node_id = collator_node_id, + .skip_store_candidate = skip_store_candidate, + .attempt_idx = attempt_idx, + .optimistic_prev_block = optimistic_prev_block, + .optimistic_prev_collated_data = optimistic_prev_collated_data.clone(), + .collated_data_deduplicator = collated_data_deduplicator}; + } }; struct ValidateParams { @@ -55,6 +73,7 @@ struct ValidateParams { // Optional - used for validation of optimistic candidates Ref optimistic_prev_block = {}; + td::BufferSlice optimistic_prev_collated_data = {}; td::actor::ActorId collated_data_merger = {}; }; diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index b40137362..2c3e6c1aa 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -85,7 +85,9 @@ class Collator final : public td::actor::Actor { adnl::AdnlNodeIdShort collator_node_id_ = adnl::AdnlNodeIdShort::zero(); bool skip_store_candidate_ = false; Ref optimistic_prev_block_; + td::BufferSlice optimistic_prev_collated_data_; std::shared_ptr collated_data_deduplicator_; + std::shared_ptr collated_data_deduplicator_local_; int attempt_idx_; bool allow_repeat_collation_ = false; ton::BlockSeqno last_block_seqno{0}; diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index c60ae5f0c..265e7eed1 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -83,6 +83,7 @@ Collator::Collator(CollateParams params, td::actor::ActorId ma , collator_node_id_(params.collator_node_id) , skip_store_candidate_(params.skip_store_candidate) , optimistic_prev_block_(std::move(params.optimistic_prev_block)) + , optimistic_prev_collated_data_(std::move(params.optimistic_prev_collated_data)) , collated_data_deduplicator_(std::move(params.collated_data_deduplicator)) , attempt_idx_(params.attempt_idx) , perf_timer_("collate", 0.1, @@ -846,6 +847,11 @@ void Collator::after_get_shard_state_optimistic(td::Result> res, fatal_error(S.move_as_error_prefix("apply error: ")); return; } + if (collated_data_deduplicator_) { + collated_data_deduplicator_local_ = std::make_shared(); + collated_data_deduplicator_local_->add_block_candidate( + optimistic_prev_block_->block_id().seqno(), optimistic_prev_block_->data(), optimistic_prev_collated_data_); + } work_timer_.pause(); stats_.work_time.optimistic_apply = timer.elapsed_both(); after_get_shard_state(0, std::move(state), {}); @@ -6776,10 +6782,15 @@ void Collator::on_cell_loaded(const vm::LoadedCell& loaded_cell) { if (stop_cell_load_processing_) { return; } - if (merge_collated_data_enabled_ && collated_data_deduplicator_ && - collated_data_deduplicator_->cell_exists(loaded_cell.data_cell->get_hash(loaded_cell.virt.get_level()), - new_block_seqno)) { - return; + if (merge_collated_data_enabled_) { + vm::CellHash hash = loaded_cell.data_cell->get_hash(loaded_cell.virt.get_level()); + if (collated_data_deduplicator_ && + collated_data_deduplicator_->cell_exists(hash, new_block_seqno - (optimistic_prev_block_.is_null() ? 0 : 1))) { + return; + } + if (collated_data_deduplicator_local_ && collated_data_deduplicator_local_->cell_exists(hash, new_block_seqno)) { + return; + } } auto context = block::StorageStatCalculationContext::get(); vm::ProofStorageStat* stat = (context && context->calculating_storage_stat() && current_tx_storage_dict_ diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index c6fb76131..32a1cb2ed 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -80,8 +80,9 @@ ValidateQuery::ValidateQuery(BlockCandidate candidate, ValidateParams params, , main_promise(std::move(promise)) , is_fake_(params.is_fake) , shard_pfx_(shard_.shard) - , shard_pfx_len_(ton::shard_prefix_length(shard_)) + , shard_pfx_len_(shard_prefix_length(shard_)) , optimistic_prev_block_(std::move(params.optimistic_prev_block)) + , optimistic_prev_collated_data_(std::move(params.optimistic_prev_collated_data)) , collated_data_merger_(std::move(params.collated_data_merger)) , merge_collated_data_enabled_(!collated_data_merger_.empty()) , perf_timer_("validateblock", 0.1, [manager](double duration) { @@ -839,6 +840,11 @@ bool ValidateQuery::extract_collated_data() { LOG(DEBUG) << "sending add_block_candidate() to CollatedDataMerger"; td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate, id_, block_root_, collated_roots_); + if (optimistic_prev_block_.not_null()) { + LOG(DEBUG) << "sending add_block_candidate() for optimistic prev block to CollatedDataMerger"; + td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate_data, id_, + optimistic_prev_block_->data(), optimistic_prev_collated_data_.clone()); + } std::vector hashes = collated_data_root_state_hashes_; hashes.insert(hashes.end(), collated_data_root_dict_hashes_.begin(), collated_data_root_dict_hashes_.end()); if (!hashes.empty()) { diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index 517e90c2f..6de046a92 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -156,6 +156,7 @@ class ValidateQuery : public td::actor::Actor { int shard_pfx_len_; td::Bits256 created_by_; Ref optimistic_prev_block_; + td::BufferSlice optimistic_prev_collated_data_; Ref prev_state_root_; Ref state_root_; diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index 2567d39b8..329ab4f7b 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -110,8 +110,9 @@ void ValidatorGroup::generate_block_candidate_cont(validatorsession::BlockSource [=, this, params = std::move(params), promise = std::move(promise)](td::Result R) mutable { TRY_STATUS_PROMISE(promise, cancellation_token.check()); TRY_STATUS_PROMISE(promise, R.move_as_status()); - td::actor::send_closure(collation_manager_, &CollationManager::collate_block, params, source_info.priority, - max_answer_size, std::move(cancellation_token), std::move(promise)); + td::actor::send_closure(collation_manager_, &CollationManager::collate_block, std::move(params), + source_info.priority, max_answer_size, std::move(cancellation_token), + std::move(promise)); }); } @@ -248,27 +249,26 @@ void ValidatorGroup::validate_block_candidate(validatorsession::BlockSourceInfo return; } VLOG(VALIDATOR_DEBUG) << "validating block candidate " << next_block_id; - Ref optimistic_prev_block_data; - if (is_optimistic) { - TRY_RESULT_PROMISE_PREFIX_ASSIGN( - P, optimistic_prev_block_data, - create_block(optimistic_prev_block.value().id, std::move(optimistic_prev_block.value().data)), - "failed to parse optimistic prev block: "); - } - ValidateParams params{.shard = shard_, .min_masterchain_block_id = min_masterchain_block_id_, .prev = std::move(prev), .validator_set = validator_set_, .local_validator_id = local_id_, - .optimistic_prev_block = optimistic_prev_block_data, .collated_data_merger = collated_data_merger_.get()}; - wait_collated_data_merged( - next_block_id.seqno(), [block = std::move(block), params = std::move(params), manager = manager_, - P = std::move(P)](td::Result R) mutable { - TRY_STATUS_PROMISE(P, R.move_as_status()); - run_validate_query(std::move(block), std::move(params), manager, td::Timestamp::in(15.0), std::move(P)); - }); + if (is_optimistic) { + TRY_RESULT_PROMISE_PREFIX_ASSIGN( + P, params.optimistic_prev_block, + create_block(optimistic_prev_block.value().id, std::move(optimistic_prev_block.value().data)), + "failed to parse optimistic prev block: "); + params.optimistic_prev_collated_data = std::move(optimistic_prev_block.value().collated_data); + } + wait_collated_data_merged(next_block_id.seqno() - (is_optimistic ? 1 : 0), + [block = std::move(block), params = std::move(params), manager = manager_, + P = std::move(P)](td::Result R) mutable { + TRY_STATUS_PROMISE(P, R.move_as_status()); + run_validate_query(std::move(block), std::move(params), manager, td::Timestamp::in(15.0), + std::move(P)); + }); } void ValidatorGroup::update_approve_cache(CacheKey key, UnixTime value) { @@ -385,8 +385,9 @@ void ValidatorGroup::get_approved_candidate(PublicKey source, RootHash root_hash } void ValidatorGroup::generate_block_optimistic(validatorsession::BlockSourceInfo source_info, - td::BufferSlice prev_block, RootHash prev_root_hash, - FileHash prev_file_hash, td::Promise promise) { + td::BufferSlice prev_block, td::BufferSlice prev_collated_data, + RootHash prev_root_hash, FileHash prev_file_hash, + td::Promise promise) { if (destroying_) { promise.set_error(td::Status::Error("validator session finished")); return; @@ -403,7 +404,7 @@ void ValidatorGroup::generate_block_optimistic(validatorsession::BlockSourceInfo promise.set_error(td::Status::Error("optimistic generation already in progress")); return; } - BlockIdExt block_id{create_next_block_id_simple(), prev_root_hash, prev_file_hash}; + BlockIdExt prev_block_id{create_next_block_id_simple(), prev_root_hash, prev_file_hash}; optimistic_generation_ = std::make_unique(); optimistic_generation_->round = source_info.priority.round; optimistic_generation_->prev = BlockIdExt{create_next_block_id_simple(), prev_root_hash, prev_file_hash}; @@ -412,24 +413,25 @@ void ValidatorGroup::generate_block_optimistic(validatorsession::BlockSourceInfo td::Promise P = [=, SelfId = actor_id(this)](td::Result R) { td::actor::send_closure(SelfId, &ValidatorGroup::generated_block_optimistic, source_info, std::move(R)); }; - LOG(WARNING) << "Optimistically generating next block after " << block_id.to_str(); + LOG(WARNING) << "Optimistically generating next block after " << prev_block_id.to_str(); td::uint64 max_answer_size = config_.max_block_size + config_.max_collated_data_size + 1024; - TRY_RESULT_PROMISE(promise, prev_block_data, create_block(block_id, std::move(prev_block))); + TRY_RESULT_PROMISE(P, prev_block_data, create_block(prev_block_id, std::move(prev_block))); CollateParams params{.shard = shard_, .min_masterchain_block_id = min_masterchain_block_id_, - .prev = {block_id}, + .prev = {prev_block_id}, .creator = Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, .validator_set = validator_set_, .collator_opts = opts_->get_collator_options(), .optimistic_prev_block = prev_block_data, + .optimistic_prev_collated_data = std::move(prev_collated_data), .collated_data_deduplicator = collated_data_deduplicator_}; auto token = optimistic_generation_->cancellation_token_source.get_cancellation_token(); - wait_collated_data_merged(block_id.seqno(), [=, this, params = std::move(params), token = std::move(token), - promise = std::move(promise)](td::Result R) mutable { - TRY_STATUS_PROMISE(promise, token.check()); - TRY_STATUS_PROMISE(promise, R.move_as_status()); + wait_collated_data_merged(prev_block_id.seqno(), [=, this, params = std::move(params), token = std::move(token), + P = std::move(P)](td::Result R) mutable { + TRY_STATUS_PROMISE(P, token.check()); + TRY_STATUS_PROMISE(P, R.move_as_status()); td::actor::send_closure(collation_manager_, &CollationManager::collate_block, std::move(params), - source_info.priority, max_answer_size, std::move(token), std::move(promise)); + source_info.priority, max_answer_size, std::move(token), std::move(P)); }); } @@ -546,10 +548,10 @@ std::unique_ptr ValidatorGroup::ma collated_data_file_hash, std::move(promise)); } void generate_block_optimistic(validatorsession::BlockSourceInfo source_info, td::BufferSlice prev_block, - RootHash prev_root_hash, FileHash prev_file_hash, + td::BufferSlice prev_collated_data, RootHash prev_root_hash, FileHash prev_file_hash, td::Promise promise) override { td::actor::send_closure(id_, &ValidatorGroup::generate_block_optimistic, source_info, std::move(prev_block), - prev_root_hash, prev_file_hash, std::move(promise)); + std::move(prev_collated_data), prev_root_hash, prev_file_hash, std::move(promise)); } void on_optimistic_candidate(validatorsession::BlockSourceInfo source_info, validatorsession::ValidatorSessionRootHash root_hash, td::BufferSlice data, @@ -701,6 +703,12 @@ void ValidatorGroup::destroy() { }); } cancellation_token_source_.cancel(); + for (auto &[_, vec] : collated_data_merged_waiters_) { + for (auto &promise : vec) { + promise.set_error(td::Status::Error("validator session finished")); + } + } + collated_data_merged_waiters_.clear(); delay_action([SelfId = actor_id(this)]() { td::actor::send_closure(SelfId, &ValidatorGroup::destroy_cont); }, td::Timestamp::in(10.0)); } diff --git a/validator/validator-group.hpp b/validator/validator-group.hpp index f9eb7228f..6d80dcafa 100644 --- a/validator/validator-group.hpp +++ b/validator/validator-group.hpp @@ -58,7 +58,8 @@ class ValidatorGroup : public td::actor::Actor { BlockId create_next_block_id_simple() const; void generate_block_optimistic(validatorsession::BlockSourceInfo source_info, td::BufferSlice prev_block, - RootHash prev_root_hash, FileHash prev_file_hash, td::Promise promise); + td::BufferSlice prev_collated_data, RootHash prev_root_hash, FileHash prev_file_hash, + td::Promise promise); void generated_block_optimistic(validatorsession::BlockSourceInfo source_info, td::Result R); void start(std::vector prev, BlockIdExt min_masterchain_block_id); From e3c1a7dd30ba13e380ef5dac0cce815e7efce138 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 10 Oct 2025 15:12:35 +0300 Subject: [PATCH 06/13] Add check to collated data builder --- crypto/vm/boc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index 3c1f29cff..872e95dc2 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -1369,6 +1369,7 @@ std::vector> ProofStorageStat::build_collated_data(std::vectoris_special()); + CHECK(entry2.result->get_hash(std::min(info.cell->get_level(), info.cell_max_level)) == hash); return entry2; }; for (auto& [hash, info] : cells_) { From fec2fd0fd97d44d6aecc47ef368f013fdf90aabc Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 10 Oct 2025 16:56:34 +0300 Subject: [PATCH 07/13] Add some time stats --- validator/impl/collated-data-merger.cpp | 23 +++++++++----- validator/impl/collated-data-merger.h | 6 ++-- validator/impl/collator.cpp | 4 +++ validator/impl/validate-query.cpp | 38 ++++++++++++++---------- validator/interfaces/validator-manager.h | 10 +++++-- validator/validator-group.cpp | 2 +- 6 files changed, 55 insertions(+), 28 deletions(-) diff --git a/validator/impl/collated-data-merger.cpp b/validator/impl/collated-data-merger.cpp index c811e2580..29ce484d8 100644 --- a/validator/impl/collated-data-merger.cpp +++ b/validator/impl/collated-data-merger.cpp @@ -29,7 +29,8 @@ class CollatedDataMergerExtCellLoader { using CollatedDataMergerExtCell = vm::ExtCell; -void CollatedDataMerger::get_cells(std::vector hashes, td::Promise>> promise) { +void CollatedDataMerger::get_cells(std::vector hashes, + td::Promise>> promise) { td::HashMap> result; for (const vm::CellHash &hash : hashes) { auto it = cells_.find(hash); @@ -101,25 +102,33 @@ void CollatedDataMerger::add_cells(Ref cell) { } void CollatedDataMerger::add_block_candidate(BlockIdExt block_id, Ref root, - std::vector> collated_roots) { + std::vector> collated_roots, + td::Promise promise) { + td::RealCpuTimer timer; + SCOPE_EXIT { + promise.set_value(timer.elapsed_both()); + }; if (!blocks_.insert(block_id).second) { return; } - td::Timer timer; add_cells(std::move(root)); for (auto &c : collated_roots) { add_cells(std::move(c)); } - LOG(INFO) << "Added block " << block_id.to_str() << " in " << timer.elapsed() + LOG(INFO) << "Added block " << block_id.to_str() << " in " << timer.elapsed_real() << " s, total cells = " << cells_.size(); } void CollatedDataMerger::add_block_candidate_data(BlockIdExt block_id, td::BufferSlice data, - td::BufferSlice collated_data) { + td::BufferSlice collated_data, + td::Promise promise) { + td::RealCpuTimer timer; + SCOPE_EXIT { + promise.set_value(timer.elapsed_both()); + }; if (!blocks_.insert(block_id).second) { return; } - td::Timer timer; auto r_root = vm::std_boc_deserialize(data); if (r_root.is_error()) { LOG(WARNING) << "Failed to deserialize block data for " << block_id.to_str() << " : " << r_root.error(); @@ -135,7 +144,7 @@ void CollatedDataMerger::add_block_candidate_data(BlockIdExt block_id, td::Buffe for (auto &c : r_collated_roots.move_as_ok()) { add_cells(std::move(c)); } - LOG(INFO) << "Added block " << block_id.to_str() << " in " << timer.elapsed() << " s"; + LOG(INFO) << "Added block " << block_id.to_str() << " in " << timer.elapsed_real() << " s"; } void CollatedDataMerger::CellInfo::set_cell(const Ref &new_cell) { diff --git a/validator/impl/collated-data-merger.h b/validator/impl/collated-data-merger.h index 64ad59310..fcd66164e 100644 --- a/validator/impl/collated-data-merger.h +++ b/validator/impl/collated-data-merger.h @@ -34,8 +34,10 @@ class CollatedDataMerger : public td::actor::Actor { public: void get_cells(std::vector hashes, td::Promise>> promise); void add_cells(Ref cell); - void add_block_candidate(BlockIdExt block_id, Ref root, std::vector> collated_roots); - void add_block_candidate_data(BlockIdExt block_id, td::BufferSlice data, td::BufferSlice collated_data); + void add_block_candidate(BlockIdExt block_id, Ref root, std::vector> collated_roots, + td::Promise promise); + void add_block_candidate_data(BlockIdExt block_id, td::BufferSlice data, td::BufferSlice collated_data, + td::Promise promise); private: struct CellInfo { diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 265e7eed1..274f07757 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -6782,6 +6782,10 @@ void Collator::on_cell_loaded(const vm::LoadedCell& loaded_cell) { if (stop_cell_load_processing_) { return; } + td::RealCpuTimer timer; + SCOPE_EXIT { + stats_.work_time.total_on_cell_loaded += timer.elapsed_both(); + }; if (merge_collated_data_enabled_) { vm::CellHash hash = loaded_cell.data_cell->get_hash(loaded_cell.virt.get_level()); if (collated_data_deduplicator_ && diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 32a1cb2ed..685bcdb11 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -837,28 +837,34 @@ bool ValidateQuery::extract_collated_data() { } if (merge_collated_data_enabled_) { LOG(INFO) << "merge_collated_data = true"; - LOG(DEBUG) << "sending add_block_candidate() to CollatedDataMerger"; - td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate, id_, block_root_, - collated_roots_); if (optimistic_prev_block_.not_null()) { LOG(DEBUG) << "sending add_block_candidate() for optimistic prev block to CollatedDataMerger"; td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate_data, id_, - optimistic_prev_block_->data(), optimistic_prev_collated_data_.clone()); + optimistic_prev_block_->data(), optimistic_prev_collated_data_.clone(), + [](td::Result) {}); } + LOG(DEBUG) << "sending add_block_candidate() to CollatedDataMerger"; + td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate, id_, block_root_, + collated_roots_, [SelfId = actor_id(this), this](td::Result R) { + if (R.is_error()) { + return; + } + td::actor::send_lambda(SelfId, [this, time = R.move_as_ok()]() { + stats_.work_time.ext_collated_data_merge += time; + }); + }); std::vector hashes = collated_data_root_state_hashes_; hashes.insert(hashes.end(), collated_data_root_dict_hashes_.begin(), collated_data_root_dict_hashes_.end()); - if (!hashes.empty()) { - LOG(DEBUG) << "sending get_cells to CollatedDataMerger"; - ++pending; - td::actor::send_closure_later( - collated_data_merger_, &CollatedDataMerger::get_cells, std::move(hashes), - [self = get_self(), token = perf_log_.start_action("CollatedDataMerger::get_cells")]( - td::Result>> res) mutable { - LOG(DEBUG) << "got answer to CollatedDataMerger::get_cells"; - td::actor::send_closure_later(std::move(self), &ValidateQuery::process_merged_collated_roots, - std::move(res), std::move(token)); - }); - } + LOG(DEBUG) << "sending get_cells to CollatedDataMerger"; + ++pending; + td::actor::send_closure_later( + collated_data_merger_, &CollatedDataMerger::get_cells, std::move(hashes), + [self = get_self(), token = perf_log_.start_action("CollatedDataMerger::get_cells")]( + td::Result>> res) mutable { + LOG(DEBUG) << "got answer to CollatedDataMerger::get_cells"; + td::actor::send_closure_later(std::move(self), &ValidateQuery::process_merged_collated_roots, + std::move(res), std::move(token)); + }); } return true; } diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index 823866df5..a29323b60 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -128,6 +128,8 @@ struct CollationStats { td::RealCpuTimer::Time create_collated_data; td::RealCpuTimer::Time create_block_candidate; + td::RealCpuTimer::Time total_on_cell_loaded; + std::string to_str(bool is_cpu) const { return PSTRING() << "total=" << total.get(is_cpu) << " optimistic_apply=" << optimistic_apply.get(is_cpu) << " queue_cleanup=" << queue_cleanup.get(is_cpu) @@ -137,7 +139,8 @@ struct CollationStats { << " final_storage_stat=" << final_storage_stat.get(is_cpu) << " create_block=" << create_block.get(is_cpu) << " create_collated_data=" << create_collated_data.get(is_cpu) - << " create_block_candidate=" << create_block_candidate.get(is_cpu); + << " create_block_candidate=" << create_block_candidate.get(is_cpu) + << " *total_on_cell_loaded=" << total_on_cell_loaded.get(is_cpu); } }; WorkTimeStats work_time; @@ -187,10 +190,13 @@ struct ValidationStats { td::RealCpuTimer::Time trx_storage_stat; td::RealCpuTimer::Time trx_other; + td::RealCpuTimer::Time ext_collated_data_merge; + std::string to_str(bool is_cpu) const { return PSTRING() << "total=" << total.get(is_cpu) << " optimistic_apply=" << optimistic_apply.get(is_cpu) << " trx_tvm=" << trx_tvm.get(is_cpu) << " trx_storage_stat=" << trx_storage_stat.get(is_cpu) - << " trx_other=" << trx_other.get(is_cpu); + << " trx_other=" << trx_other.get(is_cpu) + << " *ext_collated_data_merge=" << ext_collated_data_merge.get(is_cpu); } }; WorkTimeStats work_time; diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index 329ab4f7b..a92bc2a25 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -800,7 +800,7 @@ void ValidatorGroup::merge_collated_data(PublicKey source, BlockIdExt block_id, } if (!o_block_data.empty()) { td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate_data, block_id, - o_block_data.clone(), o_collated_data.clone()); + o_block_data.clone(), o_collated_data.clone(), [](td::Result) {}); collated_data_deduplicator_->add_block_candidate(block_id.seqno(), o_block_data.clone(), o_collated_data.clone()) .ensure(); collated_data_merged_.insert(block_id.seqno()); From be896b6094294cf67ddb3ca3a217772072ab6c24 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Sun, 12 Oct 2025 22:01:46 +0300 Subject: [PATCH 08/13] Add const max_collated_data_roots --- crypto/vm/boc-compression.cpp | 3 ++- ton/ton-types.h | 1 + validator-session/candidate-serializer.cpp | 4 ++-- validator/full-node-serializer.cpp | 7 ++++--- validator/impl/collated-data-merger.cpp | 4 ++-- validator/impl/collator.cpp | 3 +++ 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crypto/vm/boc-compression.cpp b/crypto/vm/boc-compression.cpp index 2559c0faf..c6beaf7ee 100644 --- a/crypto/vm/boc-compression.cpp +++ b/crypto/vm/boc-compression.cpp @@ -26,6 +26,7 @@ #include "vm/cellslice.h" #include "td/utils/Slice-decl.h" #include "td/utils/lz4.h" +#include "ton/ton-types.h" namespace vm { @@ -57,7 +58,7 @@ td::Result>> boc_decompress_baseline_lz4(td::Slice } TRY_RESULT(decompressed, td::lz4_decompress(compressed, decompressed_size)); - TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); + TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, ton::max_collated_data_roots + 1, true)); return roots; } diff --git a/ton/ton-types.h b/ton/ton-types.h index c05ac10d4..5633045e4 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -52,6 +52,7 @@ constexpr WorkchainId masterchainId = -1, basechainId = 0, workchainInvalid = 0x constexpr ShardId shardIdAll = (1ULL << 63); constexpr int max_shard_pfx_len = 60; +constexpr int max_collated_data_roots = 1000000; enum GlobalCapabilities { capIhrEnabled = 1, diff --git a/validator-session/candidate-serializer.cpp b/validator-session/candidate-serializer.cpp index 4336001d4..3a0f6d841 100644 --- a/validator-session/candidate-serializer.cpp +++ b/validator-session/candidate-serializer.cpp @@ -79,7 +79,7 @@ td::Result compress_candidate_data(td::Slice block, td::Slice c return td::Status::Error("block candidate should have exactly one root"); } std::vector> roots = {boc1.get_root_cell()}; - TRY_STATUS(boc2.deserialize(collated_data, 1000000)); + TRY_STATUS(boc2.deserialize(collated_data, max_collated_data_roots)); for (int i = 0; i < boc2.get_root_count(); ++i) { roots.push_back(boc2.get_root_cell(i)); } @@ -100,7 +100,7 @@ td::Result> decompress_candidate_dat if (decompressed.size() != (size_t)decompressed_size) { return td::Status::Error("decompressed size mismatch"); } - TRY_RESULT_ASSIGN(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); + TRY_RESULT_ASSIGN(roots, vm::std_boc_deserialize_multi(decompressed, max_collated_data_roots + 1, true)); } else { TRY_RESULT_ASSIGN(roots, vm::boc_decompress(compressed, max_decompressed_size)); } diff --git a/validator/full-node-serializer.cpp b/validator/full-node-serializer.cpp index 674f1c989..ed04e1c0a 100644 --- a/validator/full-node-serializer.cpp +++ b/validator/full-node-serializer.cpp @@ -153,7 +153,7 @@ static td::Status deserialize_block_full(ton_api::tonNode_dataFull& f, BlockIdEx static td::Status deserialize_block_full(ton_api::tonNode_dataFullCompressed& f, BlockIdExt& id, td::BufferSlice& proof, td::BufferSlice& data, bool& is_proof_link, int max_decompressed_size) { TRY_RESULT(decompressed, td::lz4_decompress(f.compressed_, max_decompressed_size)); - TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); + TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, max_collated_data_roots + 1, true)); if (roots.size() != 2) { return td::Status::Error("expected 2 roots in boc"); } @@ -203,7 +203,8 @@ td::Result serialize_block_candidate_broadcast(BlockIdExt block TRY_RESULT(root, vm::std_boc_deserialize(data)); std::vector> roots = {root}; if (collated_data) { - TRY_RESULT(collated_data_roots, vm::std_boc_deserialize_multi(collated_data.value(), 1000000, true)); + TRY_RESULT(collated_data_roots, + vm::std_boc_deserialize_multi(collated_data.value(), max_collated_data_roots, true)); roots.insert(roots.end(), collated_data_roots.begin(), collated_data_roots.end()); } TRY_RESULT(data_new, vm::std_boc_serialize_multi(std::move(roots), 2)); @@ -227,7 +228,7 @@ static td::Status deserialize_block_candidate_broadcast(ton_api::tonNode_blockCa cc_seqno = obj.catchain_seqno_; validator_set_hash = obj.validator_set_hash_; TRY_RESULT(decompressed, td::lz4_decompress(obj.compressed_, max_decompressed_data_size)); - TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, 1000000, true)); + TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed, max_collated_data_roots + 1, true)); if (roots.empty()) { return td::Status::Error("expected at least 1 root in boc"); } diff --git a/validator/impl/collated-data-merger.cpp b/validator/impl/collated-data-merger.cpp index 29ce484d8..9e90b571b 100644 --- a/validator/impl/collated-data-merger.cpp +++ b/validator/impl/collated-data-merger.cpp @@ -134,7 +134,7 @@ void CollatedDataMerger::add_block_candidate_data(BlockIdExt block_id, td::Buffe LOG(WARNING) << "Failed to deserialize block data for " << block_id.to_str() << " : " << r_root.error(); return; } - auto r_collated_roots = vm::std_boc_deserialize_multi(collated_data, 1000000, true); + auto r_collated_roots = vm::std_boc_deserialize_multi(collated_data, max_collated_data_roots, true); if (r_collated_roots.is_error()) { LOG(WARNING) << "Failed to deserialize collated data for " << block_id.to_str() << " : " << r_collated_roots.error(); @@ -165,7 +165,7 @@ td::Status CollatedDataDeduplicator::add_block_candidate(BlockSeqno seqno, td::S td::Slice collated_data) { td::Timer timer; TRY_RESULT(root, vm::std_boc_deserialize(block_data)); - TRY_RESULT(collated_roots, vm::std_boc_deserialize_multi(collated_data, 1000000, true)); + TRY_RESULT(collated_roots, vm::std_boc_deserialize_multi(collated_data, max_collated_data_roots, true)); std::lock_guard lock{mutex_}; td::HashSet visited; std::function &)> dfs = [&](const Ref &cell) { diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 274f07757..c5920c69b 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -6433,6 +6433,9 @@ bool Collator::create_collated_data() { } // 8. serialize collated data + if (collated_roots_.size() > max_collated_data_roots) { + return fatal_error(PSTRING() << "too many collated data roots: " << collated_roots_.size()); + } if (collated_roots_.empty()) { collated_data_ = td::BufferSlice{0}; } else { From 31b6cf39cd3fe047e7e938ecda8f53b5eeee5979 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Sun, 12 Oct 2025 22:07:52 +0300 Subject: [PATCH 09/13] Fix merging prev block collated data --- validator/impl/validate-query.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 685bcdb11..8d0a5574b 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -839,9 +839,9 @@ bool ValidateQuery::extract_collated_data() { LOG(INFO) << "merge_collated_data = true"; if (optimistic_prev_block_.not_null()) { LOG(DEBUG) << "sending add_block_candidate() for optimistic prev block to CollatedDataMerger"; - td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate_data, id_, - optimistic_prev_block_->data(), optimistic_prev_collated_data_.clone(), - [](td::Result) {}); + td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate_data, + optimistic_prev_block_->block_id(), optimistic_prev_block_->data(), + optimistic_prev_collated_data_.clone(), [](td::Result) {}); } LOG(DEBUG) << "sending add_block_candidate() to CollatedDataMerger"; td::actor::send_closure(collated_data_merger_, &CollatedDataMerger::add_block_candidate, id_, block_root_, From 3be416ef8d45683caaaece18c82285892f9d511d Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Thu, 6 Nov 2025 14:37:00 +0300 Subject: [PATCH 10/13] Fix processing applied blocks in CollatorNodeSession --- .../collator-node/collator-node-session.cpp | 51 +++++++++++++++++-- .../collator-node/collator-node-session.hpp | 5 ++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/validator/collator-node/collator-node-session.cpp b/validator/collator-node/collator-node-session.cpp index c892dd01a..03760b172 100644 --- a/validator/collator-node/collator-node-session.cpp +++ b/validator/collator-node/collator-node-session.cpp @@ -50,6 +50,7 @@ CollatorNodeSession::CollatorNodeSession(ShardIdFull shard, std::vector) {}); } @@ -401,6 +400,50 @@ void CollatorNodeSession::alarm() { } } +void CollatorNodeSession::process_accepted_block(BlockIdExt block_id) { + if (!accepted_blocks_.emplace(block_id.seqno(), block_id).second) { + return; + } + LOG(INFO) << "Accepted block " << block_id.to_str(); + try_merge_collated_data(block_id); + + if (accepted_blocks_.contains(block_id.seqno() - 1) || block_id.seqno() == first_block_seqno_) { + return; + } + LOG(INFO) << "Prev block for " << block_id.id.to_str() << " is not processed, waiting block data"; + process_accepted_block_cont(block_id); +} + +void CollatorNodeSession::process_accepted_block_cont(BlockIdExt block_id) { + td::actor::send_closure( + manager_, &ValidatorManager::wait_block_data_short, block_id, 0, td::Timestamp::in(30.0), + [SelfId = actor_id(this), block_id](td::Result> R) mutable { + if (R.is_error()) { + LOG(WARNING) << "Wait block data for #" << block_id.seqno() << ": " << R.error(); + td::actor::send_closure(SelfId, &CollatorNodeSession::process_accepted_block_cont, block_id); + } else { + td::actor::send_closure(SelfId, &CollatorNodeSession::process_accepted_block_cont2, R.move_as_ok()); + } + }); +} + +void CollatorNodeSession::process_accepted_block_cont2(Ref block) { + LOG(DEBUG) << "Wait block data for #" << block->block_id().seqno() << ": OK"; + std::vector prev; + BlockIdExt mc_block_id; + bool after_split; + if (!block::unpack_block_prev_blk(block->root_cell(), block->block_id(), prev, mc_block_id, after_split)) { + LOG(ERROR) << "Unpack block data for #" << block->block_id().seqno() << ": error"; + return; + } + if (prev.size() != 1) { + LOG(ERROR) << "Unpack block data for #" << block->block_id().seqno() << ": not single prev block"; + return; + } + process_accepted_block(prev[0]); +} + + void CollatorNodeSession::wait_collated_data_merged(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) { if (!merge_collated_data_enabled_ || collated_data_merged_upto_ >= seqno) { diff --git a/validator/collator-node/collator-node-session.hpp b/validator/collator-node/collator-node-session.hpp index ca00e86d6..d5fa4021b 100644 --- a/validator/collator-node/collator-node-session.hpp +++ b/validator/collator-node/collator-node-session.hpp @@ -76,6 +76,7 @@ class CollatorNodeSession : public td::actor::Actor { void cancel(td::Status reason); }; + BlockSeqno first_block_seqno_; BlockSeqno next_block_seqno_; std::map, std::shared_ptr> cache_; @@ -101,6 +102,10 @@ class CollatorNodeSession : public td::actor::Actor { BlockSeqno collated_data_merged_upto_ = 0; std::map, td::Timestamp>>> collated_data_merged_waiters_; + void process_accepted_block(BlockIdExt block_id); + void process_accepted_block_cont(BlockIdExt block_id); + void process_accepted_block_cont2(Ref block); + void wait_collated_data_merged(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise); void try_merge_collated_data(BlockIdExt block_id); void try_merge_collated_data_from_net(BlockIdExt block_id); From a345d37ab4b869d9223b768dd19609e60f69ad0a Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 7 Nov 2025 12:58:54 +0300 Subject: [PATCH 11/13] Reformat code --- crypto/vm/boc-compression.cpp | 24 ++--- crypto/vm/boc.cpp | 16 ++-- tdutils/td/utils/LRUCache.h | 1 - validator-session/candidate-serializer.cpp | 7 +- validator-session/validator-session.cpp | 90 +++++++++---------- validator-session/validator-session.h | 4 +- validator-session/validator-session.hpp | 4 +- validator/collation-manager.hpp | 6 +- .../collator-node/collator-node-session.cpp | 4 +- .../collator-node/collator-node-session.hpp | 5 +- validator/fabric.h | 13 +-- validator/full-node-shard.cpp | 3 +- validator/full-node.cpp | 43 ++++----- validator/full-node.hpp | 2 +- validator/impl/collated-data-merger.cpp | 4 +- validator/impl/collated-data-merger.h | 14 +-- validator/impl/collator-impl.h | 33 +++---- validator/impl/collator.cpp | 75 +++++++--------- validator/impl/validate-query.cpp | 16 ++-- validator/impl/validate-query.hpp | 20 +++-- validator/manager.cpp | 53 ++++++----- validator/validator-group.cpp | 14 +-- validator/validator-group.hpp | 11 ++- 23 files changed, 229 insertions(+), 233 deletions(-) diff --git a/crypto/vm/boc-compression.cpp b/crypto/vm/boc-compression.cpp index c6beaf7ee..7224c15e5 100644 --- a/crypto/vm/boc-compression.cpp +++ b/crypto/vm/boc-compression.cpp @@ -16,17 +16,18 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include "boc-compression.h" - #include #include -#include "vm/boc.h" -#include "vm/boc-writers.h" -#include "vm/cells.h" -#include "vm/cellslice.h" + #include "td/utils/Slice-decl.h" #include "td/utils/lz4.h" #include "ton/ton-types.h" +#include "vm/boc-writers.h" +#include "vm/boc.h" +#include "vm/cells.h" +#include "vm/cellslice.h" + +#include "boc-compression.h" namespace vm { @@ -43,7 +44,8 @@ td::Result boc_compress_baseline_lz4(const std::vector>> boc_decompress_baseline_lz4(td::Slice compressed, int max_decompressed_size) { +td::Result>> boc_decompress_baseline_lz4(td::Slice compressed, + int max_decompressed_size) { // Check minimum input size for decompressed size header if (compressed.size() < kDecompressedSizeBytes) { return td::Status::Error("BOC decompression failed: input too small for header"); @@ -284,7 +286,7 @@ td::Result boc_compress_improved_structure_lz4(const std::vecto if (rank[boc_graph[node][j]] <= i + 1) continue; - int delta = rank[boc_graph[node][j]] - i - 2; // Always >= 0 because of above check + int delta = rank[boc_graph[node][j]] - i - 2; // Always >= 0 because of above check size_t required_bits = 1 + (31 ^ td::count_leading_zeroes32(node_count - i - 3)); if (required_bits < 8 - (result.size() + 1) % 8 + 1) { @@ -341,7 +343,8 @@ td::Result boc_compress_improved_structure_lz4(const std::vecto return compressed_with_size; } -td::Result>> boc_decompress_improved_structure_lz4(td::Slice compressed, int max_decompressed_size) { +td::Result>> boc_decompress_improved_structure_lz4(td::Slice compressed, + int max_decompressed_size) { constexpr size_t kMaxCellDataLengthBits = 1024; // Check minimum input size for decompressed size header @@ -393,7 +396,6 @@ td::Result>> boc_decompress_improved_structure_lz4 return td::Status::Error("BOC decompression failed: incorrect node count provided"); } - // Validate root indexes for (int i = 0; i < root_count; ++i) { if (root_indexes[i] >= node_count) { @@ -597,7 +599,7 @@ td::Result boc_compress(const std::vector>& b } else if (algo == CompressionAlgorithm::ImprovedStructureLZ4) { TRY_RESULT_ASSIGN(compressed, boc_compress_improved_structure_lz4(boc_roots)); } else { - return td::Status::Error("Unknown compression algorithm"); + return td::Status::Error("Unknown compression algorithm"); } td::BufferSlice compressed_with_algo(compressed.size() + 1); diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index 15267e793..f6c443f39 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -16,21 +16,21 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include -#include #include -#include "vm/boc.h" +#include +#include #include "cells/MerkleProof.h" #include "cells/PrunnedCell.h" -#include "vm/boc-writers.h" -#include "vm/cells.h" -#include "vm/cellslice.h" +#include "td/utils/Slice-decl.h" #include "td/utils/bits.h" #include "td/utils/crypto.h" #include "td/utils/format.h" #include "td/utils/misc.h" -#include "td/utils/Slice-decl.h" +#include "vm/boc-writers.h" +#include "vm/boc.h" +#include "vm/cells.h" +#include "vm/cellslice.h" namespace vm { using td::Ref; @@ -1160,7 +1160,7 @@ td::Result CellStorageStat::add_used_storage(Ref CellStorageStat::add_used_storage(td::Span> cells, bool kill_dup, - unsigned skip_count_root) { + unsigned skip_count_root) { CellInfo result; for (const auto& cell : cells) { TRY_RESULT(info, add_used_storage(cell, kill_dup, skip_count_root)); diff --git a/tdutils/td/utils/LRUCache.h b/tdutils/td/utils/LRUCache.h index 73d02dcb2..5e0cd5035 100644 --- a/tdutils/td/utils/LRUCache.h +++ b/tdutils/td/utils/LRUCache.h @@ -97,7 +97,6 @@ class LRUCache { cache_.erase(it); } - private: struct Entry : ListNode { Entry(K key, uint64 weight) : key(std::move(key)), weight(weight) { diff --git a/validator-session/candidate-serializer.cpp b/validator-session/candidate-serializer.cpp index 3a0f6d841..4ac700f01 100644 --- a/validator-session/candidate-serializer.cpp +++ b/validator-session/candidate-serializer.cpp @@ -14,11 +14,12 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . */ -#include "candidate-serializer.h" -#include "tl-utils/tl-utils.hpp" -#include "vm/boc.h" #include "td/utils/lz4.h" +#include "tl-utils/tl-utils.hpp" #include "vm/boc-compression.h" +#include "vm/boc.h" + +#include "candidate-serializer.h" #include "validator-session-types.h" namespace ton::validatorsession { diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index 15cdcb1be..d88b01e69 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -16,13 +16,14 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include "validator-session.hpp" #include "td/utils/Random.h" -#include "candidate-serializer.h" -#include "delay.h" #include "td/utils/overloaded.h" #include "ton/ton-tl.hpp" +#include "candidate-serializer.h" +#include "delay.h" +#include "validator-session.hpp" + namespace ton { namespace validatorsession { @@ -209,7 +210,7 @@ void ValidatorSessionImpl::preprocess_block(catchain::CatChainBlock *block) { } bool ValidatorSessionImpl::ensure_candidate_unique(td::uint32 src_idx, td::uint32 round, - ValidatorSessionCandidateId block_id) { + ValidatorSessionCandidateId block_id) { auto it = src_round_candidate_[src_idx].find(round); if (it != src_round_candidate_[src_idx].end() && it->second != block_id) { VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << description_->get_source_adnl_id(src_idx) << "][candidate " @@ -770,8 +771,8 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) { td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_ok, round, hash, root_hash, file_hash, src, R.ok_from(), timer.elapsed(), R.is_cached()); } else { - td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_fail, round, hash, R.reason(), - src, R.proof(), timer.elapsed(), R.is_cached()); + td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_fail, round, hash, R.reason(), src, + R.proof(), timer.elapsed(), R.is_cached()); } }); pending_approve_.insert(block_id); @@ -1045,7 +1046,6 @@ void ValidatorSessionImpl::store_block_candidate(ValidatorSessionCandidateId can } } - void ValidatorSessionImpl::request_new_block(bool now) { if (requested_new_block_now_) { return; @@ -1269,9 +1269,9 @@ void ValidatorSessionImpl::start() { auto w = description().export_catchain_nodes(); - catchain_ = catchain::CatChain::create( - make_catchain_callback(), description().opts().catchain_opts, keyring_, adnl_, overlay_manager_, std::move(w), - local_id(), unique_hash_, db_root_, db_suffix_, allow_unsafe_self_blocks_resync_); + catchain_ = catchain::CatChain::create(make_catchain_callback(), description().opts().catchain_opts, keyring_, adnl_, + overlay_manager_, std::move(w), local_id(), unique_hash_, db_root_, db_suffix_, + allow_unsafe_self_blocks_resync_); check_all(); } @@ -1408,14 +1408,14 @@ void ValidatorSessionImpl::stats_init() { cur_stats_.total_weight = description().get_total_weight(); cur_stats_.self = description().get_source_id(local_idx()); - for (auto it = stats_pending_approve_.begin(); it != stats_pending_approve_.end(); ) { + for (auto it = stats_pending_approve_.begin(); it != stats_pending_approve_.end();) { if (it->first.first < cur_round_) { it = stats_pending_approve_.erase(it); } else { ++it; } } - for (auto it = stats_pending_sign_.begin(); it != stats_pending_sign_.end(); ) { + for (auto it = stats_pending_sign_.begin(); it != stats_pending_sign_.end();) { if (it->first.first < cur_round_) { it = stats_pending_sign_.erase(it); } else { @@ -1433,7 +1433,7 @@ void ValidatorSessionImpl::stats_init() { void ValidatorSessionImpl::stats_add_round() { td::uint32 round = cur_stats_.first_round + cur_stats_.rounds.size(); cur_stats_.rounds.emplace_back(); - auto& stat = cur_stats_.rounds.back(); + auto &stat = cur_stats_.rounds.back(); stat.producers.resize(description().get_max_priority() + 1); for (td::uint32 i = 0; i < description().get_total_nodes(); i++) { td::int32 priority = description().get_node_priority(i, round); @@ -1502,38 +1502,38 @@ ValidatorSessionStats::Producer *ValidatorSessionImpl::stats_get_candidate_stat_ } void ValidatorSessionImpl::stats_process_action(td::uint32 node_id, ton_api::validatorSession_round_Message &action) { - ton_api::downcast_call(action, td::overloaded( - [&](const ton_api::validatorSession_message_submittedBlock &obj) { - auto candidate_id = description().candidate_id( - node_id, obj.root_hash_, obj.file_hash_, obj.collated_data_file_hash_); - auto stat = stats_get_candidate_stat( - obj.round_, description().get_source_id(node_id), candidate_id); - if (stat && stat->got_submit_at <= 0.0) { - stat->got_submit_at = td::Clocks::system(); - stat->block_id.root_hash = obj.root_hash_; - stat->block_id.file_hash = obj.file_hash_; - stat->collated_data_hash = obj.collated_data_file_hash_; - } - }, - [&](const ton_api::validatorSession_message_approvedBlock &obj) { - if (obj.candidate_ == skip_round_candidate_id()) { - return; - } - process_approve(node_id, obj.round_, obj.candidate_); - }, - [&](const ton_api::validatorSession_message_commit &obj) { - if (obj.candidate_ == skip_round_candidate_id()) { - return; - } - auto stat = stats_get_candidate_stat_by_id(obj.round_, obj.candidate_); - if (stat) { - stat->set_signed_by(node_id, description().get_node_weight(node_id), - description().get_total_weight()); - } else { - stats_pending_sign_[{obj.round_, obj.candidate_}].push_back(node_id); - } - }, - [](const auto &) {})); + ton_api::downcast_call( + action, + td::overloaded( + [&](const ton_api::validatorSession_message_submittedBlock &obj) { + auto candidate_id = + description().candidate_id(node_id, obj.root_hash_, obj.file_hash_, obj.collated_data_file_hash_); + auto stat = stats_get_candidate_stat(obj.round_, description().get_source_id(node_id), candidate_id); + if (stat && stat->got_submit_at <= 0.0) { + stat->got_submit_at = td::Clocks::system(); + stat->block_id.root_hash = obj.root_hash_; + stat->block_id.file_hash = obj.file_hash_; + stat->collated_data_hash = obj.collated_data_file_hash_; + } + }, + [&](const ton_api::validatorSession_message_approvedBlock &obj) { + if (obj.candidate_ == skip_round_candidate_id()) { + return; + } + process_approve(node_id, obj.round_, obj.candidate_); + }, + [&](const ton_api::validatorSession_message_commit &obj) { + if (obj.candidate_ == skip_round_candidate_id()) { + return; + } + auto stat = stats_get_candidate_stat_by_id(obj.round_, obj.candidate_); + if (stat) { + stat->set_signed_by(node_id, description().get_node_weight(node_id), description().get_total_weight()); + } else { + stats_pending_sign_[{obj.round_, obj.candidate_}].push_back(node_id); + } + }, + [](const auto &) {})); } void ValidatorSessionImpl::process_approve(td::uint32 node_id, td::uint32 round, diff --git a/validator-session/validator-session.h b/validator-session/validator-session.h index 7db6b652d..186de3770 100644 --- a/validator-session/validator-session.h +++ b/validator-session/validator-session.h @@ -113,8 +113,8 @@ class ValidatorSession : public td::actor::Actor { td::Promise>> promise) = 0; virtual void set_catchain_max_block_delay(double delay, double delay_slow) = 0; virtual void get_accepted_candidate(PublicKey source, BlockIdExt block_id, - ValidatorSessionCollatedDataFileHash collated_data_file_hash, - td::Promise promise) = 0; + ValidatorSessionCollatedDataFileHash collated_data_file_hash, + td::Promise promise) = 0; static td::actor::ActorOwn create( catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id, diff --git a/validator-session/validator-session.hpp b/validator-session/validator-session.hpp index 8781445a5..25abe249e 100644 --- a/validator-session/validator-session.hpp +++ b/validator-session/validator-session.hpp @@ -234,8 +234,8 @@ class ValidatorSessionImpl : public ValidatorSession { catchain_max_block_delay_slow_ = delay_slow; } void get_accepted_candidate(PublicKey source, BlockIdExt block_id, - ValidatorSessionCollatedDataFileHash collated_data_file_hash, - td::Promise promise) override; + ValidatorSessionCollatedDataFileHash collated_data_file_hash, + td::Promise promise) override; void process_blocks(std::vector blocks); void finished_processing(); diff --git a/validator/collation-manager.hpp b/validator/collation-manager.hpp index 446c11f60..81fbf6741 100644 --- a/validator/collation-manager.hpp +++ b/validator/collation-manager.hpp @@ -16,10 +16,12 @@ */ #pragma once -#include "fabric.h" +#include + #include "interfaces/validator-manager.h" #include "rldp2/rldp.h" -#include + +#include "fabric.h" namespace ton::validator { diff --git a/validator/collator-node/collator-node-session.cpp b/validator/collator-node/collator-node-session.cpp index 03760b172..d34b0d655 100644 --- a/validator/collator-node/collator-node-session.cpp +++ b/validator/collator-node/collator-node-session.cpp @@ -15,10 +15,9 @@ along with TON Blockchain Library. If not, see . */ -#include "collator-node-session.hpp" - #include "block-auto.h" #include "checksum.h" +#include "collator-node-session.hpp" #include "collator-node.hpp" #include "fabric.h" #include "full-node.h" @@ -443,7 +442,6 @@ void CollatorNodeSession::process_accepted_block_cont2(Ref block) { process_accepted_block(prev[0]); } - void CollatorNodeSession::wait_collated_data_merged(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) { if (!merge_collated_data_enabled_ || collated_data_merged_upto_ >= seqno) { diff --git a/validator/collator-node/collator-node-session.hpp b/validator/collator-node/collator-node-session.hpp index d5fa4021b..86ff3bda2 100644 --- a/validator/collator-node/collator-node-session.hpp +++ b/validator/collator-node/collator-node-session.hpp @@ -16,12 +16,13 @@ */ #pragma once +#include +#include + #include "impl/collated-data-merger.h" #include "interfaces/validator-manager.h" #include "rldp/rldp.h" #include "rldp2/rldp.h" -#include -#include namespace ton::validator { diff --git a/validator/fabric.h b/validator/fabric.h index 712add4b5..d91fec226 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -19,11 +19,12 @@ #pragma once #include "impl/collated-data-merger.h" -#include "interfaces/validator-manager.h" #include "interfaces/db.h" -#include "validator.h" +#include "interfaces/validator-manager.h" #include "vm/fmt.hpp" +#include "validator.h" + namespace ton { namespace validator { @@ -94,8 +95,7 @@ td::Result create_block_handle(td::BufferSlice data); td::Result create_block_handle(td::Slice 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, - block::SizeLimitsConfig::ExtMsgLimits limits); +td::Result> create_ext_message(td::BufferSlice data, block::SizeLimitsConfig::ExtMsgLimits limits); td::Result> create_ihr_message(td::BufferSlice data); td::Result>> create_new_shard_block_descriptions(td::BufferSlice data); @@ -136,8 +136,9 @@ void run_collate_query(CollateParams params, td::actor::ActorId 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_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/full-node-shard.cpp b/validator/full-node-shard.cpp index f8ec1949a..a4bf5006b 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -939,7 +939,8 @@ void FullNodeShardImpl::send_block_candidate_broadcast(BlockIdExt block_id, Catc VLOG(FULL_NODE_WARNING) << "failed to serialize block candidate broadcast: " << B.move_as_error(); return; } - VLOG(FULL_NODE_DEBUG) << "Sending blockDataBroadcast (" << (collated_data ? "with" : "no") << " cdata): " << block_id.to_str(); + VLOG(FULL_NODE_DEBUG) << "Sending blockDataBroadcast (" << (collated_data ? "with" : "no") + << " cdata): " << block_id.to_str(); td::actor::send_closure(overlays_, &overlay::Overlays::send_broadcast_fec_ex, adnl_id_, overlay_id_, local_id_, overlay::Overlays::BroadcastFlagAnySender(), B.move_as_ok()); } diff --git a/validator/full-node.cpp b/validator/full-node.cpp index fee0f8b5b..daf1dc34c 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -16,15 +16,16 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include "full-node.hpp" -#include "ton/ton-io.hpp" -#include "td/actor/MultiPromise.h" -#include "full-node.h" #include "common/delay.h" #include "impl/out-msg-queue-proof.hpp" +#include "td/actor/MultiPromise.h" #include "td/utils/Random.h" +#include "ton/ton-io.hpp" #include "ton/ton-tl.hpp" +#include "full-node.h" +#include "full-node.hpp" + namespace ton { namespace validator { @@ -109,7 +110,7 @@ void FullNodeImpl::del_collator_adnl_id(adnl::AdnlNodeIdShort id) { void FullNodeImpl::sign_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, td::uint32 expiry_at, td::uint32 max_size, td::Promise promise) { auto it = shards_.find(shard_id); - if(it == shards_.end() || it->second.actor.empty()) { + if (it == shards_.end() || it->second.actor.empty()) { promise.set_error(td::Status::Error(ErrorCode::error, "shard not found")); return; } @@ -121,7 +122,7 @@ void FullNodeImpl::import_shard_overlay_certificate(ShardIdFull shard_id, Public std::shared_ptr cert, td::Promise promise) { auto it = shards_.find(shard_id); - if(it == shards_.end() || it->second.actor.empty()) { + if (it == shards_.end() || it->second.actor.empty()) { promise.set_error(td::Status::Error(ErrorCode::error, "shard not found")); return; } @@ -150,15 +151,15 @@ void FullNodeImpl::update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promise broadcast) { - auto fast_sync_overlay = fast_sync_overlays_.choose_overlay(broadcast->dst_shard).first; - if (!fast_sync_overlay.empty()) { - td::actor::send_closure(fast_sync_overlay, &FullNodeFastSyncOverlay::send_out_msg_queue_proof_broadcast, - std::move(broadcast)); - } + auto fast_sync_overlay = fast_sync_overlays_.choose_overlay(broadcast->dst_shard).first; + if (!fast_sync_overlay.empty()) { + td::actor::send_closure(fast_sync_overlay, &FullNodeFastSyncOverlay::send_out_msg_queue_proof_broadcast, + std::move(broadcast)); + } } void FullNodeImpl::send_broadcast(BlockBroadcast broadcast, int mode) { @@ -491,7 +492,7 @@ void FullNodeImpl::get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeou } void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, - td::Timestamp timeout, td::Promise promise) { + td::Timestamp timeout, td::Promise promise) { auto shard = get_shard(shard_prefix, /* historical = */ true); if (shard.empty()) { VLOG(FULL_NODE_WARNING) << "dropping download archive query to unknown shard"; @@ -738,7 +739,7 @@ void FullNodeImpl::start_up() { validator_set_hash, std::move(data), std::move(collated_data), mode); } void send_out_msg_queue_proof_broadcast(td::Ref broadcast) override { - td::actor::send_closure(id_, &FullNodeImpl::send_out_msg_queue_proof_broadcast, std::move(broadcast)); + td::actor::send_closure(id_, &FullNodeImpl::send_out_msg_queue_proof_broadcast, std::move(broadcast)); } void send_broadcast(BlockBroadcast broadcast, int mode) override { td::actor::send_closure(id_, &FullNodeImpl::send_broadcast, std::move(broadcast), mode); @@ -847,8 +848,8 @@ void FullNodeImpl::update_custom_overlay(CustomOverlayInfo &overlay) { old_actors.erase(it); } else { overlay.actors_[local_id] = td::actor::create_actor( - "CustomOverlay", local_id, params, zero_state_file_hash_, opts_, keyring_, adnl_, rldp_, rldp2_, - overlays_, validator_manager_, actor_id(this)); + "CustomOverlay", local_id, params, zero_state_file_hash_, opts_, keyring_, adnl_, rldp_, rldp2_, overlays_, + validator_manager_, actor_id(this)); } } }; @@ -936,8 +937,8 @@ td::actor::ActorOwn FullNode::create( td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId dht, td::actor::ActorId overlays, td::actor::ActorId validator_manager, td::actor::ActorId client, std::string db_root, td::Promise started_promise) { - return td::actor::create_actor("fullnode", local_id, adnl_id, zero_state_file_hash, opts, keyring, - adnl, rldp, rldp2, dht, overlays, validator_manager, client, db_root, + return td::actor::create_actor("fullnode", local_id, adnl_id, zero_state_file_hash, opts, keyring, adnl, + rldp, rldp2, dht, overlays, validator_manager, client, db_root, std::move(started_promise)); } @@ -961,7 +962,7 @@ bool CustomOverlayParams::send_shard(const ShardIdFull &shard) const { [&](const ShardIdFull &our_shard) { return shard_intersects(shard, our_shard); }); } -CustomOverlayParams CustomOverlayParams::fetch(const ton_api::engine_validator_customOverlay& f) { +CustomOverlayParams CustomOverlayParams::fetch(const ton_api::engine_validator_customOverlay &f) { CustomOverlayParams c; c.name_ = f.name_; for (const auto &node : f.nodes_) { diff --git a/validator/full-node.hpp b/validator/full-node.hpp index 5a9a2206d..6dea9ff55 100644 --- a/validator/full-node.hpp +++ b/validator/full-node.hpp @@ -70,7 +70,7 @@ class FullNodeImpl : public FullNode { void send_ext_message(AccountIdPrefixFull dst, td::BufferSlice data); void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqnp, td::BufferSlice data); void send_block_candidate_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, - td::BufferSlice data, td::optional collated_data, int mode); + td::BufferSlice data, td::optional collated_data, int mode); void send_broadcast(BlockBroadcast broadcast, int mode); void send_out_msg_queue_proof_broadcast(td::Ref broadcasts); void download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); diff --git a/validator/impl/collated-data-merger.cpp b/validator/impl/collated-data-merger.cpp index 9e90b571b..4eb38a505 100644 --- a/validator/impl/collated-data-merger.cpp +++ b/validator/impl/collated-data-merger.cpp @@ -14,10 +14,10 @@ This file is part of TON Blockchain source code. You should have received a copy of the GNU General Public License along with TON Blockchain. If not, see . */ -#include "collated-data-merger.h" - #include "vm/cells/ExtCell.h" +#include "collated-data-merger.h" + namespace ton::validator { class CollatedDataMergerExtCellLoader { diff --git a/validator/impl/collated-data-merger.h b/validator/impl/collated-data-merger.h index fcd66164e..bed967d3b 100644 --- a/validator/impl/collated-data-merger.h +++ b/validator/impl/collated-data-merger.h @@ -15,17 +15,17 @@ along with TON Blockchain. If not, see . */ #pragma once -#include "common/refcnt.hpp" -#include "vm/dict.h" -#include "ton/ton-types.h" -#include "ton/ton-shard.h" -#include "common/bitstring.h" +#include + #include "../../crypto/block/block.h" +#include "common/bitstring.h" +#include "common/refcnt.hpp" #include "td/actor/common.h" #include "td/actor/core/Actor.h" +#include "ton/ton-shard.h" +#include "ton/ton-types.h" #include "vm/db/CellHashTable.h" - -#include +#include "vm/dict.h" namespace ton::validator { using td::Ref; diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index 2c3e6c1aa..8769da232 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -17,25 +17,27 @@ Copyright 2017-2020 Telegram Systems LLP */ #pragma once -#include "collated-data-merger.h" -#include "block-parse.h" -#include "interfaces/validator-manager.h" -#include "shard.hpp" -#include "top-shard-descr.hpp" -#include "common/refcnt.hpp" -#include "vm/cells.h" -#include "vm/dict.h" -#include "block/mc-config.h" -#include "block/block.h" -#include "block/transaction.h" +#include +#include + #include "block/block-db.h" +#include "block/block.h" +#include "block/mc-config.h" #include "block/output-queue-merger.h" +#include "block/transaction.h" +#include "common/global-version.h" +#include "common/refcnt.hpp" +#include "interfaces/validator-manager.h" +#include "vm/cells.h" #include "vm/cells/MerkleProof.h" #include "vm/cells/MerkleUpdate.h" -#include -#include -#include "common/global-version.h" +#include "vm/dict.h" + +#include "block-parse.h" +#include "collated-data-merger.h" #include "fabric.h" +#include "shard.hpp" +#include "top-shard-descr.hpp" namespace ton { @@ -350,7 +352,8 @@ class Collator final : public td::actor::Actor { bool is_our_address(Ref addr_ref) const; bool is_our_address(ton::AccountIdPrefixFull addr_prefix) const; bool is_our_address(const ton::StdSmcAddress& addr) const; - void after_get_external_messages(td::Result, int>>> res, td::PerfLogAction token); + void after_get_external_messages(td::Result, int>>> res, + td::PerfLogAction token); td::Result register_external_message_cell(Ref ext_msg, const ExtMessage::Hash& ext_hash, int priority); // td::Result register_external_message(td::Slice ext_msg_boc); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index efd8a80ab..1e2aefac9 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -16,27 +16,29 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include "candidate-serializer.h" -#include "collator-impl.h" -#include "vm/boc.h" -#include "td/db/utils/BlobView.h" -#include "vm/db/StaticBagOfCellsDb.h" -#include "block/mc-config.h" -#include "block/block.h" -#include "block/block-parse.h" +#include +#include +#include + +#include "adnl/utils.hpp" #include "block/block-auto.h" -#include "vm/dict.h" +#include "block/block-parse.h" +#include "block/block.h" +#include "block/mc-config.h" #include "crypto/openssl/rand.hpp" +#include "td/db/utils/BlobView.h" +#include "td/utils/Random.h" #include "ton/ton-shard.h" -#include "adnl/utils.hpp" -#include -#include +#include "vm/boc.h" +#include "vm/db/StaticBagOfCellsDb.h" +#include "vm/dict.h" + +#include "candidate-serializer.h" +#include "collator-impl.h" #include "fabric.h" #include "storage-stat-cache.hpp" -#include "validator-set.hpp" #include "top-shard-descr.hpp" -#include -#include "td/utils/Random.h" +#include "validator-set.hpp" namespace ton { @@ -65,7 +67,7 @@ static constexpr int MAX_ATTEMPTS = 5; * @param promise The promise to return the result. */ Collator::Collator(CollateParams params, td::actor::ActorId manager, td::Timestamp timeout, - td::CancellationToken cancellation_token, td::Promise promise) + td::CancellationToken cancellation_token, td::Promise promise) : shard_(params.shard) , is_hardfork_(params.is_hardfork) , min_mc_block_id{params.min_masterchain_block_id} @@ -1077,7 +1079,7 @@ void Collator::got_neighbor_msg_queue(unsigned i, Ref res) { block_state_proofs_.emplace(block_id.root_hash, res->block_state_proof_); } - auto &neighbor_stats = stats_.neighbors.at(i); + auto& neighbor_stats = stats_.neighbors.at(i); neighbor_stats.shard = block_id.shard_full(); neighbor_stats.is_trivial = shard_intersects(block_id.shard_full(), shard_); neighbor_stats.is_local = res->is_local_; @@ -1090,9 +1092,7 @@ void Collator::got_neighbor_msg_queue(unsigned i, Ref res) { neighbor_proof_builders_.push_back(vm::MerkleProofBuilder{res->state_root_}); state_root = neighbor_proof_builders_.back().root(); if (full_collated_data_ && !block_id.is_masterchain()) { - neighbor_proof_builders_.back().set_cell_load_callback([&](const vm::LoadedCell& cell) { - on_cell_loaded(cell); - }); + neighbor_proof_builders_.back().set_cell_load_callback([&](const vm::LoadedCell& cell) { on_cell_loaded(cell); }); } } auto state = ShardStateQ::fetch(block_id, {}, state_root); @@ -1198,9 +1198,7 @@ bool Collator::unpack_merge_last_state() { // 1. prepare for creating a MerkleUpdate based on previous state state_usage_tree_ = std::make_shared(); if (full_collated_data_ && !is_masterchain()) { - state_usage_tree_->set_cell_load_callback([&](const vm::LoadedCell& cell) { - on_cell_loaded(cell); - }); + state_usage_tree_->set_cell_load_callback([&](const vm::LoadedCell& cell) { on_cell_loaded(cell); }); } prev_state_root_ = vm::UsageCell::create(prev_state_root_pure_, state_usage_tree_->root_ptr()); // 2. extract back slightly virtualized roots of the two original states @@ -1247,9 +1245,7 @@ bool Collator::unpack_last_state() { // prepare for creating a MerkleUpdate based on previous state state_usage_tree_ = std::make_shared(); if (full_collated_data_ && !is_masterchain()) { - state_usage_tree_->set_cell_load_callback([&](const vm::LoadedCell& cell) { - on_cell_loaded(cell); - }); + state_usage_tree_->set_cell_load_callback([&](const vm::LoadedCell& cell) { on_cell_loaded(cell); }); } prev_state_root_ = vm::UsageCell::create(prev_state_root_pure_, state_usage_tree_->root_ptr()); // unpack previous state @@ -2839,9 +2835,7 @@ bool Collator::init_account_storage_dict(block::Account& account) { } } dict.mpb = vm::MerkleProofBuilder(std::move(dict_root)); - dict.mpb.set_cell_load_callback([&](const vm::LoadedCell& cell) { - on_cell_loaded(cell); - }); + dict.mpb.set_cell_load_callback([&](const vm::LoadedCell& cell) { on_cell_loaded(cell); }); } auto S = account.init_account_storage_stat(dict.mpb.root()); if (S.is_error()) { @@ -2851,7 +2845,6 @@ bool Collator::init_account_storage_dict(block::Account& account) { return true; } - /** * Looks up an account in the Collator's account map. * @@ -3022,11 +3015,11 @@ bool Collator::process_account_storage_dict(block::Account& account) { switch (collated_data_stat.get_cell_status(cell->get_hash())) { case vm::ProofStorageStat::c_none: proof_size_diff += vm::ProofStorageStat::estimate_serialized_size(loaded_cell.data_cell); - break; + break; case vm::ProofStorageStat::c_prunned: proof_size_diff -= vm::ProofStorageStat::estimate_prunned_size(); - proof_size_diff += vm::ProofStorageStat::estimate_serialized_size(loaded_cell.data_cell); - break; + proof_size_diff += vm::ProofStorageStat::estimate_serialized_size(loaded_cell.data_cell); + break; case vm::ProofStorageStat::c_loaded: break; } @@ -3739,7 +3732,7 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R defer = true; } } else { - auto &x = unprocessed_deferred_messages_[src_addr]; + auto& x = unprocessed_deferred_messages_[src_addr]; CHECK(x > 0); if (--x == 0) { unprocessed_deferred_messages_.erase(src_addr); @@ -4115,7 +4108,7 @@ bool Collator::process_inbound_message(Ref enq_msg, ton::LogicalT << " enqueued_lt=" << enq_msg_descr.enqueued_lt_ << " has been already processed by us before, skipping"; // should we dequeue the message if it is ours (after a merge?) // (it should have been dequeued by out_msg_queue_cleanup() before) - auto &neighbor_stats = stats_.neighbors.at(src_nb_idx); + auto& neighbor_stats = stats_.neighbors.at(src_nb_idx); ++neighbor_stats.skipped_msgs; return true; } @@ -4210,7 +4203,7 @@ bool Collator::process_inbound_internal_messages() { block_full_ = !block_limit_status_->fits(block::ParamLimits::cl_normal); auto kv = nb_out_msgs_->extract_cur(); CHECK(kv && kv->msg.not_null()); - auto &neighbor_stats = stats_.neighbors.at(kv->source); + auto& neighbor_stats = stats_.neighbors.at(kv->source); if (kv->limit_exceeded) { LOG(INFO) << "limit for imported messages is reached, stop processing inbound internal messages"; neighbor_stats.limit_reached = true; @@ -4259,7 +4252,7 @@ bool Collator::process_inbound_internal_messages() { if (verbosity > 1) { FLOG(INFO) { sb << "invalid inbound message: lt=" << kv->lt << " from=" << kv->source << " key=" << kv->key.to_hex() - << " msg="; + << " msg="; block::gen::t_EnqueuedMsg.print(sb, kv->msg); }; } @@ -6359,12 +6352,8 @@ bool Collator::create_collated_data() { // 7. Collated data proofs collated_roots_.push_back( vm::CellBuilder().store_long(block::gen::CollatedDataSeparator::cons_tag[0], 32).finalize_novm()); - std::vector> block_data_parts = { - shard_account_blocks_, - in_msg_dict->get_root_cell(), - out_msg_dict->get_root_cell(), - state_update - }; + std::vector> block_data_parts = {shard_account_blocks_, in_msg_dict->get_root_cell(), + out_msg_dict->get_root_cell(), state_update}; std::vector> proofs = collated_data_stat.build_collated_data(/* skip_roots = */ std::move(block_data_parts)); collated_roots_.insert(collated_roots_.end(), proofs.begin(), proofs.end()); diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index eab2aa7fb..b9c25577e 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -858,14 +858,14 @@ bool ValidateQuery::extract_collated_data() { hashes.insert(hashes.end(), collated_data_root_dict_hashes_.begin(), collated_data_root_dict_hashes_.end()); LOG(DEBUG) << "sending get_cells to CollatedDataMerger"; ++pending; - td::actor::send_closure_later( - collated_data_merger_, &CollatedDataMerger::get_cells, std::move(hashes), - [self = get_self(), token = perf_log_.start_action("CollatedDataMerger::get_cells")]( - td::Result>> res) mutable { - LOG(DEBUG) << "got answer to CollatedDataMerger::get_cells"; - td::actor::send_closure_later(std::move(self), &ValidateQuery::process_merged_collated_roots, - std::move(res), std::move(token)); - }); + td::actor::send_closure_later(collated_data_merger_, &CollatedDataMerger::get_cells, std::move(hashes), + [self = get_self(), token = perf_log_.start_action("CollatedDataMerger::get_cells")]( + td::Result>> res) mutable { + LOG(DEBUG) << "got answer to CollatedDataMerger::get_cells"; + td::actor::send_closure_later(std::move(self), + &ValidateQuery::process_merged_collated_roots, + std::move(res), std::move(token)); + }); } return true; } diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index 6de046a92..e3b481e56 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -18,20 +18,22 @@ */ #pragma once -#include "collated-data-merger.h" -#include "block-parse.h" -#include "fabric.h" +#include +#include +#include + +#include "block/mc-config.h" +#include "block/transaction.h" +#include "common/global-version.h" #include "interfaces/validator-manager.h" #include "vm/cells.h" #include "vm/dict.h" -#include "block/mc-config.h" -#include "block/transaction.h" + +#include "block-parse.h" +#include "collated-data-merger.h" +#include "fabric.h" #include "shard.hpp" #include "signature-set.hpp" -#include -#include -#include -#include "common/global-version.h" namespace ton { diff --git a/validator/manager.cpp b/validator/manager.cpp index 7122186e2..be10c3491 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -16,42 +16,38 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include "manager.hpp" -#include "checksum.h" -#include "td/utils/buffer.h" -#include "validator-group.hpp" -#include "downloaders/wait-block-state.hpp" -#include "downloaders/wait-block-state-merge.hpp" -#include "downloaders/wait-block-data.hpp" -#include "fabric.h" -#include "manager.h" - -#include "block-auto.h" -#include "validate-broadcast.hpp" -#include "ton/ton-tl.hpp" -#include "ton/ton-io.hpp" -#include "state-serializer.hpp" -#include "get-next-key-blocks.h" -#include "import-db-slice.hpp" -#include "import-db-slice-local.hpp" +#include #include "auto/tl/lite_api.h" -#include "tl-utils/lite-utils.hpp" #include "auto/tl/ton_api_json.h" -#include "tl/tl_json.h" - -#include "td/utils/Random.h" -#include "td/utils/port/path.h" -#include "td/utils/JsonBuilder.h" - #include "common/delay.h" #include "db/fileref.hpp" +#include "downloaders/wait-block-data.hpp" +#include "downloaders/wait-block-state-merge.hpp" +#include "downloaders/wait-block-state.hpp" #include "td/actor/MultiPromise.h" +#include "td/utils/JsonBuilder.h" +#include "td/utils/Random.h" +#include "td/utils/buffer.h" #include "td/utils/filesystem.h" - +#include "td/utils/port/path.h" +#include "tl-utils/lite-utils.hpp" +#include "tl/tl_json.h" +#include "ton/ton-io.hpp" +#include "ton/ton-tl.hpp" #include "validator/stats-merger.h" -#include +#include "block-auto.h" +#include "checksum.h" +#include "fabric.h" +#include "get-next-key-blocks.h" +#include "import-db-slice-local.hpp" +#include "import-db-slice.hpp" +#include "manager.h" +#include "manager.hpp" +#include "state-serializer.hpp" +#include "validate-broadcast.hpp" +#include "validator-group.hpp" namespace ton { @@ -735,7 +731,8 @@ void ValidatorManagerImpl::add_cached_block_data(BlockIdExt block_id, td::Buffer td::actor::send_closure( collator.actor, &CollatorNode::on_block_candidate_broadcast, BlockCandidate(Ed25519_PublicKey(entry.creator), block_id, entry.collated_data_hash.value(), - entry.data.clone(), entry.collated_data.value().clone()), entry.cc_seqno); + entry.data.clone(), entry.collated_data.value().clone()), + entry.cc_seqno); } } } diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index a92bc2a25..98bf75ed3 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -16,17 +16,17 @@ Copyright 2017-2020 Telegram Systems LLP */ -#include "validator-group.hpp" +#include "collator-node/collator-node.hpp" +#include "common/delay.h" +#include "td/utils/Random.h" +#include "td/utils/overloaded.h" +#include "ton/lite-tl.hpp" +#include "ton/ton-io.hpp" #include "block-auto.h" #include "fabric.h" #include "full-node-master.hpp" -#include "ton/ton-io.hpp" -#include "td/utils/overloaded.h" -#include "common/delay.h" -#include "ton/lite-tl.hpp" -#include "td/utils/Random.h" -#include "collator-node/collator-node.hpp" +#include "validator-group.hpp" namespace ton { diff --git a/validator/validator-group.hpp b/validator/validator-group.hpp index 6d80dcafa..2967363e5 100644 --- a/validator/validator-group.hpp +++ b/validator/validator-group.hpp @@ -18,17 +18,16 @@ */ #pragma once +#include +#include + #include "impl/collated-data-merger.h" -#include "collation-manager.hpp" #include "interfaces/validator-manager.h" - -#include "validator-session/validator-session.h" - #include "rldp/rldp.h" #include "rldp2/rldp.h" +#include "validator-session/validator-session.h" -#include -#include +#include "collation-manager.hpp" namespace ton { From 88e9171eceef60916dfa7b263097a906d26981f9 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 7 Nov 2025 15:48:46 +0300 Subject: [PATCH 12/13] Fix error after merge --- validator/impl/collated-data-merger.cpp | 2 +- validator/impl/collator.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/impl/collated-data-merger.cpp b/validator/impl/collated-data-merger.cpp index 4eb38a505..3fc095980 100644 --- a/validator/impl/collated-data-merger.cpp +++ b/validator/impl/collated-data-merger.cpp @@ -48,7 +48,7 @@ void CollatedDataMerger::add_cells(Ref cell) { } info.visited = true; auto loaded_cell = cell->load_cell().move_as_ok(); - CHECK(loaded_cell.virt.get_virtualization() == 0); + CHECK(loaded_cell.effective_level >= loaded_cell.data_cell->get_level()); auto data_cell = std::move(loaded_cell.data_cell); info.set_cell(data_cell); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 1e2aefac9..1cc37e432 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -6779,7 +6779,7 @@ void Collator::on_cell_loaded(const vm::LoadedCell& loaded_cell) { stats_.work_time.total_on_cell_loaded += timer.elapsed_both(); }; if (merge_collated_data_enabled_) { - vm::CellHash hash = loaded_cell.data_cell->get_hash(loaded_cell.virt.get_level()); + vm::CellHash hash = loaded_cell.data_cell->get_hash(loaded_cell.effective_level); if (collated_data_deduplicator_ && collated_data_deduplicator_->cell_exists(hash, new_block_seqno - (optimistic_prev_block_.is_null() ? 0 : 1))) { return; From a505e32521f13012e27d4490358fff366c925eaf Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 14 Nov 2025 23:22:16 +0300 Subject: [PATCH 13/13] Fix cleanup of old blocks in ValidatorSession --- validator-session/validator-session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index d88b01e69..462b02789 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -1183,7 +1183,7 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) { auto it2 = blocks_.begin(); while (it2 != blocks_.end()) { if (it2->second->round_ < (td::int32)cur_round_ - MAX_PAST_ROUND_BLOCK && - !accepted_block_candidates_.contains(it->first)) { + !accepted_block_candidates_.contains(it2->first)) { it2 = blocks_.erase(it2); } else { ++it2;