mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-11 12:22:39 -03:00
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.
This commit is contained in:
parent
b24a17f039
commit
43f02ccbff
1 changed files with 30 additions and 9 deletions
|
@ -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++;
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue