Skip to content

Commit af7402c

Browse files
committed
[refactor] make TxOrphanage keep itself trimmed
1 parent d1fac25 commit af7402c

File tree

6 files changed

+124
-165
lines changed

6 files changed

+124
-165
lines changed

src/bench/txorphanage.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ static void OrphanageSinglePeerEviction(benchmark::Bench& bench)
9797
// Lastly, add the large transaction.
9898
const auto num_announcements_before_trim{orphanage->CountAnnouncements()};
9999
assert(orphanage->AddTx(large_tx, peer));
100-
orphanage->LimitOrphans();
101100

102101
// If there are multiple peers, note that they all have the same DoS score. We will evict only 1 item at a time for each new DoSiest peer.
103102
const auto num_announcements_after_trim{orphanage->CountAnnouncements()};
@@ -178,7 +177,6 @@ static void OrphanageMultiPeerEviction(benchmark::Bench& bench)
178177
const auto num_announcements_before_trim{orphanage->CountAnnouncements()};
179178
// There is a small gap between the total usage and the max usage. Add a transaction to fill it.
180179
assert(orphanage->AddTx(last_tx, 0));
181-
orphanage->LimitOrphans();
182180

183181
// If there are multiple peers, note that they all have the same DoS score. We will evict only 1 item at a time for each new DoSiest peer.
184182
const auto num_evicted{num_announcements_before_trim - orphanage->CountAnnouncements() + 1};

src/node/txdownloadman_impl.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid,
188188

189189
if (MaybeAddOrphanResolutionCandidate(unique_parents, *wtxid, peer, now)) {
190190
m_orphanage->AddAnnouncer(orphan_tx->GetWitnessHash(), peer);
191-
m_orphanage->LimitOrphans();
192191
}
193192

194193
// Return even if the peer isn't an orphan resolution candidate. This would be caught by AlreadyHaveTx.
@@ -419,9 +418,6 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
419418
// Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
420419
m_txrequest.ForgetTxHash(tx.GetHash());
421420
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
422-
423-
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
424-
m_orphanage->LimitOrphans();
425421
} else {
426422
unique_parents.clear();
427423
LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",

src/node/txorphanage.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,18 @@ class TxOrphanageImpl final : public TxOrphanage {
172172
template<typename Tag>
173173
void Erase(Iter<Tag> it);
174174

175+
/** Erase by wtxid. */
176+
bool EraseTxInternal(const Wtxid& wtxid);
177+
175178
/** Check if there is exactly one announcement with the same wtxid as it. */
176179
bool IsUnique(Iter<ByWtxid> it) const;
177180

178181
/** Check if the orphanage needs trimming. */
179182
bool NeedsTrim() const;
183+
184+
/** Limit the orphanage to MaxGlobalLatencyScore and MaxGlobalUsage. */
185+
void LimitOrphans();
186+
180187
public:
181188
TxOrphanageImpl() = default;
182189
TxOrphanageImpl(Count max_global_ann, Usage reserved_peer_usage) :
@@ -216,7 +223,6 @@ class TxOrphanageImpl final : public TxOrphanage {
216223
bool EraseTx(const Wtxid& wtxid) override;
217224
void EraseForPeer(NodeId peer) override;
218225
void EraseForBlock(const CBlock& block) override;
219-
void LimitOrphans() override;
220226
std::vector<std::pair<Wtxid, NodeId>> AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) override;
221227
bool HaveTxToReconsider(NodeId peer) override;
222228
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const override;
@@ -332,6 +338,9 @@ bool TxOrphanageImpl::AddTx(const CTransactionRef& tx, NodeId peer)
332338
peer, txid.ToString(), wtxid.ToString());
333339
Assume(!IsUnique(iter));
334340
}
341+
342+
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
343+
LimitOrphans();
335344
return brand_new;
336345
}
337346

@@ -360,10 +369,13 @@ bool TxOrphanageImpl::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
360369
peer, txid.ToString(), wtxid.ToString());
361370

