Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | |||||
static constexpr std::chrono::minutes PING_INTERVAL{2}; | static constexpr std::chrono::minutes PING_INTERVAL{2}; | ||||
/** The maximum number of entries in a locator */ | /** The maximum number of entries in a locator */ | ||||
static const unsigned int MAX_LOCATOR_SZ = 101; | static const unsigned int MAX_LOCATOR_SZ = 101; | ||||
/** The maximum number of entries in an 'inv' protocol message */ | /** The maximum number of entries in an 'inv' protocol message */ | ||||
static const unsigned int MAX_INV_SZ = 50000; | static const unsigned int MAX_INV_SZ = 50000; | ||||
static_assert(MAX_PROTOCOL_MESSAGE_LENGTH > MAX_INV_SZ * sizeof(CInv), | static_assert(MAX_PROTOCOL_MESSAGE_LENGTH > MAX_INV_SZ * sizeof(CInv), | ||||
"Max protocol message length must be greater than largest " | "Max protocol message length must be greater than largest " | ||||
"possible INV message"); | "possible INV message"); | ||||
struct DataRequestParameters { | |||||
/** | /** | ||||
* Maximum number of in-flight transaction requests from a peer. It is not a | * Maximum number of in-flight data requests from a peer. It is not a hard | ||||
* hard limit, but the threshold at which point the OVERLOADED_PEER_TX_DELAY | * limit, but the threshold at which point the overloaded_peer_delay kicks | ||||
* kicks in. | * in. | ||||
*/ | */ | ||||
static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100; | const size_t max_peer_request_in_flight; | ||||
/** | /** | ||||
* Maximum number of transactions to consider for requesting, per peer. It | * Maximum number of inventories to consider for requesting, per peer. It | ||||
* provides a reasonable DoS limit to per-peer memory usage spent on | * provides a reasonable DoS limit to per-peer memory usage spent on | ||||
* announcements, while covering peers continuously sending INVs at the maximum | * announcements, while covering peers continuously sending INVs at the | ||||
* rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several | * maximum rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for | ||||
* minutes, while not receiving the actual transaction (from any peer) in | * several minutes, while not receiving the actual data (from any peer) in | ||||
* response to requests for them. | * response to requests for them. | ||||
*/ | */ | ||||
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000; | const size_t max_peer_announcements; | ||||
/** How long to delay requesting transactions from non-preferred peers */ | |||||
static constexpr auto NONPREF_PEER_TX_DELAY = std::chrono::seconds{2}; | /** How long to delay requesting data from non-preferred peers */ | ||||
const std::chrono::seconds nonpref_peer_delay; | |||||
/** | /** | ||||
* How long to delay requesting transactions from overloaded peers (see | * How long to delay requesting data from overloaded peers (see | ||||
* MAX_PEER_TX_REQUEST_IN_FLIGHT). | * max_peer_request_in_flight). | ||||
*/ | */ | ||||
static constexpr auto OVERLOADED_PEER_TX_DELAY = std::chrono::seconds{2}; | const std::chrono::seconds overloaded_peer_delay; | ||||
/** | /** | ||||
* How long to wait (in microseconds) before downloading a transaction from an | * How long to wait (in microseconds) before a data request from an | ||||
* additional peer. | * additional peer. | ||||
*/ | */ | ||||
static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{ | const std::chrono::microseconds getdata_interval; | ||||
std::chrono::seconds{60}}; | |||||
/** | |||||
* Permission flags a peer requires to bypass the request limits tracking | |||||
* limits and delay penalty. | |||||
*/ | |||||
const NetPermissionFlags bypass_request_limits_permissions; | |||||
}; | |||||
static constexpr DataRequestParameters TX_REQUEST_PARAMS{ | |||||
100, // max_peer_request_in_flight | |||||
5000, // max_peer_announcements | |||||
std::chrono::seconds(2), // nonpref_peer_delay | |||||
std::chrono::seconds(2), // overloaded_peer_delay | |||||
std::chrono::seconds(60), // getdata_interval | |||||
PF_RELAY, // bypass_request_limits_permissions | |||||
}; | |||||
/** | /** | ||||
* Limit to avoid sending big packets. Not used in processing incoming GETDATA | * Limit to avoid sending big packets. Not used in processing incoming GETDATA | ||||
* for compatibility. | * for compatibility. | ||||
*/ | */ | ||||
static const unsigned int MAX_GETDATA_SZ = 1000; | static const unsigned int MAX_GETDATA_SZ = 1000; | ||||
/** | /** | ||||
* Number of blocks that can be requested at any given time from a single peer. | * Number of blocks that can be requested at any given time from a single peer. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 777 Lines • ▼ Show 20 Lines | while (pindexWalk->nHeight < nMaxHeight) { | ||||
waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; | waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} // namespace | } // namespace | ||||
template <class InvId> | |||||
static bool TooManyAnnouncements(const CNode &node, | |||||
const InvRequestTracker<InvId> &requestTracker, | |||||
const DataRequestParameters &requestParams) { | |||||
return !node.HasPermission( | |||||
requestParams.bypass_request_limits_permissions) && | |||||
requestTracker.Count(node.GetId()) >= | |||||
requestParams.max_peer_announcements; | |||||
} | |||||
/** | |||||
* Compute the request time for this announcement, current time plus delays for: | |||||
* - nonpref_peer_delay for announcements from non-preferred connections | |||||
* - overloaded_peer_delay for announcements from peers which have at least | |||||
* max_peer_request_in_flight requests in flight (and don't have PF_RELAY). | |||||
*/ | |||||
template <class InvId> | |||||
static std::chrono::microseconds | |||||
ComputeRequestTime(const CNode &node, | |||||
const InvRequestTracker<InvId> &requestTracker, | |||||
const DataRequestParameters &requestParams, | |||||
std::chrono::microseconds current_time, bool preferred) { | |||||
auto delay = std::chrono::microseconds{0}; | |||||
if (!preferred) { | |||||
delay += requestParams.nonpref_peer_delay; | |||||
} | |||||
if (!node.HasPermission(requestParams.bypass_request_limits_permissions) && | |||||
requestTracker.CountInFlight(node.GetId()) >= | |||||
requestParams.max_peer_request_in_flight) { | |||||
delay += requestParams.overloaded_peer_delay; | |||||
} | |||||
return current_time + delay; | |||||
} | |||||
void PeerManager::AddTxAnnouncement(const CNode &node, const TxId &txid, | void PeerManager::AddTxAnnouncement(const CNode &node, const TxId &txid, | ||||
std::chrono::microseconds current_time) { | std::chrono::microseconds current_time) { | ||||
// For m_txrequest | // For m_txrequest and state | ||||
AssertLockHeld(::cs_main); | AssertLockHeld(::cs_main); | ||||
NodeId nodeid = node.GetId(); | |||||
if (!node.HasPermission(PF_RELAY) && | if (TooManyAnnouncements(node, m_txrequest, TX_REQUEST_PARAMS)) { | ||||
m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) { | |||||
// Too many queued announcements from this peer | |||||
return; | return; | ||||
} | } | ||||
const CNodeState *state = State(nodeid); | |||||
// Decide the InvRequestTracker parameters for this announcement: | const NodeId &nodeid = node.GetId(); | ||||
// - "preferred": if fPreferredDownload is set (= outbound, or PF_NOBAN | const CNodeState *state = State(nodeid); | ||||
// permission) | |||||
// - "reqtime": current time plus delays for: | |||||
// - NONPREF_PEER_TX_DELAY for announcements from non-preferred | |||||
// connections | |||||
// - 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 PF_RELAY). | |||||
auto delay = std::chrono::microseconds{0}; | |||||
const bool preferred = state->fPreferredDownload; | const bool preferred = state->fPreferredDownload; | ||||
if (!preferred) { | |||||
delay += NONPREF_PEER_TX_DELAY; | |||||
} | |||||
const bool overloaded = | auto reqtime = ComputeRequestTime(node, m_txrequest, TX_REQUEST_PARAMS, | ||||
!node.HasPermission(PF_RELAY) && | current_time, preferred); | ||||
m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT; | |||||
if (overloaded) { | m_txrequest.ReceivedInv(nodeid, txid, preferred, reqtime); | ||||
delay += OVERLOADED_PEER_TX_DELAY; | |||||
} | |||||
m_txrequest.ReceivedInv(nodeid, txid, preferred, current_time + delay); | |||||
} | } | ||||
// This function is used for testing the stale tip eviction logic, see | // This function is used for testing the stale tip eviction logic, see | ||||
// denialofservice_tests.cpp | // denialofservice_tests.cpp | ||||
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) { | void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CNodeState *state = State(node); | CNodeState *state = State(node); | ||||
if (state) { | if (state) { | ||||
▲ Show 20 Lines • Show All 3,391 Lines • ▼ Show 20 Lines | |||||
if (msg_type == NetMsgType::GETCFCHECKPT) { | if (msg_type == NetMsgType::GETCFCHECKPT) { | ||||
ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman); | ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman); | ||||
return; | return; | ||||
} | } | ||||
if (msg_type == NetMsgType::NOTFOUND) { | if (msg_type == NetMsgType::NOTFOUND) { | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
vRecv >> vInv; | vRecv >> vInv; | ||||
if (vInv.size() <= | if (vInv.size() <= TX_REQUEST_PARAMS.max_peer_announcements + | ||||
MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | ||||
LOCK(::cs_main); | LOCK(::cs_main); | ||||
for (CInv &inv : vInv) { | for (CInv &inv : vInv) { | ||||
if (inv.IsMsgTx()) { | if (inv.IsMsgTx()) { | ||||
// If we receive a NOTFOUND message for a tx we requested, | // If we receive a NOTFOUND message for a tx we requested, | ||||
// mark the announcement for it as completed in | // mark the announcement for it as completed in | ||||
// InvRequestTracker. | // InvRequestTracker. | ||||
m_txrequest.ReceivedResponse(pfrom.GetId(), TxId(inv.hash)); | m_txrequest.ReceivedResponse(pfrom.GetId(), TxId(inv.hash)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,045 Lines • ▼ Show 20 Lines | // | ||||
m_txrequest.GetRequestable(pto->GetId(), current_time, &expired); | m_txrequest.GetRequestable(pto->GetId(), current_time, &expired); | ||||
for (const auto &entry : expired) { | for (const auto &entry : expired) { | ||||
LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", | LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", | ||||
entry.second.ToString(), entry.first); | entry.second.ToString(), entry.first); | ||||
} | } | ||||
for (const TxId &txid : requestable) { | for (const TxId &txid : requestable) { | ||||
if (!AlreadyHaveTx(txid, m_mempool)) { | if (!AlreadyHaveTx(txid, m_mempool)) { | ||||
addGetDataAndMaybeFlush(MSG_TX, txid); | addGetDataAndMaybeFlush(MSG_TX, txid); | ||||
m_txrequest.RequestedData(pto->GetId(), txid, | m_txrequest.RequestedData( | ||||
current_time + GETDATA_TX_INTERVAL); | pto->GetId(), txid, | ||||
current_time + TX_REQUEST_PARAMS.getdata_interval); | |||||
} else { | } else { | ||||
// We have already seen this transaction, no need to download. | // We have already seen this transaction, no need to download. | ||||
// This is just a belt-and-suspenders, as this should already be | // This is just a belt-and-suspenders, as this should already be | ||||
// called whenever a transaction becomes AlreadyHaveTx(). | // called whenever a transaction becomes AlreadyHaveTx(). | ||||
m_txrequest.ForgetInvId(txid); | m_txrequest.ForgetInvId(txid); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 80 Lines • Show Last 20 Lines |