From d8244eff5367bbfd4cb2873b5c24c75206d98821 Mon Sep 17 00:00:00 2001 From: ton Date: Wed, 11 Sep 2019 16:50:29 +0400 Subject: [PATCH] changed validate broadcast logic, added new queries to validator-engine-console --- adnl/CMakeLists.txt | 5 + adnl/adnl-peer-table.cpp | 12 +- adnl/adnl-pong.cpp | 195 ++++++++++++++++++ crypto/block/mc-config.cpp | 3 +- tl/generate/scheme/ton_api.tl | 13 ++ tl/generate/scheme/ton_api.tlo | Bin 50056 -> 50760 bytes .../validator-engine-console-query.cpp | 20 ++ .../validator-engine-console-query.h | 21 ++ .../validator-engine-console.cpp | 1 + validator-engine/validator-engine.cpp | 86 ++++++++ validator-engine/validator-engine.hpp | 2 + validator/impl/CMakeLists.txt | 2 + validator/impl/config.cpp | 58 ++++++ validator/impl/config.hpp | 50 +++++ validator/impl/proof.cpp | 43 ++-- validator/impl/proof.hpp | 23 +-- validator/impl/shard.hpp | 8 + validator/impl/validate-query.cpp | 25 ++- validator/interfaces/config.h | 1 + validator/interfaces/proof.h | 7 + validator/interfaces/shard.h | 1 + validator/manager.cpp | 6 +- validator/validate-broadcast.cpp | 185 +++++++++++++---- validator/validate-broadcast.hpp | 12 +- 24 files changed, 695 insertions(+), 84 deletions(-) create mode 100644 adnl/adnl-pong.cpp create mode 100644 validator/impl/config.cpp create mode 100644 validator/impl/config.hpp diff --git a/adnl/CMakeLists.txt b/adnl/CMakeLists.txt index e519e9cd..b7c2c198 100644 --- a/adnl/CMakeLists.txt +++ b/adnl/CMakeLists.txt @@ -87,6 +87,11 @@ target_include_directories(adnl-proxy PUBLIC $) +target_link_libraries(adnl-pong PUBLIC tdactor ton_crypto tl_api tdnet common + tl-utils adnl dht) + add_library(adnltest STATIC ${ADNL_TEST_SOURCE}) target_include_directories(adnltest PUBLIC $) target_link_libraries(adnltest PUBLIC adnl ) diff --git a/adnl/adnl-peer-table.cpp b/adnl/adnl-peer-table.cpp index c9a11391..04a77684 100644 --- a/adnl/adnl-peer-table.cpp +++ b/adnl/adnl-peer-table.cpp @@ -283,11 +283,19 @@ void AdnlPeerTableImpl::start_up() { void AdnlPeerTableImpl::write_new_addr_list_to_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem node, td::Promise promise) { + if (db_.empty()) { + promise.set_value(td::Unit()); + return; + } td::actor::send_closure(db_, &AdnlDb::update, local_id, peer_id, std::move(node), std::move(promise)); } void AdnlPeerTableImpl::get_addr_list_from_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, td::Promise promise) { + if (db_.empty()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "db not inited")); + return; + } td::actor::send_closure(db_, &AdnlDb::get, local_id, peer_id, std::move(promise)); } @@ -295,7 +303,9 @@ AdnlPeerTableImpl::AdnlPeerTableImpl(std::string db_root, td::actor::ActorId. + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "td/actor/actor.h" +#include "td/utils/buffer.h" +#include "td/utils/port/IPAddress.h" +#include "td/net/UdpServer.h" +#include "td/utils/port/signals.h" +#include "td/utils/OptionsParser.h" +#include "td/utils/FileLog.h" +#include "td/utils/port/path.h" +#include "td/utils/port/user.h" +#include "td/utils/filesystem.h" +#include "common/checksum.h" +#include "common/errorcode.h" +#include "tl-utils/tl-utils.hpp" +#include "auto/tl/ton_api_json.h" +#include "adnl/adnl.h" +#include + +#if TD_DARWIN || TD_LINUX +#include +#endif + +namespace ton { + +namespace adnl { + +class Callback : public adnl::Adnl::Callback { + public: + void receive_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) override { + } + void receive_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data, + td::Promise promise) override { + TRY_RESULT_PROMISE_PREFIX(promise, f, fetch_tl_object(std::move(data), true), + "adnl.ping expected"); + promise.set_value(create_serialize_tl_object(f->value_)); + } + + Callback() { + } + + private: +}; + +} // namespace adnl + +} // namespace ton + +std::atomic rotate_logs_flags{false}; +void force_rotate_logs(int sig) { + rotate_logs_flags.store(true); +} + +int main(int argc, char *argv[]) { + SET_VERBOSITY_LEVEL(verbosity_INFO); + + ton::PrivateKey pk; + td::IPAddress addr; + + td::set_default_failure_signal_handler().ensure(); + + std::unique_ptr logger_; + SCOPE_EXIT { + td::log_interface = td::default_log_interface; + }; + + std::string config = "/var/ton-work/etc/adnl-proxy.conf.json"; + + td::OptionsParser p; + p.set_description("adnl pinger"); + p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) { + int v = VERBOSITY_NAME(FATAL) + (td::to_integer(arg)); + SET_VERBOSITY_LEVEL(v); + return td::Status::OK(); + }); + p.add_option('h', "help", "prints_help", [&]() { + char b[10240]; + td::StringBuilder sb(td::MutableSlice{b, 10000}); + sb << p; + std::cout << sb.as_cslice().c_str(); + std::exit(2); + return td::Status::OK(); + }); + p.add_option('d', "daemonize", "set SIGHUP", [&]() { +#if TD_DARWIN || TD_LINUX + close(0); + setsid(); +#endif + td::set_signal_handler(td::SignalType::HangUp, force_rotate_logs).ensure(); + return td::Status::OK(); + }); + p.add_option('l', "logname", "log to file", [&](td::Slice fname) { + auto F = std::make_unique(); + TRY_STATUS(F->init(fname.str(), std::numeric_limits::max(), true)); + logger_ = std::move(F); + td::log_interface = logger_.get(); + return td::Status::OK(); + }); + td::uint32 threads = 7; + p.add_option('t', "threads", PSTRING() << "number of threads (default=" << threads << ")", [&](td::Slice fname) { + td::int32 v; + try { + v = std::stoi(fname.str()); + } catch (...) { + return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: not a number"); + } + if (v < 1 || v > 256) { + return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: should be in range [1..256]"); + } + threads = v; + return td::Status::OK(); + }); + p.add_option('u', "user", "change user", [&](td::Slice user) { return td::change_user(user); }); + p.add_option('k', "key", "private key", [&](td::Slice key) { + TRY_RESULT_ASSIGN(pk, ton::PrivateKey::import(key)); + return td::Status::OK(); + }); + p.add_option('a', "addr", "ip:port of instance", [&](td::Slice key) { + TRY_STATUS(addr.init_host_port(key.str())); + return td::Status::OK(); + }); + + p.run(argc, argv).ensure(); + + if (pk.empty()) { + LOG(FATAL) << "no --key given"; + } + if (!addr.is_valid()) { + LOG(FATAL) << "no --addr given"; + } + + td::actor::Scheduler scheduler({threads}); + + td::actor::ActorOwn keyring; + td::actor::ActorOwn adnl; + td::actor::ActorOwn network_manager; + + auto pub = pk.compute_public_key(); + + scheduler.run_in_context([&]() { + keyring = ton::keyring::Keyring::create(""); + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk), true, [](td::Unit) {}); + + adnl = ton::adnl::Adnl::create("", keyring.get()); + + network_manager = ton::adnl::AdnlNetworkManager::create(static_cast(addr.get_port())); + + td::actor::send_closure(network_manager, &ton::adnl::AdnlNetworkManager::add_self_addr, addr, 0); + + auto tladdr = ton::create_tl_object(addr.get_ipv4(), addr.get_port()); + auto addr_vec = std::vector>(); + addr_vec.push_back(std::move(tladdr)); + auto tladdrlist = ton::create_tl_object( + std::move(addr_vec), ton::adnl::Adnl::adnl_start_time(), ton::adnl::Adnl::adnl_start_time(), 0, 2000000000); + auto addrlist = ton::adnl::AdnlAddressList::create(tladdrlist).move_as_ok(); + + td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, std::move(addrlist)); + td::actor::send_closure(adnl, &ton::adnl::Adnl::subscribe, ton::adnl::AdnlNodeIdShort{pub.compute_short_id()}, + ton::adnl::Adnl::int_to_bytestring(ton::ton_api::adnl_ping::ID), + std::make_unique()); + }); + + while (scheduler.run(1)) { + if (rotate_logs_flags.exchange(false)) { + if (td::log_interface) { + td::log_interface->rotate(); + } + } + } +} diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 254b10e3..f887d50a 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -67,7 +67,7 @@ td::Result> Config::extract_from_key_block(Ref tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra) && mc_extra.key_block && mc_extra.config.not_null())) { return td::Status::Error(-400, "cannot unpack extra header of key block to extract configuration"); } - return block::Config::unpack_config(std::move(mc_extra.config)); + return block::Config::unpack_config(std::move(mc_extra.config), mode); } td::Result> Config::extract_from_state(Ref mc_state_root, int mode) { @@ -1482,6 +1482,7 @@ std::vector Config::compute_validator_set(ton::ShardIdFull std::vector Config::compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time, ton::CatchainSeqno cc_seqno) const { if (!cur_validators_) { + LOG(DEBUG) << "failed to compute validator set: cur_validators_ is empty"; return {}; } else { return compute_validator_set(shard, *cur_validators_, time, cc_seqno); diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 887c2c1b..2d5fd20a 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -498,6 +498,14 @@ engine.adnlProxy.config ports:(vector engine.adnlProxy.port) = engine.adnlProxy. ---types--- +adnl.pong value:long = adnl.Pong; + +---functions--- + +adnl.ping value:long = adnl.Pong; + +---types--- + engine.validator.keyHash key_hash:int256 = engine.validator.KeyHash; engine.validator.signature signature:bytes = engine.validator.Signature; @@ -513,6 +521,9 @@ engine.validator.jsonConfig data:string = engine.validator.JsonConfig; engine.validator.electionBid election_date:int perm_key:int256 adnl_addr:int256 to_send_payload:bytes = engine.validator.ElectionBid; +engine.validator.dhtServerStatus id:int256 status:int = engine.validator.DhtServerStatus; +engine.validator.dhtServersStatus servers:(vector engine.validator.dhtServerStatus) = engine.validator.DhtServersStatus; + ---functions--- engine.validator.getTime = engine.validator.Time; @@ -550,4 +561,6 @@ engine.validator.setVerbosity verbosity:int = engine.validator.Success; engine.validator.createElectionBid election_date:int election_addr:string wallet:string = engine.validator.ElectionBid; +engine.validator.checkDhtServers id:int256 = engine.validator.DhtServersStatus; + engine.validator.controlQuery data:bytes = Object; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index a860d74e2ecd6530438bb91d9a87eb62d787ca70..7e81b489d33372ef05bec1e3617ed5254a455a42 100644 GIT binary patch delta 458 zcmeBZXFk!x%=>7zz7+!$EZNArn3%^R36@T$f=m))q4nwOrL zm#SBmn3I{3Sdw3)=aNwpoLW?tS`=K8SW;R%IgwFXUE{BXwjyqo#SotH#Z2% z@G!D%o-3;_BLcTnuOL4!oq++wpWGmCzye}yK47`QOb5FoQcxWM^a)7aWSeXacD7=O zyXsYBJwbvnZ(fz-ioxzqxM47NGJw?GJSODB2-Gb7?7k-Z7zz7+!$EZoSun0fOJ<_o-=HH2h%7@0S_%IV8&K4G=QY;#GGh#8~R P=DRDzc{ayveIoz>YS|SJ diff --git a/validator-engine-console/validator-engine-console-query.cpp b/validator-engine-console/validator-engine-console-query.cpp index 198165c5..642aa001 100644 --- a/validator-engine-console/validator-engine-console-query.cpp +++ b/validator-engine-console/validator-engine-console-query.cpp @@ -655,3 +655,23 @@ td::Status CreateElectionBidQuery::receive(td::BufferSlice data) { TRY_STATUS(td::write_file(fname_, f->to_send_payload_.as_slice())); return td::Status::OK(); } + +td::Status CheckDhtServersQuery::run() { + TRY_RESULT_ASSIGN(id_, tokenizer_.get_token()); + return td::Status::OK(); +} + +td::Status CheckDhtServersQuery::send() { + auto b = ton::create_serialize_tl_object(id_.tl()); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status CheckDhtServersQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + for (auto &s : f->servers_) { + td::TerminalIO::out() << "id=" << s->id_ << " status=" << (s->status_ ? "SUCCESS" : "FAIL") << "\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 54851f71..ce69f790 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -821,3 +821,24 @@ class CreateElectionBidQuery : public Query { std::string fname_; }; +class CheckDhtServersQuery : public Query { + public: + CheckDhtServersQuery(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 "checkdht"; + } + static std::string get_help() { + return "checkdht \tchecks, which root DHT servers are accessible from this ADNL addr"; + } + std::string name() const override { + return get_name(); + } + + private: + ton::PublicKeyHash id_; +}; diff --git a/validator-engine-console/validator-engine-console.cpp b/validator-engine-console/validator-engine-console.cpp index f258a682..6887c375 100644 --- a/validator-engine-console/validator-engine-console.cpp +++ b/validator-engine-console/validator-engine-console.cpp @@ -130,6 +130,7 @@ 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>()); } bool ValidatorEngineConsole::envelope_send_query(td::BufferSlice query, td::Promise promise) { diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 81023fb4..3d448780 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -50,6 +50,8 @@ #include "memprof/memprof.h" +#include "dht/dht.hpp" + #if TD_DARWIN || TD_LINUX #include #endif @@ -749,6 +751,65 @@ class ValidatorElectionBidCreator : public td::actor::Actor { td::BufferSlice result_; }; +class CheckDhtServerStatusQuery : public td::actor::Actor { + public: + void start_up() override { + auto &n = dht_config_->nodes(); + + result_.resize(n.size(), false); + + pending_ = n.size(); + for (td::uint32 i = 0; i < n.size(); i++) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), idx = i](td::Result R) { + td::actor::send_closure(SelfId, &CheckDhtServerStatusQuery::got_result, idx, R.is_ok()); + }); + + auto &E = n.list().at(i); + + td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_peer, local_id_, E.adnl_id(), E.addr_list()); + td::actor::send_closure(adnl_, &ton::adnl::Adnl::send_query, local_id_, E.adnl_id().compute_short_id(), "ping", + std::move(P), td::Timestamp::in(1.0), + ton::create_serialize_tl_object()); + } + } + + void got_result(td::uint32 idx, bool result) { + result_[idx] = result; + CHECK(pending_ > 0); + if (!--pending_) { + finish_query(); + } + } + + void finish_query() { + std::vector> vec; + auto &n = dht_config_->nodes(); + for (td::uint32 i = 0; i < n.size(); i++) { + auto &E = n.list().at(i); + vec.push_back(ton::create_tl_object( + E.adnl_id().compute_short_id().bits256_value(), result_[i] ? 1 : 0)); + } + promise_.set_value( + ton::create_serialize_tl_object(std::move(vec))); + stop(); + } + + CheckDhtServerStatusQuery(std::shared_ptr dht_config, ton::adnl::AdnlNodeIdShort local_id, + td::actor::ActorId adnl, td::Promise promise) + : dht_config_(std::move(dht_config)), local_id_(local_id), adnl_(adnl), promise_(std::move(promise)) { + } + + private: + std::shared_ptr dht_config_; + + std::vector result_; + td::uint32 pending_; + + ton::adnl::AdnlNodeIdShort local_id_; + td::actor::ActorId adnl_; + td::Promise promise_; +}; + void ValidatorEngine::set_local_config(std::string str) { local_config_ = str; } @@ -2535,6 +2596,31 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_createEle .release(); } +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_checkDhtServers &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 (keyring_.empty()) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "keyring not started"))); + return; + } + if (!dht_config_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "no dht config"))); + return; + } + + if (config_.adnl_ids.count(ton::PublicKeyHash{query.id_}) == 0) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "no dht config"))); + return; + } + + td::actor::create_actor("pinger", dht_config_, ton::adnl::AdnlNodeIdShort{query.id_}, + adnl_.get(), std::move(promise)) + .release(); +} + void ValidatorEngine::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) { diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 4d014c68..d4e1c902 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -347,6 +347,8 @@ class ValidatorEngine : public td::actor::Actor { td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_createElectionBid &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_checkDhtServers &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); template void run_control_query(T &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { diff --git a/validator/impl/CMakeLists.txt b/validator/impl/CMakeLists.txt index 06be22a9..459e7724 100644 --- a/validator/impl/CMakeLists.txt +++ b/validator/impl/CMakeLists.txt @@ -9,6 +9,7 @@ set(TON_VALIDATOR_SOURCE block.cpp check-proof.cpp collator.cpp + config.cpp external-message.cpp fabric.cpp ihr-message.cpp @@ -27,6 +28,7 @@ set(TON_VALIDATOR_SOURCE collate-query-impl.h collator-impl.h collator.h + config.hpp external-message.hpp ihr-message.hpp liteserver.hpp diff --git a/validator/impl/config.cpp b/validator/impl/config.cpp new file mode 100644 index 00000000..e8f1c83c --- /dev/null +++ b/validator/impl/config.cpp @@ -0,0 +1,58 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ + +#include "config.hpp" +#include "block/block-parse.h" +#include "block/block-auto.h" +#include "vm/boc.h" +#include "vm/cells/MerkleProof.h" +#include "validator-set.hpp" + +namespace ton { + +namespace validator { + +td::Ref ConfigHolderQ::get_total_validator_set(int next) const { + if (!config_) { + LOG(ERROR) << "MasterchainStateQ::get_total_validator_set() : no config"; + return {}; + } + auto nodes = config_->compute_total_validator_set(next); + if (nodes.empty()) { + return {}; + } + return Ref{true, 0, ton::ShardIdFull{}, std::move(nodes)}; +} + +td::Ref ConfigHolderQ::get_validator_set(ShardIdFull shard, UnixTime utime, + CatchainSeqno cc_seqno) const { + if (!config_) { + LOG(ERROR) << "MasterchainStateQ::get_validator_set() : no config"; + return {}; + } + auto nodes = config_->compute_validator_set(shard, utime, cc_seqno); + if (nodes.empty()) { + return {}; + } + return Ref{true, cc_seqno, shard, std::move(nodes)}; +} + +} // namespace validator + +} // namespace ton diff --git a/validator/impl/config.hpp b/validator/impl/config.hpp new file mode 100644 index 00000000..4fe82ee3 --- /dev/null +++ b/validator/impl/config.hpp @@ -0,0 +1,50 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once +#include "validator/interfaces/config.h" + +namespace ton { +namespace validator { +using td::Ref; + +class ConfigHolderQ : public ConfigHolder { + std::shared_ptr config_; + std::shared_ptr boc_; + + public: + ConfigHolderQ() = default; + ConfigHolderQ(std::shared_ptr config, std::shared_ptr boc) + : config_(std::move(config)), boc_(std::move(boc)) { + } + ConfigHolderQ(std::shared_ptr config) : config_(std::move(config)) { + } + const block::Config *get_config() const { + return config_.get(); + } + ConfigHolderQ *make_copy() const override { + return new ConfigHolderQ(*this); + } + // if necessary, add more public methods providing interface to config_->...() + td::Ref get_total_validator_set(int next) const override; // next = -1 -> prev, next = 0 -> cur + td::Ref get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const override; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/impl/proof.cpp b/validator/impl/proof.cpp index 71403313..e42ae8e9 100644 --- a/validator/impl/proof.cpp +++ b/validator/impl/proof.cpp @@ -43,10 +43,10 @@ td::Result> ProofQ::export_as_proof_link() const { } td::Result ProofLinkQ::prev_key_mc_seqno() const { - if (!id_.is_masterchain()) { - return td::Status::Error( - -668, "cannot compute previous key masterchain block from ProofLink of non-masterchain block "s + id_.to_str()); - } + //if (!id_.is_masterchain()) { + // return td::Status::Error( + // -668, "cannot compute previous key masterchain block from ProofLink of non-masterchain block "s + id_.to_str()); + //} TRY_RESULT(pair, get_virtual_root(true)); try { block::gen::Block::Record blk; @@ -68,8 +68,7 @@ td::Result> ProofLinkQ::get_key_block_config() const { } TRY_RESULT(pair, get_virtual_root(true)); try { - TRY_RESULT(cfg, block::Config::extract_from_key_block( - std::move(pair.first), block::Config::needValidatorSet | block::Config::needWorkchainInfo)); + TRY_RESULT(cfg, block::Config::extract_from_key_block(std::move(pair.first), block::Config::needValidatorSet)); return td::make_ref(std::move(cfg), std::move(pair.second)); } catch (vm::VmVirtError &) { return td::Status::Error(-668, @@ -77,6 +76,26 @@ td::Result> ProofLinkQ::get_key_block_config() const { } } +td::Result ProofLinkQ::get_basic_header_info() const { + BasicHeaderInfo res; + TRY_RESULT(pair, get_virtual_root(true)); + try { + block::gen::Block::Record blk; + block::gen::BlockInfo::Record info; + if (!(tlb::unpack_cell(std::move(pair.first), blk) && tlb::unpack_cell(blk.info, info) && !info.version)) { + return td::Status::Error(-668, + "cannot unpack block header in the Merkle proof for masterchain block "s + id_.to_str()); + } + res.cc_seqno = info.gen_catchain_seqno; + res.utime = info.gen_utime; + res.validator_set_hash = info.gen_validator_list_hash_short; + res.prev_key_mc_seqno = info.prev_key_block_seqno; + return res; + } catch (vm::VmVirtError &) { + return td::Status::Error(-668, "virtualization error in masterchain block proof for "s + id_.to_str()); + } +} + td::Result, std::shared_ptr>> ProofLinkQ::get_virtual_root( bool lazy) const { if (data_.empty()) { @@ -127,17 +146,5 @@ td::Result, std::shared_ptr>> Pr return std::make_pair(std::move(virt_root), std::move(boc)); } -td::Ref ConfigHolderQ::get_total_validator_set(int next) const { - if (!config_) { - LOG(ERROR) << "MasterchainStateQ::get_total_validator_set() : no config"; - return {}; - } - auto nodes = config_->compute_total_validator_set(next); - if (nodes.empty()) { - return {}; - } - return Ref{true, 0, ton::ShardIdFull{}, std::move(nodes)}; -} - } // namespace validator } // namespace ton diff --git a/validator/impl/proof.hpp b/validator/impl/proof.hpp index 0506caf9..7b4fdf08 100644 --- a/validator/impl/proof.hpp +++ b/validator/impl/proof.hpp @@ -22,32 +22,12 @@ #include "block/block-db.h" #include "block/mc-config.h" #include "vm/db/StaticBagOfCellsDb.h" +#include "config.hpp" namespace ton { namespace validator { using td::Ref; -class ConfigHolderQ : public ConfigHolder { - std::shared_ptr config_; - std::shared_ptr boc_; - - public: - ConfigHolderQ() = default; - ConfigHolderQ(std::shared_ptr config, std::shared_ptr boc) - : config_(std::move(config)), boc_(std::move(boc)) { - } - ConfigHolderQ(std::shared_ptr config) : config_(std::move(config)) { - } - const block::Config *get_config() const { - return config_.get(); - } - ConfigHolderQ *make_copy() const override { - return new ConfigHolderQ(*this); - } - // if necessary, add more public methods providing interface to config_->...() - td::Ref get_total_validator_set(int next) const override; // next = -1 -> prev, next = 0 -> cur -}; - class ProofLinkQ : virtual public ProofLink { protected: BlockIdExt id_; @@ -67,6 +47,7 @@ class ProofLinkQ : virtual public ProofLink { } td::Result prev_key_mc_seqno() const override; td::Result> get_key_block_config() const override; + td::Result get_basic_header_info() const override; protected: td::Result, std::shared_ptr>> get_virtual_root( diff --git a/validator/impl/shard.hpp b/validator/impl/shard.hpp index aca66f11..b067da14 100644 --- a/validator/impl/shard.hpp +++ b/validator/impl/shard.hpp @@ -20,6 +20,7 @@ #include "interfaces/shard.h" #include "vm/db/StaticBagOfCellsDb.h" #include "block/mc-config.h" +#include "config.hpp" namespace ton { @@ -138,6 +139,13 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { std::shared_ptr get_config() const { return config_; } + td::Result> get_key_block_config() const override { + if (!config_) { + return td::Status::Error(ErrorCode::notready, "config not found"); + } else { + return td::make_ref(config_); + } + } private: ZeroStateIdExt zerostate_id_; diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index cf0eb7f1..6f723fd6 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -665,9 +665,7 @@ bool ValidateQuery::try_unpack_mc_state() { } config_ = res.move_as_ok(); CHECK(config_); - if (mc_seqno_) { - config_->set_block_id_ext(mc_blkid_); - } + config_->set_block_id_ext(mc_blkid_); old_shard_conf_ = std::make_unique(*config_); if (!is_masterchain()) { new_shard_conf_ = std::make_unique(*config_); @@ -684,6 +682,11 @@ bool ValidateQuery::try_unpack_mc_state() { } else { prev_key_block_seqno_ = 0; } + if (prev_key_seqno_ != prev_key_block_seqno_) { + return reject_query(PSTRING() << "previous key block seqno value in candidate block header is " << prev_key_seqno_ + << " while the correct value corresponding to reference masterchain state " + << mc_blkid_.to_str() << " is " << prev_key_block_seqno_); + } auto limits = config_->get_block_limits(is_masterchain()); if (limits.is_error()) { return fatal_error(limits.move_as_error()); @@ -5002,7 +5005,7 @@ bool ValidateQuery::check_mc_state_extra() { } else if (!new_extra.r1.last_key_block->prefetch_ulong(1)) { return reject_query("last_key_block:(Maybe ExtBlkRef) changed in the new state, but it became a nothing$0"); } else { - auto& cs = new_extra.r1.last_key_block.write(); + vm::CellSlice cs = *new_extra.r1.last_key_block; BlockIdExt blkid; LogicalTime lt; CHECK(cs.fetch_ulong(1) == 1 && block::tlb::t_ExtBlkRef.unpack(cs, blkid, <)); @@ -5017,6 +5020,20 @@ bool ValidateQuery::check_mc_state_extra() { ", but it is not a key block"); } } + if (new_extra.r1.last_key_block->prefetch_ulong(1)) { + auto& cs = new_extra.r1.last_key_block.write(); + BlockIdExt blkid; + LogicalTime lt; + CHECK(cs.fetch_ulong(1) == 1 && block::tlb::t_ExtBlkRef.unpack(cs, blkid, <)); + if (blkid != prev_key_block_) { + return reject_query("new masterchain state declares previous key block to be "s + blkid.to_str() + + " but the value computed from previous masterchain state is " + prev_key_block_.to_str()); + } + } else if (prev_key_block_seqno_ > 0) { + return reject_query(PSTRING() << "new masterchain state declares no previous key block, but the block header " + "announces previous key block seqno " + << prev_key_block_seqno_); + } // global_balance:CurrencyCollection block::CurrencyCollection global_balance, old_global_balance; if (!global_balance.validate_unpack(new_extra.global_balance)) { diff --git a/validator/interfaces/config.h b/validator/interfaces/config.h index 8bb55778..fba20249 100644 --- a/validator/interfaces/config.h +++ b/validator/interfaces/config.h @@ -34,6 +34,7 @@ class ConfigHolder : public td::CntObject { virtual ~ConfigHolder() = default; virtual td::Ref get_total_validator_set(int next) const = 0; // next = -1 -> prev, next = 0 -> cur + virtual td::Ref get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const = 0; }; } // namespace validator diff --git a/validator/interfaces/proof.h b/validator/interfaces/proof.h index 2a55ffc6..70598e68 100644 --- a/validator/interfaces/proof.h +++ b/validator/interfaces/proof.h @@ -27,11 +27,18 @@ namespace validator { class ProofLink : public td::CntObject { public: + struct BasicHeaderInfo { + UnixTime utime; + CatchainSeqno cc_seqno; + td::uint32 validator_set_hash; + BlockSeqno prev_key_mc_seqno; + }; virtual ~ProofLink() = default; virtual BlockIdExt block_id() const = 0; virtual td::BufferSlice data() const = 0; virtual td::Result prev_key_mc_seqno() const = 0; virtual td::Result> get_key_block_config() const = 0; + virtual td::Result get_basic_header_info() const = 0; }; class Proof : virtual public ProofLink { diff --git a/validator/interfaces/shard.h b/validator/interfaces/shard.h index ffcfd381..33b98c1e 100644 --- a/validator/interfaces/shard.h +++ b/validator/interfaces/shard.h @@ -79,6 +79,7 @@ class MasterchainState : virtual public ShardState { virtual bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const = 0; virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0; + virtual td::Result> get_key_block_config() const = 0; virtual td::Status prepare() { return td::Status::OK(); } diff --git a/validator/manager.cpp b/validator/manager.cpp index 3b69795e..6b409a66 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1908,12 +1908,14 @@ void ValidatorManagerImpl::send_peek_key_block_request() { } void ValidatorManagerImpl::prepare_stats(td::Promise>> promise) { + std::vector> vec; + vec.emplace_back("unixtime", td::to_string(static_cast(td::Clocks::system()))); if (!last_masterchain_block_handle_) { - promise.set_value(std::vector>()); + promise.set_value(std::move(vec)); return; } - std::vector> vec; vec.emplace_back("masterchainblock", last_masterchain_block_id_.to_str()); + vec.emplace_back("masterchainblocktime", td::to_string(last_masterchain_block_handle_->unix_time())); vec.emplace_back("gcmasterchainblock", gc_masterchain_handle_->id().to_str()); vec.emplace_back("keymasterchainblock", last_key_block_handle_->id().to_str()); vec.emplace_back("knownkeymasterchainblock", last_known_key_block_handle_->id().to_str()); diff --git a/validator/validate-broadcast.cpp b/validator/validate-broadcast.cpp index feec2e2c..3aca6d0f 100644 --- a/validator/validate-broadcast.cpp +++ b/validator/validate-broadcast.cpp @@ -69,33 +69,150 @@ void ValidateBroadcast::start_up() { return; } - auto val_set = last_masterchain_state_->get_validator_set(broadcast_.block_id.shard_full()); - if (val_set.not_null() && val_set->get_catchain_seqno() == broadcast_.catchain_seqno && - val_set->get_validator_set_hash() == broadcast_.validator_set_hash) { - auto S = val_set->check_signatures(broadcast_.block_id.root_hash, broadcast_.block_id.file_hash, sig_set_); - if (S.is_ok()) { - checked_signatures(); + if (broadcast_.block_id.is_masterchain()) { + auto R = create_proof(broadcast_.block_id, broadcast_.proof.clone()); + if (R.is_error()) { + abort_query(R.move_as_error_prefix("bad proof: ")); + return; + } + proof_ = R.move_as_ok(); + auto hR = proof_->get_basic_header_info(); + if (hR.is_error()) { + abort_query(hR.move_as_error_prefix("bad proof: ")); + return; + } + header_info_ = hR.move_as_ok(); + } else { + auto R = create_proof_link(broadcast_.block_id, broadcast_.proof.clone()); + if (R.is_error()) { + abort_query(R.move_as_error_prefix("bad proof link: ")); + return; + } + proof_link_ = R.move_as_ok(); + auto hR = proof_link_->get_basic_header_info(); + if (hR.is_error()) { + abort_query(hR.move_as_error_prefix("bad proof link: ")); + return; + } + header_info_ = hR.move_as_ok(); + } + + BlockSeqno key_block_seqno = header_info_.prev_key_mc_seqno; + exact_key_block_handle_ = key_block_seqno <= last_known_masterchain_block_handle_->id().seqno(); + if (key_block_seqno < last_known_masterchain_block_handle_->id().seqno()) { + if (key_block_seqno < last_masterchain_state_->get_seqno()) { + BlockIdExt block_id; + if (!last_masterchain_state_->get_old_mc_block_id(key_block_seqno, block_id)) { + abort_query(td::Status::Error(ErrorCode::error, "too old reference key block")); + return; + } + got_key_block_id(block_id); + } else if (key_block_seqno == last_masterchain_state_->get_seqno()) { + got_key_block_handle(last_masterchain_block_handle_); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, + R.move_as_error_prefix("cannot find reference key block id: ")); + } else { + td::actor::send_closure(SelfId, &ValidateBroadcast::got_key_block_id, R.move_as_ok()); + } + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_by_seqno_from_db, + AccountIdPrefixFull{masterchainId, 0}, key_block_seqno, std::move(P)); + } + } else { + got_key_block_handle(last_known_masterchain_block_handle_); + } +} + +void ValidateBroadcast::got_key_block_id(BlockIdExt block_id) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, + R.move_as_error_prefix("cannot find reference key block handle: ")); + } else { + td::actor::send_closure(SelfId, &ValidateBroadcast::got_key_block_handle, R.move_as_ok()); + } + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id, false, std::move(P)); +} + +void ValidateBroadcast::got_key_block_handle(BlockHandle handle) { + if (handle->id().seqno() == 0) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, + R.move_as_error_prefix("failed to get zero state: ")); + } else { + td::actor::send_closure(SelfId, &ValidateBroadcast::got_zero_state, td::Ref{R.move_as_ok()}); + } + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, handle, std::move(P)); + } else { + if (!handle->inited_proof() && !handle->inited_proof_link()) { + abort_query(td::Status::Error(ErrorCode::notready, "reference key block proof not received")); + return; + } + if (!handle->is_key_block()) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "reference key block is not key")); + return; + } + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, + R.move_as_error_prefix("cannot get reference key block proof: ")); + } else { + td::actor::send_closure(SelfId, &ValidateBroadcast::got_key_block_proof_link, R.move_as_ok()); + } + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_proof_link_from_db, handle, std::move(P)); + } +} + +void ValidateBroadcast::got_key_block_proof_link(td::Ref key_proof_link) { + key_proof_link_ = key_proof_link; + auto confR = key_proof_link->get_key_block_config(); + if (confR.is_error()) { + abort_query(confR.move_as_error_prefix("failed to extract config from key proof: ")); + return; + } + check_signatures_common(confR.move_as_ok()); +} + +void ValidateBroadcast::got_zero_state(td::Ref state) { + zero_state_ = state; + auto confR = state->get_key_block_config(); + if (confR.is_error()) { + abort_query(confR.move_as_error_prefix("failed to extract config from zero state: ")); + return; + } + check_signatures_common(confR.move_as_ok()); +} + +void ValidateBroadcast::check_signatures_common(td::Ref conf) { + auto val_set = conf->get_validator_set(broadcast_.block_id.shard_full(), header_info_.utime, header_info_.cc_seqno); + if (val_set.is_null()) { + abort_query(td::Status::Error(ErrorCode::notready, "failed to compute validator set")); + return; + } + + if (val_set->get_validator_set_hash() != header_info_.validator_set_hash) { + if (!exact_key_block_handle_) { + abort_query(td::Status::Error(ErrorCode::notready, "too new block, don't know recent enough key block")); return; } else { - abort_query(S.move_as_error_prefix("failed signature check: ")); + abort_query(td::Status::Error(ErrorCode::notready, "bad validator set hash")); return; } } - - val_set = last_masterchain_state_->get_next_validator_set(broadcast_.block_id.shard_full()); - if (val_set.not_null() && val_set->get_catchain_seqno() == broadcast_.catchain_seqno && - val_set->get_validator_set_hash() == broadcast_.validator_set_hash) { - auto S = val_set->check_signatures(broadcast_.block_id.root_hash, broadcast_.block_id.file_hash, sig_set_); - if (S.is_ok()) { - checked_signatures(); - return; - } else { - abort_query(S.move_as_error_prefix("failed signature check: ")); - return; - } + auto S = val_set->check_signatures(broadcast_.block_id.root_hash, broadcast_.block_id.file_hash, sig_set_); + if (S.is_ok()) { + checked_signatures(); + } else { + abort_query(S.move_as_error_prefix("failed signature check: ")); } - - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad signature set")); } void ValidateBroadcast::checked_signatures() { @@ -142,13 +259,7 @@ void ValidateBroadcast::written_block_data() { checked_proof(); return; } - auto proofR = create_proof(broadcast_.block_id, broadcast_.proof.clone()); - if (proofR.is_error()) { - abort_query(proofR.move_as_error_prefix("bad proof: ")); - return; - } - proof_ = proofR.move_as_ok(); - if (handle_->id().id.seqno == last_masterchain_block_handle_->id().id.seqno + 1) { + if (exact_key_block_handle_) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error_prefix("db error: ")); @@ -156,7 +267,12 @@ void ValidateBroadcast::written_block_data() { td::actor::send_closure(SelfId, &ValidateBroadcast::checked_proof); } }); - run_check_proof_query(broadcast_.block_id, proof_, manager_, timeout_, std::move(P)); + if (!key_proof_link_.is_null()) { + run_check_proof_query(broadcast_.block_id, proof_, manager_, timeout_, std::move(P), key_proof_link_); + } else { + CHECK(!zero_state_.not_null()); + run_check_proof_query(broadcast_.block_id, proof_, manager_, timeout_, std::move(P), zero_state_); + } } else { checked_proof(); } @@ -165,12 +281,6 @@ void ValidateBroadcast::written_block_data() { checked_proof(); return; } - auto proofR = create_proof_link(broadcast_.block_id, broadcast_.proof.clone()); - if (proofR.is_error()) { - abort_query(proofR.move_as_error()); - return; - } - proof_link_ = proofR.move_as_ok(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error_prefix("db error: ")); @@ -183,7 +293,10 @@ void ValidateBroadcast::written_block_data() { } void ValidateBroadcast::checked_proof() { - if (handle_->inited_proof()) { + if (handle_->inited_proof() && handle_->is_key_block()) { + td::actor::send_closure(manager_, &ValidatorManager::update_last_known_key_block, handle_, false); + } + if (handle_->inited_proof() && handle_->id().seqno() - last_masterchain_block_handle_->id().seqno() <= 16) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error()); diff --git a/validator/validate-broadcast.hpp b/validator/validate-broadcast.hpp index f075cc6a..4cb8ba49 100644 --- a/validator/validate-broadcast.hpp +++ b/validator/validate-broadcast.hpp @@ -31,7 +31,8 @@ class ValidateBroadcast : public td::actor::Actor { BlockHandle last_masterchain_block_handle_; td::Ref last_masterchain_state_; BlockHandle last_known_masterchain_block_handle_; - td::Ref last_known_masterchain_block_proof_; + + ProofLink::BasicHeaderInfo header_info_; td::actor::ActorId manager_; td::Timestamp timeout_; @@ -45,6 +46,10 @@ class ValidateBroadcast : public td::actor::Actor { td::PerfWarningTimer perf_timer_{"validatebroadcast", 0.1}; + bool exact_key_block_handle_; + td::Ref key_proof_link_; + td::Ref zero_state_; + public: ValidateBroadcast(BlockBroadcast broadcast, BlockHandle last_masterchain_block_handle, td::Ref last_masterchain_state, BlockHandle last_known_masterchain_block_handle, @@ -59,6 +64,11 @@ class ValidateBroadcast : public td::actor::Actor { } void start_up() override; + void got_key_block_id(BlockIdExt block_id); + void got_key_block_handle(BlockHandle block_handle); + void got_key_block_proof_link(td::Ref proof_link); + void got_zero_state(td::Ref state); + void check_signatures_common(td::Ref conf); void checked_signatures(); void got_block_handle(BlockHandle handle); void written_block_data();