From b3bea413e3e0e6db182a633f7e7cce6345c46d83 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Thu, 21 Nov 2024 11:47:39 +0300 Subject: [PATCH] Rework validator-collator interaction 1) Remove config 41, move "full collated data" to capabilities 2) Whitelist on collator nodes 3) "Ping" request for collator nodes 4) More customizable collators list for validators 5) CollationManager --- crypto/block/block.tlb | 5 - crypto/block/mc-config.cpp | 24 -- crypto/block/mc-config.h | 6 - tl/generate/scheme/ton_api.tl | 28 +- tl/generate/scheme/ton_api.tlo | Bin 105392 -> 107428 bytes ton/ton-types.h | 3 +- .../validator-engine-console-query.cpp | 144 ++++++- .../validator-engine-console-query.h | 104 +++++ .../validator-engine-console.cpp | 5 + validator-engine/validator-engine.cpp | 252 +++++++++--- validator-engine/validator-engine.hpp | 27 +- validator/CMakeLists.txt | 2 + validator/collation-manager.cpp | 373 ++++++++++++++++++ validator/collation-manager.hpp | 87 ++++ validator/collator-node.cpp | 56 ++- validator/collator-node.hpp | 1 + validator/full-node.cpp | 14 +- validator/impl/collator-impl.h | 2 +- validator/impl/collator.cpp | 7 +- validator/impl/shard.hpp | 3 - validator/impl/validate-query.cpp | 9 + validator/impl/validate-query.hpp | 2 +- validator/impl/validator-set.hpp | 3 +- validator/interfaces/shard.h | 1 - validator/interfaces/validator-set.h | 1 + validator/manager-disk.hpp | 5 + validator/manager-hardfork.hpp | 5 + validator/manager.cpp | 77 +++- validator/manager.hpp | 6 + validator/validator-group.cpp | 158 +------- validator/validator-group.hpp | 22 +- validator/validator-options.cpp | 53 ++- validator/validator-options.hpp | 17 +- validator/validator.h | 21 +- 34 files changed, 1204 insertions(+), 319 deletions(-) create mode 100644 validator/collation-manager.cpp create mode 100644 validator/collation-manager.hpp diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 5eddbde38..aa7c0d3f9 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -801,11 +801,6 @@ misbehaviour_punishment_config_v1#01 = MisbehaviourPunishmentConfig; _ MisbehaviourPunishmentConfig = ConfigParam 40; -// collator_nodes: each collator is (workchain:int32 shard:uint64 adnl_id:uint256) -collator_info#00 = CollatorInfo; -colator_config#a0 full_collated_data:Bool collator_nodes:(HashmapE 352 CollatorInfo) = CollatorConfig; -_ CollatorConfig = ConfigParam 41; - size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16 max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig; size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16 diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 0a8c0c7b1..56ee85ae3 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -2339,28 +2339,4 @@ td::optional PrecompiledContractsConfig::g return c; } -CollatorConfig Config::get_collator_config(bool need_collator_nodes) const { - CollatorConfig collator_config; - gen::CollatorConfig::Record rec; - auto cell = get_config_param(41, -41); - if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) { - return collator_config; - } - collator_config.full_collated_data = rec.full_collated_data; - if (need_collator_nodes) { - vm::Dictionary dict{rec.collator_nodes->prefetch_ref(), 32 + 64 + 256}; - dict.check_for_each([&](Ref value, td::ConstBitPtr key, int n) { - CHECK(n == 32 + 64 + 256); - auto workchain = (td::int32)key.get_int(32); - key.advance(32); - td::uint64 shard = key.get_uint(64); - key.advance(64); - td::Bits256 adnl_id(key); - collator_config.collator_nodes.push_back({ton::ShardIdFull(workchain, shard), adnl_id}); - return true; - }); - } - return collator_config; -} - } // namespace block diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 94de329bf..3c4421da5 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -543,11 +543,6 @@ struct CollatorNodeDescr { ton::NodeIdShort adnl_id; }; -struct CollatorConfig { - bool full_collated_data = false; - std::vector collator_nodes; -}; - class Config { enum { default_mc_catchain_lifetime = 200, @@ -664,7 +659,6 @@ class Config { std::vector compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time, ton::CatchainSeqno cc_seqno) const; std::vector compute_total_validator_set(int next) const; - CollatorConfig get_collator_config(bool need_collator_nodes) const; td::Result get_size_limits_config() const; static td::Result do_get_size_limits_config(td::Ref cs); std::unique_ptr get_suspended_addresses(ton::UnixTime now) const; diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 05ea249ce..0839f727d 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -646,8 +646,9 @@ engine.validator.fullNodeMaster port:int adnl:int256 = engine.validator.FullNode engine.validator.fullNodeSlave ip:int port:int adnl:PublicKey = engine.validator.FullNodeSlave; engine.validator.fullNodeConfig ext_messages_broadcast_disabled:Bool = engine.validator.FullNodeConfig; engine.validator.fastSyncMemberCertificate adnl_id:int256 certificate:overlay.MemberCertificate = engine.validator.FastSyncMemberCertificate; +engine.validator.collatorNodeWhitelist enabled:Bool adnl_ids:(vector int256) = engine.validator.CollatorNodeWhitelist; engine.validator.extraConfig state_serializer_enabled:Bool fast_sync_member_certificates:(vector engine.validator.fastSyncMemberCertificate) - = engine.validator.ExtraConfig; + collator_node_whitelist:engine.validator.collatorNodeWhitelist = engine.validator.ExtraConfig; engine.validator.config out_port:int addrs:(vector engine.Addr) adnl:(vector engine.adnl) dht:(vector engine.dht) validators:(vector engine.validator) collators:(vector engine.collator) @@ -670,11 +671,10 @@ engine.validator.collatorOptions dispatch_phase_2_max_per_initiator:int dispatch_phase_3_max_per_initiator:int whitelist:(vector string) prioritylist:(vector string) = engine.validator.CollatorOptions; -engine.validator.collatorsList.collator adnl_id:int256 trusted:Bool = engine.validator.collatorsList.Collator; +engine.validator.collatorsList.collator adnl_id:int256 = engine.validator.collatorsList.Collator; engine.validator.collatorsList.shard shard_id:tonNode.shardId collators:(vector engine.validator.collatorsList.collator) - = engine.validator.collatorsList.Shard; -engine.validator.collatorsList self_collate:Bool use_config_41:Bool shards:(vector engine.validator.collatorsList.shard) - = engine.validator.CollatorsList; + self_collate:Bool select_mode:string = engine.validator.collatorsList.Shard; +engine.validator.collatorsList shards:(vector engine.validator.collatorsList.shard) = engine.validator.CollatorsList; ---functions--- ---types--- @@ -740,6 +740,11 @@ engine.validator.perfTimerStats stats:(vector engine.validator.PerfTimerStatsByN engine.validator.shardOutQueueSize size:long = engine.validator.ShardOutQueueSize; +engine.validator.collationManagerStats.shard shard_id:tonNode.shardId self_collate:Bool select_mode:string active:Bool collators:(vector int256) = engine.validator.collationManagerStats.Shard; +engine.validator.collationManagerStats.collator adnl_id:int256 active:Bool alive:Bool ping_in:double = engine.validator.collationManagerStats.Collator; +engine.validator.collationManagerStats.localId adnl_id:int256 shards:(vector engine.validator.collationManagerStats.shard) + collators:(vector engine.validator.collationManagerStats.collator) = engine.validator.collationManagerStats.LocalId; +engine.validator.collationManagerStats local_ids:(vector engine.validator.collationManagerStats.localId) = engine.validator.CollationManagerStats; ---functions--- @@ -811,8 +816,14 @@ engine.validator.addShard shard:tonNode.shardId = engine.validator.Success; engine.validator.delCollator adnl_id:int256 shard:tonNode.shardId = engine.validator.Success; engine.validator.delShard shard:tonNode.shardId = engine.validator.Success; +engine.validator.collatorNodeSetWhitelistedValidator adnl_id:int256 add:Bool = engine.validator.Success; +engine.validator.collatorNodeSetWhitelistEnabled enabled:Bool = engine.validator.Success; +engine.validator.showCollatorNodeWhitelist = engine.validator.CollatorNodeWhitelist; + engine.validator.setCollatorsList list:engine.validator.collatorsList = engine.validator.Success; +engine.validator.clearCollatorsList = engine.validator.Success; engine.validator.showCollatorsList = engine.validator.CollatorsList; +engine.validator.getCollationManagerStats = engine.validator.CollationManagerStats; engine.validator.signOverlayMemberCertificate sign_by:int256 adnl_id:int256 slot:int expire_at:int = overlay.MemberCertificate; engine.validator.importFastSyncMemberCertificate adnl_id:int256 certificate:overlay.MemberCertificate = engine.validator.Success; @@ -887,8 +898,8 @@ validatorSession.stats success:Bool id:tonNode.blockIdExt timestamp:double self: collatorNode.candidate source:PublicKey id:tonNode.blockIdExt data:bytes collated_data:bytes = collatorNode.Candidate; collatorNode.compressedCandidate flags:# source:PublicKey id:tonNode.blockIdExt decompressed_size:int data:bytes = collatorNode.Candidate; -collatorNode.generateBlockSuccess candidate:collatorNode.Candidate = collatorNode.GenerateBlockResult; -collatorNode.generateBlockError code:int message:string = collatorNode.GenerateBlockResult; +collatorNode.pong flags:# = collatorNode.Pong; +collatorNode.error code:int message:string = collatorNode.Error; validatorSession.newValidatorGroupStats.node id:int256 weight:long = validatorSession.newValidatorGroupStats.Node; validatorSession.newValidatorGroupStats session_id:int256 workchain:int shard:long cc_seqno:int @@ -901,7 +912,8 @@ validatorSession.endValidatorGroupStats session_id:int256 timestamp:double ---functions--- collatorNode.generateBlock shard:tonNode.shardId cc_seqno:int prev_blocks:(vector tonNode.blockIdExt) - creator:int256 = collatorNode.GenerateBlockResult; + creator:int256 = collatorNode.Candidate; +collatorNode.ping flags:# = collatorNode.Pong; ---types--- diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index eaa9c416156da6d95b8fbd44bfa6b2ef83d1d1d9..dd2af2ec9de6dbde7d6644ac55b85bf42bba5df1 100644 GIT binary patch delta 1507 zcmZuxVQ3R)7``vw#bj+++oj0`uj!T6HLYDz*ABw+oNe2jUuq( z3AJ=G=ig~@??MyZVV3-bMrQ9RihU6lgDyiS+AO{1%-OT#^(1Qr3FpmcV9y(gN^s@OqdzWGQ9>ceBSc`<%|K!#4vEhwQ zKP$1!t=PyHF+?@EP;kV%oe_Tg;b}kaw7GV4!8$*ASkOdRf<|cQDgF2Ua-Kidz*l#fP)$;~A^G!6p}&GXg8a%F%;QtB%s%4AO^JPb~Y zKmMF?743qHItgNP!vI>QF14iN#;%mbL zZu%S{b=H?qGAThB6)8kiFO{3=fgAJPG)fF!T^Q=cWcA%6++D!Y9FLaR$ZnEbLz4VPqj{Otq6h8{*v+PS)h?xD_Lqkw9*y3uhg` zi2DrA7cNKmMyCl3JiRgih1IAZ(e}OI%d4}Wk}u2BIO!qulUM${ zV-)ujjvi~c^tDmiPZpq}HFsV-`tW_HDf@xJdHk<2%2N|$yeYbG>5mItlA4xPDK>3Q zQpBezmQo}^tJyz4X6Xm!e(h%79=-k7faT+GE9Rb*bVvPrRD;hF)+lAk5HspsYa2qC z?GQIi5)WVvW99(aMQ;&A@hczUjN(DkTrMTaT|cw_#>Bl|4;>!Lvn>{3-u(IT~Ngka1;e^6u z69omE%63}lq{@!>u-6XxO~XEq%U+qQ!sB`~q8a$GU9o-Y0^kK1V1qN4mFkeNIp^WN`EVnIF`ld^SGs()); + TRY_STATUS(tokenizer_.check_endl()); + return td::Status::OK(); +} + +td::Status CollatorNodeAddWhitelistedValidatorQuery::send() { + auto b = ton::create_serialize_tl_object( + adnl_id_.bits256_value(), true); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status CollatorNodeAddWhitelistedValidatorQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "success\n"; + return td::Status::OK(); +} + +td::Status CollatorNodeDelWhitelistedValidatorQuery::run() { + TRY_RESULT_ASSIGN(adnl_id_, tokenizer_.get_token()); + TRY_STATUS(tokenizer_.check_endl()); + return td::Status::OK(); +} + +td::Status CollatorNodeDelWhitelistedValidatorQuery::send() { + auto b = ton::create_serialize_tl_object( + adnl_id_.bits256_value(), false); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status CollatorNodeDelWhitelistedValidatorQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "success\n"; + return td::Status::OK(); +} + +td::Status CollatorNodeEnableWhitelistQuery::run() { + TRY_RESULT(value, tokenizer_.get_token()); + if (value != 0 && value != 1) { + return td::Status::Error("expected 0 or 1"); + } + TRY_STATUS(tokenizer_.check_endl()); + enabled_ = value; + return td::Status::OK(); +} + +td::Status CollatorNodeEnableWhitelistQuery::send() { + auto b = ton::create_serialize_tl_object(enabled_); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status CollatorNodeEnableWhitelistQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "success\n"; + return td::Status::OK(); +} + +td::Status CollatorNodeShowWhitelistQuery::run() { + TRY_STATUS(tokenizer_.check_endl()); + return td::Status::OK(); +} + +td::Status CollatorNodeShowWhitelistQuery::send() { + auto b = ton::create_serialize_tl_object(); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status CollatorNodeShowWhitelistQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, + ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "Collator node whitelist: " << (f->enabled_ ? "ENABLED" : "DISABLED") << "\n"; + td::TerminalIO::out() << f->adnl_ids_.size() << " validator adnl ids\n"; + for (const auto &id : f->adnl_ids_) { + td::TerminalIO::out() << id.to_hex() << "\n"; + } + return td::Status::OK(); +} + td::Status SetCollatorsListQuery::run() { TRY_RESULT_ASSIGN(file_name_, tokenizer_.get_token()); TRY_STATUS(tokenizer_.check_endl()); @@ -1611,9 +1697,7 @@ td::Status ClearCollatorsListQuery::run() { } td::Status ClearCollatorsListQuery::send() { - auto list = ton::create_tl_object(); - list->self_collate_ = true; - auto b = ton::create_serialize_tl_object(std::move(list)); + auto b = ton::create_serialize_tl_object(); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } @@ -1640,20 +1724,60 @@ td::Status ShowCollatorsListQuery::receive(td::BufferSlice data) { TRY_RESULT_PREFIX(list, ton::fetch_tl_object(data.as_slice(), true), "received incorrect answer: "); td::TerminalIO::out() << "Collators list:\n"; - if (list->self_collate_) { - td::TerminalIO::out() << "self_collate = true\n"; - } - if (list->use_config_41_) { - td::TerminalIO::out() << "use_config_41 = true\n"; - } if (list->shards_.empty()) { td::TerminalIO::out() << "Shard list is empty\n"; return td::Status::OK(); } for (const auto &shard : list->shards_) { td::TerminalIO::out() << "Shard " << create_shard_id(shard->shard_id_).to_str() << "\n"; + td::TerminalIO::out() << " Self collate = " << shard->self_collate_ << "\n"; + td::TerminalIO::out() << " Select mode = " << shard->select_mode_ << "\n"; for (const auto &collator : shard->collators_) { - td::TerminalIO::out() << " Collator " << collator->adnl_id_ << (collator->trusted_ ? " (trusted)" : "") << "\n"; + td::TerminalIO::out() << " Collator " << collator->adnl_id_ << "\n"; + } + } + return td::Status::OK(); +} + +td::Status GetCollationManagerStatsQuery::run() { + TRY_STATUS(tokenizer_.check_endl()); + return td::Status::OK(); +} + +td::Status GetCollationManagerStatsQuery::send() { + auto b = ton::create_serialize_tl_object(); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status GetCollationManagerStatsQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(list, + ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + if (list->local_ids_.empty()) { + td::TerminalIO::out() << "No stats\n"; + return td::Status::OK();; + } + for (auto &stats : list->local_ids_) { + td::TerminalIO::out() << "VALIDATOR ADNL ID = " << stats->adnl_id_ << "\n"; + std::map collators; + for (auto &collator: stats->collators_) { + collators[collator->adnl_id_] = collator.get(); + } + for (auto &shard : stats->shards_) { + td::TerminalIO::out() << " Shard " << create_shard_id(shard->shard_id_).to_str() << "\n"; + td::TerminalIO::out() << " Self collate = " << shard->self_collate_ << "\n"; + td::TerminalIO::out() << " Select mode = " << shard->select_mode_ << "\n"; + td::TerminalIO::out() << " Active = " << shard->active_ << "\n"; + td::TerminalIO::out() << " Collators: " << shard->collators_.size() << "\n"; + for (auto &id : shard->collators_) { + auto collator = collators[id]; + if (collator == nullptr) { + return td::Status::Error("collator not found"); + } + td::TerminalIO::out() << " " << id << " alive=" << (int)collator->alive_ + << " ping_in=" << collator->ping_in_ << "\n"; + } } } return td::Status::OK(); diff --git a/validator-engine-console/validator-engine-console-query.h b/validator-engine-console/validator-engine-console-query.h index 5708fecda..2d59c29b7 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -1457,6 +1457,91 @@ class DelShardQuery : public Query { td::int64 shard_; }; +class CollatorNodeAddWhitelistedValidatorQuery : public Query { + public: + CollatorNodeAddWhitelistedValidatorQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "collatorwhitelistadd"; + } + static std::string get_help() { + return "collatorwhitelistadd \tadd validator adnl id to collator node whitelist"; + } + std::string name() const override { + return get_name(); + } + + private: + ton::PublicKeyHash adnl_id_; +}; + +class CollatorNodeDelWhitelistedValidatorQuery : public Query { + public: + CollatorNodeDelWhitelistedValidatorQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "collatorwhitelistdel"; + } + static std::string get_help() { + return "collatorwhitelistdel \tremove validator adnl id from collator node whitelist"; + } + std::string name() const override { + return get_name(); + } + + private: + ton::PublicKeyHash adnl_id_; +}; + +class CollatorNodeEnableWhitelistQuery : public Query { + public: + CollatorNodeEnableWhitelistQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "collatorwhitelistenable"; + } + static std::string get_help() { + return "collatorwhitelistenable \tenable or disable collator node whiltelist (value is 0 or 1)"; + } + std::string name() const override { + return get_name(); + } + + private: + bool enabled_; +}; + +class CollatorNodeShowWhitelistQuery : public Query { + public: + CollatorNodeShowWhitelistQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "collatorwhitelistshow"; + } + static std::string get_help() { + return "collatorwhitelistshow\tshow collator node whitelist"; + } + std::string name() const override { + return get_name(); + } +}; + class SetCollatorsListQuery : public Query { public: SetCollatorsListQuery(td::actor::ActorId console, Tokenizer tokenizer) @@ -1517,6 +1602,25 @@ class ShowCollatorsListQuery : public Query { } }; +class GetCollationManagerStatsQuery : public Query { + public: + GetCollationManagerStatsQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "collationmanagerstats"; + } + static std::string get_help() { + return "collationmanagerstats\tshow stats of collation manager"; + } + std::string name() const override { + return get_name(); + } +}; + class SignOverlayMemberCertificateQuery : public Query { public: SignOverlayMemberCertificateQuery(td::actor::ActorId console, Tokenizer tokenizer) diff --git a/validator-engine-console/validator-engine-console.cpp b/validator-engine-console/validator-engine-console.cpp index 4c4922494..67e932115 100644 --- a/validator-engine-console/validator-engine-console.cpp +++ b/validator-engine-console/validator-engine-console.cpp @@ -157,9 +157,14 @@ void ValidatorEngineConsole::run() { add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); } diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 3722dcd28..8d2ce0bef 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -75,6 +75,7 @@ #include "common/delay.h" #include "block/precompiled-smc/PrecompiledSmartContract.h" #include "interfaces/validator-manager.h" +#include "tl-utils/lite-utils.hpp" #if TON_USE_JEMALLOC #include @@ -147,9 +148,9 @@ Config::Config(const ton::ton_api::engine_validator_config &config) { } } for (auto &col : config.collators_) { - auto key = ton::PublicKeyHash{col->adnl_id_}; + auto id = ton::adnl::AdnlNodeIdShort{col->adnl_id_}; ton::ShardIdFull shard = ton::create_shard_id(col->shard_); - config_add_collator(key, shard).ensure(); + config_add_collator(id, shard).ensure(); } config_add_full_node_adnl_id(ton::PublicKeyHash{config.fullnode_}).ensure(); @@ -175,6 +176,12 @@ Config::Config(const ton::ton_api::engine_validator_config &config) { fast_sync_member_certificates.emplace_back(adnl_id, std::move(certificate)); } } + if (config.extraconfig_->collator_node_whitelist_) { + collator_node_whiltelist_enabled = config.extraconfig_->collator_node_whitelist_->enabled_; + for (const auto& id : config.extraconfig_->collator_node_whitelist_->adnl_ids_) { + collator_node_whitelist.emplace(id); + } + } } else { state_serializer_enabled = true; } @@ -242,9 +249,11 @@ ton::tl_object_ptr Config::tl() const { val.first.tl(), std::move(temp_vec), std::move(adnl_val_vec), val.second.election_date, val.second.expire_at)); } std::vector> col_vec; - for (auto &col : collators) { - col_vec.push_back( - ton::create_tl_object(col.adnl_id.tl(), ton::create_tl_shard_id(col.shard))); + for (auto &[col, shards] : collators) { + for (auto &shard : shards) { + col_vec.push_back( + ton::create_tl_object(col.bits256_value(), ton::create_tl_shard_id(shard))); + } } std::vector> full_node_slaves_vec; @@ -263,8 +272,17 @@ ton::tl_object_ptr Config::tl() const { full_node_config_obj = full_node_config.tl(); } + ton::tl_object_ptr collator_node_whitelist_obj = {}; + if (collator_node_whiltelist_enabled || !collator_node_whitelist.empty()) { + collator_node_whitelist_obj = ton::create_tl_object(); + collator_node_whitelist_obj->enabled_ = collator_node_whiltelist_enabled; + for (const auto& id : collator_node_whitelist) { + collator_node_whitelist_obj->adnl_ids_.push_back(id.bits256_value()); + } + } + ton::tl_object_ptr extra_config_obj = {}; - if (!state_serializer_enabled || !fast_sync_member_certificates.empty()) { + if (!state_serializer_enabled || !fast_sync_member_certificates.empty() || collator_node_whitelist_obj) { // Non-default values extra_config_obj = ton::create_tl_object(); extra_config_obj->state_serializer_enabled_ = state_serializer_enabled; @@ -273,6 +291,7 @@ ton::tl_object_ptr Config::tl() const { ton::create_tl_object(adnl_id.bits256_value(), certificate.tl())); } + extra_config_obj->collator_node_whitelist_ = std::move(collator_node_whitelist_obj); } std::vector> liteserver_vec; @@ -448,28 +467,28 @@ td::Result Config::config_add_validator_adnl_id(ton::PublicKeyHash perm_ke } } -td::Result Config::config_add_collator(ton::PublicKeyHash addr, ton::ShardIdFull shard) { +td::Result Config::config_add_collator(ton::adnl::AdnlNodeIdShort addr, ton::ShardIdFull shard) { if (!shard.is_valid_ext()) { return td::Status::Error(PSTRING() << "invalid shard: " << shard.to_str()); } - Collator c{addr, shard}; - if (std::find(collators.begin(), collators.end(), c) != collators.end()) { + auto& shards = collators[addr]; + if (std::ranges::find(shards, shard) != collators[addr].end()) { return false; } - collators.push_back(c); + shards.push_back(shard); return true; } -td::Result Config::config_del_collator(ton::PublicKeyHash addr, ton::ShardIdFull shard) { +td::Result Config::config_del_collator(ton::adnl::AdnlNodeIdShort addr, ton::ShardIdFull shard) { if (!shard.is_valid_ext()) { return td::Status::Error(PSTRING() << "invalid shard: " << shard.to_str()); } - Collator c{addr, shard}; - auto it = std::find(collators.begin(), collators.end(), c); - if (it == collators.end()) { + auto& shards = collators[addr]; + auto it = std::ranges::find(shards, shard); + if (it == shards.end()) { return false; } - collators.erase(it); + shards.erase(it); return true; } @@ -1564,6 +1583,11 @@ td::Status ValidatorEngine::load_global_config() { } validator_options_.write().set_fast_state_serializer_enabled(fast_state_serializer_enabled_); + for (auto& id : config_.collator_node_whitelist) { + validator_options_.write().set_collator_node_whitelisted_validator(id, true); + } + validator_options_.write().set_collator_node_whitelist_enabled(config_.collator_node_whiltelist_enabled); + return td::Status::OK(); } @@ -1572,14 +1596,16 @@ void ValidatorEngine::set_shard_check_function() { validator_options_.write().set_shard_check_function([](ton::ShardIdFull shard) -> bool { return true; }); } else { std::vector shards = {ton::ShardIdFull(ton::masterchainId)}; - for (const auto& c : config_.collators) { - shards.push_back(c.shard); + for (const auto& [_, collator_shards] : config_.collators) { + for (const auto& shard : collator_shards) { + shards.push_back(shard); + } } for (const auto& s : config_.shards_to_monitor) { shards.push_back(s); } std::sort(shards.begin(), shards.end()); - shards.erase(std::unique(shards.begin(), shards.end()), shards.end()); + shards.erase(std::ranges::unique(shards).begin(), shards.end()); validator_options_.write().set_shard_check_function( [shards = std::move(shards)](ton::ShardIdFull shard) -> bool { for (auto s : shards) { @@ -1613,8 +1639,13 @@ void ValidatorEngine::load_collators_list() { return; } td::Ref list{true}; - list.write().unpack(*collators_list_); - validator_options_.write().set_collators_list(std::move(list)); + S = list.write().unpack(*collators_list_); + if (S.is_ok()) { + validator_options_.write().set_collators_list(std::move(list)); + } else { + LOG(ERROR) << "Invalid collators list: " << S.move_as_error(); + collators_list_ = {}; + } } void ValidatorEngine::load_empty_local_config(td::Promise promise) { @@ -2100,9 +2131,8 @@ void ValidatorEngine::start_full_node() { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_permanent_key, v.first, [](td::Unit) {}); } - for (auto &c : config_.collators) { - td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_collator_adnl_id, - ton::adnl::AdnlNodeIdShort(c.adnl_id)); + for (auto &[c, _] : config_.collators) { + td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_collator_adnl_id, c); } for (auto &x : config_.fast_sync_member_certificates) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::import_fast_sync_member_certificate, @@ -2139,9 +2169,10 @@ void ValidatorEngine::started_lite_server() { } void ValidatorEngine::start_collator() { - for (auto &c : config_.collators) { - td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_collator, - ton::adnl::AdnlNodeIdShort(c.adnl_id), c.shard); + for (auto& [id, shards] : config_.collators) { + for (auto& shard : shards) { + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_collator, id, shard); + } } started_collator(); @@ -4054,6 +4085,92 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setCollat promise.set_value(ton::create_serialize_tl_object()); } +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_collatorNodeSetWhitelistedValidator &query, + td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, + td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_modify)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + if (!started_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); + return; + } + ton::adnl::AdnlNodeIdShort adnl_id{query.adnl_id_}; + if (query.add_) { + if (!config_.collator_node_whitelist.insert(adnl_id).second) { + promise.set_value(ton::create_serialize_tl_object()); + return; + } + } else { + if (config_.collator_node_whitelist.erase(adnl_id) == 0) { + promise.set_value(ton::create_serialize_tl_object()); + return; + } + } + + validator_options_.write().set_collator_node_whitelisted_validator(adnl_id, query.add_); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, + validator_options_); + write_config([promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_control_query_error(R.move_as_error())); + } else { + promise.set_value( + ton::serialize_tl_object(ton::create_tl_object(), true)); + } + }); +} + +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_collatorNodeSetWhitelistEnabled &query, + td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, + td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_modify)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + if (!started_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); + return; + } + if (config_.collator_node_whiltelist_enabled == query.enabled_) { + promise.set_value(ton::create_serialize_tl_object()); + return; + } + config_.collator_node_whiltelist_enabled = query.enabled_; + validator_options_.write().set_collator_node_whitelist_enabled(query.enabled_); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, + validator_options_); + write_config([promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_control_query_error(R.move_as_error())); + } else { + promise.set_value( + ton::serialize_tl_object(ton::create_tl_object(), true)); + } + }); +} + +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_showCollatorNodeWhitelist &query, + td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, + td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_default)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + if (!started_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); + return; + } + ton::tl_object_ptr result = {}; + result = ton::create_tl_object(); + result->enabled_ = config_.collator_node_whiltelist_enabled; + for (const auto &id : config_.collator_node_whitelist) { + result->adnl_ids_.push_back(id.bits256_value()); + } + promise.set_value(ton::serialize_tl_object(result, true)); +} + void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getCollatorOptionsJson &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { @@ -4107,16 +4224,44 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setCollat return; } + td::Ref list{true}; + auto S = list.write().unpack(*query.list_); + if (S.is_error()) { + promise.set_value(create_control_query_error(S.move_as_error_prefix("Invalid collators list: "))); + return; + } auto s = td::json_encode(td::ToJson(*query.list_), true); - auto S = td::write_file(collators_list_file(), s); + S = td::write_file(collators_list_file(), s); if (S.is_error()) { promise.set_value(create_control_query_error(std::move(S))); return; } - collators_list_ = std::move(query.list_); - td::Ref list{true}; - list.write().unpack(*collators_list_); + validator_options_.write().set_collators_list(std::move(list)); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, + validator_options_); + promise.set_value(ton::serialize_tl_object(ton::create_tl_object(), true)); +} + +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_clearCollatorsList &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_modify)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + if (!started_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); + return; + } + + auto S = td::unlink(collators_list_file()); + if (S.is_error()) { + promise.set_value(create_control_query_error(std::move(S))); + return; + } + + td::Ref list{true, ton::validator::CollatorsList::default_list()}; + collators_list_ = {}; validator_options_.write().set_collators_list(std::move(list)); td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, validator_options_); @@ -4136,12 +4281,33 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_showColla if (collators_list_) { promise.set_value(ton::serialize_tl_object(collators_list_, true)); } else { - auto list = ton::create_tl_object(); - list->self_collate_ = true; - promise.set_value(ton::serialize_tl_object(list, true)); + promise.set_value(create_control_query_error(td::Status::Error("collators list is empty"))); } } +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getCollationManagerStats &query, + td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, + td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_default)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + if (!started_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); + return; + } + td::actor::send_closure( + validator_manager_, &ton::validator::ValidatorManagerInterface::get_collation_manager_stats, + [promise = std::move(promise)]( + td::Result> R) mutable { + if (R.is_ok()) { + promise.set_value(ton::serialize_tl_object(R.move_as_ok(), true)); + } else { + promise.set_value(create_control_query_error(R.move_as_error())); + } + }); +} + void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addCollator &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { @@ -4154,7 +4320,7 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addCollat return; } - auto id = ton::PublicKeyHash{query.adnl_id_}; + auto id = ton::adnl::AdnlNodeIdShort{query.adnl_id_}; auto shard = ton::create_shard_id(query.shard_); auto R = config_.config_add_collator(id, shard); if (R.is_error()) { @@ -4169,12 +4335,10 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addCollat if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, validator_options_); - td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_collator, - ton::adnl::AdnlNodeIdShort(id), shard); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_collator, id, shard); } if (!full_node_.empty()) { - td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_collator_adnl_id, - ton::adnl::AdnlNodeIdShort(id)); + td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_collator_adnl_id, id); } write_config([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { @@ -4229,7 +4393,7 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delCollat return; } - auto id = ton::PublicKeyHash{query.adnl_id_}; + auto id = ton::adnl::AdnlNodeIdShort{query.adnl_id_}; auto shard = ton::create_shard_id(query.shard_); auto R = config_.config_del_collator(id, shard); if (R.is_error()) { @@ -4248,12 +4412,10 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delCollat if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, validator_options_); - td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::del_collator, - ton::adnl::AdnlNodeIdShort(id), shard); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::del_collator, id, shard); } if (!full_node_.empty()) { - td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::del_collator_adnl_id, - ton::adnl::AdnlNodeIdShort(id)); + td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::del_collator_adnl_id, id); } write_config([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { @@ -4318,7 +4480,7 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_signOverl int expire_at = query.expire_at_; td::actor::send_closure( keyring_, &ton::keyring::Keyring::get_public_key, public_key_hash, - [=, promise = std::move(promise)](td::Result R) mutable { + [=, keyring = keyring_.get(), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error())); return; @@ -4330,7 +4492,7 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_signOverl return; } td::BufferSlice to_sign = certificate.to_sign_data(adnl_id); - td::actor::send_closure(keyring_, &ton::keyring::Keyring::sign_message, public_key_hash, std::move(to_sign), + td::actor::send_closure(keyring, &ton::keyring::Keyring::sign_message, public_key_hash, std::move(to_sign), [certificate = std::move(certificate), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 4a8e9bdb4..7daefb57a 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -67,14 +67,6 @@ struct Config { ton::UnixTime election_date; ton::UnixTime expire_at; }; - struct Collator { - ton::PublicKeyHash adnl_id; - ton::ShardIdFull shard; - - bool operator==(const Collator& b) const { - return adnl_id == b.adnl_id && shard == b.shard; - } - }; struct Control { ton::PublicKeyHash key; std::map clients; @@ -90,7 +82,9 @@ struct Config { std::map adnl_ids; std::set dht_ids; std::map validators; - std::vector collators; + std::map> collators; + bool collator_node_whiltelist_enabled = false; + std::set collator_node_whitelist; ton::PublicKeyHash full_node = ton::PublicKeyHash::zero(); std::vector full_node_slaves; std::map full_node_masters; @@ -120,8 +114,8 @@ struct Config { ton::UnixTime expire_at); td::Result config_add_validator_adnl_id(ton::PublicKeyHash perm_key, ton::PublicKeyHash adnl_id, ton::UnixTime expire_at); - td::Result config_add_collator(ton::PublicKeyHash addr, ton::ShardIdFull shard); - td::Result config_del_collator(ton::PublicKeyHash addr, ton::ShardIdFull shard); + td::Result config_add_collator(ton::adnl::AdnlNodeIdShort addr, ton::ShardIdFull shard); + td::Result config_del_collator(ton::adnl::AdnlNodeIdShort addr, ton::ShardIdFull shard); td::Result config_add_full_node_adnl_id(ton::PublicKeyHash id); td::Result config_add_full_node_slave(td::IPAddress addr, ton::PublicKey id); td::Result config_add_full_node_master(td::int32 port, ton::PublicKeyHash id); @@ -537,10 +531,21 @@ class ValidatorEngine : public td::actor::Actor { ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_setCollatorOptionsJson &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_collatorNodeSetWhitelistedValidator &query, + td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, + td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_collatorNodeSetWhitelistEnabled &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_showCollatorNodeWhitelist &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_setCollatorsList &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_clearCollatorsList &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_showCollatorsList &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_getCollationManagerStats &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_signOverlayMemberCertificate &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_importFastSyncMemberCertificate &query, td::BufferSlice data, diff --git a/validator/CMakeLists.txt b/validator/CMakeLists.txt index 60f66af37..abc602674 100644 --- a/validator/CMakeLists.txt +++ b/validator/CMakeLists.txt @@ -58,6 +58,7 @@ set(VALIDATOR_HEADERS import-db-slice.hpp queue-size-counter.hpp + collation-manager.hpp collator-node.hpp manager-disk.h manager-disk.hpp @@ -74,6 +75,7 @@ set(VALIDATOR_HEADERS set(VALIDATOR_SOURCE apply-block.cpp block-handle.cpp + collation-manager.cpp collator-node.cpp get-next-key-blocks.cpp import-db-slice.cpp diff --git a/validator/collation-manager.cpp b/validator/collation-manager.cpp new file mode 100644 index 000000000..2ca3a5f10 --- /dev/null +++ b/validator/collation-manager.cpp @@ -0,0 +1,373 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . +*/ +#include "collation-manager.hpp" + +#include "collator-node.hpp" +#include "fabric.h" +#include "td/utils/Random.h" + +#include +#include +#include + +namespace ton::validator { + +void CollationManager::start_up() { + td::actor::send_closure(rldp_, &rldp::Rldp::add_id, local_id_); + update_collators_list(*opts_->get_collators_list()); +} + +void CollationManager::collate_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, + std::vector prev, Ed25519_PublicKey creator, + td::Ref validator_set, td::uint64 max_answer_size, + td::CancellationToken cancellation_token, td::Promise promise) { + if (shard.is_masterchain()) { + run_collate_query(shard, min_masterchain_block_id, std::move(prev), creator, std::move(validator_set), + opts_->get_collator_options(), manager_, td::Timestamp::in(10.0), std::move(promise), + std::move(cancellation_token), 0); + return; + } + collate_shard_block(shard, min_masterchain_block_id, std::move(prev), creator, std::move(validator_set), + max_answer_size, std::move(cancellation_token), std::move(promise), td::Timestamp::in(10.0)); +} + +void CollationManager::collate_shard_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, + std::vector prev, Ed25519_PublicKey creator, + td::Ref validator_set, td::uint64 max_answer_size, + td::CancellationToken cancellation_token, + td::Promise promise, td::Timestamp timeout) { + TRY_STATUS_PROMISE(promise, cancellation_token.check()); + ShardInfo* s = select_shard_info(shard); + if (s == nullptr) { + promise.set_error( + td::Status::Error(PSTRING() << "shard " << shard.to_str() << " is not configured in collators list")); + return; + } + + adnl::AdnlNodeIdShort selected_collator = adnl::AdnlNodeIdShort::zero(); + size_t selected_idx = 0; + switch (s->select_mode) { + case CollatorsList::mode_random: { + int cnt = 0; + for (size_t i = 0; i < s->collators.size(); ++i) { + adnl::AdnlNodeIdShort collator = s->collators[i]; + if (collators_[collator].alive) { + ++cnt; + if (td::Random::fast(1, cnt) == 1) { + selected_collator = collator; + selected_idx = i; + } + } + } + break; + } + case CollatorsList::mode_ordered: { + for (size_t i = 0; i < s->collators.size(); ++i) { + adnl::AdnlNodeIdShort collator = s->collators[i]; + if (collators_[collator].alive) { + selected_collator = collator; + selected_idx = i; + break; + } + } + break; + } + case CollatorsList::mode_round_robin: { + size_t iters = 0; + for (size_t i = s->cur_idx; iters < s->collators.size(); (++i) %= s->collators.size()) { + adnl::AdnlNodeIdShort& collator = s->collators[i]; + if (collators_[collator].alive) { + selected_collator = collator; + selected_idx = i; + s->cur_idx = (i + 1) % s->collators.size(); + break; + } + } + break; + } + } + + if (selected_collator.is_zero() && s->self_collate) { + run_collate_query(shard, min_masterchain_block_id, std::move(prev), creator, std::move(validator_set), + opts_->get_collator_options(), manager_, td::Timestamp::in(10.0), std::move(promise), + std::move(cancellation_token), 0); + return; + } + + std::vector> prev_blocks; + BlockId next_block_id{shard, 0}; + for (const BlockIdExt& p : prev) { + prev_blocks.push_back(create_tl_block_id(p)); + next_block_id.seqno = std::max(next_block_id.seqno, p.seqno() + 1); + } + + promise = [=, SelfId = actor_id(this), promise = std::move(promise), + retry_at = td::Timestamp::in(0.5)](td::Result R) mutable { + if (R.is_ok()) { + promise.set_value(R.move_as_ok()); + return; + } + if (!selected_collator.is_zero()) { + td::actor::send_closure(SelfId, &CollationManager::on_collate_query_error, selected_collator); + } + LOG(INFO) << "ERROR: collate query for " << next_block_id.to_str() << " to #" << selected_idx << " (" + << selected_collator << "): " << R.error(); + if (timeout < retry_at) { + promise.set_error(R.move_as_error()); + return; + } + delay_action( + [=, promise = std::move(promise)]() mutable { + td::actor::send_closure(SelfId, &CollationManager::collate_shard_block, shard, min_masterchain_block_id, prev, + creator, validator_set, max_answer_size, cancellation_token, std::move(promise), + timeout); + }, + retry_at); + }; + + if (selected_collator.is_zero()) { + promise.set_error(td::Status::Error(PSTRING() << "shard " << shard.to_str() << " has no alive collator node")); + return; + } + + td::BufferSlice query = create_serialize_tl_object( + create_tl_shard_id(shard), validator_set->get_catchain_seqno(), std::move(prev_blocks), creator.as_bits256()); + LOG(INFO) << "sending collate query for " << next_block_id.to_str() << ": send to #" << selected_idx << "(" + << selected_collator << ")"; + + td::Promise P = [=, SelfId = actor_id(this), promise = std::move(promise), + timer = td::Timer()](td::Result R) mutable { + TRY_RESULT_PROMISE_PREFIX(promise, data, std::move(R), "rldp query failed: "); + auto r_error = fetch_tl_object(data, true); + if (r_error.is_ok()) { + auto error = r_error.move_as_ok(); + promise.set_error(td::Status::Error(error->code_, error->message_)); + return; + } + TRY_RESULT_PROMISE(promise, f, fetch_tl_object(data, true)); + TRY_RESULT_PROMISE(promise, candidate, + CollatorNode::deserialize_candidate(std::move(f), td::narrow_cast(max_answer_size))); + if (candidate.pubkey.as_bits256() != creator.as_bits256()) { + promise.set_error(td::Status::Error("collate query: block candidate source mismatch")); + return; + } + if (candidate.id.id != next_block_id) { + promise.set_error(td::Status::Error("collate query: block id mismatch")); + return; + } + LOG(INFO) << "got collated block " << next_block_id.to_str() << " from #" << selected_idx << " (" + << selected_collator << ") in " << timer.elapsed() << "s"; + promise.set_result(std::move(candidate)); + }; + td::actor::send_closure(rldp_, &rldp::Rldp::send_query_ex, local_id_, selected_collator, "collatequery", std::move(P), + timeout, std::move(query), max_answer_size); +} + +void CollationManager::update_options(td::Ref opts) { + auto old_list = opts_->get_collators_list(); + opts_ = std::move(opts); + auto list = opts_->get_collators_list(); + if (old_list != list) { + update_collators_list(*list); + } +} + +void CollationManager::validator_group_started(ShardIdFull shard) { + if (active_validator_groups_[shard]++ != 0) { + return; + } + ShardInfo* s = select_shard_info(shard); + if (s == nullptr) { + return; + } + if (s->active_cnt++ != 0) { + return; + } + for (adnl::AdnlNodeIdShort id : s->collators) { + CollatorInfo& collator = collators_[id]; + collator.active_cnt++; + } + alarm(); +} + +void CollationManager::validator_group_finished(ShardIdFull shard) { + if (--active_validator_groups_[shard] != 0) { + return; + } + active_validator_groups_.erase(shard); + ShardInfo* s = select_shard_info(shard); + if (s == nullptr) { + return; + } + if (--s->active_cnt != 0) { + return; + } + for (adnl::AdnlNodeIdShort id : s->collators) { + CollatorInfo& collator = collators_[id]; + --collator.active_cnt; + } + alarm(); +} + +void CollationManager::get_stats( + td::Promise> promise) { + auto stats = create_tl_object(); + stats->adnl_id_ = local_id_.bits256_value(); + for (ShardInfo& s : shards_) { + auto obj = create_tl_object(); + obj->shard_id_ = create_tl_shard_id(s.shard_id); + obj->active_ = s.active_cnt; + obj->self_collate_ = s.self_collate; + switch (s.select_mode) { + case CollatorsList::mode_random: + obj->select_mode_ = "random"; + break; + case CollatorsList::mode_ordered: + obj->select_mode_ = "ordered"; + break; + case CollatorsList::mode_round_robin: + obj->select_mode_ = "round_robin"; + break; + } + for (adnl::AdnlNodeIdShort& id : s.collators) { + obj->collators_.push_back(id.bits256_value()); + } + stats->shards_.push_back(std::move(obj)); + } + for (auto& [id, collator] : collators_) { + auto obj = create_tl_object(); + obj->adnl_id_ = id.bits256_value(); + obj->active_ = collator.active_cnt; + obj->alive_ = collator.alive; + if (collator.active_cnt && !collator.sent_ping) { + obj->ping_in_ = collator.ping_at.in(); + } else { + obj->ping_in_ = -1.0; + } + stats->collators_.push_back(std::move(obj)); + } + promise.set_value(std::move(stats)); +} + +void CollationManager::update_collators_list(const CollatorsList& collators_list) { + shards_.clear(); + for (auto& [_, collator] : collators_) { + collator.active_cnt = 0; + } + auto old_collators = std::move(collators_); + collators_.clear(); + for (const auto& shard : collators_list.shards) { + shards_.push_back({.shard_id = shard.shard_id, .select_mode = shard.select_mode, .collators = shard.collators}); + for (auto id : shard.collators) { + auto it = old_collators.find(id); + if (it == old_collators.end()) { + collators_[id]; + } else { + collators_[id] = std::move(it->second); + old_collators.erase(it); + } + } + } + for (auto& [shard, _] : active_validator_groups_) { + ShardInfo* s = select_shard_info(shard); + if (s == nullptr) { + continue; + } + if (s->active_cnt++ != 0) { + continue; + } + for (adnl::AdnlNodeIdShort id : s->collators) { + CollatorInfo& collator = collators_[id]; + collator.active_cnt++; + } + } + alarm(); +} + +CollationManager::ShardInfo* CollationManager::select_shard_info(ShardIdFull shard) { + for (auto& s : shards_) { + if (shard_intersects(shard, s.shard_id)) { + return &s; + } + } + return nullptr; +} + +void CollationManager::alarm() { + alarm_timestamp() = td::Timestamp::never(); + for (auto& [id, collator] : collators_) { + if (collator.active_cnt == 0 || collator.sent_ping) { + continue; + } + if (collator.ping_at.is_in_past()) { + collator.sent_ping = true; + td::BufferSlice query = create_serialize_tl_object(0); + td::Promise P = [=, SelfId = actor_id(this)](td::Result R) mutable { + td::actor::send_closure(SelfId, &CollationManager::got_pong, id, std::move(R)); + }; + LOG(DEBUG) << "sending ping to " << id; + td::actor::send_closure(rldp_, &rldp::Rldp::send_query, local_id_, id, "collatorping", std::move(P), + td::Timestamp::in(2.0), std::move(query)); + } else { + alarm_timestamp().relax(collator.ping_at); + } + } +} + +void CollationManager::got_pong(adnl::AdnlNodeIdShort id, td::Result R) { + auto it = collators_.find(id); + if (it == collators_.end()) { + return; + } + CollatorInfo& collator = it->second; + collator.sent_ping = false; + + auto r_pong = [&]() -> td::Result> { + TRY_RESULT_PREFIX(data, std::move(R), "rldp query error: "); + auto r_error = fetch_tl_object(data, true); + if (r_error.is_ok()) { + auto error = r_error.move_as_ok(); + return td::Status::Error(error->code_, error->message_); + } + return fetch_tl_object(data, true); + }(); + if (r_pong.is_error()) { + LOG(DEBUG) << "pong from " << id << " : " << r_pong.move_as_error(); + collator.alive = false; + } else { + LOG(DEBUG) << "pong from " << id << " : OK"; + collator.alive = true; + } + collator.ping_at = td::Timestamp::in(td::Random::fast(10.0, 20.0)); + if (collator.active_cnt && !collator.sent_ping) { + alarm_timestamp().relax(collator.ping_at); + } +} + +void CollationManager::on_collate_query_error(adnl::AdnlNodeIdShort id) { + auto it = collators_.find(id); + if (it == collators_.end()) { + return; + } + CollatorInfo& collator = it->second; + collator.ping_at = td::Timestamp::now(); + if (collator.active_cnt && !collator.sent_ping) { + alarm_timestamp().relax(collator.ping_at); + } +} + +} // namespace ton::validator diff --git a/validator/collation-manager.hpp b/validator/collation-manager.hpp new file mode 100644 index 000000000..7ceea1e6b --- /dev/null +++ b/validator/collation-manager.hpp @@ -0,0 +1,87 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . +*/ +#pragma once + +#include "interfaces/validator-manager.h" +#include "rldp/rldp.h" +#include + +namespace ton::validator { + +class ValidatorManager; + +class CollationManager : public td::actor::Actor { + public: + CollationManager(adnl::AdnlNodeIdShort local_id, td::Ref opts, + td::actor::ActorId manager, td::actor::ActorId rldp) + : local_id_(local_id), opts_(opts), manager_(manager), rldp_(rldp) { + } + + void start_up() override; + void alarm() override; + + void collate_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, + Ed25519_PublicKey creator, td::Ref validator_set, td::uint64 max_answer_size, + td::CancellationToken cancellation_token, td::Promise promise); + + void update_options(td::Ref opts); + + void validator_group_started(ShardIdFull shard); + void validator_group_finished(ShardIdFull shard); + + void get_stats(td::Promise> promise); + + private: + adnl::AdnlNodeIdShort local_id_; + td::Ref opts_; + td::actor::ActorId manager_; + td::actor::ActorId rldp_; + + void collate_shard_block(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, + Ed25519_PublicKey creator, td::Ref validator_set, td::uint64 max_answer_size, + td::CancellationToken cancellation_token, td::Promise promise, + td::Timestamp timeout); + + void update_collators_list(const CollatorsList& collators_list); + + struct CollatorInfo { + bool alive = false; + td::Timestamp ping_at = td::Timestamp::now(); + bool sent_ping = false; + size_t active_cnt = 0; + }; + std::map collators_; + + struct ShardInfo { + ShardIdFull shard_id; + CollatorsList::SelectMode select_mode; + std::vector collators; + bool self_collate = false; + size_t cur_idx = 0; + + size_t active_cnt = 0; + }; + std::vector shards_; + + std::map active_validator_groups_; + + ShardInfo* select_shard_info(ShardIdFull shard); + void got_pong(adnl::AdnlNodeIdShort id, td::Result R); + void on_collate_query_error(adnl::AdnlNodeIdShort id); +}; + +} // namespace ton::validator diff --git a/validator/collator-node.cpp b/validator/collator-node.cpp index 0b4204d86..776fbc9e9 100644 --- a/validator/collator-node.cpp +++ b/validator/collator-node.cpp @@ -54,17 +54,22 @@ void CollatorNode::start_up() { td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, local_id_, adnl::Adnl::int_to_bytestring(ton_api::collatorNode_generateBlock::ID), std::make_unique(actor_id(this))); + td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, local_id_, + adnl::Adnl::int_to_bytestring(ton_api::collatorNode_ping::ID), + std::make_unique(actor_id(this))); td::actor::send_closure(rldp_, &rldp::Rldp::add_id, adnl::AdnlNodeIdShort(local_id_)); } void CollatorNode::tear_down() { td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, local_id_, adnl::Adnl::int_to_bytestring(ton_api::collatorNode_generateBlock::ID)); + td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, local_id_, + adnl::Adnl::int_to_bytestring(ton_api::collatorNode_ping::ID)); } void CollatorNode::add_shard(ShardIdFull shard) { CHECK(shard.is_valid_ext() && !shard.is_masterchain()); - if (std::find(collating_shards_.begin(), collating_shards_.end(), shard) != collating_shards_.end()) { + if (std::ranges::find(collating_shards_, shard) != collating_shards_.end()) { return; } LOG(INFO) << "Collator node: local_id=" << local_id_ << " , shard=" << shard.to_str(); @@ -72,7 +77,7 @@ void CollatorNode::add_shard(ShardIdFull shard) { } void CollatorNode::del_shard(ShardIdFull shard) { - auto it = std::find(collating_shards_.begin(), collating_shards_.end(), shard); + auto it = std::ranges::find(collating_shards_, shard); if (it != collating_shards_.end()) { collating_shards_.erase(it); } @@ -127,7 +132,7 @@ void CollatorNode::new_masterchain_block_notification(td::Ref } } for (auto it = validator_groups_.begin(); it != validator_groups_.end();) { - if (new_shards.count(it->first)) { + if (new_shards.contains(it->first)) { ++it; } else { it->second.cleanup(); @@ -253,7 +258,7 @@ void CollatorNode::CacheEntry::cancel(td::Status reason) { } static td::BufferSlice serialize_error(td::Status error) { - return create_serialize_tl_object(error.code(), error.message().c_str()); + return create_serialize_tl_object(error.code(), error.message().c_str()); } static BlockCandidate change_creator(BlockCandidate block, Ed25519_PublicKey creator, CatchainSeqno& cc_seqno, @@ -285,25 +290,32 @@ static BlockCandidate change_creator(BlockCandidate block, Ed25519_PublicKey cre void CollatorNode::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise) { - td::Promise new_promise = [promise = std::move(promise), src](td::Result R) mutable { + promise = [promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { - LOG(INFO) << "adnl query from " << src << ", error: " << R.error(); if (R.error().code() == ErrorCode::timeout) { promise.set_error(R.move_as_error()); } else { promise.set_result(serialize_error(R.move_as_error())); } } else { - LOG(INFO) << "adnl query from " << src << ", success"; - promise.set_result(create_serialize_tl_object( - serialize_candidate(R.move_as_ok(), true))); + promise.set_result(R.move_as_ok()); } }; - if (!validator_adnl_ids_.count(src)) { - new_promise.set_error(td::Status::Error("src is not a validator")); + if (!opts_->check_collator_node_whitelist(src)) { + promise.set_error(td::Status::Error("not authorized")); + return; + } + if (!validator_adnl_ids_.contains(src)) { + promise.set_error(td::Status::Error("src is not a validator")); + return; + } + auto r_ping = fetch_tl_object(data, true); + if (r_ping.is_ok()) { + process_ping(src, *r_ping.ok_ref(), std::move(promise)); return; } - TRY_RESULT_PROMISE(new_promise, f, fetch_tl_object(data, true)); + + TRY_RESULT_PROMISE(promise, f, fetch_tl_object(data, true)); ShardIdFull shard = create_shard_id(f->shard_); CatchainSeqno cc_seqno = f->cc_seqno_; std::vector prev_blocks; @@ -311,6 +323,16 @@ void CollatorNode::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data prev_blocks.push_back(create_block_id(b)); } Ed25519_PublicKey creator(f->creator_); + td::Promise new_promise = [promise = std::move(promise), src, + shard](td::Result R) mutable { + if (R.is_error()) { + LOG(INFO) << "collate query from " << src << ", shard=" << shard.to_str() << ": error: " << R.error(); + promise.set_error(R.move_as_error()); + } else { + LOG(INFO) << "collate query from " << src << ", shard=" << shard.to_str() << ": success"; + promise.set_result(serialize_tl_object(serialize_candidate(R.move_as_ok(), true), true)); + } + }; new_promise = [new_promise = std::move(new_promise), creator, manager = manager_](td::Result R) mutable { TRY_RESULT_PROMISE(new_promise, block, std::move(R)); @@ -427,9 +449,15 @@ void CollatorNode::process_result(std::shared_ptr cache_entry, td::R cache_entry->promises.clear(); } +void CollatorNode::process_ping(adnl::AdnlNodeIdShort src, ton_api::collatorNode_ping& ping, + td::Promise promise) { + LOG(DEBUG) << "got ping from " << src; + promise.set_result(create_serialize_tl_object(0)); +} + bool CollatorNode::can_collate_shard(ShardIdFull shard) const { - return std::any_of(collating_shards_.begin(), collating_shards_.end(), - [&](const ShardIdFull& our_shard) { return shard_intersects(shard, our_shard); }); + return std::ranges::any_of(collating_shards_, + [&](const ShardIdFull& our_shard) { return shard_intersects(shard, our_shard); }); } tl_object_ptr CollatorNode::serialize_candidate(const BlockCandidate& block, diff --git a/validator/collator-node.hpp b/validator/collator-node.hpp index 1a71f08a4..a361ceb30 100644 --- a/validator/collator-node.hpp +++ b/validator/collator-node.hpp @@ -43,6 +43,7 @@ class CollatorNode : public td::actor::Actor { private: void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise); + void process_ping(adnl::AdnlNodeIdShort src, ton_api::collatorNode_ping& ping, td::Promise promise); bool can_collate_shard(ShardIdFull shard) const; diff --git a/validator/full-node.cpp b/validator/full-node.cpp index bb806e281..4488b77df 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -502,10 +502,16 @@ td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard) { if (pfx_len > wc_monitor_min_split_) { shard = shard_prefix(shard, wc_monitor_min_split_); } - auto it = shards_.find(shard); - if (it != shards_.end()) { - update_shard_actor(shard, it->second.active); - return it->second.actor.get(); + while (true) { + auto it = shards_.find(shard); + if (it != shards_.end()) { + update_shard_actor(shard, it->second.active); + return it->second.actor.get(); + } + if (shard.pfx_len() == 0) { + break; + } + shard = shard_parent(shard); } // Special case if shards_ was not yet initialized. diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index d91ee1bf6..72154f861 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -45,7 +45,7 @@ class Collator final : public td::actor::Actor { } static constexpr long long supported_capabilities() { return ton::capCreateStatsEnabled | ton::capBounceMsgBody | ton::capReportVersion | ton::capShortDequeue | - ton::capStoreOutMsgQueueSize | ton::capMsgMetadata | ton::capDeferMessages; + ton::capStoreOutMsgQueueSize | ton::capMsgMetadata | ton::capDeferMessages | ton::capFullCollatedData; } using LtCellRef = block::LtCellRef; using NewOutMsg = block::NewOutMsg; diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 5b82d481a..677b73ae0 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -730,6 +730,8 @@ bool Collator::unpack_last_mc_state() { store_out_msg_queue_size_ = config_->has_capability(ton::capStoreOutMsgQueueSize); msg_metadata_enabled_ = config_->has_capability(ton::capMsgMetadata); deferring_messages_enabled_ = config_->has_capability(ton::capDeferMessages); + full_collated_data_ = config_->has_capability(capFullCollatedData); + LOG(DEBUG) << "full_collated_data is " << full_collated_data_; 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_) { @@ -768,8 +770,6 @@ bool Collator::unpack_last_mc_state() { << " have been enabled in global configuration, but we support only " << supported_version() << " (upgrade validator software?)"; } - full_collated_data_ = config_->get_collator_config(false).full_collated_data; - LOG(DEBUG) << "full_collated_data is " << full_collated_data_; // TODO: extract start_lt and end_lt from prev_mc_block as well // std::cerr << " block::gen::ShardState::print_ref(mc_state_root) = "; // block::gen::t_ShardState.print_ref(std::cerr, mc_state_root, 2); @@ -817,6 +817,9 @@ bool Collator::request_neighbor_msg_queues() { auto neighbor_list = shard_conf_->get_neighbor_shard_hash_ids(shard_); LOG(DEBUG) << "got a preliminary list of " << neighbor_list.size() << " neighbors for " << shard_.to_str(); for (ton::BlockId blk_id : neighbor_list) { + if (blk_id.seqno == 0 && blk_id.shard_full() != shard_) { + continue; + } auto shard_ptr = shard_conf_->get_shard_hash(ton::ShardIdFull(blk_id)); if (shard_ptr.is_null()) { return fatal_error(-667, "cannot obtain shard hash for neighbor "s + blk_id.to_str()); diff --git a/validator/impl/shard.hpp b/validator/impl/shard.hpp index c8c7aca20..b13be0219 100644 --- a/validator/impl/shard.hpp +++ b/validator/impl/shard.hpp @@ -170,9 +170,6 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { block::WorkchainSet get_workchain_list() const override { return config_ ? config_->get_workchain_list() : block::WorkchainSet(); } - block::CollatorConfig get_collator_config(bool need_collator_nodes) const override { - return config_ ? config_->get_collator_config(need_collator_nodes) : block::CollatorConfig(); - } private: ZeroStateIdExt zerostate_id_; diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index ca00e00e9..4c6264025 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -1536,6 +1536,9 @@ bool ValidateQuery::request_neighbor_queues() { auto neighbor_list = new_shard_conf_->get_neighbor_shard_hash_ids(shard_); LOG(DEBUG) << "got a preliminary list of " << neighbor_list.size() << " neighbors for " << shard_.to_str(); for (ton::BlockId blk_id : neighbor_list) { + if (blk_id.seqno == 0 && blk_id.shard_full() != shard_) { + continue; + } auto shard_ptr = new_shard_conf_->get_shard_hash(ton::ShardIdFull(blk_id)); if (shard_ptr.is_null()) { return reject_query("cannot obtain shard hash for neighbor "s + blk_id.to_str()); @@ -2305,6 +2308,12 @@ bool ValidateQuery::prepare_out_msg_queue_size() { have_out_msg_queue_size_in_state_ = true; return true; } + if (ps_.out_msg_queue_->is_empty()) { + old_out_msg_queue_size_ = 0; + out_msg_queue_size_known_ = true; + have_out_msg_queue_size_in_state_ = true; + return true; + } if (!store_out_msg_queue_size_) { // Don't need it return true; } diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index a21a56e90..585d553c5 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -113,7 +113,7 @@ class ValidateQuery : public td::actor::Actor { } static constexpr long long supported_capabilities() { return ton::capCreateStatsEnabled | ton::capBounceMsgBody | ton::capReportVersion | ton::capShortDequeue | - ton::capStoreOutMsgQueueSize | ton::capMsgMetadata | ton::capDeferMessages; + ton::capStoreOutMsgQueueSize | ton::capMsgMetadata | ton::capDeferMessages | ton::capFullCollatedData; } public: diff --git a/validator/impl/validator-set.hpp b/validator/impl/validator-set.hpp index 3141f36c5..8b833e047 100644 --- a/validator/impl/validator-set.hpp +++ b/validator/impl/validator-set.hpp @@ -50,6 +50,7 @@ class ValidatorSetQ : public ValidatorSet { td::Ref signatures) const override; td::Result check_approve_signatures(RootHash root_hash, FileHash file_hash, td::Ref signatures) const override; + const ValidatorDescr* find_validator(const NodeIdShort& id) const override; ValidatorSetQ* make_copy() const override; @@ -62,8 +63,6 @@ class ValidatorSetQ : public ValidatorSet { ValidatorWeight total_weight_; std::vector ids_; std::vector> ids_map_; - - const ValidatorDescr* find_validator(const NodeIdShort& id) const; }; class ValidatorSetCompute { diff --git a/validator/interfaces/shard.h b/validator/interfaces/shard.h index 3383f6fc2..3546f0a39 100644 --- a/validator/interfaces/shard.h +++ b/validator/interfaces/shard.h @@ -85,7 +85,6 @@ class MasterchainState : virtual public ShardState { virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0; virtual td::Result> get_config_holder() const = 0; virtual block::WorkchainSet get_workchain_list() const = 0; - virtual block::CollatorConfig get_collator_config(bool need_collator_nodes) const = 0; virtual td::Status prepare() { return td::Status::OK(); } diff --git a/validator/interfaces/validator-set.h b/validator/interfaces/validator-set.h index b71c0bfea..20569df75 100644 --- a/validator/interfaces/validator-set.h +++ b/validator/interfaces/validator-set.h @@ -35,6 +35,7 @@ class ValidatorSet : public td::CntObject { virtual td::uint32 get_validator_set_hash() const = 0; virtual ShardId get_validator_set_from() const = 0; virtual std::vector export_vector() const = 0; + virtual const ValidatorDescr* find_validator(const NodeIdShort& id) const = 0; virtual td::Result check_signatures(RootHash root_hash, FileHash file_hash, td::Ref signatures) const = 0; virtual td::Result check_approve_signatures(RootHash root_hash, FileHash file_hash, diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index c54ccde55..c6d7337f9 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -457,6 +457,11 @@ class ValidatorManagerImpl : public ValidatorManager { UNREACHABLE(); } + void get_collation_manager_stats( + td::Promise> promise) override { + UNREACHABLE(); + } + void update_options(td::Ref opts) override { opts_ = std::move(opts); } diff --git a/validator/manager-hardfork.hpp b/validator/manager-hardfork.hpp index daf83a197..860243fb9 100644 --- a/validator/manager-hardfork.hpp +++ b/validator/manager-hardfork.hpp @@ -525,6 +525,11 @@ class ValidatorManagerImpl : public ValidatorManager { UNREACHABLE(); } + void get_collation_manager_stats( + td::Promise> promise) override { + UNREACHABLE(); + } + private: td::Ref opts_; diff --git a/validator/manager.cpp b/validator/manager.cpp index f97263570..3f59d784d 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -799,7 +799,13 @@ void ValidatorManagerImpl::wait_neighbor_msg_queue_proofs( public: Worker(size_t pending, td::Promise>> promise) : pending_(pending), promise_(std::move(promise)) { - CHECK(pending_ > 0); + } + + void start_up() override { + if (pending_ == 0) { + promise_.set_result(std::move(result_)); + stop(); + } } void on_result(td::Ref res) { @@ -2462,15 +2468,27 @@ td::actor::ActorOwn ValidatorManagerImpl::create_validator_group auto validator_id = get_validator(shard, validator_set); CHECK(!validator_id.is_zero()); + auto descr = validator_set->find_validator(validator_id.bits256_value()); + CHECK(descr); + auto adnl_id = adnl::AdnlNodeIdShort{ + descr->addr.is_zero() ? ValidatorFullId{descr->key}.compute_short_id().bits256_value() : descr->addr}; auto G = td::actor::create_actor( - PSTRING() << "valgroup" << shard.to_str(), shard, validator_id, session_id, validator_set, key_seqno, - last_masterchain_state_->get_collator_config(true), opts, keyring_, adnl_, rldp_, overlays_, db_root_, - actor_id(this), init_session, opts_->check_unsafe_resync_allowed(validator_set->get_catchain_seqno()), opts_, + PSTRING() << "valgroup" << shard.to_str(), shard, validator_id, session_id, validator_set, key_seqno, opts, + keyring_, adnl_, rldp_, overlays_, db_root_, actor_id(this), get_collation_manager(adnl_id), init_session, + opts_->check_unsafe_resync_allowed(validator_set->get_catchain_seqno()), opts_, opts_->need_monitor(shard, last_masterchain_state_)); return G; } } +td::actor::ActorId ValidatorManagerImpl::get_collation_manager(adnl::AdnlNodeIdShort adnl_id) { + auto &actor = collation_managers_[adnl_id]; + if (actor.empty()) { + actor = td::actor::create_actor("collation", adnl_id, opts_, actor_id(this), rldp_); + } + return actor.get(); +} + void ValidatorManagerImpl::add_handle_to_lru(BlockHandle handle) { auto it = handle_lru_map_.find(handle->id()); if (it != handle_lru_map_.end()) { @@ -3458,6 +3476,9 @@ void ValidatorManagerImpl::update_options(td::Ref opts) for (auto &collator : collator_nodes_) { td::actor::send_closure(collator.second.actor, &CollatorNode::update_options, opts); } + for (auto &[_, c] : collation_managers_) { + td::actor::send_closure(c, &CollationManager::update_options, opts); + } opts_ = std::move(opts); } @@ -3492,6 +3513,54 @@ void ValidatorManagerImpl::del_collator(adnl::AdnlNodeIdShort id, ShardIdFull sh } } +void ValidatorManagerImpl::get_collation_manager_stats( + td::Promise> promise) { + class Cb : public td::actor::Actor { + public: + explicit Cb(td::Promise> promise) + : promise_(std::move(promise)) { + } + + void got_stats(tl_object_ptr s) { + result_.push_back(std::move(s)); + dec_pending(); + } + + void inc_pending() { + ++pending_; + } + + void dec_pending() { + CHECK(pending_ > 0); + --pending_; + if (pending_ == 0) { + promise_.set_result(create_tl_object(std::move(result_))); + stop(); + } + } + + private: + td::Promise> promise_; + size_t pending_ = 1; + std::vector> result_; + }; + auto callback = td::actor::create_actor("stats", std::move(promise)).release(); + + for (auto &[_, actor] : collation_managers_) { + td::actor::send_closure(callback, &Cb::inc_pending); + td::actor::send_closure( + actor, &CollationManager::get_stats, + [callback](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(callback, &Cb::dec_pending); + } else { + td::actor::send_closure(callback, &Cb::got_stats, R.move_as_ok()); + } + }); + } + td::actor::send_closure(callback, &Cb::dec_pending); +} + void ValidatorManagerImpl::add_persistent_state_description(td::Ref desc) { auto now = (UnixTime)td::Clocks::system(); if (desc->end_time <= now) { diff --git a/validator/manager.hpp b/validator/manager.hpp index b7e457e27..055adf304 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -283,12 +283,15 @@ class ValidatorManagerImpl : public ValidatorManager { td::Ref validator_set, BlockSeqno key_seqno, validatorsession::ValidatorSessionOptions opts, bool create_catchain); + td::actor::ActorId get_collation_manager(adnl::AdnlNodeIdShort adnl_id); + struct ValidatorGroupEntry { td::actor::ActorOwn actor; ShardIdFull shard; }; std::map validator_groups_; std::map next_validator_groups_; + std::map> collation_managers_; std::set check_gc_list_; std::vector gc_list_; @@ -634,6 +637,9 @@ class ValidatorManagerImpl : public ValidatorManager { void add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) override; void del_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) override; + void get_collation_manager_stats( + td::Promise> promise) override; + void get_out_msg_queue_size(BlockIdExt block_id, td::Promise promise) override { if (queue_size_counter_.empty()) { if (last_masterchain_state_.is_null()) { diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index 55798a482..f95266b33 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -65,7 +65,10 @@ void ValidatorGroup::generate_block_candidate( td::actor::send_closure(SelfId, &ValidatorGroup::generated_block_candidate, source_info, std::move(cache), std::move(R)); }; - collate_block(source_info, td::Timestamp::in(10.0), std::move(P)); + 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()}, validator_set_, + max_answer_size, cancellation_token_source_.get_cancellation_token(), std::move(P)); } void ValidatorGroup::generated_block_candidate(validatorsession::BlockSourceInfo source_info, @@ -218,9 +221,9 @@ void ValidatorGroup::accept_block_query(BlockIdExt block_id, td::Ref return; } LOG_CHECK(R.error().code() == ErrorCode::timeout || R.error().code() == ErrorCode::notready) << R.move_as_error(); - td::actor::send_closure(SelfId, &ValidatorGroup::accept_block_query, block_id, std::move(block), - std::move(prev), std::move(sig_set), std::move(approve_sig_set), send_broadcast_mode, - std::move(promise), true); + td::actor::send_closure(SelfId, &ValidatorGroup::accept_block_query, block_id, std::move(block), std::move(prev), + std::move(sig_set), std::move(approve_sig_set), send_broadcast_mode, std::move(promise), + true); } else { promise.set_value(R.move_as_ok()); } @@ -292,7 +295,8 @@ std::unique_ptr ValidatorGroup::ma td::actor::send_closure(id_, &ValidatorGroup::generate_block_candidate, std::move(source_info), std::move(promise)); } - void on_block_committed(validatorsession::BlockSourceInfo source_info, validatorsession::ValidatorSessionRootHash root_hash, + void on_block_committed(validatorsession::BlockSourceInfo source_info, + validatorsession::ValidatorSessionRootHash root_hash, validatorsession::ValidatorSessionFileHash file_hash, td::BufferSlice data, std::vector> signatures, std::vector> approve_signatures, @@ -409,7 +413,7 @@ void ValidatorGroup::start(std::vector prev, BlockIdExt min_masterch stats.last_key_block_seqno = last_key_block_seqno_; stats.timestamp = td::Clocks::system(); td::uint32 idx = 0; - for (const auto& node : validator_set_->export_vector()) { + for (const auto &node : validator_set_->export_vector()) { PublicKeyHash id = ValidatorFullId{node.key}.compute_short_id(); if (id == local_id_) { stats.self_idx = idx; @@ -489,7 +493,7 @@ void ValidatorGroup::get_validator_group_info_for_litequery_cont( auto result = create_tl_object(); result->next_block_id_ = create_tl_lite_block_id_simple(next_block_id); - for (const BlockIdExt& prev : prev_block_ids_) { + for (const BlockIdExt &prev : prev_block_ids_) { result->prev_.push_back(create_tl_lite_block_id(prev)); } result->cc_seqno_ = validator_set_->get_catchain_seqno(); @@ -497,146 +501,6 @@ void ValidatorGroup::get_validator_group_info_for_litequery_cont( promise.set_result(std::move(result)); } -void ValidatorGroup::collate_block(validatorsession::BlockSourceInfo source_info, td::Timestamp timeout, - td::Promise promise, unsigned max_retries) { - if (source_info.round < last_known_round_id_) { - promise.set_error(td::Status::Error("too old")); - return; - } - BlockId next_block_id = create_next_block_id_simple(); - adnl::AdnlNodeIdShort collator_adnl_id = adnl::AdnlNodeIdShort::zero(); - bool self_collate = false; - bool trusted_collator = false; - - if (shard_.is_masterchain()) { - self_collate = true; - } else { - for (const auto &s : opts_->get_collators_list()->shards) { - if (!shard_intersects(s.shard_id, shard_)) { - continue; - } - if (!s.collators.empty()) { - const CollatorsList::Collator &col = s.collators[td::Random::fast(0, s.collators.size() - 1)]; - collator_adnl_id = col.adnl_id; - trusted_collator = col.trusted; - break; - } - } - if (collator_adnl_id.is_zero()) { - if (opts_->get_collators_list()->self_collate) { - self_collate = true; - } else if (opts_->get_collators_list()->use_config_41) { - // TODO: some way to choose node (similar to "unreliability" in full-node) - int cnt = 0; - for (const block::CollatorNodeDescr &c : collator_config_.collator_nodes) { - if (shard_intersects(shard_, c.shard)) { - if (td::Random::fast(0, cnt) == 0) { - collator_adnl_id = adnl::AdnlNodeIdShort(c.adnl_id); - } - ++cnt; - } - } - } - } - } - if (self_collate) { - run_collate_query(shard_, min_masterchain_block_id_, prev_block_ids_, - Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, validator_set_, - opts_->get_collator_options(), manager_, td::Timestamp::in(10.0), std::move(promise), - cancellation_token_source_.get_cancellation_token(), 0); - return; - } - if (collator_adnl_id.is_zero()) { - promise.set_error(td::Status::Error(PSTRING() << "no collator for shard " << shard_.to_str())); - return; - } - - promise = td::PromiseCreator::lambda([=, SelfId = actor_id(this), promise = std::move(promise), - timer = td::Timer()](td::Result R) mutable { - if (R.is_ok()) { - LOG(INFO) << "collate query for " << next_block_id.to_str() << ": success, time=" << timer.elapsed() << "s"; - promise.set_result(R.move_as_ok()); - return; - } - bool retry = (!timeout || !timeout.is_in_past()) && max_retries > 0; - LOG(WARNING) << "collate query for " << next_block_id.to_str() << ": " << R.error() << ", time=" << timer.elapsed() - << "s, " << (retry ? "retrying" : "giving up"); - if (retry) { - td::actor::send_closure(SelfId, &ValidatorGroup::collate_block, source_info, timeout, std::move(promise), - max_retries - 1); - } else { - promise.set_result(td::Status::Error(ErrorCode::timeout, "timeout")); - } - }); - - std::vector> prev_blocks; - for (const BlockIdExt &p : prev_block_ids_) { - prev_blocks.push_back(create_tl_block_id(p)); - } - td::BufferSlice query = create_serialize_tl_object( - create_tl_shard_id(shard_), validator_set_->get_catchain_seqno(), std::move(prev_blocks), - local_id_full_.ed25519_value().raw()); - - auto P = td::PromiseCreator::lambda( - [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error_prefix("rldp query failed: ")); - return; - } - td::actor::send_closure(SelfId, &ValidatorGroup::receive_collate_query_response, source_info, R.move_as_ok(), - trusted_collator, std::move(promise)); - }); - LOG(INFO) << "sending collate query for " << next_block_id.to_str() << ": send to " << collator_adnl_id; - size_t max_answer_size = config_.max_block_size + config_.max_collated_data_size + 1024; - td::Timestamp query_timeout = td::Timestamp::in(10.0); - query_timeout.relax(timeout); - td::actor::send_closure(rldp_, &rldp::Rldp::send_query_ex, local_adnl_id_, collator_adnl_id, "collatequery", - std::move(P), timeout, std::move(query), max_answer_size); -} - -void ValidatorGroup::receive_collate_query_response(validatorsession::BlockSourceInfo source_info, td::BufferSlice data, - bool trusted_collator, td::Promise promise) { - if (source_info.round < last_known_round_id_) { - promise.set_error(td::Status::Error("too old")); - return; - } - TRY_RESULT_PROMISE(promise, f, fetch_tl_object(data, true)); - td::Result res; - ton_api::downcast_call(*f, td::overloaded( - [&](ton_api::collatorNode_generateBlockError &r) { - td::Status error = td::Status::Error(r.code_, r.message_); - res = error.move_as_error_prefix("collate query: "); - }, - [&](ton_api::collatorNode_generateBlockSuccess &r) { - res = CollatorNode::deserialize_candidate( - std::move(r.candidate_), - config_.max_block_size + config_.max_collated_data_size + 1024); - })); - TRY_RESULT_PROMISE(promise, candidate, std::move(res)); - if (candidate.pubkey.as_bits256() != local_id_full_.ed25519_value().raw()) { - promise.set_error(td::Status::Error("collate query: block candidate source mismatch")); - return; - } - if (candidate.id.shard_full() != shard_) { - promise.set_error(td::Status::Error("collate query: shard mismatch")); - return; - } - - if (trusted_collator) { - promise.set_result(std::move(candidate)); - return; - } - auto P = td::PromiseCreator::lambda( - [candidate = candidate.clone(), promise = std::move(promise)](td::Result> R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error_prefix("validate received block error: ")); - return; - } - promise.set_result(std::move(candidate)); - }); - validate_block_candidate(source_info, std::move(candidate), std::move(P)); -} - } // namespace validator } // namespace ton diff --git a/validator/validator-group.hpp b/validator/validator-group.hpp index 38fc9fb44..fb7a4dcfb 100644 --- a/validator/validator-group.hpp +++ b/validator/validator-group.hpp @@ -18,6 +18,7 @@ */ #pragma once +#include "collation-manager.hpp" #include "interfaces/validator-manager.h" #include "validator-session/validator-session.h" @@ -59,6 +60,10 @@ class ValidatorGroup : public td::actor::Actor { init_ = false; create_session(); } + td::actor::send_closure(collation_manager_, &CollationManager::validator_group_started, shard_); + } + void tear_down() override { + td::actor::send_closure(collation_manager_, &CollationManager::validator_group_finished, shard_); } void get_validator_group_info_for_litequery( @@ -71,17 +76,17 @@ class ValidatorGroup : public td::actor::Actor { ValidatorGroup(ShardIdFull shard, PublicKeyHash local_id, ValidatorSessionId session_id, td::Ref validator_set, BlockSeqno last_key_block_seqno, - block::CollatorConfig collator_config, validatorsession::ValidatorSessionOptions config, - td::actor::ActorId keyring, td::actor::ActorId adnl, - td::actor::ActorId rldp, td::actor::ActorId overlays, - std::string db_root, td::actor::ActorId validator_manager, bool create_session, + validatorsession::ValidatorSessionOptions config, td::actor::ActorId keyring, + td::actor::ActorId adnl, td::actor::ActorId rldp, + td::actor::ActorId overlays, std::string db_root, + td::actor::ActorId validator_manager, + td::actor::ActorId collation_manager, bool create_session, bool allow_unsafe_self_blocks_resync, td::Ref opts, bool monitoring_shard) : shard_(shard) , local_id_(std::move(local_id)) , session_id_(session_id) , validator_set_(std::move(validator_set)) , last_key_block_seqno_(last_key_block_seqno) - , collator_config_(std::move(collator_config)) , config_(std::move(config)) , keyring_(keyring) , adnl_(adnl) @@ -89,6 +94,7 @@ class ValidatorGroup : public td::actor::Actor { , overlays_(overlays) , db_root_(std::move(db_root)) , manager_(validator_manager) + , collation_manager_(collation_manager) , init_(create_session) , allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync) , opts_(std::move(opts)) @@ -97,10 +103,6 @@ class ValidatorGroup : public td::actor::Actor { private: std::unique_ptr make_validator_session_callback(); - void collate_block(validatorsession::BlockSourceInfo source_info, td::Timestamp timeout, - td::Promise promise, unsigned max_retries = 4); - void receive_collate_query_response(validatorsession::BlockSourceInfo source_info, td::BufferSlice data, - bool trusted_collator, td::Promise promise); struct PostponedAccept { RootHash root_hash; @@ -124,7 +126,6 @@ class ValidatorGroup : public td::actor::Actor { td::Ref validator_set_; BlockSeqno last_key_block_seqno_; - block::CollatorConfig collator_config_; validatorsession::ValidatorSessionOptions config_; td::actor::ActorId keyring_; @@ -133,6 +134,7 @@ class ValidatorGroup : public td::actor::Actor { td::actor::ActorId overlays_; std::string db_root_; td::actor::ActorId manager_; + td::actor::ActorId collation_manager_; td::actor::ActorOwn session_; adnl::AdnlNodeIdShort local_adnl_id_; diff --git a/validator/validator-options.cpp b/validator/validator-options.cpp index 4f9b8c538..230a5df46 100644 --- a/validator/validator-options.cpp +++ b/validator/validator-options.cpp @@ -26,29 +26,58 @@ namespace ton { namespace validator { -void CollatorsList::unpack(const ton_api::engine_validator_collatorsList& obj) { +td::Status CollatorsList::unpack(const ton_api::engine_validator_collatorsList& obj) { shards.clear(); - self_collate = obj.self_collate_; - use_config_41 = obj.use_config_41_; + self_collate = false; for (const auto& shard_obj : obj.shards_) { + ShardIdFull shard_id = create_shard_id(shard_obj->shard_id_); + if (shard_id.is_masterchain()) { + return td::Status::Error("masterchain shard in collators list"); + } + if (!shard_id.is_valid_ext()) { + return td::Status::Error(PSTRING() << "invalid shard " << shard_id.to_str()); + } shards.emplace_back(); Shard& shard = shards.back(); - shard.shard_id = create_shard_id(shard_obj->shard_id_); + shard.shard_id = shard_id; + shard.self_collate = shard_obj->self_collate_; + if (shard.self_collate) { + self_collate = true; + } + if (shard_obj->select_mode_.empty() || shard_obj->select_mode_ == "random") { + shard.select_mode = mode_random; + } else if (shard_obj->select_mode_ == "ordered") { + shard.select_mode = mode_ordered; + } else if (shard_obj->select_mode_ == "round_robin") { + shard.select_mode = mode_round_robin; + } else { + return td::Status::Error(PSTRING() << "invalid select mode '" << shard_obj->select_mode_ + << "' (allowed: 'random', 'ordered', 'round_robin')"); + } for (const auto& collator : shard_obj->collators_) { - shard.collators.push_back({adnl::AdnlNodeIdShort{collator->adnl_id_}, collator->trusted_}); + shard.collators.push_back(adnl::AdnlNodeIdShort{collator->adnl_id_}); } } + return td::Status::OK(); +} + +CollatorsList CollatorsList::default_list() { + CollatorsList list; + list.shards.push_back( + {.shard_id = ShardIdFull{basechainId, shardIdAll}, .select_mode = mode_random, .self_collate = true}); + list.self_collate = true; + return list; } -td::Ref ValidatorManagerOptions::create( - BlockIdExt zero_block_id, BlockIdExt init_block_id, - std::function check_shard, bool allow_blockchain_init, - double sync_blocks_before, double block_ttl, double state_ttl, double max_mempool_num, - double archive_ttl, double key_proof_ttl, bool initial_sync_disabled) { +td::Ref ValidatorManagerOptions::create(BlockIdExt zero_block_id, BlockIdExt init_block_id, + std::function check_shard, + bool allow_blockchain_init, double sync_blocks_before, + double block_ttl, double state_ttl, + double max_mempool_num, double archive_ttl, + double key_proof_ttl, bool initial_sync_disabled) { return td::make_ref(zero_block_id, init_block_id, std::move(check_shard), allow_blockchain_init, sync_blocks_before, block_ttl, state_ttl, - max_mempool_num, - archive_ttl, key_proof_ttl, initial_sync_disabled); + max_mempool_num, archive_ttl, key_proof_ttl, initial_sync_disabled); } } // namespace validator diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 8e876625d..05cc73d31 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -157,6 +157,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { td::Ref get_collators_list() const override { return collators_list_; } + bool check_collator_node_whitelist(adnl::AdnlNodeIdShort id) const override { + return !collator_node_whitelist_enabled_ || collator_node_whitelist_.contains(id); + } void set_zero_block_id(BlockIdExt block_id) override { zero_block_id_ = block_id; @@ -255,6 +258,16 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { void set_collators_list(td::Ref list) override { collators_list_ = std::move(list); } + void set_collator_node_whitelisted_validator(adnl::AdnlNodeIdShort id, bool add) override { + if (add) { + collator_node_whitelist_.insert(id); + } else { + collator_node_whitelist_.erase(id); + } + } + void set_collator_node_whitelist_enabled(bool enabled) override { + collator_node_whitelist_enabled_ = enabled; + } ValidatorManagerOptionsImpl *make_copy() const override { return new ValidatorManagerOptionsImpl(*this); @@ -308,7 +321,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { bool state_serializer_enabled_ = true; td::Ref collator_options_{true}; bool fast_state_serializer_enabled_ = false; - td::Ref collators_list_{true, CollatorsList{}}; + td::Ref collators_list_{true, CollatorsList::default_list()}; + std::set collator_node_whitelist_; + bool collator_node_whitelist_enabled_ = false; }; } // namespace validator diff --git a/validator/validator.h b/validator/validator.h index 458076768..3eaaaddf9 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -73,19 +73,20 @@ struct CollatorOptions : public td::CntObject { }; struct CollatorsList : public td::CntObject { - struct Collator { - adnl::AdnlNodeIdShort adnl_id; - bool trusted; + enum SelectMode { + mode_random, mode_ordered, mode_round_robin }; struct Shard { ShardIdFull shard_id; - std::vector collators; + SelectMode select_mode = mode_random; + std::vector collators; + bool self_collate = false; }; - bool self_collate = true; - bool use_config_41 = false; std::vector shards; + bool self_collate = false; - void unpack(const ton_api::engine_validator_collatorsList& obj); + td::Status unpack(const ton_api::engine_validator_collatorsList& obj); + static CollatorsList default_list(); }; struct ValidatorManagerOptions : public td::CntObject { @@ -130,6 +131,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual td::Ref get_collator_options() const = 0; virtual bool get_fast_state_serializer_enabled() const = 0; virtual td::Ref get_collators_list() const = 0; + virtual bool check_collator_node_whitelist(adnl::AdnlNodeIdShort id) const = 0; virtual void set_zero_block_id(BlockIdExt block_id) = 0; virtual void set_init_block_id(BlockIdExt block_id) = 0; @@ -163,6 +165,8 @@ struct ValidatorManagerOptions : public td::CntObject { virtual void set_collator_options(td::Ref value) = 0; virtual void set_fast_state_serializer_enabled(bool value) = 0; virtual void set_collators_list(td::Ref list) = 0; + virtual void set_collator_node_whitelisted_validator(adnl::AdnlNodeIdShort id, bool add) = 0; + virtual void set_collator_node_whitelist_enabled(bool enabled) = 0; static td::Ref create( BlockIdExt zero_block_id, BlockIdExt init_block_id, @@ -314,6 +318,9 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) = 0; virtual void del_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) = 0; + + virtual void get_collation_manager_stats( + td::Promise> promise) = 0; }; } // namespace validator