mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
32
dht/CMakeLists.txt
Normal file
32
dht/CMakeLists.txt
Normal 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
208
dht/dht-bucket.cpp
Normal 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
64
dht/dht-bucket.hpp
Normal 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
168
dht/dht-in.hpp
Normal 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
64
dht/dht-node.cpp
Normal 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
114
dht/dht-node.hpp
Normal 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
273
dht/dht-query.cpp
Normal 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
177
dht/dht-query.hpp
Normal 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
134
dht/dht-remote-node.cpp
Normal 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
86
dht/dht-remote-node.hpp
Normal 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
383
dht/dht-types.cpp
Normal 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
268
dht/dht-types.h
Normal 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
552
dht/dht.cpp
Normal 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
60
dht/dht.h
Normal 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
122
dht/dht.hpp
Normal 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
303
dht/test/dht-test-ping.cpp
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue