1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

32
dht/CMakeLists.txt Normal file
View file

@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
set(DHT_SOURCE
dht.cpp
dht-remote-node.cpp
dht-bucket.cpp
dht-node.cpp
dht-query.cpp
dht-types.cpp
dht-bucket.hpp
dht-in.hpp
dht-query.hpp
dht-remote-node.hpp
dht-types.h
dht.h
dht.hpp
)
add_library(dht STATIC ${DHT_SOURCE})
target_include_directories(dht PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(dht PRIVATE tdutils tdactor adnl tl_api)

208
dht/dht-bucket.cpp Normal file
View file

@ -0,0 +1,208 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/tl_storers.h"
#include "td/utils/crypto.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/Random.h"
#include "td/utils/format.h"
#include "keys/encryptor.h"
#include "auto/tl/ton_api.hpp"
#include "dht-remote-node.hpp"
#include "dht-bucket.hpp"
#include "dht.hpp"
//#include <algorithm>
namespace ton {
namespace dht {
void DhtBucket::get_nearest_nodes(DhtKeyId id, td::uint32 bit, DhtNodesList &vec, td::uint32 k) {
if (active_nodes_.size() == 0) {
return;
}
std::map<DhtKeyId, size_t> list;
for (size_t i = 0; i < active_nodes_.size(); i++) {
auto &node = active_nodes_[i];
if (node) {
list.emplace(id ^ node->get_key(), i);
}
}
for (auto it = list.begin(); it != list.end() && vec.size() < k; it++) {
vec.push_back(active_nodes_[it->second]->get_node());
}
}
td::uint32 DhtBucket::active_cnt() {
td::uint32 cnt = 0;
for (auto &node : active_nodes_) {
if (node) {
cnt++;
}
}
return cnt;
}
td::Status DhtBucket::add_full_node(DhtKeyId id, DhtNode newnode, td::actor::ActorId<adnl::Adnl> adnl,
adnl::AdnlNodeIdShort self_id) {
for (auto &node : active_nodes_) {
if (node && node->get_key() == id) {
return node->update_value(std::move(newnode), adnl, self_id);
}
}
for (auto &node : backup_nodes_) {
if (node && node->get_key() == id) {
return node->update_value(std::move(newnode), adnl, self_id);
}
}
TRY_RESULT_PREFIX(N, DhtRemoteNode::create(std::move(newnode), max_missed_pings_), "failed to add new node: ");
for (auto &node : backup_nodes_) {
if (node == nullptr) {
node = std::move(N);
return td::Status::OK();
}
}
for (auto &node : backup_nodes_) {
CHECK(node);
if (node->ready_from() == 0 && node->failed_from() + 60 < td::Time::now_cached()) {
node = std::move(N);
return td::Status::OK();
}
}
return td::Status::OK();
}
void DhtBucket::receive_ping(DhtKeyId id, DhtNode result, td::actor::ActorId<adnl::Adnl> adnl,
adnl::AdnlNodeIdShort self_id) {
for (auto &node : active_nodes_) {
if (node && node->get_key() == id) {
node->receive_ping(std::move(result), adnl, self_id);
return;
}
}
for (size_t i = 0; i < backup_nodes_.size(); i++) {
auto &node = backup_nodes_[i];
if (node && node->get_key() == id) {
node->receive_ping(std::move(result), adnl, self_id);
if (node->is_ready()) {
promote_node(i);
}
return;
}
}
}
void DhtBucket::demote_node(size_t idx) {
for (auto &node : backup_nodes_) {
if (node == nullptr) {
node = std::move(active_nodes_[idx]);
return;
}
}
for (auto &node : backup_nodes_) {
if (node->ready_from() == 0 && node->failed_from() + 60 < td::Time::now_cached()) {
node = std::move(active_nodes_[idx]);
return;
}
}
active_nodes_[idx] = nullptr;
}
void DhtBucket::promote_node(size_t idx) {
CHECK(backup_nodes_[idx]);
for (auto &node : active_nodes_) {
if (node == nullptr) {
node = std::move(backup_nodes_[idx]);
return;
}
CHECK(node->is_ready());
}
}
void DhtBucket::check(td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<DhtMember> dht,
adnl::AdnlNodeIdShort src) {
size_t have_space = 0;
for (size_t i = 0; i < active_nodes_.size(); i++) {
auto &node = active_nodes_[i];
if (node && td::Time::now_cached() - node->last_ping_at() > ping_timeout_) {
node->send_ping(adnl, dht, src);
if (node->ready_from() == 0) {
demote_node(i);
}
}
if (node == nullptr) {
have_space++;
}
}
for (size_t i = 0; i < backup_nodes_.size(); i++) {
auto &node = backup_nodes_[i];
if (node && td::Time::now_cached() - node->last_ping_at() > ping_timeout_) {
node->send_ping(adnl, dht, src);
}
if (node && have_space > 0 && node->is_ready()) {
promote_node(i);
have_space--;
}
}
}
void DhtBucket::dump(td::StringBuilder &sb) const {
sb << " bucket:\n";
sb << " active:\n";
for (auto &node : active_nodes_) {
if (node) {
sb << " " << node->get_key() << "\n";
}
}
sb << " backup:\n";
for (auto &node : backup_nodes_) {
if (node) {
sb << " " << node->get_key() << "\n";
}
}
}
DhtNodesList DhtBucket::export_nodes() const {
DhtNodesList list;
for (auto &node : active_nodes_) {
if (node) {
list.push_back(node->get_node());
}
}
for (auto &node : backup_nodes_) {
if (node) {
list.push_back(node->get_node());
}
}
return list;
}
} // namespace dht
} // namespace ton

64
dht/dht-bucket.hpp Normal file
View file

@ -0,0 +1,64 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "dht-remote-node.hpp"
#include "dht.hpp"
#include <map>
namespace ton {
namespace dht {
class DhtMember;
class DhtBucket {
private:
double ping_timeout_ = 60;
td::uint32 max_missed_pings_ = 3;
std::vector<std::unique_ptr<DhtRemoteNode>> active_nodes_;
std::vector<std::unique_ptr<DhtRemoteNode>> backup_nodes_;
//std::map<td::UInt256, std::unique_ptr<DhtRemoteNode>> pending_nodes_;
td::uint32 k_;
bool check_one(td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<DhtMember> node, adnl::AdnlNodeIdShort src,
const DhtMember::PrintId &print_id);
void demote_node(size_t idx);
void promote_node(size_t idx);
public:
DhtBucket(td::uint32 k) : k_(k) {
active_nodes_.resize(k);
backup_nodes_.resize(k);
}
td::uint32 active_cnt();
td::Status add_full_node(DhtKeyId id, DhtNode node, td::actor::ActorId<adnl::Adnl> adnl,
adnl::AdnlNodeIdShort self_id);
void check(td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<DhtMember> node, adnl::AdnlNodeIdShort src);
void receive_ping(DhtKeyId id, DhtNode result, td::actor::ActorId<adnl::Adnl> adnl, adnl::AdnlNodeIdShort self_id);
void get_nearest_nodes(DhtKeyId id, td::uint32 bit, DhtNodesList &vec, td::uint32 k);
void dump(td::StringBuilder &sb) const;
DhtNodesList export_nodes() const;
};
} // namespace dht
} // namespace ton

168
dht/dht-in.hpp Normal file
View file

@ -0,0 +1,168 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "dht.hpp"
#include "td/db/KeyValueAsync.h"
#include <map>
namespace ton {
namespace dht {
class DhtRemoteNode;
class DhtBucket;
class DhtMemberImpl : public DhtMember {
private:
class DhtKeyValueLru : public td::ListNode {
public:
DhtKeyValueLru(DhtValue value) : kv_(std::move(value)) {
}
DhtValue kv_;
static inline DhtKeyValueLru *from_list_node(ListNode *node) {
return static_cast<DhtKeyValueLru *>(node);
}
};
//std::unique_ptr<adnl::AdnlDecryptor> decryptor_;
adnl::AdnlNodeIdShort id_;
DhtKeyId key_;
td::uint32 k_;
td::uint32 a_;
td::uint32 max_cache_time_ = 60;
td::uint32 max_cache_size_ = 100;
std::vector<DhtBucket> buckets_;
std::string db_root_;
// to be republished once in a while
std::map<DhtKeyId, DhtValue> our_values_;
std::map<DhtKeyId, DhtKeyValueLru> cached_values_;
td::ListNode cached_values_lru_;
std::map<DhtKeyId, DhtValue> values_;
td::Timestamp fill_att_ = td::Timestamp::in(0);
td::Timestamp republish_att_ = td::Timestamp::in(0);
DhtKeyId last_republish_key_ = DhtKeyId::zero();
DhtKeyId last_check_key_ = DhtKeyId::zero();
class Callback : public adnl::Adnl::Callback {
public:
void receive_message(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data) override {
CHECK(dst == id_);
td::actor::send_closure(self_, &DhtMemberImpl::receive_message, src, std::move(data));
}
void receive_query(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) override {
CHECK(dst == id_);
td::actor::send_closure(self_, &DhtMemberImpl::receive_query, src, std::move(data), std::move(promise));
}
Callback(td::actor::ActorId<DhtMemberImpl> self, adnl::AdnlNodeIdShort id) : self_(self), id_(id) {
}
private:
td::actor::ActorId<DhtMemberImpl> self_;
adnl::AdnlNodeIdShort id_;
};
void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data);
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<adnl::Adnl> adnl_;
td::uint64 ping_queries_{0};
td::uint64 find_node_queries_{0};
td::uint64 find_value_queries_{0};
td::uint64 store_queries_{0};
td::uint64 get_addr_list_queries_{0};
using DbType = td::KeyValueAsync<td::Bits256, td::BufferSlice>;
DbType db_;
td::Timestamp next_save_to_db_at_ = td::Timestamp::in(10.0);
void save_to_db();
DhtNodesList get_nearest_nodes(DhtKeyId id, td::uint32 k);
void check();
template <class T>
void process_query(adnl::AdnlNodeIdShort src, T &query, td::Promise<td::BufferSlice> promise) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad DHT query"));
}
void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_ping &query, td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_findNode &query, td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_findValue &query, td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_store &query, td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_getSignedAddressList &query,
td::Promise<td::BufferSlice> promise);
public:
DhtMemberImpl(adnl::AdnlNodeIdShort id, std::string db_root, td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl, td::uint32 k, td::uint32 a = 3)
: id_(id), key_{id_}, k_(k), a_(a), db_root_(db_root), keyring_(keyring), adnl_(adnl) {
for (size_t i = 0; i < 256; i++) {
buckets_.emplace_back(k_);
}
}
void add_full_node(DhtKeyId id, DhtNode node) override;
adnl::AdnlNodeIdShort get_id() const override {
return id_;
}
void receive_ping(DhtKeyId id, DhtNode result) override;
void set_value(DhtValue key_value, td::Promise<td::Unit> result) override;
td::uint32 distance(DhtKeyId key_id, td::uint32 max_value);
td::Status store_in(DhtValue value) override;
void send_store(DhtValue value, td::Promise<td::Unit> promise);
void get_value_in(DhtKeyId key, td::Promise<DhtValue> result) override;
void get_value(DhtKey key, td::Promise<DhtValue> result) override {
get_value_in(key.compute_key_id(), std::move(result));
}
void alarm() override {
alarm_timestamp() = td::Timestamp::in(1.0);
check();
}
void start_up() override;
void tear_down() override;
void dump(td::StringBuilder &sb) const override;
PrintId print_id() const override {
return PrintId{id_};
}
void get_self_node(td::Promise<DhtNode> promise) override;
};
} // namespace dht
} // namespace ton

