Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/peermanager.cpp
Show First 20 Lines • Show All 271 Lines • ▼ Show 20 Lines | bool PeerManager::registerProof(const ProofRef &proof) { | ||||
bool valid = [&](ProofValidationState &state) { | bool valid = [&](ProofValidationState &state) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | ||||
return proof->verify(state, coins); | return proof->verify(state, coins); | ||||
}(state); | }(state); | ||||
if (!valid) { | if (!valid) { | ||||
if (isOrphanState(state)) { | if (isOrphanState(state)) { | ||||
orphanProofs.addProof(proof); | // Don't create a peer but keep the utxos around. | ||||
addProofToPool(proof, false /* allowPeerOverride */); | |||||
} | } | ||||
// Reject invalid proof. | // Reject invalid proof. | ||||
return false; | return false; | ||||
} | } | ||||
if (!addProofToPool( | if (!addProofToPool( | ||||
proof, | proof, | ||||
gArgs.GetBoolArg("-enableavalancheproofreplacement", | gArgs.GetBoolArg("-enableavalancheproofreplacement", | ||||
AVALANCHE_DEFAULT_PROOF_REPLACEMENT_ENABLED))) { | AVALANCHE_DEFAULT_PROOF_REPLACEMENT_ENABLED))) { | ||||
// Rejected due to conflicting proofs | // Rejected due to conflicting proofs | ||||
orphanProofs.addProof(proof); | |||||
return false; | return false; | ||||
} | } | ||||
if (!isPeerCandidate(proof)) { | |||||
// Orphaned due to conflicting proofs | |||||
return false; | |||||
} | |||||
// This is the best candidate, override the conflicting peers | |||||
pool.forEachConflictingProof(proof, | |||||
[&](const ProofRef &p) { removePeer(p); }); | |||||
return createPeer(proof); | return createPeer(proof); | ||||
} | } | ||||
NodeId PeerManager::selectNode() { | NodeId PeerManager::selectNode() { | ||||
for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { | for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { | ||||
const PeerId p = selectPeer(); | const PeerId p = selectPeer(); | ||||
// If we cannot find a peer, it may be due to the fact that it is | // If we cannot find a peer, it may be due to the fact that it is | ||||
Show All 12 Lines | for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { | ||||
} | } | ||||
} | } | ||||
return NO_NODE; | return NO_NODE; | ||||
} | } | ||||
void PeerManager::updatedBlockTip() { | void PeerManager::updatedBlockTip() { | ||||
std::vector<PeerId> invalidPeers; | std::vector<PeerId> invalidPeers; | ||||
std::vector<ProofRef> newOrphans; | |||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | ||||
for (const auto &p : peers) { | for (const auto &p : peers) { | ||||
ProofValidationState state; | ProofValidationState state; | ||||
if (!p.proof->verify(state, coins)) { | if (!p.proof->verify(state, coins)) { | ||||
if (isOrphanState(state)) { | |||||
newOrphans.push_back(p.proof); | |||||
} | |||||
invalidPeers.push_back(p.peerid); | invalidPeers.push_back(p.peerid); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Remove the invalid peers before the orphans rescan. This makes it | // Remove the invalid peers before the orphans rescan. This makes it | ||||
// possible to pull back proofs with utxos that conflicted with these | // possible to pull back proofs with utxos that conflicted with these | ||||
// invalid peers. | // invalid peers. | ||||
for (const auto &pid : invalidPeers) { | for (const auto &pid : invalidPeers) { | ||||
removePeer(pid); | removePeer(pid); | ||||
} | } | ||||
orphanProofs.rescan(*this); | // Build a candidate set from the pool | ||||
std::set<ProofRef> candidates; | |||||
pool.forEachUtxo([&](const COutPoint &outpoint, const auto &proofs) { | |||||
for (const ProofRef &candidate : proofs) { | |||||
if (isValid(candidate->getId())) { | |||||
return; | |||||
} | |||||
candidates.emplace(candidate); | |||||
} | |||||
}); | |||||
for (auto &p : newOrphans) { | for (const ProofRef &candidate : candidates) { | ||||
orphanProofs.addProof(p); | // Temporarly remove the candidate from the orphan state. Registration | ||||
// will eventually turn it into a peer or make it an orphan again. | |||||
pool.removeProof(candidate); | |||||
registerProof(candidate); | |||||
} | } | ||||
} | } | ||||
ProofRef PeerManager::getProof(const ProofId &proofid) const { | ProofRef PeerManager::getProof(const ProofId &proofid) const { | ||||
ProofRef proof = nullptr; | ProofRef proof = nullptr; | ||||
forPeer(proofid, [&](const Peer &p) { | forPeer(proofid, [&](const Peer &p) { | ||||
proof = p.proof; | proof = p.proof; | ||||
return true; | return true; | ||||
}); | }); | ||||
if (!proof) { | if (!proof) { | ||||
proof = orphanProofs.getProof(proofid); | pool.forEachUtxo([&](const COutPoint &outpoint, const auto &proofs) { | ||||
for (const ProofRef &p : proofs) { | |||||
if (p->getId() == proofid) { | |||||
proof = p; | |||||
return; | |||||
} | |||||
} | |||||
}); | |||||
} | } | ||||
return proof; | return proof; | ||||
} | } | ||||
bool PeerManager::isValid(const ProofId &proofid) const { | bool PeerManager::isValid(const ProofId &proofid) const { | ||||
auto &pview = peers.get<proof_index>(); | auto &pview = peers.get<proof_index>(); | ||||
return pview.find(proofid) != pview.end(); | return pview.find(proofid) != pview.end(); | ||||
} | } | ||||
bool PeerManager::isOrphan(const ProofId &proofid) const { | bool PeerManager::isOrphan(const ProofId &proofid) const { | ||||
return orphanProofs.getProof(proofid) != nullptr; | return !isValid(proofid) && getProof(proofid); | ||||
} | } | ||||
bool PeerManager::createPeer(const ProofRef &proof) { | bool PeerManager::createPeer(const ProofRef &proof) { | ||||
assert(proof); | assert(proof); | ||||
const ProofId &proofid = proof->getId(); | const ProofId &proofid = proof->getId(); | ||||
if (isValid(proofid)) { | if (isValid(proofid)) { | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | bool PeerManager::removePeer(const PeerId peerid) { | ||||
// Remove nodes associated with this peer, unless their timeout is still | // Remove nodes associated with this peer, unless their timeout is still | ||||
// active. This ensure that we don't overquery them in case they are | // active. This ensure that we don't overquery them in case they are | ||||
// subsequently added to another peer. | // subsequently added to another peer. | ||||
nview.erase(nview.lower_bound(boost::make_tuple(peerid, TimePoint())), | nview.erase(nview.lower_bound(boost::make_tuple(peerid, TimePoint())), | ||||
nview.upper_bound(boost::make_tuple( | nview.upper_bound(boost::make_tuple( | ||||
peerid, std::chrono::steady_clock::now()))); | peerid, std::chrono::steady_clock::now()))); | ||||
bool removed = pool.removeProof(it->proof); | |||||
assert(removed); | |||||
m_unbroadcast_proofids.erase(it->proof->getId()); | m_unbroadcast_proofids.erase(it->proof->getId()); | ||||
peers.erase(it); | peers.erase(it); | ||||
return true; | return true; | ||||
} | } | ||||
PeerId PeerManager::selectPeer() const { | PeerId PeerManager::selectPeer() const { | ||||
if (slots.empty() || slotCount == 0) { | if (slots.empty() || slotCount == 0) { | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | for (const auto &p : peers) { | ||||
} | } | ||||
// If the score do not match, same thing. | // If the score do not match, same thing. | ||||
if (slots[p.index].getScore() != p.getScore()) { | if (slots[p.index].getScore() != p.getScore()) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// Check there is no dangling utxo in the pool | return true; | ||||
bool danglingUtxo = false; | |||||
pool.forEachUtxo([&](const COutPoint &outpoint, const auto &proof) { | |||||
danglingUtxo = | |||||
danglingUtxo || peersUtxos.find(outpoint) == peersUtxos.end(); | |||||
}); | |||||
return !danglingUtxo; | |||||
} | } | ||||
PeerId selectPeerImpl(const std::vector<Slot> &slots, const uint64_t slot, | PeerId selectPeerImpl(const std::vector<Slot> &slots, const uint64_t slot, | ||||
const uint64_t max) { | const uint64_t max) { | ||||
assert(slot <= max); | assert(slot <= max); | ||||
size_t begin = 0, end = slots.size(); | size_t begin = 0, end = slots.size(); | ||||
uint64_t bottom = 0, top = max; | uint64_t bottom = 0, top = max; | ||||
▲ Show 20 Lines • Show All 63 Lines • Show Last 20 Lines |