diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1971,6 +1971,44 @@ } } +//! 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, CConnman &connman, CTxMemPool &mempool, const std::atomic &interruptMsgProc) @@ -1990,65 +2028,38 @@ ? pfrom.m_tx_relay->m_last_mempool_req.load() : std::chrono::seconds::min(); - { - LOCK(cs_main); - - // 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 - // them. - while (it != pfrom.vRecvGetData.end() && it->type == MSG_TX) { - if (interruptMsgProc) { - return; - } - // The send buffer provides backpressure. If there's no space in - // the buffer, pause processing until the next call. - if (pfrom.fPauseSend) { - break; - } + // 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 + // them. + while (it != pfrom.vRecvGetData.end() && it->type == MSG_TX) { + if (interruptMsgProc) { + return; + } + // The send buffer provides backpressure. If there's no space in + // the buffer, pause processing until the next call. + if (pfrom.fPauseSend) { + break; + } - const CInv &inv = *it++; + const CInv &inv = *it++; - if (pfrom.m_tx_relay == nullptr) { - // Ignore GETDATA requests for transactions from blocks-only - // peers. - continue; - } + if (pfrom.m_tx_relay == nullptr) { + // Ignore GETDATA requests for transactions from blocks-only + // peers. + continue; + } - // Send stream from relay memory - bool push = false; - auto mi = mapRelay.find(inv.hash); + CTransactionRef tx = FindTxForGetData( + pfrom, TxId{inv.hash}, mempool_req, longlived_mempool_time); + if (tx) { int nSendFlags = 0; - if (mi != mapRelay.end()) { - connman.PushMessage( - &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)); - } else { - vNotFound.push_back(inv); - } + connman.PushMessage(&pfrom, + msgMaker.Make(nSendFlags, NetMsgType::TX, *tx)); + mempool.RemoveUnbroadcastTx(TxId(inv.hash)); + } else { + vNotFound.push_back(inv); } - } // release cs_main + } // Only process one BLOCK item per call, since they're uncommon and can be // expensive to process.