Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/peermanager.cpp
// Copyright (c) 2020 The Bitcoin developers | // Copyright (c) 2020 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <avalanche/peermanager.h> | #include <avalanche/peermanager.h> | ||||
#include <avalanche/delegation.h> | #include <avalanche/delegation.h> | ||||
#include <avalanche/validation.h> | #include <avalanche/validation.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <validation.h> // For ChainstateActive() | #include <validation.h> // For ChainstateActive() | ||||
#include <cassert> | #include <cassert> | ||||
namespace avalanche { | namespace avalanche { | ||||
PeerId PeerManager::getPeer(const Proof &proof) { | bool PeerManager::addNode(NodeId nodeid, const Proof &proof, | ||||
const Delegation &delegation) { | |||||
const PeerId peerid = getPeerId(proof); | |||||
if (peerid == NO_PEER) { | |||||
return false; | |||||
} | |||||
DelegationState state; | |||||
CPubKey pubkey; | |||||
if (!delegation.verify(state, proof, pubkey)) { | |||||
return false; | |||||
} | |||||
auto nit = nodes.find(nodeid); | |||||
if (nit == nodes.end()) { | |||||
return nodes.emplace(nodeid, peerid, std::move(pubkey)).second; | |||||
} | |||||
// We actually have this node already, we need to update it. | |||||
return nodes.modify(nit, [&](Node &n) { | |||||
n.peerid = peerid; | |||||
n.pubkey = std::move(pubkey); | |||||
}); | |||||
} | |||||
bool PeerManager::removeNode(NodeId nodeid) { | |||||
return nodes.erase(nodeid) > 0; | |||||
} | |||||
bool PeerManager::forNode(NodeId nodeid, | |||||
std::function<bool(const Node &n)> func) const { | |||||
auto it = nodes.find(nodeid); | |||||
return it != nodes.end() && func(*it); | |||||
} | |||||
bool PeerManager::updateNextRequestTime(NodeId nodeid, TimePoint timeout) { | |||||
auto it = nodes.find(nodeid); | |||||
if (it == nodes.end()) { | |||||
return false; | |||||
} | |||||
return nodes.modify(it, [&](Node &n) { n.nextRequestTime = timeout; }); | |||||
} | |||||
NodeId PeerManager::selectNode() { | |||||
for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { | |||||
const PeerId p = selectPeer(); | |||||
// If we cannot find a peer, it may be due to the fact that it is | |||||
// unlikely due to high fragmentation, so compact and retry. | |||||
if (p == NO_PEER) { | |||||
compact(); | |||||
continue; | |||||
} | |||||
// See if that peer has an available node. | |||||
auto &nview = nodes.get<next_request_time>(); | |||||
auto it = nview.lower_bound(boost::make_tuple(p, TimePoint())); | |||||
if (it != nview.end() && it->peerid == p && | |||||
it->nextRequestTime <= std::chrono::steady_clock::now()) { | |||||
return it->nodeid; | |||||
} | |||||
} | |||||
return NO_NODE; | |||||
} | |||||
void PeerManager::updatedBlockTip() { | |||||
std::vector<PeerId> invalidPeers; | |||||
{ | |||||
LOCK(cs_main); | |||||
const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | |||||
for (const auto &p : peers) { | |||||
ProofValidationState state; | |||||
if (!p.proof.verify(state, coins)) { | |||||
invalidPeers.push_back(p.peerid); | |||||
} | |||||
} | |||||
} | |||||
for (const auto &pid : invalidPeers) { | |||||
removePeer(pid); | |||||
} | |||||
} | |||||
PeerId PeerManager::getPeerId(const Proof &proof) { | |||||
auto it = fetchOrCreatePeer(proof); | |||||
return it == peers.end() ? NO_PEER : it->peerid; | |||||
} | |||||
PeerManager::PeerSet::iterator | |||||
PeerManager::fetchOrCreatePeer(const Proof &proof) { | |||||
{ | { | ||||
// Check if we already know of that peer. | // Check if we already know of that peer. | ||||
auto &pview = peers.get<proof_index>(); | auto &pview = peers.get<proof_index>(); | ||||
auto it = pview.find(proof.getId()); | auto it = pview.find(proof.getId()); | ||||
if (it != pview.end()) { | if (it != pview.end()) { | ||||
return it->peerid; | return peers.project<0>(it); | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
// Reject invalid proof. | // Reject invalid proof. | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | ||||
ProofValidationState state; | ProofValidationState state; | ||||
if (!proof.verify(state, coins)) { | if (!proof.verify(state, coins)) { | ||||
return NO_PEER; | return peers.end(); | ||||
} | } | ||||
} | } | ||||
// New peer means new peerid! | // New peer means new peerid! | ||||
const PeerId peerid = nextPeerId++; | const PeerId peerid = nextPeerId++; | ||||
// Attach UTXOs to this proof. | // Attach UTXOs to this proof. | ||||
std::unordered_set<PeerId> conflicting_peerids; | std::unordered_set<PeerId> conflicting_peerids; | ||||
Show All 12 Lines | if (conflicting_peerids.size() > 0) { | ||||
assert(it != utxos.end()); | assert(it != utxos.end()); | ||||
// We need to delete that one. | // We need to delete that one. | ||||
if (it->second == peerid) { | if (it->second == peerid) { | ||||
utxos.erase(it); | utxos.erase(it); | ||||
} | } | ||||
} | } | ||||
return NO_PEER; | return peers.end(); | ||||
} | } | ||||
// We have no peer for this proof, time to create it. | // We have no peer for this proof, time to create it. | ||||
auto inserted = peers.emplace(peerid, uint32_t(slots.size()), proof); | auto inserted = peers.emplace(peerid, uint32_t(slots.size()), proof); | ||||
assert(inserted.second); | assert(inserted.second); | ||||
const uint32_t score = proof.getScore(); | const uint32_t score = proof.getScore(); | ||||
const uint64_t start = slotCount; | const uint64_t start = slotCount; | ||||
slots.emplace_back(start, score, peerid); | slots.emplace_back(start, score, peerid); | ||||
slotCount = start + score; | slotCount = start + score; | ||||
return peerid; | |||||
return inserted.first; | |||||
} | } | ||||
bool PeerManager::removePeer(const PeerId peerid) { | bool PeerManager::removePeer(const PeerId peerid) { | ||||
auto it = peers.find(peerid); | auto it = peers.find(peerid); | ||||
if (it == peers.end()) { | if (it == peers.end()) { | ||||
return false; | return false; | ||||
} | } | ||||
Show All 21 Lines | for (const auto &s : it->proof.getStakes()) { | ||||
bool deleted = utxos.erase(s.getStake().getUTXO()) > 0; | bool deleted = utxos.erase(s.getStake().getUTXO()) > 0; | ||||
assert(deleted); | assert(deleted); | ||||
} | } | ||||
peers.erase(it); | peers.erase(it); | ||||
return true; | return true; | ||||
} | } | ||||
bool PeerManager::addNode(NodeId nodeid, const Proof &proof, | |||||
const Delegation &delegation) { | |||||
const PeerId peerid = getPeer(proof); | |||||
if (peerid == NO_PEER) { | |||||
return false; | |||||
} | |||||
DelegationState state; | |||||
CPubKey pubkey; | |||||
if (!delegation.verify(state, proof, pubkey)) { | |||||
return false; | |||||
} | |||||
auto nit = nodes.find(nodeid); | |||||
if (nit == nodes.end()) { | |||||
return nodes.emplace(nodeid, peerid, std::move(pubkey)).second; | |||||
} | |||||
// We actually have this node already, we need to update it. | |||||
return nodes.modify(nit, [&](Node &n) { | |||||
n.peerid = peerid; | |||||
n.pubkey = std::move(pubkey); | |||||
}); | |||||
} | |||||
bool PeerManager::removeNode(NodeId nodeid) { | |||||
return nodes.erase(nodeid) > 0; | |||||
} | |||||
bool PeerManager::forNode(NodeId nodeid, | |||||
std::function<bool(const Node &n)> func) const { | |||||
auto it = nodes.find(nodeid); | |||||
return it != nodes.end() && func(*it); | |||||
} | |||||
bool PeerManager::updateNextRequestTime(NodeId nodeid, TimePoint timeout) { | |||||
auto it = nodes.find(nodeid); | |||||
if (it == nodes.end()) { | |||||
return false; | |||||
} | |||||
return nodes.modify(it, [&](Node &n) { n.nextRequestTime = timeout; }); | |||||
} | |||||
NodeId PeerManager::selectNode() { | |||||
for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { | |||||
const PeerId p = selectPeer(); | |||||
// If we cannot find a peer, it may be due to the fact that it is | |||||
// unlikely due to high fragmentation, so compact and retry. | |||||
if (p == NO_PEER) { | |||||
compact(); | |||||
continue; | |||||
} | |||||
// See if that peer has an available node. | |||||
auto &nview = nodes.get<next_request_time>(); | |||||
auto it = nview.lower_bound(boost::make_tuple(p, TimePoint())); | |||||
if (it != nview.end() && it->peerid == p && | |||||
it->nextRequestTime <= std::chrono::steady_clock::now()) { | |||||
return it->nodeid; | |||||
} | |||||
} | |||||
return NO_NODE; | |||||
} | |||||
PeerId PeerManager::selectPeer() const { | PeerId PeerManager::selectPeer() const { | ||||
if (slots.empty() || slotCount == 0) { | if (slots.empty() || slotCount == 0) { | ||||
return NO_PEER; | return NO_PEER; | ||||
} | } | ||||
const uint64_t max = slotCount; | const uint64_t max = slotCount; | ||||
for (int retry = 0; retry < SELECT_PEER_MAX_RETRY; retry++) { | for (int retry = 0; retry < SELECT_PEER_MAX_RETRY; retry++) { | ||||
size_t i = selectPeerImpl(slots, GetRand(max), max); | size_t i = selectPeerImpl(slots, GetRand(max), max); | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | for (size_t i = begin; i < end; i++) { | ||||
return slots[i].getPeerId(); | return slots[i].getPeerId(); | ||||
} | } | ||||
} | } | ||||
// We failed to find a slot, retry. | // We failed to find a slot, retry. | ||||
return NO_PEER; | return NO_PEER; | ||||
} | } | ||||
void PeerManager::updatedBlockTip() { | |||||
std::vector<PeerId> invalidPeers; | |||||
{ | |||||
LOCK(cs_main); | |||||
const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | |||||
for (const auto &p : peers) { | |||||
ProofValidationState state; | |||||
if (!p.proof.verify(state, coins)) { | |||||
invalidPeers.push_back(p.peerid); | |||||
} | |||||
} | |||||
} | |||||
for (const auto &pid : invalidPeers) { | |||||
removePeer(pid); | |||||
} | |||||
} | |||||
} // namespace avalanche | } // namespace avalanche |