Skip to content

Commit 43f02cc

Browse files
committed
Only respond to requests for recently announced transactions
... unless they're UNCONDITIONAL_RELAY_DELAY old, or there has been a response to a MEMPOOL request in the mean time. This is accomplished using a rolling Bloom filter for the last 3500 announced transactions. The probability of seeing more than 100 broadcast events (which can be up to 35 txids each) in 2 minutes for an outbound peer (where the average frequency is one per minute), is less than 1 in a million.
1 parent b24a17f commit 43f02cc

File tree

1 file changed

+30
-9
lines changed

1 file changed

+30
-9
lines changed

src/net_processing.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,18 @@ static constexpr std::chrono::seconds AVG_ADDRESS_BROADCAST_INTERVAL{30};
121121
/** Average delay between trickled inventory transmissions in seconds.
122122
* Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
123123
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
124-
/** Maximum number of inventory items to send per transmission.
124+
/** Maximum rate of inventory items to send per second.
125125
* Limits the impact of low-fee transaction floods. */
126-
static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
126+
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
127+
/** Maximum number of inventory items to send per transmission. */
128+
static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * INVENTORY_BROADCAST_INTERVAL;
129+
/** The number of most recently announced transactions a peer can request. */
130+
static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
131+
/** Verify that INVENTORY_MAX_RECENT_RELAY is enough to cache everything typically
132+
* relayed before unconditional relay from the mempool kicks in. This is only a
133+
* lower bound, and it should be larger to account for higher inv rate to outbound
134+
* peers, and random variations in the broadcast mechanism. */
135+
static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
127136
/** Average delay between feefilter broadcasts in seconds. */
128137
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
129138
/** Maximum feefilter broadcast delay after significant change. */
@@ -397,6 +406,9 @@ struct CNodeState {
397406
//! Whether this peer is a manual connection
398407
bool m_is_manual_connection;
399408

409+
//! A rolling bloom filter of all announced tx CInvs to this peer.
410+
CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
411+
400412
CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
401413
address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
402414
m_is_manual_connection (is_manual)
@@ -424,6 +436,7 @@ struct CNodeState {
424436
fSupportsDesiredCmpctVersion = false;
425437
m_chain_sync = { 0, nullptr, false, false };
426438
m_last_block_announcement = 0;
439+
m_recently_announced_invs.reset();
427440
}
428441
};
429442

@@ -1631,19 +1644,25 @@ CTransactionRef static FindTxForGetData(const CNode& peer, const uint256& txid,
16311644

16321645
auto txinfo = mempool.info(txid);
16331646
if (txinfo.tx) {
1634-
// To protect privacy, do not answer getdata using the mempool when
1635-
// that TX couldn't have been INVed in reply to a MEMPOOL request,
1636-
// and it's more recent than UNCONDITIONAL_RELAY_DELAY.
1647+
// If a TX could have been INVed in reply to a MEMPOOL request,
1648+
// or is older than UNCONDITIONAL_RELAY_DELAY, permit the request
1649+
// unconditionally.
16371650
if ((mempool_req.count() && txinfo.m_time <= mempool_req) || txinfo.m_time <= now - UNCONDITIONAL_RELAY_DELAY) {
1638-
return txinfo.tx;
1651+
return std::move(txinfo.tx);
16391652
}
16401653
}
16411654

16421655
{
16431656
LOCK(cs_main);
1644-
// Look up transaction in relay pool
1645-
auto mi = mapRelay.find(txid);
1646-
if (mi != mapRelay.end()) return mi->second;
1657+
1658+
// Otherwise, the transaction must have been announced recently.
1659+
if (State(peer.GetId())->m_recently_announced_invs.contains(txid)) {
1660+
// If it was, it can be relayed from either the mempool...
1661+
if (txinfo.tx) return std::move(txinfo.tx);
1662+
// ... or the relay pool.
1663+
auto mi = mapRelay.find(txid);
1664+
if (mi != mapRelay.end()) return mi->second;
1665+
}
16471666
}
16481667

16491668
return {};
@@ -4155,6 +4174,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
41554174
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
41564175
}
41574176
pto->m_tx_relay->filterInventoryKnown.insert(hash);
4177+
// Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
41584178
vInv.push_back(inv);
41594179
if (vInv.size() == MAX_INV_SZ) {
41604180
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
@@ -4208,6 +4228,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
42084228
}
42094229
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
42104230
// Send
4231+
State(pto->GetId())->m_recently_announced_invs.insert(hash);
42114232
vInv.push_back(CInv(MSG_TX, hash));
42124233
nRelayedTransactions++;
42134234
{

0 commit comments

Comments
 (0)