64
dht/dht-node.cpp Normal file
View file

@ -0,0 +1,64 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "dht-node.hpp"
#include "keys/encryptor.h"
namespace ton {
namespace dht {
td::Status DhtNode::update(tl_object_ptr<ton_api::dht_node> obj) {
if (version_ && obj->version_ <= version_) {
return td::Status::Error(ErrorCode::notready, "too old version");
}
auto signature = std::move(obj->signature_);
auto B = serialize_tl_object(obj, true);
TRY_RESULT(pub, adnl::AdnlNodeIdFull::create(obj->id_));
TRY_RESULT(addr_list, adnl::AdnlAddressList::create(std::move(obj->addr_list_)));
if (!addr_list.public_only()) {
return td::Status::Error(ErrorCode::notready, "dht node must have only public addresses");
}
if (!addr_list.size()) {
return td::Status::Error(ErrorCode::notready, "dht node must have >0 addresses");
}
TRY_RESULT(E, pub.pubkey().create_encryptor());
TRY_STATUS(E->check_signature(B.as_slice(), signature.as_slice()));
id_ = pub;
addr_list_ = addr_list;
version_ = obj->version_;
signature_ = td::SharedSlice(signature.as_slice());
return td::Status::OK();
}
tl_object_ptr<ton_api::dht_nodes> DhtNodesList::tl() const {
std::vector<tl_object_ptr<ton_api::dht_node>> L;
for (auto &n : list_) {
L.emplace_back(n.tl());
}
return create_tl_object<ton_api::dht_nodes>(std::move(L));
}
} // namespace dht
} // namespace ton

114
dht/dht-node.hpp Normal file
View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "adnl/adnl-node-id.hpp"
#include "adnl/adnl-address-list.hpp"
#include "dht-types.h"
namespace ton {
namespace dht {
class DhtNode {
private:
adnl::AdnlNodeIdFull id_;
adnl::AdnlAddressList addr_list_;
td::int32 version_{0};
td::SharedSlice signature_;
public:
DhtNode() {
}
DhtNode(adnl::AdnlNodeIdFull id, adnl::AdnlAddressList addr_list, td::int32 version, td::BufferSlice signature)
: id_(id), addr_list_(addr_list), version_(version), signature_(signature.as_slice()) {
}
DhtNode(adnl::AdnlNodeIdFull id, adnl::AdnlAddressList addr_list, td::int32 version, td::SharedSlice signature)
: id_(id), addr_list_(addr_list), version_(version), signature_(std::move(signature)) {
}
static td::Result<DhtNode> create(tl_object_ptr<ton_api::dht_node> obj) {
if (obj->version_ == 0) {
return td::Status::Error(ErrorCode::protoviolation, "zero version");
}
DhtNode n;
TRY_STATUS(n.update(std::move(obj)));
return std::move(n);
}
td::Status update(tl_object_ptr<ton_api::dht_node> obj);
DhtKeyId get_key() const {
CHECK(!id_.empty());
return DhtKeyId{id_.compute_short_id()};
}
adnl::AdnlNodeIdFull adnl_id() const {
return id_;
}
adnl::AdnlAddressList addr_list() const {
return addr_list_;
}
td::int32 version() const {
return version_;
}
tl_object_ptr<ton_api::dht_node> tl() const {
return create_tl_object<ton_api::dht_node>(id_.tl(), addr_list_.tl(), version_, signature_.clone_as_buffer_slice());
}
DhtNode clone() const {
return DhtNode{id_, addr_list_, version_, signature_.clone()};
}
};
class DhtNodesList {
public:
DhtNodesList() {
}
DhtNodesList(tl_object_ptr<ton_api::dht_nodes> R) {
for (auto &n : R->nodes_) {
auto N = DhtNode::create(std::move(n));
if (N.is_ok()) {
list_.emplace_back(N.move_as_ok());
} else {
LOG(WARNING) << "bad dht node: " << N.move_as_error();
}
}
}
void push_back(DhtNode node) {
list_.emplace_back(std::move(node));
}
tl_object_ptr<ton_api::dht_nodes> tl() const;
std::vector<DhtNode> &list() {
return list_;
}
const std::vector<DhtNode> &list() const {
return list_;
}
td::uint32 size() const {
return static_cast<td::uint32>(list_.size());
}
private:
std::vector<DhtNode> list_;
};
} // namespace dht
} // namespace ton

273
dht/dht-query.cpp Normal file
View file

@ -0,0 +1,273 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "dht.hpp"
#include "td/utils/tl_storers.h"
#include "td/utils/crypto.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/Random.h"
#include "td/utils/overloaded.h"
#include "td/utils/format.h"
#include "keys/encryptor.h"
#include "auto/tl/ton_api.hpp"
#include "dht-query.hpp"
namespace ton {
namespace dht {
void DhtQuery::send_queries() {
VLOG(DHT_EXTRA_DEBUG) << this << ": sending new queries. active=" << active_queries_ << " max_active=" << a_;
while (pending_ids_.size() > 0 && active_queries_ < a_) {
active_queries_++;
auto id_xor = *pending_ids_.begin();
auto id = id_xor ^ key_;
VLOG(DHT_EXTRA_DEBUG) << this << ": sending " << get_name() << " query to " << id;
pending_ids_.erase(id_xor);
auto it = list_.find(id_xor);
CHECK(it != list_.end());
td::actor::send_closure(adnl_, &adnl::Adnl::add_peer, get_src(), it->second.adnl_id(), it->second.addr_list());
send_one_query(id.to_adnl());
}
if (active_queries_ == 0) {
CHECK(pending_ids_.size() == 0);
DhtNodesList list;
for (auto &node : list_) {
list.push_back(std::move(node.second));
}
CHECK(list.size() <= k_);
VLOG(DHT_EXTRA_DEBUG) << this << ": finalizing " << get_name() << " query. List size=" << list.size();
finish(std::move(list));
stop();
}
}
void DhtQuery::add_nodes(DhtNodesList list) {
VLOG(DHT_EXTRA_DEBUG) << this << ": " << get_name() << " query: received " << list.size() << " new dht nodes";
for (auto &node : list.list()) {
auto id = node.get_key();
auto id_xor = key_ ^ id;
if (list_.find(id_xor) != list_.end()) {
continue;
}
td::actor::send_closure(node_, &DhtMember::add_full_node, id, node.clone());
DhtKeyId last_id_xor;
if (list_.size() > 0) {
last_id_xor = list_.rbegin()->first;
}
if (list_.size() < k_ || id_xor < last_id_xor) {
list_[id_xor] = std::move(node);
pending_ids_.insert(id_xor);
if (list_.size() > k_) {
CHECK(id_xor != last_id_xor);
VLOG(DHT_EXTRA_DEBUG) << this << ": " << get_name() << " query: replacing " << (last_id_xor ^ key_)
<< " key with " << id;
pending_ids_.erase(last_id_xor);
list_.erase(last_id_xor);
} else {
VLOG(DHT_EXTRA_DEBUG) << this << ": " << get_name() << " query: adding " << id << " key";
}
}
}
}
void DhtQueryFindNodes::send_one_query(adnl::AdnlNodeIdShort id) {
auto P = create_serialize_tl_object<ton_api::dht_findNode>(get_key().tl(), get_k());
auto B = create_serialize_tl_object_suffix<ton_api::dht_query>(P.as_slice(), self_.tl());
auto Pr = td::PromiseCreator::lambda([SelfId = actor_id(this), dst = id](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &DhtQueryFindNodes::on_result, std::move(R), dst);
});
td::actor::send_closure(adnl_, &adnl::Adnl::send_query, get_src(), id, "dht findNode", std::move(Pr),
td::Timestamp::in(2.0 + td::Random::fast(0, 20) * 0.1), std::move(B));
}
void DhtQueryFindNodes::on_result(td::Result<td::BufferSlice> R, adnl::AdnlNodeIdShort dst) {
if (R.is_error()) {
VLOG(DHT_INFO) << this << ": failed find nodes query " << get_src() << "->" << dst << ": " << R.move_as_error();
finish_query();
return;
}
auto Res = fetch_tl_object<ton_api::dht_nodes>(R.move_as_ok(), true);
if (Res.is_error()) {
VLOG(DHT_WARNING) << this << ": incorrect result on dht.findNodes query from " << dst << ": "
<< Res.move_as_error();
} else {
add_nodes(DhtNodesList{Res.move_as_ok()});
}
finish_query();
}
void DhtQueryFindNodes::finish(DhtNodesList list) {
promise_.set_result(std::move(list));
}
void DhtQueryFindValue::send_one_query(adnl::AdnlNodeIdShort id) {
auto P = create_serialize_tl_object<ton_api::dht_findValue>(get_key().tl(), get_k());
auto B = create_serialize_tl_object_suffix<ton_api::dht_query>(P.as_slice(), self_.tl());
auto Pr = td::PromiseCreator::lambda([SelfId = actor_id(this), dst = id](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &DhtQueryFindValue::on_result, std::move(R), dst);
});
td::actor::send_closure(adnl_, &adnl::Adnl::send_query, get_src(), id, "dht findValue", std::move(Pr),
td::Timestamp::in(2.0 + td::Random::fast(0, 20) * 0.1), std::move(B));
}
void DhtQueryFindValue::on_result(td::Result<td::BufferSlice> R, adnl::AdnlNodeIdShort dst) {
if (R.is_error()) {
VLOG(DHT_INFO) << this << ": failed find value query " << get_src() << "->" << dst << ": " << R.move_as_error();
finish_query();
return;
}
auto Res = fetch_tl_object<ton_api::dht_ValueResult>(R.move_as_ok(), true);
if (Res.is_error()) {
VLOG(DHT_WARNING) << this << ": dropping incorrect answer on dht.findValue query from " << dst << ": "
<< Res.move_as_error();
finish_query();
return;
}
bool need_stop = false;
auto A = Res.move_as_ok();
ton_api::downcast_call(
*A.get(), td::overloaded(
[&](ton_api::dht_valueFound &v) {
auto valueR = DhtValue::create(std::move(v.value_), true);
if (valueR.is_error()) {
VLOG(DHT_WARNING) << this << ": received incorrect dht answer on find value query from " << dst
<< ": " << valueR.move_as_error();
return;
}
auto value = valueR.move_as_ok();
if (value.key_id() != key_) {
VLOG(DHT_WARNING) << this << ": received value for bad key on find value query from " << dst;
return;
}
promise_.set_value(std::move(value));
need_stop = true;
},
[&](ton_api::dht_valueNotFound &v) { add_nodes(DhtNodesList{std::move(v.nodes_)}); }));
if (need_stop) {
stop();
} else {
finish_query();
}
}
void DhtQueryFindValue::finish(DhtNodesList list) {
promise_.set_error(td::Status::Error(ErrorCode::notready, "dht key not found"));
}
DhtQueryStore::DhtQueryStore(DhtValue key_value, DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src,
DhtNodesList list, td::uint32 k, td::uint32 a, DhtNode self,
td::actor::ActorId<DhtMember> node, td::actor::ActorId<adnl::Adnl> adnl,
td::Promise<td::Unit> promise)
: print_id_(print_id)
, k_(k)
, a_(a)
, promise_(std::move(promise))
, value_(std::move(key_value))
, list_(std::move(list))
, self_(std::move(self)) {
node_ = node;
adnl_ = adnl;
src_ = src;
}
void DhtQueryStore::start_up() {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<DhtNodesList> res) {
td::actor::send_closure(SelfId, &DhtQueryStore::send_stores, std::move(res));
});
auto key = value_.key_id();
auto A = td::actor::create_actor<DhtQueryFindNodes>("FindNodesQuery", key, print_id_, src_, std::move(list_), k_, a_,
self_.clone(), node_, adnl_, std::move(P));
A.release();
}
void DhtQueryStore::send_stores(td::Result<DhtNodesList> R) {
if (R.is_error()) {
auto S = R.move_as_error();
VLOG(DHT_NOTICE) << this << ": failed to get nearest nodes to " << value_.key_id() << ": " << S;
promise_.set_error(std::move(S));
stop();
return;
}
auto list = R.move_as_ok();
if (list.size() < k_) {
td::actor::send_closure(node_, &DhtMember::store_in, value_.clone());
} else {
auto last_key = list.list().rbegin()->get_key();
auto value_key = value_.key_id();
if ((value_key ^ src_) < (value_key ^ last_key)) {
td::actor::send_closure(node_, &DhtMember::store_in, value_.clone());
}
}
remaining_ = static_cast<td::uint32>(list.size());
for (auto &node : list.list()) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &DhtQueryStore::store_ready, std::move(R));
});
auto M = create_serialize_tl_object<ton_api::dht_store>(value_.tl());
td::actor::send_closure(adnl_, &adnl::Adnl::send_query, src_, node.adnl_id().compute_short_id(), "dht store",
std::move(P), td::Timestamp::in(2.0 + td::Random::fast(0, 20) * 0.1), std::move(M));
}
}
void DhtQueryStore::store_ready(td::Result<td::BufferSlice> R) {
if (R.is_error()) {
fail_++;
VLOG(DHT_INFO) << this << ": failed store query: " << R.move_as_error();
} else {
auto R2 = fetch_tl_object<ton_api::dht_stored>(R.move_as_ok(), true);
if (R2.is_error()) {
fail_++;
VLOG(DHT_WARNING) << this << ": can not parse answer (expected dht.stored): " << R2.move_as_error();
} else {
success_++;
}
}
CHECK(remaining_ > 0);
remaining_--;
if (remaining_ == 0) {
if (success_ > 0) {
promise_.set_value(td::Unit());
} else {
promise_.set_result(td::Status::Error("failed to make actual store query"));
}
stop();
}
}
} // namespace dht
} // namespace ton

