Changeset View
Changeset View
Standalone View
Standalone View
src/invrequest.cpp
Show First 20 Lines • Show All 363 Lines • ▼ Show 20 Lines | class InvRequestTrackerImpl : public InvRequestTrackerImplInterface { | ||||
//! This tracker's main data structure. See SanityCheck() for the invariants | //! This tracker's main data structure. See SanityCheck() for the invariants | ||||
//! that apply to it. | //! that apply to it. | ||||
Index m_index; | Index m_index; | ||||
//! Map with this tracker's per-peer statistics. | //! Map with this tracker's per-peer statistics. | ||||
std::unordered_map<NodeId, PeerInfo> m_peerinfo; | std::unordered_map<NodeId, PeerInfo> m_peerinfo; | ||||
public: | public: | ||||
void SanityCheck() const { | void SanityCheck() const override { | ||||
// Recompute m_peerdata from m_index. This verifies the data in it as it | // Recompute m_peerdata from m_index. This verifies the data in it as it | ||||
// should just be caching statistics on m_index. It also verifies the | // should just be caching statistics on m_index. It also verifies the | ||||
// invariant that no PeerInfo announcements with m_total==0 exist. | // invariant that no PeerInfo announcements with m_total==0 exist. | ||||
assert(m_peerinfo == RecomputePeerInfo(m_index)); | assert(m_peerinfo == RecomputePeerInfo(m_index)); | ||||
// Calculate per-invid statistics from m_index, and validate | // Calculate per-invid statistics from m_index, and validate | ||||
// invariants. | // invariants. | ||||
for (auto &item : ComputeInvIdInfo(m_index, m_computer)) { | for (auto &item : ComputeInvIdInfo(m_index, m_computer)) { | ||||
Show All 25 Lines | void SanityCheck() const override { | ||||
// No invid can have been announced by the same peer twice. | // No invid can have been announced by the same peer twice. | ||||
std::sort(info.m_peers.begin(), info.m_peers.end()); | std::sort(info.m_peers.begin(), info.m_peers.end()); | ||||
assert( | assert( | ||||
std::adjacent_find(info.m_peers.begin(), info.m_peers.end()) == | std::adjacent_find(info.m_peers.begin(), info.m_peers.end()) == | ||||
info.m_peers.end()); | info.m_peers.end()); | ||||
} | } | ||||
} | } | ||||
void PostGetRequestableSanityCheck(std::chrono::microseconds now) const { | void PostGetRequestableSanityCheck( | ||||
std::chrono::microseconds now) const override { | |||||
for (const Announcement &ann : m_index) { | for (const Announcement &ann : m_index) { | ||||
if (ann.IsWaiting()) { | if (ann.IsWaiting()) { | ||||
// REQUESTED and CANDIDATE_DELAYED must have a time in the | // REQUESTED and CANDIDATE_DELAYED must have a time in the | ||||
// future (they should have been converted to | // future (they should have been converted to | ||||
// COMPLETED/CANDIDATE_READY respectively). | // COMPLETED/CANDIDATE_READY respectively). | ||||
assert(ann.m_time > now); | assert(ann.m_time > now); | ||||
} else if (ann.IsSelectable()) { | } else if (ann.IsSelectable()) { | ||||
// CANDIDATE_READY and CANDIDATE_BEST cannot have a time in the | // CANDIDATE_READY and CANDIDATE_BEST cannot have a time in the | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | public: | ||||
// Disable copying and assigning (a default copy won't work due the stateful | // Disable copying and assigning (a default copy won't work due the stateful | ||||
// ByInvIdViewExtractor). | // ByInvIdViewExtractor). | ||||
InvRequestTrackerImpl(const InvRequestTrackerImpl &) = delete; | InvRequestTrackerImpl(const InvRequestTrackerImpl &) = delete; | ||||
InvRequestTrackerImpl &operator=(const InvRequestTrackerImpl &) = delete; | InvRequestTrackerImpl &operator=(const InvRequestTrackerImpl &) = delete; | ||||
~InvRequestTrackerImpl() = default; | ~InvRequestTrackerImpl() = default; | ||||
void DisconnectedPeer(NodeId peer) { | void DisconnectedPeer(NodeId peer) override { | ||||
auto &index = m_index.get<ByPeer>(); | auto &index = m_index.get<ByPeer>(); | ||||
auto it = | auto it = | ||||
index.lower_bound(ByPeerView{peer, false, uint256(uint256::ZERO)}); | index.lower_bound(ByPeerView{peer, false, uint256(uint256::ZERO)}); | ||||
while (it != index.end() && it->m_peer == peer) { | while (it != index.end() && it->m_peer == peer) { | ||||
// Check what to continue with after this iteration. 'it' will be | // Check what to continue with after this iteration. 'it' will be | ||||
// deleted in what follows, so we need to decide what to continue | // deleted in what follows, so we need to decide what to continue | ||||
// with afterwards. There are a number of cases to consider: | // with afterwards. There are a number of cases to consider: | ||||
// - std::next(it) is end() or belongs to a different peer. In that | // - std::next(it) is end() or belongs to a different peer. In that | ||||
Show All 25 Lines | void DisconnectedPeer(NodeId peer) override { | ||||
// Then actually delete the announcement (unless it was already | // Then actually delete the announcement (unless it was already | ||||
// deleted by MakeCompleted). | // deleted by MakeCompleted). | ||||
Erase<ByPeer>(it); | Erase<ByPeer>(it); | ||||
} | } | ||||
it = it_next; | it = it_next; | ||||
} | } | ||||
} | } | ||||
void ForgetInvId(const uint256 &invid) { | void ForgetInvId(const uint256 &invid) override { | ||||
auto it = m_index.get<ByInvId>().lower_bound( | auto it = m_index.get<ByInvId>().lower_bound( | ||||
ByInvIdView{invid, State::CANDIDATE_DELAYED, 0}); | ByInvIdView{invid, State::CANDIDATE_DELAYED, 0}); | ||||
while (it != m_index.get<ByInvId>().end() && it->m_invid == invid) { | while (it != m_index.get<ByInvId>().end() && it->m_invid == invid) { | ||||
it = Erase<ByInvId>(it); | it = Erase<ByInvId>(it); | ||||
} | } | ||||
} | } | ||||
void ReceivedInv(NodeId peer, const uint256 &invid, bool preferred, | void ReceivedInv(NodeId peer, const uint256 &invid, bool preferred, | ||||
std::chrono::microseconds reqtime) { | std::chrono::microseconds reqtime) override { | ||||
// Bail out if we already have a CANDIDATE_BEST announcement for this | // Bail out if we already have a CANDIDATE_BEST announcement for this | ||||
// (invid, peer) combination. The case where there is a | // (invid, peer) combination. The case where there is a | ||||
// non-CANDIDATE_BEST announcement already will be caught by the | // non-CANDIDATE_BEST announcement already will be caught by the | ||||
// uniqueness property of the ByPeer index when we try to emplace the | // uniqueness property of the ByPeer index when we try to emplace the | ||||
// new object below. | // new object below. | ||||
if (m_index.get<ByPeer>().count(ByPeerView{peer, true, invid})) { | if (m_index.get<ByPeer>().count(ByPeerView{peer, true, invid})) { | ||||
return; | return; | ||||
} | } | ||||
Show All 9 Lines | void ReceivedInv(NodeId peer, const uint256 &invid, bool preferred, | ||||
} | } | ||||
// Update accounting metadata. | // Update accounting metadata. | ||||
++m_peerinfo[peer].m_total; | ++m_peerinfo[peer].m_total; | ||||
++m_current_sequence; | ++m_current_sequence; | ||||
} | } | ||||
//! Find the InvIds to request now from peer. | //! Find the InvIds to request now from peer. | ||||
std::vector<uint256> GetRequestable(NodeId peer, | std::vector<uint256> | ||||
std::chrono::microseconds now, | GetRequestable(NodeId peer, std::chrono::microseconds now, | ||||
ClearExpiredFun clearExpired, | ClearExpiredFun clearExpired, | ||||
EmplaceExpiredFun emplaceExpired) { | EmplaceExpiredFun emplaceExpired) override { | ||||
// Move time. | // Move time. | ||||
SetTimePoint(now, clearExpired, emplaceExpired); | SetTimePoint(now, clearExpired, emplaceExpired); | ||||
// Find all CANDIDATE_BEST announcements for this peer. | // Find all CANDIDATE_BEST announcements for this peer. | ||||
std::vector<const Announcement *> selected; | std::vector<const Announcement *> selected; | ||||
auto it_peer = m_index.get<ByPeer>().lower_bound( | auto it_peer = m_index.get<ByPeer>().lower_bound( | ||||
ByPeerView{peer, true, uint256(uint256::ZERO)}); | ByPeerView{peer, true, uint256(uint256::ZERO)}); | ||||
while (it_peer != m_index.get<ByPeer>().end() && | while (it_peer != m_index.get<ByPeer>().end() && | ||||
Show All 14 Lines | GetRequestable(NodeId peer, std::chrono::microseconds now, | ||||
ret.reserve(selected.size()); | ret.reserve(selected.size()); | ||||
std::transform(selected.begin(), selected.end(), | std::transform(selected.begin(), selected.end(), | ||||
std::back_inserter(ret), | std::back_inserter(ret), | ||||
[](const Announcement *ann) { return ann->m_invid; }); | [](const Announcement *ann) { return ann->m_invid; }); | ||||
return ret; | return ret; | ||||
} | } | ||||
void RequestedData(NodeId peer, const uint256 &invid, | void RequestedData(NodeId peer, const uint256 &invid, | ||||
std::chrono::microseconds expiry) { | std::chrono::microseconds expiry) override { | ||||
auto it = m_index.get<ByPeer>().find(ByPeerView{peer, true, invid}); | auto it = m_index.get<ByPeer>().find(ByPeerView{peer, true, invid}); | ||||
if (it == m_index.get<ByPeer>().end()) { | if (it == m_index.get<ByPeer>().end()) { | ||||
// There is no CANDIDATE_BEST announcement, look for a _READY or | // There is no CANDIDATE_BEST announcement, look for a _READY or | ||||
// _DELAYED instead. If the caller only ever invokes RequestedData | // _DELAYED instead. If the caller only ever invokes RequestedData | ||||
// with the values returned by GetRequestable, and no other | // with the values returned by GetRequestable, and no other | ||||
// non-const functions other than ForgetInvId and GetRequestable in | // non-const functions other than ForgetInvId and GetRequestable in | ||||
// between, this branch will never execute (as invids returned by | // between, this branch will never execute (as invids returned by | ||||
// GetRequestable always correspond to CANDIDATE_BEST | // GetRequestable always correspond to CANDIDATE_BEST | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | void RequestedData(NodeId peer, const uint256 &invid, | ||||
} | } | ||||
Modify<ByPeer>(it, [expiry](Announcement &ann) { | Modify<ByPeer>(it, [expiry](Announcement &ann) { | ||||
ann.SetState(State::REQUESTED); | ann.SetState(State::REQUESTED); | ||||
ann.m_time = expiry; | ann.m_time = expiry; | ||||
}); | }); | ||||
} | } | ||||
void ReceivedResponse(NodeId peer, const uint256 &invid) { | void ReceivedResponse(NodeId peer, const uint256 &invid) override { | ||||
// We need to search the ByPeer index for both (peer, false, invid) and | // We need to search the ByPeer index for both (peer, false, invid) and | ||||
// (peer, true, invid). | // (peer, true, invid). | ||||
auto it = m_index.get<ByPeer>().find(ByPeerView{peer, false, invid}); | auto it = m_index.get<ByPeer>().find(ByPeerView{peer, false, invid}); | ||||
if (it == m_index.get<ByPeer>().end()) { | if (it == m_index.get<ByPeer>().end()) { | ||||
it = m_index.get<ByPeer>().find(ByPeerView{peer, true, invid}); | it = m_index.get<ByPeer>().find(ByPeerView{peer, true, invid}); | ||||
} | } | ||||
if (it != m_index.get<ByPeer>().end()) { | if (it != m_index.get<ByPeer>().end()) { | ||||
MakeCompleted(m_index.project<ByInvId>(it)); | MakeCompleted(m_index.project<ByInvId>(it)); | ||||
} | } | ||||
} | } | ||||
size_t CountInFlight(NodeId peer) const { | size_t CountInFlight(NodeId peer) const override { | ||||
auto it = m_peerinfo.find(peer); | auto it = m_peerinfo.find(peer); | ||||
if (it != m_peerinfo.end()) { | if (it != m_peerinfo.end()) { | ||||
return it->second.m_requested; | return it->second.m_requested; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
size_t CountCandidates(NodeId peer) const { | size_t CountCandidates(NodeId peer) const override { | ||||
auto it = m_peerinfo.find(peer); | auto it = m_peerinfo.find(peer); | ||||
if (it != m_peerinfo.end()) { | if (it != m_peerinfo.end()) { | ||||
return it->second.m_total - it->second.m_requested - | return it->second.m_total - it->second.m_requested - | ||||
it->second.m_completed; | it->second.m_completed; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
size_t Count(NodeId peer) const { | size_t Count(NodeId peer) const override { | ||||
auto it = m_peerinfo.find(peer); | auto it = m_peerinfo.find(peer); | ||||
if (it != m_peerinfo.end()) { | if (it != m_peerinfo.end()) { | ||||
return it->second.m_total; | return it->second.m_total; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
//! Count how many announcements are being tracked in total across all peers | //! Count how many announcements are being tracked in total across all peers | ||||
//! and transactions. | //! and transactions. | ||||
size_t Size() const { return m_index.size(); } | size_t Size() const override { return m_index.size(); } | ||||
uint64_t ComputePriority(const uint256 &invid, NodeId peer, | uint64_t ComputePriority(const uint256 &invid, NodeId peer, | ||||
bool preferred) const { | bool preferred) const override { | ||||
// Return Priority as a uint64_t as Priority is internal. | // Return Priority as a uint64_t as Priority is internal. | ||||
return uint64_t{m_computer(invid, peer, preferred)}; | return uint64_t{m_computer(invid, peer, preferred)}; | ||||
} | } | ||||
}; | }; | ||||
std::unique_ptr<InvRequestTrackerImplInterface> | std::unique_ptr<InvRequestTrackerImplInterface> | ||||
InvRequestTrackerImplInterface::BuildImpl(bool deterministic) { | InvRequestTrackerImplInterface::BuildImpl(bool deterministic) { | ||||
return std::make_unique<InvRequestTrackerImpl>(deterministic); | return std::make_unique<InvRequestTrackerImpl>(deterministic); | ||||
} | } |