362371
Assume(!IsUnique(iter));
372+
373+
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
374+
LimitOrphans();
363375
return true;
364376
}
365377

366-
bool TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
378+
bool TxOrphanageImpl::EraseTxInternal(const Wtxid& wtxid)
367379
{
368380
auto& index_by_wtxid = m_orphans.get<ByWtxid>();
369381

@@ -378,12 +390,21 @@ bool TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
378390
Erase<ByWtxid>(it++);
379391
num_ann += 1;
380392
}
381-
382393
LogDebug(BCLog::TXPACKAGES, "removed orphan tx %s (wtxid=%s) (%u announcements)\n", txid.ToString(), wtxid.ToString(), num_ann);
383394

384395
return true;
385396
}
386397

398+
bool TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
399+
{
400+
const auto ret = EraseTxInternal(wtxid);
401+
402+
// Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
403+
LimitOrphans();
404+
405+
return ret;
406+
}
407+
387408
/** Erase all entries by this peer. */
388409
void TxOrphanageImpl::EraseForPeer(NodeId peer)
389410
{
@@ -400,6 +421,9 @@ void TxOrphanageImpl::EraseForPeer(NodeId peer)
400421
Assume(!m_peer_orphanage_info.contains(peer));
401422

402423
if (num_ann > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", num_ann, peer);
424+
425+
// Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
426+
LimitOrphans();
403427
}
404428

405429
/** If the data structure needs trimming, evicts announcements by selecting the DoSiest peer and evicting its oldest
@@ -565,6 +589,7 @@ bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)
565589
auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});
566590
return it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider;
567591
}
592+
568593
void TxOrphanageImpl::EraseForBlock(const CBlock& block)
569594
{
570595
std::set<Wtxid> wtxids_to_erase;
@@ -583,13 +608,19 @@ void TxOrphanageImpl::EraseForBlock(const CBlock& block)
583608

584609
unsigned int num_erased{0};
585610
for (const auto& wtxid : wtxids_to_erase) {
586-
num_erased += EraseTx(wtxid) ? 1 : 0;
611+
// Don't use EraseTx here because it calls LimitOrphans and announcements deleted in that call are not reflected
612+
// in its return result. Waiting until the end to do LimitOrphans helps save repeated computation and allows us
613+
// to check that num_erased is what we expect.
614+
num_erased += EraseTxInternal(wtxid) ? 1 : 0;
587615
}
588616

589617
if (num_erased != 0) {
590618
LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", num_erased);
591619
}
592620
Assume(wtxids_to_erase.size() == num_erased);
621+
622+
// Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
623+
LimitOrphans();
593624
}
594625

595626
/** Get all children that spend from this tx and were received from nodeid. Sorted from most
@@ -697,6 +728,8 @@ void TxOrphanageImpl::SanityCheck() const
697728
const auto summed_peer_latency_score = std::accumulate(m_peer_orphanage_info.begin(), m_peer_orphanage_info.end(),
698729
TxOrphanage::Count{0}, [](TxOrphanage::Count sum, const auto pair) { return sum + pair.second.m_total_latency_score; });
699730
assert(summed_peer_latency_score >= m_unique_rounded_input_scores + m_orphans.size());
731+
732+
assert(!NeedsTrim());
700733
}
701734

702735
TxOrphanage::Count TxOrphanageImpl::MaxGlobalLatencyScore() const { return m_max_global_latency_score; }

src/node/txorphanage.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ class TxOrphanage {
8888
/** Erase all orphans included in or invalidated by a new block */
8989
virtual void EraseForBlock(const CBlock& block) = 0;
9090

91-
/** Limit the orphanage to MaxGlobalLatencyScore and MaxGlobalUsage. */
92-
virtual void LimitOrphans() = 0;
93-
9491
/** Add any orphans that list a particular tx as a parent into the from peer's work set */
9592
virtual std::vector<std::pair<Wtxid, NodeId>> AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) = 0;
9693

0 commit comments

Comments
 (0)