177
dht/dht-query.hpp Normal file
View file

@ -0,0 +1,177 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <set>
#include <map>
#include "td/utils/int_types.h"
#include "td/actor/actor.h"
#include "adnl/adnl.h"
#include "td/actor/PromiseFuture.h"
#include "auto/tl/ton_api.hpp"
#include "dht.hpp"
namespace ton {
namespace dht {
class DhtMember;
class DhtQuery : public td::actor::Actor {
protected:
DhtKeyId key_;
DhtNode self_;
public:
DhtQuery(DhtKeyId key, DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, DhtNodesList list, td::uint32 k,
td::uint32 a, DhtNode self, td::actor::ActorId<DhtMember> node, td::actor::ActorId<adnl::Adnl> adnl)
: key_(key), self_(std::move(self)), print_id_(print_id), src_(src), k_(k), a_(a), node_(node), adnl_(adnl) {
add_nodes(std::move(list));
}
DhtMember::PrintId print_id() const {
return print_id_;
}
void send_queries();
void add_nodes(DhtNodesList list);
void finish_query() {
active_queries_--;
CHECK(active_queries_ <= k_);
send_queries();
}
DhtKeyId get_key() const {
return key_;
}
adnl::AdnlNodeIdShort get_src() const {
return src_;
}
td::uint32 get_k() const {
return k_;
}
void start_up() override {
send_queries();
}
virtual void send_one_query(adnl::AdnlNodeIdShort id) = 0;
virtual void finish(DhtNodesList list) = 0;
virtual std::string get_name() const = 0;
private:
DhtMember::PrintId print_id_;
adnl::AdnlNodeIdShort src_;
std::map<DhtKeyId, DhtNode> list_;
std::set<DhtKeyId> pending_ids_;
td::uint32 k_;
td::uint32 a_;
td::actor::ActorId<DhtMember> node_;
td::uint32 active_queries_ = 0;
protected:
td::actor::ActorId<adnl::Adnl> adnl_;
};
class DhtQueryFindNodes : public DhtQuery {
private:
td::Promise<DhtNodesList> promise_;
public:
DhtQueryFindNodes(DhtKeyId key, DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, DhtNodesList list,
td::uint32 k, td::uint32 a, DhtNode self, td::actor::ActorId<DhtMember> node,
td::actor::ActorId<adnl::Adnl> adnl, td::Promise<DhtNodesList> promise)
: DhtQuery(key, print_id, src, std::move(list), k, a, std::move(self), node, adnl), promise_(std::move(promise)) {
}
void send_one_query(adnl::AdnlNodeIdShort id) override;
void on_result(td::Result<td::BufferSlice> R, adnl::AdnlNodeIdShort dst);
void finish(DhtNodesList list) override;
std::string get_name() const override {
return "find nodes";
}
};
class DhtQueryFindValue : public DhtQuery {
private:
td::Promise<DhtValue> promise_;
public:
DhtQueryFindValue(DhtKeyId key, DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, DhtNodesList list,
td::uint32 k, td::uint32 a, DhtNode self, td::actor::ActorId<DhtMember> node,
td::actor::ActorId<adnl::Adnl> adnl, td::Promise<DhtValue> promise)
: DhtQuery(key, print_id, src, std::move(list), k, a, std::move(self), node, adnl), promise_(std::move(promise)) {
}
void send_one_query(adnl::AdnlNodeIdShort id) override;
void on_result(td::Result<td::BufferSlice> R, adnl::AdnlNodeIdShort dst);
void finish(DhtNodesList list) override;
std::string get_name() const override {
return "find value";
}
};
class DhtQueryStore : public td::actor::Actor {
private:
DhtMember::PrintId print_id_;
td::uint32 k_;
td::uint32 a_;
td::Promise<td::Unit> promise_;
td::actor::ActorId<DhtMember> node_;
td::actor::ActorId<adnl::Adnl> adnl_;
adnl::AdnlNodeIdShort src_;
DhtValue value_;
td::uint32 success_ = 0;
td::uint32 fail_ = 0;
td::uint32 remaining_;
DhtNodesList list_;
DhtNode self_;
public:
DhtQueryStore(DhtValue key_value, DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, DhtNodesList list,
td::uint32 k, td::uint32 a, DhtNode self, td::actor::ActorId<DhtMember> node,
td::actor::ActorId<adnl::Adnl> adnl, td::Promise<td::Unit> promise);
void send_stores(td::Result<DhtNodesList> res);
void store_ready(td::Result<td::BufferSlice> res);
void start_up() override;
DhtMember::PrintId print_id() const {
return print_id_;
}
};
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtQuery &dht) {
sb << dht.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtQuery *dht) {
sb << dht->print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtQueryStore &dht) {
sb << dht.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtQueryStore *dht) {
sb << dht->print_id();
return sb;
}
} // namespace dht
} // namespace ton

134
dht/dht-remote-node.cpp Normal file
View file

@ -0,0 +1,134 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "dht.hpp"
#include "td/utils/tl_storers.h"
#include "td/utils/crypto.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/Random.h"
#include "td/utils/format.h"
#include "keys/encryptor.h"
#include "auto/tl/ton_api.hpp"
#include "dht-remote-node.hpp"
namespace ton {
namespace dht {
td::Status DhtRemoteNode::receive_ping(DhtNode node, td::actor::ActorId<adnl::Adnl> adnl,
adnl::AdnlNodeIdShort self_id) {
TRY_STATUS(update_value(std::move(node), adnl, self_id));
missed_pings_ = 0;
if (ready_from_ == 0) {
ready_from_ = td::Time::now_cached();
}
return td::Status::OK();
}
td::Status DhtRemoteNode::update_value(DhtNode node, td::actor::ActorId<adnl::Adnl> adnl,
adnl::AdnlNodeIdShort self_id) {
CHECK(node.adnl_id() == node_.adnl_id());
if (node.version() <= node_.version()) {
return td::Status::OK();
}
TRY_RESULT(enc, node.adnl_id().pubkey().create_encryptor());
auto tl = node.tl();
auto sig = std::move(tl->signature_);
TRY_STATUS_PREFIX(enc->check_signature(serialize_tl_object(tl, true).as_slice(), sig.as_slice()),
"bad node signature: ");
node_ = std::move(node);
td::actor::send_closure(adnl, &adnl::Adnl::add_peer, self_id, node_.adnl_id(), node_.addr_list());
return td::Status::OK();
}
void DhtRemoteNode::send_ping(td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<DhtMember> node,
adnl::AdnlNodeIdShort src) {
missed_pings_++;
if (missed_pings_ > max_missed_pings_ && ready_from_ > 0) {
ready_from_ = 0;
failed_from_ = td::Time::now_cached();
}
last_ping_at_ = td::Time::now_cached();
td::actor::send_closure(adnl, &adnl::Adnl::add_peer, src, node_.adnl_id(), node_.addr_list());
auto P = td::PromiseCreator::lambda(
[key = id_, id = node_.adnl_id().compute_short_id(), node, src, adnl](td::Result<DhtNode> R) mutable {
if (R.is_error()) {
LOG(ERROR) << "[dht]: failed to get self node";
return;
}
auto P = td::PromiseCreator::lambda([key, node, adnl](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
VLOG(DHT_INFO) << "[dht]: received error for query to " << key << ": " << R.move_as_error();
return;
}
auto F = fetch_tl_object<ton_api::dht_node>(R.move_as_ok(), true);
if (F.is_ok()) {
auto N = DhtNode::create(F.move_as_ok());
if (N.is_ok()) {
td::actor::send_closure(node, &DhtMember::receive_ping, key, N.move_as_ok());
} else {
VLOG(DHT_WARNING) << "[dht]: bad answer from " << key
<< ": dropping bad getSignedAddressList() query answer: " << N.move_as_error();
}
} else {
VLOG(DHT_WARNING) << "[dht]: bad answer from " << key
<< ": dropping invalid getSignedAddressList() query answer: " << F.move_as_error();
}
});
auto Q = create_serialize_tl_object<ton_api::dht_getSignedAddressList>();
auto B = create_serialize_tl_object_suffix<ton_api::dht_query>(Q.as_slice(), R.move_as_ok().tl());
td::actor::send_closure(adnl, &adnl::Adnl::send_query, src, id, "dht ping", std::move(P),
td::Timestamp::in(10.0 + td::Random::fast(0, 100) * 0.1), std::move(B));
});
td::actor::send_closure(node, &DhtMember::get_self_node, std::move(P));
}
adnl::AdnlAddressList DhtRemoteNode::get_addr_list() const {
return node_.addr_list();
}
adnl::AdnlNodeIdFull DhtRemoteNode::get_full_id() const {
return node_.adnl_id();
}
td::Result<std::unique_ptr<DhtRemoteNode>> DhtRemoteNode::create(DhtNode node, td::uint32 max_missed_pings) {
TRY_RESULT(enc, node.adnl_id().pubkey().create_encryptor());
auto tl = node.tl();
auto sig = std::move(tl->signature_);
TRY_STATUS_PREFIX(enc->check_signature(serialize_tl_object(tl, true).as_slice(), sig.as_slice()),
"bad node signature: ");
return std::make_unique<DhtRemoteNode>(std::move(node), max_missed_pings);
}
} // namespace dht
} // namespace ton

