diff --git a/blockchain-explorer/CMakeLists.txt b/blockchain-explorer/CMakeLists.txt index 36d4e5069..86cb3e740 100644 --- a/blockchain-explorer/CMakeLists.txt +++ b/blockchain-explorer/CMakeLists.txt @@ -36,6 +36,7 @@ endif() target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIR}) target_link_libraries(blockchain-explorer tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ${MHD_LIBRARY}) +target_link_libraries(blockchain-explorer lite-client-common) install(TARGETS blockchain-explorer RUNTIME DESTINATION bin) diff --git a/blockchain-explorer/blockchain-explorer-query.cpp b/blockchain-explorer/blockchain-explorer-query.cpp index 1808a3c46..26a6787e1 100644 --- a/blockchain-explorer/blockchain-explorer-query.cpp +++ b/blockchain-explorer/blockchain-explorer-query.cpp @@ -1432,7 +1432,7 @@ void HttpQueryStatus::finish_query() { for (td::uint32 i = 0; i < results_.ips.size(); i++) { A << ""; if (results_.ips[i].is_valid()) { - A << "" << results_.ips[i] << ""; + A << "" << results_.ips[i].get_ip_str() << ":" << results_.ips[i].get_port() << ""; } else { A << "hidden"; } diff --git a/blockchain-explorer/blockchain-explorer.cpp b/blockchain-explorer/blockchain-explorer.cpp index 3b5346b73..ca50d5266 100644 --- a/blockchain-explorer/blockchain-explorer.cpp +++ b/blockchain-explorer/blockchain-explorer.cpp @@ -57,6 +57,7 @@ #include "auto/tl/lite_api.h" #include "ton/lite-tl.hpp" #include "tl-utils/lite-utils.hpp" +#include "lite-client/ext-client.h" #include @@ -127,7 +128,7 @@ class CoreActor : public CoreActorInterface { private: std::string global_config_ = "ton-global.config"; - std::vector> clients_; + td::actor::ActorOwn client_; td::uint32 http_port_ = 80; MHD_Daemon* daemon_ = nullptr; @@ -137,35 +138,29 @@ class CoreActor : public CoreActorInterface { bool hide_ips_ = false; - std::unique_ptr make_callback(td::uint32 idx) { - class Callback : public ton::adnl::AdnlExtClient::Callback { + td::unique_ptr make_callback() { + class Callback : public liteclient::ExtClient::Callback { public: - void on_ready() override { - td::actor::send_closure(id_, &CoreActor::conn_ready, idx_); - } - void on_stop_ready() override { - td::actor::send_closure(id_, &CoreActor::conn_closed, idx_); - } - Callback(td::actor::ActorId id, td::uint32 idx) : id_(std::move(id)), idx_(idx) { + Callback(td::actor::ActorId id) : id_(std::move(id)) { } private: td::actor::ActorId id_; - td::uint32 idx_; }; - return std::make_unique(actor_id(this), idx); + return td::make_unique(actor_id(this)); } std::shared_ptr new_result_; td::int32 attempt_ = 0; td::int32 waiting_ = 0; - std::vector ready_; + size_t n_servers_ = 0; void run_queries(); - void got_result(td::uint32 idx, td::int32 attempt, td::Result data); - void send_query(td::uint32 idx); + void got_servers_ready(td::int32 attempt, std::vector ready); + void send_ping(td::uint32 idx); + void got_ping_result(td::uint32 idx, td::int32 attempt, td::Result data); void add_result() { if (new_result_) { @@ -196,12 +191,6 @@ class CoreActor : public CoreActorInterface { static CoreActor* instance_; td::actor::ActorId self_id_; - void conn_ready(td::uint32 idx) { - ready_.at(idx) = true; - } - void conn_closed(td::uint32 idx) { - ready_.at(idx) = false; - } void set_global_config(std::string str) { global_config_ = str; } @@ -226,10 +215,7 @@ class CoreActor : public CoreActorInterface { hide_ips_ = value; } - void send_lite_query(td::uint32 idx, td::BufferSlice query, td::Promise promise); - void send_lite_query(td::BufferSlice data, td::Promise promise) override { - return send_lite_query(0, std::move(data), std::move(promise)); - } + void send_lite_query(td::BufferSlice query, td::Promise promise) override; void get_last_result(td::Promise> promise) override { } void get_results(td::uint32 max, td::Promise promise) override { @@ -449,33 +435,27 @@ class CoreActor : public CoreActorInterface { } void run() { + std::vector servers; if (remote_public_key_.empty()) { auto G = td::read_file(global_config_).move_as_ok(); auto gc_j = td::json_decode(G.as_slice()).move_as_ok(); ton::ton_api::liteclient_config_global gc; ton::ton_api::from_json(gc, gc_j.get_object()).ensure(); - - CHECK(gc.liteservers_.size() > 0); - td::uint32 size = static_cast(gc.liteservers_.size()); - ready_.resize(size, false); - - for (td::uint32 i = 0; i < size; i++) { - auto& cli = gc.liteservers_[i]; - td::IPAddress addr; - addr.init_host_port(td::IPAddress::ipv4_to_str(cli->ip_), cli->port_).ensure(); - addrs_.push_back(addr); - clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull::create(cli->id_).move_as_ok(), - addr, make_callback(i))); + auto r_servers = liteclient::LiteServerConfig::parse_global_config(gc); + r_servers.ensure(); + servers = r_servers.move_as_ok(); + for (const auto& serv : servers) { + addrs_.push_back(serv.addr); } } else { if (!remote_addr_.is_valid()) { LOG(FATAL) << "remote addr not set"; } - ready_.resize(1, false); addrs_.push_back(remote_addr_); - clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull{remote_public_key_}, - remote_addr_, make_callback(0))); + servers.push_back(liteclient::LiteServerConfig{ton::adnl::AdnlNodeIdFull{remote_public_key_}, remote_addr_}); } + n_servers_ = servers.size(); + client_ = liteclient::ExtClient::create(std::move(servers), make_callback(), true); daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(http_port_), nullptr, nullptr, &process_http_request, nullptr, MHD_OPTION_NOTIFY_COMPLETED, request_completed, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END); @@ -483,7 +463,46 @@ class CoreActor : public CoreActorInterface { } }; -void CoreActor::got_result(td::uint32 idx, td::int32 attempt, td::Result R) { +void CoreActor::run_queries() { + waiting_ = 0; + new_result_ = std::make_shared(n_servers_, td::Timestamp::at_unix(attempt_ * 60)); + td::actor::send_closure(client_, &liteclient::ExtClient::get_servers_status, + [SelfId = actor_id(this), attempt = attempt_](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &CoreActor::got_servers_ready, attempt, R.move_as_ok()); + }); +} + +void CoreActor::got_servers_ready(td::int32 attempt, std::vector ready) { + if (attempt != attempt_) { + return; + } + CHECK(ready.size() == n_servers_); + for (td::uint32 i = 0; i < n_servers_; i++) { + if (ready[i]) { + send_ping(i); + } + } + CHECK(waiting_ >= 0); + if (waiting_ == 0) { + add_result(); + } +} + +void CoreActor::send_ping(td::uint32 idx) { + waiting_++; + auto query = ton::create_tl_object(); + auto q = ton::create_tl_object(serialize_tl_object(query, true)); + + auto P = + td::PromiseCreator::lambda([SelfId = actor_id(this), idx, attempt = attempt_](td::Result R) { + td::actor::send_closure(SelfId, &CoreActor::got_ping_result, idx, attempt, std::move(R)); + }); + td::actor::send_closure(client_, &liteclient::ExtClient::send_query_to_server, "query", serialize_tl_object(q, true), + idx, td::Timestamp::in(10.0), std::move(P)); +} + +void CoreActor::got_ping_result(td::uint32 idx, td::int32 attempt, td::Result R) { if (attempt != attempt_) { return; } @@ -524,39 +543,7 @@ void CoreActor::got_result(td::uint32 idx, td::int32 attempt, td::Result(); - auto q = ton::create_tl_object(serialize_tl_object(query, true)); - - auto P = - td::PromiseCreator::lambda([SelfId = actor_id(this), idx, attempt = attempt_](td::Result R) { - td::actor::send_closure(SelfId, &CoreActor::got_result, idx, attempt, std::move(R)); - }); - td::actor::send_closure(clients_[idx], &ton::adnl::AdnlExtClient::send_query, "query", serialize_tl_object(q, true), - td::Timestamp::in(10.0), std::move(P)); -} - -void CoreActor::run_queries() { - waiting_ = 0; - new_result_ = std::make_shared(ready_.size(), td::Timestamp::at_unix(attempt_ * 60)); - for (td::uint32 i = 0; i < ready_.size(); i++) { - send_query(i); - } - CHECK(waiting_ >= 0); - if (waiting_ == 0) { - add_result(); - } -} - -void CoreActor::send_lite_query(td::uint32 idx, td::BufferSlice query, td::Promise promise) { - if (!ready_[idx]) { - promise.set_error(td::Status::Error(ton::ErrorCode::notready, "ext conn not ready")); - return; - } +void CoreActor::send_lite_query(td::BufferSlice query, td::Promise promise) { auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); @@ -574,7 +561,7 @@ void CoreActor::send_lite_query(td::uint32 idx, td::BufferSlice query, td::Promi promise.set_value(std::move(B)); }); auto q = ton::create_tl_object(std::move(query)); - td::actor::send_closure(clients_[idx], &ton::adnl::AdnlExtClient::send_query, "query", serialize_tl_object(q, true), + td::actor::send_closure(client_, &liteclient::ExtClient::send_query, "query", serialize_tl_object(q, true), td::Timestamp::in(10.0), std::move(P)); } diff --git a/create-hardfork/create-hardfork.cpp b/create-hardfork/create-hardfork.cpp index a24f3f8e0..31a60b56a 100644 --- a/create-hardfork/create-hardfork.cpp +++ b/create-hardfork/create-hardfork.cpp @@ -236,9 +236,8 @@ class HardforkCreator : public td::actor::Actor { td::actor::send_closure(id_, &ton::validator::ValidatorManager::sync_complete, td::PromiseCreator::lambda([](td::Unit) {})); } - void add_shard(ton::ShardIdFull) override { - } - void del_shard(ton::ShardIdFull) override { + void on_new_masterchain_block(td::Ref state, + std::set shards_to_monitor) override { } void send_ihr_message(ton::AccountIdPrefixFull dst, td::BufferSlice data) override { } @@ -270,9 +269,8 @@ class HardforkCreator : public td::actor::Actor { void get_next_key_blocks(ton::BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) override { } - void download_archive(ton::BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - - td::Promise promise) override { + void download_archive(ton::BlockSeqno masterchain_seqno, ton::ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override { } void new_key_block(ton::validator::BlockHandle handle) override { diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 6f9754267..560433bfe 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -818,7 +818,7 @@ _ OracleBridgeParams = ConfigParam 72; // Binance Smart Chain bridge _ OracleBridgeParams = ConfigParam 73; // Polygon bridge // Note that chains in which bridge, minter and jetton-wallet operate are fixated -jetton_bridge_prices#_ bridge_burn_fee:Coins bridge_mint_fee:Coins +jetton_bridge_prices#_ bridge_burn_fee:Coins bridge_mint_fee:Coins wallet_min_tons_for_storage:Coins wallet_gas_consumption:Coins minter_min_tons_for_storage:Coins diff --git a/dht-server/dht-server.cpp b/dht-server/dht-server.cpp index eb183cad6..fa9fad132 100644 --- a/dht-server/dht-server.cpp +++ b/dht-server/dht-server.cpp @@ -54,7 +54,7 @@ Config::Config() { out_port = 3278; } -Config::Config(ton::ton_api::engine_validator_config &config) { +Config::Config(const ton::ton_api::engine_validator_config &config) { out_port = static_cast(config.out_port_); if (!out_port) { out_port = 3278; @@ -162,6 +162,7 @@ ton::tl_object_ptr Config::tl() const { control_vec.push_back(ton::create_tl_object(x.second.key.tl(), x.first, std::move(control_proc_vec))); } + std::vector> shard_vec; auto gc_vec = ton::create_tl_object(std::vector{}); for (auto &id : gc) { @@ -170,7 +171,7 @@ ton::tl_object_ptr Config::tl() const { return ton::create_tl_object( out_port, std::move(addrs_vec), std::move(adnl_vec), std::move(dht_vec), std::move(val_vec), ton::PublicKeyHash::zero().tl(), std::move(full_node_slaves_vec), std::move(full_node_masters_vec), - nullptr, nullptr, std::move(liteserver_vec), std::move(control_vec), std::move(gc_vec)); + nullptr, nullptr, std::move(liteserver_vec), std::move(control_vec), std::move(shard_vec), std::move(gc_vec)); } td::Result Config::config_add_network_addr(td::IPAddress in_ip, td::IPAddress out_ip, diff --git a/dht-server/dht-server.hpp b/dht-server/dht-server.hpp index 5b81875be..7c9e56194 100644 --- a/dht-server/dht-server.hpp +++ b/dht-server/dht-server.hpp @@ -94,7 +94,7 @@ struct Config { ton::tl_object_ptr tl() const; Config(); - Config(ton::ton_api::engine_validator_config &config); + Config(const ton::ton_api::engine_validator_config &config); }; class DhtServer : public td::actor::Actor { diff --git a/lite-client/CMakeLists.txt b/lite-client/CMakeLists.txt index c6988cf56..b28a14e9a 100644 --- a/lite-client/CMakeLists.txt +++ b/lite-client/CMakeLists.txt @@ -1,9 +1,10 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -add_library(lite-client-common STATIC lite-client-common.cpp lite-client-common.h) +add_library(lite-client-common STATIC lite-client-common.cpp lite-client-common.h ext-client.cpp ext-client.h + query-utils.hpp query-utils.cpp) target_link_libraries(lite-client-common PUBLIC tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto) -add_executable(lite-client lite-client.cpp lite-client.h) +add_executable(lite-client lite-client.cpp lite-client.h ext-client.h ext-client.cpp) target_link_libraries(lite-client tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils terminal lite-client-common git) install(TARGETS lite-client RUNTIME DESTINATION bin) diff --git a/lite-client/ext-client.cpp b/lite-client/ext-client.cpp new file mode 100644 index 000000000..a0e48e64a --- /dev/null +++ b/lite-client/ext-client.cpp @@ -0,0 +1,228 @@ +/* + 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 "ext-client.h" +#include "td/utils/Random.h" +#include "ton/ton-shard.h" + +namespace liteclient { + +class ExtClientImpl : public ExtClient { + public: + ExtClientImpl(std::vector liteservers, td::unique_ptr callback, bool connect_to_all) + : callback_(std::move(callback)), connect_to_all_(connect_to_all) { + CHECK(!liteservers.empty()); + servers_.resize(liteservers.size()); + for (size_t i = 0; i < servers_.size(); ++i) { + servers_[i].config = std::move(liteservers[i]); + servers_[i].idx = i; + } + } + + void start_up() override { + LOG(INFO) << "Started ext client, " << servers_.size() << " liteservers"; + td::Random::Fast rnd; + td::random_shuffle(td::as_mutable_span(servers_), rnd); + server_indices_.resize(servers_.size()); + for (size_t i = 0; i < servers_.size(); ++i) { + server_indices_[servers_[i].idx] = i; + } + + if (connect_to_all_) { + for (size_t i = 0; i < servers_.size(); ++i) { + prepare_server(i, nullptr); + } + } + } + + void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, + td::Promise promise) override { + QueryInfo query_info = get_query_info(data); + TRY_RESULT_PROMISE(promise, server_idx, select_server(query_info)); + send_query_internal(std::move(name), std::move(data), std::move(query_info), server_idx, timeout, + std::move(promise)); + } + + void send_query_to_server(std::string name, td::BufferSlice data, size_t server_idx, td::Timestamp timeout, + td::Promise promise) override { + if (server_idx >= servers_.size()) { + promise.set_error(td::Status::Error(PSTRING() << "server idx " << server_idx << " is too big")); + return; + } + server_idx = server_indices_[server_idx]; + QueryInfo query_info = get_query_info(data); + prepare_server(server_idx, &query_info); + send_query_internal(std::move(name), std::move(data), std::move(query_info), server_idx, timeout, + std::move(promise)); + } + + void get_servers_status(td::Promise> promise) override { + std::vector status(servers_.size()); + for (const Server& s : servers_) { + status[s.idx] = s.alive; + } + promise.set_result(std::move(status)); + } + + void reset_servers() override { + LOG(INFO) << "Force resetting all liteservers"; + for (Server& server : servers_) { + server.alive = false; + server.timeout = {}; + server.ignore_until = {}; + server.client.reset(); + } + } + + private: + void send_query_internal(std::string name, td::BufferSlice data, QueryInfo query_info, size_t server_idx, + td::Timestamp timeout, td::Promise promise) { + auto& server = servers_[server_idx]; + CHECK(!server.client.empty()); + if (!connect_to_all_) { + alarm_timestamp().relax(server.timeout = td::Timestamp::in(MAX_NO_QUERIES_TIMEOUT)); + } + td::Promise P = [SelfId = actor_id(this), server_idx, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_error() && + (R.error().code() == ton::ErrorCode::timeout || R.error().code() == ton::ErrorCode::cancelled)) { + td::actor::send_closure(SelfId, &ExtClientImpl::on_server_error, server_idx); + } + promise.set_result(std::move(R)); + }; + LOG(DEBUG) << "Sending query " << query_info.to_str() << " to server #" << server.idx << " (" + << server.config.addr.get_ip_str() << ":" << server.config.addr.get_port() << ")"; + send_closure(server.client, &ton::adnl::AdnlExtClient::send_query, std::move(name), std::move(data), timeout, + std::move(P)); + } + + td::Result select_server(const QueryInfo& query_info) { + for (size_t i = 0; i < servers_.size(); ++i) { + if (servers_[i].alive && servers_[i].config.accepts_query(query_info)) { + return i; + } + } + size_t server_idx = servers_.size(); + int cnt = 0; + int best_priority = -1; + for (size_t i = 0; i < servers_.size(); ++i) { + Server& server = servers_[i]; + if (!server.config.accepts_query(query_info)) { + continue; + } + int priority = 0; + priority += (server.ignore_until && !server.ignore_until.is_in_past() ? 0 : 10); + if (priority < best_priority) { + continue; + } + if (priority > best_priority) { + best_priority = priority; + cnt = 0; + } + if (td::Random::fast(0, cnt) == 0) { + server_idx = i; + } + ++cnt; + } + if (server_idx == servers_.size()) { + return td::Status::Error(PSTRING() << "no liteserver for query " << query_info.to_str()); + } + prepare_server(server_idx, &query_info); + return server_idx; + } + + void prepare_server(size_t server_idx, const QueryInfo* query_info) { + Server& server = servers_[server_idx]; + if (server.alive) { + return; + } + server.alive = true; + server.ignore_until = {}; + if (!connect_to_all_) { + alarm_timestamp().relax(server.timeout = td::Timestamp::in(MAX_NO_QUERIES_TIMEOUT)); + } + if (!server.client.empty()) { + return; + } + + class Callback : public ton::adnl::AdnlExtClient::Callback { + public: + explicit Callback(td::actor::ActorId parent, size_t idx) : parent_(std::move(parent)), idx_(idx) { + } + void on_ready() override { + } + void on_stop_ready() override { + td::actor::send_closure(parent_, &ExtClientImpl::on_server_error, idx_); + } + + private: + td::actor::ActorId parent_; + size_t idx_; + }; + LOG(INFO) << "Connecting to liteserver #" << server.idx << " (" << server.config.addr.get_ip_str() << ":" + << server.config.addr.get_port() << ") for query " << (query_info ? query_info->to_str() : "[none]"); + server.client = ton::adnl::AdnlExtClient::create(server.config.adnl_id, server.config.addr, + std::make_unique(actor_id(this), server_idx)); + } + + struct Server { + LiteServerConfig config; + size_t idx = 0; + td::actor::ActorOwn client; + bool alive = false; + td::Timestamp timeout = td::Timestamp::never(); + td::Timestamp ignore_until = td::Timestamp::never(); + }; + std::vector servers_; + std::vector server_indices_; + + td::unique_ptr callback_; + bool connect_to_all_ = false; + static constexpr double MAX_NO_QUERIES_TIMEOUT = 100.0; + static constexpr double BAD_SERVER_TIMEOUT = 30.0; + + void alarm() override { + if (connect_to_all_) { + return; + } + for (Server& server : servers_) { + if (server.timeout && server.timeout.is_in_past()) { + LOG(INFO) << "Closing connection to liteserver #" << server.idx << " (" << server.config.addr.get_ip_str() + << ":" << server.config.addr.get_port() << ")"; + server.client.reset(); + server.alive = false; + server.ignore_until = {}; + } + } + } + + void on_server_error(size_t idx) { + servers_[idx].alive = false; + servers_[idx].ignore_until = td::Timestamp::in(BAD_SERVER_TIMEOUT); + } +}; + +td::actor::ActorOwn ExtClient::create(ton::adnl::AdnlNodeIdFull dst, td::IPAddress dst_addr, + td::unique_ptr callback) { + return create({LiteServerConfig{dst, dst_addr}}, std::move(callback)); +} + +td::actor::ActorOwn ExtClient::create(std::vector liteservers, + td::unique_ptr callback, bool connect_to_all) { + return td::actor::create_actor("ExtClient", std::move(liteservers), std::move(callback), + connect_to_all); +} +} // namespace liteclient diff --git a/lite-client/ext-client.h b/lite-client/ext-client.h new file mode 100644 index 000000000..ef4523fd6 --- /dev/null +++ b/lite-client/ext-client.h @@ -0,0 +1,48 @@ +/* + 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 "td/actor/actor.h" +#include "ton/ton-types.h" +#include "adnl/adnl-ext-client.h" +#include "query-utils.hpp" + +namespace liteclient { +class ExtClient : public td::actor::Actor { + public: + class Callback { + public: + virtual ~Callback() = default; + }; + + virtual void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, + td::Promise promise) = 0; + virtual void send_query_to_server(std::string name, td::BufferSlice data, size_t server_idx, td::Timestamp timeout, + td::Promise promise) { + promise.set_error(td::Status::Error("not supported")); + } + virtual void get_servers_status(td::Promise> promise) { + promise.set_error(td::Status::Error("not supported")); + } + virtual void reset_servers() { + } + + static td::actor::ActorOwn create(ton::adnl::AdnlNodeIdFull dst, td::IPAddress dst_addr, + td::unique_ptr callback); + static td::actor::ActorOwn create(std::vector liteservers, + td::unique_ptr callback, bool connect_to_all = false); +}; +} // namespace liteclient diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 77c9a8c8b..dc09ae52b 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -29,22 +29,16 @@ #include "lite-client-common.h" -#include "adnl/adnl-ext-client.h" #include "tl-utils/lite-utils.hpp" #include "auto/tl/ton_api_json.h" #include "auto/tl/lite_api.hpp" #include "td/utils/OptionParser.h" #include "td/utils/Time.h" #include "td/utils/filesystem.h" -#include "td/utils/format.h" #include "td/utils/Random.h" #include "td/utils/crypto.h" -#include "td/utils/overloaded.h" #include "td/utils/port/signals.h" -#include "td/utils/port/stacktrace.h" -#include "td/utils/port/StdStreams.h" #include "td/utils/port/FileFd.h" -#include "terminal/terminal.h" #include "ton/lite-tl.hpp" #include "block/block-db.h" #include "block/block.h" @@ -58,18 +52,14 @@ #include "vm/vm.h" #include "vm/cp0.h" #include "vm/memo.h" -#include "ton/ton-shard.h" -#include "openssl/rand.hpp" #include "crypto/vm/utils.h" #include "crypto/common/util.h" #include "common/checksum.h" #if TD_DARWIN || TD_LINUX #include -#include #endif #include -#include #include "git.h" using namespace std::literals::string_literals; @@ -77,24 +67,6 @@ using td::Ref; int verbosity; -std::unique_ptr TestNode::make_callback() { - class Callback : public ton::adnl::AdnlExtClient::Callback { - public: - void on_ready() override { - td::actor::send_closure(id_, &TestNode::conn_ready); - } - void on_stop_ready() override { - td::actor::send_closure(id_, &TestNode::conn_closed); - } - Callback(td::actor::ActorId id) : id_(std::move(id)) { - } - - private: - td::actor::ActorId id_; - }; - return std::make_unique(actor_id(this)); -} - void TestNode::run() { class Cb : public td::TerminalIO::Callback { public: @@ -110,19 +82,20 @@ void TestNode::run() { io_ = td::TerminalIO::create("> ", readline_enabled_, ex_mode_, std::make_unique(actor_id(this))); td::actor::send_closure(io_, &td::TerminalIO::set_log_interface); - if (remote_public_key_.empty()) { + std::vector servers; + if (!single_remote_public_key_.empty()) { // Use single provided liteserver + servers.push_back( + liteclient::LiteServerConfig{ton::adnl::AdnlNodeIdFull{single_remote_public_key_}, single_remote_addr_}); + td::TerminalIO::out() << "using liteserver " << single_remote_addr_ << "\n"; + } else { auto G = td::read_file(global_config_).move_as_ok(); auto gc_j = td::json_decode(G.as_slice()).move_as_ok(); ton::ton_api::liteclient_config_global gc; ton::ton_api::from_json(gc, gc_j.get_object()).ensure(); - CHECK(gc.liteservers_.size() > 0); - auto idx = liteserver_idx_ >= 0 ? liteserver_idx_ - : td::Random::fast(0, static_cast(gc.liteservers_.size() - 1)); - CHECK(idx >= 0 && static_cast(idx) <= gc.liteservers_.size()); - auto& cli = gc.liteservers_[idx]; - remote_addr_.init_host_port(td::IPAddress::ipv4_to_str(cli->ip_), cli->port_).ensure(); - remote_public_key_ = ton::PublicKey{cli->id_}; - td::TerminalIO::out() << "using liteserver " << idx << " with addr " << remote_addr_ << "\n"; + auto r_servers = liteclient::LiteServerConfig::parse_global_config(gc); + r_servers.ensure(); + servers = r_servers.move_as_ok(); + if (gc.validator_ && gc.validator_->zero_state_) { zstate_id_.workchain = gc.validator_->zero_state_->workchain_; if (zstate_id_.workchain != ton::workchainInvalid) { @@ -131,10 +104,19 @@ void TestNode::run() { td::TerminalIO::out() << "zerostate set to " << zstate_id_.to_str() << "\n"; } } + + if (single_liteserver_idx_ != -1) { // Use single liteserver from config + CHECK(single_liteserver_idx_ >= 0 && (size_t)single_liteserver_idx_ < servers.size()); + td::TerminalIO::out() << "using liteserver #" << single_liteserver_idx_ << " with addr " + << servers[single_liteserver_idx_].addr << "\n"; + servers = {servers[single_liteserver_idx_]}; + } } + CHECK(!servers.empty()); + client_ = liteclient::ExtClient::create(std::move(servers), nullptr); + ready_ = true; - client_ = - ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull{remote_public_key_}, remote_addr_, make_callback()); + run_init_queries(); } void TestNode::got_result(td::Result R, td::Promise promise) { @@ -191,8 +173,8 @@ bool TestNode::envelope_send_query(td::BufferSlice query, td::Promise(std::move(query)), true); - td::actor::send_closure(client_, &ton::adnl::AdnlExtClient::send_query, "query", std::move(b), - td::Timestamp::in(10.0), std::move(P)); + td::actor::send_closure(client_, &liteclient::ExtClient::send_query, "query", std::move(b), td::Timestamp::in(10.0), + std::move(P)); return true; } @@ -319,9 +301,10 @@ bool TestNode::get_server_time() { if (F.is_error()) { LOG(ERROR) << "cannot parse answer to liteServer.getTime"; } else { - server_time_ = F.move_as_ok()->now_; - server_time_got_at_ = now(); - LOG(INFO) << "server time is " << server_time_ << " (delta " << server_time_ - server_time_got_at_ << ")"; + mc_server_time_ = F.move_as_ok()->now_; + mc_server_time_got_at_ = now(); + LOG(INFO) << "server time is " << mc_server_time_ << " (delta " << mc_server_time_ - mc_server_time_got_at_ + << ")"; } } }); @@ -335,7 +318,7 @@ bool TestNode::get_server_version(int mode) { }; void TestNode::got_server_version(td::Result res, int mode) { - server_ok_ = false; + mc_server_ok_ = false; if (res.is_error()) { LOG(ERROR) << "cannot get server version and time (server too old?)"; } else { @@ -344,11 +327,11 @@ void TestNode::got_server_version(td::Result res, int mode) { LOG(ERROR) << "cannot parse answer to liteServer.getVersion"; } else { auto a = F.move_as_ok(); - set_server_version(a->version_, a->capabilities_); - set_server_time(a->now_); + set_mc_server_version(a->version_, a->capabilities_); + set_mc_server_time(a->now_); } } - if (!server_ok_) { + if (!mc_server_ok_) { LOG(ERROR) << "server version is too old (at least " << (min_ls_version >> 8) << "." << (min_ls_version & 0xff) << " with capabilities " << min_ls_capabilities << " required), some queries are unavailable"; } @@ -357,24 +340,24 @@ void TestNode::got_server_version(td::Result res, int mode) { } } -void TestNode::set_server_version(td::int32 version, td::int64 capabilities) { - if (server_version_ != version || server_capabilities_ != capabilities) { - server_version_ = version; - server_capabilities_ = capabilities; - LOG(WARNING) << "server version is " << (server_version_ >> 8) << "." << (server_version_ & 0xff) - << ", capabilities " << server_capabilities_; +void TestNode::set_mc_server_version(td::int32 version, td::int64 capabilities) { + if (mc_server_version_ != version || mc_server_capabilities_ != capabilities) { + mc_server_version_ = version; + mc_server_capabilities_ = capabilities; + LOG(WARNING) << "server version is " << (mc_server_version_ >> 8) << "." << (mc_server_version_ & 0xff) + << ", capabilities " << mc_server_capabilities_; } - server_ok_ = (server_version_ >= min_ls_version) && !(~server_capabilities_ & min_ls_capabilities); + mc_server_ok_ = (mc_server_version_ >= min_ls_version) && !(~mc_server_capabilities_ & min_ls_capabilities); } -void TestNode::set_server_time(int server_utime) { - server_time_ = server_utime; - server_time_got_at_ = now(); - LOG(INFO) << "server time is " << server_time_ << " (delta " << server_time_ - server_time_got_at_ << ")"; +void TestNode::set_mc_server_time(int server_utime) { + mc_server_time_ = server_utime; + mc_server_time_got_at_ = now(); + LOG(INFO) << "server time is " << mc_server_time_ << " (delta " << mc_server_time_ - mc_server_time_got_at_ << ")"; } bool TestNode::get_server_mc_block_id() { - int mode = (server_capabilities_ & 2) ? 0 : -1; + int mode = (mc_server_capabilities_ & 2) ? 0 : -1; if (mode < 0) { auto b = ton::serialize_tl_object(ton::create_tl_object(), true); return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result res) -> void { @@ -448,8 +431,8 @@ void TestNode::got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt void TestNode::got_server_mc_block_id_ext(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int mode, int version, long long capabilities, int last_utime, int server_now) { - set_server_version(version, capabilities); - set_server_time(server_now); + set_mc_server_version(version, capabilities); + set_mc_server_time(server_now); if (last_utime > server_now) { LOG(WARNING) << "server claims to have a masterchain block " << blkid.to_str() << " created at " << last_utime << " (" << last_utime - server_now << " seconds in the future)"; @@ -457,10 +440,10 @@ void TestNode::got_server_mc_block_id_ext(ton::BlockIdExt blkid, ton::ZeroStateI LOG(WARNING) << "server appears to be out of sync: its newest masterchain block is " << blkid.to_str() << " created at " << last_utime << " (" << server_now - last_utime << " seconds ago according to the server's clock)"; - } else if (last_utime < server_time_got_at_ - 60) { + } else if (last_utime < mc_server_time_got_at_ - 60) { LOG(WARNING) << "either the server is out of sync, or the local clock is set incorrectly: the newest masterchain " "block known to server is " - << blkid.to_str() << " created at " << last_utime << " (" << server_now - server_time_got_at_ + << blkid.to_str() << " created at " << last_utime << " (" << server_now - mc_server_time_got_at_ << " seconds ago according to the local clock)"; } got_server_mc_block_id(blkid, zstateid, last_utime); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 90a2fb8a4..721d2b20d 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -26,6 +26,7 @@ Copyright 2017-2020 Telegram Systems LLP */ #pragma once +#include "ext-client.h" #include "adnl/adnl-ext-client.h" #include "tl-utils/tl-utils.hpp" #include "ton/ton-types.h" @@ -46,22 +47,24 @@ class TestNode : public td::actor::Actor { min_ls_version = 0x101, min_ls_capabilities = 1 }; // server version >= 1.1, capabilities at least +1 = build proof chains - td::actor::ActorOwn client_; + td::actor::ActorOwn client_; td::actor::ActorOwn io_; + bool ready_ = false; + + td::int32 single_liteserver_idx_ = -1; + td::IPAddress single_remote_addr_; + ton::PublicKey single_remote_public_key_; bool readline_enabled_ = true; - bool server_ok_ = false; - td::int32 liteserver_idx_ = -1; int print_limit_ = 1024; - bool ready_ = false; - bool inited_ = false; std::string db_root_; - int server_time_ = 0; - int server_time_got_at_ = 0; - int server_version_ = 0; - long long server_capabilities_ = 0; + int mc_server_time_ = 0; + int mc_server_time_got_at_ = 0; + int mc_server_version_ = 0; + long long mc_server_capabilities_ = 0; + bool mc_server_ok_ = false; ton::ZeroStateIdExt zstate_id_; ton::BlockIdExt mc_last_id_; @@ -76,9 +79,6 @@ class TestNode : public td::actor::Actor { const char *parse_ptr_, *parse_end_; td::Status error_; - td::IPAddress remote_addr_; - ton::PublicKey remote_public_key_; - std::vector known_blk_ids_; std::size_t shown_blk_ids_ = 0; @@ -89,8 +89,6 @@ class TestNode : public td::actor::Actor { std::map> cell_cache_; - std::unique_ptr make_callback(); - using creator_stats_func_t = std::function; @@ -183,8 +181,8 @@ class TestNode : public td::actor::Actor { void got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int created_at); void got_server_mc_block_id_ext(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int mode, int version, long long capabilities, int last_utime, int server_now); - void set_server_version(td::int32 version, td::int64 capabilities); - void set_server_time(int server_utime); + void set_mc_server_version(td::int32 version, td::int64 capabilities); + void set_mc_server_time(int server_utime); bool request_block(ton::BlockIdExt blkid); bool request_state(ton::BlockIdExt blkid); void got_mc_block(ton::BlockIdExt blkid, td::BufferSlice data); @@ -370,9 +368,6 @@ class TestNode : public td::actor::Actor { bool parse_shard_id(ton::ShardIdFull& shard); bool parse_block_id_ext(ton::BlockIdExt& blkid, bool allow_incomplete = false); bool parse_block_id_ext(std::string blk_id_string, ton::BlockIdExt& blkid, bool allow_incomplete = false) const; - bool parse_stack_value(td::Slice str, vm::StackEntry& value); - bool parse_stack_value(vm::StackEntry& value); - bool parse_stack_values(std::vector& values); bool register_blkid(const ton::BlockIdExt& blkid); bool show_new_blkids(bool all = false); bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const; @@ -391,16 +386,6 @@ class TestNode : public td::actor::Actor { static const tlb::TypenameLookup& get_tlb_dict(); public: - void conn_ready() { - LOG(ERROR) << "conn ready"; - ready_ = true; - if (!inited_) { - run_init_queries(); - } - } - void conn_closed() { - ready_ = false; - } void set_global_config(std::string str) { global_config_ = str; } @@ -411,10 +396,10 @@ class TestNode : public td::actor::Actor { readline_enabled_ = value; } void set_liteserver_idx(td::int32 idx) { - liteserver_idx_ = idx; + single_liteserver_idx_ = idx; } void set_remote_addr(td::IPAddress addr) { - remote_addr_ = addr; + single_remote_addr_ = addr; } void set_public_key(td::BufferSlice file_name) { auto R = [&]() -> td::Result { @@ -425,7 +410,7 @@ class TestNode : public td::actor::Actor { if (R.is_error()) { LOG(FATAL) << "bad server public key: " << R.move_as_error(); } - remote_public_key_ = R.move_as_ok(); + single_remote_public_key_ = R.move_as_ok(); } void decode_public_key(td::BufferSlice b64_key) { auto R = [&]() -> td::Result { @@ -437,7 +422,7 @@ class TestNode : public td::actor::Actor { if (R.is_error()) { LOG(FATAL) << "bad b64 server public key: " << R.move_as_error(); } - remote_public_key_ = R.move_as_ok(); + single_remote_public_key_ = R.move_as_ok(); } void set_fail_timeout(td::Timestamp ts) { fail_timeout_ = ts; @@ -475,8 +460,7 @@ class TestNode : public td::actor::Actor { bool envelope_send_query(td::BufferSlice query, td::Promise promise); void parse_line(td::BufferSlice data); - TestNode() { - } + TestNode() = default; void run(); }; diff --git a/lite-client/query-utils.cpp b/lite-client/query-utils.cpp new file mode 100644 index 000000000..a3a663be0 --- /dev/null +++ b/lite-client/query-utils.cpp @@ -0,0 +1,400 @@ +/* + 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 "query-utils.hpp" + +#include "block-parse.h" +#include "td/utils/overloaded.h" +#include "tl-utils/common-utils.hpp" + +#include "block/block-auto.h" +#include "auto/tl/lite_api.hpp" +#include "overlay/overlay-broadcast.hpp" +#include "tl-utils/lite-utils.hpp" +#include "ton/lite-tl.hpp" +#include "ton/ton-shard.h" + +#include + +namespace liteclient { + +using namespace ton; + +std::string QueryInfo::to_str() const { + td::StringBuilder sb; + sb << "[ " << lite_query_name_by_id(query_id) << " " << shard_id.to_str(); + switch (type) { + case t_simple: + break; + case t_seqno: + sb << " seqno=" << value; + break; + case t_utime: + sb << " utime=" << value; + break; + case t_lt: + sb << " lt=" << value; + break; + case t_mc_seqno: + sb << " mc_seqno=" << value; + break; + } + sb << " ]"; + return sb.as_cslice().str(); +} + +QueryInfo get_query_info(td::Slice data) { + auto F = fetch_tl_object(data, true); + if (F.is_ok()) { + data = F.ok()->data_; + } else { + fetch_tl_prefix(data, true).ignore(); + } + fetch_tl_prefix(data, true).ignore(); + auto Q = fetch_tl_object(data, true); + if (Q.is_error()) { + return {}; + } + return get_query_info(*Q.ok()); +} + +QueryInfo get_query_info(const lite_api::Function& f) { + QueryInfo info; + info.query_id = f.get_id(); + auto from_block_id = [&](const tl_object_ptr& id) { + BlockIdExt block_id = create_block_id(id); + info.shard_id = block_id.shard_full(); + info.type = QueryInfo::t_seqno; + info.value = block_id.seqno(); + }; + downcast_call( + const_cast(f), + td::overloaded([&](const lite_api::liteServer_getTime& q) { /* t_simple */ }, + [&](const lite_api::liteServer_getVersion& q) { /* t_simple */ }, + [&](const lite_api::liteServer_getMasterchainInfo& q) { /* t_simple */ }, + [&](const lite_api::liteServer_getMasterchainInfoExt& q) { /* t_simple */ }, + [&](const lite_api::liteServer_getBlock& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getBlockHeader& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getState& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getAccountState& q) { + BlockIdExt block_id = create_block_id(q.id_); + AccountIdPrefixFull acc_id_prefix = extract_addr_prefix(q.account_->workchain_, q.account_->id_); + info.shard_id = acc_id_prefix.as_leaf_shard(); + // See LiteQuery::perform_getAccountState + if (block_id.id.workchain != masterchainId) { + info.type = QueryInfo::t_seqno; + info.value = block_id.seqno(); + } else if (block_id.id.seqno != ~0U) { + info.type = QueryInfo::t_mc_seqno; + info.value = block_id.seqno(); + } else { + info.type = QueryInfo::t_simple; + } + }, + [&](const lite_api::liteServer_getAccountStatePrunned& q) { + BlockIdExt block_id = create_block_id(q.id_); + AccountIdPrefixFull acc_id_prefix = extract_addr_prefix(q.account_->workchain_, q.account_->id_); + info.shard_id = acc_id_prefix.as_leaf_shard(); + // See LiteQuery::perform_getAccountState + if (block_id.id.workchain != masterchainId) { + info.type = QueryInfo::t_seqno; + info.value = block_id.seqno(); + } else if (block_id.id.seqno != ~0U) { + info.type = QueryInfo::t_mc_seqno; + info.value = block_id.seqno(); + } else { + info.type = QueryInfo::t_simple; + } + }, + [&](const lite_api::liteServer_getOneTransaction& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getTransactions& q) { + AccountIdPrefixFull acc_id_prefix = extract_addr_prefix(q.account_->workchain_, q.account_->id_); + info.shard_id = acc_id_prefix.as_leaf_shard(); + info.type = QueryInfo::t_lt; + info.value = q.lt_; + }, + [&](const lite_api::liteServer_sendMessage& q) { + info.type = QueryInfo::t_simple; + auto r_root = vm::std_boc_deserialize(q.body_); + if (r_root.is_error()) { + return; + } + block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info; + if (!tlb::unpack_cell_inexact(r_root.ok(), msg_info)) { + return; + } + auto dest_prefix = block::tlb::MsgAddressInt::get_prefix(msg_info.dest); + if (!dest_prefix.is_valid()) { + return; + } + info.shard_id = dest_prefix.as_leaf_shard(); + }, + [&](const lite_api::liteServer_getShardInfo& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getAllShardsInfo& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_lookupBlock& q) { + BlockId block_id = create_block_id_simple(q.id_); + info.shard_id = block_id.shard_full(); + // See LiteQuery::perform_lookupBlock + if (q.mode_ & 1) { + info.type = QueryInfo::t_seqno; + info.value = block_id.seqno; + } else if (q.mode_ == 2) { + info.type = QueryInfo::t_lt; + info.value = q.lt_; + } else if (q.mode_ == 4) { + info.type = QueryInfo::t_utime; + info.value = q.utime_; + } + }, + [&](const lite_api::liteServer_lookupBlockWithProof& q) { + BlockId block_id = create_block_id_simple(q.id_); + info.shard_id = block_id.shard_full(); + // See LiteQuery::perform_lookupBlockWithProof + if (q.mode_ & 1) { + info.type = QueryInfo::t_seqno; + info.value = block_id.seqno; + } else if (q.mode_ == 2) { + info.type = QueryInfo::t_lt; + info.value = q.lt_; + } else if (q.mode_ == 4) { + info.type = QueryInfo::t_utime; + info.value = q.utime_; + } + }, + [&](const lite_api::liteServer_listBlockTransactions& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_listBlockTransactionsExt& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getConfigParams& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getConfigAll& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getBlockProof& q) { + info.shard_id = ShardIdFull{masterchainId}; + BlockIdExt from = create_block_id(q.known_block_); + BlockIdExt to = create_block_id(q.target_block_); + // See LiteQuery::perform_getBlockProof + if ((q.mode_ & 1) && (q.mode_ & 0x1000)) { + info.type = QueryInfo::t_seqno; + info.value = std::max(from.seqno(), to.seqno()); + } else { + info.type = QueryInfo::t_simple; + } + }, + [&](const lite_api::liteServer_getValidatorStats& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_runSmcMethod& q) { + BlockIdExt block_id = create_block_id(q.id_); + AccountIdPrefixFull acc_id_prefix = extract_addr_prefix(q.account_->workchain_, q.account_->id_); + info.shard_id = acc_id_prefix.as_leaf_shard(); + // See LiteQuery::perform_getAccountState + if (block_id.id.workchain != masterchainId) { + info.type = QueryInfo::t_seqno; + info.value = block_id.seqno(); + } else if (block_id.id.seqno != ~0U) { + info.type = QueryInfo::t_mc_seqno; + info.value = block_id.seqno(); + } else { + info.type = QueryInfo::t_simple; + } + }, + [&](const lite_api::liteServer_getLibraries& q) { /* t_simple */ }, + [&](const lite_api::liteServer_getLibrariesWithProof& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getShardBlockProof& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_nonfinal_getCandidate& q) { /* t_simple */ }, + [&](const lite_api::liteServer_nonfinal_getValidatorGroups& q) { /* t_simple */ }, + [&](const lite_api::liteServer_getOutMsgQueueSizes& q) { + // This query is expected to be removed, as it is not fully compatible with separated liteservers + /* t_simple */ + }, + [&](const lite_api::liteServer_getBlockOutMsgQueueSize& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getDispatchQueueInfo& q) { from_block_id(q.id_); }, + [&](const lite_api::liteServer_getDispatchQueueMessages& q) { from_block_id(q.id_); }, + [&](const auto&) { /* t_simple */ })); + if (info.shard_id.workchain == masterchainId) { + info.shard_id.shard = shardIdAll; + } + if (!info.shard_id.is_valid_ext()) { + info.shard_id = ShardIdFull{masterchainId}; + info.type = QueryInfo::t_simple; + info.value = 0; + } + return info; +} + +bool LiteServerConfig::accepts_query(const QueryInfo& query_info) const { + if (is_full) { + return true; + } + for (const Slice& s : slices) { + if (s.accepts_query(query_info)) { + return true; + } + } + return false; +} + +bool LiteServerConfig::Slice::accepts_query(const QueryInfo& query_info) const { + if (unlimited) { + for (const ShardInfo& shard : shards_from) { + if (shard_intersects(shard.shard_id, query_info.shard_id)) { + return true; + } + } + return false; + } + if (!shards_from.empty()) { + bool from_ok = false; + DCHECK(shards_from[0].shard_id.is_masterchain()); + for (const ShardInfo& shard : shards_from) { + if (shard_intersects(shard.shard_id, query_info.shard_id)) { + switch (query_info.type) { + case QueryInfo::t_simple: + from_ok = true; + break; + case QueryInfo::t_seqno: + from_ok = shard.seqno <= query_info.value; + break; + case QueryInfo::t_utime: + from_ok = shard.utime <= query_info.value; + break; + case QueryInfo::t_lt: + from_ok = shard.lt <= query_info.value; + break; + case QueryInfo::t_mc_seqno: + from_ok = shards_from[0].seqno <= query_info.value; + break; + } + if (from_ok) { + break; + } + } + } + if (!from_ok) { + return false; + } + } + if (!shards_to.empty()) { + bool to_ok = false; + DCHECK(shards_to[0].shard_id.is_masterchain()); + for (const ShardInfo& shard : shards_to) { + if (shard_intersects(shard.shard_id, query_info.shard_id)) { + switch (query_info.type) { + case QueryInfo::t_simple: + break; + case QueryInfo::t_seqno: + to_ok = shard.seqno >= query_info.value; + break; + case QueryInfo::t_utime: + to_ok = shard.utime >= query_info.value; + break; + case QueryInfo::t_lt: + to_ok = shard.lt >= query_info.value; + break; + case QueryInfo::t_mc_seqno: + to_ok = shards_from[0].seqno >= query_info.value; + break; + } + if (to_ok) { + break; + } + } + } + if (!to_ok) { + return false; + } + } + return true; +} + +td::Result> LiteServerConfig::parse_global_config( + const ton_api::liteclient_config_global& config) { + std::vector servers; + for (const auto& f : config.liteservers_) { + LiteServerConfig server; + TRY_STATUS(server.addr.init_host_port(td::IPAddress::ipv4_to_str(f->ip_), f->port_)); + server.adnl_id = adnl::AdnlNodeIdFull{PublicKey{f->id_}}; + server.is_full = true; + servers.push_back(std::move(server)); + } + for (const auto& f : config.liteservers_v2_) { + LiteServerConfig server; + TRY_STATUS(server.addr.init_host_port(td::IPAddress::ipv4_to_str(f->ip_), f->port_)); + server.adnl_id = adnl::AdnlNodeIdFull{PublicKey{f->id_}}; + server.is_full = false; + for (const auto& slice_obj : f->slices_) { + Slice slice; + td::Status S = td::Status::OK(); + downcast_call(*slice_obj, + td::overloaded( + [&](const ton_api::liteserver_descV2_sliceSimple& s) { + slice.unlimited = true; + slice.shards_from.push_back({ShardIdFull{masterchainId}, 0, 0, 0}); + for (const auto& shard_obj : s.shards_) { + ShardIdFull shard_id = create_shard_id(shard_obj); + if (!shard_id.is_valid_ext()) { + S = td::Status::Error(PSTRING() << "invalid shard id " << shard_id.to_str()); + break; + } + if (!shard_id.is_masterchain()) { + slice.shards_from.push_back({shard_id, 0, 0, 0}); + } + } + }, + [&](const ton_api::liteserver_descV2_sliceTimed& s) { + auto parse_shards = + [](const std::vector>& shard_objs, + std::vector& shards) -> td::Status { + if (shard_objs.empty()) { + return td::Status::OK(); + } + size_t i = 0; + int mc_idx = -1; + for (const auto& shard_obj : shard_objs) { + ShardIdFull shard_id = create_shard_id(shard_obj->shard_id_); + if (!shard_id.is_valid_ext()) { + return td::Status::Error(PSTRING() << "invalid shard id " << shard_id.to_str()); + } + if (shard_id.is_masterchain()) { + shard_id = ShardIdFull{masterchainId}; + if (mc_idx != -1) { + return td::Status::Error("duplicate masterchain shard in sliceTimed"); + } + mc_idx = (int)i; + } + shards.push_back({shard_id, (BlockSeqno)shard_obj->seqno_, (UnixTime)shard_obj->utime_, + (LogicalTime)shard_obj->lt_}); + ++i; + } + if (mc_idx == -1) { + return td::Status::Error("no masterchain shard in sliceTimed"); + } + std::swap(shards[0], shards[mc_idx]); + return td::Status::OK(); + }; + S = parse_shards(s.shards_from_, slice.shards_from); + if (S.is_ok()) { + S = parse_shards(s.shards_to_, slice.shards_to); + } + if (S.is_ok() && slice.shards_from.empty() && slice.shards_to.empty()) { + S = td::Status::Error("shards_from and shards_to are both empty"); + } + })); + TRY_STATUS(std::move(S)); + server.slices.push_back(slice); + } + + servers.push_back(std::move(server)); + } + return servers; +} + +} // namespace liteclient \ No newline at end of file diff --git a/lite-client/query-utils.hpp b/lite-client/query-utils.hpp new file mode 100644 index 000000000..28500e266 --- /dev/null +++ b/lite-client/query-utils.hpp @@ -0,0 +1,89 @@ +/* + 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 "ton/ton-types.h" +#include "auto/tl/lite_api.h" +#include "td/utils/port/IPAddress.h" +#include "adnl/adnl-node-id.hpp" + +namespace liteclient { + +struct QueryInfo { + enum Type { t_simple, t_seqno, t_utime, t_lt, t_mc_seqno }; + int query_id = 0; + ton::ShardIdFull shard_id{ton::masterchainId}; + Type type = t_simple; + td::uint64 value = 0; + /* Query types and examples: + * t_simple - query to the recent blocks in a shard, or general info. value = 0. + * getTime, getMasterchainInfo (shard_id = masterchain) + * sendMessage + * getAccountState, runSmcMethod - when no block is given + * t_seqno - query to block with seqno in a shard. value = seqno. + * lookupBlock by seqno + * getBlock, getBlockHeader + * getAccountState, runSmcMethod - when shard block is given + * t_utime - query to a block with given unixtime in a shard. value = utime. + * lookupBlock by utime + * t_lt - query to a block with given lt in a shard. value = lt. + * lookupBlock by lt + * getTransactions + * t_mc_seqno - query to a block in a shard, masterchain seqno is given. value = mc_seqno. + * getAccountState, runSmcMethod - when mc block is given + */ + + std::string to_str() const; +}; + +QueryInfo get_query_info(td::Slice data); +QueryInfo get_query_info(const ton::lite_api::Function& f); + +struct LiteServerConfig { + private: + struct ShardInfo { + ton::ShardIdFull shard_id; + ton::BlockSeqno seqno; + ton::UnixTime utime; + ton::LogicalTime lt; + }; + + struct Slice { + std::vector shards_from, shards_to; + bool unlimited = false; + + bool accepts_query(const QueryInfo& query_info) const; + }; + + bool is_full = false; + std::vector slices; + + public: + ton::adnl::AdnlNodeIdFull adnl_id; + td::IPAddress addr; + + LiteServerConfig() = default; + LiteServerConfig(ton::adnl::AdnlNodeIdFull adnl_id, td::IPAddress addr) + : is_full(true), adnl_id(adnl_id), addr(addr) { + } + + bool accepts_query(const QueryInfo& query_info) const; + + static td::Result> parse_global_config( + const ton::ton_api::liteclient_config_global& config); +}; + +} // namespace liteclient diff --git a/test/test-ton-collator.cpp b/test/test-ton-collator.cpp index 78e0e6039..118248519 100644 --- a/test/test-ton-collator.cpp +++ b/test/test-ton-collator.cpp @@ -323,9 +323,8 @@ class TestNode : public td::actor::Actor { td::actor::send_closure(id_, &ton::validator::ValidatorManager::sync_complete, td::PromiseCreator::lambda([](td::Unit) {})); } - void add_shard(ton::ShardIdFull) override { - } - void del_shard(ton::ShardIdFull) override { + void on_new_masterchain_block(td::Ref state, + std::set shards_to_monitor) override { } void send_ihr_message(ton::AccountIdPrefixFull dst, td::BufferSlice data) override { } @@ -371,9 +370,8 @@ class TestNode : public td::actor::Actor { void get_next_key_blocks(ton::BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) override { } - void download_archive(ton::BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - - td::Promise promise) override { + void download_archive(ton::BlockSeqno masterchain_seqno, ton::ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override { } void new_key_block(ton::validator::BlockHandle handle) override { diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index f7bbfd868..16f48345c 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -447,13 +447,15 @@ tonNode.dataFull id:tonNode.blockIdExt proof:bytes block:bytes is_link:Bool = to tonNode.dataFullCompressed id:tonNode.blockIdExt flags:# compressed:bytes is_link:Bool = tonNode.DataFull; tonNode.dataFullEmpty = tonNode.DataFull; -tonNode.capabilities version:int capabilities:long = tonNode.Capabilities; +tonNode.capabilities#f5bf60c0 version_major:int version_minor:int flags:# = tonNode.Capabilities; tonNode.success = tonNode.Success; tonNode.archiveNotFound = tonNode.ArchiveInfo; tonNode.archiveInfo id:long = tonNode.ArchiveInfo; +tonNode.forgetPeer = tonNode.ForgetPeer; + ---functions--- tonNode.getNextBlockDescription prev_block:tonNode.blockIdExt = tonNode.BlockDescription; @@ -479,6 +481,7 @@ tonNode.downloadKeyBlockProof block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadBlockProofLink block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadKeyBlockProofLink block:tonNode.blockIdExt = tonNode.Data; tonNode.getArchiveInfo masterchain_seqno:int = tonNode.ArchiveInfo; +tonNode.getShardArchiveInfo masterchain_seqno:int shard_prefix:tonNode.shardId = tonNode.ArchiveInfo; tonNode.getArchiveSlice archive_id:long offset:long max_size:int = tonNode.Data; tonNode.getCapabilities = tonNode.Capabilities; @@ -545,6 +548,9 @@ db.state.shardClient block:tonNode.blockIdExt = db.state.ShardClient; db.state.asyncSerializer block:tonNode.blockIdExt last:tonNode.blockIdExt last_ts:int = db.state.AsyncSerializer; db.state.hardforks blocks:(vector tonNode.blockIdExt) = db.state.Hardforks; db.state.dbVersion version:int = db.state.DbVersion; +db.state.persistentStateDescriptionShards shard_blocks:(vector tonNode.blockIdExt) = db.state.PersistentStateDescriptionShards; +db.state.persistentStateDescriptionHeader masterchain_id:tonNode.blockIdExt start_time:int end_time:int = db.state.PersistentStateDescriptionHeader; +db.state.persistentStateDescriptionsList list:(vector db.state.persistentStateDescriptionHeader) = db.state.PersistentStateDescriptionsList; db.state.key.destroyedSessions = db.state.Key; db.state.key.initBlockId = db.state.Key; @@ -553,6 +559,8 @@ db.state.key.shardClient = db.state.Key; db.state.key.asyncSerializer = db.state.Key; db.state.key.hardforks = db.state.Key; db.state.key.dbVersion = db.state.Key; +db.state.key.persistentStateDescriptionShards masterchain_seqno:int = db.state.Key; +db.state.key.persistentStateDescriptionsList = db.state.Key; db.lt.el.key workchain:int shard:long idx:int = db.lt.Key; db.lt.desc.key workchain:int shard:long = db.lt.Key; @@ -608,8 +616,13 @@ dummyworkchain0.config.global zero_state_hash:int256 = dummyworkchain0.config.Gl validator.config.global zero_state:tonNode.blockIdExt init_block:tonNode.blockIdExt hardforks:(vector tonNode.blockIdExt) = validator.config.Global; config.global adnl:adnl.config.global dht:dht.config.Global validator:validator.config.global = config.Global; +liteserver.descV2.sliceSimple shards:(vector tonNode.shardId) = liteserver.descV2.Slice; +liteserver.descV2.shardInfo shard_id:tonNode.shardId seqno:int utime:int lt:long = liteserver.descV2.ShardInfo; +liteserver.descV2.sliceTimed shards_from:(vector liteserver.descV2.shardInfo) shards_to:(vector liteserver.descV2.shardInfo) = liteserver.descV2.Slice; + liteserver.desc id:PublicKey ip:int port:int = liteserver.Desc; -liteclient.config.global liteservers:(vector liteserver.desc) validator:validator.config.global = liteclient.config.Global; +liteserver.descV2 id:PublicKey ip:int port:int slices:(vector liteserver.descV2.Slice) = liteserver.DescV2; +liteclient.config.global liteservers:(vector liteserver.desc) liteservers_v2:(vector liteserver.descV2) validator:validator.config.global = liteclient.config.Global; engine.adnl id:int256 category:int = engine.Adnl; engine.addr ip:int port:int categories:(vector int) priority_categories:(vector int) = engine.Addr; @@ -636,10 +649,12 @@ engine.validator.config out_port:int addrs:(vector engine.Addr) adnl:(vector eng fullnodeconfig:engine.validator.fullNodeConfig extraconfig:engine.validator.extraConfig liteservers:(vector engine.liteServer) control:(vector engine.controlInterface) + shards_to_monitor:(vector tonNode.shardId) gc:engine.gc = engine.validator.Config; engine.validator.customOverlayNode adnl_id:int256 msg_sender:Bool msg_sender_priority:int block_sender:Bool = engine.validator.CustomOverlayNode; -engine.validator.customOverlay name:string nodes:(vector engine.validator.customOverlayNode) = engine.validator.CustomOverlay; +engine.validator.customOverlay name:string nodes:(vector engine.validator.customOverlayNode) sender_shards:(vector tonNode.shardId) + = engine.validator.CustomOverlay; engine.validator.customOverlaysConfig overlays:(vector engine.validator.customOverlay) = engine.validator.CustomOverlaysConfig; engine.validator.collatorOptions @@ -699,6 +714,11 @@ engine.validator.overlayStats overlay_id:int256 overlay_id_full:PublicKey adnl_i extra:string = engine.validator.OverlayStats; engine.validator.overlaysStats overlays:(vector engine.validator.overlayStats) = engine.validator.OverlaysStats; +engine.validator.shardOverlayStats.neighbour id:string verison_major:int version_minor:int flags:# + roundtrip:double unreliability:double = engine.validator.shardOverlayStats.Neighbour; +engine.validator.shardOverlayStats shard:string active:Bool + neighbours:(vector engine.validator.shardOverlayStats.neighbour) = engine.validator.ShardOverlayStats; + engine.validator.onePerfTimerStat time:int min:double avg:double max:double = engine.validator.OnePerfTimerStat; engine.validator.perfTimerStatsByName name:string stats:(vector engine.validator.OnePerfTimerStat) = engine.validator.PerfTimerStatsByName; engine.validator.perfTimerStats stats:(vector engine.validator.PerfTimerStatsByName) = engine.validator.PerfTimerStats; @@ -771,6 +791,9 @@ engine.validator.getCollatorOptionsJson = engine.validator.JsonConfig; engine.validator.getAdnlStats all:Bool = adnl.Stats; engine.validator.getActorTextStats = engine.validator.TextStats; +engine.validator.addShard shard:tonNode.shardId = engine.validator.Success; +engine.validator.delShard shard:tonNode.shardId = engine.validator.Success; + ---types--- storage.pong = storage.Pong; @@ -985,3 +1008,6 @@ storage.daemon.withdraw contract:string = storage.daemon.Success; storage.daemon.sendCoins address:string amount:string message:string = storage.daemon.Success; storage.daemon.closeStorageContract address:string = storage.daemon.Success; storage.daemon.removeStorageProvider = storage.daemon.Success; + +---types--- +proxyLiteserver.config port:int id:PublicKey = proxyLiteserver.Config; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 0cd394bd5..1689260bc 100644 Binary files a/tl/generate/scheme/ton_api.tlo and b/tl/generate/scheme/ton_api.tlo differ diff --git a/ton/ton-types.h b/ton/ton-types.h index efdb795d1..cd9700814 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -408,6 +408,9 @@ struct Ed25519_PublicKey { bool operator==(const Ed25519_PublicKey& other) const { return _pubkey == other._pubkey; } + bool operator!=(const Ed25519_PublicKey& other) const { + return _pubkey != other._pubkey; + } bool clear() { _pubkey.set_zero(); return true; @@ -490,4 +493,14 @@ struct ValidatorSessionConfig { static const td::uint32 BLOCK_HASH_COVERS_DATA_FROM_VERSION = 2; }; +struct PersistentStateDescription : public td::CntObject { + BlockIdExt masterchain_id; + std::vector shard_blocks; + UnixTime start_time, end_time; + + virtual CntObject* make_copy() const { + return new PersistentStateDescription(*this); + } +}; + } // namespace ton diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index 67d18e019..0855012cc 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -10,7 +10,6 @@ set(TONLIB_SOURCE tonlib/Client.cpp tonlib/Config.cpp tonlib/ExtClient.cpp - tonlib/ExtClientLazy.cpp tonlib/ExtClientOutbound.cpp tonlib/KeyStorage.cpp tonlib/KeyValue.cpp @@ -25,7 +24,6 @@ set(TONLIB_SOURCE tonlib/Client.h tonlib/Config.h tonlib/ExtClient.h - tonlib/ExtClientLazy.h tonlib/ExtClientOutbound.h tonlib/KeyStorage.h tonlib/KeyValue.h diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index b7423853c..47d5d6a26 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -659,11 +659,12 @@ TEST(Tonlib, ConfigCache) { ], "validator": { "@type": "validator.config.global", - "zero_state": { + "init_block": { "workchain": -1, "shard": -9223372036854775808, "seqno": 0, "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + "root_hash": "ZXSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", } } })abc"; diff --git a/tonlib/tonlib/Config.cpp b/tonlib/tonlib/Config.cpp index 3cd9d50ea..063b34bbd 100644 --- a/tonlib/tonlib/Config.cpp +++ b/tonlib/tonlib/Config.cpp @@ -19,6 +19,8 @@ #include "Config.h" #include "adnl/adnl-node-id.hpp" #include "td/utils/JsonBuilder.h" +#include "auto/tl/ton_api_json.h" +#include "ton/ton-tl.hpp" namespace tonlib { td::Result parse_block_id_ext(td::JsonObject &obj) { @@ -63,75 +65,26 @@ td::Result parse_block_id_ext(td::JsonObject &obj) { td::Result Config::parse(std::string str) { TRY_RESULT(json, td::json_decode(str)); if (json.type() != td::JsonValue::Type::Object) { - return td::Status::Error("Invalid config (1)"); + return td::Status::Error("Invalid config: json is not an object"); } - //TRY_RESULT(main_type, td::get_json_object_string_field(json.get_object(), "@type", false)); - //if (main_type != "config.global") { - //return td::Status::Error("Invalid config (3)"); - //} - TRY_RESULT(lite_clients_obj, - td::get_json_object_field(json.get_object(), "liteservers", td::JsonValue::Type::Array, false)); - auto &lite_clients = lite_clients_obj.get_array(); - Config res; - for (auto &value : lite_clients) { - if (value.type() != td::JsonValue::Type::Object) { - return td::Status::Error("Invalid config (2)"); - } - auto &object = value.get_object(); - //TRY_RESULT(value_type, td::get_json_object_string_field(object, "@type", false)); - //if (value_type != "liteclient.config.global") { - //return td::Status::Error("Invalid config (4)"); - //} - - TRY_RESULT(ip, td::get_json_object_long_field(object, "ip", false)); - TRY_RESULT(port, td::get_json_object_int_field(object, "port", false)); - Config::LiteClient client; - TRY_STATUS(client.address.init_host_port(td::IPAddress::ipv4_to_str(static_cast(ip)), port)); - - TRY_RESULT(id_obj, td::get_json_object_field(object, "id", td::JsonValue::Type::Object, false)); - auto &id = id_obj.get_object(); - TRY_RESULT(id_type, td::get_json_object_string_field(id, "@type", false)); - if (id_type != "pub.ed25519") { - return td::Status::Error("Invalid config (5)"); - } - TRY_RESULT(key_base64, td::get_json_object_string_field(id, "key", false)); - TRY_RESULT(key, td::base64_decode(key_base64)); - if (key.size() != 32) { - return td::Status::Error("Invalid config (6)"); - } + ton::ton_api::liteclient_config_global conf; + TRY_STATUS(ton::ton_api::from_json(conf, json.get_object())); + TRY_RESULT_ASSIGN(res.lite_servers, liteclient::LiteServerConfig::parse_global_config(conf)); - client.adnl_id = ton::adnl::AdnlNodeIdFull(ton::pubkeys::Ed25519(td::Bits256(td::Slice(key).ubegin()))); - res.lite_clients.push_back(std::move(client)); + if (!conf.validator_) { + return td::Status::Error("Invalid config: no 'validator' section"); } - - TRY_RESULT(validator_obj, - td::get_json_object_field(json.get_object(), "validator", td::JsonValue::Type::Object, false)); - auto &validator = validator_obj.get_object(); - TRY_RESULT(validator_type, td::get_json_object_string_field(validator, "@type", false)); - if (validator_type != "validator.config.global") { - return td::Status::Error("Invalid config (7)"); + if (!conf.validator_->zero_state_) { + return td::Status::Error("Invalid config: no zerostate"); } - TRY_RESULT(zero_state_obj, td::get_json_object_field(validator, "zero_state", td::JsonValue::Type::Object, false)); - TRY_RESULT(zero_state_id, parse_block_id_ext(zero_state_obj.get_object())); - res.zero_state_id = zero_state_id; - auto r_init_block_obj = td::get_json_object_field(validator, "init_block", td::JsonValue::Type::Object, false); - if (r_init_block_obj.is_ok()) { - TRY_RESULT(init_block_id, parse_block_id_ext(r_init_block_obj.move_as_ok().get_object())); - res.init_block_id = init_block_id; + res.zero_state_id = ton::create_block_id(conf.validator_->zero_state_); + if (conf.validator_->init_block_) { + res.init_block_id = ton::create_block_id(conf.validator_->init_block_); } - auto r_hardforks = td::get_json_object_field(validator, "hardforks", td::JsonValue::Type::Array, false); - if (r_hardforks.is_ok()) { - auto hardforks_obj = r_hardforks.move_as_ok(); - auto &hardforks = hardforks_obj.get_array(); - for (auto &fork : hardforks) { - if (fork.type() != td::JsonValue::Type::Object) { - return td::Status::Error("Invalid config (8)"); - } - TRY_RESULT(fork_block, parse_block_id_ext(fork.get_object())); - res.hardforks.push_back(std::move(fork_block)); - } + for (auto &fork : conf.validator_->hardforks_) { + res.hardforks.push_back(ton::create_block_id(fork)); } for (auto hardfork : res.hardforks) { diff --git a/tonlib/tonlib/Config.h b/tonlib/tonlib/Config.h index 3902c3419..28f23881b 100644 --- a/tonlib/tonlib/Config.h +++ b/tonlib/tonlib/Config.h @@ -20,17 +20,14 @@ #include "adnl/adnl-node-id.hpp" #include "td/utils/port/IPAddress.h" #include "ton/ton-types.h" +#include "lite-client/ext-client.h" namespace tonlib { struct Config { - struct LiteClient { - ton::adnl::AdnlNodeIdFull adnl_id; - td::IPAddress address; - }; ton::BlockIdExt zero_state_id; ton::BlockIdExt init_block_id; std::vector hardforks; - std::vector lite_clients; + std::vector lite_servers; std::string name; static td::Result parse(std::string str); }; diff --git a/tonlib/tonlib/ExtClient.cpp b/tonlib/tonlib/ExtClient.cpp index 30a29b59c..b66ca25c1 100644 --- a/tonlib/tonlib/ExtClient.cpp +++ b/tonlib/tonlib/ExtClient.cpp @@ -65,7 +65,7 @@ void ExtClient::send_raw_query(td::BufferSlice query, td::Promise adnl_ext_client_; + td::actor::ActorId adnl_ext_client_; td::actor::ActorId last_block_actor_; td::actor::ActorId last_config_actor_; }; @@ -97,7 +97,7 @@ class ExtClient { void force_change_liteserver() { if (!client_.adnl_ext_client_.empty()) { - td::actor::send_closure(client_.adnl_ext_client_, &ExtClientLazy::force_change_liteserver); + td::actor::send_closure(client_.adnl_ext_client_, &liteclient::ExtClient::reset_servers); } } diff --git a/tonlib/tonlib/ExtClientLazy.cpp b/tonlib/tonlib/ExtClientLazy.cpp deleted file mode 100644 index 335a0ff96..000000000 --- a/tonlib/tonlib/ExtClientLazy.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - 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-2020 Telegram Systems LLP -*/ -#include "ExtClientLazy.h" -#include "TonlibError.h" -#include "td/utils/Random.h" -namespace tonlib { - -class ExtClientLazyImp : public ExtClientLazy { - public: - ExtClientLazyImp(std::vector> servers, - td::unique_ptr callback) - : servers_(std::move(servers)), callback_(std::move(callback)) { - CHECK(!servers_.empty()); - } - - void start_up() override { - td::Random::Fast rnd; - td::random_shuffle(td::as_mutable_span(servers_), rnd); - } - - void check_ready(td::Promise promise) override { - before_query(); - if (client_.empty()) { - return promise.set_error(TonlibError::Cancelled()); - } - send_closure(client_, &ton::adnl::AdnlExtClient::check_ready, std::move(promise)); - } - - void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, - td::Promise promise) override { - before_query(); - if (client_.empty()) { - return promise.set_error(TonlibError::Cancelled()); - } - td::Promise P = [SelfId = actor_id(this), idx = cur_server_idx_, - promise = std::move(promise)](td::Result R) mutable { - if (R.is_error() && - (R.error().code() == ton::ErrorCode::timeout || R.error().code() == ton::ErrorCode::cancelled)) { - td::actor::send_closure(SelfId, &ExtClientLazyImp::set_server_bad, idx, true); - } - promise.set_result(std::move(R)); - }; - send_closure(client_, &ton::adnl::AdnlExtClient::send_query, std::move(name), std::move(data), timeout, - std::move(P)); - } - - void force_change_liteserver() override { - if (servers_.size() == 1) { - return; - } - cur_server_bad_ = cur_server_bad_force_ = true; - } - - private: - void before_query() { - if (is_closing_) { - return; - } - alarm_timestamp() = td::Timestamp::in(MAX_NO_QUERIES_TIMEOUT); - if (cur_server_bad_) { - ++cur_server_idx_; - } else if (!client_.empty()) { - return; - } - class Callback : public ton::adnl::AdnlExtClient::Callback { - public: - explicit Callback(td::actor::ActorShared parent, size_t idx) - : parent_(std::move(parent)), idx_(idx) { - } - void on_ready() override { - td::actor::send_closure(parent_, &ExtClientLazyImp::set_server_bad, idx_, false); - } - void on_stop_ready() override { - td::actor::send_closure(parent_, &ExtClientLazyImp::set_server_bad, idx_, true); - } - - private: - td::actor::ActorShared parent_; - size_t idx_; - }; - ref_cnt_++; - cur_server_bad_ = false; - cur_server_bad_force_ = false; - const auto& s = servers_[cur_server_idx_ % servers_.size()]; - LOG(INFO) << "Connecting to liteserver " << s.second; - client_ = ton::adnl::AdnlExtClient::create( - s.first, s.second, std::make_unique(td::actor::actor_shared(this), cur_server_idx_)); - } - - std::vector> servers_; - size_t cur_server_idx_ = 0; - bool cur_server_bad_ = false; - bool cur_server_bad_force_ = false; - - td::actor::ActorOwn client_; - td::unique_ptr callback_; - static constexpr double MAX_NO_QUERIES_TIMEOUT = 100; - - bool is_closing_{false}; - td::uint32 ref_cnt_{1}; - - void set_server_bad(size_t idx, bool bad) { - if (idx == cur_server_idx_ && servers_.size() > 1 && !cur_server_bad_force_) { - cur_server_bad_ = bad; - } - } - void alarm() override { - client_.reset(); - } - void hangup_shared() override { - ref_cnt_--; - try_stop(); - } - void hangup() override { - is_closing_ = true; - ref_cnt_--; - client_.reset(); - try_stop(); - } - void try_stop() { - if (is_closing_ && ref_cnt_ == 0) { - stop(); - } - } -}; - -td::actor::ActorOwn ExtClientLazy::create(ton::adnl::AdnlNodeIdFull dst, td::IPAddress dst_addr, - td::unique_ptr callback) { - return create({std::make_pair(dst, dst_addr)}, std::move(callback)); -} - -td::actor::ActorOwn ExtClientLazy::create( - std::vector> servers, td::unique_ptr callback) { - return td::actor::create_actor("ExtClientLazy", std::move(servers), std::move(callback)); -} -} // namespace tonlib diff --git a/tonlib/tonlib/ExtClientLazy.h b/tonlib/tonlib/ExtClientLazy.h deleted file mode 100644 index dc4490b3a..000000000 --- a/tonlib/tonlib/ExtClientLazy.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - 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-2020 Telegram Systems LLP -*/ -#pragma once -#include "td/actor/actor.h" - -#include "adnl/adnl-ext-client.h" - -namespace tonlib { -class ExtClientLazy : public ton::adnl::AdnlExtClient { - public: - class Callback { - public: - virtual ~Callback() { - } - }; - - virtual void force_change_liteserver() = 0; - - static td::actor::ActorOwn create(ton::adnl::AdnlNodeIdFull dst, td::IPAddress dst_addr, - td::unique_ptr callback); - static td::actor::ActorOwn create( - std::vector> servers, td::unique_ptr callback); -}; - -} // namespace tonlib diff --git a/tonlib/tonlib/ExtClientOutbound.cpp b/tonlib/tonlib/ExtClientOutbound.cpp index 025ba848e..e5fac8b47 100644 --- a/tonlib/tonlib/ExtClientOutbound.cpp +++ b/tonlib/tonlib/ExtClientOutbound.cpp @@ -20,15 +20,12 @@ #include "ExtClientOutbound.h" #include "TonlibError.h" #include + namespace tonlib { -class ExtClientOutboundImp : public ExtClientOutbound { +class ExtClientOutboundImpl : public ExtClientOutbound { public: - ExtClientOutboundImp(td::unique_ptr callback) : callback_(std::move(callback)) { - } - - void check_ready(td::Promise promise) override { - promise.set_error(td::Status::Error("Not supported")); + ExtClientOutboundImpl(td::unique_ptr callback) : callback_(std::move(callback)) { } void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, @@ -38,9 +35,6 @@ class ExtClientOutboundImp : public ExtClientOutbound { callback_->request(query_id, data.as_slice().str()); } - void force_change_liteserver() override { - } - void on_query_result(td::int64 id, td::Result r_data, td::Promise promise) override { auto it = queries_.find(id); if (it == queries_.end()) { @@ -66,6 +60,6 @@ class ExtClientOutboundImp : public ExtClientOutbound { }; td::actor::ActorOwn ExtClientOutbound::create(td::unique_ptr callback) { - return td::actor::create_actor("ExtClientOutbound", std::move(callback)); + return td::actor::create_actor("ExtClientOutbound", std::move(callback)); } } // namespace tonlib diff --git a/tonlib/tonlib/ExtClientOutbound.h b/tonlib/tonlib/ExtClientOutbound.h index 87b73b9e0..bf52c8c26 100644 --- a/tonlib/tonlib/ExtClientOutbound.h +++ b/tonlib/tonlib/ExtClientOutbound.h @@ -18,11 +18,10 @@ */ #pragma once #include "td/actor/actor.h" - -#include "ExtClientLazy.h" +#include "lite-client/ext-client.h" namespace tonlib { -class ExtClientOutbound : public ExtClientLazy { +class ExtClientOutbound : public liteclient::ExtClient { public: class Callback { public: diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 6a32bf088..b9ff4899c 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -18,7 +18,6 @@ */ #include "TonlibClient.h" -#include "tonlib/ExtClientLazy.h" #include "tonlib/ExtClientOutbound.h" #include "tonlib/LastBlock.h" #include "tonlib/LastConfig.h" @@ -2184,21 +2183,8 @@ void TonlibClient::init_ext_client() { ext_client_outbound_ = client.get(); raw_client_ = std::move(client); } else { - std::vector> servers; - for (const auto& s : config_.lite_clients) { - servers.emplace_back(s.adnl_id, s.address); - } - class Callback : public ExtClientLazy::Callback { - public: - explicit Callback(td::actor::ActorShared<> parent) : parent_(std::move(parent)) { - } - - private: - td::actor::ActorShared<> parent_; - }; ext_client_outbound_ = {}; - ref_cnt_++; - raw_client_ = ExtClientLazy::create(std::move(servers), td::make_unique(td::actor::actor_shared())); + raw_client_ = liteclient::ExtClient::create(config_.lite_servers, nullptr); } } @@ -2860,7 +2846,7 @@ td::Result TonlibClient::validate_config(tonlib_api::o TRY_RESULT_PREFIX(new_config, Config::parse(std::move(config->config_)), TonlibError::InvalidConfig("can't parse config")); - if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { + if (new_config.lite_servers.empty() && !config->use_callbacks_for_network_) { return TonlibError::InvalidConfig("no lite clients"); } td::optional o_master_config; @@ -4613,7 +4599,7 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_getLibraries& request, if (query_context_.block_id) { get_libraries(query_context_.block_id.value(), request.library_list_, std::move(promise)); } else { - client_.with_last_block([this, promise = std::move(promise), library_list = request.library_list_](td::Result r_last_block) mutable { + client_.with_last_block([this, promise = std::move(promise), library_list = request.library_list_](td::Result r_last_block) mutable { if (r_last_block.is_error()) { promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed "))); } else { @@ -4647,7 +4633,7 @@ void TonlibClient::get_libraries(ton::BlockIdExt blkid, std::vector client_.send_query(ton::lite_api::liteServer_getLibrariesWithProof(ton::create_tl_lite_block_id(blkid), 1, std::move(not_cached_hashes)), promise.wrap([self=this, blkid, result_entries = std::move(result_entries), not_cached_hashes] - (td::Result> r_libraries) mutable + (td::Result> r_libraries) mutable -> td::Result> { if (r_libraries.is_error()) { LOG(WARNING) << "cannot obtain found libraries: " << r_libraries.move_as_error().to_string(); @@ -4674,7 +4660,7 @@ void TonlibClient::get_libraries(ton::BlockIdExt blkid, std::vector auto csr = libraries_dict.lookup(hash.bits(), 256); if (csr.is_null()) { LOG(WARNING) << "library " << hash.to_hex() << " not found in config"; - if (std::any_of(libraries->result_.begin(), libraries->result_.end(), + if (std::any_of(libraries->result_.begin(), libraries->result_.end(), [&hash](const auto& lib) { return lib->hash_.bits().equals(hash.cbits(), 256); })) { return TonlibError::Internal("library is included in response but it's not found in proof"); } @@ -4685,7 +4671,7 @@ void TonlibClient::get_libraries(ton::BlockIdExt blkid, std::vector return TonlibError::Internal("cannot unpack LibDescr record"); } - auto lib_it = std::find_if(libraries->result_.begin(), libraries->result_.end(), + auto lib_it = std::find_if(libraries->result_.begin(), libraries->result_.end(), [&hash](const auto& lib) { return lib->hash_.bits().equals(hash.cbits(), 256); }); if (lib_it == libraries->result_.end()) { return TonlibError::Internal("library is found in proof but not in response"); @@ -4703,7 +4689,7 @@ void TonlibClient::get_libraries(ton::BlockIdExt blkid, std::vector if (contents.ok()->get_hash() != libdescr.lib->get_hash()) { return TonlibError::Internal(PSLICE() << "library hash mismatch data " << lib->hash_.to_hex() << " != proof " << libdescr.lib->get_hash().to_hex()); } - + result_entries.push_back(tonlib_api::make_object(lib->hash_, lib->data_.as_slice().str())); self->libraries.set_ref(lib->hash_, contents.move_as_ok()); LOG(DEBUG) << "registered library " << lib->hash_.to_hex(); @@ -5667,14 +5653,14 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_lookupBlock& reques auto blkid = ton::BlockId(request.id_->workchain_, request.id_->shard_, request.id_->seqno_); client_.with_last_block( [self = this, blkid, lite_block = std::move(lite_block), mode = request.mode_, lt = (td::uint64)request.lt_, - utime = (td::uint32)request.utime_, promise = std::move(promise)](td::Result r_last_block) mutable { + utime = (td::uint32)request.utime_, promise = std::move(promise)](td::Result r_last_block) mutable { if (r_last_block.is_error()) { promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed "))); return; } self->client_.send_query(ton::lite_api::liteServer_lookupBlockWithProof(mode, std::move(lite_block), ton::create_tl_lite_block_id(r_last_block.ok().last_block_id), lt, utime), - promise.wrap([blkid, mode, utime, lt, last_block = r_last_block.ok().last_block_id](lite_api_ptr&& result) + promise.wrap([blkid, mode, utime, lt, last_block = r_last_block.ok().last_block_id](lite_api_ptr&& result) -> td::Result> { TRY_STATUS(check_lookup_block_proof(result, mode, blkid, last_block, lt, utime)); return to_tonlib_api(*result->id_); @@ -5706,7 +5692,7 @@ td::Status check_lookup_block_proof(lite_api_ptr lt) { return td::Status::Error("prev header end_lt > lt"); @@ -5831,7 +5817,7 @@ td::Status check_lookup_block_proof(lite_api_ptr after; @@ -5945,7 +5931,7 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& re if (!request.after_) { return td::Status::Error("Missing field `after`"); } - TRY_RESULT_ASSIGN(start_addr, to_bits256(request.after_->account_, "account")); + TRY_RESULT_ASSIGN(start_addr, to_bits256(request.after_->account_, "account")); start_lt = request.after_->lt_; after = ton::lite_api::make_object(start_addr, start_lt); } else { diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 7db443247..28239a8a4 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -33,6 +33,7 @@ #include "td/utils/optional.h" #include "smc-envelope/ManualDns.h" +#include "lite-client/ext-client.h" #include @@ -113,7 +114,7 @@ class TonlibClient : public td::actor::Actor { vm::Dictionary libraries{256}; // network - td::actor::ActorOwn raw_client_; + td::actor::ActorOwn raw_client_; td::actor::ActorId ext_client_outbound_; td::actor::ActorOwn raw_last_block_; td::actor::ActorOwn raw_last_config_; diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 8fddedd40..2c7100f24 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -47,8 +47,6 @@ #include "tonlib/TonlibClient.h" #include "tonlib/TonlibCallback.h" -#include "tonlib/ExtClientLazy.h" - #include "smc-envelope/ManualDns.h" #include "smc-envelope/PaymentChannel.h" @@ -57,6 +55,7 @@ #include "crypto/util/Miner.h" #include "vm/boc.h" #include "vm/cells/CellBuilder.h" +#include "lite-client/ext-client.h" #include #include @@ -174,7 +173,7 @@ class TonlibCli : public td::actor::Actor { std::map>> query_handlers_; - td::actor::ActorOwn raw_client_; + td::actor::ActorOwn raw_client_; bool is_closing_{false}; td::uint32 ref_cnt_{1}; @@ -223,11 +222,7 @@ class TonlibCli : public td::actor::Actor { if (options_.use_callbacks_for_network) { auto config = tonlib::Config::parse(options_.config).move_as_ok(); - auto lite_clients_size = config.lite_clients.size(); - CHECK(lite_clients_size != 0); - auto lite_client_id = td::Random::fast(0, td::narrow_cast(lite_clients_size) - 1); - auto& lite_client = config.lite_clients[lite_client_id]; - class Callback : public tonlib::ExtClientLazy::Callback { + class Callback : public liteclient::ExtClient::Callback { public: explicit Callback(td::actor::ActorShared<> parent) : parent_(std::move(parent)) { } @@ -236,14 +231,14 @@ class TonlibCli : public td::actor::Actor { td::actor::ActorShared<> parent_; }; ref_cnt_++; - raw_client_ = tonlib::ExtClientLazy::create(lite_client.adnl_id, lite_client.address, + raw_client_ = liteclient::ExtClient::create(config.lite_servers, td::make_unique(td::actor::actor_shared())); } auto config = !options_.config.empty() - ? make_object(options_.config, options_.name, - options_.use_callbacks_for_network, options_.ignore_cache) - : nullptr; + ? make_object(options_.config, options_.name, + options_.use_callbacks_for_network, options_.ignore_cache) + : nullptr; tonlib_api::object_ptr ks_type; if (options_.in_memory) { @@ -1545,7 +1540,7 @@ class TonlibCli : public td::actor::Actor { auto update = tonlib_api::move_object_as(std::move(result)); CHECK(!raw_client_.empty()); snd_bytes_ += update->data_.size(); - send_closure(raw_client_, &ton::adnl::AdnlExtClient::send_query, "query", td::BufferSlice(update->data_), + send_closure(raw_client_, &liteclient::ExtClient::send_query, "query", td::BufferSlice(update->data_), td::Timestamp::in(5), [actor_id = actor_id(this), id = update->id_](td::Result res) { send_closure(actor_id, &TonlibCli::on_adnl_result, id, std::move(res)); diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index ec6eb0137..d62fc2167 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -21,4 +21,7 @@ add_executable(opcode-timing opcode-timing.cpp ) target_link_libraries(opcode-timing ton_crypto) target_include_directories(pack-viewer PUBLIC $/..) +add_executable(proxy-liteserver proxy-liteserver.cpp) +target_link_libraries(proxy-liteserver tdutils tdactor adnl dht tl_api ton_crypto git lite-client-common) + install(TARGETS generate-random-id RUNTIME DESTINATION bin) diff --git a/utils/proxy-liteserver.cpp b/utils/proxy-liteserver.cpp new file mode 100644 index 000000000..a9baa7595 --- /dev/null +++ b/utils/proxy-liteserver.cpp @@ -0,0 +1,473 @@ +/* + This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + TON Blockchain is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . + + 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. +*/ +#include "td/utils/filesystem.h" +#include "td/actor/actor.h" +#include "td/actor/MultiPromise.h" +#include "td/utils/OptionParser.h" +#include "td/utils/port/path.h" +#include "td/utils/port/signals.h" +#include "td/utils/port/IPAddress.h" +#include "td/utils/Random.h" +#include "td/utils/FileLog.h" +#include "git.h" +#include "auto/tl/ton_api.h" +#include "auto/tl/lite_api.h" +#include "tl-utils/lite-utils.hpp" +#include "auto/tl/ton_api_json.h" +#include "adnl/adnl.h" +#include "lite-client/ext-client.h" + +#if TD_DARWIN || TD_LINUX +#include +#endif +#include "td/utils/overloaded.h" + +#include +#include +#include +#include "td/utils/tl_storers.h" + +using namespace ton; + +class ProxyLiteserver : public td::actor::Actor { + public: + ProxyLiteserver(std::string global_config, std::string db_root, td::uint16 port, PublicKeyHash public_key_hash) + : global_config_(std::move(global_config)) + , db_root_(std::move(db_root)) + , port_(port) + , public_key_hash_(public_key_hash) { + } + + void start_up() override { + LOG_CHECK(!db_root_.empty()) << "db root is not set"; + td::mkdir(db_root_).ensure(); + db_root_ = td::realpath(db_root_).move_as_ok(); + keyring_ = keyring::Keyring::create(db_root_ + "/keyring"); + + if (public_key_hash_.is_zero()) { + id_ = {}; + run(); + } else { + td::actor::send_closure(keyring_, &keyring::Keyring::get_public_key, public_key_hash_, + [SelfId = actor_id(this)](td::Result R) mutable { + if (R.is_error()) { + LOG(FATAL) << "Failed to load public key: " << R.move_as_error(); + } + td::actor::send_closure(SelfId, &ProxyLiteserver::got_public_key, R.move_as_ok()); + }); + } + } + + void got_public_key(PublicKey pub) { + id_ = adnl::AdnlNodeIdFull{pub}; + run(); + } + + void run() { + td::Status S = prepare_local_config(); + if (S.is_error()) { + LOG(FATAL) << "Local config error: " << S; + } + + S = parse_global_config(); + if (S.is_error()) { + LOG(FATAL) << S; + } + + run_clients(); + create_ext_server(); + } + + td::Status prepare_local_config() { + auto r_conf_data = td::read_file(config_file()); + if (r_conf_data.is_ok()) { + auto conf_data = r_conf_data.move_as_ok(); + TRY_RESULT_PREFIX(conf_json, td::json_decode(conf_data.as_slice()), "failed to parse json: "); + TRY_STATUS_PREFIX(ton_api::from_json(*config_, conf_json.get_object()), "json does not fit TL scheme: "); + TRY_RESULT_PREFIX(cfg_port, td::narrow_cast_safe(config_->port_), "invalid port: "); + TRY_RESULT_PREFIX(cfg_id, adnl::AdnlNodeIdFull::create(config_->id_), "invalid id: "); + bool rewrite_config = false; + if (port_ == 0) { + port_ = cfg_port; + } else { + rewrite_config |= (port_ != cfg_port); + } + if (id_.empty()) { + id_ = std::move(cfg_id); + } else { + rewrite_config |= (id_ != cfg_id); + } + if (!rewrite_config) { + return td::Status::OK(); + } + } else { + LOG(WARNING) << "First launch, creating local config"; + } + if (port_ == 0) { + return td::Status::Error("port is not set"); + } + config_->port_ = port_; + if (id_.empty()) { + auto pk = PrivateKey{privkeys::Ed25519::random()}; + id_ = adnl::AdnlNodeIdFull{pk.compute_public_key()}; + td::actor::send_closure(keyring_, &keyring::Keyring::add_key, std::move(pk), false, [](td::Result R) { + if (R.is_error()) { + LOG(FATAL) << "Failed to store private key"; + } + }); + } + config_->id_ = id_.tl(); + + auto s = td::json_encode(td::ToJson(*config_), true); + TRY_STATUS_PREFIX(td::write_file(config_file(), s), "failed to write file: "); + LOG(WARNING) << "Writing config.json"; + return td::Status::OK(); + } + + td::Status parse_global_config() { + TRY_RESULT_PREFIX(global_config_data, td::read_file(global_config_), "Failed to read global config: "); + TRY_RESULT_PREFIX(global_config_json, td::json_decode(global_config_data.as_slice()), + "Failed to parse global config: "); + ton_api::liteclient_config_global gc; + TRY_STATUS_PREFIX(ton_api::from_json(gc, global_config_json.get_object()), "Failed to parse global config: "); + TRY_RESULT_PREFIX(servers, liteclient::LiteServerConfig::parse_global_config(gc), + "Falied to parse liteservers in global config: "); + if (servers.empty()) { + return td::Status::Error("No liteservers in global config"); + } + for (auto& s : servers) { + servers_.emplace_back(); + servers_.back().config = std::move(s); + } + return td::Status::OK(); + } + + void run_clients() { + class Callback : public adnl::AdnlExtClient::Callback { + public: + explicit Callback(td::actor::ActorId id, size_t idx) : id_(std::move(id)), idx_(idx) { + } + void on_ready() override { + td::actor::send_closure(id_, &ProxyLiteserver::on_client_status, idx_, true); + } + void on_stop_ready() override { + td::actor::send_closure(id_, &ProxyLiteserver::on_client_status, idx_, false); + } + + private: + td::actor::ActorId id_; + size_t idx_; + }; + + for (size_t i = 0; i < servers_.size(); ++i) { + Server& server = servers_[i]; + server.client = adnl::AdnlExtClient::create(server.config.adnl_id, server.config.addr, + std::make_unique(actor_id(this), i)); + server.alive = false; + } + } + + void on_client_status(size_t idx, bool ready) { + Server& server = servers_[idx]; + if (server.alive == ready) { + return; + } + server.alive = ready; + LOG(WARNING) << (ready ? "Connected to" : "Disconnected from") << " server #" << idx << " (" + << server.config.addr.get_ip_str() << ":" << server.config.addr.get_port() << ")"; + } + + void create_ext_server() { + adnl_ = adnl::Adnl::create("", keyring_.get()); + td::actor::send_closure(adnl_, &adnl::Adnl::add_id, id_, adnl::AdnlAddressList{}, (td::uint8)255); + + class AdnlCallback : public adnl::Adnl::Callback { + public: + explicit AdnlCallback(td::actor::ActorId id) : id_(id) { + } + + void receive_message(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data) override { + } + void receive_query(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data, + td::Promise promise) override { + td::actor::send_closure(id_, &ProxyLiteserver::receive_query, std::move(data), std::move(promise)); + } + + private: + td::actor::ActorId id_; + }; + td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, id_.compute_short_id(), + adnl::Adnl::int_to_bytestring(lite_api::liteServer_query::ID), + std::make_unique(actor_id(this))); + td::actor::send_closure(adnl_, &adnl::Adnl::create_ext_server, std::vector{id_.compute_short_id()}, + std::vector{port_}, + [SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ProxyLiteserver::created_ext_server, R.move_as_ok()); + }); + } + + void created_ext_server(td::actor::ActorOwn s) { + ext_server_ = std::move(s); + LOG(WARNING) << "Started proxy liteserver on port " << port_; + alarm(); + } + + td::Result select_server(const liteclient::QueryInfo& query_info) { + size_t best_idx = servers_.size(); + int cnt = 0; + for (size_t i = 0; i < servers_.size(); ++i) { + Server& server = servers_[i]; + if (!server.alive || !server.config.accepts_query(query_info)) { + continue; + } + ++cnt; + if (td::Random::fast(1, cnt) == 1) { + best_idx = i; + } + } + if (best_idx == servers_.size()) { + return td::Status::Error(PSTRING() << "no liteserver for query " << query_info.to_str()); + } + return best_idx; + } + + void receive_query(td::BufferSlice data, td::Promise promise) { + // Like in ValidatorManagerImpl::run_ext_query + auto F = fetch_tl_object(data, true); + if (F.is_ok()) { + data = std::move(F.move_as_ok()->data_); + } else { + auto G = fetch_tl_prefix(data, true); + if (G.is_error()) { + promise.set_error(G.move_as_error()); + return; + } + } + + tl_object_ptr wait_mc_seqno_obj; + auto E = fetch_tl_prefix(data, true); + if (E.is_ok()) { + wait_mc_seqno_obj = E.move_as_ok(); + } + liteclient::QueryInfo query_info = liteclient::get_query_info(data); + ++ls_stats_[query_info.query_id]; + promise = [promise = std::move(promise), query_info, timer = td::Timer(), + wait_mc_seqno = + (wait_mc_seqno_obj ? wait_mc_seqno_obj->seqno_ : 0)](td::Result R) mutable { + if (R.is_ok()) { + LOG(INFO) << "Query " << query_info.to_str() + << (wait_mc_seqno ? PSTRING() << " (wait seqno " << wait_mc_seqno << ")" : "") + << ": OK, time=" << timer.elapsed() << ", response_size=" << R.ok().size(); + promise.set_value(R.move_as_ok()); + return; + } + LOG(INFO) << "Query " << query_info.to_str() + << (wait_mc_seqno ? PSTRING() << " (wait seqno " << wait_mc_seqno << ")" : "") << ": " << R.error(); + promise.set_value(create_serialize_tl_object( + R.error().code(), "Gateway error: " + R.error().message().str())); + }; + + TRY_RESULT_PROMISE(promise, server_idx, select_server(query_info)); + Server& server = servers_[server_idx]; + LOG(INFO) << "Sending query " << query_info.to_str() + << (wait_mc_seqno_obj ? PSTRING() << " (wait seqno " << wait_mc_seqno_obj->seqno_ << ")" : "") + << ", size=" << data.size() << ", to server #" << server_idx << " (" << server.config.addr.get_ip_str() + << ":" << server.config.addr.get_port() << ")"; + + BlockSeqno wait_mc_seqno = wait_mc_seqno_obj ? wait_mc_seqno_obj->seqno_ : 0; + wait_mc_seqno = std::max(wait_mc_seqno, last_known_masterchain_seqno_); + if (server.last_known_masterchain_seqno < wait_mc_seqno) { + int timeout_ms = wait_mc_seqno_obj ? wait_mc_seqno_obj->timeout_ms_ : 8000; + data = serialize_tl_object(create_tl_object(wait_mc_seqno, timeout_ms), + true, std::move(data)); + } + data = create_serialize_tl_object(std::move(data)); + td::actor::send_closure(server.client, &adnl::AdnlExtClient::send_query, "q", std::move(data), + td::Timestamp::in(8.0), + [SelfId = actor_id(this), promise = std::move(promise), server_idx, + wait_mc_seqno](td::Result R) mutable { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ProxyLiteserver::process_query_response, + R.ok().clone(), server_idx, wait_mc_seqno); + } + promise.set_result(std::move(R)); + }); + } + + void process_query_response(td::BufferSlice data, size_t server_idx, BlockSeqno wait_mc_seqno) { + auto F = fetch_tl_object(data, true); + if (F.is_error() || F.ok()->get_id() == lite_api::liteServer_error::ID) { + return; + } + BlockSeqno new_seqno = wait_mc_seqno; + lite_api::downcast_call(*F.ok_ref(), td::overloaded( + [&](lite_api::liteServer_masterchainInfo& f) { + new_seqno = std::max(new_seqno, f.last_->seqno_); + }, + [&](lite_api::liteServer_masterchainInfoExt& f) { + new_seqno = std::max(new_seqno, f.last_->seqno_); + }, + [&](lite_api::liteServer_accountState& f) { + if (f.id_->workchain_ == masterchainId) { + new_seqno = std::max(new_seqno, f.id_->seqno_); + } + }, + [&](auto& obj) {})); + servers_[server_idx].last_known_masterchain_seqno = + std::max(servers_[server_idx].last_known_masterchain_seqno, new_seqno); + if (new_seqno > last_known_masterchain_seqno_) { + last_known_masterchain_seqno_ = new_seqno; + LOG(INFO) << "Last known masterchain seqno = " << new_seqno; + } + } + + void alarm() override { + alarm_timestamp() = td::Timestamp::in(60.0); + if (!ls_stats_.empty()) { + td::StringBuilder sb; + sb << "Liteserver stats (1 minute):"; + td::uint32 total = 0; + for (const auto& p : ls_stats_) { + sb << " " << lite_query_name_by_id(p.first) << ":" << p.second; + total += p.second; + } + sb << " TOTAL:" << total; + LOG(WARNING) << sb.as_cslice(); + ls_stats_.clear(); + } + } + + private: + std::string global_config_; + std::string db_root_; + td::uint16 port_; + PublicKeyHash public_key_hash_; + + tl_object_ptr config_ = create_tl_object(); + adnl::AdnlNodeIdFull id_; + + td::actor::ActorOwn keyring_; + td::actor::ActorOwn adnl_; + td::actor::ActorOwn ext_server_; + + struct Server { + liteclient::LiteServerConfig config; + td::actor::ActorOwn client; + bool alive = false; + BlockSeqno last_known_masterchain_seqno = 0; + }; + std::vector servers_; + + std::map ls_stats_; // lite_api ID -> count, 0 for unknown + + BlockSeqno last_known_masterchain_seqno_ = 0; + tl_object_ptr last_masterchain_info_; + + std::string config_file() const { + return db_root_ + "/config.json"; + } +}; + +int main(int argc, char* argv[]) { + SET_VERBOSITY_LEVEL(verbosity_WARNING); + td::set_default_failure_signal_handler().ensure(); + + td::unique_ptr logger_; + SCOPE_EXIT { + td::log_interface = td::default_log_interface; + }; + + std::string global_config, db_root; + td::uint16 port = 0; + PublicKeyHash public_key_hash = PublicKeyHash::zero(); + td::uint32 threads = 4; + + td::OptionParser p; + p.set_description("Proxy liteserver: distributes incoming queries to servers in global config\n"); + p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) { + int v = VERBOSITY_NAME(FATAL) + (td::to_integer(arg)); + SET_VERBOSITY_LEVEL(v); + }); + p.add_option('V', "version", "show build information", [&]() { + std::cout << "proxy-liteserver build information: [ Commit: " << GitMetadata::CommitSHA1() + << ", Date: " << GitMetadata::CommitDate() << "]\n"; + std::exit(0); + }); + p.add_option('h', "help", "print help", [&]() { + char b[10240]; + td::StringBuilder sb(td::MutableSlice{b, 10000}); + sb << p; + std::cout << sb.as_cslice().c_str(); + std::exit(2); + }); + p.add_checked_option('p', "port", "liteserver port (required only on first launch)", + [&](td::Slice arg) -> td::Status { + TRY_RESULT_ASSIGN(port, td::to_integer_safe(arg)); + return td::Status::OK(); + }); + p.add_checked_option( + 'A', "adnl-id", + "liteserver public key hash in hex (optional). The corresponding private key is required in /keyring/", + [&](td::Slice arg) -> td::Status { + td::Bits256 value; + if (value.from_hex(arg) != 256) { + return td::Status::Error("invalid adnl-id"); + } + public_key_hash = PublicKeyHash{value}; + return td::Status::OK(); + }); + p.add_option('C', "global-config", "global TON configuration file", + [&](td::Slice arg) { global_config = arg.str(); }); + p.add_option('D', "db", "db root", [&](td::Slice arg) { db_root = arg.str(); }); + p.add_option('d', "daemonize", "set SIGHUP", [&]() { + td::set_signal_handler(td::SignalType::HangUp, [](int) { +#if TD_DARWIN || TD_LINUX + close(0); + setsid(); +#endif + }).ensure(); + }); + p.add_option('l', "logname", "log to file", [&](td::Slice fname) { + logger_ = td::FileLog::create(fname.str()).move_as_ok(); + td::log_interface = logger_.get(); + }); + p.add_checked_option('t', "threads", PSTRING() << "number of threads (default=" << 4 << ")", + [&](td::Slice arg) -> td::Status { + TRY_RESULT_ASSIGN(threads, td::to_integer_safe(arg)); + return td::Status::OK(); + }); + + p.run(argc, argv).ensure(); + td::actor::Scheduler scheduler({threads}); + + scheduler.run_in_context([&] { + td::actor::create_actor("proxy-liteserver", global_config, db_root, port, public_key_hash) + .release(); + }); + while (scheduler.run(1)) { + } +} diff --git a/validator-engine-console/validator-engine-console-query.cpp b/validator-engine-console/validator-engine-console-query.cpp index ff4e6e04e..926b79887 100644 --- a/validator-engine-console/validator-engine-console-query.cpp +++ b/validator-engine-console/validator-engine-console-query.cpp @@ -1,4 +1,4 @@ -/* +/* This file is part of TON Blockchain source code. TON Blockchain is free software; you can redistribute it and/or @@ -35,6 +35,7 @@ #include "ton/ton-tl.hpp" #include "td/utils/JsonBuilder.h" #include "auto/tl/ton_api_json.h" +#include "tl/tl_json.h" #include #include @@ -948,8 +949,18 @@ td::Status GetOverlaysStatsJsonQuery::receive(td::BufferSlice data) { sb << " \"" << t->key_ << "\": \"" << t->value_ << "\""; } - sb << "\n }\n"; - sb << "}\n"; + sb << "\n }"; + if (!s->extra_.empty()) { + sb << ",\n \"extra\": "; + for (char c : s->extra_) { + if (c == '\n') { + sb << "\n "; + } else { + sb << c; + } + } + } + sb << "\n}\n"; } sb << "]\n"; sb << std::flush; @@ -1216,6 +1227,12 @@ td::Status ShowCustomOverlaysQuery::receive(td::BufferSlice data) { : "") << (node->block_sender_ ? " (block sender)" : "") << "\n"; } + if (!overlay->sender_shards_.empty()) { + td::TerminalIO::out() << "Sender shards:\n"; + for (const auto &shard : overlay->sender_shards_) { + td::TerminalIO::out() << " " << ton::create_shard_id(shard).to_str() << "\n"; + } + } td::TerminalIO::out() << "\n"; } return td::Status::OK(); @@ -1482,3 +1499,43 @@ td::Status GetAdnlStatsQuery::receive(td::BufferSlice data) { td::TerminalIO::out() << sb.as_cslice(); return td::Status::OK(); } + +td::Status AddShardQuery::run() { + TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token()); + TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); + return td::Status::OK(); +} + +td::Status AddShardQuery::send() { + auto b = ton::create_serialize_tl_object( + ton::create_tl_shard_id(ton::ShardIdFull(wc_, shard_))); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status AddShardQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "successfully added shard\n"; + return td::Status::OK(); +} + +td::Status DelShardQuery::run() { + TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token()); + TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); + return td::Status::OK(); +} + +td::Status DelShardQuery::send() { + auto b = ton::create_serialize_tl_object( + ton::create_tl_shard_id(ton::ShardIdFull(wc_, shard_))); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status DelShardQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "successfully removed shard\n"; + return td::Status::OK(); +} \ No newline at end of file diff --git a/validator-engine-console/validator-engine-console-query.h b/validator-engine-console/validator-engine-console-query.h index 0e21c9c21..4ea172a3d 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -1362,3 +1362,49 @@ class GetAdnlStatsQuery : public Query { std::string file_name_; bool all_ = false; }; + +class AddShardQuery : public Query { + public: + AddShardQuery(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 "addshard"; + } + static std::string get_help() { + return "addshard \tstart monitoring shard"; + } + std::string name() const override { + return get_name(); + } + + private: + td::int32 wc_; + td::int64 shard_; +}; + +class DelShardQuery : public Query { + public: + DelShardQuery(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 "delshard"; + } + static std::string get_help() { + return "delshard \tstop monitoring shard"; + } + std::string name() const override { + return get_name(); + } + + private: + td::int32 wc_; + td::int64 shard_; +}; \ No newline at end of file diff --git a/validator-engine-console/validator-engine-console.cpp b/validator-engine-console/validator-engine-console.cpp index 59e2f2e8a..1f348e284 100644 --- a/validator-engine-console/validator-engine-console.cpp +++ b/validator-engine-console/validator-engine-console.cpp @@ -153,6 +153,8 @@ 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>()); } 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 1316667b4..78d3ba2b5 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -54,6 +54,7 @@ #include "td/utils/Random.h" #include "auto/tl/lite_api.h" +#include "tl/tl_json.h" #include "memprof/memprof.h" @@ -64,10 +65,10 @@ #endif #include #include -#include #include #include #include +#include #include "git.h" #include "block-auto.h" #include "block-parse.h" @@ -84,7 +85,7 @@ Config::Config() { full_node = ton::PublicKeyHash::zero(); } -Config::Config(ton::ton_api::engine_validator_config &config) { +Config::Config(const ton::ton_api::engine_validator_config &config) { full_node = ton::PublicKeyHash::zero(); out_port = static_cast(config.out_port_); if (!out_port) { @@ -97,7 +98,7 @@ Config::Config(ton::ton_api::engine_validator_config &config) { std::vector categories; std::vector priority_categories; ton::ton_api::downcast_call( - *addr.get(), + *addr, td::overloaded( [&](const ton::ton_api::engine_addr &obj) { in_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.ip_), static_cast(obj.port_)).ensure(); @@ -179,6 +180,10 @@ Config::Config(ton::ton_api::engine_validator_config &config) { } } + for (auto &shard : config.shards_to_monitor_) { + config_add_shard(ton::create_shard_id(shard)).ensure(); + } + if (config.gc_) { for (auto &gc : config.gc_->ids_) { config_add_gc(ton::PublicKeyHash{gc}).ensure(); @@ -262,14 +267,21 @@ ton::tl_object_ptr Config::tl() const { std::move(control_proc_vec))); } + std::vector> shards_vec; + for (auto &shard : shards_to_monitor) { + shards_vec.push_back(ton::create_tl_shard_id(shard)); + } + auto gc_vec = ton::create_tl_object(std::vector{}); for (auto &id : gc) { gc_vec->ids_.push_back(id.tl()); } + return ton::create_tl_object( - out_port, std::move(addrs_vec), std::move(adnl_vec), std::move(dht_vec), std::move(val_vec), full_node.tl(), - std::move(full_node_slaves_vec), std::move(full_node_masters_vec), std::move(full_node_config_obj), - std::move(extra_config_obj), std::move(liteserver_vec), std::move(control_vec), std::move(gc_vec)); + out_port, std::move(addrs_vec), std::move(adnl_vec), std::move(dht_vec), std::move(val_vec), + full_node.tl(), std::move(full_node_slaves_vec), std::move(full_node_masters_vec), + std::move(full_node_config_obj), std::move(extra_config_obj), std::move(liteserver_vec), std::move(control_vec), + std::move(shards_vec), std::move(gc_vec)); } td::Result Config::config_add_network_addr(td::IPAddress in_ip, td::IPAddress out_ip, @@ -532,6 +544,32 @@ td::Result Config::config_add_control_process(ton::PublicKeyHash key, td:: } } +td::Result Config::config_add_shard(ton::ShardIdFull shard) { + if (shard.is_masterchain()) { + return td::Status::Error("masterchain is monitored by default"); + } + if (!shard.is_valid_ext()) { + return td::Status::Error(PSTRING() << "invalid shard " << shard.to_str()); + } + if (std::find(shards_to_monitor.begin(), shards_to_monitor.end(), shard) != shards_to_monitor.end()) { + return false; + } + shards_to_monitor.push_back(shard); + return true; +} + +td::Result Config::config_del_shard(ton::ShardIdFull shard) { + if (!shard.is_valid_ext()) { + return td::Status::Error(PSTRING() << "invalid shard " << shard.to_str()); + } + auto it = std::find(shards_to_monitor.begin(), shards_to_monitor.end(), shard); + if (it == shards_to_monitor.end()) { + return false; + } + shards_to_monitor.erase(it); + return true; +} + td::Result Config::config_add_gc(ton::PublicKeyHash key) { return gc.insert(key).second; } @@ -1393,15 +1431,6 @@ td::Status ValidatorEngine::load_global_config() { } validator_options_ = ton::validator::ValidatorManagerOptions::create(zero_state, init_block); - validator_options_.write().set_shard_check_function( - [](ton::ShardIdFull shard, ton::CatchainSeqno cc_seqno, - ton::validator::ValidatorManagerOptions::ShardCheckMode mode) -> bool { - if (mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_monitor) { - return true; - } - CHECK(mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_validate); - return true; - }); if (state_ttl_ != 0) { validator_options_.write().set_state_ttl(state_ttl_); } @@ -1490,6 +1519,28 @@ td::Status ValidatorEngine::load_global_config() { return td::Status::OK(); } +void ValidatorEngine::set_shard_check_function() { + if (!not_all_shards_) { + validator_options_.write().set_shard_check_function([](ton::ShardIdFull shard) -> bool { return true; }); + } else { + std::vector shards = {ton::ShardIdFull(ton::masterchainId)}; + 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()); + validator_options_.write().set_shard_check_function( + [shards = std::move(shards)](ton::ShardIdFull shard) -> bool { + for (auto s : shards) { + if (shard_intersects(shard, s)) { + return true; + } + } + return false; + }); + } +} + void ValidatorEngine::load_empty_local_config(td::Promise promise) { auto ret_promise = td::PromiseCreator::lambda( [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { @@ -1531,6 +1582,14 @@ void ValidatorEngine::load_empty_local_config(td::Promise promise) { } void ValidatorEngine::load_local_config(td::Promise promise) { + for (ton::ShardIdFull shard : add_shard_cmds_) { + auto R = config_.config_add_shard(shard); + if (R.is_error()) { + LOG(WARNING) << "Cannot add shard " << shard.to_str() << " : " << R.move_as_error(); + } else if (R.ok()) { + LOG(WARNING) << "Adding shard to monitor " << shard.to_str(); + } + } if (local_config_.size() == 0) { load_empty_local_config(std::move(promise)); return; @@ -1746,6 +1805,15 @@ void ValidatorEngine::load_config(td::Promise promise) { td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key_short, key.first, get_key_promise(ig)); } + for (ton::ShardIdFull shard : add_shard_cmds_) { + auto R = config_.config_add_shard(shard); + if (R.is_error()) { + LOG(WARNING) << "Cannot add shard " << shard.to_str() << " : " << R.move_as_error(); + } else if (R.ok()) { + LOG(WARNING) << "Adding shard to monitor " << shard.to_str(); + } + } + write_config(ig.get_promise()); } @@ -1781,6 +1849,7 @@ void ValidatorEngine::got_key(ton::PublicKey key) { } void ValidatorEngine::start() { + set_shard_check_function(); read_config_ = true; start_adnl(); } @@ -1876,6 +1945,8 @@ void ValidatorEngine::started_dht() { void ValidatorEngine::start_rldp() { rldp_ = ton::rldp::Rldp::create(adnl_.get()); rldp2_ = ton::rldp2::Rldp::create(adnl_.get()); + td::actor::send_closure(rldp_, &ton::rldp::Rldp::set_default_mtu, 2048); + td::actor::send_closure(rldp2_, &ton::rldp2::Rldp::set_default_mtu, 2048); started_rldp(); } @@ -1939,24 +2010,27 @@ void ValidatorEngine::start_full_node() { }; full_node_client_ = ton::adnl::AdnlExtMultiClient::create(std::move(vec), std::make_unique()); } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorEngine::started_full_node); + }); full_node_ = ton::validator::fullnode::FullNode::create( short_id, ton::adnl::AdnlNodeIdShort{config_.full_node}, validator_options_->zero_block_id().file_hash, config_.full_node_config, keyring_.get(), adnl_.get(), rldp_.get(), rldp2_.get(), default_dht_node_.is_zero() ? td::actor::ActorId{} : dht_nodes_[default_dht_node_].get(), - overlay_manager_.get(), validator_manager_.get(), full_node_client_.get(), db_root_); + overlay_manager_.get(), validator_manager_.get(), full_node_client_.get(), db_root_, std::move(P)); + for (auto &v : config_.validators) { + td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_permanent_key, v.first, + [](td::Unit) {}); + } load_custom_overlays_config(); if (!validator_telemetry_filename_.empty()) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::set_validator_telemetry_filename, validator_telemetry_filename_); } + } else { + started_full_node(); } - - for (auto &v : config_.validators) { - td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_permanent_key, v.first, - [](td::Unit) {}); - } - - started_full_node(); } void ValidatorEngine::started_full_node() { @@ -3928,6 +4002,74 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getAdnlSt }); } +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addShard &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 shard = ton::create_shard_id(query.shard_); + auto R = config_.config_add_shard(shard); + if (R.is_error()) { + promise.set_value(create_control_query_error(R.move_as_error())); + return; + } + set_shard_check_function(); + if (!validator_manager_.empty()) { + 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_delShard &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 shard = ton::create_shard_id(query.shard_); + auto R = config_.config_del_shard(shard); + if (R.is_error()) { + promise.set_value(create_control_query_error(R.move_as_error())); + return; + } + if (!R.move_as_ok()) { + promise.set_value(create_control_query_error(td::Status::Error("No such shard"))); + return; + } + set_shard_check_function(); + if (!validator_manager_.empty()) { + 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::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) { @@ -3961,7 +4103,7 @@ void ValidatorEngine::process_control_query(td::uint16 port, ton::adnl::AdnlNode } auto f = F.move_as_ok(); - ton::ton_api::downcast_call(*f.get(), [&](auto &obj) { + ton::ton_api::downcast_call(*f, [&](auto &obj) { run_control_query(obj, std::move(data), src.pubkey_hash(), it->second, std::move(promise)); }); } @@ -4220,6 +4362,23 @@ int main(int argc, char *argv[]) { }); return td::Status::OK(); }); + p.add_option('M', "not-all-shards", "monitor only a necessary set of shards instead of all", [&]() { + acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_not_all_shards); }); + }); + p.add_checked_option( + '\0', "add-shard", "add shard to monitor (same as addshard in validator console), format: 0:8000000000000000", + [&](td::Slice arg) -> td::Status { + std::string str = arg.str(); + int wc; + unsigned long long shard; + if (sscanf(str.c_str(), "%d:%016llx", &wc, &shard) != 2) { + return td::Status::Error(PSTRING() << "invalid shard " << str); + } + acts.push_back([=, &x]() { + td::actor::send_closure(x, &ValidatorEngine::add_shard_cmd, ton::ShardIdFull{wc, (ton::ShardId)shard}); + }); + return td::Status::OK(); + }); td::uint32 threads = 7; p.add_checked_option( 't', "threads", PSTRING() << "number of threads (default=" << threads << ")", [&](td::Slice arg) { diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 5beef477e..65af61788 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -89,6 +89,7 @@ struct Config { ton::validator::fullnode::FullNodeConfig full_node_config; std::map controls; std::set gc; + std::vector shards_to_monitor; bool state_serializer_enabled = true; @@ -115,6 +116,8 @@ struct Config { td::Result config_add_control_interface(ton::PublicKeyHash key, td::int32 port); td::Result config_add_control_process(ton::PublicKeyHash key, td::int32 port, ton::PublicKeyHash id, td::uint32 permissions); + td::Result config_add_shard(ton::ShardIdFull shard); + td::Result config_del_shard(ton::ShardIdFull shard); td::Result config_add_gc(ton::PublicKeyHash key); td::Result config_del_network_addr(td::IPAddress addr, std::vector cats, std::vector prio_cats); @@ -132,7 +135,7 @@ struct Config { ton::tl_object_ptr tl() const; Config(); - Config(ton::ton_api::engine_validator_config &config); + Config(const ton::ton_api::engine_validator_config &config); }; class ValidatorEngine : public td::actor::Actor { @@ -223,6 +226,8 @@ class ValidatorEngine : public td::actor::Actor { std::string session_logs_file_; bool fast_state_serializer_enabled_ = false; std::string validator_telemetry_filename_; + bool not_all_shards_ = false; + std::vector add_shard_cmds_; std::set unsafe_catchains_; std::map> unsafe_catchain_rotations_; @@ -314,6 +319,13 @@ class ValidatorEngine : public td::actor::Actor { void set_validator_telemetry_filename(std::string value) { validator_telemetry_filename_ = std::move(value); } + void set_not_all_shards() { + not_all_shards_ = true; + } + void add_shard_cmd(ton::ShardIdFull shard) { + add_shard_cmds_.push_back(shard); + } + void start_up() override; ValidatorEngine() { } @@ -323,6 +335,7 @@ class ValidatorEngine : public td::actor::Actor { void load_empty_local_config(td::Promise promise); void load_local_config(td::Promise promise); void load_config(td::Promise promise); + void set_shard_check_function(); void start(); @@ -484,6 +497,10 @@ class ValidatorEngine : public td::actor::Actor { ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_getActorTextStats &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_addShard &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_delShard &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_getPerfTimerStats &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_getShardOutQueueSize &query, td::BufferSlice data, diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 71b3b73de..d349f9d87 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -624,7 +624,7 @@ void ArchiveManager::load_package(PackageId id) { } } - desc.file = td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, + desc.file = td::actor::create_actor("slice", id.id, id.key, id.temp, false, 0, db_root_, archive_lru_.get(), statistics_); m.emplace(id, std::move(desc)); @@ -659,7 +659,8 @@ const ArchiveManager::FileDescription *ArchiveManager::add_file_desc(ShardIdFull FileDescription new_desc{id, false}; td::mkdir(db_root_ + id.path()).ensure(); std::string prefix = PSTRING() << db_root_ << id.path() << id.name(); - new_desc.file = td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, + new_desc.file = td::actor::create_actor("slice", id.id, id.key, id.temp, false, + id.key || id.temp ? 0 : cur_shard_split_depth_, db_root_, archive_lru_.get(), statistics_); const FileDescription &desc = f.emplace(id, std::move(new_desc)); if (!id.temp) { @@ -1132,14 +1133,16 @@ PackageId ArchiveManager::get_package_id_force(BlockSeqno masterchain_seqno, Sha return it->first; } -void ArchiveManager::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { +void ArchiveManager::get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) { auto F = get_file_desc_by_seqno(ShardIdFull{masterchainId}, masterchain_seqno, false); if (!F) { promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found")); return; } - td::actor::send_closure(F->file_actor_id(), &ArchiveSlice::get_archive_id, masterchain_seqno, std::move(promise)); + td::actor::send_closure(F->file_actor_id(), &ArchiveSlice::get_archive_id, masterchain_seqno, shard_prefix, + std::move(promise)); } void ArchiveManager::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, diff --git a/validator/db/archive-manager.hpp b/validator/db/archive-manager.hpp index 622969ec5..90fc6a0b0 100644 --- a/validator/db/archive-manager.hpp +++ b/validator/db/archive-manager.hpp @@ -67,7 +67,7 @@ class ArchiveManager : public td::actor::Actor { void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); - void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise); + void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, td::Promise promise); void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, td::Promise promise); @@ -77,6 +77,10 @@ class ArchiveManager : public td::actor::Actor { void commit_transaction(); void set_async_mode(bool mode, td::Promise promise); + void set_current_shard_split_depth(td::uint32 value) { + cur_shard_split_depth_ = value; + } + static constexpr td::uint32 archive_size() { return 20000; } @@ -175,6 +179,7 @@ class ArchiveManager : public td::actor::Actor { bool async_mode_ = false; bool huge_transaction_started_ = false; td::uint32 huge_transaction_size_ = 0; + td::uint32 cur_shard_split_depth_ = 0; DbStatistics statistics_; diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp index 83fe144b4..41ed50997 100644 --- a/validator/db/archive-slice.cpp +++ b/validator/db/archive-slice.cpp @@ -39,7 +39,7 @@ class PackageStatistics { void record_close(uint64_t count = 1) { close_count.fetch_add(count, std::memory_order_relaxed); } - + void record_read(double time, uint64_t bytes) { read_bytes.fetch_add(bytes, std::memory_order_relaxed); std::lock_guard guard(read_mutex); @@ -56,10 +56,10 @@ class PackageStatistics { std::stringstream ss; ss.setf(std::ios::fixed); ss.precision(6); - + ss << "ton.pack.open COUNT : " << open_count.exchange(0, std::memory_order_relaxed) << "\n"; ss << "ton.pack.close COUNT : " << close_count.exchange(0, std::memory_order_relaxed) << "\n"; - + ss << "ton.pack.read.bytes COUNT : " << read_bytes.exchange(0, std::memory_order_relaxed) << "\n"; ss << "ton.pack.write.bytes COUNT : " << write_bytes.exchange(0, std::memory_order_relaxed) << "\n"; @@ -118,7 +118,7 @@ void PackageWriter::append(std::string filename, td::BufferSlice data, return; } start = td::Timestamp::now(); - offset = p->append(std::move(filename), std::move(data), !async_mode_); + offset = p->append(std::move(filename), std::move(data), !async_mode_); end = td::Timestamp::now(); size = p->size(); } @@ -152,6 +152,21 @@ class PackageReader : public td::actor::Actor { std::shared_ptr statistics_; }; +static std::string get_package_file_name(PackageId p_id, ShardIdFull shard_prefix) { + td::StringBuilder sb; + sb << p_id.name(); + if (!shard_prefix.is_masterchain()) { + sb << "."; + sb << shard_prefix.workchain << ":" << shard_to_str(shard_prefix.shard); + } + sb << ".pack"; + return sb.as_cslice().str(); +} + +static std::string package_info_to_str(BlockSeqno seqno, ShardIdFull shard_prefix) { + return PSTRING() << seqno << "." << shard_prefix.workchain << ":" << shard_to_str(shard_prefix.shard); +} + void ArchiveSlice::add_handle(BlockHandle handle, td::Promise promise) { if (destroyed_) { promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); @@ -271,7 +286,8 @@ void ArchiveSlice::add_file(BlockHandle handle, FileReference ref_id, td::Buffer TRY_RESULT_PROMISE( promise, p, choose_package( - handle ? handle->id().is_masterchain() ? handle->id().seqno() : handle->masterchain_ref_block() : 0, true)); + handle ? handle->id().is_masterchain() ? handle->id().seqno() : handle->masterchain_ref_block() : 0, + handle ? handle->id().shard_full() : ShardIdFull{masterchainId}, true)); std::string value; auto R = kv_->get(ref_id.hash().to_hex(), value); R.ensure(); @@ -376,7 +392,8 @@ void ArchiveSlice::get_file(ConstBlockHandle handle, FileReference ref_id, td::P TRY_RESULT_PROMISE( promise, p, choose_package( - handle ? handle->id().is_masterchain() ? handle->id().seqno() : handle->masterchain_ref_block() : 0, false)); + handle ? handle->id().is_masterchain() ? handle->id().seqno() : handle->masterchain_ref_block() : 0, + handle ? handle->id().shard_full() : ShardIdFull{masterchainId}, false)); promise = begin_async_query(std::move(promise)); auto P = td::PromiseCreator::lambda( [promise = std::move(promise)](td::Result> R) mutable { @@ -536,18 +553,32 @@ void ArchiveSlice::get_slice(td::uint64 archive_id, td::uint64 offset, td::uint3 } before_query(); auto value = static_cast(archive_id >> 32); - TRY_RESULT_PROMISE(promise, p, choose_package(value, false)); + PackageInfo *p; + if (shard_split_depth_ == 0) { + TRY_RESULT_PROMISE_ASSIGN(promise, p, choose_package(value, ShardIdFull{masterchainId}, false)); + } else { + if (value >= packages_.size()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "no such package")); + return; + } + p = &packages_[value]; + } promise = begin_async_query(std::move(promise)); td::actor::create_actor("readfile", p->path, offset, limit, 0, std::move(promise)).release(); } -void ArchiveSlice::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { +void ArchiveSlice::get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) { before_query(); if (!sliced_mode_) { promise.set_result(archive_id_); } else { - TRY_RESULT_PROMISE(promise, p, choose_package(masterchain_seqno, false)); - promise.set_result(p->id * (1ull << 32) + archive_id_); + TRY_RESULT_PROMISE(promise, p, choose_package(masterchain_seqno, shard_prefix, false)); + if (shard_split_depth_ == 0) { + promise.set_result(p->seqno * (1ull << 32) + archive_id_); + } else { + promise.set_result(p->idx * (1ull << 32) + archive_id_); + } } } @@ -573,9 +604,18 @@ void ArchiveSlice::before_query() { R2.ensure(); slice_size_ = td::to_integer(value); CHECK(slice_size_ > 0); + R2 = kv_->get("shard_split_depth", value); + R2.ensure(); + if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) { + shard_split_depth_ = td::to_integer(value); + CHECK(shard_split_depth_ <= 60); + } else { + shard_split_depth_ = 0; + } for (td::uint32 i = 0; i < tot; i++) { R2 = kv_->get(PSTRING() << "status." << i, value); R2.ensure(); + CHECK(R2.move_as_ok() == td::KeyValue::GetStatus::Ok); auto len = td::to_integer(value); R2 = kv_->get(PSTRING() << "version." << i, value); R2.ensure(); @@ -583,12 +623,24 @@ void ArchiveSlice::before_query() { if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) { ver = td::to_integer(value); } - auto v = archive_id_ + slice_size_ * i; - add_package(v, len, ver); + td::uint32 seqno; + ShardIdFull shard_prefix; + if (shard_split_depth_ == 0) { + seqno = archive_id_ + slice_size_ * i; + shard_prefix = ShardIdFull{masterchainId}; + } else { + R2 = kv_->get(PSTRING() << "info." << i, value); + R2.ensure(); + CHECK(R2.move_as_ok() == td::KeyValue::GetStatus::Ok); + unsigned long long shard; + CHECK(sscanf(value.c_str(), "%u.%d:%016llx", &seqno, &shard_prefix.workchain, &shard) == 3); + shard_prefix.shard = shard; + } + add_package(seqno, shard_prefix, len, ver); } } else { auto len = td::to_integer(value); - add_package(archive_id_, len, 0); + add_package(archive_id_, ShardIdFull{masterchainId}, len, 0); } } else { if (!temp_ && !key_blocks_only_) { @@ -599,13 +651,17 @@ void ArchiveSlice::before_query() { kv_->set("slice_size", td::to_string(slice_size_)).ensure(); kv_->set("status.0", "0").ensure(); kv_->set("version.0", td::to_string(default_package_version())).ensure(); + if (shard_split_depth_ > 0) { + kv_->set("info.0", package_info_to_str(archive_id_, ShardIdFull{masterchainId})).ensure(); + kv_->set("shard_split_depth", td::to_string(shard_split_depth_)).ensure(); + } kv_->commit_transaction().ensure(); - add_package(archive_id_, 0, default_package_version()); + add_package(archive_id_, ShardIdFull{masterchainId}, 0, default_package_version()); } else { kv_->begin_transaction().ensure(); kv_->set("status", "0").ensure(); kv_->commit_transaction().ensure(); - add_package(archive_id_, 0, 0); + add_package(archive_id_, ShardIdFull{masterchainId}, 0, 0); } } } @@ -642,6 +698,7 @@ void ArchiveSlice::do_close() { statistics_.pack_statistics->record_close(packages_.size()); } packages_.clear(); + id_to_package_.clear(); } template @@ -697,48 +754,61 @@ void ArchiveSlice::set_async_mode(bool mode, td::Promise promise) { } } -ArchiveSlice::ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, std::string db_root, +ArchiveSlice::ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, + td::uint32 shard_split_depth, std::string db_root, td::actor::ActorId archive_lru, DbStatistics statistics) : archive_id_(archive_id) , key_blocks_only_(key_blocks_only) , temp_(temp) , finalized_(finalized) , p_id_(archive_id_, key_blocks_only_, temp_) + , shard_split_depth_(temp || key_blocks_only ? 0 : shard_split_depth) , db_root_(std::move(db_root)) , archive_lru_(std::move(archive_lru)) , statistics_(statistics) { db_path_ = PSTRING() << db_root_ << p_id_.path() << p_id_.name() << ".index"; } -td::Result ArchiveSlice::choose_package(BlockSeqno masterchain_seqno, bool force) { +td::Result ArchiveSlice::choose_package(BlockSeqno masterchain_seqno, + ShardIdFull shard_prefix, bool force) { if (temp_ || key_blocks_only_ || !sliced_mode_) { return &packages_[0]; } if (masterchain_seqno < archive_id_) { return td::Status::Error(ErrorCode::notready, "too small masterchain seqno"); } - auto v = (masterchain_seqno - archive_id_) / slice_size_; - if (v >= packages_.size()) { + masterchain_seqno -= (masterchain_seqno - archive_id_) % slice_size_; + CHECK((masterchain_seqno - archive_id_) % slice_size_ == 0); + if (shard_split_depth_ == 0) { + shard_prefix = ShardIdFull{masterchainId}; + } else if (!shard_prefix.is_masterchain()) { + shard_prefix.shard |= 1; // In case length is < split depth + shard_prefix = ton::shard_prefix(shard_prefix, shard_split_depth_); + } + auto it = id_to_package_.find({masterchain_seqno, shard_prefix}); + if (it == id_to_package_.end()) { if (!force) { - return td::Status::Error(ErrorCode::notready, "too big masterchain seqno"); + return td::Status::Error(ErrorCode::notready, "no such package"); } - CHECK(v == packages_.size()); begin_transaction(); + size_t v = packages_.size(); kv_->set("slices", td::to_string(v + 1)).ensure(); kv_->set(PSTRING() << "status." << v, "0").ensure(); kv_->set(PSTRING() << "version." << v, td::to_string(default_package_version())).ensure(); + if (shard_split_depth_ > 0) { + kv_->set(PSTRING() << "info." << v, package_info_to_str(masterchain_seqno, shard_prefix)).ensure(); + } commit_transaction(); - CHECK((masterchain_seqno - archive_id_) % slice_size_ == 0); - add_package(masterchain_seqno, 0, default_package_version()); + add_package(masterchain_seqno, shard_prefix, 0, default_package_version()); return &packages_[v]; } else { - return &packages_[v]; + return &packages_[it->second]; } } -void ArchiveSlice::add_package(td::uint32 seqno, td::uint64 size, td::uint32 version) { +void ArchiveSlice::add_package(td::uint32 seqno, ShardIdFull shard_prefix, td::uint64 size, td::uint32 version) { PackageId p_id{seqno, key_blocks_only_, temp_}; - std::string path = PSTRING() << db_root_ << p_id.path() << p_id.name() << ".pack"; + std::string path = PSTRING() << db_root_ << p_id.path() << get_package_file_name(p_id, shard_prefix); auto R = Package::open(path, false, true); if (R.is_error()) { LOG(FATAL) << "failed to open/create archive '" << path << "': " << R.move_as_error(); @@ -748,8 +818,9 @@ void ArchiveSlice::add_package(td::uint32 seqno, td::uint64 size, td::uint32 ver statistics_.pack_statistics->record_open(); } auto idx = td::narrow_cast(packages_.size()); + id_to_package_[{seqno, shard_prefix}] = idx; if (finalized_) { - packages_.emplace_back(nullptr, td::actor::ActorOwn(), seqno, path, idx, version); + packages_.emplace_back(nullptr, td::actor::ActorOwn(), seqno, shard_prefix, path, idx, version); return; } auto pack = std::make_shared(R.move_as_ok()); @@ -757,7 +828,7 @@ void ArchiveSlice::add_package(td::uint32 seqno, td::uint64 size, td::uint32 ver pack->truncate(size).ensure(); } auto writer = td::actor::create_actor("writer", pack, async_mode_, statistics_.pack_statistics); - packages_.emplace_back(std::move(pack), std::move(writer), seqno, path, idx, version); + packages_.emplace_back(std::move(pack), std::move(writer), seqno, shard_prefix, path, idx, version); } namespace { @@ -790,6 +861,7 @@ void ArchiveSlice::destroy(td::Promise promise) { statistics_.pack_statistics->record_close(packages_.size()); } packages_.clear(); + id_to_package_.clear(); kv_ = nullptr; delay_action([name = db_path_, attempt = 0, @@ -861,7 +933,7 @@ void ArchiveSlice::move_handle(ConstBlockHandle handle, Package *old_pack, Packa move_file(fileref::Block{handle->id()}, old_pack, pack); } -bool ArchiveSlice::truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block_id, td::uint32 cutoff_idx, +bool ArchiveSlice::truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block_id, td::uint32 cutoff_seqno, Package *pack) { std::string value; auto R = kv_->get(get_db_key_block_info(block_id), value); @@ -876,18 +948,18 @@ bool ArchiveSlice::truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block return false; } - auto S = choose_package(seqno, false); + auto S = choose_package(seqno, block_id.shard_full(), false); S.ensure(); auto p = S.move_as_ok(); - CHECK(p->idx <= cutoff_idx); - if (p->idx == cutoff_idx) { + CHECK(p->seqno <= cutoff_seqno); + if (p->seqno == cutoff_seqno) { move_handle(std::move(handle), p->package.get(), pack); } return true; } -void ArchiveSlice::truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shard, td::uint32 cutoff_idx, +void ArchiveSlice::truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shard, td::uint32 cutoff_seqno, Package *pack) { auto key = get_db_key_lt_desc(shard); std::string value; @@ -913,7 +985,7 @@ void ArchiveSlice::truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shar E.ensure(); auto e = E.move_as_ok(); - if (truncate_block(masterchain_seqno, create_block_id(e->id_), cutoff_idx, pack)) { + if (truncate_block(masterchain_seqno, create_block_id(e->id_), cutoff_seqno, pack)) { CHECK(new_last_idx == i); new_last_idx = i + 1; } @@ -925,7 +997,7 @@ void ArchiveSlice::truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shar } } -void ArchiveSlice::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handle, td::Promise promise) { +void ArchiveSlice::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle, td::Promise promise) { if (temp_ || archive_id_ > masterchain_seqno) { destroy(std::move(promise)); return; @@ -938,15 +1010,8 @@ void ArchiveSlice::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handl return; } - auto cutoff = choose_package(masterchain_seqno, false); - cutoff.ensure(); - auto pack = cutoff.move_as_ok(); - CHECK(pack); - - auto pack_r = Package::open(pack->path + ".new", false, true); - pack_r.ensure(); - auto new_package = std::make_shared(pack_r.move_as_ok()); - new_package->truncate(0).ensure(); + std::map old_packages; + std::map> new_packages; std::string value; auto status_key = create_serialize_tl_object(); @@ -967,38 +1032,71 @@ void ArchiveSlice::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handl auto G = fetch_tl_object(value, true); G.ensure(); auto g = G.move_as_ok(); + ShardIdFull shard{g->workchain_, static_cast(g->shard_)}; + + auto package_r = choose_package(masterchain_seqno, shard, false); + if (package_r.is_error()) { + continue; + } + auto package = package_r.move_as_ok(); + CHECK(package); + if (!old_packages.count(package->shard_prefix)) { + old_packages[package->shard_prefix] = package; + auto new_package_r = Package::open(package->path + ".new", false, true); + new_package_r.ensure(); + auto new_package = std::make_shared(new_package_r.move_as_ok()); + new_package->truncate(0).ensure(); + new_packages[package->shard_prefix] = std::move(new_package); + } + truncate_shard(masterchain_seqno, shard, package->seqno, new_packages[package->shard_prefix].get()); + } - truncate_shard(masterchain_seqno, ShardIdFull{g->workchain_, static_cast(g->shard_)}, pack->idx, - new_package.get()); + for (auto& [shard_prefix, package] : old_packages) { + auto new_package = new_packages[shard_prefix]; + CHECK(new_package); + package->package = new_package; + package->writer.reset(); + td::unlink(package->path).ensure(); + td::rename(package->path + ".new", package->path).ensure(); + package->writer = td::actor::create_actor("writer", new_package, async_mode_); } + std::vector new_packages_info; + if (!sliced_mode_) { - kv_->set("status", td::to_string(new_package->size())).ensure(); + kv_->set("status", td::to_string(packages_.at(0).package->size())).ensure(); } else { - kv_->set(PSTRING() << "status." << pack->idx, td::to_string(new_package->size())).ensure(); - for (size_t i = pack->idx + 1; i < packages_.size(); i++) { + for (PackageInfo &package : packages_) { + if (package.seqno <= masterchain_seqno) { + new_packages_info.push_back(std::move(package)); + } else { + td::unlink(package.path).ensure(); + } + } + id_to_package_.clear(); + for (td::uint32 i = 0; i < new_packages_info.size(); ++i) { + PackageInfo &package = new_packages_info[i]; + package.idx = i; + kv_->set(PSTRING() << "status." << i, td::to_string(package.package->size())).ensure(); + kv_->set(PSTRING() << "version." << i, td::to_string(package.version)).ensure(); + if (shard_split_depth_ > 0) { + kv_->set(PSTRING() << "info." << i, package_info_to_str(package.seqno, package.shard_prefix)).ensure(); + } + id_to_package_[{package.seqno, package.shard_prefix}] = i; + } + for (size_t i = new_packages_info.size(); i < packages_.size(); i++) { kv_->erase(PSTRING() << "status." << i); kv_->erase(PSTRING() << "version." << i); + kv_->erase(PSTRING() << "info." << i); } - kv_->set("slices", td::to_string(pack->idx + 1)); - } - - pack->package = new_package; - pack->writer.reset(); - td::unlink(pack->path).ensure(); - td::rename(pack->path + ".new", pack->path).ensure(); - pack->writer = td::actor::create_actor("writer", new_package, async_mode_); - - for (auto idx = pack->idx + 1; idx < packages_.size(); idx++) { - td::unlink(packages_[idx].path).ensure(); - } - if (statistics_.pack_statistics) { - statistics_.pack_statistics->record_close(packages_.size() - pack->idx - 1); + kv_->set("slices", td::to_string(new_packages_info.size())); + if (statistics_.pack_statistics) { + statistics_.pack_statistics->record_close(packages_.size() - new_packages_info.size()); + } + packages_ = std::move(new_packages_info); } - packages_.erase(packages_.begin() + pack->idx + 1, packages_.end()); kv_->commit_transaction().ensure(); - promise.set_value(td::Unit()); } diff --git a/validator/db/archive-slice.hpp b/validator/db/archive-slice.hpp index faec2fb83..a027ec0ff 100644 --- a/validator/db/archive-slice.hpp +++ b/validator/db/archive-slice.hpp @@ -96,10 +96,10 @@ class ArchiveLru; class ArchiveSlice : public td::actor::Actor { public: - ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, std::string db_root, - td::actor::ActorId archive_lru, DbStatistics statistics = {}); + ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, td::uint32 shard_split_depth, + std::string db_root, td::actor::ActorId archive_lru, DbStatistics statistics = {}); - void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise); + void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, td::Promise promise); void add_handle(BlockHandle handle, td::Promise promise); void update_handle(BlockHandle handle, td::Promise promise); @@ -159,6 +159,7 @@ class ArchiveSlice : public td::actor::Actor { bool sliced_mode_{false}; td::uint32 huge_transaction_size_ = 0; td::uint32 slice_size_{100}; + td::uint32 shard_split_depth_ = 0; enum Status { st_closed, st_open, st_want_close @@ -171,28 +172,31 @@ class ArchiveSlice : public td::actor::Actor { std::unique_ptr kv_; struct PackageInfo { - PackageInfo(std::shared_ptr package, td::actor::ActorOwn writer, BlockSeqno id, + PackageInfo(std::shared_ptr package, td::actor::ActorOwn writer, BlockSeqno seqno, ShardIdFull shard_prefix, std::string path, td::uint32 idx, td::uint32 version) : package(std::move(package)) , writer(std ::move(writer)) - , id(id) + , seqno(seqno) + , shard_prefix(shard_prefix) , path(std::move(path)) , idx(idx) , version(version) { } std::shared_ptr package; td::actor::ActorOwn writer; - BlockSeqno id; + BlockSeqno seqno; + ShardIdFull shard_prefix; std::string path; td::uint32 idx; td::uint32 version; }; std::vector packages_; + std::map, td::uint32> id_to_package_; - td::Result choose_package(BlockSeqno masterchain_seqno, bool force); - void add_package(BlockSeqno masterchain_seqno, td::uint64 size, td::uint32 version); - void truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shard, td::uint32 cutoff_idx, Package *pack); - bool truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block_id, td::uint32 cutoff_idx, Package *pack); + td::Result choose_package(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, bool force); + void add_package(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, td::uint64 size, td::uint32 version); + void truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shard, td::uint32 cutoff_seqno, Package *pack); + bool truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block_id, td::uint32 cutoff_seqno, Package *pack); void delete_handle(ConstBlockHandle handle); void delete_file(FileReference ref_id); diff --git a/validator/db/archiver.cpp b/validator/db/archiver.cpp index c8cbd7b97..93ba18ca2 100644 --- a/validator/db/archiver.cpp +++ b/validator/db/archiver.cpp @@ -25,11 +25,27 @@ namespace ton { namespace validator { BlockArchiver::BlockArchiver(BlockHandle handle, td::actor::ActorId archive_db, - td::Promise promise) - : handle_(std::move(handle)), archive_(archive_db), promise_(std::move(promise)) { + td::actor::ActorId db, td::Promise promise) + : handle_(std::move(handle)), archive_(archive_db), db_(std::move(db)), promise_(std::move(promise)) { } void BlockArchiver::start_up() { + if (handle_->id().is_masterchain()) { + td::actor::send_closure(db_, &Db::get_block_state, handle_, + [SelfId = actor_id(this), archive = archive_](td::Result> R) { + R.ensure(); + td::Ref state{R.move_as_ok()}; + td::uint32 monitor_min_split = state->monitor_min_split_depth(basechainId); + td::actor::send_closure(archive, &ArchiveManager::set_current_shard_split_depth, + monitor_min_split); + td::actor::send_closure(SelfId, &BlockArchiver::move_handle); + }); + } else { + move_handle(); + } +} + +void BlockArchiver::move_handle() { if (handle_->handle_moved_to_archive()) { moved_handle(); } else { diff --git a/validator/db/archiver.hpp b/validator/db/archiver.hpp index 859f269cd..9498977fd 100644 --- a/validator/db/archiver.hpp +++ b/validator/db/archiver.hpp @@ -33,11 +33,13 @@ class FileDb; class BlockArchiver : public td::actor::Actor { public: - BlockArchiver(BlockHandle handle, td::actor::ActorId archive_db, td::Promise promise); + BlockArchiver(BlockHandle handle, td::actor::ActorId archive_db, td::actor::ActorId db, + td::Promise promise); void abort_query(td::Status error); void start_up() override; + void move_handle(); void moved_handle(); void got_proof(td::BufferSlice data); void written_proof(); @@ -50,6 +52,7 @@ class BlockArchiver : public td::actor::Actor { private: BlockHandle handle_; td::actor::ActorId archive_; + td::actor::ActorId db_; td::Promise promise_; }; diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index bb5d767fd..e0579d570 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -347,7 +347,8 @@ void RootDb::try_get_static_file(FileHash file_hash, td::Promise promise) { - td::actor::create_actor("archiver", std::move(handle), archive_db_.get(), std::move(promise)) + td::actor::create_actor("archiver", std::move(handle), archive_db_.get(), actor_id(this), + std::move(promise)) .release(); } @@ -421,7 +422,8 @@ void RootDb::start_up() { } void RootDb::archive(BlockHandle handle, td::Promise promise) { - td::actor::create_actor("archiveblock", std::move(handle), archive_db_.get(), std::move(promise)) + td::actor::create_actor("archiveblock", std::move(handle), archive_db_.get(), actor_id(this), + std::move(promise)) .release(); } @@ -501,8 +503,9 @@ void RootDb::check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise< std::move(P)); } -void RootDb::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { - td::actor::send_closure(archive_db_, &ArchiveManager::get_archive_id, masterchain_seqno, std::move(promise)); +void RootDb::get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::get_archive_id, masterchain_seqno, shard_prefix, + std::move(promise)); } void RootDb::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, @@ -519,6 +522,14 @@ void RootDb::run_gc(UnixTime mc_ts, UnixTime gc_ts, UnixTime archive_ttl) { td::actor::send_closure(archive_db_, &ArchiveManager::run_gc, mc_ts, gc_ts, archive_ttl); } +void RootDb::add_persistent_state_description(td::Ref desc, td::Promise promise) { + td::actor::send_closure(state_db_, &StateDb::add_persistent_state_description, std::move(desc), std::move(promise)); +} + +void RootDb::get_persistent_state_descriptions(td::Promise>> promise) { + td::actor::send_closure(state_db_, &StateDb::get_persistent_state_descriptions, std::move(promise)); +} + } // namespace validator } // namespace ton diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index 755ff2578..52f6098e4 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -132,12 +132,15 @@ class RootDb : public Db { void check_key_block_proof_exists(BlockIdExt block_id, td::Promise promise) override; void check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise promise) override; - void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override; + void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, td::Promise promise) override; void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, td::Promise promise) override; void set_async_mode(bool mode, td::Promise promise) override; void run_gc(UnixTime mc_ts, UnixTime gc_ts, UnixTime archive_ttl) override; + void add_persistent_state_description(td::Ref desc, td::Promise promise) override; + void get_persistent_state_descriptions(td::Promise>> promise) override; + private: td::actor::ActorId validator_manager_; diff --git a/validator/db/statedb.cpp b/validator/db/statedb.cpp index 5d49ae2bd..fe3b9d735 100644 --- a/validator/db/statedb.cpp +++ b/validator/db/statedb.cpp @@ -240,6 +240,101 @@ void StateDb::start_up() { } } +void StateDb::add_persistent_state_description(td::Ref desc, + td::Promise promise) { + std::string value; + auto list_key = create_hash_tl_object(); + auto R = kv_->get(list_key.as_slice(), value); + R.ensure(); + tl_object_ptr list; + if (R.ok() == td::KeyValue::GetStatus::Ok) { + auto F = fetch_tl_object(value, true); + F.ensure(); + list = F.move_as_ok(); + } else { + list = create_tl_object( + std::vector>()); + } + for (const auto& obj : list->list_) { + if ((BlockSeqno)obj->masterchain_id_->seqno_ == desc->masterchain_id.seqno()) { + promise.set_error(td::Status::Error("duplicate masterchain seqno")); + return; + } + } + + auto now = (UnixTime)td::Clocks::system(); + size_t new_size = 0; + kv_->begin_write_batch().ensure(); + for (auto& obj : list->list_) { + auto end_time = (UnixTime)obj->end_time_; + if (end_time <= now) { + auto key = + create_hash_tl_object(obj->masterchain_id_->seqno_); + kv_->erase(key.as_slice()).ensure(); + } else { + list->list_[new_size++] = std::move(obj); + } + } + list->list_.resize(new_size); + + std::vector> shard_blocks; + for (const BlockIdExt& block_id : desc->shard_blocks) { + shard_blocks.push_back(create_tl_block_id(block_id)); + } + auto key = + create_hash_tl_object(desc->masterchain_id.seqno()); + kv_->set(key.as_slice(), + create_serialize_tl_object(std::move(shard_blocks)) + .as_slice()) + .ensure(); + + list->list_.push_back(create_tl_object( + create_tl_block_id(desc->masterchain_id), desc->start_time, desc->end_time)); + kv_->set(list_key.as_slice(), serialize_tl_object(list, true).as_slice()).ensure(); + + kv_->commit_write_batch().ensure(); + + promise.set_result(td::Unit()); +} + +void StateDb::get_persistent_state_descriptions(td::Promise>> promise) { + std::string value; + auto R = kv_->get(create_hash_tl_object().as_slice(), value); + R.ensure(); + if (R.ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_value({}); + return; + } + auto F = fetch_tl_object(value, true); + F.ensure(); + std::vector> result; + auto now = (UnixTime)td::Clocks::system(); + for (const auto& obj : F.ok()->list_) { + auto end_time = (UnixTime)obj->end_time_; + if (end_time <= now) { + continue; + } + PersistentStateDescription desc; + desc.start_time = (UnixTime)obj->start_time_; + desc.end_time = end_time; + desc.masterchain_id = create_block_id(obj->masterchain_id_); + auto key = + create_hash_tl_object(desc.masterchain_id.seqno()); + auto R2 = kv_->get(key.as_slice(), value); + R2.ensure(); + if (R2.ok() == td::KeyValue::GetStatus::NotFound) { + continue; + } + auto F2 = fetch_tl_object(value, true); + F2.ensure(); + for (const auto& block_id : F2.ok()->shard_blocks_) { + desc.shard_blocks.push_back(create_block_id(block_id)); + } + result.push_back(td::Ref(true, std::move(desc))); + } + promise.set_result(std::move(result)); +} + void StateDb::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handle, td::Promise promise) { { auto key = create_hash_tl_object(); diff --git a/validator/db/statedb.hpp b/validator/db/statedb.hpp index a7a004522..fe23898f4 100644 --- a/validator/db/statedb.hpp +++ b/validator/db/statedb.hpp @@ -50,6 +50,9 @@ class StateDb : public td::actor::Actor { void update_hardforks(std::vector blocks, td::Promise promise); void get_hardforks(td::Promise> promise); + void add_persistent_state_description(td::Ref desc, td::Promise promise); + void get_persistent_state_descriptions(td::Promise>> promise); + StateDb(td::actor::ActorId root_db, std::string path); void start_up() override; diff --git a/validator/downloaders/wait-block-data.cpp b/validator/downloaders/wait-block-data.cpp index 220a8a2cf..53a3d351b 100644 --- a/validator/downloaders/wait-block-data.cpp +++ b/validator/downloaders/wait-block-data.cpp @@ -106,13 +106,24 @@ void WaitBlockData::start() { }); td::actor::send_closure(manager_, &ValidatorManager::try_get_static_file, handle_->id().file_hash, std::move(P)); + } else if (try_get_candidate_) { + try_get_candidate_ = false; + td::actor::send_closure( + manager_, &ValidatorManager::get_candidate_data_by_block_id_from_db, handle_->id(), + [SelfId = actor_id(this), id = handle_->id()](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &WaitBlockData::start); + } else { + td::actor::send_closure(SelfId, &WaitBlockData::loaded_data, ReceivedBlock{id, R.move_as_ok()}); + } + }); } else { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &WaitBlockData::failed_to_get_block_data_from_net, R.move_as_error_prefix("net error: ")); } else { - td::actor::send_closure(SelfId, &WaitBlockData::got_data_from_net, R.move_as_ok()); + td::actor::send_closure(SelfId, &WaitBlockData::loaded_data, R.move_as_ok()); } }); @@ -137,16 +148,16 @@ void WaitBlockData::failed_to_get_block_data_from_net(td::Status reason) { td::Timestamp::in(0.1)); } -void WaitBlockData::got_data_from_net(ReceivedBlock block) { +void WaitBlockData::loaded_data(ReceivedBlock block) { auto X = create_block(std::move(block)); if (X.is_error()) { failed_to_get_block_data_from_net(X.move_as_error_prefix("bad block from net: ")); return; } - got_block_data_from_net(X.move_as_ok()); + loaded_block_data(X.move_as_ok()); } -void WaitBlockData::got_block_data_from_net(td::Ref block) { +void WaitBlockData::loaded_block_data(td::Ref block) { if (data_.not_null()) { return; } diff --git a/validator/downloaders/wait-block-data.hpp b/validator/downloaders/wait-block-data.hpp index 229b4bfc8..f3b367d51 100644 --- a/validator/downloaders/wait-block-data.hpp +++ b/validator/downloaders/wait-block-data.hpp @@ -30,15 +30,16 @@ class ValidatorManager; class WaitBlockData : public td::actor::Actor { public: WaitBlockData(BlockHandle handle, td::uint32 priority, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise> promise) + td::Timestamp timeout, bool try_get_candidate, td::Promise> promise) : handle_(std::move(handle)) , priority_(priority) , manager_(manager) , timeout_(timeout) + , try_get_candidate_(try_get_candidate) , promise_(std::move(promise)) , perf_timer_("waitdata", 1.0, [manager](double duration) { - send_closure(manager, &ValidatorManager::add_perf_timer_stat, "waitdata", duration); - }) { + send_closure(manager, &ValidatorManager::add_perf_timer_stat, "waitdata", duration); + }) { } void update_timeout(td::Timestamp timeout, td::uint32 priority) { @@ -57,8 +58,8 @@ class WaitBlockData : public td::actor::Actor { void set_is_hardfork(bool value); void start(); void got_block_data_from_db(td::Ref data); - void got_data_from_net(ReceivedBlock data); - void got_block_data_from_net(td::Ref block); + void loaded_data(ReceivedBlock data); + void loaded_block_data(td::Ref block); void checked_proof_link(); void failed_to_get_block_data_from_net(td::Status reason); @@ -73,6 +74,7 @@ class WaitBlockData : public td::actor::Actor { td::actor::ActorId manager_; td::Timestamp timeout_; + bool try_get_candidate_; td::Promise> promise_; td::Ref data_; diff --git a/validator/downloaders/wait-block-state.cpp b/validator/downloaders/wait-block-state.cpp index f8d2cdcbe..b61b94922 100644 --- a/validator/downloaders/wait-block-state.cpp +++ b/validator/downloaders/wait-block-state.cpp @@ -21,6 +21,7 @@ #include "ton/ton-io.hpp" #include "common/checksum.h" #include "common/delay.h" +#include "validator/downloaders/download-state.hpp" namespace ton { @@ -106,6 +107,19 @@ void WaitBlockState::start() { }); td::actor::send_closure(manager_, &ValidatorManager::send_get_zero_state_request, handle_->id(), priority_, std::move(P)); + } else if (check_persistent_state_desc()) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + LOG(WARNING) << "failed to get persistent state: " << R.move_as_error(); + td::actor::send_closure(SelfId, &WaitBlockState::start); + } else { + td::actor::send_closure(SelfId, &WaitBlockState::written_state, R.move_as_ok()); + } + }); + BlockIdExt masterchain_id = persistent_state_desc_->masterchain_id; + td::actor::create_actor("downloadstate", handle_->id(), masterchain_id, priority_, manager_, + timeout_, std::move(P)) + .release(); } else if (!handle_->inited_prev() || (!handle_->inited_proof() && !handle_->inited_proof_link())) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result R) { if (R.is_error()) { diff --git a/validator/downloaders/wait-block-state.hpp b/validator/downloaders/wait-block-state.hpp index 4b484ca83..6a14d909f 100644 --- a/validator/downloaders/wait-block-state.hpp +++ b/validator/downloaders/wait-block-state.hpp @@ -27,12 +27,14 @@ namespace validator { class WaitBlockState : public td::actor::Actor { public: WaitBlockState(BlockHandle handle, td::uint32 priority, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise> promise) + td::Timestamp timeout, td::Promise> promise, + td::Ref persistent_state_desc = {}) : handle_(std::move(handle)) , priority_(priority) , manager_(manager) , timeout_(timeout) , promise_(std::move(promise)) + , persistent_state_desc_(std::move(persistent_state_desc)) , perf_timer_("waitstate", 1.0, [manager](double duration) { send_closure(manager, &ValidatorManager::add_perf_timer_stat, "waitstate", duration); }) { @@ -90,6 +92,7 @@ class WaitBlockState : public td::actor::Actor { td::actor::ActorId manager_; td::Timestamp timeout_; td::Promise> promise_; + td::Ref persistent_state_desc_; td::Ref prev_state_; td::Ref block_; @@ -99,7 +102,15 @@ class WaitBlockState : public td::actor::Actor { bool waiting_proof_ = false; td::Timestamp next_static_file_attempt_; - td::PerfWarningTimer perf_timer_; + td::PerfWarningTimer perf_timer_{"waitstate", 1.0}; + + bool check_persistent_state_desc() const { + if (persistent_state_desc_.is_null()) { + return false; + } + auto now = (UnixTime)td::Clocks::system(); + return persistent_state_desc_->end_time > now + 3600 && persistent_state_desc_->start_time < now - 6 * 3600; + } }; } // namespace validator diff --git a/validator/full-node-master.cpp b/validator/full-node-master.cpp index f26b11984..da49f0e2e 100644 --- a/validator/full-node-master.cpp +++ b/validator/full-node-master.cpp @@ -371,7 +371,8 @@ void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNo void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getCapabilities &query, td::Promise promise) { - promise.set_value(create_serialize_tl_object(proto_version(), proto_capabilities())); + promise.set_value( + create_serialize_tl_object(proto_version_major(), proto_version_minor(), 0)); } void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, @@ -385,7 +386,7 @@ void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNo } }); td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_id, query.masterchain_seqno_, - std::move(P)); + ShardIdFull{masterchainId}, std::move(P)); } void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, diff --git a/validator/full-node-master.hpp b/validator/full-node-master.hpp index 5e9d22e18..ce0aedd35 100644 --- a/validator/full-node-master.hpp +++ b/validator/full-node-master.hpp @@ -28,10 +28,10 @@ namespace fullnode { class FullNodeMasterImpl : public FullNodeMaster { public: - static constexpr td::uint32 proto_version() { + static constexpr td::uint32 proto_version_major() { return 1; } - static constexpr td::uint64 proto_capabilities() { + static constexpr td::uint32 proto_version_minor() { return 0; } void start_up() override; diff --git a/validator/full-node-shard.cpp b/validator/full-node-shard.cpp index fbdbbfd7a..9c1ba3c35 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -20,6 +20,7 @@ #include "checksum.h" #include "overlays.h" #include "td/utils/SharedSlice.h" +#include "td/utils/overloaded.h" #include "full-node-shard.hpp" #include "full-node-shard-queries.hpp" #include "full-node-serializer.hpp" @@ -27,7 +28,6 @@ #include "td/utils/buffer.h" #include "ton/ton-shard.h" #include "ton/ton-tl.hpp" -#include "ton/ton-io.hpp" #include "adnl/utils.hpp" #include "net/download-block-new.hpp" @@ -41,6 +41,9 @@ #include "td/utils/Random.h" #include "common/delay.h" +#include "td/utils/JsonBuilder.h" +#include "tl/tl_json.h" +#include "auto/tl/ton_api_json.h" namespace ton { @@ -50,9 +53,10 @@ namespace fullnode { Neighbour Neighbour::zero = Neighbour{adnl::AdnlNodeIdShort::zero()}; -void Neighbour::update_proto_version(const ton_api::tonNode_capabilities &q) { - proto_version = q.version_; - capabilities = q.capabilities_; +void Neighbour::update_proto_version(ton_api::tonNode_capabilities &q) { + version_major = q.version_major_; + version_minor = q.version_minor_; + flags = q.flags_; } void Neighbour::query_success(double t) { @@ -74,8 +78,9 @@ void Neighbour::update_roundtrip(double t) { void FullNodeShardImpl::create_overlay() { class Callback : public overlay::Overlays::Callback { public: - void receive_message(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id, td::BufferSlice data) override { - // just ignore + void receive_message(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id, + td::BufferSlice data) override { + td::actor::send_closure(node_, &FullNodeShardImpl::receive_message, src, std::move(data)); } void receive_query(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id, td::BufferSlice data, td::Promise promise) override { @@ -88,15 +93,22 @@ void FullNodeShardImpl::create_overlay() { td::Promise promise) override { td::actor::send_closure(node_, &FullNodeShardImpl::check_broadcast, src, std::move(data), std::move(promise)); } + void get_stats_extra(td::Promise promise) override { + td::actor::send_closure(node_, &FullNodeShardImpl::get_stats_extra, std::move(promise)); + } Callback(td::actor::ActorId node) : node_(node) { } private: td::actor::ActorId node_; }; - - td::actor::send_closure(overlays_, &overlay::Overlays::create_public_overlay, adnl_id_, overlay_id_full_.clone(), - std::make_unique(actor_id(this)), rules_, PSTRING() << "{ \"type\": \"shard\", \"shard_id\": " << get_shard() << ", \"workchain_id\": " << get_workchain() << " }"); + overlay::OverlayOptions opts; + opts.announce_self_ = active_; + td::actor::send_closure(overlays_, &overlay::Overlays::create_public_overlay_ex, adnl_id_, overlay_id_full_.clone(), + std::make_unique(actor_id(this)), rules_, + PSTRING() << "{ \"type\": \"shard\", \"shard_id\": " << get_shard() + << ", \"workchain_id\": " << get_workchain() << " }", + opts); td::actor::send_closure(rldp_, &rldp::Rldp::add_id, adnl_id_); td::actor::send_closure(rldp2_, &rldp2::Rldp::add_id, adnl_id_); @@ -106,6 +118,9 @@ void FullNodeShardImpl::create_overlay() { } void FullNodeShardImpl::check_broadcast(PublicKeyHash src, td::BufferSlice broadcast, td::Promise promise) { + if (!active_) { + return promise.set_error(td::Status::Error("cannot check broadcast: shard is not active")); + } auto B = fetch_tl_object(std::move(broadcast), true); if (B.is_error()) { return promise.set_error(B.move_as_error_prefix("failed to parse external message broadcast: ")); @@ -134,6 +149,10 @@ void FullNodeShardImpl::check_broadcast(PublicKeyHash src, td::BufferSlice broad promise.wrap([](td::Ref) { return td::Unit(); })); } +void FullNodeShardImpl::remove_neighbour(adnl::AdnlNodeIdShort id) { + neighbours_.erase(id); +} + void FullNodeShardImpl::update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promise promise) { td::actor::send_closure(overlays_, &ton::overlay::Overlays::delete_overlay, adnl_id_, overlay_id_); adnl_id_ = adnl_id; @@ -141,6 +160,18 @@ void FullNodeShardImpl::update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promis create_overlay(); } +void FullNodeShardImpl::set_active(bool active) { + if (shard_.is_masterchain()) { + return; + } + if (active_ == active) { + return; + } + active_ = active; + td::actor::send_closure(overlays_, &ton::overlay::Overlays::delete_overlay, adnl_id_, overlay_id_); + create_overlay(); +} + void FullNodeShardImpl::try_get_next_block(td::Timestamp timeout, td::Promise promise) { if (timeout.is_in_past()) { promise.set_error(td::Status::Error(ErrorCode::timeout, "timeout")); @@ -148,7 +179,7 @@ void FullNodeShardImpl::try_get_next_block(td::Timestamp timeout, td::Promise= 1) { + if (!b.adnl_id.is_zero() && b.version_major >= 1) { VLOG(FULL_NODE_DEBUG) << "using new download method with adnlid=" << b.adnl_id; td::actor::create_actor("downloadnext", adnl_id_, overlay_id_, handle_->id(), b.adnl_id, download_next_priority(), timeout, validator_manager_, rldp_, overlays_, @@ -187,7 +218,6 @@ void FullNodeShardImpl::got_next_block(td::Result R) { } void FullNodeShardImpl::get_next_block() { - //return; attempt_++; auto P = td::PromiseCreator::lambda([validator_manager = validator_manager_, attempt = attempt_, block_id = handle_->id(), SelfId = actor_id(this)](td::Result R) { @@ -591,7 +621,8 @@ void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNod void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getCapabilities &query, td::Promise promise) { VLOG(FULL_NODE_DEBUG) << "Got query getCapabilities from " << src; - promise.set_value(create_serialize_tl_object(proto_version(), proto_capabilities())); + promise.set_value( + create_serialize_tl_object(proto_version_major(), proto_version_minor(), 0)); } void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, @@ -606,7 +637,24 @@ void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNod }); VLOG(FULL_NODE_DEBUG) << "Got query getArchiveInfo " << query.masterchain_seqno_ << " from " << src; td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_id, query.masterchain_seqno_, - std::move(P)); + ShardIdFull{masterchainId}, std::move(P)); +} + +void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getShardArchiveInfo &query, + td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_serialize_tl_object()); + } else { + promise.set_value(create_serialize_tl_object(R.move_as_ok())); + } + }); + ShardIdFull shard_prefix = create_shard_id(query.shard_prefix_); + VLOG(FULL_NODE_DEBUG) << "Got query getShardArchiveInfo " << query.masterchain_seqno_ << " " << shard_prefix.to_str() + << " from " << src; + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_id, query.masterchain_seqno_, + shard_prefix, std::move(P)); } void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, @@ -623,6 +671,12 @@ void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNod void FullNodeShardImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise) { + if (!active_) { + td::actor::send_closure(overlays_, &overlay::Overlays::send_message, src, adnl_id_, overlay_id_, + create_serialize_tl_object()); + promise.set_error(td::Status::Error("shard is inactive")); + return; + } auto B = fetch_tl_object(std::move(query), true); if (B.is_error()) { promise.set_error(td::Status::Error(ErrorCode::protoviolation, "cannot parse tonnode query")); @@ -631,6 +685,16 @@ void FullNodeShardImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice ton_api::downcast_call(*B.move_as_ok().get(), [&](auto &obj) { this->process_query(src, obj, std::move(promise)); }); } +void FullNodeShardImpl::receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data) { + auto B = fetch_tl_object(std::move(data), true); + if (B.is_error()) { + return; + } + VLOG(FULL_NODE_DEBUG) << "Got tonNode.forgetPeer from " << src; + neighbours_.erase(src); + td::actor::send_closure(overlays_, &overlay::Overlays::forget_peer, adnl_id_, overlay_id_, src); +} + void FullNodeShardImpl::process_broadcast(PublicKeyHash src, ton_api::tonNode_ihrMessageBroadcast &query) { td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::new_ihr_message, std::move(query.message_->data_)); @@ -691,11 +755,19 @@ void FullNodeShardImpl::process_block_broadcast(PublicKeyHash src, ton_api::tonN LOG(DEBUG) << "dropped broadcast: " << B.move_as_error(); return; } + //if (!shard_is_ancestor(shard_, block_id.shard_full())) { + // LOG(FULL_NODE_WARNING) << "dropping block broadcast: shard mismatch. overlay=" << shard_.to_str() + // << " block=" << block_id.to_str(); + // return; + //} VLOG(FULL_NODE_DEBUG) << "Received block broadcast from " << src << ": " << B.ok().block_id.to_str(); td::actor::send_closure(full_node_, &FullNode::process_block_broadcast, B.move_as_ok()); } void FullNodeShardImpl::receive_broadcast(PublicKeyHash src, td::BufferSlice broadcast) { + if (!active_) { + return; + } auto B = fetch_tl_object(std::move(broadcast), true); if (B.is_error()) { return; @@ -804,7 +876,7 @@ void FullNodeShardImpl::send_broadcast(BlockBroadcast broadcast) { void FullNodeShardImpl::download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) { auto &b = choose_neighbour(); - if (!b.adnl_id.is_zero() && b.proto_version >= 1) { + if (!b.adnl_id.is_zero() && b.version_major >= 1) { VLOG(FULL_NODE_DEBUG) << "new block download"; td::actor::create_actor("downloadreq", id, adnl_id_, overlay_id_, b.adnl_id, priority, timeout, validator_manager_, rldp_, overlays_, adnl_, client_, @@ -863,12 +935,12 @@ void FullNodeShardImpl::get_next_key_blocks(BlockIdExt block_id, td::Timestamp t .release(); } -void FullNodeShardImpl::download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise) { +void FullNodeShardImpl::download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) { auto &b = choose_neighbour(); - td::actor::create_actor("archive", masterchain_seqno, std::move(tmp_dir), adnl_id_, overlay_id_, - b.adnl_id, timeout, validator_manager_, rldp2_, overlays_, adnl_, - client_, create_neighbour_promise(b, std::move(promise))) + td::actor::create_actor( + "archive", masterchain_seqno, shard_prefix, std::move(tmp_dir), adnl_id_, overlay_id_, b.adnl_id, timeout, + validator_manager_, rldp2_, overlays_, adnl_, client_, create_neighbour_promise(b, std::move(promise))) .release(); } @@ -936,6 +1008,10 @@ void FullNodeShardImpl::start_up() { } } +void FullNodeShardImpl::tear_down() { + td::actor::send_closure(overlays_, &ton::overlay::Overlays::delete_overlay, adnl_id_, overlay_id_); +} + void FullNodeShardImpl::sign_new_certificate(PublicKeyHash sign_by) { if (sign_by.is_zero()) { return; @@ -1083,15 +1159,19 @@ const Neighbour &FullNodeShardImpl::choose_neighbour() const { return Neighbour::zero; } + double min_unreliability = 1e9; + for (auto &x : neighbours_) { + min_unreliability = std::min(min_unreliability, x.second.unreliability); + } const Neighbour *best = nullptr; td::uint32 sum = 0; for (auto &x : neighbours_) { - td::uint32 unr = static_cast(x.second.unreliability); + auto unr = static_cast(x.second.unreliability - min_unreliability); - if (x.second.proto_version < proto_version()) { + if (x.second.version_major < proto_version_major()) { unr += 4; - } else if (x.second.proto_version == proto_version() && x.second.capabilities < proto_capabilities()) { + } else if (x.second.version_major == proto_version_major() && x.second.version_minor < proto_version_minor()) { unr += 2; } @@ -1105,7 +1185,10 @@ const Neighbour &FullNodeShardImpl::choose_neighbour() const { } } } - return best ? *best : Neighbour::zero; + if (best) { + return *best; + } + return Neighbour::zero; } void FullNodeShardImpl::update_neighbour_stats(adnl::AdnlNodeIdShort adnl_id, double t, bool success) { @@ -1128,7 +1211,7 @@ void FullNodeShardImpl::got_neighbour_capabilities(adnl::AdnlNodeIdShort adnl_id if (F.is_error()) { it->second.query_failed(); } else { - it->second.update_proto_version(*F.move_as_ok().get()); + it->second.update_proto_version(*F.ok()); it->second.query_success(t); } } @@ -1157,7 +1240,7 @@ void FullNodeShardImpl::ping_neighbours() { td::Time::now() - start_time, R.move_as_ok()); } }); - auto q = create_serialize_tl_object(); + td::BufferSlice q = create_serialize_tl_object(); td::actor::send_closure(overlays_, &overlay::Overlays::send_query, it->first, adnl_id_, overlay_id_, "get_prepare_block", std::move(P), td::Timestamp::in(1.0), std::move(q)); @@ -1167,6 +1250,24 @@ void FullNodeShardImpl::ping_neighbours() { } } +void FullNodeShardImpl::get_stats_extra(td::Promise promise) { + auto res = create_tl_object(); + res->shard_ = shard_.to_str(); + res->active_ = active_; + for (const auto &p : neighbours_) { + const auto &n = p.second; + auto f = create_tl_object(); + f->id_ = n.adnl_id.bits256_value().to_hex(); + f->verison_major_ = n.version_major; + f->version_minor_ = n.version_minor; + f->flags_ = n.flags; + f->roundtrip_ = n.roundtrip; + f->unreliability_ = n.unreliability; + res->neighbours_.push_back(std::move(f)); + } + promise.set_result(td::json_encode(td::ToJson(*res), true)); +} + FullNodeShardImpl::FullNodeShardImpl(ShardIdFull shard, PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id, FileHash zero_state_file_hash, FullNodeConfig config, td::actor::ActorId keyring, td::actor::ActorId adnl, @@ -1174,7 +1275,7 @@ FullNodeShardImpl::FullNodeShardImpl(ShardIdFull shard, PublicKeyHash local_id, td::actor::ActorId overlays, td::actor::ActorId validator_manager, td::actor::ActorId client, - td::actor::ActorId full_node) + td::actor::ActorId full_node, bool active) : shard_(shard) , local_id_(local_id) , adnl_id_(adnl_id) @@ -1187,6 +1288,7 @@ FullNodeShardImpl::FullNodeShardImpl(ShardIdFull shard, PublicKeyHash local_id, , validator_manager_(validator_manager) , client_(client) , full_node_(full_node) + , active_(active) , config_(config) { } @@ -1195,10 +1297,10 @@ td::actor::ActorOwn FullNodeShard::create( FullNodeConfig config, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId overlays, td::actor::ActorId validator_manager, - td::actor::ActorId client, td::actor::ActorId full_node) { - return td::actor::create_actor("tonnode", shard, local_id, adnl_id, zero_state_file_hash, config, - keyring, adnl, rldp, rldp2, overlays, validator_manager, client, - full_node); + td::actor::ActorId client, td::actor::ActorId full_node, bool active) { + return td::actor::create_actor(PSTRING() << "tonnode" << shard.to_str(), shard, local_id, adnl_id, + zero_state_file_hash, config, keyring, adnl, rldp, rldp2, overlays, + validator_manager, client, full_node, active); } } // namespace fullnode diff --git a/validator/full-node-shard.h b/validator/full-node-shard.h index e89031fe9..f8f500f39 100644 --- a/validator/full-node-shard.h +++ b/validator/full-node-shard.h @@ -36,6 +36,7 @@ class FullNodeShard : public td::actor::Actor { virtual ShardIdFull get_shard_full() const = 0; virtual void update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promise promise) = 0; + virtual void set_active(bool active) = 0; virtual void set_config(FullNodeConfig config) = 0; virtual void send_ihr_message(td::BufferSlice data) = 0; @@ -45,9 +46,10 @@ class FullNodeShard : public td::actor::Actor { td::BufferSlice data) = 0; virtual void send_broadcast(BlockBroadcast broadcast) = 0; - virtual void sign_overlay_certificate(PublicKeyHash signed_key, td::uint32 expiry_at, td::uint32 max_size, td::Promise promise) = 0; - virtual void import_overlay_certificate(PublicKeyHash signed_key, std::shared_ptr cert, td::Promise promise) = 0; - + virtual void sign_overlay_certificate(PublicKeyHash signed_key, td::uint32 expiry_at, td::uint32 max_size, + td::Promise promise) = 0; + virtual void import_overlay_certificate(PublicKeyHash signed_key, std::shared_ptr cert, + td::Promise promise) = 0; virtual void download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) = 0; @@ -62,8 +64,8 @@ class FullNodeShard : public td::actor::Actor { td::Promise promise) = 0; virtual void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) = 0; - virtual void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise) = 0; + virtual void download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) = 0; virtual void set_handle(BlockHandle handle, td::Promise promise) = 0; @@ -74,7 +76,7 @@ class FullNodeShard : public td::actor::Actor { FullNodeConfig config, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId overlays, td::actor::ActorId validator_manager, - td::actor::ActorId client, td::actor::ActorId full_node); + td::actor::ActorId client, td::actor::ActorId full_node, bool active); }; } // namespace fullnode diff --git a/validator/full-node-shard.hpp b/validator/full-node-shard.hpp index a7cf89ac5..0ae26a7c0 100644 --- a/validator/full-node-shard.hpp +++ b/validator/full-node-shard.hpp @@ -32,16 +32,17 @@ namespace fullnode { struct Neighbour { adnl::AdnlNodeIdShort adnl_id; - td::uint32 proto_version = 0; - td::uint64 capabilities = 0; + td::uint32 version_major = 0; + td::uint32 version_minor = 0; + td::uint32 flags = 0; double roundtrip = 0; double roundtrip_relax_at = 0; double roundtrip_weight = 0; double unreliability = 0; - Neighbour(adnl::AdnlNodeIdShort adnl_id) : adnl_id(std::move(adnl_id)) { + explicit Neighbour(adnl::AdnlNodeIdShort adnl_id) : adnl_id(std::move(adnl_id)) { } - void update_proto_version(const ton_api::tonNode_capabilities &q); + void update_proto_version(ton_api::tonNode_capabilities &q); void query_success(double t); void query_failed(); void update_roundtrip(double t); @@ -64,12 +65,12 @@ class FullNodeShardImpl : public FullNodeShard { static constexpr td::uint32 download_next_priority() { return 1; } - static constexpr td::uint32 proto_version() { - return 2; - } - static constexpr td::uint64 proto_capabilities() { + static constexpr td::uint32 proto_version_major() { return 3; } + static constexpr td::uint32 proto_version_minor() { + return 0; + } static constexpr td::uint32 max_neighbours() { return 16; } @@ -82,14 +83,12 @@ class FullNodeShardImpl : public FullNodeShard { void create_overlay(); void update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promise promise) override; + void set_active(bool active) override; void set_config(FullNodeConfig config) override { config_ = config; } - //td::Result fetch_block(td::BufferSlice data); - void prevalidate_block(BlockIdExt block_id, td::BufferSlice data, td::BufferSlice proof, - td::Promise promise); void try_get_next_block(td::Timestamp timestamp, td::Promise promise); void got_next_block(td::Result block); void get_next_block(); @@ -136,11 +135,14 @@ class FullNodeShardImpl : public FullNodeShard { td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getShardArchiveInfo &query, + td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, td::Promise promise); // void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_prepareNextKeyBlockProof &query, // td::Promise promise); void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise); + void receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data); void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockBroadcast &query); void process_broadcast(PublicKeyHash src, ton_api::tonNode_blockBroadcastCompressed &query); @@ -156,6 +158,8 @@ class FullNodeShardImpl : public FullNodeShard { void receive_broadcast(PublicKeyHash src, td::BufferSlice query); void check_broadcast(PublicKeyHash src, td::BufferSlice query, td::Promise promise); + void get_stats_extra(td::Promise promise); + void remove_neighbour(adnl::AdnlNodeIdShort id); void send_ihr_message(td::BufferSlice data) override; void send_external_message(td::BufferSlice data) override; @@ -177,12 +181,13 @@ class FullNodeShardImpl : public FullNodeShard { td::Promise promise) override; void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) override; - void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise) override; + void download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override; void set_handle(BlockHandle handle, td::Promise promise) override; void start_up() override; + void tear_down() override; void alarm() override; void update_validators(std::vector public_key_hashes, PublicKeyHash local_hash) override; @@ -218,7 +223,8 @@ class FullNodeShardImpl : public FullNodeShard { td::actor::ActorId adnl, td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId overlays, td::actor::ActorId validator_manager, - td::actor::ActorId client, td::actor::ActorId full_node); + td::actor::ActorId client, td::actor::ActorId full_node, + bool active); private: bool use_new_download() const { @@ -258,6 +264,8 @@ class FullNodeShardImpl : public FullNodeShard { td::Timestamp ping_neighbours_at_; adnl::AdnlNodeIdShort last_pinged_neighbour_ = adnl::AdnlNodeIdShort::zero(); + bool active_; + FullNodeConfig config_; std::set my_ext_msg_broadcasts_; diff --git a/validator/full-node.cpp b/validator/full-node.cpp index 350606ca9..c1173e445 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -17,10 +17,12 @@ Copyright 2017-2020 Telegram Systems LLP */ #include "full-node.hpp" -#include "ton/ton-shard.h" #include "ton/ton-io.hpp" #include "td/actor/MultiPromise.h" #include "full-node.h" +#include "common/delay.h" +#include "td/utils/Random.h" +#include "ton/ton-tl.hpp" namespace ton { @@ -28,6 +30,8 @@ namespace validator { namespace fullnode { +static const double INACTIVE_SHARD_TTL = (double)overlay::Overlays::overlay_peer_ttl() + 60.0; + void FullNodeImpl::add_permanent_key(PublicKeyHash key, td::Promise promise) { if (local_keys_.count(key)) { promise.set_value(td::Unit()); @@ -52,7 +56,9 @@ void FullNodeImpl::add_permanent_key(PublicKeyHash key, td::Promise pr } for (auto &shard : shards_) { - td::actor::send_closure(shard.second, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + if (!shard.second.actor.empty()) { + td::actor::send_closure(shard.second.actor, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + } } promise.set_value(td::Unit()); } @@ -82,30 +88,34 @@ void FullNodeImpl::del_permanent_key(PublicKeyHash key, td::Promise pr } for (auto &shard : shards_) { - td::actor::send_closure(shard.second, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + if (!shard.second.actor.empty()) { + td::actor::send_closure(shard.second.actor, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + } } promise.set_value(td::Unit()); } -void FullNodeImpl::sign_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, - td::uint32 expiry_at, td::uint32 max_size, - td::Promise promise) { - auto it = shards_.find(shard_id); - if(it == shards_.end()) { - promise.set_error(td::Status::Error(ErrorCode::error, "shard not found")); - return; - } - td::actor::send_closure(it->second, &FullNodeShard::sign_overlay_certificate, signed_key, expiry_at, max_size, std::move(promise)); +void FullNodeImpl::sign_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, td::uint32 expiry_at, + td::uint32 max_size, td::Promise promise) { + auto it = shards_.find(shard_id); + if(it == shards_.end() || it->second.actor.empty()) { + promise.set_error(td::Status::Error(ErrorCode::error, "shard not found")); + return; + } + td::actor::send_closure(it->second.actor, &FullNodeShard::sign_overlay_certificate, signed_key, expiry_at, max_size, + std::move(promise)); } void FullNodeImpl::import_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, std::shared_ptr cert, td::Promise promise) { - auto it = shards_.find(shard_id); - if(it == shards_.end()) { - promise.set_error(td::Status::Error(ErrorCode::error, "shard not found")); - } - td::actor::send_closure(it->second, &FullNodeShard::import_overlay_certificate, signed_key, cert, std::move(promise)); + auto it = shards_.find(shard_id); + if(it == shards_.end() || it->second.actor.empty()) { + promise.set_error(td::Status::Error(ErrorCode::error, "shard not found")); + return; + } + td::actor::send_closure(it->second.actor, &FullNodeShard::import_overlay_certificate, signed_key, cert, + std::move(promise)); } void FullNodeImpl::update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promise promise) { @@ -116,7 +126,9 @@ void FullNodeImpl::update_adnl_id(adnl::AdnlNodeIdShort adnl_id, td::Promisesecond, &FullNodeShard::set_handle, top_handle, std::move(P)); -} - -void FullNodeImpl::add_shard(ShardIdFull shard) { - while (true) { - if (shards_.count(shard) == 0) { - shards_.emplace(shard, - FullNodeShard::create(shard, local_id_, adnl_id_, zero_state_file_hash_, config_, keyring_, adnl_, - rldp_, rldp2_, overlays_, validator_manager_, client_, actor_id(this))); - if (all_validators_.size() > 0) { - td::actor::send_closure(shards_[shard], &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + CHECK(it != shards_.end() && !it->second.actor.empty()); + td::actor::send_closure(it->second.actor, &FullNodeShard::set_handle, top_handle, std::move(P)); +} + +void FullNodeImpl::on_new_masterchain_block(td::Ref state, std::set shards_to_monitor) { + CHECK(shards_to_monitor.count(ShardIdFull(masterchainId))); + bool join_all_overlays = !sign_cert_by_.is_zero(); + std::set all_shards; + std::set new_active; + all_shards.insert(ShardIdFull(masterchainId)); + std::set workchains; + wc_monitor_min_split_ = state->monitor_min_split_depth(basechainId); + auto cut_shard = [&](ShardIdFull shard) -> ShardIdFull { + return wc_monitor_min_split_ < shard.pfx_len() ? shard_prefix(shard, wc_monitor_min_split_) : shard; + }; + for (auto &info : state->get_shards()) { + workchains.insert(info->shard().workchain); + ShardIdFull shard = cut_shard(info->shard()); + while (true) { + all_shards.insert(shard); + if (shard.pfx_len() == 0) { + break; } + shard = shard_parent(shard); + } + } + for (const auto &[wc, winfo] : state->get_workchain_list()) { + if (!workchains.contains(wc) && winfo->active && winfo->enabled_since <= state->get_unix_time()) { + all_shards.insert(ShardIdFull(wc)); + } + } + for (ShardIdFull shard : shards_to_monitor) { + shard = cut_shard(shard); + while (true) { + new_active.insert(shard); + if (shard.pfx_len() == 0) { + break; + } + shard = shard_parent(shard); + } + } + + for (auto it = shards_.begin(); it != shards_.end(); ) { + if (all_shards.contains(it->first)) { + ++it; } else { - break; + it = shards_.erase(it); } - if (shard.shard == shardIdAll) { - break; + } + for (ShardIdFull shard : all_shards) { + bool active = new_active.contains(shard); + bool overlay_exists = !shards_[shard].actor.empty(); + if (active || join_all_overlays || overlay_exists) { + update_shard_actor(shard, active); + } + } + + for (auto &[_, shard_info] : shards_) { + if (!shard_info.active && shard_info.delete_at && shard_info.delete_at.is_in_past() && !join_all_overlays) { + shard_info.actor = {}; + shard_info.delete_at = td::Timestamp::never(); } - shard = shard_parent(shard); } } -void FullNodeImpl::del_shard(ShardIdFull shard) { - LOG(FATAL) << "deleting shards not implemented: shard=" << shard; - shards_.erase(shard); +void FullNodeImpl::update_shard_actor(ShardIdFull shard, bool active) { + ShardInfo &info = shards_[shard]; + if (info.actor.empty()) { + info.actor = FullNodeShard::create(shard, local_id_, adnl_id_, zero_state_file_hash_, config_, keyring_, adnl_, rldp_, + rldp2_, overlays_, validator_manager_, client_, actor_id(this), active); + if (!all_validators_.empty()) { + td::actor::send_closure(info.actor, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + } + } else if (info.active != active) { + td::actor::send_closure(info.actor, &FullNodeShard::set_active, active); + } + info.active = active; + info.delete_at = active ? td::Timestamp::never() : td::Timestamp::in(INACTIVE_SHARD_TTL); } void FullNodeImpl::sync_completed() { @@ -206,7 +272,7 @@ void FullNodeImpl::sync_completed() { } void FullNodeImpl::send_ihr_message(AccountIdPrefixFull dst, td::BufferSlice data) { - auto shard = get_shard(ShardIdFull{masterchainId}); + auto shard = get_shard(dst); if (shard.empty()) { VLOG(FULL_NODE_WARNING) << "dropping OUT ihr message to unknown shard"; return; @@ -220,11 +286,12 @@ void FullNodeImpl::send_ext_message(AccountIdPrefixFull dst, td::BufferSlice dat VLOG(FULL_NODE_WARNING) << "dropping OUT ext message to unknown shard"; return; } - for (auto &private_overlay : custom_overlays_) { - for (auto &actor : private_overlay.second.actors_) { - auto local_id = actor.first; - if (private_overlay.second.params_.msg_senders_.count(local_id)) { - td::actor::send_closure(actor.second, &FullNodeCustomOverlay::send_external_message, data.clone()); + for (auto &[_, private_overlay] : custom_overlays_) { + if (private_overlay.params_.send_shard(dst.as_leaf_shard())) { + for (auto &[local_id, actor] : private_overlay.actors_) { + if (private_overlay.params_.msg_senders_.contains(local_id)) { + td::actor::send_closure(actor, &FullNodeCustomOverlay::send_external_message, data.clone()); + } } } } @@ -232,7 +299,7 @@ void FullNodeImpl::send_ext_message(AccountIdPrefixFull dst, td::BufferSlice dat } void FullNodeImpl::send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) { - auto shard = get_shard(ShardIdFull{masterchainId, shardIdAll}); + auto shard = get_shard(ShardIdFull{masterchainId}); if (shard.empty()) { VLOG(FULL_NODE_WARNING) << "dropping OUT shard block info message to unknown shard"; return; @@ -266,14 +333,16 @@ void FullNodeImpl::send_broadcast(BlockBroadcast broadcast, int mode) { if (mode & broadcast_mode_custom) { send_block_broadcast_to_custom_overlays(broadcast); } - auto shard = get_shard(ShardIdFull{masterchainId}); + auto shard = get_shard(broadcast.block_id.shard_full()); if (shard.empty()) { VLOG(FULL_NODE_WARNING) << "dropping OUT broadcast to unknown shard"; return; } - if (!private_block_overlays_.empty() && (mode & broadcast_mode_private_block)) { - td::actor::send_closure(private_block_overlays_.begin()->second, &FullNodePrivateBlockOverlay::send_broadcast, - broadcast.clone()); + if (mode & broadcast_mode_private_block) { + if (!private_block_overlays_.empty()) { + td::actor::send_closure(private_block_overlays_.begin()->second, &FullNodePrivateBlockOverlay::send_broadcast, + broadcast.clone()); + } } if (mode & broadcast_mode_public) { td::actor::send_closure(shard, &FullNodeShard::send_broadcast, std::move(broadcast)); @@ -348,27 +417,43 @@ void FullNodeImpl::get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeou td::actor::send_closure(shard, &FullNodeShard::get_next_key_blocks, block_id, timeout, std::move(promise)); } -void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise) { - auto shard = get_shard(ShardIdFull{masterchainId}); +void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) { + auto shard = get_shard(shard_prefix); + if (shard.empty()) { + VLOG(FULL_NODE_WARNING) << "dropping download archive query to unknown shard"; + promise.set_error(td::Status::Error(ErrorCode::notready, "shard not ready")); + return; + } CHECK(!shard.empty()); - td::actor::send_closure(shard, &FullNodeShard::download_archive, masterchain_seqno, std::move(tmp_dir), timeout, - std::move(promise)); + td::actor::send_closure(shard, &FullNodeShard::download_archive, masterchain_seqno, shard_prefix, std::move(tmp_dir), + timeout, std::move(promise)); } td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard) { - add_shard(ShardIdFull{shard.workchain, shardIdAll}); - while (shards_.count(shard) == 0) { - if (shard.shard == shardIdAll) { - return td::actor::ActorId{}; - } - shard = shard_parent(shard); + if (shard.is_masterchain()) { + return shards_[ShardIdFull{masterchainId}].actor.get(); } - return shards_[shard].get(); + if (shard.workchain != basechainId) { + return {}; + } + int pfx_len = shard.pfx_len(); + 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(); + } + + // Special case if shards_ was not yet initialized. + // This can happen briefly on node startup. + return shards_[ShardIdFull{masterchainId}].actor.get(); } td::actor::ActorId FullNodeImpl::get_shard(AccountIdPrefixFull dst) { - return get_shard(shard_prefix(dst, 60)); + return get_shard(shard_prefix(dst, max_shard_pfx_len)); } void FullNodeImpl::got_key_block_config(td::Ref config) { @@ -407,7 +492,9 @@ void FullNodeImpl::got_key_block_config(td::Ref config) { CHECK(all_validators_.size() > 0); for (auto &shard : shards_) { - td::actor::send_closure(shard.second, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + if (!shard.second.actor.empty()) { + td::actor::send_closure(shard.second.actor, &FullNodeShard::update_validators, all_validators_, sign_cert_by_); + } } } @@ -489,9 +576,9 @@ void FullNodeImpl::update_validator_telemetry_collector() { } void FullNodeImpl::start_up() { - add_shard(ShardIdFull{masterchainId}); + update_shard_actor(ShardIdFull{masterchainId}, true); if (local_id_.is_zero()) { - if(adnl_id_.is_zero()) { + if (adnl_id_.is_zero()) { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; local_id_ = pk.compute_short_id(); @@ -505,11 +592,9 @@ void FullNodeImpl::start_up() { void initial_read_complete(BlockHandle handle) override { td::actor::send_closure(id_, &FullNodeImpl::initial_read_complete, handle); } - void add_shard(ShardIdFull shard) override { - td::actor::send_closure(id_, &FullNodeImpl::add_shard, shard); - } - void del_shard(ShardIdFull shard) override { - td::actor::send_closure(id_, &FullNodeImpl::del_shard, shard); + void on_new_masterchain_block(td::Ref state, std::set shards_to_monitor) override { + td::actor::send_closure(id_, &FullNodeImpl::on_new_masterchain_block, std::move(state), + std::move(shards_to_monitor)); } void send_ihr_message(AccountIdPrefixFull dst, td::BufferSlice data) override { td::actor::send_closure(id_, &FullNodeImpl::send_ihr_message, dst, std::move(data)); @@ -555,10 +640,10 @@ void FullNodeImpl::start_up() { td::Promise> promise) override { td::actor::send_closure(id_, &FullNodeImpl::get_next_key_blocks, block_id, timeout, std::move(promise)); } - void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise) override { - td::actor::send_closure(id_, &FullNodeImpl::download_archive, masterchain_seqno, std::move(tmp_dir), timeout, - std::move(promise)); + void download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override { + td::actor::send_closure(id_, &FullNodeImpl::download_archive, masterchain_seqno, shard_prefix, std::move(tmp_dir), + timeout, std::move(promise)); } void new_key_block(BlockHandle handle) override { @@ -568,16 +653,15 @@ void FullNodeImpl::start_up() { td::actor::send_closure(id_, &FullNodeImpl::send_validator_telemetry, key, std::move(telemetry)); } - Callback(td::actor::ActorId id) : id_(id) { + explicit Callback(td::actor::ActorId id) : id_(id) { } private: td::actor::ActorId id_; }; - auto P = td::PromiseCreator::lambda([](td::Unit R) {}); td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::install_callback, - std::make_unique(actor_id(this)), std::move(P)); + std::make_unique(actor_id(this)), std::move(started_promise_)); } void FullNodeImpl::update_private_overlays() { @@ -635,7 +719,7 @@ void FullNodeImpl::update_custom_overlay(CustomOverlayInfo &overlay) { } } -void FullNodeImpl::send_block_broadcast_to_custom_overlays(const BlockBroadcast& broadcast) { +void FullNodeImpl::send_block_broadcast_to_custom_overlays(const BlockBroadcast &broadcast) { if (!custom_overlays_sent_broadcasts_.insert(broadcast.block_id).second) { return; } @@ -644,11 +728,12 @@ void FullNodeImpl::send_block_broadcast_to_custom_overlays(const BlockBroadcast& custom_overlays_sent_broadcasts_.erase(custom_overlays_sent_broadcasts_lru_.front()); custom_overlays_sent_broadcasts_lru_.pop(); } - for (auto &private_overlay : custom_overlays_) { - for (auto &actor : private_overlay.second.actors_) { - auto local_id = actor.first; - if (private_overlay.second.params_.block_senders_.count(local_id)) { - td::actor::send_closure(actor.second, &FullNodeCustomOverlay::send_broadcast, broadcast.clone()); + for (auto &[_, private_overlay] : custom_overlays_) { + if (private_overlay.params_.send_shard(broadcast.block_id.shard_full())) { + for (auto &[local_id, actor] : private_overlay.actors_) { + if (private_overlay.params_.block_senders_.contains(local_id)) { + td::actor::send_closure(actor, &FullNodeCustomOverlay::send_broadcast, broadcast.clone()); + } } } } @@ -666,12 +751,13 @@ void FullNodeImpl::send_block_candidate_broadcast_to_custom_overlays(const Block custom_overlays_sent_broadcasts_.erase(custom_overlays_sent_broadcasts_lru_.front()); custom_overlays_sent_broadcasts_lru_.pop(); } - for (auto &private_overlay : custom_overlays_) { - for (auto &actor : private_overlay.second.actors_) { - auto local_id = actor.first; - if (private_overlay.second.params_.block_senders_.count(local_id)) { - td::actor::send_closure(actor.second, &FullNodeCustomOverlay::send_block_candidate, block_id, cc_seqno, - validator_set_hash, data.clone()); + for (auto &[_, private_overlay] : custom_overlays_) { + if (private_overlay.params_.send_shard(block_id.shard_full())) { + for (auto &[local_id, actor] : private_overlay.actors_) { + if (private_overlay.params_.block_senders_.contains(local_id)) { + td::actor::send_closure(actor, &FullNodeCustomOverlay::send_block_candidate, block_id, cc_seqno, + validator_set_hash, data.clone()); + } } } } @@ -683,7 +769,8 @@ FullNodeImpl::FullNodeImpl(PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id td::actor::ActorId rldp2, td::actor::ActorId dht, td::actor::ActorId overlays, td::actor::ActorId validator_manager, - td::actor::ActorId client, std::string db_root) + td::actor::ActorId client, std::string db_root, + td::Promise started_promise) : local_id_(local_id) , adnl_id_(adnl_id) , zero_state_file_hash_(zero_state_file_hash) @@ -696,19 +783,19 @@ FullNodeImpl::FullNodeImpl(PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id , validator_manager_(validator_manager) , client_(client) , db_root_(db_root) + , started_promise_(std::move(started_promise)) , config_(config) { } -td::actor::ActorOwn FullNode::create(ton::PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id, - FileHash zero_state_file_hash, FullNodeConfig config, - td::actor::ActorId keyring, - td::actor::ActorId adnl, td::actor::ActorId rldp, - td::actor::ActorId rldp2, td::actor::ActorId dht, - td::actor::ActorId overlays, - td::actor::ActorId validator_manager, - td::actor::ActorId client, std::string db_root) { +td::actor::ActorOwn FullNode::create( + ton::PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id, FileHash zero_state_file_hash, FullNodeConfig config, + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId dht, + td::actor::ActorId overlays, td::actor::ActorId validator_manager, + td::actor::ActorId client, std::string db_root, td::Promise started_promise) { return td::actor::create_actor("fullnode", local_id, adnl_id, zero_state_file_hash, config, keyring, - adnl, rldp, rldp2, dht, overlays, validator_manager, client, db_root); + adnl, rldp, rldp2, dht, overlays, validator_manager, client, db_root, + std::move(started_promise)); } FullNodeConfig::FullNodeConfig(const tl_object_ptr &obj) @@ -725,18 +812,27 @@ bool FullNodeConfig::operator!=(const FullNodeConfig &rhs) const { return !(*this == rhs); } +bool CustomOverlayParams::send_shard(const ShardIdFull &shard) const { + return sender_shards_.empty() || + std::any_of(sender_shards_.begin(), sender_shards_.end(), + [&](const ShardIdFull &our_shard) { return shard_intersects(shard, our_shard); }); +} + CustomOverlayParams CustomOverlayParams::fetch(const ton_api::engine_validator_customOverlay& f) { CustomOverlayParams c; c.name_ = f.name_; for (const auto &node : f.nodes_) { c.nodes_.emplace_back(node->adnl_id_); if (node->msg_sender_) { - c.msg_senders_[ton::adnl::AdnlNodeIdShort{node->adnl_id_}] = node->msg_sender_priority_; + c.msg_senders_[adnl::AdnlNodeIdShort{node->adnl_id_}] = node->msg_sender_priority_; } if (node->block_sender_) { c.block_senders_.emplace(node->adnl_id_); } } + for (const auto &shard : f.sender_shards_) { + c.sender_shards_.push_back(create_shard_id(shard)); + } return c; } diff --git a/validator/full-node.h b/validator/full-node.h index 67e090c63..73ecbd720 100644 --- a/validator/full-node.h +++ b/validator/full-node.h @@ -60,7 +60,9 @@ struct CustomOverlayParams { std::vector nodes_; std::map msg_senders_; std::set block_senders_; + std::vector sender_shards_; + bool send_shard(const ShardIdFull& shard) const; static CustomOverlayParams fetch(const ton_api::engine_validator_customOverlay& f); }; @@ -103,14 +105,12 @@ class FullNode : public td::actor::Actor { } enum { broadcast_mode_public = 1, broadcast_mode_private_block = 2, broadcast_mode_custom = 4 }; - static td::actor::ActorOwn create(ton::PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id, - FileHash zero_state_file_hash, FullNodeConfig config, - td::actor::ActorId keyring, - td::actor::ActorId adnl, td::actor::ActorId rldp, - td::actor::ActorId rldp2, td::actor::ActorId dht, - td::actor::ActorId overlays, - td::actor::ActorId validator_manager, - td::actor::ActorId client, std::string db_root); + static td::actor::ActorOwn create( + ton::PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id, FileHash zero_state_file_hash, FullNodeConfig config, + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId dht, + td::actor::ActorId overlays, td::actor::ActorId validator_manager, + td::actor::ActorId client, std::string db_root, td::Promise started_promise); }; } // namespace fullnode diff --git a/validator/full-node.hpp b/validator/full-node.hpp index bf530f29e..b82dd473a 100644 --- a/validator/full-node.hpp +++ b/validator/full-node.hpp @@ -44,9 +44,8 @@ class FullNodeImpl : public FullNode { void add_permanent_key(PublicKeyHash key, td::Promise promise) override; void del_permanent_key(PublicKeyHash key, td::Promise promise) override; - void sign_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, - td::uint32 expiry_at, td::uint32 max_size, - td::Promise promise) override; + void sign_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, td::uint32 expiry_at, + td::uint32 max_size, td::Promise promise) override; void import_shard_overlay_certificate(ShardIdFull shard_id, PublicKeyHash signed_key, std::shared_ptr cert, td::Promise promise) override; @@ -57,8 +56,7 @@ class FullNodeImpl : public FullNode { void add_custom_overlay(CustomOverlayParams params, td::Promise promise) override; void del_custom_overlay(std::string name, td::Promise promise) override; - void add_shard(ShardIdFull shard); - void del_shard(ShardIdFull shard); + void on_new_masterchain_block(td::Ref state, std::set shards_to_monitor); void sync_completed(); @@ -79,8 +77,8 @@ class FullNodeImpl : public FullNode { void download_block_proof_link(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise); - void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise); + void download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise); void got_key_block_config(td::Ref config); void new_key_block(BlockHandle handle); @@ -99,17 +97,26 @@ class FullNodeImpl : public FullNode { td::actor::ActorId rldp, td::actor::ActorId rldp2, td::actor::ActorId dht, td::actor::ActorId overlays, td::actor::ActorId validator_manager, - td::actor::ActorId client, std::string db_root); + td::actor::ActorId client, std::string db_root, + td::Promise started_promise); private: + struct ShardInfo { + td::actor::ActorOwn actor; + bool active = false; + td::Timestamp delete_at = td::Timestamp::never(); + }; + + void update_shard_actor(ShardIdFull shard, bool active); + PublicKeyHash local_id_; adnl::AdnlNodeIdShort adnl_id_; FileHash zero_state_file_hash_; td::actor::ActorId get_shard(AccountIdPrefixFull dst); - td::actor::ActorId get_shard(ShardIdFull dst); - - std::map> shards_; + td::actor::ActorId get_shard(ShardIdFull shard); + std::map shards_; + int wc_monitor_min_split_ = 0; td::actor::ActorId keyring_; td::actor::ActorId adnl_; @@ -127,6 +134,8 @@ class FullNodeImpl : public FullNode { std::map current_validators_; std::set local_keys_; + + td::Promise started_promise_; FullNodeConfig config_; std::map> private_block_overlays_; diff --git a/validator/impl/CMakeLists.txt b/validator/impl/CMakeLists.txt index 9df6725d6..b8f2edfb2 100644 --- a/validator/impl/CMakeLists.txt +++ b/validator/impl/CMakeLists.txt @@ -40,8 +40,7 @@ set(TON_VALIDATOR_SOURCE signature-set.hpp top-shard-descr.hpp validate-query.hpp - validator-set.hpp -) + validator-set.hpp) add_library(ton_validator STATIC ${TON_VALIDATOR_SOURCE}) diff --git a/validator/impl/shard.hpp b/validator/impl/shard.hpp index d07d533e1..fa36e1e66 100644 --- a/validator/impl/shard.hpp +++ b/validator/impl/shard.hpp @@ -160,6 +160,9 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { return td::make_ref(config_); } } + block::WorkchainSet get_workchain_list() const override { + return config_ ? config_->get_workchain_list() : block::WorkchainSet(); + } private: ZeroStateIdExt zerostate_id_; diff --git a/validator/import-db-slice.cpp b/validator/import-db-slice.cpp index a93fb05be..06573d347 100644 --- a/validator/import-db-slice.cpp +++ b/validator/import-db-slice.cpp @@ -17,6 +17,7 @@ Copyright 2019-2020 Telegram Systems LLP */ #include "import-db-slice.hpp" + #include "validator/db/fileref.hpp" #include "td/utils/overloaded.h" #include "validator/fabric.h" @@ -26,35 +27,91 @@ #include "ton/ton-io.hpp" #include "downloaders/download-state.hpp" +#include + namespace ton { namespace validator { -ArchiveImporter::ArchiveImporter(std::string path, td::Ref state, BlockSeqno shard_client_seqno, +ArchiveImporter::ArchiveImporter(std::string db_root, td::Ref state, BlockSeqno shard_client_seqno, td::Ref opts, td::actor::ActorId manager, - td::Promise> promise) - : path_(std::move(path)) - , state_(std::move(state)) + std::vector to_import_files, + td::Promise> promise) + : db_root_(std::move(db_root)) + , last_masterchain_state_(std::move(state)) , shard_client_seqno_(shard_client_seqno) + , start_import_seqno_(shard_client_seqno + 1) , opts_(std::move(opts)) , manager_(manager) + , to_import_files_(std::move(to_import_files)) + , use_imported_files_(!to_import_files_.empty()) , promise_(std::move(promise)) { } void ArchiveImporter::start_up() { - auto R = Package::open(path_, false, false); - if (R.is_error()) { - abort_query(R.move_as_error()); + if (use_imported_files_) { + LOG(INFO) << "Importing archive for masterchain seqno #" << start_import_seqno_ << " from disk"; + for (const std::string& path : to_import_files_) { + LOG(INFO) << "Importing file from disk " << path; + td::Status S = process_package(path, true); + if (S.is_error()) { + LOG(INFO) << "Error processing package " << path << ": " << S; + } + } + files_to_cleanup_.clear(); + processed_mc_archive(); + return; + } + LOG(INFO) << "Importing archive for masterchain seqno #" << start_import_seqno_ << " from net"; + td::actor::send_closure(manager_, &ValidatorManager::send_download_archive_request, start_import_seqno_, + ShardIdFull{masterchainId}, db_root_ + "/tmp/", td::Timestamp::in(3600.0), + [SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::downloaded_mc_archive, R.move_as_ok()); + } + }); +} + +void ArchiveImporter::downloaded_mc_archive(std::string path) { + td::Status S = process_package(path, true); + if (S.is_error()) { + abort_query(std::move(S)); + return; + } + processed_mc_archive(); +} + +void ArchiveImporter::processed_mc_archive() { + if (masterchain_blocks_.empty()) { + LOG(DEBUG) << "No masterhchain blocks in archive"; + last_masterchain_seqno_ = last_masterchain_state_->get_seqno(); + checked_all_masterchain_blocks(); + return; + } + + auto seqno = masterchain_blocks_.begin()->first; + LOG(DEBUG) << "First mc seqno in archive = " << seqno; + if (seqno > last_masterchain_state_->get_seqno() + 1) { + abort_query(td::Status::Error(ErrorCode::notready, "too big first masterchain seqno")); return; } - package_ = std::make_shared(R.move_as_ok()); - bool fail = false; - package_->iterate([&](std::string filename, td::BufferSlice data, td::uint64 offset) -> bool { + check_masterchain_block(seqno); +} + +td::Status ArchiveImporter::process_package(std::string path, bool with_masterchain) { + LOG(DEBUG) << "Processing package " << path << " (with_masterchain=" << with_masterchain << ")"; + files_to_cleanup_.push_back(path); + TRY_RESULT(p, Package::open(path, false, false)); + auto package = std::make_shared(std::move(p)); + + td::Status S = td::Status::OK(); + package->iterate([&](std::string filename, td::BufferSlice, td::uint64 offset) -> bool { auto F = FileReference::create(filename); if (F.is_error()) { - abort_query(F.move_as_error()); - fail = true; + S = F.move_as_error(); return false; } auto f = F.move_as_ok(); @@ -79,33 +136,26 @@ void ArchiveImporter::start_up() { ignore = false; is_proof = false; }, - [&](const auto &p) { ignore = true; })); - - if (!ignore) { - blocks_[b][is_proof ? 0 : 1] = offset; + [&](const auto &) { ignore = true; })); + + if (!ignore && (with_masterchain || !b.is_masterchain())) { + if (is_proof) { + blocks_[b].proof_pkg = package; + blocks_[b].proof_offset = offset; + } else { + blocks_[b].data_pkg = package; + blocks_[b].data_offset = offset; + } if (b.is_masterchain()) { masterchain_blocks_[b.seqno()] = b; + last_masterchain_seqno_ = std::max(last_masterchain_seqno_, b.seqno()); + } else { + have_shard_blocks_ = true; } } return true; }); - - if (fail) { - return; - } - - if (masterchain_blocks_.size() == 0) { - abort_query(td::Status::Error(ErrorCode::notready, "archive does not contain any masterchain blocks")); - return; - } - - auto seqno = masterchain_blocks_.begin()->first; - if (seqno > state_->get_seqno() + 1) { - abort_query(td::Status::Error(ErrorCode::notready, "too big first masterchain seqno")); - return; - } - - check_masterchain_block(seqno); + return S; } void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { @@ -115,17 +165,17 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { abort_query(td::Status::Error(ErrorCode::notready, "no new blocks")); return; } - checked_all_masterchain_blocks(seqno - 1); + checked_all_masterchain_blocks(); return; } - while (seqno <= state_->get_block_id().seqno()) { - if (seqno < state_->get_block_id().seqno()) { - if (!state_->check_old_mc_block_id(it->second)) { + while (seqno <= last_masterchain_state_->get_block_id().seqno()) { + if (seqno < last_masterchain_state_->get_block_id().seqno()) { + if (!last_masterchain_state_->check_old_mc_block_id(it->second)) { abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); return; } } else { - if (state_->get_block_id() != it->second) { + if (last_masterchain_state_->get_block_id() != it->second) { abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); return; } @@ -133,18 +183,27 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { seqno++; it = masterchain_blocks_.find(seqno); if (it == masterchain_blocks_.end()) { - checked_all_masterchain_blocks(seqno - 1); + checked_all_masterchain_blocks(); return; } } - if (seqno != state_->get_block_id().seqno() + 1) { + LOG(DEBUG) << "Checking masterchain block #" << seqno; + if (seqno != last_masterchain_state_->get_block_id().seqno() + 1) { abort_query(td::Status::Error(ErrorCode::protoviolation, "hole in masterchain seqno")); return; } auto it2 = blocks_.find(it->second); CHECK(it2 != blocks_.end()); + if (!it2->second.proof_pkg) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "no masterchain block proof")); + return; + } + if (!it2->second.data_pkg) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "no masterchain block data")); + return; + } - auto R1 = package_->read(it2->second[0]); + auto R1 = it2->second.proof_pkg->read(it2->second.proof_offset); if (R1.is_error()) { abort_query(R1.move_as_error()); return; @@ -156,7 +215,7 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { return; } - auto R2 = package_->read(it2->second[1]); + auto R2 = it2->second.data_pkg->read(it2->second.data_offset); if (R2.is_error()) { abort_query(R2.move_as_error()); return; @@ -175,7 +234,7 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { auto proof = proofR.move_as_ok(); auto data = dataR.move_as_ok(); - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = state_->get_block_id(), + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = last_masterchain_state_->get_block_id(), data](td::Result R) mutable { if (R.is_error()) { td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); @@ -191,11 +250,12 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { td::actor::send_closure(SelfId, &ArchiveImporter::checked_masterchain_proof, std::move(handle), std::move(data)); }); - run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), state_, - opts_->is_hardfork(it->second)); + run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), + last_masterchain_state_, opts_->is_hardfork(it->second)); } void ArchiveImporter::checked_masterchain_proof(BlockHandle handle, td::Ref data) { + LOG(DEBUG) << "Checked proof for masterchain block #" << handle->id().seqno(); CHECK(data.not_null()); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result R) { R.ensure(); @@ -205,6 +265,7 @@ void ArchiveImporter::checked_masterchain_proof(BlockHandle handle, td::Refid().seqno(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { R.ensure(); td::actor::send_closure(SelfId, &ArchiveImporter::got_new_materchain_state, @@ -214,22 +275,87 @@ void ArchiveImporter::applied_masterchain_block(BlockHandle handle) { } void ArchiveImporter::got_new_materchain_state(td::Ref state) { - state_ = std::move(state); - check_masterchain_block(state_->get_block_id().seqno() + 1); + last_masterchain_state_ = std::move(state); + imported_any_ = true; + check_masterchain_block(last_masterchain_state_->get_block_id().seqno() + 1); } -void ArchiveImporter::checked_all_masterchain_blocks(BlockSeqno seqno) { - check_next_shard_client_seqno(shard_client_seqno_ + 1); +void ArchiveImporter::checked_all_masterchain_blocks() { + LOG(DEBUG) << "Done importing masterchain blocks. Last block seqno = " << last_masterchain_seqno_; + if (start_import_seqno_ > last_masterchain_state_->get_seqno()) { + abort_query(td::Status::Error("no new masterchain blocks were imported")); + return; + } + BlockIdExt block_id; + CHECK(last_masterchain_state_->get_old_mc_block_id(start_import_seqno_, block_id)); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db_short, block_id, + [SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::Ref state{R.move_as_ok()}; + td::actor::send_closure(SelfId, &ArchiveImporter::download_shard_archives, + std::move(state)); + }); +} + +void ArchiveImporter::download_shard_archives(td::Ref start_state) { + start_state_ = start_state; + td::uint32 monitor_min_split = start_state->monitor_min_split_depth(basechainId); + LOG(DEBUG) << "Monitor min split = " << monitor_min_split; + // If monitor_min_split == 0, we use the old archive format (packages are not separated by shard) + // If masterchain package has shard blocks then it's old archive format, don't need to download shards + if (monitor_min_split > 0 && !have_shard_blocks_ && !use_imported_files_) { + for (td::uint64 i = 0; i < (1ULL << monitor_min_split); ++i) { + ShardIdFull shard_prefix{basechainId, (i * 2 + 1) << (64 - monitor_min_split - 1)}; + if (opts_->need_monitor(shard_prefix, start_state)) { + ++pending_shard_archives_; + LOG(DEBUG) << "Downloading shard archive #" << start_import_seqno_ << " " << shard_prefix.to_str(); + download_shard_archive(shard_prefix); + } + } + } else { + LOG(DEBUG) << "Skip downloading shard archives"; + } + if (pending_shard_archives_ == 0) { + check_next_shard_client_seqno(shard_client_seqno_ + 1); + } +} + +void ArchiveImporter::download_shard_archive(ShardIdFull shard_prefix) { + td::actor::send_closure( + manager_, &ValidatorManager::send_download_archive_request, start_import_seqno_, shard_prefix, db_root_ + "/tmp/", + td::Timestamp::in(3600.0), + [SelfId = actor_id(this), seqno = start_import_seqno_, shard_prefix](td::Result R) { + if (R.is_error()) { + LOG(WARNING) << "Failed to download archive slice #" << seqno << " for shard " << shard_prefix.to_str(); + delay_action( + [=]() { td::actor::send_closure(SelfId, &ArchiveImporter::download_shard_archive, shard_prefix); }, + td::Timestamp::in(2.0)); + } else { + LOG(DEBUG) << "Downloaded shard archive #" << seqno << " " << shard_prefix.to_str(); + td::actor::send_closure(SelfId, &ArchiveImporter::downloaded_shard_archive, R.move_as_ok()); + } + }); +} + +void ArchiveImporter::downloaded_shard_archive(std::string path) { + td::Status S = process_package(path, false); + if (S.is_error()) { + LOG(INFO) << "Error processing package: " << S; + } + --pending_shard_archives_; + if (pending_shard_archives_ == 0) { + check_next_shard_client_seqno(shard_client_seqno_ + 1); + } } void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { - if (seqno > state_->get_seqno()) { + if (seqno > last_masterchain_state_->get_seqno() || seqno > last_masterchain_seqno_) { finish_query(); - } else if (seqno == state_->get_seqno()) { - got_masterchain_state(state_); + } else if (seqno == last_masterchain_state_->get_seqno()) { + got_masterchain_state(last_masterchain_state_); } else { BlockIdExt b; - bool f = state_->get_old_mc_block_id(seqno, b); + bool f = last_masterchain_state_->get_old_mc_block_id(seqno, b); CHECK(f); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { R.ensure(); @@ -241,33 +367,38 @@ void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { } void ArchiveImporter::got_masterchain_state(td::Ref state) { + if (state->get_seqno() != start_import_seqno_ && state->is_key_state()) { + finish_query(); + return; + } + LOG(DEBUG) << "Applying shard client seqno " << state->get_seqno(); auto s = state->get_shards(); - - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = state->get_seqno()](td::Result R) { + td::MultiPromise mp; + auto ig = mp.init_guard(); + for (auto &shard : s) { + if (opts_->need_monitor(shard->shard(), state)) { + apply_shard_block(shard->top_block_id(), state->get_block_id(), ig.get_promise()); + } + } + ig.add_promise([SelfId = actor_id(this), seqno = state->get_seqno()](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); } else { td::actor::send_closure(SelfId, &ArchiveImporter::checked_shard_client_seqno, seqno); } }); - - td::MultiPromise mp; - auto ig = mp.init_guard(); - ig.add_promise(std::move(P)); - - for (auto &shard : s) { - apply_shard_block(shard->top_block_id(), state->get_block_id(), ig.get_promise()); - } } void ArchiveImporter::checked_shard_client_seqno(BlockSeqno seqno) { CHECK(shard_client_seqno_ + 1 == seqno); shard_client_seqno_++; + imported_any_ = true; check_next_shard_client_seqno(seqno + 1); } void ArchiveImporter::apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { + LOG(DEBUG) << "Applying shard block " << block_id.id.to_str(); auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), masterchain_block_id, promise = std::move(promise)](td::Result R) mutable { R.ensure(); @@ -286,7 +417,7 @@ void ArchiveImporter::apply_shard_block_cont1(BlockHandle handle, BlockIdExt mas if (handle->id().seqno() == 0) { auto P = td::PromiseCreator::lambda( - [promise = std::move(promise)](td::Result> R) mutable { promise.set_value(td::Unit()); }); + [promise = std::move(promise)](td::Result>) mutable { promise.set_value(td::Unit()); }); td::actor::create_actor("downloadstate", handle->id(), masterchain_block_id, 2, manager_, td::Timestamp::in(3600), std::move(P)) .release(); @@ -294,12 +425,13 @@ void ArchiveImporter::apply_shard_block_cont1(BlockHandle handle, BlockIdExt mas } auto it = blocks_.find(handle->id()); - if (it == blocks_.end()) { - promise.set_error(td::Status::Error(ErrorCode::notready, PSTRING() << "no proof for shard block " << handle->id())); + if (it == blocks_.end() || !it->second.proof_pkg || !it->second.data_pkg) { + promise.set_error( + td::Status::Error(ErrorCode::notready, PSTRING() << "no data/proof for shard block " << handle->id())); return; } - TRY_RESULT_PROMISE(promise, data, package_->read(it->second[0])); - TRY_RESULT_PROMISE(promise, proof, create_proof_link(handle->id(), std::move(data.second))); + TRY_RESULT_PROMISE(promise, proof_data, it->second.proof_pkg->read(it->second.proof_offset)); + TRY_RESULT_PROMISE(promise, proof, create_proof_link(handle->id(), std::move(proof_data.second))); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { @@ -345,8 +477,8 @@ void ArchiveImporter::apply_shard_block_cont2(BlockHandle handle, BlockIdExt mas void ArchiveImporter::apply_shard_block_cont3(BlockHandle handle, BlockIdExt masterchain_block_id, td::Promise promise) { auto it = blocks_.find(handle->id()); - CHECK(it != blocks_.end()); - TRY_RESULT_PROMISE(promise, data, package_->read(it->second[1])); + CHECK(it != blocks_.end() && it->second.data_pkg); + TRY_RESULT_PROMISE(promise, data, it->second.data_pkg->read(it->second.data_offset)); if (sha256_bits256(data.second.as_slice()) != handle->id().file_hash) { promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); return; @@ -367,6 +499,7 @@ void ArchiveImporter::check_shard_block_applied(BlockIdExt block_id, td::Promise if (!handle->is_applied()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not applied")); } else { + LOG(DEBUG) << "Applied shard block " << handle->id().id.to_str(); promise.set_value(td::Unit()); } } @@ -375,13 +508,24 @@ void ArchiveImporter::check_shard_block_applied(BlockIdExt block_id, td::Promise } void ArchiveImporter::abort_query(td::Status error) { - LOG(INFO) << error; + if (!imported_any_) { + for (const std::string &f : files_to_cleanup_) { + td::unlink(f).ignore(); + } + promise_.set_error(std::move(error)); + return; + } + LOG(INFO) << "Archive import: " << error; finish_query(); } + void ArchiveImporter::finish_query() { + for (const std::string &f : files_to_cleanup_) { + td::unlink(f).ignore(); + } if (promise_) { - promise_.set_value( - std::vector{state_->get_seqno(), std::min(state_->get_seqno(), shard_client_seqno_)}); + promise_.set_value({last_masterchain_state_->get_seqno(), + std::min(last_masterchain_state_->get_seqno(), shard_client_seqno_)}); } stop(); } diff --git a/validator/import-db-slice.hpp b/validator/import-db-slice.hpp index 0993d4bba..04f22642d 100644 --- a/validator/import-db-slice.hpp +++ b/validator/import-db-slice.hpp @@ -19,6 +19,7 @@ #pragma once #include "td/actor/actor.h" +#include "td/utils/port/path.h" #include "validator/interfaces/validator-manager.h" #include "validator/db/package.hpp" @@ -28,19 +29,27 @@ namespace validator { class ArchiveImporter : public td::actor::Actor { public: - ArchiveImporter(std::string path, td::Ref state, BlockSeqno shard_client_seqno, + ArchiveImporter(std::string db_root, td::Ref state, BlockSeqno shard_client_seqno, td::Ref opts, td::actor::ActorId manager, - td::Promise> promise); + std::vector to_import_files, td::Promise> promise); void start_up() override; void abort_query(td::Status error); void finish_query(); + void downloaded_mc_archive(std::string path); + td::Status process_package(std::string path, bool with_masterchain); + + void processed_mc_archive(); void check_masterchain_block(BlockSeqno seqno); void checked_masterchain_proof(BlockHandle handle, td::Ref data); void applied_masterchain_block(BlockHandle handle); void got_new_materchain_state(td::Ref state); - void checked_all_masterchain_blocks(BlockSeqno seqno); + + void checked_all_masterchain_blocks(); + void download_shard_archives(td::Ref start_state); + void download_shard_archive(ShardIdFull shard_prefix); + void downloaded_shard_archive(std::string path); void check_next_shard_client_seqno(BlockSeqno seqno); void checked_shard_client_seqno(BlockSeqno seqno); @@ -52,19 +61,36 @@ class ArchiveImporter : public td::actor::Actor { void check_shard_block_applied(BlockIdExt block_id, td::Promise promise); private: - std::string path_; - td::Ref state_; + std::string db_root_; + td::Ref last_masterchain_state_; BlockSeqno shard_client_seqno_; + BlockSeqno start_import_seqno_; td::Ref opts_; - std::shared_ptr package_; - td::actor::ActorId manager_; - td::Promise> promise_; + + std::vector to_import_files_; + bool use_imported_files_; + td::Promise> promise_; std::map masterchain_blocks_; - std::map> blocks_; + BlockSeqno last_masterchain_seqno_ = 0; + + struct BlockInfo { + std::shared_ptr data_pkg; + td::uint64 data_offset = 0; + std::shared_ptr proof_pkg; + td::uint64 proof_offset = 0; + }; + std::map blocks_; + + td::Ref start_state_; + size_t pending_shard_archives_ = 0; + + bool imported_any_ = false; + bool have_shard_blocks_ = false; + std::vector files_to_cleanup_; }; } // namespace validator diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index 466203226..29ef715b3 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -117,12 +117,18 @@ class Db : public td::actor::Actor { virtual void check_key_block_proof_exists(BlockIdExt block_id, td::Promise promise) = 0; virtual void check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise promise) = 0; - virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) = 0; + virtual void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) = 0; virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, td::Promise promise) = 0; virtual void set_async_mode(bool mode, td::Promise promise) = 0; virtual void run_gc(UnixTime mc_ts, UnixTime gc_ts, UnixTime archive_ttl) = 0; + + virtual void add_persistent_state_description(td::Ref desc, + td::Promise promise) = 0; + virtual void get_persistent_state_descriptions( + td::Promise>> promise) = 0; }; } // namespace validator diff --git a/validator/interfaces/shard.h b/validator/interfaces/shard.h index 7a731ab5e..64aea9b62 100644 --- a/validator/interfaces/shard.h +++ b/validator/interfaces/shard.h @@ -84,6 +84,7 @@ class MasterchainState : virtual public ShardState { 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_config_holder() const = 0; + virtual block::WorkchainSet get_workchain_list() const = 0; virtual td::Status prepare() { return td::Status::OK(); } diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index 78257fa07..3a568ba43 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -66,6 +66,8 @@ using ValidateCandidateResult = td::Variant; class ValidatorManager : public ValidatorManagerInterface { public: + virtual void init_last_masterchain_state(td::Ref state) { + } virtual void set_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) = 0; virtual void get_cell_db_reader(td::Promise> promise) = 0; @@ -75,10 +77,6 @@ class ValidatorManager : public ValidatorManagerInterface { std::function write_data, td::Promise promise) = 0; virtual void store_zero_state_file(BlockIdExt block_id, td::BufferSlice state, td::Promise promise) = 0; - virtual void wait_block_state(BlockHandle handle, td::uint32 priority, td::Timestamp timeout, - td::Promise> promise) = 0; - virtual void wait_block_state_short(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, - td::Promise> promise) = 0; virtual void set_block_data(BlockHandle handle, td::Ref data, td::Promise promise) = 0; virtual void wait_block_data(BlockHandle handle, td::uint32 priority, td::Timestamp, @@ -148,10 +146,11 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void send_top_shard_block_description(td::Ref desc) = 0; virtual void send_block_broadcast(BlockBroadcast broadcast, int mode) = 0; virtual void send_validator_telemetry(PublicKeyHash key, tl_object_ptr telemetry) = 0; + virtual void send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) = 0; virtual void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) = 0; virtual void get_shard_client_state(bool from_db, td::Promise promise) = 0; - virtual void subscribe_to_shard(ShardIdFull shard) = 0; virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) = 0; virtual void get_async_serializer_state(td::Promise promise) = 0; @@ -212,6 +211,8 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void record_validate_query_stats(BlockIdExt block_id, double work_time, double cpu_work_time) { } + virtual void add_persistent_state_description(td::Ref desc) = 0; + static bool is_persistent_state(UnixTime ts, UnixTime prev_ts) { return ts / (1 << 17) != prev_ts / (1 << 17); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index 48b72d3b0..18649ba89 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -265,11 +265,13 @@ class ValidatorManagerImpl : public ValidatorManager { } void send_validator_telemetry(PublicKeyHash key, tl_object_ptr telemetry) override { } + void send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override { + UNREACHABLE(); + } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; void get_shard_client_state(bool from_db, td::Promise promise) override; - void subscribe_to_shard(ShardIdFull shard) override { - } void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override { UNREACHABLE(); @@ -285,7 +287,8 @@ class ValidatorManagerImpl : public ValidatorManager { promise.set_error(td::Status::Error(ErrorCode::error, "download disabled")); } - void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override { + void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) override { UNREACHABLE(); } void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, @@ -402,8 +405,8 @@ class ValidatorManagerImpl : public ValidatorManager { } void get_out_msg_queue_size(BlockIdExt block_id, td::Promise promise) override { if (queue_size_counter_.empty()) { - queue_size_counter_ = - td::actor::create_actor("queuesizecounter", td::Ref{}, actor_id(this)); + queue_size_counter_ = td::actor::create_actor("queuesizecounter", td::Ref{}, + opts_, actor_id(this)); } td::actor::send_closure(queue_size_counter_, &QueueSizeCounter::get_queue_size, block_id, std::move(promise)); } @@ -437,6 +440,8 @@ class ValidatorManagerImpl : public ValidatorManager { td::Promise> promise) override { promise.set_result(td::Status::Error("not implemented")); } + void add_persistent_state_description(td::Ref desc) override { + } void update_options(td::Ref opts) override { opts_ = std::move(opts); diff --git a/validator/manager-hardfork.hpp b/validator/manager-hardfork.hpp index bb0dab5fd..7cd8c7057 100644 --- a/validator/manager-hardfork.hpp +++ b/validator/manager-hardfork.hpp @@ -20,6 +20,7 @@ #include "interfaces/validator-manager.h" #include "interfaces/db.h" +#include "ton/ton-types.h" #include "validator-group.hpp" #include "manager-init.h" #include "manager-hardfork.h" @@ -334,6 +335,10 @@ class ValidatorManagerImpl : public ValidatorManager { } void send_validator_telemetry(PublicKeyHash key, tl_object_ptr telemetry) override { } + void send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override { + UNREACHABLE(); + } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override { UNREACHABLE(); @@ -341,8 +346,6 @@ class ValidatorManagerImpl : public ValidatorManager { void get_shard_client_state(bool from_db, td::Promise promise) override { UNREACHABLE(); } - void subscribe_to_shard(ShardIdFull shard) override { - } void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override { UNREACHABLE(); @@ -358,7 +361,8 @@ class ValidatorManagerImpl : public ValidatorManager { promise.set_error(td::Status::Error(ErrorCode::error, "download disabled")); } - void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override { + void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) override { UNREACHABLE(); } void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, @@ -466,8 +470,8 @@ class ValidatorManagerImpl : public ValidatorManager { } void get_out_msg_queue_size(BlockIdExt block_id, td::Promise promise) override { if (queue_size_counter_.empty()) { - queue_size_counter_ = - td::actor::create_actor("queuesizecounter", td::Ref{}, actor_id(this)); + queue_size_counter_ = td::actor::create_actor("queuesizecounter", td::Ref{}, + opts_, actor_id(this)); } td::actor::send_closure(queue_size_counter_, &QueueSizeCounter::get_queue_size, block_id, std::move(promise)); } @@ -504,6 +508,8 @@ class ValidatorManagerImpl : public ValidatorManager { void update_options(td::Ref opts) override { opts_ = std::move(opts); } + void add_persistent_state_description(td::Ref desc) override { + } private: td::Ref opts_; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 64a0a547a..c2944b257 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -268,6 +268,7 @@ void ValidatorManagerMasterchainReiniter::downloaded_masterchain_state(td::Refreceived_state()); CHECK(handle_->is_applied()); LOG(INFO) << "downloaded masterchain state"; + td::actor::send_closure(manager_, &ValidatorManager::init_last_masterchain_state, state_); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::downloaded_all_shards); @@ -567,7 +568,7 @@ void ValidatorManagerMasterchainStarter::truncated() { truncate_shard_next(handle_->id(), ig.get_promise()); auto s = state_->get_shards(); for (auto &shard : s) { - if (opts_->need_monitor(shard->shard())) { + if (opts_->need_monitor(shard->shard(), state_)) { truncate_shard_next(shard->top_block_id(), ig.get_promise()); } } diff --git a/validator/manager.cpp b/validator/manager.cpp index 76f8a845b..a75de0a90 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -20,11 +20,9 @@ #include "checksum.h" #include "td/utils/buffer.h" #include "validator-group.hpp" -#include "adnl/utils.hpp" #include "downloaders/wait-block-state.hpp" #include "downloaders/wait-block-state-merge.hpp" #include "downloaders/wait-block-data.hpp" -#include "validator-group.hpp" #include "fabric.h" #include "manager.h" #include "validate-broadcast.hpp" @@ -202,7 +200,7 @@ void ValidatorManagerImpl::validate_block(ReceivedBlock block, td::Promise R) mutable { + [SelfId = actor_id(this), promise = std::move(promise), id = blkid](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); } else { @@ -217,12 +215,26 @@ void ValidatorManagerImpl::prevalidate_block(BlockBroadcast broadcast, td::Promi promise.set_error(td::Status::Error(ErrorCode::notready, "node not started")); return; } + if (!need_monitor(broadcast.block_id.shard_full())) { + promise.set_error(td::Status::Error("not monitoring shard")); + return; + } + promise = [SelfId = actor_id(this), promise = std::move(promise), block_id = broadcast.block_id, + cc_seqno = broadcast.catchain_seqno](td::Result R) mutable { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::validated_block_broadcast, block_id, cc_seqno); + } + promise.set_result(std::move(R)); + }; td::actor::create_actor("broadcast", std::move(broadcast), last_masterchain_block_handle_, last_masterchain_state_, last_known_key_block_handle_, actor_id(this), td::Timestamp::in(2.0), std::move(promise)) .release(); } +void ValidatorManagerImpl::validated_block_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno) { +} + void ValidatorManagerImpl::sync_complete(td::Promise promise) { started_ = true; @@ -474,7 +486,7 @@ void ValidatorManagerImpl::new_ihr_message(td::BufferSlice data) { } void ValidatorManagerImpl::new_shard_block(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) { - if (!is_validator()) { + if (!is_validator() && !cached_block_candidates_.count(block_id)) { return; } if (!last_masterchain_block_handle_) { @@ -508,37 +520,36 @@ void ValidatorManagerImpl::new_block_candidate(BlockIdExt block_id, td::BufferSl if (!started_) { return; } + if (!need_monitor(block_id.shard_full())) { + VLOG(VALIDATOR_DEBUG) << "dropping block candidate broadcast: not monitoring shard"; + return; + } add_cached_block_candidate(ReceivedBlock{block_id, std::move(data)}); } void ValidatorManagerImpl::add_shard_block_description(td::Ref desc) { - if (desc->may_be_valid(last_masterchain_block_handle_, last_masterchain_state_)) { - auto it = shard_blocks_.find(ShardTopBlockDescriptionId{desc->shard(), desc->catchain_seqno()}); - if (it != shard_blocks_.end() && desc->block_id().id.seqno <= it->second->block_id().id.seqno) { - VLOG(VALIDATOR_DEBUG) << "dropping duplicate shard block broadcast"; - return; - } - shard_blocks_[ShardTopBlockDescriptionId{desc->block_id().shard_full(), desc->catchain_seqno()}] = desc; - VLOG(VALIDATOR_DEBUG) << "new shard block descr for " << desc->block_id(); - if (last_masterchain_block_handle_ && last_masterchain_seqno_ > 0 && - desc->generated_at() < last_masterchain_block_handle_->unix_time() + 60) { - delay_action( - [SelfId = actor_id(this), desc]() { - auto P = td::PromiseCreator::lambda([](td::Result> R) { - if (R.is_error()) { - auto S = R.move_as_error(); - if (S.code() != ErrorCode::timeout && S.code() != ErrorCode::notready) { - VLOG(VALIDATOR_NOTICE) << "failed to get shard state: " << S; - } else { - VLOG(VALIDATOR_DEBUG) << "failed to get shard state: " << S; - } - } - }); - td::actor::send_closure(SelfId, &ValidatorManager::wait_block_state_short, desc->block_id(), 0, - td::Timestamp::in(60.0), std::move(P)); - }, - td::Timestamp::in(1.0)); - } + if (!desc->may_be_valid(last_masterchain_block_handle_, last_masterchain_state_)) { + return; + } + auto it = shard_blocks_.find(ShardTopBlockDescriptionId{desc->shard(), desc->catchain_seqno()}); + if (it != shard_blocks_.end() && desc->block_id().id.seqno <= it->second->block_id().id.seqno) { + VLOG(VALIDATOR_DEBUG) << "dropping duplicate shard block broadcast"; + return; + } + shard_blocks_[ShardTopBlockDescriptionId{desc->block_id().shard_full(), desc->catchain_seqno()}] = desc; + VLOG(VALIDATOR_DEBUG) << "new shard block descr for " << desc->block_id(); + if (need_monitor(desc->block_id().shard_full())) { + auto P = td::PromiseCreator::lambda([](td::Result> R) { + if (R.is_error()) { + auto S = R.move_as_error(); + if (S.code() != ErrorCode::timeout && S.code() != ErrorCode::notready) { + VLOG(VALIDATOR_NOTICE) << "failed to get shard state: " << S; + } else { + VLOG(VALIDATOR_DEBUG) << "failed to get shard state: " << S; + } + } + }); + wait_block_state_short(desc->block_id(), 0, td::Timestamp::in(60.0), std::move(P)); } } @@ -554,7 +565,7 @@ void ValidatorManagerImpl::add_cached_block_candidate(ReceivedBlock block) { if (it != wait_block_data_.end()) { auto r_block = create_block(cached_block_candidates_[id].clone()); if (r_block.is_ok()) { - td::actor::send_closure(it->second.actor_, &WaitBlockData::got_block_data_from_net, r_block.move_as_ok()); + td::actor::send_closure(it->second.actor_, &WaitBlockData::loaded_block_data, r_block.move_as_ok()); } } } @@ -672,6 +683,10 @@ void ValidatorManagerImpl::run_ext_query(td::BufferSlice data, td::Promise> promise) { + if (last_masterchain_state_.not_null() && !opts_->need_monitor(handle->id().shard_full(), last_masterchain_state_)) { + return promise.set_error( + td::Status::Error(PSTRING() << "not monitoring shard " << handle->id().shard_full().to_str())); + } auto it0 = block_state_cache_.find(handle->id()); if (it0 != block_state_cache_.end()) { it0->second.ttl_ = td::Timestamp::in(30.0); @@ -683,9 +698,11 @@ void ValidatorManagerImpl::wait_block_state(BlockHandle handle, td::uint32 prior auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result> R) { td::actor::send_closure(SelfId, &ValidatorManagerImpl::finished_wait_state, handle, std::move(R)); }); - auto id = td::actor::create_actor("waitstate", handle, priority, actor_id(this), - td::Timestamp::in(10.0), std::move(P)) - .release(); + auto id = + td::actor::create_actor("waitstate", handle, priority, actor_id(this), + td::Timestamp::at(timeout.at() + 10.0), std::move(P), + get_block_persistent_state(handle->id())) + .release(); wait_state_[handle->id()].actor_ = id; it = wait_state_.find(handle->id()); } @@ -717,7 +734,7 @@ void ValidatorManagerImpl::wait_block_data(BlockHandle handle, td::uint32 priori td::actor::send_closure(SelfId, &ValidatorManagerImpl::finished_wait_data, handle, std::move(R)); }); auto id = td::actor::create_actor("waitdata", handle, priority, actor_id(this), - td::Timestamp::in(10.0), std::move(P)) + td::Timestamp::at(timeout.at() + 10.0), false, std::move(P)) .release(); wait_block_data_[handle->id()].actor_ = id; it = wait_block_data_.find(handle->id()); @@ -744,6 +761,10 @@ void ValidatorManagerImpl::wait_block_data_short(BlockIdExt block_id, td::uint32 void ValidatorManagerImpl::wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) { + if (last_masterchain_state_.not_null() && !opts_->need_monitor(left_id.shard_full(), last_masterchain_state_)) { + return promise.set_error( + td::Status::Error(PSTRING() << "not monitoring shard " << left_id.shard_full().to_str())); + } td::actor::create_actor("merge", left_id, right_id, priority, actor_id(this), timeout, std::move(promise)) .release(); @@ -1128,7 +1149,7 @@ void ValidatorManagerImpl::finished_wait_state(BlockHandle handle, td::Result("waitstate", handle, X.second, actor_id(this), X.first, - std::move(P)) + std::move(P), get_block_persistent_state(handle->id())) .release(); it->second.actor_ = id; return; @@ -1158,8 +1179,9 @@ void ValidatorManagerImpl::finished_wait_data(BlockHandle handle, td::Result("waitdata", handle, X.second, actor_id(this), X.first, std::move(P)) - .release(); + td::actor::create_actor("waitdata", handle, X.second, actor_id(this), X.first, false, + std::move(P)) + .release(); it->second.actor_ = id; return; } @@ -1617,6 +1639,7 @@ void ValidatorManagerImpl::send_top_shard_block_description(td::Refblock_id().shard_full(), desc->catchain_seqno()}] = desc; callback_->send_shard_block_info(desc->block_id(), desc->catchain_seqno(), desc->serialize()); + add_shard_block_description(desc); } } @@ -1629,6 +1652,12 @@ void ValidatorManagerImpl::send_validator_telemetry(PublicKeyHash key, callback_->send_validator_telemetry(key, std::move(telemetry)); } +void ValidatorManagerImpl::send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, + std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) { + callback_->download_archive(mc_seqno, shard_prefix, std::move(tmp_dir), timeout, std::move(promise)); +} + void ValidatorManagerImpl::start_up() { db_ = create_db_actor(actor_id(this), db_root_, opts_); actor_stats_ = td::actor::create_actor("actor_stats"); @@ -1664,7 +1693,6 @@ void ValidatorManagerImpl::start_up() { if (fname.substr(fname.size() - 5) != ".pack") { return; } - fname = fname.substr(0, fname.size() - 5); if (fname.substr(0, 8) != "archive.") { return; } @@ -1673,13 +1701,18 @@ void ValidatorManagerImpl::start_up() { while (fname.size() > 1 && fname[0] == '0') { fname.remove_prefix(1); } + auto i = fname.find('.'); + if (i == td::Slice::npos) { + return; + } + fname = fname.substr(0, i); auto v = td::to_integer_safe(fname); if (v.is_error()) { return; } - auto pos = v.move_as_ok(); - LOG(INFO) << "found archive slice '" << cfname << "' for position " << pos; - to_import_[pos] = std::make_pair(cfname.str(), true); + auto seqno = v.move_as_ok(); + LOG(INFO) << "found archive slice '" << cfname << "' for seqno " << seqno; + to_import_[seqno].push_back(cfname.str()); } }); if (S.is_error()) { @@ -1692,6 +1725,14 @@ void ValidatorManagerImpl::start_up() { alarm_timestamp().relax(check_waiters_at_); } +void ValidatorManagerImpl::init_last_masterchain_state(td::Ref state) { + if (last_masterchain_state_.not_null()) { + return; + } + last_masterchain_state_ = std::move(state); + update_shard_overlays(); +} + void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { CHECK(R.handle); CHECK(R.state.not_null()); @@ -1730,6 +1771,17 @@ void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { candidates_buffer_ = td::actor::create_actor("candidates-buffer", actor_id(this)); } init_validator_telemetry(); + + auto Q = td::PromiseCreator::lambda( + [SelfId = actor_id(this)](td::Result>> R) { + if (R.is_error()) { + LOG(FATAL) << "db error: " << R.move_as_error(); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::got_persistent_state_descriptions, R.move_as_ok()); + } + }); + td::actor::send_closure(db_, &Db::get_persistent_state_descriptions, std::move(Q)); + update_shard_overlays(); } void ValidatorManagerImpl::read_gc_list(std::vector list) { @@ -1832,61 +1884,35 @@ void ValidatorManagerImpl::download_next_archive() { } auto seqno = std::min(last_masterchain_seqno_, shard_client_handle_->id().seqno()); + std::vector to_import_files; auto it = to_import_.upper_bound(seqno + 1); if (it != to_import_.begin()) { - it--; - if (it->second.second) { - it->second.second = false; - downloaded_archive_slice(it->second.first, false); - return; - } + --it; + to_import_files = std::move(it->second); + it->second.clear(); } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { - LOG(INFO) << "failed to download archive slice: " << R.error(); + LOG(INFO) << "failed to download and import archive slice: " << R.error(); delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); }, td::Timestamp::in(2.0)); } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok(), true); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.ok().first, R.ok().second); } }); - callback_->download_archive(seqno + 1, db_root_ + "/tmp/", td::Timestamp::in(36000.0), std::move(P)); -} - -void ValidatorManagerImpl::downloaded_archive_slice(std::string name, bool is_tmp) { - LOG(INFO) << "downloaded archive slice: " << name; - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), name, is_tmp](td::Result> R) { - if (is_tmp) { - td::unlink(name).ensure(); - } - if (R.is_error()) { - LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); - delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); }, - td::Timestamp::in(2.0)); - } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); - } - }); - - auto seqno = std::min(last_masterchain_seqno_, shard_client_handle_->id().seqno()); - - td::actor::create_actor("archiveimport", name, last_masterchain_state_, seqno, opts_, actor_id(this), - std::move(P)) + td::actor::create_actor("archiveimport", db_root_, last_masterchain_state_, seqno, opts_, + actor_id(this), std::move(to_import_files), std::move(P)) .release(); } -void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) { - CHECK(seqno.size() == 2); - LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; - CHECK(seqno[0] <= last_masterchain_seqno_); - CHECK(seqno[1] <= last_masterchain_seqno_); +void ValidatorManagerImpl::checked_archive_slice(BlockSeqno new_last_mc_seqno, BlockSeqno new_shard_client_seqno) { + LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << new_last_mc_seqno + << " shard_top_seqno_=" << new_shard_client_seqno; + CHECK(new_last_mc_seqno <= last_masterchain_seqno_); + CHECK(new_shard_client_seqno <= last_masterchain_seqno_); - BlockIdExt b; - if (seqno[1] < last_masterchain_seqno_) { - CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); - } else { - b = last_masterchain_block_id_; - } + BlockIdExt shard_client_block_id; + CHECK(last_masterchain_state_->get_old_mc_block_id(new_shard_client_seqno, shard_client_block_id)); auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), db = db_.get(), client = shard_client_.get()](td::Result R) { @@ -1902,7 +1928,7 @@ void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) }); td::actor::send_closure(db, &Db::get_block_state, std::move(handle), std::move(P)); }); - get_block_handle(b, true, std::move(P)); + get_block_handle(shard_client_block_id, true, std::move(P)); } void ValidatorManagerImpl::finish_prestart_sync() { @@ -1939,6 +1965,7 @@ void ValidatorManagerImpl::new_masterchain_block() { init_validator_telemetry(); } + update_shard_overlays(); update_shards(); update_shard_blocks(); @@ -1952,6 +1979,26 @@ void ValidatorManagerImpl::new_masterchain_block() { } } +void ValidatorManagerImpl::update_shard_overlays() { + CHECK(last_masterchain_state_.not_null()); + std::set shards_to_monitor; + shards_to_monitor.insert(ShardIdFull{masterchainId}); + std::set workchains; + for (const auto& shard : last_masterchain_state_->get_shards()) { + workchains.insert(shard->shard().workchain); + if (opts_->need_monitor(shard->shard(),last_masterchain_state_)) { + shards_to_monitor.insert(shard->shard()); + } + } + for (const auto &[wc, desc] : last_masterchain_state_->get_workchain_list()) { + if (!workchains.count(wc) && desc->active && + opts_->need_monitor(ShardIdFull{wc, shardIdAll}, last_masterchain_state_)) { + shards_to_monitor.insert(ShardIdFull{wc, shardIdAll}); + } + } + callback_->on_new_masterchain_block(last_masterchain_state_, std::move(shards_to_monitor)); +} + void ValidatorManagerImpl::update_shards() { if ((last_masterchain_state_->rotated_all_shards() || last_masterchain_seqno_ == 0) && opts_->get_last_fork_masterchain_seqno() <= last_masterchain_seqno_) { @@ -2268,7 +2315,8 @@ td::actor::ActorOwn ValidatorManagerImpl::create_validator_group auto validator_id = get_validator(shard, validator_set); CHECK(!validator_id.is_zero()); auto G = td::actor::create_actor( - "validatorgroup", shard, validator_id, session_id, validator_set, key_seqno, opts, keyring_, adnl_, rldp_, + PSTRING() << "valgroup" << shard.to_str(), shard, validator_id, session_id, validator_set, key_seqno, opts, + keyring_, adnl_, rldp_, overlays_, db_root_, actor_id(this), init_session, opts_->check_unsafe_resync_allowed(validator_set->get_catchain_seqno()), opts_); return G; @@ -2444,7 +2492,7 @@ void ValidatorManagerImpl::allow_block_state_gc(BlockIdExt block_id, td::Promise return; } auto shards = gc_masterchain_state_->get_shards(); - for (auto shard : shards) { + for (const auto &shard : shards) { if (shard_intersects(shard->shard(), block_id.shard_full())) { promise.set_result(block_id.id.seqno < shard->top_block_id().id.seqno); return; @@ -2682,10 +2730,6 @@ void ValidatorManagerImpl::get_shard_client_state(bool from_db, td::Promiseadd_shard(shard); -} - void ValidatorManagerImpl::update_async_serializer_state(AsyncSerializerState state, td::Promise promise) { td::actor::send_closure(db_, &Db::update_async_serializer_state, std::move(state), std::move(promise)); } @@ -2698,12 +2742,13 @@ void ValidatorManagerImpl::try_get_static_file(FileHash file_hash, td::Promise promise) { +void ValidatorManagerImpl::get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) { if (masterchain_seqno > last_masterchain_seqno_) { promise.set_error(td::Status::Error(ErrorCode::notready, "masterchain seqno too big")); return; } - td::actor::send_closure(db_, &Db::get_archive_id, masterchain_seqno, std::move(promise)); + td::actor::send_closure(db_, &Db::get_archive_id, masterchain_seqno, shard_prefix, std::move(promise)); } void ValidatorManagerImpl::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, @@ -2715,10 +2760,13 @@ bool ValidatorManagerImpl::is_validator() { return temp_keys_.size() > 0 || permanent_keys_.size() > 0; } +bool ValidatorManagerImpl::validating_masterchain() { + return !get_validator(ShardIdFull(masterchainId), + last_masterchain_state_->get_validator_set(ShardIdFull(masterchainId))) + .is_zero(); +} + PublicKeyHash ValidatorManagerImpl::get_validator(ShardIdFull shard, td::Ref val_set) { - if (!opts_->need_validate(shard, val_set->get_catchain_seqno())) { - return PublicKeyHash::zero(); - } for (auto &key : temp_keys_) { if (val_set->is_validator(key.bits256_value())) { return key; @@ -3224,10 +3272,15 @@ void ValidatorManagerImpl::get_validator_groups_info_for_litequery( } void ValidatorManagerImpl::update_options(td::Ref opts) { - // Currently options can be updated only to change state_serializer_enabled flag and collator_options + if (!shard_client_.empty()) { + td::actor::send_closure(shard_client_, &ShardClient::update_options, opts); + } if (!serializer_.empty()) { td::actor::send_closure(serializer_, &AsyncStateSerializer::update_options, opts); } + if (!queue_size_counter_.empty()) { + td::actor::send_closure(queue_size_counter_, &QueueSizeCounter::update_options, opts); + } for (auto &group : validator_groups_) { td::actor::send_closure(group.second.actor, &ValidatorGroup::update_options, opts); } @@ -3237,6 +3290,53 @@ void ValidatorManagerImpl::update_options(td::Ref opts) opts_ = std::move(opts); } +void ValidatorManagerImpl::add_persistent_state_description(td::Ref desc) { + auto now = (UnixTime)td::Clocks::system(); + if (desc->end_time <= now) { + return; + } + td::actor::send_closure(db_, &Db::add_persistent_state_description, desc, [](td::Result) {}); + auto it = persistent_state_descriptions_.begin(); + while (it != persistent_state_descriptions_.end()) { + const auto &prev_desc = it->second; + if (prev_desc->end_time <= now) { + for (const BlockIdExt &block_id : prev_desc->shard_blocks) { + persistent_state_blocks_.erase(block_id); + } + it = persistent_state_descriptions_.erase(it); + } else { + ++it; + } + } + add_persistent_state_description_impl(std::move(desc)); +} + +void ValidatorManagerImpl::add_persistent_state_description_impl(td::Ref desc) { + if (!persistent_state_descriptions_.emplace(desc->masterchain_id.seqno(), desc).second) { + return; + } + LOG(DEBUG) << "Add persistent state description for mc block " << desc->masterchain_id.to_str() + << " start_time=" << desc->start_time << " end_time=" << desc->end_time; + for (const BlockIdExt &block_id : desc->shard_blocks) { + persistent_state_blocks_[block_id] = desc; + LOG(DEBUG) << "Persistent state description: shard block " << block_id.to_str(); + } +} + +void ValidatorManagerImpl::got_persistent_state_descriptions(std::vector> descs) { + for (auto &desc : descs) { + add_persistent_state_description_impl(std::move(desc)); + } +} + +td::Ref ValidatorManagerImpl::get_block_persistent_state(BlockIdExt block_id) { + auto it = persistent_state_blocks_.find(block_id); + if (it == persistent_state_blocks_.end()) { + return {}; + } + return it->second; +} + td::actor::ActorOwn ValidatorManagerFactory::create( td::Ref opts, std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId rldp, diff --git a/validator/manager.hpp b/validator/manager.hpp index e8768e1bf..d55e3442e 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -189,6 +189,12 @@ class ValidatorManagerImpl : public ValidatorManager { waiting_.resize(j); } }; + template + struct WaitListCaching : public WaitList { + bool done_ = false; + ResType result_; + td::Timestamp remove_at_; + }; std::map>> wait_state_; std::map>> wait_block_data_; @@ -305,6 +311,7 @@ class ValidatorManagerImpl : public ValidatorManager { std::vector perf_timer_stats; void new_masterchain_block(); + void update_shard_overlays(); void update_shards(); void update_shard_blocks(); void written_destroyed_validator_sessions(std::vector> groups); @@ -323,8 +330,7 @@ class ValidatorManagerImpl : public ValidatorManager { void applied_hardfork(); void prestart_sync(); void download_next_archive(); - void downloaded_archive_slice(std::string name, bool is_tmp); - void checked_archive_slice(std::vector seqno); + void checked_archive_slice(BlockSeqno new_last_mc_seqno, BlockSeqno new_shard_client_seqno); void finish_prestart_sync(); void completed_prestart_sync(); @@ -361,6 +367,7 @@ class ValidatorManagerImpl : public ValidatorManager { td::Promise promise) override; void validate_block(ReceivedBlock block, td::Promise promise) override; void prevalidate_block(BlockBroadcast broadcast, td::Promise promise) override; + void validated_block_broadcast(BlockIdExt block_id, CatchainSeqno cc_seqno); //void create_validate_block(BlockId block, td::BufferSlice data, td::Promise promise) = 0; void sync_complete(td::Promise promise) override; @@ -505,10 +512,11 @@ class ValidatorManagerImpl : public ValidatorManager { void send_top_shard_block_description(td::Ref desc) override; void send_block_broadcast(BlockBroadcast broadcast, int mode) override; void send_validator_telemetry(PublicKeyHash key, tl_object_ptr telemetry) override; + void send_download_archive_request(BlockSeqno mc_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) override; void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; void get_shard_client_state(bool from_db, td::Promise promise) override; - void subscribe_to_shard(ShardIdFull shard) override; void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override; void get_async_serializer_state(td::Promise promise) override; @@ -521,7 +529,7 @@ class ValidatorManagerImpl : public ValidatorManager { std::move(promise)); } - void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override; + void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, td::Promise promise) override; void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, td::Promise promise) override; @@ -542,10 +550,12 @@ class ValidatorManagerImpl : public ValidatorManager { void finished_wait_data(BlockHandle handle, td::Result> R); void start_up() override; + void init_last_masterchain_state(td::Ref state) override; void started(ValidatorManagerInitResult result); void read_gc_list(std::vector list); bool is_validator(); + bool validating_masterchain(); PublicKeyHash get_validator(ShardIdFull shard, td::Ref val_set); ValidatorManagerImpl(td::Ref opts, std::string db_root, @@ -604,14 +614,20 @@ class ValidatorManagerImpl : public ValidatorManager { void update_options(td::Ref opts) override; + void add_persistent_state_description(td::Ref desc) 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()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not ready")); return; } - queue_size_counter_ = td::actor::create_actor("queuesizecounter", - last_masterchain_state_, actor_id(this)); + queue_size_counter_ = + td::actor::create_actor("queuesizecounter", last_masterchain_state_, opts_, actor_id(this)); + } + if (!opts_->need_monitor(block_id.shard_full(), last_masterchain_state_)) { + return promise.set_error( + td::Status::Error(PSTRING() << "not monitoring shard " << block_id.shard_full().to_str())); } td::actor::send_closure(queue_size_counter_, &QueueSizeCounter::get_queue_size, block_id, std::move(promise)); } @@ -682,7 +698,7 @@ class ValidatorManagerImpl : public ValidatorManager { td::actor::ActorOwn serializer_; - std::map> to_import_; + std::map> to_import_; private: std::unique_ptr callback_; @@ -712,7 +728,15 @@ class ValidatorManagerImpl : public ValidatorManager { return 3 * 10; } + void got_persistent_state_descriptions(std::vector> descs); + void add_persistent_state_description_impl(td::Ref desc); + td::Ref get_block_persistent_state(BlockIdExt block_id); + private: + bool need_monitor(ShardIdFull shard) const { + return opts_->need_monitor(shard, last_masterchain_state_); + } + std::map> shard_client_waiters_; td::actor::ActorOwn queue_size_counter_; @@ -740,6 +764,9 @@ class ValidatorManagerImpl : public ValidatorManager { std::map> validator_telemetry_; void init_validator_telemetry(); + + std::map> persistent_state_descriptions_; + std::map> persistent_state_blocks_; }; } // namespace validator diff --git a/validator/net/download-archive-slice.cpp b/validator/net/download-archive-slice.cpp index 6235b8b08..c2f8eceac 100644 --- a/validator/net/download-archive-slice.cpp +++ b/validator/net/download-archive-slice.cpp @@ -20,6 +20,8 @@ #include "td/utils/port/path.h" #include "td/utils/overloaded.h" +#include + namespace ton { namespace validator { @@ -27,12 +29,13 @@ namespace validator { namespace fullnode { DownloadArchiveSlice::DownloadArchiveSlice( - BlockSeqno masterchain_seqno, std::string tmp_dir, adnl::AdnlNodeIdShort local_id, + BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, adnl::AdnlNodeIdShort local_id, overlay::OverlayIdShort overlay_id, adnl::AdnlNodeIdShort download_from, td::Timestamp timeout, td::actor::ActorId validator_manager, td::actor::ActorId rldp, td::actor::ActorId overlays, td::actor::ActorId adnl, td::actor::ActorId client, td::Promise promise) : masterchain_seqno_(masterchain_seqno) + , shard_prefix_(shard_prefix) , tmp_dir_(std::move(tmp_dir)) , local_id_(local_id) , overlay_id_(overlay_id) @@ -114,7 +117,13 @@ void DownloadArchiveSlice::got_node_to_download(adnl::AdnlNodeIdShort download_f } }); - auto q = create_serialize_tl_object(masterchain_seqno_); + td::BufferSlice q; + if (shard_prefix_.is_masterchain()) { + q = create_serialize_tl_object(masterchain_seqno_); + } else { + q = create_serialize_tl_object(masterchain_seqno_, + create_tl_shard_id(shard_prefix_)); + } if (client_.empty()) { td::actor::send_closure(overlays_, &overlay::Overlays::send_query, download_from_, local_id_, overlay_id_, "get_archive_info", std::move(P), td::Timestamp::in(3.0), std::move(q)); @@ -145,7 +154,8 @@ void DownloadArchiveSlice::got_archive_info(td::BufferSlice data) { } prev_logged_timer_ = td::Timer(); - LOG(INFO) << "downloading archive slice #" << masterchain_seqno_ << " from " << download_from_; + LOG(INFO) << "downloading archive slice #" << masterchain_seqno_ << " " << shard_prefix_.to_str() << " from " + << download_from_; get_archive_slice(); } @@ -186,13 +196,15 @@ void DownloadArchiveSlice::got_archive_slice(td::BufferSlice data) { double elapsed = prev_logged_timer_.elapsed(); if (elapsed > 10.0) { prev_logged_timer_ = td::Timer(); - LOG(INFO) << "downloading archive slice #" << masterchain_seqno_ << ": total=" << offset_ << " (" + LOG(INFO) << "downloading archive slice #" << masterchain_seqno_ << " " << shard_prefix_.to_str() + << ": total=" << offset_ << " (" << td::format::as_size((td::uint64)(double(offset_ - prev_logged_sum_) / elapsed)) << "/s)"; prev_logged_sum_ = offset_; } if (data.size() < slice_size()) { - LOG(INFO) << "finished downloading arcrive slice #" << masterchain_seqno_ << ": total=" << offset_; + LOG(INFO) << "finished downloading arcrive slice #" << masterchain_seqno_ << " " << shard_prefix_.to_str() + << ": total=" << offset_; finish_query(); } else { get_archive_slice(); diff --git a/validator/net/download-archive-slice.hpp b/validator/net/download-archive-slice.hpp index 0384ac8c9..42fd715f7 100644 --- a/validator/net/download-archive-slice.hpp +++ b/validator/net/download-archive-slice.hpp @@ -32,8 +32,9 @@ namespace fullnode { class DownloadArchiveSlice : public td::actor::Actor { public: - DownloadArchiveSlice(BlockSeqno masterchain_seqno, std::string tmp_dir, adnl::AdnlNodeIdShort local_id, - overlay::OverlayIdShort overlay_id, adnl::AdnlNodeIdShort download_from, td::Timestamp timeout, + DownloadArchiveSlice(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + adnl::AdnlNodeIdShort local_id, overlay::OverlayIdShort overlay_id, + adnl::AdnlNodeIdShort download_from, td::Timestamp timeout, td::actor::ActorId validator_manager, td::actor::ActorId rldp, td::actor::ActorId overlays, td::actor::ActorId adnl, @@ -55,6 +56,7 @@ class DownloadArchiveSlice : public td::actor::Actor { private: BlockSeqno masterchain_seqno_; + ShardIdFull shard_prefix_; std::string tmp_dir_; std::string tmp_name_; td::FileFd fd_; diff --git a/validator/queue-size-counter.cpp b/validator/queue-size-counter.cpp index eb8580894..4fe55ae31 100644 --- a/validator/queue-size-counter.cpp +++ b/validator/queue-size-counter.cpp @@ -234,7 +234,9 @@ void QueueSizeCounter::process_top_shard_blocks_cont(td::Ref s last_top_blocks_.clear(); last_top_blocks_.push_back(state->get_block_id()); for (auto &shard : state->get_shards()) { - last_top_blocks_.push_back(shard->top_block_id()); + if (opts_->need_monitor(shard->shard(), state)) { + last_top_blocks_.push_back(shard->top_block_id()); + } } for (const BlockIdExt &block_id : last_top_blocks_) { get_queue_size_ex_retry(block_id, init, ig.get_promise()); diff --git a/validator/queue-size-counter.hpp b/validator/queue-size-counter.hpp index 4825a43c0..7e5f76274 100644 --- a/validator/queue-size-counter.hpp +++ b/validator/queue-size-counter.hpp @@ -21,16 +21,22 @@ namespace ton::validator { class QueueSizeCounter : public td::actor::Actor { public: - QueueSizeCounter(td::Ref last_masterchain_state, td::actor::ActorId manager) - : init_masterchain_state_(last_masterchain_state), manager_(std::move(manager)) { + QueueSizeCounter(td::Ref last_masterchain_state, td::Ref opts, + td::actor::ActorId manager) + : init_masterchain_state_(last_masterchain_state), opts_(std::move(opts)), manager_(std::move(manager)) { } void start_up() override; void get_queue_size(BlockIdExt block_id, td::Promise promise); void alarm() override; + void update_options(td::Ref opts) { + opts_ = std::move(opts); + } + private: td::Ref init_masterchain_state_; + td::Ref opts_; td::actor::ActorId manager_; bool simple_mode_ = false; diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index 24dd77e87..ac86cf37d 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -70,18 +70,13 @@ void ShardClient::got_init_handle_from_db(BlockHandle handle) { } void ShardClient::got_init_state_from_db(td::Ref state) { - masterchain_state_ = std::move(state); - build_shard_overlays(); - masterchain_state_.clear(); - saved_to_db(); } void ShardClient::start_up_init_mode() { - build_shard_overlays(); std::vector shards; for (const auto& s : masterchain_state_->get_shards()) { - if (opts_->need_monitor(s->shard())) { + if (opts_->need_monitor(s->shard(), masterchain_state_)) { shards.push_back(s->top_block_id()); } } @@ -166,7 +161,6 @@ void ShardClient::download_masterchain_state() { void ShardClient::got_masterchain_block_state(td::Ref state) { masterchain_state_ = std::move(state); - build_shard_overlays(); if (started_) { apply_all_shards(); } @@ -189,8 +183,10 @@ void ShardClient::apply_all_shards() { ig.add_promise(std::move(P)); auto vec = masterchain_state_->get_shards(); + std::set workchains; for (auto &shard : vec) { - if (opts_->need_monitor(shard->shard())) { + workchains.insert(shard->shard().workchain); + if (opts_->need_monitor(shard->shard(), masterchain_state_)) { auto Q = td::PromiseCreator::lambda([SelfId = actor_id(this), promise = ig.get_promise(), shard = shard->shard()](td::Result> R) mutable { if (R.is_error()) { @@ -200,7 +196,22 @@ void ShardClient::apply_all_shards() { } }); td::actor::send_closure(manager_, &ValidatorManager::wait_block_state_short, shard->top_block_id(), - shard_client_priority(), td::Timestamp::in(600), std::move(Q)); + shard_client_priority(), td::Timestamp::in(1500), std::move(Q)); + } + } + for (const auto &[wc, desc] : masterchain_state_->get_workchain_list()) { + if (!workchains.count(wc) && desc->active && opts_->need_monitor(ShardIdFull{wc, shardIdAll}, masterchain_state_)) { + auto Q = td::PromiseCreator::lambda([SelfId = actor_id(this), promise = ig.get_promise(), + workchain = wc](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error_prefix(PSTRING() << "workchain " << workchain << ": ")); + } else { + td::actor::send_closure(SelfId, &ShardClient::downloaded_shard_state, R.move_as_ok(), std::move(promise)); + } + }); + td::actor::send_closure(manager_, &ValidatorManager::wait_block_state_short, + BlockIdExt{wc, shardIdAll, 0, desc->zerostate_root_hash, desc->zerostate_file_hash}, + shard_client_priority(), td::Timestamp::in(1500), std::move(Q)); } } } @@ -223,7 +234,6 @@ void ShardClient::new_masterchain_block_notification(BlockHandle handle, td::Ref masterchain_block_handle_ = std::move(handle); masterchain_state_ = std::move(state); waiting_ = false; - build_shard_overlays(); apply_all_shards(); } @@ -244,26 +254,6 @@ void ShardClient::get_processed_masterchain_block_id(td::Promise pro } } -void ShardClient::build_shard_overlays() { - auto v = masterchain_state_->get_shards(); - - for (auto &x : v) { - auto shard = x->shard(); - if (opts_->need_monitor(shard)) { - auto d = masterchain_state_->monitor_min_split_depth(shard.workchain); - auto l = shard_prefix_length(shard.shard); - if (l > d) { - shard = shard_prefix(shard, d); - } - - if (created_overlays_.count(shard) == 0) { - created_overlays_.insert(shard); - td::actor::send_closure(manager_, &ValidatorManager::subscribe_to_shard, shard); - } - } - } -} - void ShardClient::force_update_shard_client(BlockHandle handle, td::Promise promise) { CHECK(!init_mode_); CHECK(!started_); @@ -294,10 +284,13 @@ void ShardClient::force_update_shard_client_ex(BlockHandle handle, td::Ref opts) { + opts_ = std::move(opts); +} + } // namespace validator } // namespace ton diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index c1676debd..7c2c978c2 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -42,8 +42,6 @@ class ShardClient : public td::actor::Actor { td::Promise promise_; - std::set created_overlays_; - public: ShardClient(td::Ref opts, BlockHandle masterchain_block_handle, td::Ref masterchain_state, td::actor::ActorId manager, @@ -64,8 +62,6 @@ class ShardClient : public td::actor::Actor { return 2; } - void build_shard_overlays(); - void start_up() override; void start_up_init_mode(); void download_shard_states(BlockIdExt masterchain_block_id, std::vector shards, size_t idx); @@ -90,6 +86,8 @@ class ShardClient : public td::actor::Actor { void force_update_shard_client(BlockHandle handle, td::Promise promise); void force_update_shard_client_ex(BlockHandle handle, td::Ref state, td::Promise promise); + + void update_options(td::Ref opts); }; } // namespace validator diff --git a/validator/state-serializer.cpp b/validator/state-serializer.cpp index ab38a6e99..b693232b4 100644 --- a/validator/state-serializer.cpp +++ b/validator/state-serializer.cpp @@ -18,7 +18,6 @@ */ #include "state-serializer.hpp" #include "td/utils/Random.h" -#include "adnl/utils.hpp" #include "ton/ton-io.hpp" #include "common/delay.h" #include "td/utils/filesystem.h" @@ -151,6 +150,21 @@ void AsyncStateSerializer::next_iteration() { CHECK(masterchain_handle_->id() == last_block_id_); if (attempt_ < max_attempt() && last_key_block_id_.id.seqno < last_block_id_.id.seqno && need_serialize(masterchain_handle_)) { + if (!stored_persistent_state_description_) { + LOG(INFO) << "storing persistent state description for " << masterchain_handle_->id().id; + running_ = true; + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &AsyncStateSerializer::fail_handler, + R.move_as_error_prefix("failed to get masterchain state: ")); + } else { + td::actor::send_closure(SelfId, &AsyncStateSerializer::store_persistent_state_description, + td::Ref(R.move_as_ok())); + } + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, masterchain_handle_, std::move(P)); + return; + } if (!have_masterchain_state_ && !opts_->get_state_serializer_enabled()) { LOG(ERROR) << "skipping serializing persistent state for " << masterchain_handle_->id().id.to_str() << ": serializer is disabled (by user)"; @@ -174,14 +188,10 @@ void AsyncStateSerializer::next_iteration() { td::Timestamp::in(delay)); return; } - while (next_idx_ < shards_.size()) { - if (!need_monitor(shards_[next_idx_].shard_full())) { - next_idx_++; - } else { - running_ = true; - request_shard_state(shards_[next_idx_]); - return; - } + if (next_idx_ < shards_.size()) { + running_ = true; + request_shard_state(shards_[next_idx_]); + return; } LOG(ERROR) << "finished serializing persistent state for " << masterchain_handle_->id().id.to_str(); } @@ -203,6 +213,7 @@ void AsyncStateSerializer::next_iteration() { if (masterchain_handle_->inited_next_left()) { last_block_id_ = masterchain_handle_->one_next(true); have_masterchain_state_ = false; + stored_persistent_state_description_ = false; masterchain_handle_ = nullptr; saved_to_db_ = false; shards_.clear(); @@ -217,6 +228,24 @@ void AsyncStateSerializer::got_top_masterchain_handle(BlockIdExt block_id) { } } +void AsyncStateSerializer::store_persistent_state_description(td::Ref state) { + stored_persistent_state_description_ = true; + attempt_ = 0; + running_ = false; + + PersistentStateDescription desc; + desc.masterchain_id = state->get_block_id(); + desc.start_time = state->get_unix_time(); + desc.end_time = ValidatorManager::persistent_state_ttl(desc.start_time); + for (const auto &v : state->get_shards()) { + desc.shard_blocks.push_back(v->top_block_id()); + } + td::actor::send_closure(manager_, &ValidatorManager::add_persistent_state_description, + td::Ref(true, std::move(desc))); + + next_iteration(); +} + void AsyncStateSerializer::got_masterchain_handle(BlockHandle handle) { CHECK(!masterchain_handle_); masterchain_handle_ = std::move(handle); @@ -318,8 +347,10 @@ void AsyncStateSerializer::got_masterchain_state(td::Ref state CHECK(shards_.size() == 0); auto vec = state->get_shards(); - for (auto& v : vec) { - shards_.push_back(v->top_block_id()); + for (auto &v : vec) { + if (opts_->need_monitor(v->shard(), state)) { + shards_.push_back(v->top_block_id()); + } } auto write_data = [shard = state->get_shard(), root = state->root_cell(), cell_db_reader, @@ -447,11 +478,6 @@ void AsyncStateSerializer::auto_disable_serializer(bool disabled) { } } - -bool AsyncStateSerializer::need_monitor(ShardIdFull shard) { - return opts_->need_monitor(shard); -} - bool AsyncStateSerializer::need_serialize(BlockHandle handle) { if (handle->id().id.seqno == 0 || !handle->is_key_block()) { return false; diff --git a/validator/state-serializer.hpp b/validator/state-serializer.hpp index b38a216b7..1e7f5c9ca 100644 --- a/validator/state-serializer.hpp +++ b/validator/state-serializer.hpp @@ -46,6 +46,7 @@ class AsyncStateSerializer : public td::actor::Actor { td::uint32 next_idx_ = 0; BlockHandle masterchain_handle_; + bool stored_persistent_state_description_ = false; bool have_masterchain_state_ = false; std::vector shards_; @@ -69,7 +70,6 @@ class AsyncStateSerializer : public td::actor::Actor { } bool need_serialize(BlockHandle handle); - bool need_monitor(ShardIdFull shard); bool have_newer_persistent_state(UnixTime cur_ts); void alarm() override; @@ -84,6 +84,7 @@ class AsyncStateSerializer : public td::actor::Actor { void next_iteration(); void got_top_masterchain_handle(BlockIdExt block_id); + void store_persistent_state_description(td::Ref state); void got_masterchain_handle(BlockHandle handle_); void got_masterchain_state(td::Ref state, std::shared_ptr cell_db_reader); void stored_masterchain_state(); diff --git a/validator/validator-options.cpp b/validator/validator-options.cpp index 93fe05e6c..cb26fe44d 100644 --- a/validator/validator-options.cpp +++ b/validator/validator-options.cpp @@ -26,7 +26,7 @@ namespace validator { td::Ref ValidatorManagerOptions::create( BlockIdExt zero_block_id, BlockIdExt init_block_id, - std::function check_shard, bool allow_blockchain_init, + 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), diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 203aa5ebc..e958d8864 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -32,11 +32,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { BlockIdExt init_block_id() const override { return init_block_id_; } - bool need_monitor(ShardIdFull shard) const override { - return check_shard_(shard, 0, ShardCheckMode::m_monitor); - } - bool need_validate(ShardIdFull shard, CatchainSeqno cc_seqno) const override { - return check_shard_(shard, cc_seqno, ShardCheckMode::m_validate); + bool need_monitor(ShardIdFull shard, const td::Ref& state) const override { + td::uint32 min_split = state->monitor_min_split_depth(shard.workchain); + return check_shard_((td::uint32)shard.pfx_len() <= min_split ? shard : shard_prefix(shard, min_split)); } bool allow_blockchain_init() const override { return allow_blockchain_init_; @@ -163,7 +161,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { void set_init_block_id(BlockIdExt block_id) override { init_block_id_ = block_id; } - void set_shard_check_function(std::function check_shard) override { + void set_shard_check_function(std::function check_shard) override { check_shard_ = std::move(check_shard); } void set_allow_blockchain_init(bool value) override { @@ -257,11 +255,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { } ValidatorManagerOptionsImpl(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) + 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) : zero_block_id_(zero_block_id) , init_block_id_(init_block_id) , check_shard_(std::move(check_shard)) @@ -278,7 +274,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { private: BlockIdExt zero_block_id_; BlockIdExt init_block_id_; - std::function check_shard_; + std::function check_shard_; bool allow_blockchain_init_; double sync_blocks_before_; double block_ttl_; diff --git a/validator/validator.h b/validator/validator.h index 77a62540f..cc7cbe627 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -77,8 +77,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual BlockIdExt zero_block_id() const = 0; virtual BlockIdExt init_block_id() const = 0; - virtual bool need_monitor(ShardIdFull shard) const = 0; - virtual bool need_validate(ShardIdFull shard, CatchainSeqno cc_seqno) const = 0; + virtual bool need_monitor(ShardIdFull shard, const td::Ref& state) const = 0; virtual bool allow_blockchain_init() const = 0; virtual double sync_blocks_before() const = 0; virtual double block_ttl() const = 0; @@ -118,8 +117,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual void set_zero_block_id(BlockIdExt block_id) = 0; virtual void set_init_block_id(BlockIdExt block_id) = 0; - virtual void set_shard_check_function( - std::function check_shard) = 0; + virtual void set_shard_check_function(std::function check_shard) = 0; virtual void set_allow_blockchain_init(bool value) = 0; virtual void set_sync_blocks_before(double value) = 0; virtual void set_block_ttl(double value) = 0; @@ -151,12 +149,11 @@ struct ValidatorManagerOptions : public td::CntObject { static td::Ref create( BlockIdExt zero_block_id, BlockIdExt init_block_id, - std::function check_shard = [](ShardIdFull, CatchainSeqno, - ShardCheckMode) { return true; }, + + std::function check_shard = [](ShardIdFull) { return true; }, bool allow_blockchain_init = false, double sync_blocks_before = 3600, double block_ttl = 86400, double state_ttl = 86400, double archive_ttl = 86400 * 7, double key_proof_ttl = 86400 * 3650, - double max_mempool_num = 999999, - bool initial_sync_disabled = false); + double max_mempool_num = 999999, bool initial_sync_disabled = false); }; class ValidatorManagerInterface : public td::actor::Actor { @@ -166,8 +163,8 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual ~Callback() = default; virtual void initial_read_complete(BlockHandle top_masterchain_blocks) = 0; - virtual void add_shard(ShardIdFull shard) = 0; - virtual void del_shard(ShardIdFull shard) = 0; + virtual void on_new_masterchain_block(td::Ref state, + std::set shards_to_monitor) = 0; virtual void send_ihr_message(AccountIdPrefixFull dst, td::BufferSlice data) = 0; virtual void send_ext_message(AccountIdPrefixFull dst, td::BufferSlice data) = 0; @@ -187,8 +184,8 @@ class ValidatorManagerInterface : public td::actor::Actor { td::Promise promise) = 0; virtual void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) = 0; - virtual void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, - td::Promise promise) = 0; + virtual void download_archive(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, std::string tmp_dir, + td::Timestamp timeout, td::Promise promise) = 0; virtual void new_key_block(BlockHandle handle) = 0; virtual void send_validator_telemetry(PublicKeyHash key, tl_object_ptr telemetry) = 0; @@ -272,7 +269,13 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) = 0; - virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) = 0; + virtual void wait_block_state(BlockHandle handle, td::uint32 priority, td::Timestamp timeout, + td::Promise> promise) = 0; + virtual void wait_block_state_short(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, + td::Promise> promise) = 0; + + virtual void get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull shard_prefix, + td::Promise promise) = 0; virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, td::Promise promise) = 0;