Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | |||||
/// Age after which a block is considered historical for purposes of rate | /// Age after which a block is considered historical for purposes of rate | ||||
/// limiting block relay. Set to one week, denominated in seconds. | /// limiting block relay. Set to one week, denominated in seconds. | ||||
static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; | static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; | ||||
/** Maximum number of in-flight transactions from a peer */ | /** Maximum number of in-flight transactions from a peer */ | ||||
static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100; | static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100; | ||||
/** Maximum number of announced transactions from a peer */ | /** Maximum number of announced transactions from a peer */ | ||||
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ; | static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ; | ||||
/** How many microseconds to delay requesting transactions from inbound peers */ | /** How many microseconds to delay requesting transactions from inbound peers */ | ||||
// 2 seconds | static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{ | ||||
static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; | std::chrono::seconds{2}}; | ||||
/** | /** | ||||
* How long to wait (in microseconds) before downloading a transaction from an | * How long to wait (in microseconds) before downloading a transaction from an | ||||
* additional peer. | * additional peer. | ||||
*/ | */ | ||||
// 1 minute | static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{ | ||||
static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; | std::chrono::seconds{60}}; | ||||
/** | /** | ||||
* Maximum delay (in microseconds) for transaction requests to avoid biasing | * Maximum delay (in microseconds) for transaction requests to avoid biasing | ||||
* some peers over others. | * some peers over others. | ||||
*/ | */ | ||||
// 2 seconds | static constexpr std::chrono::microseconds MAX_GETDATA_RANDOM_DELAY{ | ||||
static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; | std::chrono::seconds{2}}; | ||||
/** | /** | ||||
* How long to wait (in microseconds) before expiring an in-flight getdata | * How long to wait (in microseconds) before expiring an in-flight getdata | ||||
* request to a peer. | * request to a peer. | ||||
*/ | */ | ||||
static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL; | static constexpr std::chrono::microseconds TX_EXPIRY_INTERVAL{ | ||||
GETDATA_TX_INTERVAL * 10}; | |||||
static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY, | static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY, | ||||
"To preserve security, MAX_GETDATA_RANDOM_DELAY should not " | "To preserve security, MAX_GETDATA_RANDOM_DELAY should not " | ||||
"exceed INBOUND_PEER_DELAY"); | "exceed INBOUND_PEER_DELAY"); | ||||
/** | /** | ||||
* 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; | ||||
▲ Show 20 Lines • Show All 281 Lines • ▼ Show 20 Lines | struct CNodeState { | ||||
* the reject filter, then we will eventually redownload from other | * the reject filter, then we will eventually redownload from other | ||||
* peers. | * peers. | ||||
*/ | */ | ||||
struct TxDownloadState { | struct TxDownloadState { | ||||
/** | /** | ||||
* Track when to attempt download of announced transactions (process | * Track when to attempt download of announced transactions (process | ||||
* time in micros -> txid) | * time in micros -> txid) | ||||
*/ | */ | ||||
std::multimap<int64_t, TxId> m_tx_process_time; | std::multimap<std::chrono::microseconds, TxId> m_tx_process_time; | ||||
//! Store all the transactions a peer has recently announced | //! Store all the transactions a peer has recently announced | ||||
std::set<TxId> m_tx_announced; | std::set<TxId> m_tx_announced; | ||||
//! Store transactions which were requested by us, with timestamp | //! Store transactions which were requested by us, with timestamp | ||||
std::map<TxId, int64_t> m_tx_in_flight; | std::map<TxId, std::chrono::microseconds> m_tx_in_flight; | ||||
//! Periodically check for stuck getdata requests | //! Periodically check for stuck getdata requests | ||||
int64_t m_check_expiry_timer{0}; | std::chrono::microseconds m_check_expiry_timer{0}; | ||||
}; | }; | ||||
TxDownloadState m_tx_download; | TxDownloadState m_tx_download; | ||||
struct AvalancheState { | struct AvalancheState { | ||||
std::chrono::time_point<std::chrono::steady_clock> last_poll; | std::chrono::time_point<std::chrono::steady_clock> last_poll; | ||||
}; | }; | ||||
Show All 22 Lines | CNodeState(CAddress addrIn, std::string addrNameIn) | ||||
fSupportsDesiredCmpctVersion = false; | fSupportsDesiredCmpctVersion = false; | ||||
m_chain_sync = {0, nullptr, false, false}; | m_chain_sync = {0, nullptr, false, false}; | ||||
m_last_block_announcement = 0; | m_last_block_announcement = 0; | ||||
} | } | ||||
}; | }; | ||||
// Keeps track of the time (in microseconds) when transactions were requested | // Keeps track of the time (in microseconds) when transactions were requested | ||||
// last time | // last time | ||||
limitedmap<TxId, int64_t> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ); | limitedmap<TxId, std::chrono::microseconds> | ||||
g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ); | |||||
/** Map maintaining per-node state. */ | /** Map maintaining per-node state. */ | ||||
static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main); | static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main); | ||||
static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); | std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); | ||||
if (it == mapNodeState.end()) { | if (it == mapNodeState.end()) { | ||||
return nullptr; | return nullptr; | ||||
▲ Show 20 Lines • Show All 368 Lines • ▼ Show 20 Lines | while (pindexWalk->nHeight < nMaxHeight) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void EraseTxRequest(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | void EraseTxRequest(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
g_already_asked_for.erase(txid); | g_already_asked_for.erase(txid); | ||||
} | } | ||||
int64_t GetTxRequestTime(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | std::chrono::microseconds GetTxRequestTime(const TxId &txid) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | |||||
auto it = g_already_asked_for.find(txid); | auto it = g_already_asked_for.find(txid); | ||||
if (it != g_already_asked_for.end()) { | if (it != g_already_asked_for.end()) { | ||||
return it->second; | return it->second; | ||||
} | } | ||||
return 0; | return {}; | ||||
} | } | ||||
void UpdateTxRequestTime(const TxId &txid, int64_t request_time) | void UpdateTxRequestTime(const TxId &txid, | ||||
std::chrono::microseconds request_time) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
auto it = g_already_asked_for.find(txid); | auto it = g_already_asked_for.find(txid); | ||||
if (it == g_already_asked_for.end()) { | if (it == g_already_asked_for.end()) { | ||||
g_already_asked_for.insert(std::make_pair(txid, request_time)); | g_already_asked_for.insert(std::make_pair(txid, request_time)); | ||||
} else { | } else { | ||||
g_already_asked_for.update(it, request_time); | g_already_asked_for.update(it, request_time); | ||||
} | } | ||||
} | } | ||||
int64_t CalculateTxGetDataTime(const TxId &txid, int64_t current_time, | std::chrono::microseconds | ||||
CalculateTxGetDataTime(const TxId &txid, std::chrono::microseconds current_time, | |||||
bool use_inbound_delay) | bool use_inbound_delay) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
int64_t process_time; | std::chrono::microseconds process_time; | ||||
int64_t last_request_time = GetTxRequestTime(txid); | const auto last_request_time = GetTxRequestTime(txid); | ||||
// First time requesting this tx | // First time requesting this tx | ||||
if (last_request_time == 0) { | if (last_request_time.count() == 0) { | ||||
process_time = current_time; | process_time = current_time; | ||||
} else { | } else { | ||||
// Randomize the delay to avoid biasing some peers over others (such as | // Randomize the delay to avoid biasing some peers over others (such as | ||||
// due to fixed ordering of peer processing in ThreadMessageHandler) | // due to fixed ordering of peer processing in ThreadMessageHandler) | ||||
process_time = last_request_time + GETDATA_TX_INTERVAL + | process_time = last_request_time + GETDATA_TX_INTERVAL + | ||||
GetRand(MAX_GETDATA_RANDOM_DELAY); | GetRandMicros(MAX_GETDATA_RANDOM_DELAY); | ||||
} | } | ||||
// We delay processing announcements from inbound peers | // We delay processing announcements from inbound peers | ||||
if (use_inbound_delay) { | if (use_inbound_delay) { | ||||
process_time += INBOUND_PEER_TX_DELAY; | process_time += INBOUND_PEER_TX_DELAY; | ||||
} | } | ||||
return process_time; | return process_time; | ||||
} | } | ||||
void RequestTx(CNodeState *state, const TxId &txid, int64_t nNow) | void RequestTx(CNodeState *state, const TxId &txid, | ||||
std::chrono::microseconds current_time) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
CNodeState::TxDownloadState &peer_download_state = state->m_tx_download; | CNodeState::TxDownloadState &peer_download_state = state->m_tx_download; | ||||
if (peer_download_state.m_tx_announced.size() >= | if (peer_download_state.m_tx_announced.size() >= | ||||
MAX_PEER_TX_ANNOUNCEMENTS || | MAX_PEER_TX_ANNOUNCEMENTS || | ||||
peer_download_state.m_tx_process_time.size() >= | peer_download_state.m_tx_process_time.size() >= | ||||
MAX_PEER_TX_ANNOUNCEMENTS || | MAX_PEER_TX_ANNOUNCEMENTS || | ||||
peer_download_state.m_tx_announced.count(txid)) { | peer_download_state.m_tx_announced.count(txid)) { | ||||
// Too many queued announcements from this peer, or we already have | // Too many queued announcements from this peer, or we already have | ||||
// this announcement | // this announcement | ||||
return; | return; | ||||
} | } | ||||
peer_download_state.m_tx_announced.insert(txid); | peer_download_state.m_tx_announced.insert(txid); | ||||
// Calculate the time to try requesting this transaction. Use | // Calculate the time to try requesting this transaction. Use | ||||
// fPreferredDownload as a proxy for outbound peers. | // fPreferredDownload as a proxy for outbound peers. | ||||
int64_t process_time = | const auto process_time = | ||||
CalculateTxGetDataTime(txid, nNow, !state->fPreferredDownload); | CalculateTxGetDataTime(txid, current_time, !state->fPreferredDownload); | ||||
peer_download_state.m_tx_process_time.emplace(process_time, txid); | peer_download_state.m_tx_process_time.emplace(process_time, txid); | ||||
} | } | ||||
} // namespace | } // namespace | ||||
// 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 | ||||
▲ Show 20 Lines • Show All 1,553 Lines • ▼ Show 20 Lines | if (strCommand == NetMsgType::INV) { | ||||
// Allow whitelisted peers to send data other than blocks in blocks only | // Allow whitelisted peers to send data other than blocks in blocks only | ||||
// mode if whitelistrelay is true | // mode if whitelistrelay is true | ||||
if (pfrom->HasPermission(PF_RELAY)) { | if (pfrom->HasPermission(PF_RELAY)) { | ||||
fBlocksOnly = false; | fBlocksOnly = false; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
int64_t nNow = GetTimeMicros(); | const auto current_time = GetTime<std::chrono::microseconds>(); | ||||
for (CInv &inv : vInv) { | for (CInv &inv : vInv) { | ||||
if (interruptMsgProc) { | if (interruptMsgProc) { | ||||
return true; | return true; | ||||
} | } | ||||
bool fAlreadyHave = AlreadyHave(inv); | bool fAlreadyHave = AlreadyHave(inv); | ||||
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), | LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), | ||||
Show All 24 Lines | if (strCommand == NetMsgType::INV) { | ||||
pfrom->AddInventoryKnown(inv); | pfrom->AddInventoryKnown(inv); | ||||
if (fBlocksOnly) { | if (fBlocksOnly) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"transaction (%s) inv sent in violation of " | "transaction (%s) inv sent in violation of " | ||||
"protocol peer=%d\n", | "protocol peer=%d\n", | ||||
inv.hash.ToString(), pfrom->GetId()); | inv.hash.ToString(), pfrom->GetId()); | ||||
} else if (!fAlreadyHave && !fImporting && !fReindex && | } else if (!fAlreadyHave && !fImporting && !fReindex && | ||||
!::ChainstateActive().IsInitialBlockDownload()) { | !::ChainstateActive().IsInitialBlockDownload()) { | ||||
RequestTx(State(pfrom->GetId()), TxId(inv.hash), nNow); | RequestTx(State(pfrom->GetId()), TxId(inv.hash), | ||||
current_time); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (strCommand == NetMsgType::GETDATA) { | if (strCommand == NetMsgType::GETDATA) { | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
▲ Show 20 Lines • Show All 367 Lines • ▼ Show 20 Lines | if (strCommand == NetMsgType::TX) { | ||||
bool fRejectedParents = false; | bool fRejectedParents = false; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (recentRejects->contains(txin.prevout.GetTxId())) { | if (recentRejects->contains(txin.prevout.GetTxId())) { | ||||
fRejectedParents = true; | fRejectedParents = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (!fRejectedParents) { | if (!fRejectedParents) { | ||||
int64_t nNow = GetTimeMicros(); | const auto current_time = GetTime<std::chrono::microseconds>(); | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
// FIXME: MSG_TX should use a TxHash, not a TxId. | // FIXME: MSG_TX should use a TxHash, not a TxId. | ||||
const TxId _txid = txin.prevout.GetTxId(); | const TxId _txid = txin.prevout.GetTxId(); | ||||
CInv _inv(MSG_TX, _txid); | CInv _inv(MSG_TX, _txid); | ||||
pfrom->AddInventoryKnown(_inv); | pfrom->AddInventoryKnown(_inv); | ||||
if (!AlreadyHave(_inv)) { | if (!AlreadyHave(_inv)) { | ||||
RequestTx(State(pfrom->GetId()), _txid, nNow); | RequestTx(State(pfrom->GetId()), _txid, current_time); | ||||
} | } | ||||
} | } | ||||
AddOrphanTx(ptx, pfrom->GetId()); | AddOrphanTx(ptx, pfrom->GetId()); | ||||
// DoS prevention: do not allow mapOrphanTransactions to grow | // DoS prevention: do not allow mapOrphanTransactions to grow | ||||
// unbounded | // unbounded | ||||
unsigned int nMaxOrphanTx = (unsigned int)std::max( | unsigned int nMaxOrphanTx = (unsigned int)std::max( | ||||
int64_t(0), gArgs.GetArg("-maxorphantx", | int64_t(0), gArgs.GetArg("-maxorphantx", | ||||
▲ Show 20 Lines • Show All 1,714 Lines • ▼ Show 20 Lines | std::vector<CInv> vInv; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!vInv.empty()) { | if (!vInv.empty()) { | ||||
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); | connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); | ||||
} | } | ||||
// Detect whether we're stalling | // Detect whether we're stalling | ||||
const auto current_time = GetTime<std::chrono::microseconds>(); | |||||
// nNow is the current system time (GetTimeMicros is not mockable) and | |||||
// should be replaced by the mockable current_time eventually | |||||
nNow = GetTimeMicros(); | nNow = GetTimeMicros(); | ||||
if (state.nStallingSince && | if (state.nStallingSince && | ||||
state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { | state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { | ||||
// Stalling only triggers when the block download window cannot move. | // Stalling only triggers when the block download window cannot move. | ||||
// During normal steady state, the download window should be much larger | // During normal steady state, the download window should be much larger | ||||
// than the to-be-downloaded set of blocks, so disconnection should only | // than the to-be-downloaded set of blocks, so disconnection should only | ||||
// happen during initial block download. | // happen during initial block download. | ||||
LogPrintf("Peer=%d is stalling block download, disconnecting\n", | LogPrintf("Peer=%d is stalling block download, disconnecting\n", | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | bool PeerLogicValidation::SendMessages(const Config &config, CNode *pto, | ||||
// | // | ||||
// Message: getdata (transactions) | // Message: getdata (transactions) | ||||
// | // | ||||
// For robustness, expire old requests after a long timeout, so that we can | // For robustness, expire old requests after a long timeout, so that we can | ||||
// resume downloading transactions from a peer even if they were | // resume downloading transactions from a peer even if they were | ||||
// unresponsive in the past. Eventually we should consider disconnecting | // unresponsive in the past. Eventually we should consider disconnecting | ||||
// peers, but this is conservative. | // peers, but this is conservative. | ||||
if (state.m_tx_download.m_check_expiry_timer <= nNow) { | if (state.m_tx_download.m_check_expiry_timer <= current_time) { | ||||
for (auto it = state.m_tx_download.m_tx_in_flight.begin(); | for (auto it = state.m_tx_download.m_tx_in_flight.begin(); | ||||
it != state.m_tx_download.m_tx_in_flight.end();) { | it != state.m_tx_download.m_tx_in_flight.end();) { | ||||
if (it->second <= nNow - TX_EXPIRY_INTERVAL) { | if (it->second <= current_time - TX_EXPIRY_INTERVAL) { | ||||
LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", | LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", | ||||
it->first.ToString(), pto->GetId()); | it->first.ToString(), pto->GetId()); | ||||
state.m_tx_download.m_tx_announced.erase(it->first); | state.m_tx_download.m_tx_announced.erase(it->first); | ||||
state.m_tx_download.m_tx_in_flight.erase(it++); | state.m_tx_download.m_tx_in_flight.erase(it++); | ||||
} else { | } else { | ||||
++it; | ++it; | ||||
} | } | ||||
} | } | ||||
// On average, we do this check every TX_EXPIRY_INTERVAL. Randomize | // On average, we do this check every TX_EXPIRY_INTERVAL. Randomize | ||||
// so that we're not doing this for all peers at the same time. | // so that we're not doing this for all peers at the same time. | ||||
state.m_tx_download.m_check_expiry_timer = | state.m_tx_download.m_check_expiry_timer = | ||||
nNow + TX_EXPIRY_INTERVAL / 2 + GetRand(TX_EXPIRY_INTERVAL); | current_time + TX_EXPIRY_INTERVAL / 2 + | ||||
GetRandMicros(TX_EXPIRY_INTERVAL); | |||||
} | } | ||||
auto &tx_process_time = state.m_tx_download.m_tx_process_time; | auto &tx_process_time = state.m_tx_download.m_tx_process_time; | ||||
while (!tx_process_time.empty() && tx_process_time.begin()->first <= nNow && | while (!tx_process_time.empty() && | ||||
tx_process_time.begin()->first <= current_time && | |||||
state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) { | state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) { | ||||
const TxId txid = tx_process_time.begin()->second; | const TxId txid = tx_process_time.begin()->second; | ||||
// Erase this entry from tx_process_time (it may be added back for | // Erase this entry from tx_process_time (it may be added back for | ||||
// processing at a later time, see below) | // processing at a later time, see below) | ||||
tx_process_time.erase(tx_process_time.begin()); | tx_process_time.erase(tx_process_time.begin()); | ||||
CInv inv(MSG_TX, txid); | CInv inv(MSG_TX, txid); | ||||
if (!AlreadyHave(inv)) { | if (!AlreadyHave(inv)) { | ||||
// If this transaction was last requested more than 1 minute ago, | // If this transaction was last requested more than 1 minute ago, | ||||
// then request. | // then request. | ||||
int64_t last_request_time = GetTxRequestTime(txid); | const auto last_request_time = GetTxRequestTime(txid); | ||||
if (last_request_time <= nNow - GETDATA_TX_INTERVAL) { | if (last_request_time <= current_time - GETDATA_TX_INTERVAL) { | ||||
LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), | LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), | ||||
pto->GetId()); | pto->GetId()); | ||||
vGetData.push_back(inv); | vGetData.push_back(inv); | ||||
if (vGetData.size() >= MAX_GETDATA_SZ) { | if (vGetData.size() >= MAX_GETDATA_SZ) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); | pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); | ||||
vGetData.clear(); | vGetData.clear(); | ||||
} | } | ||||
UpdateTxRequestTime(txid, nNow); | UpdateTxRequestTime(txid, current_time); | ||||
state.m_tx_download.m_tx_in_flight.emplace(txid, nNow); | state.m_tx_download.m_tx_in_flight.emplace(txid, current_time); | ||||
} else { | } else { | ||||
// This transaction is in flight from someone else; queue | // This transaction is in flight from someone else; queue | ||||
// up processing to happen after the download times out | // up processing to happen after the download times out | ||||
// (with a slight delay for inbound peers, to prefer | // (with a slight delay for inbound peers, to prefer | ||||
// requests to outbound peers). | // requests to outbound peers). | ||||
int64_t next_process_time = CalculateTxGetDataTime( | const auto next_process_time = CalculateTxGetDataTime( | ||||
txid, nNow, !state.fPreferredDownload); | txid, current_time, !state.fPreferredDownload); | ||||
tx_process_time.emplace(next_process_time, txid); | tx_process_time.emplace(next_process_time, txid); | ||||
} | } | ||||
} else { | } else { | ||||
// We have already seen this transaction, no need to download. | // We have already seen this transaction, no need to download. | ||||
state.m_tx_download.m_tx_announced.erase(txid); | state.m_tx_download.m_tx_announced.erase(txid); | ||||
state.m_tx_download.m_tx_in_flight.erase(txid); | state.m_tx_download.m_tx_in_flight.erase(txid); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 58 Lines • Show Last 20 Lines |