mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-14 06:32:33 +01:00
[refactor] move tx inv/getdata handling to txdownload
This commit is contained in:
@@ -19,6 +19,22 @@ class TxRequestTracker;
|
||||
namespace node {
|
||||
class TxDownloadManagerImpl;
|
||||
|
||||
/** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which
|
||||
* point the OVERLOADED_PEER_TX_DELAY kicks in. */
|
||||
static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
|
||||
/** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to
|
||||
* per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum
|
||||
* rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving
|
||||
* the actual transaction (from any peer) in response to requests for them. */
|
||||
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
|
||||
/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
|
||||
static constexpr auto TXID_RELAY_DELAY{2s};
|
||||
/** How long to delay requesting transactions from non-preferred peers */
|
||||
static constexpr auto NONPREF_PEER_TX_DELAY{2s};
|
||||
/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */
|
||||
static constexpr auto OVERLOADED_PEER_TX_DELAY{2s};
|
||||
/** How long to wait before downloading a transaction from an additional peer */
|
||||
static constexpr auto GETDATA_TX_INTERVAL{60s};
|
||||
struct TxDownloadOptions {
|
||||
/** Read-only reference to mempool. */
|
||||
const CTxMemPool& m_mempool;
|
||||
@@ -84,6 +100,14 @@ public:
|
||||
|
||||
/** Deletes all txrequest announcements and orphans for a given peer. */
|
||||
void DisconnectedPeer(NodeId nodeid);
|
||||
|
||||
/** New inv has been received. May be added as a candidate to txrequest.
|
||||
* @param[in] p2p_inv When true, only add this announcement if we don't already have the tx.
|
||||
* Returns true if this was a dropped inv (p2p_inv=true and we already have the tx), false otherwise. */
|
||||
bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv);
|
||||
|
||||
/** Get getdata requests to send. */
|
||||
std::vector<GenTxid> GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time);
|
||||
};
|
||||
} // namespace node
|
||||
#endif // BITCOIN_NODE_TXDOWNLOADMAN_H
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <chain.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <logging.h>
|
||||
#include <txmempool.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
@@ -58,6 +59,14 @@ void TxDownloadManager::DisconnectedPeer(NodeId nodeid)
|
||||
{
|
||||
m_impl->DisconnectedPeer(nodeid);
|
||||
}
|
||||
bool TxDownloadManager::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv)
|
||||
{
|
||||
return m_impl->AddTxAnnouncement(peer, gtxid, now, p2p_inv);
|
||||
}
|
||||
std::vector<GenTxid> TxDownloadManager::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
|
||||
{
|
||||
return m_impl->GetRequestsToSend(nodeid, current_time);
|
||||
}
|
||||
|
||||
// TxDownloadManagerImpl
|
||||
void TxDownloadManagerImpl::ActiveTipChange()
|
||||
@@ -142,4 +151,58 @@ void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv)
|
||||
{
|
||||
// If this is an inv received from a peer and we already have it, we can drop it.
|
||||
if (p2p_inv && AlreadyHaveTx(gtxid, /*include_reconsiderable=*/true)) return true;
|
||||
|
||||
auto it = m_peer_info.find(peer);
|
||||
if (it == m_peer_info.end()) return false;
|
||||
const auto& info = it->second.m_connection_info;
|
||||
if (!info.m_relay_permissions && m_txrequest.Count(peer) >= MAX_PEER_TX_ANNOUNCEMENTS) {
|
||||
// Too many queued announcements for this peer
|
||||
return false;
|
||||
}
|
||||
// Decide the TxRequestTracker parameters for this announcement:
|
||||
// - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission)
|
||||
// - "reqtime": current time plus delays for:
|
||||
// - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
|
||||
// - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
|
||||
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
|
||||
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
|
||||
auto delay{0us};
|
||||
if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY;
|
||||
if (!gtxid.IsWtxid() && m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY;
|
||||
const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(peer) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
|
||||
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
|
||||
|
||||
m_txrequest.ReceivedInv(peer, gtxid, info.m_preferred, now + delay);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GenTxid> TxDownloadManagerImpl::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
|
||||
{
|
||||
std::vector<GenTxid> requests;
|
||||
std::vector<std::pair<NodeId, GenTxid>> expired;
|
||||
auto requestable = m_txrequest.GetRequestable(nodeid, current_time, &expired);
|
||||
for (const auto& entry : expired) {
|
||||
LogDebug(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx",
|
||||
entry.second.GetHash().ToString(), entry.first);
|
||||
}
|
||||
for (const GenTxid& gtxid : requestable) {
|
||||
if (!AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
|
||||
LogDebug(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
|
||||
gtxid.GetHash().ToString(), nodeid);
|
||||
requests.emplace_back(gtxid);
|
||||
m_txrequest.RequestedTx(nodeid, gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL);
|
||||
} else {
|
||||
// We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as
|
||||
// this should already be called whenever a transaction becomes AlreadyHaveTx().
|
||||
m_txrequest.ForgetTxHash(gtxid.GetHash());
|
||||
}
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
} // namespace node
|
||||
|
||||
@@ -150,6 +150,12 @@ public:
|
||||
|
||||
void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info);
|
||||
void DisconnectedPeer(NodeId nodeid);
|
||||
|
||||
/** New inv has been received. May be added as a candidate to txrequest. */
|
||||
bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv);
|
||||
|
||||
/** Get getdata requests to send. */
|
||||
std::vector<GenTxid> GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time);
|
||||
};
|
||||
} // namespace node
|
||||
#endif // BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H
|
||||
|
||||
Reference in New Issue
Block a user