mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
net_processing: drop m_recently_announced_invs bloom filter
Rather than using a bloom filter to track announced invs, simply allow a peer to request any tx that entered the mempool prior to the last INV message we sent them. This also obsoletes the UNCONDITIONAL_RELAY_DELAY.
This commit is contained in:
parent
a70beafdb2
commit
6ec1809d33
3 changed files with 30 additions and 59 deletions
|
@ -51,8 +51,6 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
|
||||||
/** How long a transaction has to be in the mempool before it can unconditionally be relayed. */
|
|
||||||
static constexpr auto UNCONDITIONAL_RELAY_DELAY = 2min;
|
|
||||||
/** Headers download timeout.
|
/** Headers download timeout.
|
||||||
* Timeout = base + per_header * (expected number of headers) */
|
* Timeout = base + per_header * (expected number of headers) */
|
||||||
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
|
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
|
||||||
|
@ -151,13 +149,6 @@ static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s};
|
||||||
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
|
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
|
||||||
/** Maximum number of inventory items to send per transmission. */
|
/** Maximum number of inventory items to send per transmission. */
|
||||||
static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * count_seconds(INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * count_seconds(INBOUND_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. */
|
/** Average delay between feefilter broadcasts in seconds. */
|
||||||
static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL{10min};
|
static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL{10min};
|
||||||
/** Maximum feefilter broadcast delay after significant change. */
|
/** Maximum feefilter broadcast delay after significant change. */
|
||||||
|
@ -273,9 +264,6 @@ struct Peer {
|
||||||
/** A bloom filter for which transactions to announce to the peer. See BIP37. */
|
/** A bloom filter for which transactions to announce to the peer. See BIP37. */
|
||||||
std::unique_ptr<CBloomFilter> m_bloom_filter PT_GUARDED_BY(m_bloom_filter_mutex) GUARDED_BY(m_bloom_filter_mutex){nullptr};
|
std::unique_ptr<CBloomFilter> m_bloom_filter PT_GUARDED_BY(m_bloom_filter_mutex) GUARDED_BY(m_bloom_filter_mutex){nullptr};
|
||||||
|
|
||||||
/** A rolling bloom filter of all announced tx CInvs to this peer */
|
|
||||||
CRollingBloomFilter m_recently_announced_invs GUARDED_BY(NetEventsInterface::g_msgproc_mutex){INVENTORY_MAX_RECENT_RELAY, 0.000001};
|
|
||||||
|
|
||||||
mutable RecursiveMutex m_tx_inventory_mutex;
|
mutable RecursiveMutex m_tx_inventory_mutex;
|
||||||
/** A filter of all the txids and wtxids that the peer has announced to
|
/** A filter of all the txids and wtxids that the peer has announced to
|
||||||
* us or we have announced to the peer. We use this to avoid announcing
|
* us or we have announced to the peer. We use this to avoid announcing
|
||||||
|
@ -290,11 +278,12 @@ struct Peer {
|
||||||
* permitted if the peer has NetPermissionFlags::Mempool or we advertise
|
* permitted if the peer has NetPermissionFlags::Mempool or we advertise
|
||||||
* NODE_BLOOM. See BIP35. */
|
* NODE_BLOOM. See BIP35. */
|
||||||
bool m_send_mempool GUARDED_BY(m_tx_inventory_mutex){false};
|
bool m_send_mempool GUARDED_BY(m_tx_inventory_mutex){false};
|
||||||
/** The last time a BIP35 `mempool` request was serviced. */
|
|
||||||
std::atomic<std::chrono::seconds> m_last_mempool_req{0s};
|
|
||||||
/** The next time after which we will send an `inv` message containing
|
/** The next time after which we will send an `inv` message containing
|
||||||
* transaction announcements to this peer. */
|
* transaction announcements to this peer. */
|
||||||
std::chrono::microseconds m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){0};
|
std::chrono::microseconds m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){0};
|
||||||
|
/** The mempool sequence num at which we sent the last `inv` message to this peer.
|
||||||
|
* Can relay txs with lower sequence numbers than this (see CTxMempool::info_for_relay). */
|
||||||
|
uint64_t m_last_inv_sequence GUARDED_BY(NetEventsInterface::g_msgproc_mutex){1};
|
||||||
|
|
||||||
/** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
|
/** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
|
||||||
std::atomic<CAmount> m_fee_filter_received{0};
|
std::atomic<CAmount> m_fee_filter_received{0};
|
||||||
|
@ -908,7 +897,7 @@ private:
|
||||||
std::atomic<std::chrono::seconds> m_last_tip_update{0s};
|
std::atomic<std::chrono::seconds> m_last_tip_update{0s};
|
||||||
|
|
||||||
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
|
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
|
||||||
CTransactionRef FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now)
|
CTransactionRef FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, NetEventsInterface::g_msgproc_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, NetEventsInterface::g_msgproc_mutex);
|
||||||
|
|
||||||
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
|
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
|
||||||
|
@ -2290,22 +2279,14 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef PeerManagerImpl::FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now)
|
CTransactionRef PeerManagerImpl::FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid)
|
||||||
{
|
{
|
||||||
auto txinfo = m_mempool.info(gtxid);
|
// If a tx was in the mempool prior to the last INV for this peer, permit the request.
|
||||||
|
auto txinfo = m_mempool.info_for_relay(gtxid, tx_relay.m_last_inv_sequence);
|
||||||
if (txinfo.tx) {
|
if (txinfo.tx) {
|
||||||
// If a TX could have been INVed in reply to a MEMPOOL request,
|
return std::move(txinfo.tx);
|
||||||
// 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 std::move(txinfo.tx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, the transaction might have been announced recently.
|
|
||||||
bool recent = tx_relay.m_recently_announced_invs.contains(gtxid.GetHash());
|
|
||||||
if (recent && txinfo.tx) return std::move(txinfo.tx);
|
|
||||||
|
|
||||||
// Or it might be from the most recent block
|
// Or it might be from the most recent block
|
||||||
{
|
{
|
||||||
LOCK(m_most_recent_block_mutex);
|
LOCK(m_most_recent_block_mutex);
|
||||||
|
@ -2328,10 +2309,6 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
|
||||||
std::vector<CInv> vNotFound;
|
std::vector<CInv> vNotFound;
|
||||||
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
|
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
|
||||||
|
|
||||||
const auto now{GetTime<std::chrono::seconds>()};
|
|
||||||
// Get last mempool request time
|
|
||||||
const auto mempool_req = tx_relay != nullptr ? tx_relay->m_last_mempool_req.load() : std::chrono::seconds::min();
|
|
||||||
|
|
||||||
// Process as many TX items from the front of the getdata queue as
|
// Process as many TX items from the front of the getdata queue as
|
||||||
// possible, since they're common and it's efficient to batch process
|
// possible, since they're common and it's efficient to batch process
|
||||||
// them.
|
// them.
|
||||||
|
@ -2349,33 +2326,12 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef tx = FindTxForGetData(*tx_relay, ToGenTxid(inv), mempool_req, now);
|
CTransactionRef tx = FindTxForGetData(*tx_relay, ToGenTxid(inv));
|
||||||
if (tx) {
|
if (tx) {
|
||||||
// WTX and WITNESS_TX imply we serialize with witness
|
// WTX and WITNESS_TX imply we serialize with witness
|
||||||
int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
|
int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
|
||||||
m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
|
m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
|
||||||
m_mempool.RemoveUnbroadcastTx(tx->GetHash());
|
m_mempool.RemoveUnbroadcastTx(tx->GetHash());
|
||||||
// As we're going to send tx, make sure its unconfirmed parents are made requestable.
|
|
||||||
std::vector<uint256> parent_ids_to_add;
|
|
||||||
{
|
|
||||||
LOCK(m_mempool.cs);
|
|
||||||
auto tx_iter = m_mempool.GetIter(tx->GetHash());
|
|
||||||
if (tx_iter) {
|
|
||||||
const CTxMemPoolEntry::Parents& parents = (*tx_iter)->GetMemPoolParentsConst();
|
|
||||||
parent_ids_to_add.reserve(parents.size());
|
|
||||||
for (const CTxMemPoolEntry& parent : parents) {
|
|
||||||
if (parent.GetTime() > now - UNCONDITIONAL_RELAY_DELAY) {
|
|
||||||
parent_ids_to_add.push_back(parent.GetTx().GetHash());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const uint256& parent_txid : parent_ids_to_add) {
|
|
||||||
// Relaying a transaction with a recent but unconfirmed parent.
|
|
||||||
if (WITH_LOCK(tx_relay->m_tx_inventory_mutex, return !tx_relay->m_tx_inventory_known_filter.contains(parent_txid))) {
|
|
||||||
tx_relay->m_recently_announced_invs.insert(parent_txid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
vNotFound.push_back(inv);
|
vNotFound.push_back(inv);
|
||||||
}
|
}
|
||||||
|
@ -5734,14 +5690,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
if (!tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
if (!tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||||
}
|
}
|
||||||
tx_relay->m_tx_inventory_known_filter.insert(hash);
|
tx_relay->m_tx_inventory_known_filter.insert(hash);
|
||||||
// Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
|
|
||||||
vInv.push_back(inv);
|
vInv.push_back(inv);
|
||||||
if (vInv.size() == MAX_INV_SZ) {
|
if (vInv.size() == MAX_INV_SZ) {
|
||||||
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
||||||
vInv.clear();
|
vInv.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx_relay->m_last_mempool_req = std::chrono::duration_cast<std::chrono::seconds>(current_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine transactions to relay
|
// Determine transactions to relay
|
||||||
|
@ -5781,14 +5735,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
if (!txinfo.tx) {
|
if (!txinfo.tx) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto txid = txinfo.tx->GetHash();
|
|
||||||
// Peer told you to not send transactions at that feerate? Don't bother sending it.
|
// Peer told you to not send transactions at that feerate? Don't bother sending it.
|
||||||
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
|
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||||
// Send
|
// Send
|
||||||
tx_relay->m_recently_announced_invs.insert(hash);
|
|
||||||
vInv.push_back(inv);
|
vInv.push_back(inv);
|
||||||
nRelayedTransactions++;
|
nRelayedTransactions++;
|
||||||
if (vInv.size() == MAX_INV_SZ) {
|
if (vInv.size() == MAX_INV_SZ) {
|
||||||
|
@ -5796,15 +5748,19 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
vInv.clear();
|
vInv.clear();
|
||||||
}
|
}
|
||||||
tx_relay->m_tx_inventory_known_filter.insert(hash);
|
tx_relay->m_tx_inventory_known_filter.insert(hash);
|
||||||
if (hash != txid) {
|
if (peer->m_wtxid_relay && hash != txinfo.tx->GetHash()) {
|
||||||
// Insert txid into m_tx_inventory_known_filter, even for
|
// Insert txid into m_tx_inventory_known_filter, even for
|
||||||
// wtxidrelay peers. This prevents re-adding of
|
// wtxidrelay peers. This prevents re-adding of
|
||||||
// unconfirmed parents to the recently_announced
|
// unconfirmed parents to the recently_announced
|
||||||
// filter, when a child tx is requested. See
|
// filter, when a child tx is requested. See
|
||||||
// ProcessGetData().
|
// ProcessGetData().
|
||||||
tx_relay->m_tx_inventory_known_filter.insert(txid);
|
tx_relay->m_tx_inventory_known_filter.insert(txinfo.tx->GetHash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we'll respond to GETDATA requests for anything we've just announced
|
||||||
|
LOCK(m_mempool.cs);
|
||||||
|
tx_relay->m_last_inv_sequence = m_mempool.GetSequence();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!vInv.empty())
|
if (!vInv.empty())
|
||||||
|
|
|
@ -853,6 +853,17 @@ TxMempoolInfo CTxMemPool::info(const GenTxid& gtxid) const
|
||||||
return GetInfo(i);
|
return GetInfo(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TxMempoolInfo CTxMemPool::info_for_relay(const GenTxid& gtxid, uint64_t last_sequence) const
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
indexed_transaction_set::const_iterator i = (gtxid.IsWtxid() ? get_iter_from_wtxid(gtxid.GetHash()) : mapTx.find(gtxid.GetHash()));
|
||||||
|
if (i != mapTx.end() && i->GetSequence() < last_sequence) {
|
||||||
|
return GetInfo(i);
|
||||||
|
} else {
|
||||||
|
return TxMempoolInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
|
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
|
|
@ -708,6 +708,10 @@ public:
|
||||||
return mapTx.project<0>(mapTx.get<index_by_wtxid>().find(wtxid));
|
return mapTx.project<0>(mapTx.get<index_by_wtxid>().find(wtxid));
|
||||||
}
|
}
|
||||||
TxMempoolInfo info(const GenTxid& gtxid) const;
|
TxMempoolInfo info(const GenTxid& gtxid) const;
|
||||||
|
|
||||||
|
/** Returns info for a transaction if its entry_sequence < last_sequence */
|
||||||
|
TxMempoolInfo info_for_relay(const GenTxid& gtxid, uint64_t last_sequence) const;
|
||||||
|
|
||||||
std::vector<TxMempoolInfo> infoAll() const;
|
std::vector<TxMempoolInfo> infoAll() const;
|
||||||
|
|
||||||
size_t DynamicMemoryUsage() const;
|
size_t DynamicMemoryUsage() const;
|
||||||
|
|
Loading…
Add table
Reference in a new issue