Skip to content

Commit 3da6d7f

Browse files
committed
[prep/refactor] make TxOrphanage a virtual class implemented by TxOrphanageImpl
1 parent 77ebe8f commit 3da6d7f

File tree

8 files changed

+329
-301
lines changed

8 files changed

+329
-301
lines changed

src/node/txdownloadman_impl.cpp

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void TxDownloadManagerImpl::ActiveTipChange()
9797

9898
void TxDownloadManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
9999
{
100-
m_orphanage.EraseForBlock(*pblock);
100+
m_orphanage->EraseForBlock(*pblock);
101101

102102
for (const auto& ptx : pblock->vtx) {
103103
RecentConfirmedTransactionsFilter().insert(ptx->GetHash().ToUint256());
@@ -137,7 +137,7 @@ bool TxDownloadManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_rec
137137
// While we won't query by txid, we can try to "guess" what the wtxid is based on the txid.
138138
// A non-segwit transaction's txid == wtxid. Query this txhash "casted" to a wtxid. This will
139139
// help us find non-segwit transactions, saving bandwidth, and should have no false positives.
140-
if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true;
140+
if (m_orphanage->HaveTx(Wtxid::FromUint256(hash))) return true;
141141

142142
if (include_reconsiderable && RecentRejectsReconsiderableFilter().contains(hash)) return true;
143143

@@ -157,7 +157,7 @@ void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnect
157157

158158
void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
159159
{
160-
m_orphanage.EraseForPeer(nodeid);
160+
m_orphanage->EraseForPeer(nodeid);
161161
m_txrequest.DisconnectedPeer(nodeid);
162162

163163
if (auto it = m_peer_info.find(nodeid); it != m_peer_info.end()) {
@@ -174,7 +174,7 @@ bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid,
174174
// - exists in orphanage
175175
// - peer can be an orphan resolution candidate
176176
if (const auto* wtxid = std::get_if<Wtxid>(&gtxid)) {
177-
if (auto orphan_tx{m_orphanage.GetTx(*wtxid)}) {
177+
if (auto orphan_tx{m_orphanage->GetTx(*wtxid)}) {
178178
auto unique_parents{GetUniqueParents(*orphan_tx)};
179179
std::erase_if(unique_parents, [&](const auto& txid) {
180180
return AlreadyHaveTx(txid, /*include_reconsiderable=*/false);
@@ -187,7 +187,7 @@ bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid,
187187
}
188188

189189
if (MaybeAddOrphanResolutionCandidate(unique_parents, *wtxid, peer, now)) {
190-
m_orphanage.AddAnnouncer(orphan_tx->GetWitnessHash(), peer);
190+
m_orphanage->AddAnnouncer(orphan_tx->GetWitnessHash(), peer);
191191
}
192192

193193
// Return even if the peer isn't an orphan resolution candidate. This would be caught by AlreadyHaveTx.
@@ -227,7 +227,7 @@ bool TxDownloadManagerImpl::MaybeAddOrphanResolutionCandidate(const std::vector<
227227
{
228228
auto it_peer = m_peer_info.find(nodeid);
229229
if (it_peer == m_peer_info.end()) return false;
230-
if (m_orphanage.HaveTxFromPeer(wtxid, nodeid)) return false;
230+
if (m_orphanage->HaveTxFromPeer(wtxid, nodeid)) return false;
231231

232232
const auto& peer_entry = m_peer_info.at(nodeid);
233233
const auto& info = peer_entry.m_connection_info;
@@ -305,7 +305,7 @@ std::optional<PackageToValidate> TxDownloadManagerImpl::Find1P1CPackage(const CT
305305
// children instead of the real one provided by the honest peer. Since we track all announcers
306306
// of an orphan, this does not exclude parent + orphan pairs that we happened to request from
307307
// different peers.
308-
const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)};
308+
const auto cpfp_candidates_same_peer{m_orphanage->GetChildrenFromSamePeer(ptx, nodeid)};
309309

310310
// These children should be sorted from newest to oldest. In the (probably uncommon) case
311311
// of children that replace each other, this helps us accept the highest feerate (probably the
@@ -327,9 +327,9 @@ void TxDownloadManagerImpl::MempoolAcceptedTx(const CTransactionRef& tx)
327327
m_txrequest.ForgetTxHash(tx->GetHash());
328328
m_txrequest.ForgetTxHash(tx->GetWitnessHash());
329329

330-
m_orphanage.AddChildrenToWorkSet(*tx, m_opts.m_rng);
330+
m_orphanage->AddChildrenToWorkSet(*tx, m_opts.m_rng);
331331
// If it came from the orphanage, remove it. No-op if the tx is not in txorphanage.
332-
m_orphanage.EraseTx(tx->GetWitnessHash());
332+
m_orphanage->EraseTx(tx->GetWitnessHash());
333333
}
334334

335335
std::vector<Txid> TxDownloadManagerImpl::GetUniqueParents(const CTransaction& tx)
@@ -398,7 +398,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
398398
const auto& wtxid = ptx->GetWitnessHash();
399399
// Potentially flip add_extra_compact_tx to false if tx is already in orphanage, which
400400
// means it was already added to vExtraTxnForCompact.
401-
add_extra_compact_tx &= !m_orphanage.HaveTx(wtxid);
401+
add_extra_compact_tx &= !m_orphanage->HaveTx(wtxid);
402402

403403
// If there is no candidate for orphan resolution, AddTx will not be called. This means
404404
// that if a peer is overloading us with invs and orphans, they will eventually not be
@@ -411,7 +411,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
411411

412412
for (const auto& nodeid : orphan_resolution_candidates) {
413413
if (MaybeAddOrphanResolutionCandidate(unique_parents, ptx->GetWitnessHash(), nodeid, now)) {
414-
m_orphanage.AddTx(ptx, nodeid);
414+
m_orphanage->AddTx(ptx, nodeid);
415415
}
416416
}
417417

@@ -422,7 +422,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
422422
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
423423
// Note that, if the orphanage reaches capacity, it's possible that we immediately evict
424424
// the transaction we just added.
425-
m_orphanage.LimitOrphans(m_opts.m_rng);
425+
m_orphanage->LimitOrphans(m_opts.m_rng);
426426
} else {
427427
unique_parents.clear();
428428
LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",
@@ -491,7 +491,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
491491

492492
// If the tx failed in ProcessOrphanTx, it should be removed from the orphanage unless the
493493
// tx was still missing inputs. If the tx was not in the orphanage, EraseTx does nothing and returns 0.
494-
if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage.EraseTx(ptx->GetWitnessHash()) > 0) {
494+
if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage->EraseTx(ptx->GetWitnessHash()) > 0) {
495495
LogDebug(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
496496
}
497497

@@ -561,28 +561,28 @@ std::pair<bool, std::optional<PackageToValidate>> TxDownloadManagerImpl::Receive
561561

562562
bool TxDownloadManagerImpl::HaveMoreWork(NodeId nodeid)
563563
{
564-
return m_orphanage.HaveTxToReconsider(nodeid);
564+
return m_orphanage->HaveTxToReconsider(nodeid);
565565
}
566566

567567
CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid)
568568
{
569-
return m_orphanage.GetTxToReconsider(nodeid);
569+
return m_orphanage->GetTxToReconsider(nodeid);
570570
}
571571

572572
void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
573573
{
574574
assert(m_txrequest.Count(nodeid) == 0);
575-
assert(m_orphanage.UsageByPeer(nodeid) == 0);
575+
assert(m_orphanage->UsageByPeer(nodeid) == 0);
576576
}
577577
void TxDownloadManagerImpl::CheckIsEmpty()
578578
{
579-
assert(m_orphanage.TotalOrphanUsage() == 0);
580-
assert(m_orphanage.Size() == 0);
579+
assert(m_orphanage->TotalOrphanUsage() == 0);
580+
assert(m_orphanage->Size() == 0);
581581
assert(m_txrequest.Size() == 0);
582582
assert(m_num_wtxid_peers == 0);
583583
}
584584
std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
585585
{
586-
return m_orphanage.GetOrphanTransactions();
586+
return m_orphanage->GetOrphanTransactions();
587587
}
588588
} // namespace node

src/node/txdownloadman_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class TxDownloadManagerImpl {
2222
TxDownloadOptions m_opts;
2323

2424
/** Manages unvalidated tx data (orphan transactions for which we are downloading ancestors). */
25-
TxOrphanage m_orphanage;
25+
std::unique_ptr<TxOrphanage> m_orphanage;
2626
/** Tracks candidates for requesting and downloading transaction data. */
2727
TxRequestTracker m_txrequest;
2828

@@ -128,7 +128,7 @@ class TxDownloadManagerImpl {
128128
return *m_lazy_recent_confirmed_transactions;
129129
}
130130

131-
TxDownloadManagerImpl(const TxDownloadOptions& options) : m_opts{options}, m_txrequest{options.m_deterministic_txrequest} {}
131+
TxDownloadManagerImpl(const TxDownloadOptions& options) : m_opts{options}, m_orphanage{MakeTxOrphanage()}, m_txrequest{options.m_deterministic_txrequest} {}
132132

133133
struct PeerInfo {
134134
/** Information relevant to scheduling tx requests. */

src/node/txorphanage.cpp

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,85 @@
1212

1313
#include <cassert>
1414

15-
namespace node{
16-
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
15+
namespace node {
16+
17+
class TxOrphanageImpl final : public TxOrphanage {
18+
private:
19+
struct OrphanTx : public OrphanTxBase {
20+
NodeSeconds nTimeExpire;
21+
size_t list_pos;
22+
};
23+
24+
/** Total usage (weight) of all entries in m_orphans. */
25+
int64_t m_total_orphan_usage{0};
26+
27+
/** Total number of <peer, tx> pairs. Can be larger than m_orphans.size() because multiple peers
28+
* may have announced the same orphan. */
29+
unsigned int m_total_announcements{0};
30+
31+
/** Map from wtxid to orphan transaction record. Limited by
32+
* DEFAULT_MAX_ORPHAN_TRANSACTIONS */
33+
std::map<Wtxid, OrphanTx> m_orphans;
34+
35+
struct PeerOrphanInfo {
36+
/** List of transactions that should be reconsidered: added to in AddChildrenToWorkSet,
37+
* removed from one-by-one with each call to GetTxToReconsider. The wtxids may refer to
38+
* transactions that are no longer present in orphanage; these are lazily removed in
39+
* GetTxToReconsider. */
40+
std::set<Wtxid> m_work_set;
41+
42+
/** Total weight of orphans for which this peer is an announcer.
43+
* If orphans are provided by different peers, its weight will be accounted for in each
44+
* PeerOrphanInfo, so the total of all peers' m_total_usage may be larger than
45+
* m_total_orphan_size. If a peer is removed as an announcer, even if the orphan still
46+
* remains in the orphanage, this number will be decremented. */
47+
int64_t m_total_usage{0};
48+
};
49+
std::map<NodeId, PeerOrphanInfo> m_peer_orphanage_info;
50+
51+
using OrphanMap = decltype(m_orphans);
52+
53+
struct IteratorComparator
54+
{
55+
template<typename I>
56+
bool operator()(const I& a, const I& b) const
57+
{
58+
return a->first < b->first;
59+
}
60+
};
61+
62+
/** Index from the parents' COutPoint into the m_orphans. Used
63+
* to remove orphan transactions from the m_orphans */
64+
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it;
65+
66+
/** Orphan transactions in vector for quick random eviction */
67+
std::vector<OrphanMap::iterator> m_orphan_list;
68+
69+
/** Timestamp for the next scheduled sweep of expired orphans */
70+
NodeSeconds m_next_sweep{0s};
71+
72+
public:
73+
bool AddTx(const CTransactionRef& tx, NodeId peer) override;
74+
bool AddAnnouncer(const Wtxid& wtxid, NodeId peer) override;
75+
CTransactionRef GetTx(const Wtxid& wtxid) const override;
76+
bool HaveTx(const Wtxid& wtxid) const override;
77+
bool HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const override;
78+
CTransactionRef GetTxToReconsider(NodeId peer) override;
79+
int EraseTx(const Wtxid& wtxid) override;
80+
void EraseForPeer(NodeId peer) override;
81+
void EraseForBlock(const CBlock& block) override;
82+
void LimitOrphans(FastRandomContext& rng) override;
83+
void AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) override;
84+
bool HaveTxToReconsider(NodeId peer) override;
85+
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const override;
86+
size_t Size() const override { return m_orphans.size(); }
87+
std::vector<OrphanTxBase> GetOrphanTransactions() const override;
88+
int64_t TotalOrphanUsage() const override { return m_total_orphan_usage; }
89+
int64_t UsageByPeer(NodeId peer) const override;
90+
void SanityCheck() const override;
91+
};
92+
93+
bool TxOrphanageImpl::AddTx(const CTransactionRef& tx, NodeId peer)
1794
{
1895
const Txid& hash = tx->GetHash();
1996
const Wtxid& wtxid = tx->GetWitnessHash();
@@ -53,7 +130,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
53130
return true;
54131
}
55132

56-
bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
133+
bool TxOrphanageImpl::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
57134
{
58135
const auto it = m_orphans.find(wtxid);
59136
if (it != m_orphans.end()) {
@@ -70,7 +147,7 @@ bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
70147
return false;
71148
}
72149

73-
int TxOrphanage::EraseTx(const Wtxid& wtxid)
150+
int TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
74151
{
75152
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
76153
if (it == m_orphans.end())
@@ -116,7 +193,7 @@ int TxOrphanage::EraseTx(const Wtxid& wtxid)
116193
return 1;
117194
}
118195

119-
void TxOrphanage::EraseForPeer(NodeId peer)
196+
void TxOrphanageImpl::EraseForPeer(NodeId peer)
120197
{
121198
// Zeroes out this peer's m_total_usage.
122199
m_peer_orphanage_info.erase(peer);
@@ -141,7 +218,7 @@ void TxOrphanage::EraseForPeer(NodeId peer)
141218
if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
142219
}
143220

144-
void TxOrphanage::LimitOrphans(FastRandomContext& rng)
221+
void TxOrphanageImpl::LimitOrphans(FastRandomContext& rng)
145222
{
146223
unsigned int nEvicted = 0;
147224
auto nNow{Now<NodeSeconds>()};
@@ -173,7 +250,7 @@ void TxOrphanage::LimitOrphans(FastRandomContext& rng)
173250
if (nEvicted > 0) LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
174251
}
175252

176-
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
253+
void TxOrphanageImpl::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
177254
{
178255
for (unsigned int i = 0; i < tx.vout.size(); i++) {
179256
const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
@@ -201,24 +278,25 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext
201278
}
202279
}
203280

204-
bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
281+
bool TxOrphanageImpl::HaveTx(const Wtxid& wtxid) const
205282
{
206283
return m_orphans.count(wtxid);
207284
}
208285

209-
CTransactionRef TxOrphanage::GetTx(const Wtxid& wtxid) const
286+
CTransactionRef TxOrphanageImpl::GetTx(const Wtxid& wtxid) const
210287
{
211288
auto it = m_orphans.find(wtxid);
212289
return it != m_orphans.end() ? it->second.tx : nullptr;
213290
}
214291

215-
bool TxOrphanage::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
292+
293+
bool TxOrphanageImpl::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
216294
{
217295
auto it = m_orphans.find(wtxid);
218296
return (it != m_orphans.end() && it->second.announcers.contains(peer));
219297
}
220298

221-
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
299+
CTransactionRef TxOrphanageImpl::GetTxToReconsider(NodeId peer)
222300
{
223301
auto peer_it = m_peer_orphanage_info.find(peer);
224302
if (peer_it == m_peer_orphanage_info.end()) return nullptr;
@@ -236,7 +314,7 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
236314
return nullptr;
237315
}
238316

239-
bool TxOrphanage::HaveTxToReconsider(NodeId peer)
317+
bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)
240318
{
241319
auto peer_it = m_peer_orphanage_info.find(peer);
242320
if (peer_it == m_peer_orphanage_info.end()) return false;
@@ -245,7 +323,7 @@ bool TxOrphanage::HaveTxToReconsider(NodeId peer)
245323
return !work_set.empty();
246324
}
247325

248-
void TxOrphanage::EraseForBlock(const CBlock& block)
326+
void TxOrphanageImpl::EraseForBlock(const CBlock& block)
249327
{
250328
std::vector<Wtxid> vOrphanErase;
251329

@@ -273,7 +351,7 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
273351
}
274352
}
275353

276-
std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
354+
std::vector<CTransactionRef> TxOrphanageImpl::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
277355
{
278356
// First construct a vector of iterators to ensure we do not return duplicates of the same tx
279357
// and so we can sort by nTimeExpire.
@@ -313,7 +391,7 @@ std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransac
313391
return children_found;
314392
}
315393

316-
std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() const
394+
std::vector<TxOrphanage::OrphanTxBase> TxOrphanageImpl::GetOrphanTransactions() const
317395
{
318396
std::vector<OrphanTxBase> ret;
319397
ret.reserve(m_orphans.size());
@@ -323,7 +401,13 @@ std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() cons
323401
return ret;
324402
}
325403

326-
void TxOrphanage::SanityCheck() const
404+
int64_t TxOrphanageImpl::UsageByPeer(NodeId peer) const
405+
{
406+
auto peer_it = m_peer_orphanage_info.find(peer);
407+
return peer_it == m_peer_orphanage_info.end() ? 0 : peer_it->second.m_total_usage;
408+
}
409+
410+
void TxOrphanageImpl::SanityCheck() const
327411
{
328412
// Check that cached m_total_announcements is correct
329413
unsigned int counted_total_announcements{0};
@@ -362,4 +446,10 @@ void TxOrphanage::SanityCheck() const
362446
}
363447
}
364448
}
449+
450+
std::unique_ptr<TxOrphanage> MakeTxOrphanage() noexcept
451+
{
452+
return std::make_unique<TxOrphanageImpl>();
453+
}
454+
365455
} // namespace node

0 commit comments

Comments
 (0)