86
dht/dht-remote-node.hpp Normal file
View file

@ -0,0 +1,86 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
#include "td/actor/actor.h"
#include "adnl/adnl.h"
#include "adnl/utils.hpp"
#include "dht.hpp"
#include "auto/tl/ton_api.hpp"
namespace ton {
namespace dht {
class DhtMember;
class DhtRemoteNode {
private:
DhtKeyId id_;
DhtNode node_;
td::uint32 max_missed_pings_;
td::uint32 missed_pings_ = 0;
double last_ping_at_ = 0;
double ready_from_ = 0;
double failed_from_ = 0;
td::int32 version_;
public:
DhtRemoteNode(DhtNode node, td::uint32 max_missed_pings)
: node_(std::move(node)), max_missed_pings_(max_missed_pings) {
failed_from_ = td::Time::now_cached();
id_ = node.get_key();
}
static td::Result<std::unique_ptr<DhtRemoteNode>> create(DhtNode node, td::uint32 max_missed_pings);
DhtNode get_node() const {
return node_.clone();
}
double failed_from() const {
return failed_from_;
}
adnl::AdnlAddressList get_addr_list() const;
adnl::AdnlNodeIdFull get_full_id() const;
DhtKeyId get_key() const {
return id_;
}
td::uint32 missed_pings() const {
return missed_pings_;
}
bool is_ready() const {
return ready_from_ > 0;
}
double ready_from() const {
return ready_from_;
}
double last_ping_at() const {
return last_ping_at_;
}
void send_ping(td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<DhtMember> node, adnl::AdnlNodeIdShort src);
td::Status receive_ping(DhtNode node, td::actor::ActorId<adnl::Adnl> adnl, adnl::AdnlNodeIdShort self_id);
td::Status update_value(DhtNode node, td::actor::ActorId<adnl::Adnl> adnl, adnl::AdnlNodeIdShort self_id);
};
} // namespace dht
} // namespace ton

383
dht/dht-types.cpp Normal file
View file

@ -0,0 +1,383 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "dht-types.h"
#include "td/utils/Random.h"
#include "td/utils/overloaded.h"
#include "keys/encryptor.h"
#include "auto/tl/ton_api.hpp"
#include <map>
namespace ton {
namespace dht {
td::Result<DhtKey> DhtKey::create(tl_object_ptr<ton_api::dht_key> key) {
if (key->name_.length() > max_name_length()) {
return td::Status::Error(ErrorCode::error, PSTRING() << "too big name length. length=" << key->name_.length());
}
if (!key->name_.length()) {
return td::Status::Error(ErrorCode::error, PSTRING() << "empty dht key name");
}
if (key->idx_ < 0 || static_cast<td::uint32>(key->idx_) > max_index()) {
return td::Status::Error(ErrorCode::error, PSTRING() << "bad dht key index " << key->idx_);
}
return DhtKey{PublicKeyHash{key->id_}, key->name_.as_slice().str(), static_cast<td::uint32>(key->idx_)};
}
tl_object_ptr<ton_api::dht_key> DhtKey::tl() const {
return create_tl_object<ton_api::dht_key>(id_.tl(), td::BufferSlice{namestr_}, idx_);
}
td::Status DhtKey::check() const {
if (namestr_.length() > max_name_length()) {
return td::Status::Error(ErrorCode::error, PSTRING() << "too big name length. length=" << namestr_.length());
}
if (namestr_.length() == 0) {
return td::Status::Error(ErrorCode::error, PSTRING() << "empty dht key name");
}
if (static_cast<td::uint32>(idx_) > max_index()) {
return td::Status::Error(ErrorCode::error, PSTRING() << "bad dht key index " << idx_);
}
return td::Status::OK();
}
DhtKeyId DhtKey::compute_key_id() const {
return DhtKeyId{get_tl_object_sha_bits256(tl())};
}
DhtKey DhtKey::clone() const {
return DhtKey{id_, namestr_, idx_};
}
void DhtKeyDescription::update_signature(td::BufferSlice signature) {
signature_ = td::SharedSlice{signature.as_slice()};
}
void DhtKeyDescription::update_signature(td::SharedSlice signature) {
signature_ = std::move(signature);
}
td::Status DhtKeyDescription::check() const {
TRY_STATUS(key_.check());
if (public_key_.compute_short_id() != key_.public_key_hash()) {
return td::Status::Error(ErrorCode::protoviolation, "key hash mismatch");
}
auto obj = tl();
obj->signature_ = td::BufferSlice{};
auto B = serialize_tl_object(obj, true);
TRY_RESULT(E, public_key_.create_encryptor());
TRY_STATUS(E->check_signature(B.as_slice(), signature_.as_slice()));
return td::Status::OK();
}
tl_object_ptr<ton_api::dht_keyDescription> DhtKeyDescription::tl() const {
return create_tl_object<ton_api::dht_keyDescription>(key_.tl(), public_key_.tl(), update_rule_->tl(),
signature_.clone_as_buffer_slice());
}
td::BufferSlice DhtKeyDescription::to_sign() const {
return create_serialize_tl_object<ton_api::dht_keyDescription>(key_.tl(), public_key_.tl(), update_rule_->tl(),
td::BufferSlice());
}
DhtKeyDescription DhtKeyDescription::clone() const {
return DhtKeyDescription{key_.clone(), public_key_, update_rule_, signature_.clone_as_buffer_slice()};
}
td::Result<DhtKeyDescription> DhtKeyDescription::create(DhtKey key, PublicKey public_key,
std::shared_ptr<DhtUpdateRule> update_rule,
td::BufferSlice signature) {
DhtKeyDescription desc{std::move(key), std::move(public_key), std::move(update_rule), std::move(signature)};
TRY_STATUS(desc.check());
return std::move(desc);
}
td::Result<DhtKeyDescription> DhtKeyDescription::create(DhtKey key, PublicKey public_key,
std::shared_ptr<DhtUpdateRule> update_rule,
td::SharedSlice signature) {
DhtKeyDescription desc{std::move(key), std::move(public_key), std::move(update_rule), std::move(signature)};
TRY_STATUS(desc.check());
return std::move(desc);
}
td::Result<DhtKeyDescription> DhtKeyDescription::create(tl_object_ptr<ton_api::dht_keyDescription> desc,
bool check_signature) {
auto signature = std::move(desc->signature_);
td::BufferSlice to_sign;
if (check_signature) {
to_sign = serialize_tl_object(desc, true);
}
auto public_key = PublicKey{desc->id_};
TRY_RESULT(key, DhtKey::create(std::move(desc->key_)));
if (key.public_key_hash() != public_key.compute_short_id()) {
return td::Status::Error(ErrorCode::error, "inconsistent dht key description");
}
TRY_RESULT(update_rule, DhtUpdateRule::create(std::move(desc->update_rule_)));
if (check_signature) {
TRY_RESULT(E, public_key.create_encryptor());
TRY_STATUS(E->check_signature(to_sign.as_slice(), signature.as_slice()));
}
return DhtKeyDescription{std::move(key), std::move(public_key), std::move(update_rule), std::move(signature)};
}
td::Result<DhtValue> DhtValue::create(tl_object_ptr<ton_api::dht_value> obj, bool check_signature) {
TRY_RESULT(desc, DhtKeyDescription::create(std::move(obj->key_), check_signature));
return create(std::move(desc), std::move(obj->value_), obj->ttl_, std::move(obj->signature_));
}
td::Result<DhtValue> DhtValue::create(DhtKeyDescription key, td::BufferSlice value, td::uint32 ttl,
td::BufferSlice signature) {
TRY_STATUS(key.check());
DhtValue v{std::move(key), std::move(value), ttl, std::move(signature)};
TRY_STATUS(v.key().update_rule()->check_value(v));
return std::move(v);
}
td::Result<DhtValue> DhtValue::create(DhtKeyDescription key, td::SharedSlice value, td::uint32 ttl,
td::SharedSlice signature) {
TRY_STATUS(key.check());
DhtValue v{std::move(key), std::move(value), ttl, std::move(signature)};
TRY_STATUS(v.key().update_rule()->check_value(v));
return std::move(v);
}
DhtValue DhtValue::clone() const {
return DhtValue{key_.clone(), value_.clone(), ttl_, signature_.clone()};
}
tl_object_ptr<ton_api::dht_value> DhtValue::tl() const {
return create_tl_object<ton_api::dht_value>(key_.tl(), value_.clone_as_buffer_slice(), ttl_,
signature_.clone_as_buffer_slice());
}
td::BufferSlice DhtValue::to_sign() const {
return create_serialize_tl_object<ton_api::dht_value>(key_.tl(), value_.clone_as_buffer_slice(), ttl_,
td::BufferSlice());
}
td::Status DhtValue::update(DhtValue &&value) {
TRY_STATUS(value.check());
return key_.update_rule()->update_value(*this, std::move(value));
}
void DhtValue::set(td::BufferSlice value, td::uint32 ttl, td::BufferSlice signature) {
value_ = td::SharedSlice{value.as_slice()};
ttl_ = ttl;
signature_ = td::SharedSlice{signature.as_slice()};
}
void DhtValue::set(td::SharedSlice value, td::uint32 ttl, td::SharedSlice signature) {
value_ = std::move(value);
ttl_ = ttl;
signature_ = std::move(signature);
}
void DhtValue::update_signature(td::BufferSlice signature) {
signature_ = td::SharedSlice{signature.as_slice()};
}
void DhtValue::update_signature(td::SharedSlice signature) {
signature_ = std::move(signature);
}
td::Status DhtValue::check() const {
TRY_STATUS(key_.check());
return key_.update_rule()->check_value(*this);
}
DhtKeyId DhtValue::key_id() const {
return key_.key().compute_key_id();
}
td::Status DhtUpdateRuleSignature::check_value(const DhtValue &value) {
if (value.value().size() > DhtValue::max_value_size()) {
return td::Status::Error(ErrorCode::protoviolation, "too big value");
}
TRY_RESULT(E, value.key().public_key().create_encryptor());
auto tl = value.tl();
auto sig = std::move(tl->signature_);
auto B = serialize_tl_object(tl, true);
return E->check_signature(B.as_slice(), sig.as_slice());
}
td::Status DhtUpdateRuleSignature::update_value(DhtValue &value, DhtValue &&new_value) {
TRY_STATUS(new_value.check());
CHECK(value.key_id() == new_value.key_id());
if (new_value.ttl() > value.ttl()) {
value.set(new_value.value().clone(), new_value.ttl(), new_value.signature().clone());
value.check().ensure();
}
return td::Status::OK();
}
tl_object_ptr<ton_api::dht_UpdateRule> DhtUpdateRuleSignature::tl() const {
return create_tl_object<ton_api::dht_updateRule_signature>();
}
td::Result<std::shared_ptr<DhtUpdateRule>> DhtUpdateRuleSignature::create() {
return std::make_shared<DhtUpdateRuleSignature>();
}
td::Status DhtUpdateRuleAnybody::check_value(const DhtValue &value) {
if (value.value().size() > DhtValue::max_value_size()) {
return td::Status::Error(ErrorCode::protoviolation, "too big value");
}
if (value.signature().size() > 0) {
return td::Status::Error(ErrorCode::protoviolation, "cannot have signature in DhtUpdateRuleAnybody");
}
return td::Status::OK();
}
td::Status DhtUpdateRuleAnybody::update_value(DhtValue &value, DhtValue &&new_value) {
CHECK(value.key_id() == new_value.key_id());
value.set(new_value.value().clone(), new_value.ttl(), new_value.signature().clone());
return td::Status::OK();
}
tl_object_ptr<ton_api::dht_UpdateRule> DhtUpdateRuleAnybody::tl() const {
return create_tl_object<ton_api::dht_updateRule_anybody>();
}
td::Result<std::shared_ptr<DhtUpdateRule>> DhtUpdateRuleAnybody::create() {
return std::make_shared<DhtUpdateRuleAnybody>();
}
td::Status DhtUpdateRuleOverlayNodes::check_value(const DhtValue &value) {
if (value.value().size() > DhtValue::max_value_size()) {
return td::Status::Error(ErrorCode::protoviolation, "too big value");
}
if (value.signature().size() > 0) {
return td::Status::Error(ErrorCode::protoviolation, "cannot have signature in DhtUpdateRuleOverlayNodes");
}
auto F = fetch_tl_object<ton_api::overlay_nodes>(value.value().clone_as_buffer_slice(), true);
if (F.is_error()) {
return td::Status::Error(ErrorCode::protoviolation, "bad overlay nodes value");
}
auto L = F.move_as_ok();
for (auto &node : L->nodes_) {
TRY_RESULT(pub, adnl::AdnlNodeIdFull::create(node->id_));
auto sig = std::move(node->signature_);
auto obj =
create_tl_object<ton_api::overlay_node_toSign>(pub.compute_short_id().tl(), node->overlay_, node->version_);
if (node->overlay_ != value.key().key().public_key_hash().bits256_value()) {
return td::Status::Error(ErrorCode::protoviolation, "bad overlay id");
}
auto B = serialize_tl_object(obj, true);
TRY_RESULT(E, pub.pubkey().create_encryptor());
TRY_STATUS(E->check_signature(B.as_slice(), sig.as_slice()));
}
return td::Status::OK();
}
td::Status DhtUpdateRuleOverlayNodes::update_value(DhtValue &value, DhtValue &&new_value) {
TRY_RESULT_PREFIX(N, fetch_tl_object<ton_api::overlay_nodes>(value.value().clone_as_buffer_slice(), true),
"bad dht value in updateRule.overlayNodes: ");
TRY_RESULT_PREFIX(L, fetch_tl_object<ton_api::overlay_nodes>(new_value.value().clone_as_buffer_slice(), true),
"bad dht value in updateRule.overlayNodes: ");
std::vector<tl_object_ptr<ton_api::overlay_node>> res;
std::map<adnl::AdnlNodeIdShort, size_t> S;
for (auto &n : N->nodes_) {
TRY_RESULT(pub, adnl::AdnlNodeIdFull::create(n->id_));
auto id = pub.compute_short_id();
auto it = S.find(id);
if (it != S.end()) {
auto &m = res[it->second];
if (m->version_ < n->version_) {
m = std::move(n);
}
} else {
S.emplace(id, res.size());
res.emplace_back(std::move(n));
}
}
for (auto &n : L->nodes_) {
TRY_RESULT(pub, adnl::AdnlNodeIdFull::create(n->id_));
auto id = pub.compute_short_id();
auto it = S.find(id);
if (it != S.end()) {
auto &m = res[it->second];
if (m->version_ < n->version_) {
m = std::move(n);
}
} else {
S.emplace(id, res.size());
res.emplace_back(std::move(n));
}
}
size_t size = 8; // magic + size
std::vector<std::pair<td::uint32, size_t>> v;
for (td::uint32 i = 0; i < res.size(); i++) {
v.emplace_back(i, serialize_tl_object(res[i], false).size());
size += v[i].second;
}
while (size > DhtValue::max_value_size()) {
CHECK(v.size() > 0);
auto idx = td::Random::fast(0, static_cast<td::int32>(v.size() - 1));
size -= v[idx].second;
v[idx] = v[v.size() - 1];
v.resize(v.size() - 1);
}
std::vector<tl_object_ptr<ton_api::overlay_node>> vec;
for (auto &p : v) {
vec.push_back(std::move(res[p.first]));
}
auto nodes = create_serialize_tl_object<ton_api::overlay_nodes>(std::move(vec));
CHECK(nodes.size() == size);
CHECK(nodes.size() <= DhtValue::max_value_size());
value.set(std::move(nodes), std::max(value.ttl(), new_value.ttl()), td::BufferSlice{});
value.check().ensure();
return td::Status::OK();
}
tl_object_ptr<ton_api::dht_UpdateRule> DhtUpdateRuleOverlayNodes::tl() const {
return create_tl_object<ton_api::dht_updateRule_overlayNodes>();
}
td::Result<std::shared_ptr<DhtUpdateRule>> DhtUpdateRuleOverlayNodes::create() {
return std::make_shared<DhtUpdateRuleOverlayNodes>();
}
td::Result<std::shared_ptr<DhtUpdateRule>> DhtUpdateRule::create(tl_object_ptr<ton_api::dht_UpdateRule> obj) {
td::Result<std::shared_ptr<DhtUpdateRule>> R;
ton_api::downcast_call(
*obj.get(),
td::overloaded([&](ton_api::dht_updateRule_signature &obj) { R = DhtUpdateRuleSignature::create(); },
[&](ton_api::dht_updateRule_anybody &obj) { R = DhtUpdateRuleAnybody::create(); },
[&](ton_api::dht_updateRule_overlayNodes &obj) { R = DhtUpdateRuleOverlayNodes::create(); }));
return R;
}
} // namespace dht
} // namespace ton

