1
0
mirror of https://github.com/danog/ton.git synced 2024-11-30 04:29:19 +01:00
ton/dht-server/dht-server.cpp
2019-09-24 01:10:57 +04:00

1249 lines
45 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
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 "dht-server.hpp"
#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/user.h"
#include "td/utils/port/signals.h"
#include "td/utils/ThreadSafeCounter.h"
#include "td/utils/TsFileLog.h"
#include "td/utils/Random.h"
#include "memprof/memprof.h"
#if TD_DARWIN || TD_LINUX
#include <unistd.h>
#endif
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <set>
Config::Config() {
out_port = 3278;
}
Config::Config(ton::ton_api::engine_validator_config &config) {
out_port = static_cast<td::uint16>(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<ton::adnl::AdnlProxy> proxy = nullptr;
std::vector<AdnlCategory> categories;
std::vector<AdnlCategory> 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<td::uint16>(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<td::uint16>(obj.in_port_))
.ensure();
out_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.out_ip_), static_cast<td::uint16>(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 &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<ton::ton_api::engine_validator_config> Config::tl() const {
std::vector<ton::tl_object_ptr<ton::ton_api::engine_Addr>> addrs_vec;
for (auto &x : addrs) {
if (x.second.proxy) {
addrs_vec.push_back(ton::create_tl_object<ton::ton_api::engine_addrProxy>(
static_cast<td::int32>(x.second.in_addr.get_ipv4()), x.second.in_addr.get_port(),
static_cast<td::int32>(x.first.addr.get_ipv4()), x.first.addr.get_port(), x.second.proxy->tl(),
std::vector<td::int32>(x.second.cats.begin(), x.second.cats.end()),
std::vector<td::int32>(x.second.priority_cats.begin(), x.second.priority_cats.end())));
} else {
addrs_vec.push_back(ton::create_tl_object<ton::ton_api::engine_addr>(
static_cast<td::int32>(x.first.addr.get_ipv4()), x.first.addr.get_port(),
std::vector<td::int32>(x.second.cats.begin(), x.second.cats.end()),
std::vector<td::int32>(x.second.priority_cats.begin(), x.second.priority_cats.end())));
}
}
std::vector<ton::tl_object_ptr<ton::ton_api::engine_adnl>> adnl_vec;
for (auto &x : adnl_ids) {
adnl_vec.push_back(ton::create_tl_object<ton::ton_api::engine_adnl>(x.first.tl(), x.second));
}
std::vector<ton::tl_object_ptr<ton::ton_api::engine_dht>> dht_vec;
for (auto &x : dht_ids) {
dht_vec.push_back(ton::create_tl_object<ton::ton_api::engine_dht>(x.tl()));
}
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator>> val_vec;
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator_fullNodeSlave>> full_node_slaves_vec;
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator_fullNodeMaster>> full_node_masters_vec;
std::vector<ton::tl_object_ptr<ton::ton_api::engine_liteServer>> liteserver_vec;
std::vector<ton::tl_object_ptr<ton::ton_api::engine_controlInterface>> control_vec;
for (auto &x : controls) {
std::vector<ton::tl_object_ptr<ton::ton_api::engine_controlProcess>> control_proc_vec;
for (auto &y : x.second.clients) {
control_proc_vec.push_back(ton::create_tl_object<ton::ton_api::engine_controlProcess>(y.first.tl(), y.second));
}
control_vec.push_back(ton::create_tl_object<ton::ton_api::engine_controlInterface>(x.second.key.tl(), x.first,
std::move(control_proc_vec)));
}
auto gc_vec = ton::create_tl_object<ton::ton_api::engine_gc>(std::vector<td::Bits256>{});
for (auto &id : gc) {
gc_vec->ids_.push_back(id.tl());
}
return ton::create_tl_object<ton::ton_api::engine_validator_config>(
out_port, std::move(addrs_vec), std::move(adnl_vec), std::move(dht_vec), std::move(val_vec),
ton::PublicKeyHash::zero().tl(), std::move(full_node_slaves_vec), std::move(full_node_masters_vec),
std::move(liteserver_vec), std::move(control_vec), std::move(gc_vec));
}
td::Result<bool> Config::config_add_network_addr(td::IPAddress in_ip, td::IPAddress out_ip,
std::shared_ptr<ton::adnl::AdnlProxy> proxy,
std::vector<AdnlCategory> cats, std::vector<AdnlCategory> 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<bool> 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<bool> 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<bool> Config::config_add_control_interface(ton::PublicKeyHash key, td::int32 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<bool> 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<bool> 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<bool> Config::config_del_network_addr(td::IPAddress a, std::vector<AdnlCategory> cats,
std::vector<AdnlCategory> 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;
}
}
return mod;
} else {
return false;
}
}
td::Result<bool> 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");
}
decref(addr);
adnl_ids.erase(addr);
return true;
}
td::Result<bool> 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<bool> 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<bool> 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<bool> Config::config_del_gc(ton::PublicKeyHash key) {
return gc.erase(key);
}
void DhtServer::set_local_config(std::string str) {
local_config_ = str;
}
void DhtServer::set_global_config(std::string str) {
global_config_ = str;
}
void DhtServer::set_db_root(std::string db_root) {
db_root_ = db_root;
}
void DhtServer::start_up() {
alarm_timestamp() = td::Timestamp::in(1.0 + td::Random::fast(0, 100) * 0.01);
}
void DhtServer::alarm() {
alarm_timestamp() = td::Timestamp::in(1.0 + td::Random::fast(0, 100) * 0.01);
if (started_) {
for (auto &x : config_.gc) {
if (running_gc_.count(x) == 0) {
running_gc_.insert(x);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), x](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &DhtServer::deleted_key, x);
});
td::actor::send_closure(keyring_, &ton::keyring::Keyring::del_key, x, std::move(P));
}
}
}
}
void DhtServer::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 DhtServer::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_) {
// td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_static_nodes_from_config,
// std::move(conf.adnl_->static_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]");
}
return td::Status::OK();
}
void DhtServer::load_empty_local_config(td::Promise<td::Unit> promise) {
auto ret_promise = td::PromiseCreator::lambda(
[SelfId = actor_id(this), promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
} else {
td::actor::send_closure(SelfId, &DhtServer::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<td::int32>{0, 1, 2, 3}, std::vector<td::int32>{})
.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();
}
}
void DhtServer::load_local_config(td::Promise<td::Unit> 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<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
} else {
td::actor::send_closure(SelfId, &DhtServer::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<td::int32>{0, 1, 2, 3}, std::vector<td::int32>{})
.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());
}
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();
}
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 DhtServer::load_config(td::Promise<td::Unit> 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<td::Unit> 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_data_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 DhtServer::write_config(td::Promise<td::Unit> promise) {
auto s = td::json_encode<std::string>(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<ton::PublicKey> DhtServer::get_key_promise(td::MultiPromise::InitGuard &ig) {
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), promise = ig.get_promise()](td::Result<ton::PublicKey> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
} else {
td::actor::send_closure(SelfId, &DhtServer::got_key, R.move_as_ok());
promise.set_value(td::Unit());
}
});
return std::move(P);
}
void DhtServer::got_key(ton::PublicKey key) {
keys_[key.compute_short_id()] = key;
}
void DhtServer::start() {
read_config_ = true;
start_adnl();
}
void DhtServer::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);
}
started_adnl();
}
void DhtServer::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::uint32>(td::Clocks::system());
for (auto cat : cats.cats) {
CHECK(cat >= 0);
ton::adnl::AdnlAddress x = ton::adnl::AdnlAddressImpl::create(
ton::create_tl_object<ton::ton_api::adnl_address_udp>(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<ton::ton_api::adnl_address_udp>(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 DhtServer::add_adnl(ton::PublicKeyHash id, AdnlCategory cat) {
CHECK(addr_lists_[cat].size() > 0);
CHECK(keys_.count(id) > 0);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, addr_lists_[cat]);
}
void DhtServer::started_adnl() {
start_dht();
}
void DhtServer::start_dht() {
for (auto &dht : config_.dht_ids) {
auto D = ton::dht::Dht::create(ton::adnl::AdnlNodeIdShort{dht}, db_root_, dht_config_, keyring_.get(), adnl_.get());
D.ensure();
dht_nodes_[dht] = D.move_as_ok();
if (default_dht_node_.is_zero()) {
default_dht_node_ = dht;
}
}
CHECK(!default_dht_node_.is_zero());
td::actor::send_closure(adnl_, &ton::adnl::Adnl::register_dht_node, dht_nodes_[default_dht_node_].get());
started_dht();
}
void DhtServer::started_dht() {
start_control_interface();
}
void DhtServer::start_control_interface() {
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<td::BufferSlice> promise) override {
td::actor::send_closure(id_, &DhtServer::process_control_query, src, dst, std::move(data), std::move(promise));
}
Callback(td::actor::ActorId<DhtServer> id) : id_(id) {
}
private:
td::actor::ActorId<DhtServer> id_;
};
std::vector<ton::adnl::AdnlNodeIdShort> c_ids;
std::vector<td::uint16> ports;
for (auto &s : config_.controls) {
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[s.second.key]},
ton::adnl::AdnlAddressList{});
td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, ton::adnl::AdnlNodeIdShort{s.second.key},
std::string(""), std::make_unique<Callback>(actor_id(this)));
c_ids.push_back(ton::adnl::AdnlNodeIdShort{s.second.key});
ports.push_back(static_cast<td::uint16>(s.first));
for (auto &p : s.second.clients) {
control_permissions_[p.first] |= p.second;
}
}
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this)](td::Result<td::actor::ActorOwn<ton::adnl::AdnlExtServer>> R) {
R.ensure();
td::actor::send_closure(SelfId, &DhtServer::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 DhtServer::started_control_interface(td::actor::ActorOwn<ton::adnl::AdnlExtServer> control_ext_server) {
control_ext_server_ = std::move(control_ext_server);
started();
}
void DhtServer::started() {
started_ = true;
}
void DhtServer::add_adnl_node(ton::PublicKey key, AdnlCategory cat, td::Promise<td::Unit> promise) {
if (cat < 0 || static_cast<td::uint32>(cat) > max_cat()) {
promise.set_error(td::Status::Error(ton::ErrorCode::protoviolation, "bad category value"));
return;
}
auto R = config_.config_add_adnl_addr(key.compute_short_id(), cat);
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
if (!R.move_as_ok()) {
promise.set_value(td::Unit());
return;
}
if (!adnl_.empty()) {
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{key}, addr_lists_[cat]);
}
write_config(std::move(promise));
}
void DhtServer::add_dht_node(ton::PublicKeyHash key_hash, td::Promise<td::Unit> 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;
}
if (dht_nodes_.size() > 0) {
auto D =
ton::dht::Dht::create(ton::adnl::AdnlNodeIdShort{key_hash}, db_root_, dht_config_, keyring_.get(), adnl_.get());
D.ensure();
dht_nodes_[key_hash] = D.move_as_ok();
if (default_dht_node_.is_zero()) {
default_dht_node_ = key_hash;
}
}
write_config(std::move(promise));
}
void DhtServer::add_control_interface(ton::PublicKeyHash id, td::int32 port, td::Promise<td::Unit> 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;
}
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, static_cast<td::uint16>(port));
write_config(std::move(promise));
}
void DhtServer::add_control_process(ton::PublicKeyHash id, td::int32 port, ton::PublicKeyHash pub,
td::int32 permissions, td::Promise<td::Unit> 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;
}
control_permissions_[pub] |= permissions;
write_config(std::move(promise));
}
td::BufferSlice DhtServer::create_control_query_error(td::Status error) {
return ton::serialize_tl_object(
ton::create_tl_object<ton::ton_api::engine_validator_controlQueryError>(error.code(), error.message().str()),
true);
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_getTime &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::vep_default)) {
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized")));
return;
}
auto obj = ton::create_tl_object<ton::ton_api::engine_validator_time>(static_cast<td::int32>(td::Clocks::system()));
promise.set_value(ton::serialize_tl_object(obj, true));
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_importPrivateKey &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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<td::Unit> 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<ton::ton_api::engine_validator_keyHash>(hash.tl()), true));
}
});
td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, std::move(P));
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_exportPrivateKey &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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 DhtServer::run_control_query(ton::ton_api::engine_validator_exportPublicKey &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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<ton::PublicKey> 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 DhtServer::run_control_query(ton::ton_api::engine_validator_generateKeyPair &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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<td::Unit> 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<ton::ton_api::engine_validator_keyHash>(hash.tl()), true));
}
});
td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), false, std::move(P));
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_addAdnlId &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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([SelfId = actor_id(this), cat = query.category_,
promise = std::move(promise)](td::Result<ton::PublicKey> 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 pub = R.move_as_ok();
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::Unit> 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<ton::ton_api::engine_validator_success>(), true));
}
});
td::actor::send_closure(SelfId, &DhtServer::add_adnl_node, pub, cat, std::move(P));
});
td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, ton::PublicKeyHash{query.key_hash_},
std::move(P));
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_addDhtId &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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([SelfId = actor_id(this), key_hash = ton::PublicKeyHash{query.key_hash_},
promise = std::move(promise)](td::Result<td::Unit> 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<td::Unit> 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<ton::ton_api::engine_validator_success>(), true));
}
});
td::actor::send_closure(SelfId, &DhtServer::add_dht_node, key_hash, std::move(P));
});
td::actor::send_closure(keyring_, &ton::keyring::Keyring::check_key, ton::PublicKeyHash{query.key_hash_},
std::move(P));
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_getConfig &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::vep_default)) {
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized")));
return;
}
auto s = td::json_encode<std::string>(td::ToJson(*config_.tl().get()), true);
promise.set_value(td::BufferSlice{s});
}
void DhtServer::run_control_query(ton::ton_api::engine_validator_sign &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
if (!(perm & DhtServerPermissions::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;
}
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::BufferSlice> 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<ton::ton_api::engine_validator_signature>(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 DhtServer::process_control_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst,
td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
auto G = ton::fetch_tl_object<ton::ton_api::engine_validator_controlQuery>(std::move(data), true);
if (G.is_error()) {
promise.set_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<ton::ton_api::Function>(data.clone(), true);
if (F.is_error()) {
promise.set_error(F.move_as_error_prefix("failed to parse validator query: "));
return;
}
auto f = F.move_as_ok();
auto it = control_permissions_.find(src.pubkey_hash());
if (it == control_permissions_.end()) {
promise.set_error(td::Status::Error(ton::ErrorCode::protoviolation, "forbidden"));
return;
}
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 DhtServer::run() {
td::mkdir(db_root_).ensure();
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<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "failed to parse config: " << R.move_as_error();
std::_Exit(2);
} else {
td::actor::send_closure(SelfId, &DhtServer::start);
}
});
load_config(std::move(P));
}
std::atomic<bool> need_stats_flag{false};
void need_stats(int sig) {
need_stats_flag.store(true);
}
std::atomic<bool> 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<AllocInfo> 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<DhtServer> x;
td::unique_ptr<td::LogInterface> logger_;
SCOPE_EXIT {
td::log_interface = td::default_log_interface;
};
std::vector<std::function<void()>> acts;
td::OptionsParser p;
p.set_description("dht server for TON DHT network");
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
int v = VERBOSITY_NAME(FATAL) + (td::to_integer<int>(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, &DhtServer::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, &DhtServer::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, &DhtServer::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, &DhtServer::set_db_root, 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();
});
td::uint32 threads = 7;
p.add_option('t', "threads", PSTRING() << "number of threads (default=" << threads << ")", [&](td::Slice fname) {
td::int32 v;
try {
v = std::stoi(fname.str());
} catch (...) {
return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: not a number");
}
if (v < 1 || v > 256) {
return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: should be in range [1..256]");
}
threads = v;
return td::Status::OK();
});
p.add_option('u', "user", "change user", [&](td::Slice user) { return td::change_user(user); });
p.run(argc, argv).ensure();
td::set_runtime_signal_handler(1, need_stats).ensure();
td::actor::Scheduler scheduler({threads});
scheduler.run_in_context([&] {
x = td::actor::create_actor<DhtServer>("dht-server");
for (auto &act : acts) {
act();
}
acts.clear();
td::actor::send_closure(x, &DhtServer::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;
}