Skip to content

Commit eeb0b31

Browse files
committed
Merge bitcoin/bitcoin#32941: p2p: TxOrphanage revamp cleanups
c0642e5 [fuzz] fix latency score check in txorphan_protected (glozow) 3d4d4f0 scripted-diff: rename "ann" variables to "latency_score" (monlovesmango) 3b92448 [doc] comment fixups for orphanage changes (glozow) 1384dba [config] emit warning for -maxorphantx, but allow it to be set (glozow) b10c55b fix up TxOrphanage lower_bound sanity checks (glozow) cfd71c6 scripted-diff: rename TxOrphanage outpoints index (glozow) edb97bb [logging] add logs for inner loop of LimitOrphans (glozow) 8a58d0e scripted-diff: rename OrphanTxBase to OrphanInfo (glozow) cc50f2f [cleanup] replace TxOrphanage::Size() with CountUniqueOrphans (glozow) ed24e01 [optimization] Maintain at most 1 reconsiderable announcement per wtxid (Pieter Wuille) af7402c [refactor] make TxOrphanage keep itself trimmed (glozow) d1fac25 [doc] 31829 release note (glozow) Pull request description: Followup to #31829: - Release notes - Have the orphanage auto-trim itself whenever necessary (and test changes) bitcoin/bitcoin#31829 (comment) - Reduce duplicate reconsiderations by keeping track of which txns are already reconsiderable so we only mark it for reconsideration for 1 peer at a time bitcoin/bitcoin#31829 (comment) - Rename `OrphanTxBase` to `OrphanInfo` - Get rid of `Size()` method by replacing all calls with `CountUniqueOrphans` - Rename outpoints index since they point to wtxids, not iterators bitcoin/bitcoin#31829 (comment) - Add more logging in the `LimitOrphans` inner loop to make it easy to see which peers are being trimmed bitcoin/bitcoin#31829 (comment) ACKs for top commit: sipa: utACK c0642e5 marcofleon: Nice, ACK c0642e5 Tree-SHA512: f298eae92cf906ed5e4f15a24eeffa7b9e620bcff457772cd77522dd9f0b3b183ffc976871b1b0e6fe93009e64877d518e53d4b9e186e0df58fc16d17f6de90a
2 parents 0cb1ed2 + c0642e5 commit eeb0b31

14 files changed

+284
-276
lines changed

doc/release-notes-31829.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
P2P
2+
3+
- The transaction orphanage, which holds transactions with missing inputs temporarily while the node attempts to fetch
4+
its parents, now has improved Denial of Service protections. Previously, it enforced a maximum number of unique
5+
transactions (default 100, configurable using `-maxorphantx`). Now, its limits are as follows: the number of entries
6+
(unique by wtxid and peer), plus each unique transaction's input count divided by 10, must not exceed 3,000. The total
7+
weight of unique transactions must not exceed 404,000 Wu multiplied by the number of peers.
8+
9+
- The `-maxorphantx` option no longer has any effect, since the orphanage no longer limits the number of unique
10+
transactions. Users should remove this configuration option if they were using it, as the setting will cause an
11+
error in future versions when it is no longer recognized.

src/bench/txorphanage.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static void OrphanageSinglePeerEviction(benchmark::Bench& bench)
6868
auto large_tx = MakeTransactionBulkedTo(1, MAX_STANDARD_TX_WEIGHT, det_rand);
6969
assert(GetTransactionWeight(*large_tx) <= MAX_STANDARD_TX_WEIGHT);
7070