268
dht/dht-types.h Normal file
View file

@ -0,0 +1,268 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
#include "td/utils/SharedSlice.h"
#include "adnl/adnl-node-id.hpp"
#include "tl-utils/tl-utils.hpp"
#include "common/io.hpp"
namespace ton {
namespace dht {
using DhtKeyName = std::string;
class DhtKeyId {
public:
explicit DhtKeyId(td::Bits256 value) : value_(value) {
}
explicit DhtKeyId(adnl::AdnlNodeIdShort value) : value_(value.bits256_value()) {
}
DhtKeyId() {
}
td::Bits256 tl() const {
return value_;
}
bool operator<(const DhtKeyId &with) const {
return value_ < with.value_;
}
bool operator==(const DhtKeyId &with) const {
return value_ == with.value_;
}
bool operator!=(const DhtKeyId &with) const {
return value_ != with.value_;
}
DhtKeyId operator^(const DhtKeyId &with) const {
return DhtKeyId{value_ ^ with.value_};
}
DhtKeyId operator^(const adnl::AdnlNodeIdShort &with) const {
return DhtKeyId{value_ ^ with.bits256_value()};
}
bool get_bit(td::uint32 bit) const {
return value_[bit];
}
td::uint32 count_leading_zeroes() const {
return value_.count_leading_zeroes();
}
adnl::AdnlNodeIdShort to_adnl() const {
return adnl::AdnlNodeIdShort{value_};
}
static DhtKeyId zero() {
return DhtKeyId{td::Bits256::zero()};
}
private:
td::Bits256 value_;
};
using DhtXoredKeyId = DhtKeyId;
class DhtKey {
public:
static constexpr td::uint32 max_name_length() {
return 127;
}
static constexpr td::uint32 max_index() {
return 15;
}
DhtKey(PublicKeyHash id, DhtKeyName namestr, td::uint32 idx)
: id_(std::move(id)), namestr_(std::move(namestr)), idx_(idx) {
CHECK(namestr.size() <= max_name_length());
}
static td::Result<DhtKey> create(tl_object_ptr<ton_api::dht_key> key);
td::Status check() const;
const auto &public_key_hash() const {
return id_;
}
const auto &name() const {
return namestr_;
}
td::uint32 idx() const {
return idx_;
}
tl_object_ptr<ton_api::dht_key> tl() const;
DhtKeyId compute_key_id() const;
DhtKey clone() const;
private:
PublicKeyHash id_;
DhtKeyName namestr_;
td::uint32 idx_;
};
class DhtValue;
class DhtUpdateRule {
public:
virtual ~DhtUpdateRule() = default;
virtual td::Status check_value(const DhtValue &value) = 0;
virtual td::Status update_value(DhtValue &value, DhtValue &&new_value) = 0;
virtual bool need_republish() const = 0;
virtual tl_object_ptr<ton_api::dht_UpdateRule> tl() const = 0;
static td::Result<std::shared_ptr<DhtUpdateRule>> create(tl_object_ptr<ton_api::dht_UpdateRule> obj);
};
class DhtKeyDescription {
public:
DhtKeyDescription(DhtKey key, PublicKey public_key, std::shared_ptr<DhtUpdateRule> update_rule,
td::BufferSlice signature)
: key_(std::move(key))
, public_key_(std::move(public_key))
, update_rule_(std::move(update_rule))
, signature_(signature.as_slice()) {
}
DhtKeyDescription(DhtKey key, PublicKey public_key, std::shared_ptr<DhtUpdateRule> update_rule,
td::SharedSlice signature)
: key_(std::move(key))
, public_key_(std::move(public_key))
, update_rule_(std::move(update_rule))
, signature_(std::move(signature)) {
}
const auto &key() const {
return key_;
}
const auto &public_key() const {
return public_key_;
}
const auto &update_rule() const {
return update_rule_;
}
void update_signature(td::BufferSlice signature);
void update_signature(td::SharedSlice signature);
td::BufferSlice to_sign() const;
td::Status check() const;
tl_object_ptr<ton_api::dht_keyDescription> tl() const;
DhtKeyDescription clone() const;
static td::Result<DhtKeyDescription> create(tl_object_ptr<ton_api::dht_keyDescription> desc, bool check_signature);
static td::Result<DhtKeyDescription> create(DhtKey key, PublicKey public_key,
std::shared_ptr<DhtUpdateRule> update_rule, td::BufferSlice signature);
static td::Result<DhtKeyDescription> create(DhtKey key, PublicKey public_key,
std::shared_ptr<DhtUpdateRule> update_rule, td::SharedSlice signature);
private:
DhtKey key_;
PublicKey public_key_;
std::shared_ptr<DhtUpdateRule> update_rule_;
td::SharedSlice signature_;
};
class DhtValue {
public:
static constexpr td::uint32 max_value_size() {
return 768;
}
DhtValue(DhtKeyDescription key, td::BufferSlice value, td::uint32 ttl, td::BufferSlice signature)
: key_(std::move(key)), value_(value.as_slice()), ttl_(ttl), signature_(signature.as_slice()) {
}
DhtValue(DhtKeyDescription key, td::SharedSlice value, td::uint32 ttl, td::SharedSlice signature)
: key_(std::move(key)), value_(std::move(value)), ttl_(ttl), signature_(std::move(signature)) {
}
static td::Result<DhtValue> create(tl_object_ptr<ton_api::dht_value> obj, bool check_signature);
static td::Result<DhtValue> create(DhtKeyDescription key, td::BufferSlice value, td::uint32 ttl,
td::BufferSlice signature);
static td::Result<DhtValue> create(DhtKeyDescription key, td::SharedSlice value, td::uint32 ttl,
td::SharedSlice signature);
const auto &key() const {
return key_;
}
const auto &value() const {
return value_;
}
const auto &signature() const {
return signature_;
}
td::uint32 ttl() const {
return ttl_;
}
bool expired() const {
return ttl_ < td::Clocks::system();
}
DhtValue clone() const;
tl_object_ptr<ton_api::dht_value> tl() const;
td::BufferSlice to_sign() const;
td::Status update(DhtValue &&value);
void set(td::BufferSlice value, td::uint32 ttl, td::BufferSlice signature);
void set(td::SharedSlice value, td::uint32 ttl, td::SharedSlice signature);
void update_signature(td::BufferSlice signature);
void update_signature(td::SharedSlice signature);
td::Status check() const;
DhtKeyId key_id() const;
private:
DhtKeyDescription key_;
td::SharedSlice value_;
td::uint32 ttl_;
td::SharedSlice signature_;
};
class DhtUpdateRuleSignature : public DhtUpdateRule {
public:
td::Status check_value(const DhtValue &value) override;
td::Status update_value(DhtValue &value, DhtValue &&new_value) override;
bool need_republish() const override {
return true;
}
tl_object_ptr<ton_api::dht_UpdateRule> tl() const override;
static td::Result<std::shared_ptr<DhtUpdateRule>> create();
};
class DhtUpdateRuleAnybody : public DhtUpdateRule {
public:
td::Status check_value(const DhtValue &value) override;
td::Status update_value(DhtValue &value, DhtValue &&new_value) override;
bool need_republish() const override {
return false;
}
tl_object_ptr<ton_api::dht_UpdateRule> tl() const override;
static td::Result<std::shared_ptr<DhtUpdateRule>> create();
};
class DhtUpdateRuleOverlayNodes : public DhtUpdateRule {
public:
td::Status check_value(const DhtValue &value) override;
td::Status update_value(DhtValue &value, DhtValue &&new_value) override;
bool need_republish() const override {
return false;
}
tl_object_ptr<ton_api::dht_UpdateRule> tl() const override;
static td::Result<std::shared_ptr<DhtUpdateRule>> create();
};
} // namespace dht
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::dht::DhtKeyId &dht) {
sb << dht.tl();
return sb;
}
} // namespace td

