/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2019 Telegram Systems LLP */ #include "overlay.hpp" namespace ton { namespace overlay { void OverlayImpl::del_peer(adnl::AdnlNodeIdShort id) { auto P = peers_.get(id); CHECK(P != nullptr); VLOG(OVERLAY_DEBUG) << this << ": deleting peer " << id; if (P->is_neighbour()) { VLOG(OVERLAY_INFO) << this << ": deleting neighbour " << id; bool deleted = false; for (auto &n : neighbours_) { if (n == id) { n = neighbours_[neighbours_.size() - 1]; neighbours_.resize(neighbours_.size() - 1); deleted = true; break; } } CHECK(deleted); P->set_neighbour(false); } peers_.remove(id); update_neighbours(0); } void OverlayImpl::del_some_peers() { if (!public_) { return; } while (peers_.size() > max_peers()) { auto P = get_random_peer(); if (P) { auto id = P->get_id(); del_peer(id); } } } void OverlayImpl::do_add_peer(OverlayNode node) { auto id = node.adnl_id_short(); auto V = peers_.get(id); if (V) { VLOG(OVERLAY_DEBUG) << this << ": updating peer " << id << " up to version " << node.version(); V->update(std::move(node)); } else { VLOG(OVERLAY_DEBUG) << this << ": adding peer " << id << " of version " << node.version(); peers_.insert(id, OverlayPeer(std::move(node))); del_some_peers(); update_neighbours(0); } } void OverlayImpl::add_peer_in_cont(OverlayNode node) { CHECK(public_); do_add_peer(std::move(node)); } void OverlayImpl::add_peer_in(OverlayNode node) { CHECK(public_); if (node.overlay_id() != overlay_id_) { VLOG(OVERLAY_WARNING) << this << ": received node with bad overlay"; return; } auto t = td::Clocks::system(); if (node.version() + 600 < t || node.version() > t + 60) { VLOG(OVERLAY_INFO) << this << ": ignoring node of too old version " << node.version(); return; } auto pub_id = node.adnl_id_full(); if (pub_id.compute_short_id() == local_id_) { VLOG(OVERLAY_DEBUG) << this << ": ignoring self node"; return; } auto S = node.check_signature(); if (S.is_error()) { VLOG(OVERLAY_WARNING) << this << ": bad signature: " << S; return; } add_peer_in_cont(std::move(node)); } void OverlayImpl::add_peers(std::vector peers) { for (auto &node : peers) { add_peer_in(std::move(node)); } } void OverlayImpl::add_peer(OverlayNode P) { add_peer_in(std::move(P)); } void OverlayImpl::receive_random_peers(adnl::AdnlNodeIdShort src, td::BufferSlice data) { CHECK(public_); auto R = fetch_tl_object(std::move(data), true); if (R.is_error()) { VLOG(OVERLAY_WARNING) << this << ": dropping incorrect answer to overlay.getRandomPeers query from " << src << ": " << R.move_as_error(); return; } auto res = R.move_as_ok(); std::vector nodes; for (auto &n : res->nodes_) { auto N = OverlayNode::create(n); if (N.is_ok()) { nodes.emplace_back(N.move_as_ok()); } } add_peers(std::move(nodes)); } void OverlayImpl::send_random_peers_cont(adnl::AdnlNodeIdShort src, OverlayNode node, td::Promise promise) { std::vector> vec; vec.emplace_back(node.tl()); for (td::uint32 i = 0; i < nodes_to_send(); i++) { auto P = get_random_peer(); if (P) { vec.emplace_back(P->get().tl()); } else { break; } } if (promise) { auto Q = create_tl_object(std::move(vec)); promise.set_value(serialize_tl_object(Q, true)); } else { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), src, oid = print_id()](td::Result res) { if (res.is_error()) { VLOG(OVERLAY_NOTICE) << oid << ": failed getRandomPeers query: " << res.move_as_error(); return; } td::actor::send_closure(SelfId, &OverlayImpl::receive_random_peers, src, res.move_as_ok()); }); auto Q = create_tl_object(create_tl_object(std::move(vec))); td::actor::send_closure(manager_, &OverlayManager::send_query, src, local_id_, overlay_id_, "overlay getRandomPeers", std::move(P), td::Timestamp::in(5.0 + td::Random::fast(0, 50) * 0.1), serialize_tl_object(Q, true)); } } void OverlayImpl::send_random_peers(adnl::AdnlNodeIdShort src, td::Promise promise) { auto P = td::PromiseCreator::lambda([src, promise = std::move(promise), SelfId = actor_id(this)](td::Result res) mutable { if (res.is_error()) { promise.set_error(td::Status::Error(ErrorCode::error, "cannot get self node")); return; } td::actor::send_closure(SelfId, &OverlayImpl::send_random_peers_cont, src, res.move_as_ok(), std::move(promise)); }); get_self_node(std::move(P)); } void OverlayImpl::update_neighbours(td::uint32 nodes_to_change) { if (peers_.size() == 0) { return; } td::uint32 iter = 0; while (iter < 10 && (nodes_to_change > 0 || neighbours_.size() < max_neighbours())) { auto X = peers_.get_random(); if (!X) { break; } if (X->get_id() == local_id_) { iter++; continue; } if (X->get_version() <= td::Clocks::system() - 600) { if (X->is_neighbour()) { bool found = false; for (auto &n : neighbours_) { if (n == X->get_id()) { n = *neighbours_.rbegin(); found = true; break; } } CHECK(found); neighbours_.pop_back(); X->set_neighbour(false); } peers_.remove(X->get_id()); continue; } if (X->is_neighbour()) { iter++; continue; } if (neighbours_.size() < max_neighbours()) { VLOG(OVERLAY_INFO) << this << ": adding new neighbour " << X->get_id(); neighbours_.push_back(X->get_id()); X->set_neighbour(true); } else { CHECK(nodes_to_change > 0); auto i = td::Random::fast(0, static_cast(neighbours_.size()) - 1); auto Y = peers_.get(neighbours_[i]); CHECK(Y != nullptr); CHECK(Y->is_neighbour()); Y->set_neighbour(false); neighbours_[i] = X->get_id(); X->set_neighbour(true); nodes_to_change--; VLOG(OVERLAY_INFO) << this << ": changing neighbour " << Y->get_id() << " -> " << X->get_id(); } } } OverlayPeer *OverlayImpl::get_random_peer() { while (peers_.size() > 0) { auto P = peers_.get_random(); if (public_ && P->get_version() + 3600 < td::Clocks::system()) { VLOG(OVERLAY_INFO) << this << ": deleting outdated peer " << P->get_id(); del_peer(P->get_id()); } else { return P; } } return nullptr; } void OverlayImpl::get_overlay_random_peers(td::uint32 max_peers, td::Promise> promise) { std::vector v; auto t = td::Clocks::system(); while (peers_.size() > v.size()) { auto P = peers_.get_random(); if (P->get_version() + 3600 < t) { VLOG(OVERLAY_INFO) << this << ": deleting outdated peer " << P->get_id(); del_peer(P->get_id()); } else { bool dup = false; for (auto &n : v) { if (n == P->get_id()) { dup = true; continue; } } if (!dup) { v.push_back(P->get_id()); } } } promise.set_result(std::move(v)); } void OverlayImpl::receive_nodes_from_db(tl_object_ptr tl_nodes) { if (public_) { std::vector nodes; for (auto &n : tl_nodes->nodes_) { auto N = OverlayNode::create(n); if (N.is_ok()) { nodes.emplace_back(N.move_as_ok()); } } add_peers(std::move(nodes)); } } } // namespace overlay } // namespace ton