/* 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. Copyright 2017-2019 Telegram Systems LLP */ #include "validator-engine.hpp" #include "ton/ton-types.h" #include "ton/ton-tl.hpp" #include "common/errorlog.h" #include "crypto/vm/cp0.h" #include "crypto/fift/utils.h" #include "td/utils/filesystem.h" #include "td/actor/MultiPromise.h" #include "td/utils/overloaded.h" #include "td/utils/OptionsParser.h" #include "td/utils/port/path.h" #include "td/utils/port/signals.h" #include "td/utils/port/user.h" #include "td/utils/ThreadSafeCounter.h" #include "td/utils/TsFileLog.h" #include "td/utils/Random.h" #include "auto/tl/lite_api.h" #include "memprof/memprof.h" #include "dht/dht.hpp" #if TD_DARWIN || TD_LINUX #include #endif #include #include #include #include #include Config::Config() { out_port = 3278; full_node = ton::PublicKeyHash::zero(); } Config::Config(ton::ton_api::engine_validator_config &config) { full_node = ton::PublicKeyHash::zero(); out_port = static_cast(config.out_port_); if (!out_port) { out_port = 3278; } for (auto &addr : config.addrs_) { td::IPAddress in_ip; td::IPAddress out_ip; std::shared_ptr proxy = nullptr; std::vector categories; std::vector priority_categories; ton::ton_api::downcast_call( *addr.get(), 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(); out_ip = in_ip; categories = obj.categories_; priority_categories = obj.priority_categories_; }, [&](const ton::ton_api::engine_addrProxy &obj) { in_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.in_ip_), static_cast(obj.in_port_)) .ensure(); out_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.out_ip_), static_cast(obj.out_port_)) .ensure(); if (obj.proxy_type_) { auto R = ton::adnl::AdnlProxy::create(*obj.proxy_type_.get()); R.ensure(); proxy = R.move_as_ok(); categories = obj.categories_; priority_categories = obj.priority_categories_; } })); config_add_network_addr(in_ip, out_ip, std::move(proxy), categories, priority_categories).ensure(); } for (auto &adnl : config.adnl_) { config_add_adnl_addr(ton::PublicKeyHash{adnl->id_}, adnl->category_).ensure(); } for (auto &dht : config.dht_) { config_add_dht_node(ton::PublicKeyHash{dht->id_}).ensure(); } for (auto &val : config.validators_) { auto key = ton::PublicKeyHash{val->id_}; config_add_validator_permanent_key(key, val->election_date_, val->expire_at_).ensure(); for (auto &temp : val->temp_keys_) { config_add_validator_temp_key(key, ton::PublicKeyHash{temp->key_}, temp->expire_at_).ensure(); } for (auto &adnl : val->adnl_addrs_) { config_add_validator_adnl_id(key, ton::PublicKeyHash{adnl->id_}, adnl->expire_at_).ensure(); } } config_add_full_node_adnl_id(ton::PublicKeyHash{config.fullnode_}).ensure(); for (auto &s : config.fullnodeslaves_) { td::IPAddress ip; ip.init_ipv4_port(td::IPAddress::ipv4_to_str(s->ip_), static_cast(s->port_)).ensure(); config_add_full_node_slave(ip, ton::PublicKey{s->adnl_}).ensure(); } for (auto &s : config.fullnodemasters_) { config_add_full_node_master(s->port_, ton::PublicKeyHash{s->adnl_}).ensure(); } for (auto &serv : config.liteservers_) { config_add_lite_server(ton::PublicKeyHash{serv->id_}, serv->port_).ensure(); } for (auto &serv : config.control_) { auto key = ton::PublicKeyHash{serv->id_}; config_add_control_interface(key, serv->port_).ensure(); for (auto &proc : serv->allowed_) { config_add_control_process(key, serv->port_, ton::PublicKeyHash{proc->id_}, proc->permissions_).ensure(); } } if (config.gc_) { for (auto &gc : config.gc_->ids_) { config_add_gc(ton::PublicKeyHash{gc}).ensure(); } } } ton::tl_object_ptr Config::tl() const { std::vector> addrs_vec; for (auto &x : addrs) { if (x.second.proxy) { addrs_vec.push_back(ton::create_tl_object( static_cast(x.second.in_addr.get_ipv4()), x.second.in_addr.get_port(), static_cast(x.first.addr.get_ipv4()), x.first.addr.get_port(), x.second.proxy->tl(), std::vector(x.second.cats.begin(), x.second.cats.end()), std::vector(x.second.priority_cats.begin(), x.second.priority_cats.end()))); } else { addrs_vec.push_back(ton::create_tl_object( static_cast(x.first.addr.get_ipv4()), x.first.addr.get_port(), std::vector(x.second.cats.begin(), x.second.cats.end()), std::vector(x.second.priority_cats.begin(), x.second.priority_cats.end()))); } } std::vector> adnl_vec; for (auto &x : adnl_ids) { adnl_vec.push_back(ton::create_tl_object(x.first.tl(), x.second)); } std::vector> dht_vec; for (auto &x : dht_ids) { dht_vec.push_back(ton::create_tl_object(x.tl())); } std::vector> val_vec; for (auto &val : validators) { std::vector> temp_vec; for (auto &t : val.second.temp_keys) { temp_vec.push_back(ton::create_tl_object(t.first.tl(), t.second)); } std::vector> adnl_val_vec; for (auto &t : val.second.adnl_ids) { adnl_val_vec.push_back(ton::create_tl_object(t.first.tl(), t.second)); } val_vec.push_back(ton::create_tl_object( val.first.tl(), std::move(temp_vec), std::move(adnl_val_vec), val.second.election_date, val.second.expire_at)); } std::vector> full_node_slaves_vec; for (auto &x : full_node_slaves) { full_node_slaves_vec.push_back(ton::create_tl_object( x.addr.get_ipv4(), x.addr.get_port(), x.key.tl())); } std::vector> full_node_masters_vec; for (auto &x : full_node_masters) { full_node_masters_vec.push_back( ton::create_tl_object(x.first, x.second.tl())); } std::vector> liteserver_vec; for (auto &x : liteservers) { liteserver_vec.push_back(ton::create_tl_object(x.second.tl(), x.first)); } std::vector> control_vec; for (auto &x : controls) { std::vector> control_proc_vec; for (auto &y : x.second.clients) { control_proc_vec.push_back(ton::create_tl_object(y.first.tl(), y.second)); } control_vec.push_back(ton::create_tl_object(x.second.key.tl(), x.first, std::move(control_proc_vec))); } 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(liteserver_vec), std::move(control_vec), std::move(gc_vec)); } td::Result Config::config_add_network_addr(td::IPAddress in_ip, td::IPAddress out_ip, std::shared_ptr proxy, std::vector cats, std::vector prio_cats) { Addr addr{out_ip}; auto it = addrs.find(addr); if (it != addrs.end()) { bool mod = false; if (!(it->second.in_addr == in_ip)) { it->second.in_addr = in_ip; mod = true; } if (it->second.proxy != proxy) { it->second.proxy = std::move(proxy); mod = true; } for (auto &c : cats) { if (it->second.cats.insert(c).second) { mod = true; } } for (auto &c : prio_cats) { if (it->second.priority_cats.insert(c).second) { mod = true; } } return mod; } else { it = addrs.emplace(std::move(addr), AddrCats{}).first; it->second.in_addr = in_ip; it->second.proxy = std::move(proxy); for (auto &c : cats) { it->second.cats.insert(c); } for (auto &c : prio_cats) { it->second.priority_cats.insert(c); } return true; } } td::Result Config::config_add_adnl_addr(ton::PublicKeyHash addr, AdnlCategory cat) { auto it = adnl_ids.find(addr); if (it != adnl_ids.end()) { if (it->second != cat) { it->second = cat; return true; } else { return false; } } else { incref(addr); adnl_ids.emplace(addr, cat); return true; } } td::Result Config::config_add_dht_node(ton::PublicKeyHash id) { if (dht_ids.count(id) > 0) { return false; } if (adnl_ids.count(id) == 0) { return td::Status::Error(ton::ErrorCode::notready, "to-be-added dht node not in adnl nodes list"); } incref(id); dht_ids.insert(id); return true; } td::Result Config::config_add_validator_permanent_key(ton::PublicKeyHash id, ton::UnixTime election_date, ton::UnixTime expire_at) { for (auto &x : validators) { if (x.second.election_date == election_date && x.first != id) { return td::Status::Error(ton::ErrorCode::protoviolation, "duplicate election date"); } } auto it = validators.find(id); if (it != validators.end()) { if (it->second.election_date != election_date) { return td::Status::Error(ton::ErrorCode::protoviolation, "election date changed"); } if (it->second.expire_at != expire_at) { it->second.expire_at = expire_at; return true; } else { return false; } } else { incref(id); validators[id] = Validator{{}, {}, election_date, expire_at}; return true; } } td::Result Config::config_add_validator_temp_key(ton::PublicKeyHash perm_key, ton::PublicKeyHash id, ton::UnixTime expire_at) { if (validators.count(perm_key) == 0) { return td::Status::Error(ton::ErrorCode::notready, "unknown permanent validator key"); } auto &v = validators[perm_key]; auto it = v.temp_keys.find(id); if (it != v.temp_keys.end()) { if (it->second != expire_at) { it->second = expire_at; return true; } else { return false; } } else { incref(id); v.temp_keys.emplace(id, expire_at); return true; } } td::Result Config::config_add_validator_adnl_id(ton::PublicKeyHash perm_key, ton::PublicKeyHash adnl_id, ton::UnixTime expire_at) { if (adnl_ids.count(adnl_id) == 0) { return td::Status::Error(ton::ErrorCode::notready, "to-be-added validator adnl address not in adnl nodes list"); } if (validators.count(perm_key) == 0) { return td::Status::Error(ton::ErrorCode::notready, "unknown permanent validator key"); } auto &v = validators[perm_key]; auto it = v.adnl_ids.find(adnl_id); if (it != v.adnl_ids.end()) { if (it->second != expire_at) { it->second = expire_at; return true; } else { return false; } } else { incref(adnl_id); v.adnl_ids.emplace(adnl_id, expire_at); return true; } } td::Result Config::config_add_full_node_adnl_id(ton::PublicKeyHash id) { if (full_node == id) { return false; } if (adnl_ids.count(id) == 0) { return td::Status::Error(ton::ErrorCode::notready, "to-be-added full node adnl address not in adnl nodes list"); } if (!full_node.is_zero()) { decref(full_node); } if (!id.is_zero()) { incref(id); } full_node = id; return true; } td::Result Config::config_add_full_node_slave(td::IPAddress addr, ton::PublicKey id) { for (auto &s : full_node_slaves) { if (s.addr == addr) { if (s.key == id) { return true; } else { return td::Status::Error(ton::ErrorCode::error, "duplicate slave ip"); } } } full_node_slaves.push_back(FullNodeSlave{id, addr}); return true; } td::Result Config::config_add_full_node_master(td::int32 port, ton::PublicKeyHash id) { if (adnl_ids.count(id) == 0) { return td::Status::Error(ton::ErrorCode::notready, "to-be-added full node master adnl address not in adnl nodes list"); } auto it = full_node_masters.find(port); if (it != full_node_masters.end()) { if (it->second == id) { return false; } else { return td::Status::Error("duplicate master port"); } } if (liteservers.count(port) > 0 || controls.count(port) > 0) { return td::Status::Error("duplicate master port"); } incref(id); full_node_masters.emplace(port, id); return true; } td::Result Config::config_add_lite_server(ton::PublicKeyHash key, td::int32 port) { if (controls.count(port) > 0) { return td::Status::Error(ton::ErrorCode::error, "duplicate port"); } auto it = liteservers.find(port); if (it != liteservers.end()) { if (it->second == key) { return false; } else { return td::Status::Error(ton::ErrorCode::error, "duplicate port"); } } else { incref(key); liteservers.emplace(port, key); return true; } } td::Result Config::config_add_control_interface(ton::PublicKeyHash key, td::int32 port) { if (liteservers.count(port) > 0) { return td::Status::Error(ton::ErrorCode::error, "duplicate port"); } auto it = controls.find(port); if (it != controls.end()) { if (it->second.key == key) { return false; } else { return td::Status::Error(ton::ErrorCode::error, "duplicate port"); } } else { incref(key); controls.emplace(port, Control{key, {}}); return true; } } td::Result Config::config_add_control_process(ton::PublicKeyHash key, td::int32 port, ton::PublicKeyHash id, td::uint32 permissions) { if (controls.count(port) == 0) { return td::Status::Error(ton::ErrorCode::error, "unknown control interface"); } auto &v = controls[port]; if (v.key != key) { return td::Status::Error(ton::ErrorCode::error, "unknown control interface"); } auto it = v.clients.find(id); if (it != v.clients.end()) { if (!permissions) { v.clients.erase(id); return true; } if (it->second != permissions) { it->second = permissions; return true; } else { return false; } } else { if (!permissions) { return false; } v.clients.emplace(id, permissions); return true; } } td::Result Config::config_add_gc(ton::PublicKeyHash key) { return gc.insert(key).second; } void Config::decref(ton::PublicKeyHash key) { auto v = keys_refcnt[key]--; CHECK(v > 0); if (v == 1) { config_add_gc(key).ensure(); } } td::Result Config::config_del_network_addr(td::IPAddress a, std::vector cats, std::vector prio_cats) { Addr addr{a}; auto it = addrs.find(addr); if (it != addrs.end()) { bool mod = false; for (auto &c : cats) { if (it->second.cats.erase(c)) { mod = true; } } for (auto &c : prio_cats) { if (it->second.priority_cats.erase(c)) { mod = true; } } if (it->second.cats.size() == 0 && it->second.priority_cats.size() == 0) { addrs.erase(it); } return mod; } else { return false; } } td::Result Config::config_del_adnl_addr(ton::PublicKeyHash addr) { if (adnl_ids.count(addr) == 0) { return false; } if (dht_ids.count(addr)) { return td::Status::Error(ton::ErrorCode::error, "adnl addr still in use"); } if (full_node == addr) { return td::Status::Error(ton::ErrorCode::error, "adnl addr still in use"); } for (auto &x : validators) { if (x.second.adnl_ids.count(addr)) { return td::Status::Error(ton::ErrorCode::error, "adnl addr still in use"); } } decref(addr); adnl_ids.erase(addr); return true; } td::Result Config::config_del_dht_node(ton::PublicKeyHash id) { if (dht_ids.count(id) == 0) { return false; } decref(id); dht_ids.erase(id); return true; } td::Result Config::config_del_validator_permanent_key(ton::PublicKeyHash id) { if (validators.count(id) == 0) { return false; } auto &v = validators[id]; for (auto &temp_key : v.temp_keys) { decref(temp_key.first); } for (auto &adnl_id : v.adnl_ids) { decref(adnl_id.first); } decref(id); validators.erase(id); return true; } td::Result Config::config_del_validator_temp_key(ton::PublicKeyHash perm_key, ton::PublicKeyHash id) { if (validators.count(perm_key) == 0) { return td::Status::Error(ton::ErrorCode::notready, "unknown permanent validator key"); } auto &v = validators[perm_key]; auto it = v.temp_keys.find(id); if (it != v.temp_keys.end()) { decref(id); v.temp_keys.erase(id); return true; } else { return false; } } td::Result Config::config_del_validator_adnl_id(ton::PublicKeyHash perm_key, ton::PublicKeyHash adnl_id) { if (validators.count(perm_key) == 0) { return td::Status::Error(ton::ErrorCode::notready, "unknown permanent validator key"); } auto &v = validators[perm_key]; auto it = v.adnl_ids.find(adnl_id); if (it != v.temp_keys.end()) { decref(adnl_id); v.adnl_ids.erase(adnl_id); return true; } else { return false; } } td::Result Config::config_del_full_node_adnl_id() { return config_add_full_node_adnl_id(ton::PublicKeyHash::zero()); } td::Result Config::config_del_lite_server(td::int32 port) { auto it = liteservers.find(port); if (it != liteservers.end()) { decref(it->second); liteservers.erase(it); return true; } else { return false; } } td::Result Config::config_del_control_interface(td::int32 port) { auto it = controls.find(port); if (it != controls.end()) { decref(it->second.key); controls.erase(it); return true; } else { return false; } } td::Result Config::config_del_control_process(td::int32 port, ton::PublicKeyHash id) { auto it = controls.find(port); if (it != controls.end()) { return it->second.clients.erase(id); } else { return false; } } td::Result Config::config_del_gc(ton::PublicKeyHash key) { return gc.erase(key); } class ValidatorElectionBidCreator : public td::actor::Actor { public: ValidatorElectionBidCreator(td::uint32 date, std::string addr, std::string wallet, std::string dir, td::actor::ActorId engine, td::actor::ActorId keyring, td::Promise promise) : date_(date) , addr_(addr) , wallet_(wallet) , dir_(dir) , engine_(engine) , keyring_(keyring) , promise_(std::move(promise)) { ttl_ = date_ + 7 * 86400; } void start_up() override { auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; perm_key_full_ = pk1.compute_public_key(); perm_key_ = perm_key_full_.compute_short_id(); auto pk2 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; adnl_key_full_ = ton::adnl::AdnlNodeIdFull{pk2.compute_public_key()}; adnl_addr_ = adnl_key_full_.compute_short_id(); td::MultiPromise mp; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::abort_query, R.move_as_error_prefix("keyring fail: ")); } else { td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::written_keys); } }); auto ig = mp.init_guard(); ig.add_promise(std::move(P)); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk1), false, ig.get_promise()); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk2), false, ig.get_promise()); } void written_keys() { td::MultiPromise mp; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::abort_query, R.move_as_error_prefix("update config fail: ")); } else { td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::updated_config); } }); auto ig = mp.init_guard(); ig.add_promise(std::move(P)); td::actor::send_closure(engine_, &ValidatorEngine::add_key_to_set, perm_key_full_); td::actor::send_closure(engine_, &ValidatorEngine::add_key_to_set, adnl_key_full_.pubkey()); td::actor::send_closure(engine_, &ValidatorEngine::try_add_validator_permanent_key, perm_key_, date_, ttl_, ig.get_promise()); td::actor::send_closure(engine_, &ValidatorEngine::try_add_validator_temp_key, perm_key_, perm_key_, ttl_, ig.get_promise()); td::actor::send_closure(engine_, &ValidatorEngine::try_add_adnl_node, adnl_addr_.pubkey_hash(), cat_, ig.get_promise()); td::actor::send_closure(engine_, &ValidatorEngine::try_add_validator_adnl_addr, perm_key_, adnl_addr_.pubkey_hash(), ttl_, ig.get_promise()); } void updated_config() { auto codeR = td::read_file_str(dir_ + "/validator-elect-req.fif"); if (codeR.is_error()) { abort_query(codeR.move_as_error_prefix("fif not found (validator-elect-req.fif)")); return; } auto R = fift::mem_run_fift(codeR.move_as_ok(), {"validator-elect-req.fif", wallet_, td::to_string(date_), td::to_string(frac), adnl_addr_.bits256_value().to_hex(), "OUTPUT"}, dir_ + "/"); if (R.is_error()) { abort_query(R.move_as_error_prefix("fift fail (validator-elect-req.fif)")); return; } auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::abort_query, R.move_as_error_prefix("sign fail: ")); } else { td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::signed_bid, R.move_as_ok()); } }); auto res = R.move_as_ok(); auto to_signR = res.source_lookup.read_file("OUTPUT"); if (to_signR.is_error()) { abort_query(td::Status::Error(PSTRING() << "strange error: no to sign file. Output: " << res.output)); return; } td::actor::send_closure(keyring_, &ton::keyring::Keyring::sign_message, perm_key_, td::BufferSlice{to_signR.move_as_ok().data}, std::move(P)); } void signed_bid(td::BufferSlice signature) { signature_ = std::move(signature); auto codeR = td::read_file_str(dir_ + "/validator-elect-signed.fif"); if (codeR.is_error()) { abort_query(codeR.move_as_error_prefix("fif not found (validator-elect-req.fif)")); return; } auto R = fift::mem_run_fift( codeR.move_as_ok(), {"validator-elect-signed.fif", wallet_, td::to_string(date_), td::to_string(frac), adnl_addr_.bits256_value().to_hex(), td::base64_encode(perm_key_full_.export_as_slice().as_slice()), td::base64_encode(signature_.as_slice()), "OUTPUT"}, dir_ + "/"); if (R.is_error()) { abort_query(R.move_as_error_prefix("fift fail (validator-elect-req.fif)")); return; } auto res = R.move_as_ok(); auto dataR = res.source_lookup.read_file("OUTPUT"); if (dataR.is_error()) { abort_query(td::Status::Error("strage error: no result boc")); return; } result_ = td::BufferSlice(dataR.move_as_ok().data); finish_query(); } void abort_query(td::Status error) { promise_.set_value(ValidatorEngine::create_control_query_error(std::move(error))); stop(); } void finish_query() { promise_.set_value(ton::create_serialize_tl_object( date_, perm_key_.tl(), adnl_addr_.bits256_value(), std::move(result_))); stop(); } private: td::uint32 date_; std::string addr_; std::string wallet_; std::string dir_; td::actor::ActorId engine_; td::actor::ActorId keyring_; td::Promise promise_; td::uint32 ttl_; AdnlCategory cat_ = 2; double frac = 2.7; ton::PublicKeyHash perm_key_; ton::PublicKey perm_key_full_; ton::adnl::AdnlNodeIdShort adnl_addr_; ton::adnl::AdnlNodeIdFull adnl_key_full_; td::BufferSlice signature_; td::BufferSlice result_; }; class CheckDhtServerStatusQuery : public td::actor::Actor { public: void start_up() override { auto &n = dht_config_->nodes(); result_.resize(n.size(), false); pending_ = n.size(); for (td::uint32 i = 0; i < n.size(); i++) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), idx = i](td::Result R) { td::actor::send_closure(SelfId, &CheckDhtServerStatusQuery::got_result, idx, R.is_ok()); }); auto &E = n.list().at(i); td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_peer, local_id_, E.adnl_id(), E.addr_list()); td::actor::send_closure(adnl_, &ton::adnl::Adnl::send_query, local_id_, E.adnl_id().compute_short_id(), "ping", std::move(P), td::Timestamp::in(1.0), ton::create_serialize_tl_object()); } } void got_result(td::uint32 idx, bool result) { result_[idx] = result; CHECK(pending_ > 0); if (!--pending_) { finish_query(); } } void finish_query() { std::vector> vec; auto &n = dht_config_->nodes(); for (td::uint32 i = 0; i < n.size(); i++) { auto &E = n.list().at(i); vec.push_back(ton::create_tl_object( E.adnl_id().compute_short_id().bits256_value(), result_[i] ? 1 : 0)); } promise_.set_value( ton::create_serialize_tl_object(std::move(vec))); stop(); } CheckDhtServerStatusQuery(std::shared_ptr dht_config, ton::adnl::AdnlNodeIdShort local_id, td::actor::ActorId adnl, td::Promise promise) : dht_config_(std::move(dht_config)), local_id_(local_id), adnl_(adnl), promise_(std::move(promise)) { } private: std::shared_ptr dht_config_; std::vector result_; td::uint32 pending_; ton::adnl::AdnlNodeIdShort local_id_; td::actor::ActorId adnl_; td::Promise promise_; }; void ValidatorEngine::set_local_config(std::string str) { local_config_ = str; } void ValidatorEngine::set_global_config(std::string str) { global_config_ = str; } void ValidatorEngine::set_db_root(std::string db_root) { db_root_ = db_root; } void ValidatorEngine::start_up() { alarm_timestamp() = td::Timestamp::in(1.0 + td::Random::fast(0, 100) * 0.01); } void ValidatorEngine::alarm() { alarm_timestamp() = td::Timestamp::in(1.0 + td::Random::fast(0, 100) * 0.01); if (started_) { if (!validator_manager_.empty()) { auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this)](td::Result> R) { if (R.is_ok()) { td::actor::send_closure(SelfId, &ValidatorEngine::got_state, R.move_as_ok()); } }); td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::get_top_masterchain_state, std::move(P)); } if (state_.not_null()) { bool need_write = false; auto val_set = state_->get_total_validator_set(0); auto e = val_set->export_vector(); std::set adnl_ids; for (auto &el : e) { adnl_ids.insert(ton::PublicKeyHash{el.addr}); } std::set to_del; for (auto &val : config_.validators) { if (val.second.expire_at < state_->get_unix_time() && !val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) { to_del.insert(val.first); } else { std::set to_del_2; for (auto &x : val.second.adnl_ids) { if (x.second < state_->get_unix_time() && !adnl_ids.count(x.first)) { to_del_2.insert(x.first); } } for (auto &x : to_del_2) { config_.config_del_validator_temp_key(val.first, x); need_write = true; } } } for (auto &x : to_del) { config_.config_del_validator_permanent_key(x); if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::del_permanent_key, x, [](td::Unit) {}); } if (!full_node_.empty()) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::del_permanent_key, x, [](td::Unit) {}); } need_write = true; } if (need_write) { write_config([](td::Unit) {}); } } for (auto &x : config_.gc) { if (running_gc_.count(x) == 0) { running_gc_.insert(x); keys_.erase(x); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), x](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &ValidatorEngine::deleted_key, x); }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::del_key, x, std::move(P)); } } } } void ValidatorEngine::deleted_key(ton::PublicKeyHash x) { CHECK(running_gc_.count(x) == 1); running_gc_.erase(x); auto R = config_.config_del_gc(x); R.ensure(); if (R.move_as_ok()) { write_config([](td::Unit) {}); } } td::Status ValidatorEngine::load_global_config() { TRY_RESULT_PREFIX(conf_data, td::read_file(global_config_), "failed to read: "); TRY_RESULT_PREFIX(conf_json, td::json_decode(conf_data.as_slice()), "failed to parse json: "); ton::ton_api::config_global conf; TRY_STATUS_PREFIX(ton::ton_api::from_json(conf, conf_json.get_object()), "json does not fit TL scheme: "); // TODO // add adnl static nodes if (conf.adnl_) { if (conf.adnl_->static_nodes_) { TRY_RESULT_PREFIX_ASSIGN(adnl_static_nodes_, ton::adnl::AdnlNodesList::create(conf.adnl_->static_nodes_), "bad static adnl nodes: "); } } if (!conf.dht_) { return td::Status::Error(ton::ErrorCode::error, "does not contain [dht] section"); } TRY_RESULT_PREFIX(dht, ton::dht::Dht::create_global_config(std::move(conf.dht_)), "bad [dht] section: "); dht_config_ = std::move(dht); if (!conf.validator_) { return td::Status::Error(ton::ErrorCode::error, "does not contain [validator] section"); } if (!conf.validator_->zero_state_) { return td::Status::Error(ton::ErrorCode::error, "[validator] section does not contain [zero_state]"); } auto zero_state = ton::create_block_id(conf.validator_->zero_state_); if (zero_state.id.workchain != ton::masterchainId || zero_state.id.shard != ton::shardIdAll || zero_state.id.seqno != 0) { return td::Status::Error(ton::ErrorCode::error, "[validator] section contains invalid [zero_state]"); } if (zero_state.root_hash.is_zero() || zero_state.file_hash.is_zero()) { return td::Status::Error(ton::ErrorCode::error, "[validator] section contains incomplete [zero_state]"); } ton::BlockIdExt init_block; if (!conf.validator_->init_block_) { init_block = zero_state; } else { init_block = ton::create_block_id(conf.validator_->init_block_); if (init_block.id.workchain != ton::masterchainId || init_block.id.shard != ton::shardIdAll) { return td::Status::Error(ton::ErrorCode::error, "[validator] section contains invalid [init_block]"); } if (init_block.root_hash.is_zero() || init_block.file_hash.is_zero()) { return td::Status::Error(ton::ErrorCode::error, "[validator] section contains incomplete [init_block]"); } } validator_options_ = ton::validator::ValidatorManagerOptions::create(zero_state, init_block); if (state_ttl_ != 0) { validator_options_.write().set_state_ttl(state_ttl_); } if (block_ttl_ != 0) { validator_options_.write().set_block_ttl(block_ttl_); } if (sync_ttl_ != 0) { validator_options_.write().set_sync_blocks_before(sync_ttl_); } if (archive_ttl_ != 0) { validator_options_.write().set_archive_ttl(archive_ttl_); } if (key_proof_ttl_ != 0) { validator_options_.write().set_key_proof_ttl(key_proof_ttl_); } if (db_depth_ <= 32) { validator_options_.write().set_filedb_depth(db_depth_); } std::vector h; for (auto &x : conf.validator_->hardforks_) { auto b = ton::create_block_id(x); if (!b.is_masterchain()) { return td::Status::Error(ton::ErrorCode::error, "[validator/hardforks] section contains not masterchain block id"); } if (!b.is_valid_full()) { return td::Status::Error(ton::ErrorCode::error, "[validator/hardforks] section contains invalid block_id"); } for (auto &y : h) { if (y.is_valid() && y.seqno() >= b.seqno()) { y.invalidate(); } } h.push_back(b); } validator_options_.write().set_hardforks(std::move(h)); return td::Status::OK(); } 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 { if (R.is_error()) { promise.set_error(R.move_as_error()); } else { td::actor::send_closure(SelfId, &ValidatorEngine::write_config, std::move(promise)); } }); td::MultiPromise mp; auto ig = mp.init_guard(); ig.add_promise(std::move(ret_promise)); for (auto &addr : addrs_) { config_.config_add_network_addr(addr, addr, nullptr, std::vector{0, 1, 2, 3}, std::vector{}) .ensure(); } { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); auto id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_adnl_addr(id, 0).ensure(); config_.config_add_dht_node(id).ensure(); } { auto adnl_pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; keys_.emplace(adnl_pk.compute_short_id(), adnl_pk.compute_public_key()); auto adnl_short_id = adnl_pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(adnl_pk), false, ig.get_promise()); config_.config_add_adnl_addr(adnl_short_id, 1).ensure(); config_.config_add_full_node_adnl_id(adnl_short_id).ensure(); } } void ValidatorEngine::load_local_config(td::Promise promise) { if (local_config_.size() == 0) { load_empty_local_config(std::move(promise)); return; } auto conf_data_R = td::read_file(local_config_); if (conf_data_R.is_error()) { promise.set_error(conf_data_R.move_as_error_prefix("failed to read: ")); return; } auto conf_data = conf_data_R.move_as_ok(); auto conf_json_R = td::json_decode(conf_data.as_slice()); if (conf_json_R.is_error()) { promise.set_error(conf_data_R.move_as_error_prefix("failed to parse json: ")); return; } auto conf_json = conf_json_R.move_as_ok(); ton::ton_api::config_local conf; auto S = ton::ton_api::from_json(conf, conf_json.get_object()); if (S.is_error()) { promise.set_error(S.move_as_error_prefix("json does not fit TL scheme")); return; } auto ret_promise = td::PromiseCreator::lambda( [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); } else { td::actor::send_closure(SelfId, &ValidatorEngine::write_config, std::move(promise)); } }); td::MultiPromise mp; auto ig = mp.init_guard(); ig.add_promise(std::move(ret_promise)); for (auto &addr : addrs_) { config_.config_add_network_addr(addr, addr, nullptr, std::vector{0, 1, 2, 3}, std::vector{}) .ensure(); } for (auto &local_id : conf.local_ids_) { ton::PrivateKey pk{local_id->id_}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); } td::uint32 max_time = 2000000000; if (conf.dht_.size() > 0) { for (auto &d : conf.dht_) { ton::ton_api::downcast_call(*d.get(), td::overloaded( [&](ton::ton_api::dht_config_local &obj) { auto node_id = ton::adnl::AdnlNodeIdShort{obj.id_->id_}; auto it = keys_.find(node_id.pubkey_hash()); if (it == keys_.end()) { ig.get_promise().set_error(td::Status::Error( ton::ErrorCode::error, "cannot find private key for dht")); return; } config_.config_add_adnl_addr(node_id.pubkey_hash(), 0).ensure(); config_.config_add_dht_node(node_id.pubkey_hash()).ensure(); }, [&](ton::ton_api::dht_config_random_local &obj) { for (td::int32 i = 0; i < obj.cnt_; i++) { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); auto id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_adnl_addr(id, 0).ensure(); config_.config_add_dht_node(id).ensure(); } })); } } else { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); auto id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_adnl_addr(id, 0).ensure(); config_.config_add_dht_node(id).ensure(); } if (conf.validators_.size() > 0) { for (auto &val : conf.validators_) { ton::ton_api::downcast_call( *val.get(), td::overloaded( [&](ton::ton_api::validator_config_local &obj) { auto id = ton::PublicKeyHash{obj.id_->id_}; auto it = keys_.find(id); if (it == keys_.end()) { ig.get_promise().set_error( td::Status::Error(ton::ErrorCode::error, "cannot find private key for dht")); return; } config_.config_add_adnl_addr(id, 2).ensure(); config_.config_add_validator_permanent_key(id, 0, max_time).ensure(); config_.config_add_validator_temp_key(id, id, max_time).ensure(); config_.config_add_validator_adnl_id(id, id, max_time).ensure(); }, [&](ton::ton_api::validator_config_random_local &obj) { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); auto id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_adnl_addr(id, 2).ensure(); config_.config_add_validator_permanent_key(id, 0, max_time).ensure(); config_.config_add_validator_temp_key(id, id, max_time).ensure(); config_.config_add_validator_adnl_id(id, id, max_time).ensure(); })); } } else { // DO NOTHING } { auto adnl_pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; keys_.emplace(adnl_pk.compute_short_id(), adnl_pk.compute_public_key()); auto adnl_short_id = adnl_pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(adnl_pk), false, ig.get_promise()); config_.config_add_adnl_addr(adnl_short_id, 1).ensure(); config_.config_add_full_node_adnl_id(adnl_short_id).ensure(); } for (auto &ls : conf.liteservers_) { ton::ton_api::downcast_call(*ls.get(), td::overloaded( [&](ton::ton_api::liteserver_config_local &cfg) { ton::PrivateKey pk{cfg.id_}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); auto short_id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_lite_server(short_id, cfg.port_).ensure(); }, [&](ton::ton_api::liteserver_config_random_local &cfg) { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; auto short_id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_lite_server(short_id, cfg.port_).ensure(); })); } for (auto &ci : conf.control_) { ton::PrivateKey pk{ci->priv_}; keys_.emplace(pk.compute_short_id(), pk.compute_public_key()); auto short_id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, ig.get_promise()); config_.config_add_control_interface(short_id, ci->port_).ensure(); config_.config_add_control_process(short_id, ci->port_, ton::PublicKeyHash{ci->pub_}, 0x7fffffff).ensure(); } } void ValidatorEngine::load_config(td::Promise promise) { if (!config_file_.size()) { config_file_ = db_root_ + "/config.json"; } auto conf_data_R = td::read_file(config_file_); if (conf_data_R.is_error()) { auto P = td::PromiseCreator::lambda( [name = local_config_, new_name = config_file_, promise = std::move(promise)](td::Result R) { if (R.is_error()) { LOG(ERROR) << "failed to parse local config '" << name << "': " << R.move_as_error(); std::_Exit(2); } else { LOG(ERROR) << "created config file '" << new_name << "'"; LOG(ERROR) << "check it manually before continue"; std::_Exit(0); } }); load_local_config(std::move(P)); return; } auto conf_data = conf_data_R.move_as_ok(); auto conf_json_R = td::json_decode(conf_data.as_slice()); if (conf_json_R.is_error()) { promise.set_error(conf_json_R.move_as_error_prefix("failed to parse json: ")); return; } auto conf_json = conf_json_R.move_as_ok(); ton::ton_api::engine_validator_config conf; auto S = ton::ton_api::from_json(conf, conf_json.get_object()); if (S.is_error()) { promise.set_error(S.move_as_error_prefix("json does not fit TL scheme")); return; } config_ = Config{conf}; td::MultiPromise mp; auto ig = mp.init_guard(); ig.add_promise(std::move(promise)); for (auto &key : config_.keys_refcnt) { td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key_short, key.first, get_key_promise(ig)); } write_config(ig.get_promise()); } void ValidatorEngine::write_config(td::Promise promise) { auto s = td::json_encode(td::ToJson(*config_.tl().get()), true); auto S = td::write_file(config_file_, s); if (S.is_ok()) { promise.set_value(td::Unit()); } else { promise.set_error(std::move(S)); } } td::Promise ValidatorEngine::get_key_promise(td::MultiPromise::InitGuard &ig) { auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), promise = ig.get_promise()](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); } else { td::actor::send_closure(SelfId, &ValidatorEngine::got_key, R.move_as_ok()); promise.set_value(td::Unit()); } }); return std::move(P); } void ValidatorEngine::got_key(ton::PublicKey key) { keys_[key.compute_short_id()] = key; } void ValidatorEngine::start() { read_config_ = true; start_adnl(); } void ValidatorEngine::start_adnl() { adnl_network_manager_ = ton::adnl::AdnlNetworkManager::create(config_.out_port); adnl_ = ton::adnl::Adnl::create(db_root_, keyring_.get()); td::actor::send_closure(adnl_, &ton::adnl::Adnl::register_network_manager, adnl_network_manager_.get()); for (auto &addr : config_.addrs) { add_addr(addr.first, addr.second); } for (auto &adnl : config_.adnl_ids) { add_adnl(adnl.first, adnl.second); } td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_static_nodes_from_config, std::move(adnl_static_nodes_)); started_adnl(); } void ValidatorEngine::add_addr(const Config::Addr &addr, const Config::AddrCats &cats) { if (!cats.proxy) { td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr.addr, cats.cats.size() ? 0 : 1); } else { td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_proxy_addr, addr.addr, cats.proxy, cats.cats.size() ? 0 : 1); } td::uint32 ts = static_cast(td::Clocks::system()); for (auto cat : cats.cats) { CHECK(cat >= 0); ton::adnl::AdnlAddress x = ton::adnl::AdnlAddressImpl::create( ton::create_tl_object(cats.in_addr.get_ipv4(), cats.in_addr.get_port())); addr_lists_[cat].add_addr(std::move(x)); addr_lists_[cat].set_version(ts); addr_lists_[cat].set_reinit_date(ton::adnl::Adnl::adnl_start_time()); } for (auto cat : cats.priority_cats) { CHECK(cat >= 0); ton::adnl::AdnlAddress x = ton::adnl::AdnlAddressImpl::create( ton::create_tl_object(cats.in_addr.get_ipv4(), cats.in_addr.get_port())); prio_addr_lists_[cat].add_addr(std::move(x)); prio_addr_lists_[cat].set_version(ts); prio_addr_lists_[cat].set_reinit_date(ton::adnl::Adnl::adnl_start_time()); } } void ValidatorEngine::add_adnl(ton::PublicKeyHash id, AdnlCategory cat) { CHECK(keys_.count(id) > 0); td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, addr_lists_[cat]); } void ValidatorEngine::started_adnl() { start_dht(); } void ValidatorEngine::add_dht(ton::PublicKeyHash id) { auto D = ton::dht::Dht::create(ton::adnl::AdnlNodeIdShort{id}, db_root_, dht_config_, keyring_.get(), adnl_.get()); D.ensure(); dht_nodes_[id] = D.move_as_ok(); if (default_dht_node_.is_zero()) { default_dht_node_ = id; } } void ValidatorEngine::start_dht() { for (auto &dht : config_.dht_ids) { add_dht(dht); } if (default_dht_node_.is_zero()) { LOG(ERROR) << "trying to work without DHT"; } else { td::actor::send_closure(adnl_, &ton::adnl::Adnl::register_dht_node, dht_nodes_[default_dht_node_].get()); } started_dht(); } void ValidatorEngine::started_dht() { start_rldp(); } void ValidatorEngine::start_rldp() { rldp_ = ton::rldp::Rldp::create(adnl_.get()); started_rldp(); } void ValidatorEngine::started_rldp() { start_overlays(); } void ValidatorEngine::start_overlays() { if (!default_dht_node_.is_zero()) { overlay_manager_ = ton::overlay::Overlays::create(db_root_, keyring_.get(), adnl_.get(), dht_nodes_[default_dht_node_].get()); } started_overlays(); } void ValidatorEngine::started_overlays() { start_validator(); } void ValidatorEngine::start_validator() { validator_options_.write().set_allow_blockchain_init(config_.validators.size() > 0); validator_manager_ = ton::validator::ValidatorManagerFactory::create( validator_options_, db_root_, keyring_.get(), adnl_.get(), rldp_.get(), overlay_manager_.get()); for (auto &v : config_.validators) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_permanent_key, v.first, [](td::Unit) {}); for (auto &t : v.second.temp_keys) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_temp_key, t.first, [](td::Unit) {}); } } started_validator(); } void ValidatorEngine::started_validator() { start_full_node(); } void ValidatorEngine::start_full_node() { if (!config_.full_node.is_zero() || config_.full_node_slaves.size() > 0) { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; auto short_id = pk.compute_short_id(); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), true, [](td::Unit) {}); if (config_.full_node_slaves.size() > 0) { std::vector> vec; for (auto &x : config_.full_node_slaves) { vec.emplace_back(ton::adnl::AdnlNodeIdFull{x.key}, x.addr); } class Cb : public ton::adnl::AdnlExtClient::Callback { public: void on_ready() override { } void on_stop_ready() override { } }; full_node_client_ = ton::adnl::AdnlExtMultiClient::create(std::move(vec), std::make_unique()); } full_node_ = ton::validator::fullnode::FullNode::create( short_id, ton::adnl::AdnlNodeIdShort{config_.full_node}, validator_options_->zero_block_id().file_hash, keyring_.get(), adnl_.get(), rldp_.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_); } 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() { start_lite_server(); } void ValidatorEngine::add_lite_server(ton::PublicKeyHash id, td::uint16 port) { td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, ton::adnl::AdnlAddressList{}); td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_ext_server_id, ton::adnl::AdnlNodeIdShort{id}); td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_ext_server_port, port); } void ValidatorEngine::start_lite_server() { for (auto &s : config_.liteservers) { add_lite_server(s.second, static_cast(s.first)); } started_lite_server(); } void ValidatorEngine::started_lite_server() { start_control_interface(); } void ValidatorEngine::add_control_interface(ton::PublicKeyHash id, td::uint16 port) { class Callback : public ton::adnl::Adnl::Callback { public: void receive_message(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data) override { } void receive_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) override { td::actor::send_closure(id_, &ValidatorEngine::process_control_query, port_, src, dst, std::move(data), std::move(promise)); } Callback(td::actor::ActorId id, td::uint16 port) : id_(id), port_(port) { } private: td::actor::ActorId id_; td::uint16 port_; }; td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, ton::adnl::AdnlAddressList{}); td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, ton::adnl::AdnlNodeIdShort{id}, std::string(""), std::make_unique(actor_id(this), port)); td::actor::send_closure(control_ext_server_, &ton::adnl::AdnlExtServer::add_local_id, ton::adnl::AdnlNodeIdShort{id}); td::actor::send_closure(control_ext_server_, &ton::adnl::AdnlExtServer::add_tcp_port, port); } void ValidatorEngine::add_control_process(ton::PublicKeyHash id, td::uint16 port, ton::PublicKeyHash pub, td::int32 permissions) { control_permissions_[CI_key{id, port, pub}] |= permissions; } void ValidatorEngine::start_control_interface() { std::vector c_ids; std::vector ports; auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this)](td::Result> R) { R.ensure(); td::actor::send_closure(SelfId, &ValidatorEngine::started_control_interface, R.move_as_ok()); }); td::actor::send_closure(adnl_, &ton::adnl::Adnl::create_ext_server, std::move(c_ids), std::move(ports), std::move(P)); } void ValidatorEngine::started_control_interface(td::actor::ActorOwn control_ext_server) { control_ext_server_ = std::move(control_ext_server); for (auto &s : config_.controls) { add_control_interface(s.second.key, static_cast(s.first)); for (auto &p : s.second.clients) { add_control_process(s.second.key, static_cast(s.first), p.first, p.second); } } start_full_node_masters(); } void ValidatorEngine::start_full_node_masters() { for (auto &x : config_.full_node_masters) { full_node_masters_.emplace( static_cast(x.first), ton::validator::fullnode::FullNodeMaster::create( ton::adnl::AdnlNodeIdShort{x.second}, static_cast(x.first), validator_options_->zero_block_id().file_hash, keyring_.get(), adnl_.get(), validator_manager_.get())); } started_full_node_masters(); } void ValidatorEngine::started_full_node_masters() { started(); } void ValidatorEngine::started() { started_ = true; } void ValidatorEngine::try_add_adnl_node(ton::PublicKeyHash key, AdnlCategory cat, td::Promise promise) { if (cat < 0 || static_cast(cat) > max_cat()) { promise.set_error(td::Status::Error(ton::ErrorCode::protoviolation, "bad category value")); return; } auto R = config_.config_add_adnl_addr(key, cat); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } add_adnl(key, cat); write_config(std::move(promise)); } void ValidatorEngine::try_add_dht_node(ton::PublicKeyHash key_hash, td::Promise promise) { auto R = config_.config_add_dht_node(key_hash); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } add_dht(key_hash); write_config(std::move(promise)); } void ValidatorEngine::try_add_validator_permanent_key(ton::PublicKeyHash key_hash, td::uint32 election_date, td::uint32 ttl, td::Promise promise) { auto R = config_.config_add_validator_permanent_key(key_hash, election_date, ttl); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } td::MultiPromise mp; auto ig = mp.init_guard(); ig.add_promise(std::move(promise)); if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_permanent_key, key_hash, ig.get_promise()); } if (!full_node_.empty()) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::add_permanent_key, key_hash, ig.get_promise()); } write_config(ig.get_promise()); } void ValidatorEngine::try_add_validator_temp_key(ton::PublicKeyHash perm_key, ton::PublicKeyHash temp_key, td::uint32 ttl, td::Promise promise) { auto R = config_.config_add_validator_temp_key(perm_key, temp_key, ttl); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } td::MultiPromise mp; auto ig = mp.init_guard(); ig.add_promise(std::move(promise)); if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_temp_key, temp_key, ig.get_promise()); } write_config(ig.get_promise()); } void ValidatorEngine::try_add_validator_adnl_addr(ton::PublicKeyHash perm_key, ton::PublicKeyHash adnl_id, td::uint32 ttl, td::Promise promise) { auto R = config_.config_add_validator_adnl_id(perm_key, adnl_id, ttl); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } write_config(std::move(promise)); } void ValidatorEngine::try_add_full_node_adnl_addr(ton::PublicKeyHash id, td::Promise promise) { auto R = config_.config_add_full_node_adnl_id(id); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } if (!full_node_.empty()) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::update_adnl_id, ton::adnl::AdnlNodeIdShort{id}, [](td::Unit) {}); } write_config(std::move(promise)); } void ValidatorEngine::try_add_liteserver(ton::PublicKeyHash id, td::int32 port, td::Promise promise) { auto R = config_.config_add_lite_server(id, port); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } add_lite_server(id, static_cast(port)); write_config(std::move(promise)); } void ValidatorEngine::try_add_control_interface(ton::PublicKeyHash id, td::int32 port, td::Promise promise) { auto R = config_.config_add_control_interface(id, port); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } add_control_interface(id, static_cast(port)); write_config(std::move(promise)); } void ValidatorEngine::try_add_control_process(ton::PublicKeyHash id, td::int32 port, ton::PublicKeyHash pub, td::int32 permissions, td::Promise promise) { auto R = config_.config_add_control_process(id, port, pub, permissions); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } add_control_process(id, static_cast(port), pub, permissions); write_config(std::move(promise)); } void ValidatorEngine::try_del_adnl_node(ton::PublicKeyHash pub, td::Promise promise) { auto R = config_.config_del_adnl_addr(pub); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } td::actor::send_closure(adnl_, &ton::adnl::Adnl::del_id, ton::adnl::AdnlNodeIdShort{pub}, [](td::Unit) {}); write_config(std::move(promise)); } void ValidatorEngine::try_del_dht_node(ton::PublicKeyHash pub, td::Promise promise) { if (dht_nodes_.size() == 1 && pub == default_dht_node_) { promise.set_error(td::Status::Error(ton::ErrorCode::error, "cannot remove last dht node")); return; } auto R = config_.config_del_dht_node(pub); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } if (pub == default_dht_node_) { default_dht_node_ = *config_.dht_ids.begin(); auto d = dht_nodes_[default_dht_node_].get(); CHECK(!d.empty()); td::actor::send_closure(adnl_, &ton::adnl::Adnl::register_dht_node, d); td::actor::send_closure(overlay_manager_, &ton::overlay::Overlays::update_dht_node, d); if (!full_node_.empty()) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::update_dht_node, d); } } dht_nodes_.erase(pub); write_config(std::move(promise)); } void ValidatorEngine::try_del_validator_permanent_key(ton::PublicKeyHash pub, td::Promise promise) { auto R = config_.config_del_validator_permanent_key(pub); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::del_permanent_key, pub, [](td::Unit) {}); } if (!full_node_.empty()) { td::actor::send_closure(full_node_, &ton::validator::fullnode::FullNode::del_permanent_key, pub, [](td::Unit) {}); } write_config(std::move(promise)); } void ValidatorEngine::try_del_validator_temp_key(ton::PublicKeyHash perm, ton::PublicKeyHash temp_key, td::Promise promise) { auto R = config_.config_del_validator_temp_key(perm, temp_key); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } if (!validator_manager_.empty()) { td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::del_temp_key, temp_key, [](td::Unit) {}); } write_config(std::move(promise)); } void ValidatorEngine::try_del_validator_adnl_addr(ton::PublicKeyHash perm, ton::PublicKeyHash adnl_id, td::Promise promise) { auto R = config_.config_del_validator_adnl_id(perm, adnl_id); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } write_config(std::move(promise)); } void ValidatorEngine::reload_adnl_addrs() { addr_lists_.clear(); prio_addr_lists_.clear(); for (auto &addr : config_.addrs) { add_addr(addr.first, addr.second); } for (auto &adnl : config_.adnl_ids) { add_adnl(adnl.first, adnl.second); } } void ValidatorEngine::try_add_listening_port(td::uint32 ip, td::int32 port, std::vector cats, std::vector prio_cats, td::Promise promise) { td::IPAddress a; a.init_ipv4_port(td::IPAddress::ipv4_to_str(ip), static_cast(port)).ensure(); auto R = config_.config_add_network_addr(a, a, nullptr, std::move(cats), std::move(prio_cats)); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } reload_adnl_addrs(); write_config(std::move(promise)); } void ValidatorEngine::try_del_listening_port(td::uint32 ip, td::int32 port, std::vector cats, std::vector prio_cats, td::Promise promise) { td::IPAddress a; a.init_ipv4_port(td::IPAddress::ipv4_to_str(ip), static_cast(port)).ensure(); auto R = config_.config_del_network_addr(a, std::move(cats), std::move(prio_cats)); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } reload_adnl_addrs(); write_config(std::move(promise)); } void ValidatorEngine::try_add_proxy(td::uint32 in_ip, td::int32 in_port, td::uint32 out_ip, td::int32 out_port, std::shared_ptr proxy, std::vector cats, std::vector prio_cats, td::Promise promise) { td::IPAddress in_addr; in_addr.init_ipv4_port(td::IPAddress::ipv4_to_str(in_ip), static_cast(in_port)).ensure(); td::IPAddress out_addr; out_addr.init_ipv4_port(td::IPAddress::ipv4_to_str(out_ip), static_cast(out_port)).ensure(); auto R = config_.config_add_network_addr(in_addr, out_addr, std::move(proxy), std::move(cats), std::move(prio_cats)); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } reload_adnl_addrs(); write_config(std::move(promise)); } void ValidatorEngine::try_del_proxy(td::uint32 ip, td::int32 port, std::vector cats, std::vector prio_cats, td::Promise promise) { td::IPAddress a; a.init_ipv4_port(td::IPAddress::ipv4_to_str(ip), static_cast(port)).ensure(); auto R = config_.config_del_network_addr(a, std::move(cats), std::move(prio_cats)); if (R.is_error()) { promise.set_error(R.move_as_error()); return; } if (!R.move_as_ok()) { promise.set_value(td::Unit()); return; } reload_adnl_addrs(); write_config(std::move(promise)); } void ValidatorEngine::check_key(ton::PublicKeyHash id, td::Promise promise) { if (keys_.count(id) == 1) { promise.set_value(td::Unit()); return; } auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); } else { td::actor::send_closure(SelfId, &ValidatorEngine::got_key, R.move_as_ok()); promise.set_value(td::Unit()); } }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, id, std::move(P)); } td::BufferSlice ValidatorEngine::create_control_query_error(td::Status error) { return ton::serialize_tl_object( ton::create_tl_object(error.code(), error.message().str()), true); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getTime &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } auto obj = ton::create_tl_object(static_cast(td::Clocks::system())); promise.set_value(ton::serialize_tl_object(obj, true)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_importPrivateKey &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (keyring_.empty()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started keyring"))); return; } auto pk = ton::PrivateKey{query.key_}; auto P = td::PromiseCreator::lambda( [promise = std::move(promise), hash = pk.compute_short_id()](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(hash.tl()), true)); } }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_exportPrivateKey &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_unsafe)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (keyring_.empty()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started keyring"))); return; } promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not implemented"))); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_exportPublicKey &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (keyring_.empty()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started keyring"))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error())); } else { auto pub = R.move_as_ok(); promise.set_value(ton::serialize_tl_object(pub.tl(), true)); } }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, ton::PublicKeyHash{query.key_hash_}, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_generateKeyPair &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (keyring_.empty()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started keyring"))); return; } auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; auto P = td::PromiseCreator::lambda( [promise = std::move(promise), hash = pk.compute_short_id()](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(hash.tl()), true)); } }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addAdnlId &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, cat = query.category_, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add adnl node: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_adnl_node, id, cat, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addDhtId &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), id, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add dht node: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_dht_node, id, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addValidatorPermanentKey &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, election_date = query.election_date_, ttl = query.ttl_, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value( create_control_query_error(R.move_as_error_prefix("failed to add validator permanent key: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_validator_permanent_key, id, election_date, ttl, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addValidatorTempKey &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), perm_key = ton::PublicKeyHash{query.permanent_key_hash_}, id, ttl = query.ttl_, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add validator temp key: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_validator_temp_key, perm_key, id, ttl, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addValidatorAdnlAddress &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), perm_key = ton::PublicKeyHash{query.permanent_key_hash_}, id, ttl = query.ttl_, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add validator adnl address: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_validator_adnl_addr, perm_key, id, ttl, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_changeFullNodeAdnlAddress &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 id = ton::PublicKeyHash{query.adnl_id_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to change full node address: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_full_node_adnl_addr, id, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addLiteserver &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, port = static_cast(query.port_), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add liteserver: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_liteserver, id, port, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addControlInterface &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, port = static_cast(query.port_), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add control interface: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); td::actor::send_closure(SelfId, &ValidatorEngine::try_add_control_interface, id, port, std::move(P)); }); check_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delAdnlId &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del adnl node: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_adnl_node(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delDhtId &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del adnl node: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_dht_node(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delValidatorPermanentKey &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del validator permanent key: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_validator_permanent_key(id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delValidatorTempKey &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del validator temp key: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_validator_temp_key(ton::PublicKeyHash{query.permanent_key_hash_}, id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delValidatorAdnlAddress &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 id = ton::PublicKeyHash{query.key_hash_}; auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del validator adnl addr: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_validator_adnl_addr(ton::PublicKeyHash{query.permanent_key_hash_}, id, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addListeningPort &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 P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add listening port: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_add_listening_port(query.ip_, query.port_, query.categories_, query.priority_categories_, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delListeningPort &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 P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del listening port: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_listening_port(query.ip_, query.port_, query.categories_, query.priority_categories_, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addProxy &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 R = ton::adnl::AdnlProxy::create(*query.proxy_.get()); if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("bad proxy type: "))); return; } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add listening proxy: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_add_proxy(query.in_ip_, query.in_port_, query.out_ip_, query.out_port_, R.move_as_ok(), query.categories_, query.priority_categories_, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delProxy &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 P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to del listening proxy: "))); } else { promise.set_value( ton::serialize_tl_object(ton::create_tl_object(), true)); } }); try_del_proxy(query.out_ip_, query.out_port_, query.categories_, query.priority_categories_, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getConfig &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } auto s = td::json_encode(td::ToJson(*config_.tl().get()), true); promise.set_value(ton::create_serialize_tl_object(s)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_sign &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_unsafe)) { 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; } LOG(WARNING) << "received sign request: src=" << src.bits256_value().to_hex() << " key=" << query.key_hash_.to_hex() << " string=\n" << td::base64_encode(query.data_.as_slice()); auto P = td::PromiseCreator::lambda([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(R.move_as_ok()), true)); } }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::sign_message, ton::PublicKeyHash{query.key_hash_}, std::move(query.data_), std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setVerbosity &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (query.verbosity_ < 0 || query.verbosity_ > 10) { promise.set_value( create_control_query_error(td::Status::Error(ton::ErrorCode::error, "verbosity should be in range [0..10]"))); return; } SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR) + query.verbosity_); promise.set_value(ton::serialize_tl_object(ton::create_tl_object(), true)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getStats &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (validator_manager_.empty()) { promise.set_value( create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "validator manager not started"))); return; } auto P = td::PromiseCreator::lambda( [promise = std::move(promise)](td::Result>> R) mutable { if (R.is_error()) { promise.set_value(create_control_query_error(R.move_as_error())); } else { auto r = R.move_as_ok(); std::vector> vec; for (auto &s : r) { vec.push_back(ton::create_tl_object(s.first, s.second)); } promise.set_value(ton::create_serialize_tl_object(std::move(vec))); } }); td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::prepare_stats, std::move(P)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_createElectionBid &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (!started_) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); return; } if (fift_dir_.empty()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "no fift dir"))); return; } td::actor::create_actor("bidcreate", query.election_date_, query.election_addr_, query.wallet_, fift_dir_, actor_id(this), keyring_.get(), std::move(promise)) .release(); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_checkDhtServers &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; } if (keyring_.empty()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "keyring not started"))); return; } if (!dht_config_) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "no dht config"))); return; } if (config_.adnl_ids.count(ton::PublicKeyHash{query.id_}) == 0) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "no dht config"))); return; } td::actor::create_actor("pinger", dht_config_, ton::adnl::AdnlNodeIdShort{query.id_}, adnl_.get(), std::move(promise)) .release(); } void ValidatorEngine::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) { auto it = control_permissions_.find(CI_key{dst.pubkey_hash(), port, src.pubkey_hash()}); if (it == control_permissions_.end()) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "forbidden"))); return; } auto E = ton::fetch_tl_object(data.clone(), true); if (E.is_ok()) { if (!started_) { return; } td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::run_ext_query, std::move(data), std::move(promise)); return; } auto G = ton::fetch_tl_object(std::move(data), true); if (G.is_error()) { promise.set_value(create_control_query_error(G.move_as_error_prefix("failed to parse validator query: "))); return; } data = std::move(G.move_as_ok()->data_); auto F = ton::fetch_tl_object(data.clone(), true); if (F.is_error()) { promise.set_value(create_control_query_error(F.move_as_error_prefix("failed to parse validator query: "))); return; } auto f = F.move_as_ok(); ton::ton_api::downcast_call(*f.get(), [&](auto &obj) { run_control_query(obj, std::move(data), src.pubkey_hash(), it->second, std::move(promise)); }); } void ValidatorEngine::run() { td::mkdir(db_root_).ensure(); ton::errorlog::ErrorLog::create(db_root_); auto Sr = load_global_config(); if (Sr.is_error()) { LOG(ERROR) << "failed to load global config'" << global_config_ << "': " << Sr; std::_Exit(2); } keyring_ = ton::keyring::Keyring::create(db_root_ + "/keyring"); // TODO wait for password started_keyring_ = true; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { LOG(ERROR) << "failed to parse config: " << R.move_as_error(); std::_Exit(2); } else { td::actor::send_closure(SelfId, &ValidatorEngine::start); } }); load_config(std::move(P)); } std::atomic need_stats_flag{false}; void need_stats(int sig) { need_stats_flag.store(true); } std::atomic rotate_logs_flags{false}; void force_rotate_logs(int sig) { rotate_logs_flags.store(true); } void dump_memory_stats() { if (!is_memprof_on()) { return; } LOG(WARNING) << "memory_dump"; std::vector v; dump_alloc([&](const AllocInfo &info) { v.push_back(info); }); std::sort(v.begin(), v.end(), [](const AllocInfo &a, const AllocInfo &b) { return a.size > b.size; }); size_t total_size = 0; size_t other_size = 0; int cnt = 0; for (auto &info : v) { if (cnt++ < 50) { LOG(WARNING) << td::format::as_size(info.size) << td::format::as_array(info.backtrace); } else { other_size += info.size; } total_size += info.size; } LOG(WARNING) << td::tag("other", td::format::as_size(other_size)); LOG(WARNING) << td::tag("total", td::format::as_size(total_size)); LOG(WARNING) << td::tag("total traces", get_ht_size()); LOG(WARNING) << td::tag("fast_backtrace_success_rate", get_fast_backtrace_success_rate()); } void dump_stats() { dump_memory_stats(); LOG(WARNING) << td::NamedThreadSafeCounter::get_default(); } int main(int argc, char *argv[]) { SET_VERBOSITY_LEVEL(verbosity_INFO); td::set_default_failure_signal_handler().ensure(); td::actor::ActorOwn x; td::unique_ptr logger_; SCOPE_EXIT { td::log_interface = td::default_log_interface; }; std::vector> acts; td::OptionsParser p; p.set_description("validator or full node for TON network"); p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) { int v = VERBOSITY_NAME(FATAL) + (td::to_integer(arg)); SET_VERBOSITY_LEVEL(v); return td::Status::OK(); }); p.add_option('h', "help", "prints_help", [&]() { char b[10240]; td::StringBuilder sb(td::MutableSlice{b, 10000}); sb << p; std::cout << sb.as_cslice().c_str(); std::exit(2); return td::Status::OK(); }); p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) { acts.push_back( [&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_global_config, fname); }); return td::Status::OK(); }); p.add_option('c', "local-config", "file to read local config", [&](td::Slice fname) { acts.push_back( [&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_local_config, fname); }); return td::Status::OK(); }); p.add_option('I', "ip", "ip:port of instance", [&](td::Slice arg) { td::IPAddress addr; TRY_STATUS(addr.init_host_port(arg.str())); acts.push_back([&x, addr]() { td::actor::send_closure(x, &ValidatorEngine::add_ip, addr); }); return td::Status::OK(); }); p.add_option('D', "db", "root for dbs", [&](td::Slice fname) { acts.push_back([&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_db_root, fname); }); return td::Status::OK(); }); p.add_option('f', "fift-dir", "directory with fift scripts", [&](td::Slice fname) { acts.push_back([&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_fift_dir, fname); }); return td::Status::OK(); }); p.add_option('F', "filedb-depth", "depth of autodirs for blocks, proofs, etc. Default value is 2. You need to clear the " "database, if you need to change this option", [&](td::Slice fname) { acts.push_back([&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_db_depth, td::to_integer(fname)); }); return td::Status::OK(); }); p.add_option('d', "daemonize", "set SIGHUP", [&]() { #if TD_DARWIN || TD_LINUX close(0); setsid(); #endif td::set_signal_handler(td::SignalType::HangUp, force_rotate_logs).ensure(); return td::Status::OK(); }); p.add_option('l', "logname", "log to file", [&](td::Slice fname) { logger_ = td::TsFileLog::create(fname.str()).move_as_ok(); td::log_interface = logger_.get(); return td::Status::OK(); }); p.add_option('s', "state-ttl", "state will be gc'd after this time (in seconds) default=3600", [&](td::Slice fname) { auto v = td::to_double(fname); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_state_ttl, v); }); return td::Status::OK(); }); p.add_option('b', "block-ttl", "blocks will be gc'd after this time (in seconds) default=7*86400", [&](td::Slice fname) { auto v = td::to_double(fname); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_block_ttl, v); }); return td::Status::OK(); }); p.add_option('A', "archive-ttl", "archived blocks will be deleted after this time (in seconds) default=365*86400", [&](td::Slice fname) { auto v = td::to_double(fname); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_archive_ttl, v); }); return td::Status::OK(); }); p.add_option('K', "key-proof-ttl", "key blocks will be deleted after this time (in seconds) default=365*86400*10", [&](td::Slice fname) { auto v = td::to_double(fname); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_key_proof_ttl, v); }); return td::Status::OK(); }); p.add_option('S', "sync-before", "in initial sync download all blocks for last given seconds default=3600", [&](td::Slice fname) { auto v = td::to_double(fname); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_sync_ttl, v); }); return td::Status::OK(); }); td::uint32 threads = 7; p.add_option('t', "threads", PSTRING() << "number of threads (default=" << threads << ")", [&](td::Slice fname) { td::int32 v; try { v = std::stoi(fname.str()); } catch (...) { return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: not a number"); } if (v < 1 || v > 256) { return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: should be in range [1..256]"); } threads = v; return td::Status::OK(); }); p.add_option('u', "user", "change user", [&](td::Slice user) { return td::change_user(user); }); auto S = p.run(argc, argv); if (S.is_error()) { LOG(ERROR) << "failed to parse options: " << S.move_as_error(); std::_Exit(2); } td::set_runtime_signal_handler(1, need_stats).ensure(); td::actor::Scheduler scheduler({threads}); scheduler.run_in_context([&] { CHECK(vm::init_op_cp0()); x = td::actor::create_actor("validator-engine"); for (auto &act : acts) { act(); } acts.clear(); td::actor::send_closure(x, &ValidatorEngine::run); }); while (scheduler.run(1)) { if (need_stats_flag.exchange(false)) { dump_stats(); } if (rotate_logs_flags.exchange(false)) { if (td::log_interface) { td::log_interface->rotate(); } } } return 0; }