aboutsummaryrefslogtreecommitdiff
path: root/src/net_processing.cpp
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2020-05-29 11:33:17 -0700
committerPieter Wuille <pieter@wuille.net>2020-07-08 18:29:56 -0700
commit43f02ccbff9b137d59458da7a8afdb0bf80e127f (patch)
tree336adbc240caad2a74572aee912f11d92e9da820 /src/net_processing.cpp
parentb24a17f03982c9cd8fd6ec665b16e022374c96f0 (diff)
downloadbitcoin-43f02ccbff9b137d59458da7a8afdb0bf80e127f.tar.xz
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.
Diffstat (limited to 'src/net_processing.cpp')
-rw-r--r--src/net_processing.cpp39
1 files changed, 30 insertions, 9 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 9221774761..dc28d8cca5 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -121,9 +121,18 @@ static constexpr std::chrono::seconds AVG_ADDRESS_BROADCAST_INTERVAL{30};
/** Average delay between trickled inventory transmissions in seconds.
* Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
-/** Maximum number of inventory items to send per transmission.
+/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
-static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
+static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
+/** Maximum number of inventory items to send per transmission. */
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * INVENTORY_BROADCAST_INTERVAL;
+/** The number of most recently announced transactions a peer can request. */
+static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
+/** Verify that INVENTORY_MAX_RECENT_RELAY is enough to cache everything typically
+ * relayed before unconditional relay from the mempool kicks in. This is only a
+ * lower bound, and it should be larger to account for higher inv rate to outbound
+ * peers, and random variations in the broadcast mechanism. */
+static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
/** Maximum feefilter broadcast delay after significant change. */
@@ -397,6 +406,9 @@ struct CNodeState {
//! Whether this peer is a manual connection
bool m_is_manual_connection;
+ //! A rolling bloom filter of all announced tx CInvs to this peer.
+ CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
+
CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
m_is_manual_connection (is_manual)
@@ -424,6 +436,7 @@ struct CNodeState {
fSupportsDesiredCmpctVersion = false;
m_chain_sync = { 0, nullptr, false, false };
m_last_block_announcement = 0;
+ m_recently_announced_invs.reset();
}
};
@@ -1631,19 +1644,25 @@ CTransactionRef static FindTxForGetData(const CNode& peer, const uint256& txid,
auto txinfo = mempool.info(txid);
if (txinfo.tx) {
- // To protect privacy, do not answer getdata using the mempool when
- // that TX couldn't have been INVed in reply to a MEMPOOL request,
- // and it's more recent than UNCONDITIONAL_RELAY_DELAY.
+ // If a TX could have been INVed in reply to a MEMPOOL request,
+ // or is older than UNCONDITIONAL_RELAY_DELAY, permit the request
+ // unconditionally.
if ((mempool_req.count() && txinfo.m_time <= mempool_req) || txinfo.m_time <= now - UNCONDITIONAL_RELAY_DELAY) {
- return txinfo.tx;
+ return std::move(txinfo.tx);
}
}
{
LOCK(cs_main);
- // Look up transaction in relay pool
- auto mi = mapRelay.find(txid);
- if (mi != mapRelay.end()) return mi->second;
+
+ // Otherwise, the transaction must have been announced recently.
+ if (State(peer.GetId())->m_recently_announced_invs.contains(txid)) {
+ // If it was, it can be relayed from either the mempool...
+ if (txinfo.tx) return std::move(txinfo.tx);
+ // ... or the relay pool.
+ auto mi = mapRelay.find(txid);
+ if (mi != mapRelay.end()) return mi->second;
+ }
}
return {};
@@ -4155,6 +4174,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
}
pto->m_tx_relay->filterInventoryKnown.insert(hash);
+ // Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
@@ -4208,6 +4228,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
// Send
+ State(pto->GetId())->m_recently_announced_invs.insert(hash);
vInv.push_back(CInv(MSG_TX, hash));
nRelayedTransactions++;
{