Skip to content

Commit 042a97c

Browse files
committed
[refactor] move tx inv/getdata handling to txdownload
1 parent 58e09f2 commit 042a97c

File tree

4 files changed

+105
-82
lines changed

4 files changed

+105
-82
lines changed

src/net_processing.cpp

Lines changed: 12 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,6 @@ static constexpr auto PING_INTERVAL{2min};
8989
static const unsigned int MAX_LOCATOR_SZ = 101;
9090
/** The maximum number of entries in an 'inv' protocol message */
9191
static const unsigned int MAX_INV_SZ = 50000;
92-
/** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which
93-
* point the OVERLOADED_PEER_TX_DELAY kicks in. */
94-
static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
95-
/** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to
96-
* per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum
97-
* rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving
98-
* the actual transaction (from any peer) in response to requests for them. */
99-
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
100-
/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
101-
static constexpr auto TXID_RELAY_DELAY{2s};
102-
/** How long to delay requesting transactions from non-preferred peers */
103-
static constexpr auto NONPREF_PEER_TX_DELAY{2s};
104-
/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */
105-
static constexpr auto OVERLOADED_PEER_TX_DELAY{2s};
106-
/** How long to wait before downloading a transaction from an additional peer */
107-
static constexpr auto GETDATA_TX_INTERVAL{60s};
10892
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
10993
static const unsigned int MAX_GETDATA_SZ = 1000;
11094
/** Number of blocks that can be requested at any given time from a single peer. */
@@ -156,7 +140,7 @@ static constexpr unsigned int INVENTORY_BROADCAST_TARGET = INVENTORY_BROADCAST_P
156140
/** Maximum number of inventory items to send per transmission. */
157141
static constexpr unsigned int INVENTORY_BROADCAST_MAX = 1000;
158142
static_assert(INVENTORY_BROADCAST_MAX >= INVENTORY_BROADCAST_TARGET, "INVENTORY_BROADCAST_MAX too low");
159-
static_assert(INVENTORY_BROADCAST_MAX <= MAX_PEER_TX_ANNOUNCEMENTS, "INVENTORY_BROADCAST_MAX too high");
143+
static_assert(INVENTORY_BROADCAST_MAX <= node::MAX_PEER_TX_ANNOUNCEMENTS, "INVENTORY_BROADCAST_MAX too high");
160144
/** Average delay between feefilter broadcasts in seconds. */
161145
static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL{10min};
162146
/** Maximum feefilter broadcast delay after significant change. */
@@ -720,12 +704,6 @@ class PeerManagerImpl final : public PeerManager
720704

721705
void SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlock& block, const BlockTransactionsRequest& req);
722706

723-
/** Register with TxRequestTracker that an INV has been received from a
724-
* peer. The announcement parameters are decided in PeerManager and then
725-
* passed to TxRequestTracker. */
726-
void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
727-
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_tx_download_mutex);
728-
729707
/** Send a message to a peer */
730708
void PushMessage(CNode& node, CSerializedNetMsg&& msg) const { m_connman.PushMessage(&node, std::move(msg)); }
731709
template <typename... Args>
@@ -1571,36 +1549,6 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
15711549
}
15721550
}
15731551

