11#include <boost/multi_index/ordered_index.hpp>
12#include <boost/multi_index_container.hpp>
17#include <unordered_map>
76 std::chrono::microseconds m_time;
82 const bool m_preferred : 1;
93 State GetState()
const {
return static_cast<State
>(m_state); }
102 bool IsSelected()
const {
103 return GetState() == State::CANDIDATE_BEST ||
104 GetState() == State::REQUESTED;
109 return GetState() == State::REQUESTED ||
110 GetState() == State::CANDIDATE_DELAYED;
118 return GetState() == State::CANDIDATE_READY ||
119 GetState() == State::CANDIDATE_BEST;
128 : m_invid(
invid), m_time(
reqtime), m_peer(peer), m_sequence(sequence),
141class PriorityComputer {
145 explicit PriorityComputer(
bool deterministic)
146 : m_k0{deterministic ? 0 :
GetRand(0xFFFFFFFFFFFFFFFF)},
147 m_k1{deterministic ? 0 :
GetRand(0xFFFFFFFFFFFFFFFF)} {}
159 Priority operator()(
const Announcement &
ann)
const {
160 return operator()(
ann.m_invid,
ann.m_peer,
ann.m_preferred);
180using ByPeerView = std::tuple<NodeId, bool, const uint256 &>;
181struct ByPeerViewExtractor {
183 result_type operator()(
const Announcement &
ann)
const {
200using ByInvIdView = std::tuple<const uint256 &, State, Priority>;
201class ByInvIdViewExtractor {
202 const PriorityComputer &m_computer;
205 explicit ByInvIdViewExtractor(
const PriorityComputer &
computer)
208 result_type operator()(
const Announcement &
ann)
const {
210 (
ann.GetState() == State::CANDIDATE_READY) ? m_computer(
ann) : 0;
215enum class WaitState {
227 if (
ann.IsWaiting()) {
228 return WaitState::FUTURE_EVENT;
230 if (
ann.IsSelectable()) {
231 return WaitState::PAST_EVENT;
233 return WaitState::NO_EVENT;
248using ByTimeView = std::pair<WaitState, std::chrono::microseconds>;
249struct ByTimeViewExtractor {
251 result_type operator()(
const Announcement &
ann)
const {
260using Index = boost::multi_index_container<
262 boost::multi_index::indexed_by<
263 boost::multi_index::ordered_unique<boost::multi_index::tag<ByPeer>,
264 ByPeerViewExtractor>,
265 boost::multi_index::ordered_non_unique<boost::multi_index::tag<ByInvId>,
266 ByInvIdViewExtractor>,
267 boost::multi_index::ordered_non_unique<boost::multi_index::tag<ByTime>,
268 ByTimeViewExtractor>>>;
271template <
typename Tag>
using Iter =
typename Index::index<Tag>::type::iterator;
278 size_t m_completed = 0;
280 size_t m_requested = 0;
286 size_t m_candidate_delayed = 0;
288 size_t m_candidate_ready = 0;
290 size_t m_candidate_best = 0;
293 size_t m_requested = 0;
296 Priority m_priority_candidate_best = std::numeric_limits<Priority>::max();
299 Priority m_priority_best_candidate_ready =
300 std::numeric_limits<Priority>::min();
302 std::vector<NodeId> m_peers;
307 return std::tie(
a.m_total,
a.m_completed,
a.m_requested) ==
308 std::tie(
b.m_total,
b.m_completed,
b.m_requested);
315 std::unordered_map<NodeId, PeerInfo>
ret;
316 for (
const Announcement &
ann : index) {
317 PeerInfo &info =
ret[
ann.m_peer];
319 info.m_requested += (
ann.GetState() == State::REQUESTED);
320 info.m_completed += (
ann.GetState() == State::COMPLETED);
326std::map<uint256, InvIdInfo>
328 std::map<uint256, InvIdInfo>
ret;
329 for (
const Announcement &
ann : index) {
330 InvIdInfo &info =
ret[
ann.m_invid];
332 info.m_candidate_delayed +=
333 (
ann.GetState() == State::CANDIDATE_DELAYED);
334 info.m_candidate_ready += (
ann.GetState() == State::CANDIDATE_READY);
335 info.m_candidate_best += (
ann.GetState() == State::CANDIDATE_BEST);
336 info.m_requested += (
ann.GetState() == State::REQUESTED);
339 if (
ann.GetState() == State::CANDIDATE_BEST) {
342 if (
ann.GetState() == State::CANDIDATE_READY) {
343 info.m_priority_best_candidate_ready =
344 std::max(info.m_priority_best_candidate_ready,
computer(
ann));
348 info.m_peers.push_back(
ann.m_peer);
381 InvIdInfo &info = item.second;
385 assert(info.m_candidate_delayed + info.m_candidate_ready +
386 info.m_candidate_best + info.m_requested >
390 assert(info.m_candidate_best + info.m_requested <= 1);
394 if (info.m_candidate_ready > 0) {
395 assert(info.m_candidate_best + info.m_requested == 1);
401 if (info.m_candidate_ready && info.m_candidate_best) {
402 assert(info.m_priority_candidate_best >=
403 info.m_priority_best_candidate_ready);
407 std::sort(info.m_peers.begin(), info.m_peers.end());
409 std::adjacent_find(info.m_peers.begin(), info.m_peers.end()) ==
416 if (
ann.IsWaiting()) {
421 }
else if (
ann.IsSelectable()) {
435 peerit->second.m_completed -= it->GetState() == State::COMPLETED;
436 peerit->second.m_requested -= it->GetState() == State::REQUESTED;
437 if (--
peerit->second.m_total == 0) {
444 template <
typename Tag,
typename Modifier>
447 peerit->second.m_completed -= it->GetState() == State::COMPLETED;
448 peerit->second.m_requested -= it->GetState() == State::REQUESTED;
450 peerit->second.m_completed += it->GetState() == State::COMPLETED;
451 peerit->second.m_requested += it->GetState() == State::REQUESTED;
460 assert(it->GetState() == State::CANDIDATE_DELAYED);
463 ann.SetState(State::CANDIDATE_READY);
473 it_next->m_invid != it->m_invid ||
474 it_next->GetState() == State::COMPLETED) {
478 ann.SetState(State::CANDIDATE_BEST);
480 }
else if (
it_next->GetState() == State::CANDIDATE_BEST) {
487 ann.SetState(State::CANDIDATE_READY);
490 ann.SetState(State::CANDIDATE_BEST);
503 if (it->IsSelected() && it !=
m_index.get<ByInvId>().begin()) {
507 if (
it_prev->m_invid == it->m_invid &&
508 it_prev->GetState() == State::CANDIDATE_READY) {
512 ann.SetState(State::CANDIDATE_BEST);
525 assert(it->GetState() != State::COMPLETED);
530 if (it !=
m_index.get<ByInvId>().begin() &&
531 std::prev(it)->m_invid == it->m_invid) {
537 if (std::next(it) !=
m_index.get<ByInvId>().end() &&
538 std::next(it)->m_invid == it->m_invid &&
539 std::next(it)->GetState() != State::COMPLETED) {
557 if (it->GetState() == State::COMPLETED) {
567 }
while (it !=
m_index.get<ByInvId>().end() &&
568 it->m_invid ==
invid);
593 auto it =
m_index.get<ByTime>().begin();
594 if (it->GetState() == State::CANDIDATE_DELAYED &&
597 }
else if (it->GetState() == State::REQUESTED &&
612 auto it = std::prev(
m_index.get<ByTime>().end());
613 if (it->IsSelectable() && it->m_time > now) {
615 State::CANDIDATE_DELAYED);
642 auto &index =
m_index.get<ByPeer>();
645 while (it != index.end() && it->m_peer == peer) {
667 (std::next(it) == index.end() || std::next(it)->m_peer != peer)
684 auto it =
m_index.get<ByInvId>().lower_bound(
686 while (it !=
m_index.get<ByInvId>().end() && it->m_invid ==
invid) {
692 std::chrono::microseconds
reqtime) {
719 std::chrono::microseconds now,
726 std::vector<const Announcement *>
selected;
731 it_peer->GetState() == State::CANDIDATE_BEST) {
738 [](
const Announcement *
a,
const Announcement *
b) {
739 return a->m_sequence < b->m_sequence;
743 std::vector<uint256>
ret;
746 std::back_inserter(
ret),
747 [](
const Announcement *
ann) { return ann->m_invid; });
752 std::chrono::microseconds expiry) {
754 if (it ==
m_index.get<ByPeer>().end()) {
764 if (it ==
m_index.get<ByPeer>().end() ||
765 (it->GetState() != State::CANDIDATE_DELAYED &&
766 it->GetState() != State::CANDIDATE_READY)) {
783 if (
it_old->GetState() == State::CANDIDATE_BEST) {
795 ann.SetState(State::CANDIDATE_READY);
797 }
else if (
it_old->GetState() == State::REQUESTED) {
802 ann.SetState(State::COMPLETED);
809 ann.SetState(State::REQUESTED);
818 if (it ==
m_index.get<ByPeer>().end()) {
821 if (it !=
m_index.get<ByPeer>().end()) {
829 return it->second.m_requested;
837 return it->second.m_total - it->second.m_requested -
838 it->second.m_completed;
846 return it->second.m_total;
862std::unique_ptr<InvRequestTrackerImplInterface>
864 return std::make_unique<InvRequestTrackerImpl>(deterministic);
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
CSipHasher & Write(uint64_t data)
Hash a 64-bit integer worth of data.
Actual implementation for InvRequestTracker's data structure.
InvRequestTrackerImpl(const InvRequestTrackerImpl &)=delete
size_t CountInFlight(NodeId peer) const
void Modify(Iter< Tag > it, Modifier modifier)
Wrapper around Index::...::modify that keeps m_peerinfo up to date.
SequenceNumber m_current_sequence
The current sequence number.
std::vector< uint256 > GetRequestable(NodeId peer, std::chrono::microseconds now, ClearExpiredFun clearExpired, EmplaceExpiredFun emplaceExpired)
Find the InvIds to request now from peer.
void ReceivedResponse(NodeId peer, const uint256 &invid)
Iter< Tag > Erase(Iter< Tag > it)
Wrapper around Index::...::erase that keeps m_peerinfo up to date.
std::unordered_map< NodeId, PeerInfo > m_peerinfo
Map with this tracker's per-peer statistics.
InvRequestTrackerImpl(bool deterministic)
bool MakeCompleted(Iter< ByInvId > it)
Convert any announcement to a COMPLETED one.
void SetTimePoint(std::chrono::microseconds now, ClearExpiredFun clearExpired, EmplaceExpiredFun emplaceExpired)
Make the data structure consistent with a given point in time:
void DisconnectedPeer(NodeId peer)
uint64_t ComputePriority(const uint256 &invid, NodeId peer, bool preferred) const
void ForgetInvId(const uint256 &invid)
InvRequestTrackerImpl & operator=(const InvRequestTrackerImpl &)=delete
void ReceivedInv(NodeId peer, const uint256 &invid, bool preferred, std::chrono::microseconds reqtime)
bool IsOnlyNonCompleted(Iter< ByInvId > it)
Check if 'it' is the only announcement for a given invid that isn't COMPLETED.
void RequestedData(NodeId peer, const uint256 &invid, std::chrono::microseconds expiry)
size_t Count(NodeId peer) const
~InvRequestTrackerImpl()=default
size_t Size() const
Count how many announcements are being tracked in total across all peers and transactions.
void ChangeAndReselect(Iter< ByInvId > it, State new_state)
Change the state of an announcement to something non-IsSelected().
void PromoteCandidateReady(Iter< ByInvId > it)
Convert a CANDIDATE_DELAYED announcement into a CANDIDATE_READY.
const PriorityComputer m_computer
This tracker's priority computer.
void PostGetRequestableSanityCheck(std::chrono::microseconds now) const
Index m_index
This tracker's main data structure.
size_t CountCandidates(NodeId peer) const
Data structure to keep track of, and schedule, inventory downloads from peers.
static std::unique_ptr< InvRequestTrackerImplInterface > BuildImpl(bool deterministic)
const std::function< void()> & ClearExpiredFun
const std::function< void(const NodeId &, const uint256 &)> & EmplaceExpiredFun
static const uint256 ZERO
Implement std::hash so RCUPtr can be used as a key for maps or sets.
bool operator==(const CNetAddr &a, const CNetAddr &b)
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...