71-
const auto orphanage{node::MakeTxOrphanage(/*max_global_ann=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
71+
const auto orphanage{node::MakeTxOrphanage(/*max_global_latency_score=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
7272

7373
// Populate the orphanage. To maximize the number of evictions, first fill up with tiny transactions, then add a huge one.
7474
NodeId peer{0};
@@ -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()};
@@ -132,7 +131,7 @@ static void OrphanageMultiPeerEviction(benchmark::Bench& bench)
132131
indexes.resize(NUM_UNIQUE_TXNS);
133132
std::iota(indexes.begin(), indexes.end(), 0);
134133

135-
const auto orphanage{node::MakeTxOrphanage(/*max_global_ann=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
134+
const auto orphanage{node::MakeTxOrphanage(/*max_global_latency_score=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
136135
// Every peer sends the same transactions, all from shared_txs.
137136
// Each peer has 1 or 2 assigned transactions, which they must place as the last and second-to-last positions.
138137
// The assignments ensure that every transaction is in some peer's last 2 transactions, and is thus remains in the orphanage until the end of LimitOrphans.
@@ -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};
@@ -191,7 +189,7 @@ static void OrphanageMultiPeerEviction(benchmark::Bench& bench)
191189
static void OrphanageEraseAll(benchmark::Bench& bench, bool block_or_disconnect)
192190
{
193191
FastRandomContext det_rand{true};
194-
const auto orphanage{node::MakeTxOrphanage(/*max_global_ann=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
192+
const auto orphanage{node::MakeTxOrphanage(/*max_global_latency_score=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
195193
// This is an unrealistically large number of inputs for a block, as there is almost no room given to witness data,
196194
// outputs, and overhead for individual transactions. The entire block is 1 transaction with 20,000 inputs.
197195
constexpr unsigned int NUM_BLOCK_INPUTS{MAX_BLOCK_WEIGHT / APPROX_WEIGHT_PER_INPUT};

src/init.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
490490
argsman.AddArg("-allowignoredconf", strprintf("For backwards compatibility, treat an unused %s file in the datadir as a warning, not an error.", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
491491
argsman.AddArg("-loadblock=<file>", "Imports blocks from external file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
492492
argsman.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE_MB), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
493+
// TODO: remove in v31.0
494+
argsman.AddArg("-maxorphantx=<n>", strprintf("(Removed option, see release notes)"), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
493495
argsman.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY_HOURS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
494496
argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet3: %s, testnet4: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnet4ChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
495497
argsman.AddArg("-par=<n>", strprintf("Set the number of script verification threads (0 = auto, up to %d, <0 = leave that many cores free, default: %d)",
@@ -893,6 +895,11 @@ bool AppInitParameterInteraction(const ArgsManager& args)
893895
InitWarning(_("Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated. They will be removed in a future version."));
894896
}
895897

898+
// We no longer limit the orphanage based on number of transactions but keep the option to warn users who still have it in their config.
899+
if (args.IsArgSet("-maxorphantx")) {
900+
InitWarning(_("Option '-maxorphantx' is set but no longer has any effect (see release notes). Please remove it from your configuration."));
901+
}
902+
896903
// Error if network-specific options (-addnode, -connect, etc) are
897904
// specified in default section of config file, but not overridden
898905
// on the command line or in this chain's section of the config file.

src/net_processing.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ class PeerManagerImpl final : public PeerManager
533533
std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override
534534
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
535535
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
536-
std::vector<node::TxOrphanage::OrphanTxBase> GetOrphanTransactions() override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
536+
std::vector<node::TxOrphanage::OrphanInfo> GetOrphanTransactions() override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
537537
PeerManagerInfo GetInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
538538
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
539539
void RelayTransaction(const Txid& txid, const Wtxid& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
@@ -1754,7 +1754,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
17541754
return true;
17551755
}
17561756

1757-
std::vector<node::TxOrphanage::OrphanTxBase> PeerManagerImpl::GetOrphanTransactions()
1757+
std::vector<node::TxOrphanage::OrphanInfo> PeerManagerImpl::GetOrphanTransactions()
17581758
{
17591759
LOCK(m_tx_download_mutex);
17601760
return m_txdownloadman.GetOrphanTransactions();

src/net_processing.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
109109
/** Get statistics from node state */
110110
virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const = 0;
111111

112-
virtual std::vector<node::TxOrphanage::OrphanTxBase> GetOrphanTransactions() = 0;
112+
virtual std::vector<node::TxOrphanage::OrphanInfo> GetOrphanTransactions() = 0;
113113

114114
/** Get peer manager info. */
115115
virtual PeerManagerInfo GetInfo() const = 0;

src/node/txdownloadman.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class TxDownloadManager {
170170
void CheckIsEmpty(NodeId nodeid) const;
171171

172172
/** Wrapper for TxOrphanage::GetOrphanTransactions */
173-
std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() const;
173+
std::vector<TxOrphanage::OrphanInfo> GetOrphanTransactions() const;
174174
};
175175
} // namespace node
176176
#endif // BITCOIN_NODE_TXDOWNLOADMAN_H

src/node/txdownloadman_impl.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ void TxDownloadManager::CheckIsEmpty(NodeId nodeid) const
8383
{
8484
m_impl->CheckIsEmpty(nodeid);
8585
}
86-
std::vector<TxOrphanage::OrphanTxBase> TxDownloadManager::GetOrphanTransactions() const
86+
std::vector<TxOrphanage::OrphanInfo> TxDownloadManager::GetOrphanTransactions() const
8787
{
8888
return m_impl->GetOrphanTransactions();
8989
}
@@ -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",
@@ -576,11 +572,11 @@ void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
576572
void TxDownloadManagerImpl::CheckIsEmpty()
577573
{
578574
assert(m_orphanage->TotalOrphanUsage() == 0);
579-
assert(m_orphanage->Size() == 0);
575+
assert(m_orphanage->CountUniqueOrphans() == 0);
580576
assert(m_txrequest.Size() == 0);
581577
assert(m_num_wtxid_peers == 0);
582578
}
583-
std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
579+
std::vector<TxOrphanage::OrphanInfo> TxDownloadManagerImpl::GetOrphanTransactions() const
584580
{
585581
return m_orphanage->GetOrphanTransactions();
586582
}

src/node/txdownloadman_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ class TxDownloadManagerImpl {
188188
void CheckIsEmpty();
189189
void CheckIsEmpty(NodeId nodeid);
190190

191-
std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() const;
191+
std::vector<TxOrphanage::OrphanInfo> GetOrphanTransactions() const;
192192

193193
protected:
194194
/** Helper for getting deduplicated vector of Txids in vin. */

0 commit comments

Comments
 (0)