Skip to content

Commit de11b0a

Browse files
committed
Reduce MAX_PEER_TX_ANNOUNCEMENTS for non-PF_RELAY peers
Maintaining up to 100000 INVs per peer is excessive, as that is far more than fits in a typical mempool. Also disable the "overload" penalty for PF_RELAY peers.
1 parent 242d164 commit de11b0a

File tree

5 files changed

+38
-6
lines changed

5 files changed

+38
-6
lines changed

doc/release-notes-19988.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
P2P changes
2+
-----------
3+
4+
The size of the set of transactions that peers have announced and we consider
5+
for requests has been reduced from 100000 to 5000 (per peer), and further
6+
announcements will be ignored when that limit is reached. If you need to
7+
dump (very) large batches of transactions, exceptions can be made for trusted
8+
peers using the "relay" network permission. For localhost for example it can
9+
be enabled using the command line option `[email protected]`.

src/net_permissions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const std::vector<std::string> NET_PERMISSIONS_DOC{
1212
"bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
1313
"noban (do not ban for misbehavior; implies download)",
1414
"forcerelay (relay transactions that are already in the mempool; implies relay)",
15-
"relay (relay even in -blocksonly mode)",
15+
"relay (relay even in -blocksonly mode, and unlimited transaction announcements)",
1616
"mempool (allow requesting BIP35 mempool contents)",
1717
"download (allow getheaders during IBD, no disconnect after maxuploadtarget limit)",
1818
"addr (responses to GETADDR avoid hitting the cache and contain random records with the most up-to-date info)"

src/net_permissions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ enum NetPermissionFlags {
1919
// Can query bloomfilter even if -peerbloomfilters is false
2020
PF_BLOOMFILTER = (1U << 1),
2121
// Relay and accept transactions from this peer, even if -blocksonly is true
22+
// This peer is also not subject to limits on how many transaction INVs are tracked
2223
PF_RELAY = (1U << 3),
2324
// Always relay transactions from this peer, even if already in mempool
2425
// Keep parameter interaction: forcerelay implies relay

src/net_processing.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ static const unsigned int MAX_INV_SZ = 50000;
7575
/** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which
7676
* point the OVERLOADED_PEER_TX_DELAY kicks in. */
7777
static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
78-
/** Maximum number of announced transactions from a peer */
79-
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
78+
/** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to
79+
* per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum
80+
* rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving
81+
* the actual transaction (from any peer) in response to requests for them. */
82+
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
8083
/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
8184
static constexpr auto TXID_RELAY_DELAY = std::chrono::seconds{2};
8285
/** How long to delay requesting transactions from non-preferred peers */
@@ -754,7 +757,7 @@ void PeerManager::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std
754757
{
755758
AssertLockHeld(::cs_main); // For m_txrequest
756759
NodeId nodeid = node.GetId();
757-
if (m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
760+
if (!node.HasPermission(PF_RELAY) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
758761
// Too many queued announcements from this peer
759762
return;
760763
}
@@ -766,12 +769,13 @@ void PeerManager::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std
766769
// - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
767770
// - TXID_RELAY_DELAY for announcements from txid peers while wtxid peers are available
768771
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
769-
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight.
772+
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have PF_RELAY).
770773
auto delay = std::chrono::microseconds{0};
771774
const bool preferred = state->fPreferredDownload;
772775
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
773776
if (!state->m_wtxid_relay && g_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
774-
const bool overloaded = m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
777+
const bool overloaded = !node.HasPermission(PF_RELAY) &&
778+
m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
775779
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
776780
m_txrequest.ReceivedInv(nodeid, gtxid, preferred, current_time + delay);
777781
}

test/functional/p2p_tx_download.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def on_getdata(self, message):
4646
TXID_RELAY_DELAY = 2 # seconds
4747
OVERLOADED_PEER_DELAY = 2 # seconds
4848
MAX_GETDATA_IN_FLIGHT = 100
49+
MAX_PEER_TX_ANNOUNCEMENTS = 5000
4950

5051
# Python test constants
5152
NUM_INBOUND = 10
@@ -215,6 +216,22 @@ def test_preferred_inv(self):
215216
with p2p_lock:
216217
assert_equal(peer.tx_getdata_count, 1)
217218

219+
def test_large_inv_batch(self):
220+
self.log.info('Test how large inv batches are handled with relay permission')
221+
self.restart_node(0, extra_args=['[email protected]'])
222+
peer = self.nodes[0].add_p2p_connection(TestP2PConn())
223+
peer.send_message(msg_inv([CInv(t=MSG_WTX, h=wtxid) for wtxid in range(MAX_PEER_TX_ANNOUNCEMENTS + 1)]))
224+
peer.wait_until(lambda: peer.tx_getdata_count == MAX_PEER_TX_ANNOUNCEMENTS + 1)
225+
226+
self.log.info('Test how large inv batches are handled without relay permission')
227+
self.restart_node(0)
228+
peer = self.nodes[0].add_p2p_connection(TestP2PConn())
229+
peer.send_message(msg_inv([CInv(t=MSG_WTX, h=wtxid) for wtxid in range(MAX_PEER_TX_ANNOUNCEMENTS + 1)]))
230+
peer.wait_until(lambda: peer.tx_getdata_count == MAX_PEER_TX_ANNOUNCEMENTS)
231+
peer.sync_with_ping()
232+
with p2p_lock:
233+
assert_equal(peer.tx_getdata_count, MAX_PEER_TX_ANNOUNCEMENTS)
234+
218235
def test_spurious_notfound(self):
219236
self.log.info('Check that spurious notfound is ignored')
220237
self.nodes[0].p2ps[0].send_message(msg_notfound(vec=[CInv(MSG_TX, 1)]))
@@ -225,6 +242,7 @@ def run_test(self):
225242
self.test_disconnect_fallback()
226243
self.test_notfound_fallback()
227244
self.test_preferred_inv()
245+
self.test_large_inv_batch()
228246
self.test_spurious_notfound()
229247

230248
# Run each test against new bitcoind instances, as setting mocktimes has long-term effects on when

0 commit comments

Comments
 (0)