|
|
@ -36,6 +36,103 @@ namespace { |
|
|
|
|
|
|
|
namespace libp2p::protocol::kademlia { |
|
|
|
|
|
|
|
size_t Bucket::size() const { |
|
|
|
return peers_.size(); |
|
|
|
} |
|
|
|
|
|
|
|
void Bucket::append(const Bucket &bucket) { |
|
|
|
peers_.insert(peers_.end(), bucket.peers_.begin(), bucket.peers_.end()); |
|
|
|
} |
|
|
|
|
|
|
|
void Bucket::sort(const NodeId &node_id) { |
|
|
|
XorDistanceComparator cmp(node_id); |
|
|
|
peers_.sort(cmp); |
|
|
|
} |
|
|
|
|
|
|
|
auto Bucket::find(const peer::PeerId &p) const { |
|
|
|
return std::find_if(peers_.begin(), peers_.end(), |
|
|
|
[&p](const auto &i) { return i.peer_id == p; }); |
|
|
|
} |
|
|
|
|
|
|
|
bool Bucket::moveToFront(const PeerId &pid) { |
|
|
|
auto it = find(pid); |
|
|
|
if (it != peers_.end()) { |
|
|
|
if (it != peers_.begin()) { |
|
|
|
peers_.splice(peers_.begin(), peers_, it); |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void Bucket::emplaceToFront(const PeerId &pid, bool is_replaceable) { |
|
|
|
peers_.emplace(peers_.begin(), pid, is_replaceable); |
|
|
|
} |
|
|
|
|
|
|
|
boost::optional<PeerId> Bucket::removeReplaceableItem() { |
|
|
|
boost::optional<PeerId> result; |
|
|
|
|
|
|
|
for (auto it = peers_.rbegin(); it != peers_.rend(); ++it) { |
|
|
|
if (it->is_replaceable) { |
|
|
|
result = std::move(it->peer_id); |
|
|
|
peers_.erase((++it).base()); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
void Bucket::truncate(size_t limit) { |
|
|
|
if (limit == 0) { |
|
|
|
peers_.clear(); |
|
|
|
} else if (peers_.size() > limit) { |
|
|
|
peers_.erase(std::next(peers_.begin(), static_cast<long>(limit)), |
|
|
|
peers_.end()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<peer::PeerId> Bucket::peerIds() const { |
|
|
|
std::vector<peer::PeerId> peerIds; |
|
|
|
peerIds.reserve(peers_.size()); |
|
|
|
std::transform(peers_.begin(), peers_.end(), std::back_inserter(peerIds), |
|
|
|
[](const auto &bpi) { return bpi.peer_id; }); |
|
|
|
return peerIds; |
|
|
|
} |
|
|
|
|
|
|
|
bool Bucket::contains(const peer::PeerId &p) const { |
|
|
|
return find(p) != peers_.end(); |
|
|
|
} |
|
|
|
|
|
|
|
bool Bucket::remove(const peer::PeerId &p) { |
|
|
|
auto it = find(p); |
|
|
|
if (it != peers_.end()) { |
|
|
|
peers_.erase(it); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
Bucket Bucket::split(size_t commonLenPrefix, const NodeId &target) { |
|
|
|
Bucket b{}; |
|
|
|
|
|
|
|
std::list<BucketPeerInfo> new_peers; |
|
|
|
|
|
|
|
while (!peers_.empty()) { |
|
|
|
auto it = peers_.begin(); |
|
|
|
if (it->node_id.commonPrefixLen(target) > commonLenPrefix) { |
|
|
|
b.peers_.splice(b.peers_.end(), peers_, it); |
|
|
|
} else { |
|
|
|
new_peers.splice(new_peers.end(), peers_, it); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
peers_.swap(new_peers); |
|
|
|
|
|
|
|
return b; |
|
|
|
} |
|
|
|
|
|
|
|
PeerRoutingTableImpl::PeerRoutingTableImpl( |
|
|
|
const Config &config, |
|
|
|
std::shared_ptr<peer::IdentityManager> identity_manager, |
|
|
@ -74,23 +171,36 @@ namespace libp2p::protocol::kademlia { |
|
|
|
// if this happens, search both surrounding buckets for nearby peers
|
|
|
|
if (bucketId > 0) { |
|
|
|
auto &left = buckets_.at(bucketId - 1); |
|
|
|
bucket.insert(bucket.end(), left.begin(), left.end()); |
|
|
|
bucket.append(left); |
|
|
|
} |
|
|
|
if (bucketId < buckets_.size() - 1) { |
|
|
|
auto &right = buckets_.at(bucketId + 1); |
|
|
|
bucket.insert(bucket.end(), right.begin(), right.end()); |
|
|
|
bucket.append(right); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// sort bucket in ascending order by XOR distance from local peer.
|
|
|
|
XorDistanceComparator cmp(node_id); |
|
|
|
std::sort(bucket.begin(), bucket.end(), cmp); |
|
|
|
bucket.sort(node_id); |
|
|
|
|
|
|
|
bucket.truncate(count); |
|
|
|
|
|
|
|
return bucket.peerIds(); |
|
|
|
} |
|
|
|
|
|
|
|
namespace { |
|
|
|
outcome::result<bool> replacePeer(Bucket &bucket, const peer::PeerId &pid, |
|
|
|
bool is_replaceable, event::Bus &bus) { |
|
|
|
const auto removed = bucket.removeReplaceableItem(); |
|
|
|
if (!removed.has_value()) { |
|
|
|
return PeerRoutingTableImpl::Error::PEER_REJECTED_NO_CAPACITY; |
|
|
|
} |
|
|
|
bus.getChannel<events::PeerRemovedChannel>().publish(removed.value()); |
|
|
|
bucket.emplaceToFront(pid, is_replaceable); |
|
|
|
bus.getChannel<events::PeerAddedChannel>().publish(pid); |
|
|
|
return true; |
|
|
|
} |
|
|
|
} // namespace
|
|
|
|
|
|
|
|
outcome::result<bool> PeerRoutingTableImpl::update(const peer::PeerId &pid, |
|
|
|
bool is_permanent, |
|
|
|
bool is_connected) { |
|
|
@ -102,12 +212,7 @@ namespace libp2p::protocol::kademlia { |
|
|
|
|
|
|
|
// Trying to find and move to front if its a long lived connected peer
|
|
|
|
if (is_connected) { |
|
|
|
auto it = |
|
|
|
std::find_if(bucket.begin(), bucket.end(), |
|
|
|
[&pid](const auto &bpi) { return bpi.peer_id == pid; }); |
|
|
|
if (it != bucket.end()) { |
|
|
|
bucket.push_front(*it); |
|
|
|
bucket.erase(it); |
|
|
|
if (!bucket.moveToFront(pid)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} else if (bucket.contains(pid)) { |
|
|
@ -115,7 +220,7 @@ namespace libp2p::protocol::kademlia { |
|
|
|
} |
|
|
|
|
|
|
|
if (bucket.size() < config_.maxBucketSize) { |
|
|
|
bucket.emplace_front(pid, !is_permanent); |
|
|
|
bucket.emplaceToFront(pid, !is_permanent); |
|
|
|
bus_->getChannel<events::PeerAddedChannel>().publish(pid); |
|
|
|
return true; |
|
|
|
} |
|
|
@ -131,39 +236,15 @@ namespace libp2p::protocol::kademlia { |
|
|
|
auto resizedBucketId = getBucketId(buckets_, cpl); |
|
|
|
auto &resizedBucket = buckets_.at(resizedBucketId); |
|
|
|
if (resizedBucket.size() < config_.maxBucketSize) { |
|
|
|
resizedBucket.emplace_front(pid, is_permanent); |
|
|
|
resizedBucket.emplaceToFront(pid, !is_permanent); |
|
|
|
bus_->getChannel<events::PeerAddedChannel>().publish(pid); |
|
|
|
return true; |
|
|
|
} |
|
|
|
auto replaceablePeerIt = |
|
|
|
std::find_if(resizedBucket.rbegin(), resizedBucket.rend(), |
|
|
|
[](const auto &bpi) { return bpi.is_replaceable; }); |
|
|
|
|
|
|
|
if (replaceablePeerIt == resizedBucket.rend()) { |
|
|
|
return Error::PEER_REJECTED_NO_CAPACITY; |
|
|
|
} |
|
|
|
auto removedPeer = (*replaceablePeerIt).peer_id; |
|
|
|
bus_->getChannel<events::PeerRemovedChannel>().publish(removedPeer); |
|
|
|
std::advance(replaceablePeerIt, 1); |
|
|
|
resizedBucket.erase(replaceablePeerIt.base()); |
|
|
|
resizedBucket.emplace_front(pid, !is_permanent); |
|
|
|
bus_->getChannel<events::PeerAddedChannel>().publish(pid); |
|
|
|
return true; |
|
|
|
return replacePeer(resizedBucket, pid, !is_permanent, *bus_); |
|
|
|
} |
|
|
|
auto replaceablePeerIt = |
|
|
|
std::find_if(bucket.rbegin(), bucket.rend(), |
|
|
|
[](const auto &bpi) { return bpi.is_replaceable; }); |
|
|
|
|
|
|
|
if (replaceablePeerIt == bucket.rend()) { |
|
|
|
return Error::PEER_REJECTED_NO_CAPACITY; |
|
|
|
} |
|
|
|
auto removedPeer = (*replaceablePeerIt).peer_id; |
|
|
|
bus_->getChannel<events::PeerRemovedChannel>().publish(removedPeer); |
|
|
|
std::advance(replaceablePeerIt, 1); |
|
|
|
bucket.erase(replaceablePeerIt.base()); |
|
|
|
bucket.emplace_front(pid, !is_permanent); |
|
|
|
bus_->getChannel<events::PeerAddedChannel>().publish(pid); |
|
|
|
return true; |
|
|
|
return replacePeer(bucket, pid, !is_permanent, *bus_); |
|
|
|
} |
|
|
|
|
|
|
|
void PeerRoutingTableImpl::nextBucket() { |
|
|
@ -193,7 +274,7 @@ namespace libp2p::protocol::kademlia { |
|
|
|
|
|
|
|
std::vector<peer::PeerId> PeerRoutingTableImpl::getAllPeers() const { |
|
|
|
std::vector<peer::PeerId> vec; |
|
|
|
for (auto &bucket : buckets_) { |
|
|
|
for (const auto &bucket : buckets_) { |
|
|
|
auto peer_ids = bucket.peerIds(); |
|
|
|
vec.insert(vec.end(), peer_ids.begin(), peer_ids.end()); |
|
|
|
} |
|
|
|