552
dht/dht.cpp Normal file
View file

@ -0,0 +1,552 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "dht.hpp"
#include "td/utils/tl_storers.h"
#include "td/utils/crypto.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/Random.h"
#include "td/utils/base64.h"
#include "td/utils/format.h"
#include "td/db/RocksDb.h"
#include "keys/encryptor.h"
#include "adnl/utils.hpp"
#include "auto/tl/ton_api.hpp"
#include "dht.h"
#include "dht-bucket.hpp"
#include "dht-query.hpp"
#include "dht-in.hpp"
namespace ton {
namespace dht {
td::actor::ActorOwn<DhtMember> DhtMember::create(adnl::AdnlNodeIdShort id, std::string db_root,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl, td::uint32 k, td::uint32 a) {
return td::actor::ActorOwn<DhtMember>(
td::actor::create_actor<DhtMemberImpl>("dht", id, db_root, keyring, adnl, k, a));
}
td::Result<td::actor::ActorOwn<Dht>> Dht::create(adnl::AdnlNodeIdShort id, std::string db_root,
std::shared_ptr<DhtGlobalConfig> conf,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl) {
CHECK(conf->get_k() > 0);
CHECK(conf->get_a() > 0);
auto D = DhtMember::create(id, db_root, keyring, adnl, conf->get_k(), conf->get_a());
auto &nodes = conf->nodes();
for (auto &node : nodes.list()) {
auto key = node.get_key();
td::actor::send_closure(D, &DhtMember::add_full_node, key, node.clone());
}
return std::move(D);
}
void DhtMemberImpl::start_up() {
std::shared_ptr<td::KeyValue> kv = std::make_shared<td::RocksDb>(
td::RocksDb::open(PSTRING() << db_root_ << "/dht-" << td::base64url_encode(id_.as_slice())).move_as_ok());
std::vector<td::int32> methods = {ton_api::dht_getSignedAddressList::ID,
ton_api::dht_findNode::ID,
ton_api::dht_findValue::ID,
ton_api::dht_store::ID,
ton_api::dht_ping::ID,
ton_api::dht_query::ID,
ton_api::dht_message::ID};
for (auto it : methods) {
td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, id_, adnl::Adnl::int_to_bytestring(it),
std::make_unique<Callback>(actor_id(this), id_));
}
alarm_timestamp() = td::Timestamp::in(1.0);
for (td::uint32 bit = 0; bit < 256; bit++) {
auto key = create_hash_tl_object<ton_api::dht_db_key_bucket>(bit);
std::string value;
auto R = kv->get(key.as_slice(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) {
auto V = fetch_tl_object<ton_api::dht_db_bucket>(td::BufferSlice{value}, true);
V.ensure();
auto nodes = std::move(V.move_as_ok()->nodes_);
auto s = nodes->nodes_.size();
DhtNodesList list{std::move(nodes)};
CHECK(list.size() == s);
auto &B = buckets_[bit];
for (auto &node : list.list()) {
auto key = node.get_key();
B.add_full_node(key, std::move(node), adnl_, id_);
}
}
}
db_ = DbType{std::move(kv)};
}
void DhtMemberImpl::tear_down() {
std::vector<td::int32> methods = {ton_api::dht_getSignedAddressList::ID,
ton_api::dht_findNode::ID,
ton_api::dht_findValue::ID,
ton_api::dht_store::ID,
ton_api::dht_ping::ID,
ton_api::dht_query::ID,
ton_api::dht_message::ID};
for (auto it : methods) {
td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, id_, adnl::Adnl::int_to_bytestring(it));
}
}
void DhtMemberImpl::save_to_db() {
next_save_to_db_at_ = td::Timestamp::in(10.0);
alarm_timestamp().relax(next_save_to_db_at_);
td::uint32 bit = td::Random::fast(0, 255);
auto &B = buckets_[bit];
auto list = B.export_nodes();
if (list.size() > 0) {
auto key = create_hash_tl_object<ton_api::dht_db_key_bucket>(bit);
auto value = create_serialize_tl_object<ton_api::dht_db_bucket>(list.tl());
db_.set(key, std::move(value));
}
}
DhtNodesList DhtMemberImpl::get_nearest_nodes(DhtKeyId id, td::uint32 k) {
DhtNodesList vec;
auto id_xor = id ^ key_;
for (td::uint32 bit = 0; bit < 256; bit++) {
if (id_xor.get_bit(bit)) {
buckets_[bit].get_nearest_nodes(id, bit, vec, k);
if (vec.size() >= k) {
break;
}
}
}
for (auto &el : vec.list()) {
CHECK((el.get_key() ^ id) < id_xor);
}
if (vec.size() < k) {
for (td::uint32 bit = 255; bit != 256; bit = bit ? (bit - 1) : 256) {
if (!id_xor.get_bit(bit)) {
buckets_[bit].get_nearest_nodes(id, bit, vec, k);
if (vec.size() >= k) {
break;
}
}
}
}
CHECK(vec.size() <= k);
return vec;
}
td::uint32 DhtMemberImpl::distance(DhtKeyId key_id, td::uint32 max_value) {
if (!max_value) {
max_value = 2 * k_;
}
td::uint32 res = 0;
auto id_xor = key_id ^ key_;
for (td::uint32 bit = 0; bit < 256; bit++) {
if (id_xor.get_bit(bit)) {
res += buckets_[bit].active_cnt();
if (res >= max_value) {
return max_value;
}
}
}
return res;
}
void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_ping &query,
td::Promise<td::BufferSlice> promise) {
ping_queries_++;
promise.set_value(create_serialize_tl_object<ton_api::dht_pong>(query.random_id_));
}
void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_findNode &query,
td::Promise<td::BufferSlice> promise) {
find_node_queries_++;
auto k = static_cast<td::uint32>(query.k_);
if (k > max_k()) {
k = max_k();
}
auto R = get_nearest_nodes(DhtKeyId{query.key_}, k);
promise.set_value(serialize_tl_object(R.tl(), true));
}
void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_findValue &query,
td::Promise<td::BufferSlice> promise) {
find_value_queries_++;
auto it = values_.find(DhtKeyId{query.key_});
if (it != values_.end() && it->second.expired()) {
values_.erase(it);
it = values_.end();
}
if (it != values_.end()) {
promise.set_value(create_serialize_tl_object<ton_api::dht_valueFound>(it->second.tl()));
return;
}
auto k = static_cast<td::uint32>(query.k_);
if (k > max_k()) {
k = max_k();
}
auto R = get_nearest_nodes(DhtKeyId{query.key_}, k);
promise.set_value(create_serialize_tl_object<ton_api::dht_valueNotFound>(R.tl()));
}
td::Status DhtMemberImpl::store_in(DhtValue value) {
if (value.expired()) {
VLOG(DHT_INFO) << this << ": dropping expired value: " << value.key_id() << " expire_at = " << value.ttl();
return td::Status::OK();
}
TRY_STATUS(value.check());
auto key_id = value.key_id();
auto dist = distance(key_id, k_ + 10);
if (dist < k_ + 10) {
auto it = values_.find(key_id);
if (it != values_.end()) {
it->second.update(std::move(value));
} else {
values_.emplace(key_id, std::move(value));
}
} else {
VLOG(DHT_INFO) << this << ": dropping too remote value: " << value.key_id() << " distance = " << dist;
}
return td::Status::OK();
}
void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_store &query,
td::Promise<td::BufferSlice> promise) {
store_queries_++;
auto V = DhtValue::create(std::move(query.value_), true);
if (V.is_error()) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation,
PSTRING() << "dropping invalid dht_store() value: " << V.error().to_string()));
VLOG(DHT_INFO) << this << ": dropping invalid value: " << V.move_as_error();
return;
}
auto b = store_in(V.move_as_ok());
if (b.is_ok()) {
promise.set_value(create_serialize_tl_object<ton_api::dht_stored>());
} else {
VLOG(DHT_INFO) << this << ": dropping store() query from " << src << ": " << b.move_as_error();
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "dropping dht_store() query"));
}
}
void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_getSignedAddressList &query,
td::Promise<td::BufferSlice> promise) {
get_addr_list_queries_++;
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<DhtNode> R) mutable {
R.ensure();
promise.set_value(serialize_tl_object(R.move_as_ok().tl(), true));
});
get_self_node(std::move(P));
}
void DhtMemberImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
{
auto R = fetch_tl_prefix<ton_api::dht_query>(data, true);
if (R.is_ok()) {
auto N = DhtNode::create(std::move(R.move_as_ok()->node_));
if (N.is_ok()) {
auto node = N.move_as_ok();
auto key = node.get_key();
add_full_node(key, std::move(node));
} else {
VLOG(DHT_WARNING) << this << ": dropping bad node " << N.move_as_error();
}
}
}
auto R = fetch_tl_object<ton_api::Function>(std::move(data), true);
if (R.is_error()) {
VLOG(DHT_WARNING) << this << ": dropping unknown query to DHT node: " << R.move_as_error();
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "failed to parse dht query"));
return;
}
auto Q = R.move_as_ok();
if (td::Random::fast(0, 127) == 0) {
VLOG(DHT_DEBUG) << this << ": ping=" << ping_queries_ << " fnode=" << find_node_queries_
<< " fvalue=" << find_value_queries_ << " store=" << store_queries_
<< " addrlist=" << get_addr_list_queries_;
VLOG(DHT_DEBUG) << this << ": query to DHT from " << src << ": " << ton_api::to_string(Q);
}
VLOG(DHT_EXTRA_DEBUG) << this << ": query to DHT from " << src << ": " << ton_api::to_string(Q);
ton_api::downcast_call(*Q.get(), [&](auto &object) { this->process_query(src, object, std::move(promise)); });
}
void DhtMemberImpl::add_full_node(DhtKeyId key, DhtNode node) {
VLOG(DHT_EXTRA_DEBUG) << this << ": adding full node " << key;
auto eid = key ^ key_;
auto bit = eid.count_leading_zeroes();
#ifndef NDEBUG
for (td::uint32 i = 0; i < bit; i++) {
CHECK(key.get_bit(i) == key_.get_bit(i));
}
#endif
if (bit < 256) {
CHECK(key.get_bit(bit) != key_.get_bit(bit));
buckets_[bit].add_full_node(key, std::move(node), adnl_, id_);
} else {
CHECK(key == key_);
}
}
void DhtMemberImpl::receive_ping(DhtKeyId key, DhtNode result) {
VLOG(DHT_EXTRA_DEBUG) << this << ": received ping from " << key;
auto eid = key ^ key_;
auto bit = eid.count_leading_zeroes();
if (bit < 256) {
buckets_[bit].receive_ping(key, std::move(result), adnl_, id_);
} else {
CHECK(key == key_);
}
}
void DhtMemberImpl::receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data) {
}
void DhtMemberImpl::set_value(DhtValue value, td::Promise<td::Unit> promise) {
auto S = value.check();
if (S.is_error()) {
promise.set_error(std::move(S));
return;
}
auto h = value.key_id();
our_values_.emplace(h, value.clone());
send_store(std::move(value), std::move(promise));
}
void DhtMemberImpl::get_value_in(DhtKeyId key, td::Promise<DhtValue> result) {
auto P = td::PromiseCreator::lambda([key, promise = std::move(result), SelfId = actor_id(this), print_id = print_id(),
adnl = adnl_, list = get_nearest_nodes(key, k_), k = k_, a = a_,
id = id_](td::Result<DhtNode> R) mutable {
R.ensure();
td::actor::create_actor<DhtQueryFindValue>("FindValueQuery", key, print_id, id, std::move(list), k, a,
R.move_as_ok(), SelfId, adnl, std::move(promise))
.release();
});
get_self_node(std::move(P));
}
void DhtMemberImpl::check() {
VLOG(DHT_INFO) << this << ": ping=" << ping_queries_ << " fnode=" << find_node_queries_
<< " fvalue=" << find_value_queries_ << " store=" << store_queries_
<< " addrlist=" << get_addr_list_queries_;
for (auto &bucket : buckets_) {
bucket.check(adnl_, actor_id(this), id_);
}
if (next_save_to_db_at_.is_in_past()) {
save_to_db();
}
if (values_.size() > 0) {
auto it = values_.lower_bound(last_check_key_);
if (it != values_.end() && it->first == last_check_key_) {
it++;
}
if (it == values_.end()) {
it = values_.begin();
}
td::uint32 cnt = 0;
auto s = last_check_key_;
while (values_.size() > 0 && cnt < 1 && it->first != s) {
last_check_key_ = it->first;
cnt++;
if (it->second.expired()) {
it = values_.erase(it);
// do not republish soon-to-be-expired values
} else if (it->second.ttl() > td::Clocks::system() + 60) {
auto dist = distance(it->first, k_ + 10);
if (dist == 0) {
if (it->second.key().update_rule()->need_republish()) {
auto P = td::PromiseCreator::lambda([print_id = print_id()](td::Result<td::Unit> R) {
if (R.is_error()) {
VLOG(DHT_INFO) << print_id << ": failed to store: " << R.move_as_error();
}
});
send_store(it->second.clone(), std::move(P));
}
it++;
} else if (dist >= k_ + 10) {
it = values_.erase(it);
} else {
it++;
}
} else {
it++;
}
if (values_.size() == 0) {
break;
}
if (it == values_.end()) {
it = values_.begin();
}
}
}
if (republish_att_.is_in_past()) {
auto it = our_values_.lower_bound(last_republish_key_);
if (it != our_values_.end() && it->first == last_republish_key_) {
it++;
}
if (it == our_values_.end()) {
it = our_values_.begin();
}
if (it != our_values_.end()) {
if (it->second.ttl() > td::Clocks::system() + 60) {
auto P = td::PromiseCreator::lambda([print_id = print_id()](td::Result<td::Unit> R) {
if (R.is_error()) {
VLOG(DHT_INFO) << print_id << ": failed to store: " << R.move_as_error();
}
});
send_store(it->second.clone(), std::move(P));
}
last_republish_key_ = it->first;
}
republish_att_ = td::Timestamp::in(10.0 + td::Random::fast(0, 1000) * 0.001);
}
if (fill_att_.is_in_past()) {
auto promise = td::PromiseCreator::lambda([](td::Result<DhtNodesList> R) {
if (R.is_error()) {
VLOG(DHT_WARNING) << "failed find self query: " << R.move_as_error();
}
});
td::Bits256 x;
td::uint32 t = td::Random::fast(0, 6);
td::uint32 b = 64 - td::Random::fast(0, 1 << t);
td::Random::secure_bytes(x.as_slice());
for (td::uint32 i = 0; i < b; i++) {
x.bits()[i] = key_.get_bit(i);
}
DhtKeyId key{x};
auto P = td::PromiseCreator::lambda([key, promise = std::move(promise), SelfId = actor_id(this),
print_id = print_id(), adnl = adnl_, list = get_nearest_nodes(key, k_), k = k_,
a = a_, id = id_](td::Result<DhtNode> R) mutable {
R.ensure();
td::actor::create_actor<DhtQueryFindNodes>("FindNodesQuery", key, print_id, id, std::move(list), k, a,
R.move_as_ok(), SelfId, adnl, std::move(promise))
.release();
});
get_self_node(std::move(P));
fill_att_ = td::Timestamp::in(10.0 + td::Random::fast(0, 100) * 0.1);
}
}
void DhtMemberImpl::dump(td::StringBuilder &sb) const {
for (auto &B : buckets_) {
B.dump(sb);
}
}
void DhtMemberImpl::send_store(DhtValue value, td::Promise<td::Unit> promise) {
value.check().ensure();
auto key_id = value.key_id();
auto P = td::PromiseCreator::lambda([value = std::move(value), print_id = print_id(), id = id_,
list = get_nearest_nodes(key_id, k_), k = k_, a = a_, SelfId = actor_id(this),
adnl = adnl_, promise = std::move(promise)](td::Result<DhtNode> R) mutable {
R.ensure();
td::actor::create_actor<DhtQueryStore>("StoreQuery", std::move(value), print_id, id, std::move(list), k, a,
R.move_as_ok(), SelfId, adnl, std::move(promise))
.release();
});
get_self_node(std::move(P));
}
void DhtMemberImpl::get_self_node(td::Promise<DhtNode> promise) {
auto P = td::PromiseCreator::lambda([promise = std::move(promise), print_id = print_id(), id = id_,
keyring = keyring_](td::Result<adnl::AdnlNode> R) mutable {
R.ensure();
auto node = R.move_as_ok();
auto version = static_cast<td::int32>(td::Clocks::system());
auto B = create_serialize_tl_object<ton_api::dht_node>(node.pub_id().tl(), node.addr_list().tl(), version,
td::BufferSlice());
CHECK(node.addr_list().size() > 0);
auto P = td::PromiseCreator::lambda(
[promise = std::move(promise), node = std::move(node), version](td::Result<td::BufferSlice> R) mutable {
R.ensure();
DhtNode n{node.pub_id(), node.addr_list(), version, R.move_as_ok()};
promise.set_result(std::move(n));
});
td::actor::send_closure(keyring, &keyring::Keyring::sign_message, id.pubkey_hash(), std::move(B), std::move(P));
});
td::actor::send_closure(adnl_, &adnl::Adnl::get_self_node, id_, std::move(P));
}
td::Result<std::shared_ptr<DhtGlobalConfig>> Dht::create_global_config(tl_object_ptr<ton_api::dht_config_global> conf) {
td::uint32 k;
if (conf->k_ == 0) {
k = DhtMember::default_k();
} else if (conf->k_ > 0 && static_cast<td::uint32>(conf->k_) <= DhtMember::max_k()) {
k = conf->k_;
} else {
return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad value k=" << conf->k_);
}
td::uint32 a;
if (conf->a_ == 0) {
a = DhtMember::default_a();
} else if (conf->a_ > 0 && static_cast<td::uint32>(conf->a_) <= DhtMember::max_a()) {
a = conf->a_;
} else {
return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad value a=" << conf->a_);
}
DhtNodesList l{std::move(conf->static_nodes_)};
return std::make_shared<DhtGlobalConfig>(k, a, std::move(l));
}
} // namespace dht
} // namespace ton

