Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 1,965 Lines • ▼ Show 20 Lines | if (send && pindex->nStatus.hasData()) { | ||||
vInv.push_back( | vInv.push_back( | ||||
CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash())); | CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash())); | ||||
connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | ||||
pfrom.hashContinue = BlockHash(); | pfrom.hashContinue = BlockHash(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
//! Determine whether or not a peer can request a transaction, and return it (or | |||||
//! nullptr if not found or not allowed). | |||||
CTransactionRef static FindTxForGetData( | |||||
const CNode &peer, const TxId &txid, const std::chrono::seconds mempool_req, | |||||
const std::chrono::seconds longlived_mempool_time) LOCKS_EXCLUDED(cs_main) { | |||||
// Check if the requested transaction is so recent that we're just | |||||
// about to announce it to the peer; if so, they certainly shouldn't | |||||
// know we already have it. | |||||
{ | |||||
LOCK(peer.m_tx_relay->cs_tx_inventory); | |||||
if (peer.m_tx_relay->setInventoryTxToSend.count(txid)) { | |||||
return {}; | |||||
} | |||||
} | |||||
{ | |||||
LOCK(cs_main); | |||||
// Look up transaction in relay pool | |||||
auto mi = mapRelay.find(txid); | |||||
if (mi != mapRelay.end()) { | |||||
return mi->second; | |||||
} | |||||
} | |||||
auto txinfo = g_mempool.info(txid); | |||||
if (txinfo.tx) { | |||||
// To protect privacy, do not answer getdata using the mempool when | |||||
// that TX couldn't have been INVed in reply to a MEMPOOL request, | |||||
// or when it's too recent to have expired from mapRelay. | |||||
if ((mempool_req.count() && txinfo.m_time <= mempool_req) || | |||||
txinfo.m_time <= longlived_mempool_time) { | |||||
return txinfo.tx; | |||||
} | |||||
} | |||||
return {}; | |||||
} | |||||
static void ProcessGetData(const Config &config, CNode &pfrom, | static void ProcessGetData(const Config &config, CNode &pfrom, | ||||
CConnman &connman, CTxMemPool &mempool, | CConnman &connman, CTxMemPool &mempool, | ||||
const std::atomic<bool> &interruptMsgProc) | const std::atomic<bool> &interruptMsgProc) | ||||
LOCKS_EXCLUDED(cs_main) { | LOCKS_EXCLUDED(cs_main) { | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin(); | std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin(); | ||||
std::vector<CInv> vNotFound; | std::vector<CInv> vNotFound; | ||||
const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | ||||
// mempool entries added before this time have likely expired from mapRelay | // mempool entries added before this time have likely expired from mapRelay | ||||
const std::chrono::seconds longlived_mempool_time = | const std::chrono::seconds longlived_mempool_time = | ||||
GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; | GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; | ||||
// Get last mempool request time | // Get last mempool request time | ||||
const std::chrono::seconds mempool_req = | const std::chrono::seconds mempool_req = | ||||
pfrom.m_tx_relay != nullptr | pfrom.m_tx_relay != nullptr | ||||
? pfrom.m_tx_relay->m_last_mempool_req.load() | ? pfrom.m_tx_relay->m_last_mempool_req.load() | ||||
: std::chrono::seconds::min(); | : std::chrono::seconds::min(); | ||||
{ | |||||
LOCK(cs_main); | |||||
// 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. | ||||
while (it != pfrom.vRecvGetData.end() && it->type == MSG_TX) { | while (it != pfrom.vRecvGetData.end() && it->type == MSG_TX) { | ||||
if (interruptMsgProc) { | if (interruptMsgProc) { | ||||
return; | return; | ||||
} | } | ||||
// The send buffer provides backpressure. If there's no space in | // The send buffer provides backpressure. If there's no space in | ||||
// the buffer, pause processing until the next call. | // the buffer, pause processing until the next call. | ||||
if (pfrom.fPauseSend) { | if (pfrom.fPauseSend) { | ||||
break; | break; | ||||
} | } | ||||
const CInv &inv = *it++; | const CInv &inv = *it++; | ||||
if (pfrom.m_tx_relay == nullptr) { | if (pfrom.m_tx_relay == nullptr) { | ||||
// Ignore GETDATA requests for transactions from blocks-only | // Ignore GETDATA requests for transactions from blocks-only | ||||
// peers. | // peers. | ||||
continue; | continue; | ||||
} | } | ||||
// Send stream from relay memory | CTransactionRef tx = FindTxForGetData( | ||||
bool push = false; | pfrom, TxId{inv.hash}, mempool_req, longlived_mempool_time); | ||||
auto mi = mapRelay.find(inv.hash); | if (tx) { | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
if (mi != mapRelay.end()) { | connman.PushMessage(&pfrom, | ||||
connman.PushMessage( | msgMaker.Make(nSendFlags, NetMsgType::TX, *tx)); | ||||
&pfrom, | |||||
msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); | |||||
push = true; | |||||
} else { | |||||
auto txinfo = mempool.info(TxId(inv.hash)); | |||||
// To protect privacy, do not answer getdata using the mempool | |||||
// when that TX couldn't have been INVed in reply to a MEMPOOL | |||||
// request, or when it's too recent to have expired from | |||||
// mapRelay. | |||||
if (txinfo.tx && | |||||
((mempool_req.count() && txinfo.m_time <= mempool_req) || | |||||
(txinfo.m_time <= longlived_mempool_time))) { | |||||
connman.PushMessage( | |||||
&pfrom, | |||||
msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); | |||||
push = true; | |||||
} | |||||
} | |||||
if (push) { | |||||
// We interpret fulfilling a GETDATA for a transaction as a | |||||
// successful initial broadcast and remove it from our | |||||
// unbroadcast set. | |||||
mempool.RemoveUnbroadcastTx(TxId(inv.hash)); | mempool.RemoveUnbroadcastTx(TxId(inv.hash)); | ||||
} else { | } else { | ||||
vNotFound.push_back(inv); | vNotFound.push_back(inv); | ||||
} | } | ||||
} | } | ||||
} // release cs_main | |||||
// Only process one BLOCK item per call, since they're uncommon and can be | // Only process one BLOCK item per call, since they're uncommon and can be | ||||
// expensive to process. | // expensive to process. | ||||
if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) { | if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) { | ||||
const CInv &inv = *it++; | const CInv &inv = *it++; | ||||
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || | if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || | ||||
inv.type == MSG_CMPCT_BLOCK) { | inv.type == MSG_CMPCT_BLOCK) { | ||||
ProcessGetBlockData(config, pfrom, inv, connman, interruptMsgProc); | ProcessGetBlockData(config, pfrom, inv, connman, interruptMsgProc); | ||||
▲ Show 20 Lines • Show All 3,455 Lines • Show Last 20 Lines |