1574-
void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
1575-
{
1576-
AssertLockHeld(::cs_main); // for State
1577-
AssertLockHeld(m_tx_download_mutex); // For m_txrequest
1578-
NodeId nodeid = node.GetId();
1579-
1580-
auto& m_txrequest = m_txdownloadman.GetTxRequestRef();
1581-
if (!node.HasPermission(NetPermissionFlags::Relay) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
1582-
// Too many queued announcements from this peer
1583-
return;
1584-
}
1585-
const CNodeState* state = State(nodeid);
1586-
1587-
// Decide the TxRequestTracker parameters for this announcement:
1588-
// - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission)
1589-
// - "reqtime": current time plus delays for:
1590-
// - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
1591-
// - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
1592-
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
1593-
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
1594-
auto delay{0us};
1595-
const bool preferred = state->fPreferredDownload;
1596-
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
1597-
if (!gtxid.IsWtxid() && m_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
1598-
const bool overloaded = !node.HasPermission(NetPermissionFlags::Relay) &&
1599-
m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
1600-
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
1601-
m_txrequest.ReceivedInv(nodeid, gtxid, preferred, current_time + delay);
1602-
}
1603-
16041552
void PeerManagerImpl::UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
16051553
{
16061554
LOCK(cs_main);
@@ -4133,11 +4081,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
41334081
AddKnownTx(*peer, inv.hash);
41344082

41354083
if (!m_chainman.IsInitialBlockDownload()) {
4136-
const bool fAlreadyHave = m_txdownloadman.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/true);
4084+
const bool fAlreadyHave{m_txdownloadman.AddTxAnnouncement(pfrom.GetId(), gtxid, current_time, /*p2p_inv=*/true)};
41374085
LogDebug(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
4138-
if (!fAlreadyHave) {
4139-
AddTxAnnouncement(pfrom, gtxid, current_time);
4140-
}
41414086
}
41424087
} else {
41434088
LogDebug(BCLog::NET, "Unknown inv type \"%s\" received from peer=%d\n", inv.ToString(), pfrom.GetId());
@@ -4546,7 +4491,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
45464491
AddKnownTx(*peer, parent_txid);
45474492
// Exclude m_lazy_recent_rejects_reconsiderable: the missing parent may have been
45484493
// previously rejected for being too low feerate. This orphan might CPFP it.
4549-
if (!m_txdownloadman.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) AddTxAnnouncement(pfrom, gtxid, current_time);
4494+
if (!m_txdownloadman.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
4495+
m_txdownloadman.AddTxAnnouncement(pfrom.GetId(), gtxid, current_time, /*p2p_inv=*/false);
4496+
}
45504497
}
45514498

45524499
if (m_orphanage.AddTx(ptx, pfrom.GetId())) {
@@ -5186,7 +5133,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
51865133
if (msg_type == NetMsgType::NOTFOUND) {
51875134
std::vector<CInv> vInv;
51885135
vRecv >> vInv;
5189-
if (vInv.size() <= MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
5136+
if (vInv.size() <= node::MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
51905137
LOCK(m_tx_download_mutex);
51915138
for (CInv &inv : vInv) {
51925139
if (inv.IsGenTxMsg()) {
@@ -6210,31 +6157,14 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
62106157
//
62116158
{
62126159
LOCK(m_tx_download_mutex);
6213-
std::vector<std::pair<NodeId, GenTxid>> expired;
6214-
auto requestable = m_txdownloadman.GetTxRequestRef().GetRequestable(pto->GetId(), current_time, &expired);
6215-
for (const auto& entry : expired) {
6216-
LogDebug(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx",
6217-
entry.second.GetHash().ToString(), entry.first);
6218-
}
6219-
for (const GenTxid& gtxid : requestable) {
6220-
// Exclude m_lazy_recent_rejects_reconsiderable: we may be requesting a missing parent
6221-
// that was previously rejected for being too low feerate.
6222-
if (!m_txdownloadman.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
6223-
LogDebug(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
6224-
gtxid.GetHash().ToString(), pto->GetId());
6225-
vGetData.emplace_back(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*peer)), gtxid.GetHash());
6226-
if (vGetData.size() >= MAX_GETDATA_SZ) {
6227-
MakeAndPushMessage(*pto, NetMsgType::GETDATA, vGetData);
6228-
vGetData.clear();
6229-
}
6230-
m_txdownloadman.GetTxRequestRef().RequestedTx(pto->GetId(), gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL);
6231-
} else {
6232-
// We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as
6233-
// this should already be called whenever a transaction becomes AlreadyHaveTx().
6234-
m_txdownloadman.GetTxRequestRef().ForgetTxHash(gtxid.GetHash());
6160+
for (const GenTxid& gtxid : m_txdownloadman.GetRequestsToSend(pto->GetId(), current_time)) {
6161+
vGetData.emplace_back(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*peer)), gtxid.GetHash());
6162+
if (vGetData.size() >= MAX_GETDATA_SZ) {
6163+
MakeAndPushMessage(*pto, NetMsgType::GETDATA, vGetData);
6164+
vGetData.clear();
62356165
}
62366166
}
6237-
} // release m_tx_download_mutex
6167+
}
62386168

62396169
if (!vGetData.empty())
62406170
MakeAndPushMessage(*pto, NetMsgType::GETDATA, vGetData);

src/node/txdownloadman.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ class TxRequestTracker;
1919
namespace node {
2020
class TxDownloadManagerImpl;
2121

22+
/** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which
23+
* point the OVERLOADED_PEER_TX_DELAY kicks in. */
24+
static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
25+
/** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to
26+
* per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum
27+
* rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving
28+
* the actual transaction (from any peer) in response to requests for them. */
29+
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
30+
/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
31+
static constexpr auto TXID_RELAY_DELAY{2s};
32+
/** How long to delay requesting transactions from non-preferred peers */
33+
static constexpr auto NONPREF_PEER_TX_DELAY{2s};
34+
/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */
35+
static constexpr auto OVERLOADED_PEER_TX_DELAY{2s};
36+
/** How long to wait before downloading a transaction from an additional peer */
37+
static constexpr auto GETDATA_TX_INTERVAL{60s};
2238
struct TxDownloadOptions {
2339
/** Read-only reference to mempool. */
2440
const CTxMemPool& m_mempool;
@@ -84,6 +100,14 @@ class TxDownloadManager {
84100

85101
/** Deletes all txrequest announcements and orphans for a given peer. */
86102
void DisconnectedPeer(NodeId nodeid);
103+
104+
/** New inv has been received. May be added as a candidate to txrequest.
105+
* @param[in] p2p_inv When true, only add this announcement if we don't already have the tx.
106+
* Returns true if this was a dropped inv (p2p_inv=true and we already have the tx), false otherwise. */
107+
bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv);
108+
109+
/** Get getdata requests to send. */
110+
std::vector<GenTxid> GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time);
87111
};
88112
} // namespace node
89113
#endif // BITCOIN_NODE_TXDOWNLOADMAN_H

src/node/txdownloadman_impl.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <chain.h>
99
#include <consensus/validation.h>
10+
#include <logging.h>
1011
#include <txmempool.h>
1112
#include <validation.h>
1213
#include <validationinterface.h>
@@ -58,6 +59,14 @@ void TxDownloadManager::DisconnectedPeer(NodeId nodeid)
5859
{
5960
m_impl->DisconnectedPeer(nodeid);
6061
}
62+
bool TxDownloadManager::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv)
63+
{
64+
return m_impl->AddTxAnnouncement(peer, gtxid, now, p2p_inv);
65+
}
66+
std::vector<GenTxid> TxDownloadManager::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
67+
{
68+
return m_impl->GetRequestsToSend(nodeid, current_time);
69+
}
6170

6271
// TxDownloadManagerImpl
6372
void TxDownloadManagerImpl::ActiveTipChange()
@@ -142,4 +151,58 @@ void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
142151
}
143152

144153
}
154+
155+
bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv)
156+
{
157+
// If this is an inv received from a peer and we already have it, we can drop it.
158+
if (p2p_inv && AlreadyHaveTx(gtxid, /*include_reconsiderable=*/true)) return true;
159+
160+
auto it = m_peer_info.find(peer);
161+
if (it == m_peer_info.end()) return false;
162+
const auto& info = it->second.m_connection_info;
163+
if (!info.m_relay_permissions && m_txrequest.Count(peer) >= MAX_PEER_TX_ANNOUNCEMENTS) {
164+
// Too many queued announcements for this peer
165+
return false;
166+
}
167+
// Decide the TxRequestTracker parameters for this announcement:
168+
// - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission)
169+
// - "reqtime": current time plus delays for:
170+
// - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
171+
// - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
172+
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
173+
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
174+
auto delay{0us};
175+
if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY;
176+
if (!gtxid.IsWtxid() && m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY;
177+
const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(peer) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
178+
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
179+
180+
m_txrequest.ReceivedInv(peer, gtxid, info.m_preferred, now + delay);
181+
182+
return false;
183+
}
184+
185+
std::vector<GenTxid> TxDownloadManagerImpl::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
186+
{
187+
std::vector<GenTxid> requests;
188+
std::vector<std::pair<NodeId, GenTxid>> expired;
189+
auto requestable = m_txrequest.GetRequestable(nodeid, current_time, &expired);
190+
for (const auto& entry : expired) {
191+
LogDebug(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx",
192+
entry.second.GetHash().ToString(), entry.first);
193+
}
194+
for (const GenTxid& gtxid : requestable) {
195+
if (!AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
196+
LogDebug(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
197+
gtxid.GetHash().ToString(), nodeid);
198+
requests.emplace_back(gtxid);
199+
m_txrequest.RequestedTx(nodeid, gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL);
200+
} else {
201+
// We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as
202+
// this should already be called whenever a transaction becomes AlreadyHaveTx().
203+
m_txrequest.ForgetTxHash(gtxid.GetHash());
204+
}
205+
}
206+
return requests;
207+
}
145208
} // namespace node

src/node/txdownloadman_impl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ class TxDownloadManagerImpl {
150150

151151
void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info);
152152
void DisconnectedPeer(NodeId nodeid);
153+
154+
/** New inv has been received. May be added as a candidate to txrequest. */
155+
bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv);
156+
157+
/** Get getdata requests to send. */
158+
std::vector<GenTxid> GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time);
153159
};
154160
} // namespace node
155161
#endif // BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H

0 commit comments

Comments
 (0)