60
dht/dht.h Normal file
View file

@ -0,0 +1,60 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
#include "td/actor/PromiseFuture.h"
#include "td/actor/actor.h"
#include "adnl/adnl.h"
#include "auto/tl/ton_api.hpp"
#include "dht-types.h"
namespace ton {
namespace dht {
class DhtGlobalConfig;
class Dht : public td::actor::Actor {
public:
static td::Result<td::actor::ActorOwn<Dht>> create(adnl::AdnlNodeIdShort id, std::string db_root,
std::shared_ptr<DhtGlobalConfig> conf,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl);
static td::Result<std::shared_ptr<DhtGlobalConfig>> create_global_config(
tl_object_ptr<ton_api::dht_config_global> conf);
virtual adnl::AdnlNodeIdShort get_id() const = 0;
virtual void set_value(DhtValue key_value, td::Promise<td::Unit> result) = 0;
virtual void get_value(DhtKey key, td::Promise<DhtValue> result) = 0;
virtual void dump(td::StringBuilder &sb) const = 0;
virtual ~Dht() = default;
};
} // namespace dht
} // namespace ton

122
dht/dht.hpp Normal file
View file

@ -0,0 +1,122 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <set>
#include "td/utils/int_types.h"
#include "td/actor/actor.h"
#include "td/utils/List.h"
#include "adnl/adnl.h"
#include "adnl/utils.hpp"
#include "keys/encryptor.h"
#include "dht.h"
#include "dht-node.hpp"
#include "auto/tl/ton_api.hpp"
namespace ton {
namespace dht {
constexpr int VERBOSITY_NAME(DHT_WARNING) = verbosity_INFO;
constexpr int VERBOSITY_NAME(DHT_NOTICE) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(DHT_INFO) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(DHT_DEBUG) = verbosity_DEBUG + 1;
constexpr int VERBOSITY_NAME(DHT_EXTRA_DEBUG) = verbosity_DEBUG + 10;
class DhtGlobalConfig {
public:
auto get_k() const {
return k_;
}
auto get_a() const {
return a_;
}
const auto &nodes() const {
return static_nodes_;
}
DhtGlobalConfig(td::uint32 k, td::uint32 a, DhtNodesList nodes) : k_(k), a_(a), static_nodes_(std::move(nodes)) {
}
private:
td::uint32 k_;
td::uint32 a_;
DhtNodesList static_nodes_;
};
class DhtMember : public Dht {
public:
static constexpr td::uint32 default_k() {
return 10;
}
static constexpr td::uint32 default_a() {
return 3;
}
static constexpr td::uint32 max_k() {
return 100;
}
static constexpr td::uint32 max_a() {
return 10;
}
struct PrintId {
adnl::AdnlNodeIdShort id;
};
static td::actor::ActorOwn<DhtMember> create(adnl::AdnlNodeIdShort id, std::string db_root,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
td::uint32 k = 10, td::uint32 a = 3);
//virtual void update_addr_list(tl_object_ptr<ton_api::adnl_addressList> addr_list) = 0;
//virtual void add_node(adnl::AdnlNodeIdShort id) = 0;
virtual void add_full_node(DhtKeyId id, DhtNode node) = 0;
virtual void receive_ping(DhtKeyId id, DhtNode result) = 0;
virtual void get_value_in(DhtKeyId key, td::Promise<DhtValue> result) = 0;
virtual td::Status store_in(DhtValue value) = 0;
virtual void get_self_node(td::Promise<DhtNode> promise) = 0;
virtual PrintId print_id() const = 0;
};
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtMember::PrintId &id) {
sb << "[dhtnode " << id.id << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtMember &dht) {
sb << dht.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtMember *dht) {
sb << dht->print_id();
return sb;
}
} // namespace dht
} // namespace ton

