17 const Txid& hash = tx->GetHash();
18 const Wtxid& wtxid = tx->GetWitnessHash();
42 for (
const CTxIn& txin : tx->vin) {
48 peer_info.m_total_usage += sz;
59 Assume(!it->second.announcers.empty());
60 const auto ret = it->second.announcers.insert(peer);
63 peer_info.m_total_usage += it->second.GetUsage();
74 std::map<Wtxid, OrphanTx>::iterator it =
m_orphans.find(wtxid);
77 for (
const CTxIn& txin : it->second.tx->vin)
82 itPrev->second.erase(it);
83 if (itPrev->second.empty())
87 const auto tx_size{it->second.GetUsage()};
91 for (
const auto& peer : it->second.announcers) {
94 peer_it->second.m_total_usage -= tx_size;
98 size_t old_pos = it->second.list_pos;
105 it_last->second.list_pos = old_pos;
107 const auto& txid = it->second.tx->GetHash();
124 std::map<Wtxid, OrphanTx>::iterator iter =
m_orphans.begin();
128 auto& [wtxid, orphan] = *iter++;
129 auto orphan_it = orphan.announcers.find(peer);
130 if (orphan_it != orphan.announcers.end()) {
131 orphan.announcers.erase(peer);
135 if (orphan.announcers.empty()) {
136 nErased +=
EraseTx(orphan.tx->GetWitnessHash());
145 unsigned int nEvicted = 0;
146 auto nNow{Now<NodeSeconds>()};
151 std::map<Wtxid, OrphanTx>::iterator iter =
m_orphans.begin();
154 std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
155 if (maybeErase->second.nTimeExpire <= nNow) {
156 nErased +=
EraseTx(maybeErase->first);
158 nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
177 for (
unsigned int i = 0; i < tx.
vout.size(); i++) {
180 for (
const auto& elem : it_by_prev->second) {
182 if (!
Assume(!elem->second.announcers.empty()))
continue;
187 auto announcer_iter = std::begin(elem->second.announcers);
188 std::advance(announcer_iter, rng.
randrange(elem->second.announcers.size()));
189 auto announcer = *(announcer_iter);
193 std::set<Wtxid>& orphan_work_set =
m_peer_orphanage_info.try_emplace(announcer).first->second.m_work_set;
195 orphan_work_set.insert(elem->first);
211 return it !=
m_orphans.end() ? it->second.tx :
nullptr;
217 return (it !=
m_orphans.end() && it->second.announcers.contains(peer));
225 auto& work_set = peer_it->second.m_work_set;
226 while (!work_set.empty()) {
228 work_set.erase(work_set.begin());
230 const auto orphan_it =
m_orphans.find(wtxid);
232 return orphan_it->second.tx;
243 auto& work_set = peer_it->second.m_work_set;
244 return !work_set.empty();
249 std::vector<Wtxid> vOrphanErase;
255 for (
const auto& txin : tx.
vin) {
258 for (
auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
266 if (vOrphanErase.size()) {
268 for (
const auto& orphanHash : vOrphanErase) {
269 nErased +=
EraseTx(orphanHash);
279 std::vector<OrphanMap::iterator> iters;
282 for (
unsigned int i = 0; i < parent->vout.size(); i++) {
285 for (
const auto& elem : it_by_prev->second) {
286 if (elem->second.announcers.contains(nodeid)) {
287 iters.emplace_back(elem);
296 std::sort(iters.begin(), iters.end(), [](
const auto& lhs,
const auto& rhs) {
297 if (lhs->second.nTimeExpire == rhs->second.nTimeExpire) {
298 return &(*lhs) < &(*rhs);
300 return lhs->second.nTimeExpire > rhs->second.nTimeExpire;
304 iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
307 std::vector<CTransactionRef> children_found;
308 children_found.reserve(iters.size());
309 for (
const auto& child_iter : iters) {
310 children_found.emplace_back(child_iter->second.tx);
312 return children_found;
317 std::vector<OrphanTxBase>
ret;
320 ret.push_back({o.second.tx, o.second.announcers, o.second.nTimeExpire});
328 unsigned int counted_total_announcements{0};
330 unsigned int counted_total_usage{0};
333 std::map<NodeId, unsigned int> counted_size_per_peer;
335 for (
const auto& [wtxid, orphan] :
m_orphans) {
336 counted_total_announcements += orphan.announcers.size();
337 counted_total_usage += orphan.GetUsage();
339 Assume(!orphan.announcers.empty());
340 for (
const auto& peer : orphan.announcers) {
341 auto& count_peer_entry = counted_size_per_peer.try_emplace(peer).first->second;
342 count_peer_entry += orphan.GetUsage();
356 auto it_counted = counted_size_per_peer.find(peerid);
357 if (it_counted == counted_size_per_peer.end()) {
358 Assume(info.m_total_usage == 0);
360 Assume(it_counted->second == info.m_total_usage);
#define Assume(val)
Assume is the identity function.
std::vector< CTransactionRef > vtx
An outpoint - a combination of a transaction hash and an index n into its vout.
The basic transaction that is broadcasted on the network and contained in blocks.
const std::vector< CTxOut > vout
const Wtxid & GetWitnessHash() const LIFETIMEBOUND
const Txid & GetHash() const LIFETIMEBOUND
const std::vector< CTxIn > vin
An input of a transaction.
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
unsigned int m_total_orphan_usage
Total usage (weight) of all entries in m_orphans.
bool AddTx(const CTransactionRef &tx, NodeId peer)
Add a new orphan transaction.
void EraseForPeer(NodeId peer)
Maybe erase all orphans announced by a peer (eg, after that peer disconnects).
std::vector< OrphanTxBase > GetOrphanTransactions() const
void LimitOrphans(unsigned int max_orphans, FastRandomContext &rng)
Limit the orphanage to the given maximum.
int EraseTx(const Wtxid &wtxid)
Erase an orphan by wtxid.
void EraseForBlock(const CBlock &block)
Erase all orphans included in or invalidated by a new block.
std::map< NodeId, PeerOrphanInfo > m_peer_orphanage_info
unsigned int m_total_announcements
Total number of <peer, tx> pairs.
std::map< COutPoint, std::set< OrphanMap::iterator, IteratorComparator > > m_outpoint_to_orphan_it
Index from the parents' COutPoint into the m_orphans.
std::vector< OrphanMap::iterator > m_orphan_list
Orphan transactions in vector for quick random eviction.
CTransactionRef GetTx(const Wtxid &wtxid) const
bool AddAnnouncer(const Wtxid &wtxid, NodeId peer)
Add an additional announcer to an orphan if it exists.
std::vector< CTransactionRef > GetChildrenFromSamePeer(const CTransactionRef &parent, NodeId nodeid) const
Get all children that spend from this tx and were received from nodeid.
std::map< Wtxid, OrphanTx > m_orphans
Map from wtxid to orphan transaction record.
void AddChildrenToWorkSet(const CTransaction &tx, FastRandomContext &rng)
Add any orphans that list a particular tx as a parent into the from peer's work set.
void SanityCheck() const
Check consistency between PeerOrphanInfo and m_orphans.
bool HaveTx(const Wtxid &wtxid) const
Check if we already have an orphan transaction (by wtxid only)
bool HaveTxToReconsider(NodeId peer)
Does this peer have any work to do?
CTransactionRef GetTxToReconsider(NodeId peer)
Extract a transaction from a peer's work set Returns nullptr if there are no transactions to work on.
NodeSeconds m_next_sweep
Timestamp for the next scheduled sweep of expired orphans.
bool HaveTxFromPeer(const Wtxid &wtxid, NodeId peer) const
Check if a {tx, peer} exists in the orphanage.
std::string ToString() const
constexpr const std::byte * begin() const
static int32_t GetTransactionWeight(const CTransaction &tx)
#define LogDebug(category,...)
static constexpr int32_t MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we're willing to relay/mine.
std::shared_ptr< const CTransaction > CTransactionRef
static time_point now() noexcept
Return current system time or mocked time, if set.
static constexpr auto ORPHAN_TX_EXPIRE_TIME
Expiration time for orphan transactions.
static constexpr auto ORPHAN_TX_EXPIRE_INTERVAL
Minimum time between orphan transactions expire time checks.