Changeset View
Changeset View
Standalone View
Standalone View
src/txrequest.cpp
// Copyright (c) 2020 The Bitcoin Core developers | // Copyright (c) 2020 The Bitcoin Core 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 <txrequest.h> | #include <txrequest.h> | ||||
#include <crypto/siphash.h> | #include <crypto/siphash.h> | ||||
#include <net.h> | #include <net.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <boost/multi_index/ordered_index.hpp> | #include <boost/multi_index/ordered_index.hpp> | ||||
#include <boost/multi_index_container.hpp> | #include <boost/multi_index_container.hpp> | ||||
#include <cassert> | #include <cassert> | ||||
#include <chrono> | #include <chrono> | ||||
#include <functional> | |||||
#include <unordered_map> | #include <unordered_map> | ||||
#include <utility> | #include <utility> | ||||
namespace { | namespace { | ||||
/** | /** | ||||
* The various states a (txid, peer) pair can be in. | * The various states a (txid, peer) pair can be in. | ||||
* | * | ||||
Show All 38 Lines | |||||
using SequenceNumber = uint64_t; | using SequenceNumber = uint64_t; | ||||
/** | /** | ||||
* An announcement. This is the data we track for each txid that is announced | * An announcement. This is the data we track for each txid that is announced | ||||
* to us by each peer. | * to us by each peer. | ||||
*/ | */ | ||||
struct Announcement { | struct Announcement { | ||||
/** TxId that was announced. */ | /** TxId that was announced. */ | ||||
const TxId m_txid; | const uint256 m_txid; | ||||
/** | /** | ||||
* For CANDIDATE_{DELAYED,BEST,READY} the reqtime; for REQUESTED the | * For CANDIDATE_{DELAYED,BEST,READY} the reqtime; for REQUESTED the | ||||
* expiry. | * expiry. | ||||
*/ | */ | ||||
std::chrono::microseconds m_time; | std::chrono::microseconds m_time; | ||||
/** What peer the request was from. */ | /** What peer the request was from. */ | ||||
const NodeId m_peer; | const NodeId m_peer; | ||||
/** What sequence number this announcement has. */ | /** What sequence number this announcement has. */ | ||||
Show All 38 Lines | bool IsSelectable() const { | ||||
return GetState() == State::CANDIDATE_READY || | return GetState() == State::CANDIDATE_READY || | ||||
GetState() == State::CANDIDATE_BEST; | GetState() == State::CANDIDATE_BEST; | ||||
} | } | ||||
/** | /** | ||||
* Construct a new announcement from scratch, initially in | * Construct a new announcement from scratch, initially in | ||||
* CANDIDATE_DELAYED state. | * CANDIDATE_DELAYED state. | ||||
*/ | */ | ||||
Announcement(const TxId &txid, NodeId peer, bool preferred, | Announcement(const uint256 &txid, NodeId peer, bool preferred, | ||||
std::chrono::microseconds reqtime, SequenceNumber sequence) | std::chrono::microseconds reqtime, SequenceNumber sequence) | ||||
: m_txid(txid), m_time(reqtime), m_peer(peer), m_sequence(sequence), | : m_txid(txid), m_time(reqtime), m_peer(peer), m_sequence(sequence), | ||||
m_preferred(preferred), | m_preferred(preferred), | ||||
m_state(static_cast<uint8_t>(State::CANDIDATE_DELAYED)) {} | m_state(static_cast<uint8_t>(State::CANDIDATE_DELAYED)) {} | ||||
}; | }; | ||||
//! Type alias for priorities. | //! Type alias for priorities. | ||||
using Priority = uint64_t; | using Priority = uint64_t; | ||||
/** | /** | ||||
* A functor with embedded salt that computes priority of an announcement. | * A functor with embedded salt that computes priority of an announcement. | ||||
* | * | ||||
* Higher priorities are selected first. | * Higher priorities are selected first. | ||||
*/ | */ | ||||
class PriorityComputer { | class PriorityComputer { | ||||
const uint64_t m_k0, m_k1; | const uint64_t m_k0, m_k1; | ||||
public: | public: | ||||
explicit PriorityComputer(bool deterministic) | explicit PriorityComputer(bool deterministic) | ||||
: m_k0{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)}, | : m_k0{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)}, | ||||
m_k1{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)} {} | m_k1{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)} {} | ||||
Priority operator()(const TxId &txid, NodeId peer, bool preferred) const { | Priority operator()(const uint256 &txid, NodeId peer, | ||||
bool preferred) const { | |||||
uint64_t low_bits = CSipHasher(m_k0, m_k1) | uint64_t low_bits = CSipHasher(m_k0, m_k1) | ||||
.Write(txid.begin(), txid.size()) | .Write(txid.begin(), txid.size()) | ||||
.Write(peer) | .Write(peer) | ||||
.Finalize() >> | .Finalize() >> | ||||
1; | 1; | ||||
return low_bits | uint64_t{preferred} << 63; | return low_bits | uint64_t{preferred} << 63; | ||||
} | } | ||||
Show All 13 Lines | |||||
// The ByPeer index is sorted by (peer, state == CANDIDATE_BEST, txid) | // The ByPeer index is sorted by (peer, state == CANDIDATE_BEST, txid) | ||||
// | // | ||||
// Uses: | // Uses: | ||||
// * Looking up existing announcements by peer/txid, by checking both (peer, | // * Looking up existing announcements by peer/txid, by checking both (peer, | ||||
// false, txid) and (peer, true, txid). | // false, txid) and (peer, true, txid). | ||||
// * Finding all CANDIDATE_BEST announcements for a given peer in | // * Finding all CANDIDATE_BEST announcements for a given peer in | ||||
// GetRequestable. | // GetRequestable. | ||||
struct ByPeer {}; | struct ByPeer {}; | ||||
using ByPeerView = std::tuple<NodeId, bool, const TxId &>; | using ByPeerView = std::tuple<NodeId, bool, const uint256 &>; | ||||
struct ByPeerViewExtractor { | struct ByPeerViewExtractor { | ||||
using result_type = ByPeerView; | using result_type = ByPeerView; | ||||
result_type operator()(const Announcement &ann) const { | result_type operator()(const Announcement &ann) const { | ||||
return ByPeerView{ann.m_peer, ann.GetState() == State::CANDIDATE_BEST, | return ByPeerView{ann.m_peer, ann.GetState() == State::CANDIDATE_BEST, | ||||
ann.m_txid}; | ann.m_txid}; | ||||
} | } | ||||
}; | }; | ||||
// The ByTxId index is sorted by (txid, state, priority). | // The ByTxId index is sorted by (txid, state, priority). | ||||
// | // | ||||
// Note: priority == 0 whenever state != CANDIDATE_READY. | // Note: priority == 0 whenever state != CANDIDATE_READY. | ||||
// | // | ||||
// Uses: | // Uses: | ||||
// * Deleting all announcements with a given txid in ForgetTxId. | // * Deleting all announcements with a given txid in ForgetTxId. | ||||
// * Finding the best CANDIDATE_READY to convert to CANDIDATE_BEST, when no | // * Finding the best CANDIDATE_READY to convert to CANDIDATE_BEST, when no | ||||
// other CANDIDATE_READY or REQUESTED announcement exists for that txid. | // other CANDIDATE_READY or REQUESTED announcement exists for that txid. | ||||
// * Determining when no more non-COMPLETED announcements for a given txid | // * Determining when no more non-COMPLETED announcements for a given txid | ||||
// exist, so the COMPLETED ones can be deleted. | // exist, so the COMPLETED ones can be deleted. | ||||
struct ByTxId {}; | struct ByTxId {}; | ||||
using ByTxIdView = std::tuple<const TxId &, State, Priority>; | using ByTxIdView = std::tuple<const uint256 &, State, Priority>; | ||||
class ByTxIdViewExtractor { | class ByTxIdViewExtractor { | ||||
const PriorityComputer &m_computer; | const PriorityComputer &m_computer; | ||||
public: | public: | ||||
ByTxIdViewExtractor(const PriorityComputer &computer) | ByTxIdViewExtractor(const PriorityComputer &computer) | ||||
: m_computer(computer) {} | : m_computer(computer) {} | ||||
using result_type = ByTxIdView; | using result_type = ByTxIdView; | ||||
result_type operator()(const Announcement &ann) const { | result_type operator()(const Announcement &ann) const { | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | for (const Announcement &ann : index) { | ||||
++info.m_total; | ++info.m_total; | ||||
info.m_requested += (ann.GetState() == State::REQUESTED); | info.m_requested += (ann.GetState() == State::REQUESTED); | ||||
info.m_completed += (ann.GetState() == State::COMPLETED); | info.m_completed += (ann.GetState() == State::COMPLETED); | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
/** Compute the TxIdInfo map. Only used for sanity checking. */ | /** Compute the TxIdInfo map. Only used for sanity checking. */ | ||||
std::map<TxId, TxIdInfo> ComputeTxIdInfo(const Index &index, | std::map<uint256, TxIdInfo> ComputeTxIdInfo(const Index &index, | ||||
const PriorityComputer &computer) { | const PriorityComputer &computer) { | ||||
std::map<TxId, TxIdInfo> ret; | std::map<uint256, TxIdInfo> ret; | ||||
for (const Announcement &ann : index) { | for (const Announcement &ann : index) { | ||||
TxIdInfo &info = ret[ann.m_txid]; | TxIdInfo &info = ret[ann.m_txid]; | ||||
// Classify how many announcements of each state we have for this txid. | // Classify how many announcements of each state we have for this txid. | ||||
info.m_candidate_delayed += | info.m_candidate_delayed += | ||||
(ann.GetState() == State::CANDIDATE_DELAYED); | (ann.GetState() == State::CANDIDATE_DELAYED); | ||||
info.m_candidate_ready += (ann.GetState() == State::CANDIDATE_READY); | info.m_candidate_ready += (ann.GetState() == State::CANDIDATE_READY); | ||||
info.m_candidate_best += (ann.GetState() == State::CANDIDATE_BEST); | info.m_candidate_best += (ann.GetState() == State::CANDIDATE_BEST); | ||||
info.m_requested += (ann.GetState() == State::REQUESTED); | info.m_requested += (ann.GetState() == State::REQUESTED); | ||||
// And track the priority of the best CANDIDATE_READY/CANDIDATE_BEST | // And track the priority of the best CANDIDATE_READY/CANDIDATE_BEST | ||||
// announcements. | // announcements. | ||||
if (ann.GetState() == State::CANDIDATE_BEST) { | if (ann.GetState() == State::CANDIDATE_BEST) { | ||||
info.m_priority_candidate_best = computer(ann); | info.m_priority_candidate_best = computer(ann); | ||||
} | } | ||||
if (ann.GetState() == State::CANDIDATE_READY) { | if (ann.GetState() == State::CANDIDATE_READY) { | ||||
info.m_priority_best_candidate_ready = | info.m_priority_best_candidate_ready = | ||||
std::max(info.m_priority_best_candidate_ready, computer(ann)); | std::max(info.m_priority_best_candidate_ready, computer(ann)); | ||||
} | } | ||||
// Also keep track of which peers this txid has an announcement for | // Also keep track of which peers this txid has an announcement for | ||||
// (so we can detect duplicates). | // (so we can detect duplicates). | ||||
info.m_peers.push_back(ann.m_peer); | info.m_peers.push_back(ann.m_peer); | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
using clearExpiredFun = const std::function<void()> &; | |||||
using emplaceExpiredFun = | |||||
const std::function<void(const NodeId &, const uint256 &)> &; | |||||
} // namespace | } // namespace | ||||
/** Actual implementation for TxRequestTracker's data structure. */ | /** Actual implementation for TxRequestTracker's data structure. */ | ||||
class TxRequestTracker::Impl { | class TxRequestTracker::Impl { | ||||
//! The current sequence number. Increases for every announcement. This is | //! The current sequence number. Increases for every announcement. This is | ||||
//! used to sort txid returned by GetRequestable in announcement order. | //! used to sort txid returned by GetRequestable in announcement order. | ||||
SequenceNumber m_current_sequence{0}; | SequenceNumber m_current_sequence{0}; | ||||
▲ Show 20 Lines • Show All 195 Lines • ▼ Show 20 Lines | bool MakeCompleted(Iter<ByTxId> it) { | ||||
// Nothing to be done if it's already COMPLETED. | // Nothing to be done if it's already COMPLETED. | ||||
if (it->GetState() == State::COMPLETED) { | if (it->GetState() == State::COMPLETED) { | ||||
return true; | return true; | ||||
} | } | ||||
if (IsOnlyNonCompleted(it)) { | if (IsOnlyNonCompleted(it)) { | ||||
// This is the last non-COMPLETED announcement for this txid. | // This is the last non-COMPLETED announcement for this txid. | ||||
// Delete all. | // Delete all. | ||||
TxId txid = it->m_txid; | uint256 txid = it->m_txid; | ||||
do { | do { | ||||
it = Erase<ByTxId>(it); | it = Erase<ByTxId>(it); | ||||
} while (it != m_index.get<ByTxId>().end() && it->m_txid == txid); | } while (it != m_index.get<ByTxId>().end() && it->m_txid == txid); | ||||
return false; | return false; | ||||
} | } | ||||
// Mark the announcement COMPLETED, and select the next best | // Mark the announcement COMPLETED, and select the next best | ||||
// announcement (the first CANDIDATE_READY) if needed. | // announcement (the first CANDIDATE_READY) if needed. | ||||
ChangeAndReselect(it, State::COMPLETED); | ChangeAndReselect(it, State::COMPLETED); | ||||
return true; | return true; | ||||
} | } | ||||
//! Make the data structure consistent with a given point in time: | //! Make the data structure consistent with a given point in time: | ||||
//! - REQUESTED annoucements with expiry <= now are turned into COMPLETED. | //! - REQUESTED annoucements with expiry <= now are turned into COMPLETED. | ||||
//! - CANDIDATE_DELAYED announcements with reqtime <= now are turned into | //! - CANDIDATE_DELAYED announcements with reqtime <= now are turned into | ||||
//! CANDIDATE_{READY,BEST}. | //! CANDIDATE_{READY,BEST}. | ||||
//! - CANDIDATE_{READY,BEST} announcements with reqtime > now are turned | //! - CANDIDATE_{READY,BEST} announcements with reqtime > now are turned | ||||
//! into CANDIDATE_DELAYED. | //! into CANDIDATE_DELAYED. | ||||
void SetTimePoint(std::chrono::microseconds now, | void SetTimePoint(std::chrono::microseconds now, | ||||
std::vector<std::pair<NodeId, TxId>> *expired) { | clearExpiredFun clearExpired, | ||||
if (expired) { | emplaceExpiredFun emplaceExpired) { | ||||
expired->clear(); | clearExpired(); | ||||
} | |||||
// Iterate over all CANDIDATE_DELAYED and REQUESTED from old to new, as | // Iterate over all CANDIDATE_DELAYED and REQUESTED from old to new, as | ||||
// long as they're in the past, and convert them to CANDIDATE_READY and | // long as they're in the past, and convert them to CANDIDATE_READY and | ||||
// COMPLETED respectively. | // COMPLETED respectively. | ||||
while (!m_index.empty()) { | while (!m_index.empty()) { | ||||
auto it = m_index.get<ByTime>().begin(); | auto it = m_index.get<ByTime>().begin(); | ||||
if (it->GetState() == State::CANDIDATE_DELAYED && | if (it->GetState() == State::CANDIDATE_DELAYED && | ||||
it->m_time <= now) { | it->m_time <= now) { | ||||
PromoteCandidateReady(m_index.project<ByTxId>(it)); | PromoteCandidateReady(m_index.project<ByTxId>(it)); | ||||
} else if (it->GetState() == State::REQUESTED && | } else if (it->GetState() == State::REQUESTED && | ||||
it->m_time <= now) { | it->m_time <= now) { | ||||
if (expired) { | emplaceExpired(it->m_peer, it->m_txid); | ||||
expired->emplace_back(it->m_peer, it->m_txid); | |||||
} | |||||
MakeCompleted(m_index.project<ByTxId>(it)); | MakeCompleted(m_index.project<ByTxId>(it)); | ||||
} else { | } else { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
while (!m_index.empty()) { | while (!m_index.empty()) { | ||||
// If time went backwards, we may need to demote CANDIDATE_BEST and | // If time went backwards, we may need to demote CANDIDATE_BEST and | ||||
Show All 26 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 | ||||
// ByTxIdViewExtractor). | // ByTxIdViewExtractor). | ||||
Impl(const Impl &) = delete; | Impl(const Impl &) = delete; | ||||
Impl &operator=(const Impl &) = delete; | Impl &operator=(const Impl &) = delete; | ||||
void DisconnectedPeer(NodeId peer) { | void DisconnectedPeer(NodeId peer) { | ||||
auto &index = m_index.get<ByPeer>(); | auto &index = m_index.get<ByPeer>(); | ||||
auto it = | auto it = | ||||
index.lower_bound(ByPeerView{peer, false, TxId(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 | ||||
// case, this is the last iteration of the loop (denote this by | // case, this is the last iteration of the loop (denote this by | ||||
// setting it_next to end()). | // setting it_next to end()). | ||||
// - 'it' is not the only non-COMPLETED announcement for its txid. | // - 'it' is not the only non-COMPLETED announcement for its txid. | ||||
Show All 22 Lines | void DisconnectedPeer(NodeId peer) { | ||||
// 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 ForgetTxId(const TxId &txid) { | void ForgetTxId(const uint256 &txid) { | ||||
auto it = m_index.get<ByTxId>().lower_bound( | auto it = m_index.get<ByTxId>().lower_bound( | ||||
ByTxIdView{txid, State::CANDIDATE_DELAYED, 0}); | ByTxIdView{txid, State::CANDIDATE_DELAYED, 0}); | ||||
while (it != m_index.get<ByTxId>().end() && it->m_txid == txid) { | while (it != m_index.get<ByTxId>().end() && it->m_txid == txid) { | ||||
it = Erase<ByTxId>(it); | it = Erase<ByTxId>(it); | ||||
} | } | ||||
} | } | ||||
void ReceivedInv(NodeId peer, const TxId &txid, bool preferred, | void ReceivedInv(NodeId peer, const uint256 &txid, bool preferred, | ||||
std::chrono::microseconds reqtime) { | std::chrono::microseconds reqtime) { | ||||
// Bail out if we already have a CANDIDATE_BEST announcement for this | // Bail out if we already have a CANDIDATE_BEST announcement for this | ||||
// (txid, peer) combination. The case where there is a | // (txid, 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, txid})) { | if (m_index.get<ByPeer>().count(ByPeerView{peer, true, txid})) { | ||||
return; | return; | ||||
Show All 10 Lines | void ReceivedInv(NodeId peer, const uint256 &txid, 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 TxIds to request now from peer. | //! Find the TxIds to request now from peer. | ||||
std::vector<TxId> | std::vector<uint256> GetRequestable(NodeId peer, | ||||
GetRequestable(NodeId peer, std::chrono::microseconds now, | std::chrono::microseconds now, | ||||
std::vector<std::pair<NodeId, TxId>> *expired) { | clearExpiredFun clearExpired, | ||||
emplaceExpiredFun emplaceExpired) { | |||||
// Move time. | // Move time. | ||||
SetTimePoint(now, expired); | 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, TxId(uint256::ZERO)}); | ByPeerView{peer, true, uint256(uint256::ZERO)}); | ||||
while (it_peer != m_index.get<ByPeer>().end() && | while (it_peer != m_index.get<ByPeer>().end() && | ||||
it_peer->m_peer == peer && | it_peer->m_peer == peer && | ||||
it_peer->GetState() == State::CANDIDATE_BEST) { | it_peer->GetState() == State::CANDIDATE_BEST) { | ||||
selected.emplace_back(&*it_peer); | selected.emplace_back(&*it_peer); | ||||
++it_peer; | ++it_peer; | ||||
} | } | ||||
// Sort by sequence number. | // Sort by sequence number. | ||||
std::sort(selected.begin(), selected.end(), | std::sort(selected.begin(), selected.end(), | ||||
[](const Announcement *a, const Announcement *b) { | [](const Announcement *a, const Announcement *b) { | ||||
return a->m_sequence < b->m_sequence; | return a->m_sequence < b->m_sequence; | ||||
}); | }); | ||||
// Convert to TxId and return. | // Convert to TxId and return. | ||||
std::vector<TxId> ret; | std::vector<uint256> ret; | ||||
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_txid; }); | [](const Announcement *ann) { return ann->m_txid; }); | ||||
return ret; | return ret; | ||||
} | } | ||||
void RequestedTx(NodeId peer, const TxId &txid, | void RequestedTx(NodeId peer, const uint256 &txid, | ||||
std::chrono::microseconds expiry) { | std::chrono::microseconds expiry) { | ||||
auto it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txid}); | auto it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txid}); | ||||
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 RequestedTx | // _DELAYED instead. If the caller only ever invokes RequestedTx | ||||
// with the values returned by GetRequestable, and no other | // with the values returned by GetRequestable, and no other | ||||
// non-const functions other than ForgetTxHash and GetRequestable in | // non-const functions other than ForgetTxHash and GetRequestable in | ||||
// between, this branch will never execute (as txids returned by | // between, this branch will never execute (as txids returned by | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | void RequestedTx(NodeId peer, const uint256 &txid, | ||||
} | } | ||||
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 TxId &txid) { | void ReceivedResponse(NodeId peer, const uint256 &txid) { | ||||
// We need to search the ByPeer index for both (peer, false, txid) and | // We need to search the ByPeer index for both (peer, false, txid) and | ||||
// (peer, true, txid). | // (peer, true, txid). | ||||
auto it = m_index.get<ByPeer>().find(ByPeerView{peer, false, txid}); | auto it = m_index.get<ByPeer>().find(ByPeerView{peer, false, txid}); | ||||
if (it == m_index.get<ByPeer>().end()) { | if (it == m_index.get<ByPeer>().end()) { | ||||
it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txid}); | it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txid}); | ||||
} | } | ||||
if (it != m_index.get<ByPeer>().end()) { | if (it != m_index.get<ByPeer>().end()) { | ||||
MakeCompleted(m_index.project<ByTxId>(it)); | MakeCompleted(m_index.project<ByTxId>(it)); | ||||
Show All 24 Lines | size_t Count(NodeId peer) const { | ||||
} | } | ||||
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 { return m_index.size(); } | ||||
uint64_t ComputePriority(const TxId &txid, NodeId peer, | uint64_t ComputePriority(const uint256 &txid, NodeId peer, | ||||
bool preferred) const { | bool preferred) const { | ||||
// 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(txid, peer, preferred)}; | return uint64_t{m_computer(txid, peer, preferred)}; | ||||
} | } | ||||
}; | }; | ||||
TxRequestTracker::TxRequestTracker(bool deterministic) | TxRequestTracker::TxRequestTracker(bool deterministic) | ||||
: m_impl{std::make_unique<TxRequestTracker::Impl>(deterministic)} {} | : m_impl{std::make_unique<TxRequestTracker::Impl>(deterministic)} {} | ||||
Show All 40 Lines | |||||
void TxRequestTracker::ReceivedResponse(NodeId peer, const TxId &txid) { | void TxRequestTracker::ReceivedResponse(NodeId peer, const TxId &txid) { | ||||
m_impl->ReceivedResponse(peer, txid); | m_impl->ReceivedResponse(peer, txid); | ||||
} | } | ||||
std::vector<TxId> TxRequestTracker::GetRequestable( | std::vector<TxId> TxRequestTracker::GetRequestable( | ||||
NodeId peer, std::chrono::microseconds now, | NodeId peer, std::chrono::microseconds now, | ||||
std::vector<std::pair<NodeId, TxId>> *expired) { | std::vector<std::pair<NodeId, TxId>> *expired) { | ||||
return m_impl->GetRequestable(peer, now, expired); | clearExpiredFun clearExpired = [expired]() { | ||||
if (expired) { | |||||
expired->clear(); | |||||
} | |||||
}; | |||||
emplaceExpiredFun emplaceExpired = [expired](const NodeId &nodeid, | |||||
const uint256 &txid) { | |||||
if (expired) { | |||||
expired->emplace_back(nodeid, TxId(txid)); | |||||
} | |||||
}; | |||||
std::vector<uint256> hashes = | |||||
m_impl->GetRequestable(peer, now, clearExpired, emplaceExpired); | |||||
return std::vector<TxId>(hashes.begin(), hashes.end()); | |||||
} | } | ||||
uint64_t TxRequestTracker::ComputePriority(const TxId &txid, NodeId peer, | uint64_t TxRequestTracker::ComputePriority(const TxId &txid, NodeId peer, | ||||
bool preferred) const { | bool preferred) const { | ||||
return m_impl->ComputePriority(txid, peer, preferred); | return m_impl->ComputePriority(txid, peer, preferred); | ||||
} | } |