303
dht/test/dht-test-ping.cpp Normal file
View file

@ -0,0 +1,303 @@
/*
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 "adnl/adnl-network-manager.h"
#include "adnl/adnl-peer-table.h"
#include "adnl/utils.hpp"
#include "keys/encryptor.h"
#include "td/utils/Time.h"
#include "td/utils/format.h"
#include "td/utils/OptionsParser.h"
#include "td/utils/filesystem.h"
#include "dht/dht.h"
#include "auto/tl/ton_api_json.h"
#include <iostream>
#include <sstream>
template <std::size_t size>
std::ostream &operator<<(std::ostream &stream, const td::UInt<size> &x) {
for (size_t i = 0; i < size / 8; i++) {
stream << td::format::hex_digit((x.raw[i] >> 4) & 15) << td::format::hex_digit(x.raw[i] & 15);
}
return stream;
}
class adnl::AdnlNode : public td::actor::Actor {
private:
std::vector<td::UInt256> ping_ids_;
td::actor::ActorOwn<ton::adnl::AdnlNetworkManager> network_manager_;
td::actor::ActorOwn<ton::adnl::AdnlPeerTable> peer_table_;
td::actor::ActorOwn<ton::DhtNode> dht_node_;
td::UInt256 local_id_;
bool local_id_set_ = false;
std::string host_ = "127.0.0.1";
td::uint32 ip_ = 0x7f000001;
td::uint16 port_ = 2380;
std::string local_config_ = "ton-local.config";
std::string global_config_ = "ton-global.config";
void receive_message(td::UInt256 src, td::UInt256 dst, td::BufferSlice data) {
std::cout << "MESSAGE FROM " << src << " to " << dst << " of size " << std::to_string(data.size()) << "\n";
}
void receive_query(td::UInt256 src, td::UInt256 dst, td::uint64 query_id, td::BufferSlice data) {
std::cout << "QUERY " << std::to_string(query_id) << " FROM " << src << " to " << dst << " of size "
<< std::to_string(data.size()) << "\n";
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::answer_query, dst, src, query_id,
ton::create_tl_object<ton::ton_api::testObject>());
}
std::unique_ptr<ton::adnl::AdnlPeerTable::Callback> make_callback() {
class Callback : public ton::adnl::AdnlPeerTable::Callback {
public:
void receive_message(td::UInt256 src, td::UInt256 dst, td::BufferSlice data) override {
td::actor::send_closure(id_, &adnl::AdnlNode::receive_message, src, dst, std::move(data));
}
void receive_query(td::UInt256 src, td::UInt256 dst, td::uint64 query_id, td::BufferSlice data) override {
td::actor::send_closure(id_, &adnl::AdnlNode::receive_query, src, dst, query_id, std::move(data));
}
Callback(td::actor::ActorId<adnl::AdnlNode> id) : id_(std::move(id)) {
}
private:
td::actor::ActorId<adnl::AdnlNode> id_;
};
return std::make_unique<Callback>(td::actor::actor_id(this));
}
public:
void set_local_config(std::string str) {
local_config_ = str;
}
void set_global_config(std::string str) {
global_config_ = str;
}
void start_up() override {
alarm_timestamp() = td::Timestamp::in(1);
}
adnl::AdnlNode() {
network_manager_ = ton::adnl::AdnlNetworkManager::create();
peer_table_ = ton::adnl::AdnlPeerTable::create();
td::actor::send_closure(network_manager_, &ton::adnl::AdnlNetworkManager::register_peer_table, peer_table_.get());
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::register_network_manager, network_manager_.get());
}
void listen_udp(td::uint16 port) {
td::actor::send_closure(network_manager_, &ton::adnl::AdnlNetworkManager::add_listening_udp_port, "0.0.0.0", port);
port_ = port;
}
void run() {
auto L = td::read_file(local_config_);
if (L.is_error()) {
LOG(FATAL) << "can not read local config: " << L.move_as_error();
}
auto L2 = td::json_decode(L.move_as_ok().as_slice());
if (L2.is_error()) {
LOG(FATAL) << "can not parse local config: " << L2.move_as_error();
}
auto lc_j = L2.move_as_ok();
if (lc_j.type() != td::JsonValue::Type::Object) {
LOG(FATAL) << "can not parse local config: expected json object";
}
ton::ton_api::config_local lc;
auto rl = ton::ton_api::from_json(lc, lc_j.get_object());
if (rl.is_error()) {
LOG(FATAL) << "can not interpret local config: " << rl.move_as_error();
}
auto G = td::read_file(global_config_);
if (G.is_error()) {
LOG(FATAL) << "can not read global config: " << G.move_as_error();
}
auto G2 = td::json_decode(G.move_as_ok().as_slice());
if (G2.is_error()) {
LOG(FATAL) << "can not parse global config: " << G2.move_as_error();
}
auto gc_j = G2.move_as_ok();
if (gc_j.type() != td::JsonValue::Type::Object) {
LOG(FATAL) << "can not parse global config: expected json object";
}
ton::ton_api::config_global gc;
auto rg = ton::ton_api::from_json(gc, gc_j.get_object());
if (rg.is_error()) {
LOG(FATAL) << "can not interpret local config: " << rg.move_as_error();
}
if (gc.adnl_) {
auto it = gc.adnl_->static_nodes_.begin();
while (it != gc.adnl_->static_nodes_.end()) {
auto R = ton::adnl_validate_full_id(std::move((*it)->id_));
if (R.is_error()) {
LOG(FATAL) << "can not apply global config: " << R.move_as_error();
}
auto R2 = ton::adnl_validate_addr_list(std::move((*it)->addr_list_));
if (R2.is_error()) {
LOG(FATAL) << "can not apply global config: " << R2.move_as_error();
}
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::add_peer, R.move_as_ok(), R2.move_as_ok());
it++;
}
}
if (!gc.dht_) {
LOG(FATAL) << "global config does not contain dht section";
}
if (lc.dht_.size() != 1) {
LOG(FATAL) << "local config must contain exactly one dht section";
}
auto R = ton::DhtNode::create_from_json(std::move(gc.dht_), std::move(lc.dht_[0]), peer_table_.get());
if (R.is_error()) {
LOG(FATAL) << "fail creating dht node: " << R.move_as_error();
}
dht_node_ = R.move_as_ok();
}
/*
void set_host(td::IPAddress ip, std::string host) {
ip_ = ip.get_ipv4();
host_ = host;
}
void send_pings_to(td::UInt256 id) {
std::cout << "send pings to " << id << "\n";
ping_ids_.push_back(id);
}
void add_local_id(ton::tl_object_ptr<ton::ton_api::adnl_id_Pk> pk_) {
auto pub_ = ton::get_public_key(pk_);
local_id_ = ton::adnl_short_id(pub_);
std::cout << "local_id = '" << local_id_ << "'\n";
auto x = ton::create_tl_object<ton::ton_api::adnl_address_udp>(ip_, port_);
auto v = std::vector<ton::tl_object_ptr<ton::ton_api::adnl_Address>>();
v.push_back(ton::move_tl_object_as<ton::ton_api::adnl_Address>(x));
auto y =
ton::create_tl_object<ton::ton_api::adnl_addressList>(std::move(v), static_cast<td::int32>(td::Time::now()));
LOG(INFO) << "local_addr_list: " << ton::ton_api::to_string(y);
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::add_id, ton::clone_tl_object(pk_),
ton::clone_tl_object(y));
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::subscribe_custom, local_id_, "TEST", make_callback());
local_id_set_ = true;
dht_node_ = ton::DhtNode::create(std::move(pk_), peer_table_.get());
td::actor::send_closure(dht_node_, &ton::DhtNode::update_addr_list, std::move(y));
}
void add_static_dht_node(ton::tl_object_ptr<ton::ton_api::adnl_id_Full> id,
ton::tl_object_ptr<ton::ton_api::adnl_addressList> addr_list,
td::BufferSlice signature) {
auto Id = ton::adnl_short_id(id);
td::actor::send_closure(
dht_node_, &ton::DhtNode::add_full_node, Id,
ton::create_tl_object<ton::ton_api::dht_node>(std::move(id), std::move(addr_list), signature.as_slice().str()));
}
void add_foreign(ton::tl_object_ptr<ton::ton_api::adnl_id_Full> id,
ton::tl_object_ptr<ton::ton_api::adnl_addressList> addr_list) {
std::cout << ton::adnl_short_id(id) << "\n";
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::add_peer, std::move(id), std::move(addr_list));
}
void alarm() override {
std::cout << "alarm\n";
if (local_id_set_) {
for (auto it = ping_ids_.begin(); it != ping_ids_.end(); it++) {
auto P = td::PromiseCreator::lambda([](td::Result<ton::tl_object_ptr<ton::ton_api::adnl_Message>> result) {
if (result.is_error()) {
std::cout << "received error " << result.move_as_error().to_string() << "\n";
} else {
auto message = result.move_as_ok();
std::cout << "received answer to query\n";
}
});
td::actor::send_closure(peer_table_, &ton::adnl::AdnlPeerTable::send_query, local_id_, *it, std::move(P),
td::Timestamp::in(5),
ton::move_tl_object_as<ton::ton_api::adnl_Message>(
ton::create_tl_object<ton::ton_api::adnl_message_custom>("TEST")));
}
}
}
*/
};
td::Result<td::UInt256> get_uint256(std::string str) {
if (str.size() != 64) {
return td::Status::Error("uint256 must have 64 bytes");
}
td::UInt256 res;
for (size_t i = 0; i < 32; i++) {
res.raw[i] = static_cast<td::uint8>(td::hex_to_int(str[2 * i]) * 16 + td::hex_to_int(str[2 * i + 1]));
}
return res;
}
int main(int argc, char *argv[]) {
td::actor::ActorOwn<adnl::AdnlNode> x;
td::OptionsParser p;
p.set_description("test basic adnl functionality");
p.add_option('h', "help", "prints_help", [&]() {
char b[10240];
td::StringBuilder sb({b, 10000});
sb << p;
std::cout << sb.as_cslice().c_str();
std::exit(2);
return td::Status::OK();
});
p.add_option('p', "port", "sets udp port", [&](td::Slice port) {
td::actor::send_closure(x, &adnl::AdnlNode::listen_udp, static_cast<td::uint16>(std::stoi(port.str())));
return td::Status::OK();
});
p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) {
td::actor::send_closure(x, &adnl::AdnlNode::set_global_config, fname.str());
return td::Status::OK();
});
p.add_option('c', "local-config", "file to read local config", [&](td::Slice fname) {
td::actor::send_closure(x, &adnl::AdnlNode::set_local_config, fname.str());
return td::Status::OK();
});
td::actor::Scheduler scheduler({2});
scheduler.run_in_context([&] {
x = td::actor::create_actor<adnl::AdnlNode>(td::actor::ActorInfoCreator::Options().with_name("A").with_poll());
});
scheduler.run_in_context([&] { p.run(argc, argv).ensure(); });
scheduler.run_in_context([&] { td::actor::send_closure(x, &adnl::AdnlNode::run); });
scheduler.run();
return 0;
}