@@ -362,6 +362,9 @@ struct CNodeState {
362362 // ! Whether this peer is a manual connection
363363 bool m_is_manual_connection;
364364
365+ // ! Whether this peer relays txs via wtxid
366+ bool m_wtxid_relay{false };
367+
365368 CNodeState (CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
366369 address (addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
367370 m_is_manual_connection (is_manual)
@@ -1332,6 +1335,7 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
13321335 {
13331336 case MSG_TX:
13341337 case MSG_WITNESS_TX:
1338+ case MSG_WTX:
13351339 {
13361340 assert (recentRejects);
13371341 if (::ChainActive ().Tip ()->GetBlockHash () != hashRecentRejectsChainTip)
@@ -1346,16 +1350,20 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
13461350
13471351 {
13481352 LOCK (g_cs_orphans);
1349- if (mapOrphanTransactions.count (inv.hash )) return true ;
1353+ if (inv.type != MSG_WTX && mapOrphanTransactions.count (inv.hash )) {
1354+ return true ;
1355+ } else if (inv.type == MSG_WTX && g_orphans_by_wtxid.count (inv.hash )) {
1356+ return true ;
1357+ }
13501358 }
13511359
13521360 {
13531361 LOCK (g_cs_recent_confirmed_transactions);
13541362 if (g_recent_confirmed_transactions->contains (inv.hash )) return true ;
13551363 }
13561364
1357- return recentRejects-> contains (inv.hash ) ||
1358- mempool.exists (inv.hash );
1365+ const bool by_wtxid = (inv.type == MSG_WTX);
1366+ return recentRejects-> contains (inv. hash ) || mempool.exists (inv.hash , by_wtxid );
13591367 }
13601368 case MSG_BLOCK:
13611369 case MSG_WITNESS_BLOCK:
@@ -1365,12 +1373,17 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
13651373 return true ;
13661374}
13671375
1368- void RelayTransaction (const uint256& txid, const CConnman& connman)
1376+ void RelayTransaction (const uint256& txid, const uint256& wtxid, const CConnman& connman)
13691377{
1370- CInv inv (MSG_TX, txid);
1371- connman.ForEachNode ([&inv](CNode* pnode)
1378+ connman.ForEachNode ([&txid, &wtxid](CNode* pnode)
13721379 {
1373- pnode->PushInventory (inv);
1380+ AssertLockHeld (cs_main);
1381+ CNodeState &state = *State (pnode->GetId ());
1382+ if (state.m_wtxid_relay ) {
1383+ pnode->PushInventory ({MSG_TX, wtxid}); // inv type is MSG_TX even for wtxid relay
1384+ } else {
1385+ pnode->PushInventory ({MSG_TX, txid});
1386+ }
13741387 });
13751388}
13761389
@@ -1585,7 +1598,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
15851598 // Process as many TX items from the front of the getdata queue as
15861599 // possible, since they're common and it's efficient to batch process
15871600 // them.
1588- while (it != pfrom->vRecvGetData .end () && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
1601+ while (it != pfrom->vRecvGetData .end () && (it->type == MSG_TX || it->type == MSG_WITNESS_TX || it-> type == MSG_WTX )) {
15891602 if (interruptMsgProc)
15901603 return ;
15911604 // The send buffer provides backpressure. If there's no space in
@@ -1608,7 +1621,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
16081621 connman->PushMessage (pfrom, msgMaker.Make (nSendFlags, NetMsgType::TX, *mi->second ));
16091622 push = true ;
16101623 } else {
1611- auto txinfo = mempool.info (inv.hash );
1624+ auto txinfo = mempool.info (inv.hash , inv. type == MSG_WTX );
16121625 // To protect privacy, do not answer getdata using the mempool when
16131626 // that TX couldn't have been INVed in reply to a MEMPOOL request,
16141627 // or when it's too recent to have expired from mapRelay.
@@ -1888,7 +1901,7 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
18881901 if (setMisbehaving.count (fromPeer)) continue ;
18891902 if (AcceptToMemoryPool (mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */ , 0 /* nAbsurdFee */ )) {
18901903 LogPrint (BCLog::MEMPOOL, " accepted orphan tx %s\n " , orphanHash.ToString ());
1891- RelayTransaction (orphanHash, *connman);
1904+ RelayTransaction (orphanHash, porphanTx-> GetWitnessHash (), *connman);
18921905 for (unsigned int i = 0 ; i < orphanTx.vout .size (); i++) {
18931906 auto it_by_prev = mapOrphanTransactionsByPrev.find (COutPoint (orphanHash, i));
18941907 if (it_by_prev != mapOrphanTransactionsByPrev.end ()) {
@@ -2559,23 +2572,47 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
25592572 const CTransaction& tx = *ptx;
25602573
25612574 const uint256& txid = ptx->GetHash ();
2562- pfrom-> AddInventoryKnown (txid );
2575+ const uint256& wtxid = ptx-> GetWitnessHash ( );
25632576
25642577 LOCK2 (cs_main, g_cs_orphans);
25652578
2579+ CNodeState* nodestate = State (pfrom->GetId ());
2580+
2581+ const uint256& hash = nodestate->m_wtxid_relay ? wtxid : txid;
2582+ pfrom->AddInventoryKnown (hash);
2583+ if (nodestate->m_wtxid_relay && txid != wtxid) {
2584+ // Insert txid into filterInventoryKnown, even for
2585+ // wtxidrelay peers. This prevents re-adding of
2586+ // unconfirmed parents to the recently_announced
2587+ // filter, when a child tx is requested. See
2588+ // ProcessGetData().
2589+ pfrom->AddInventoryKnown (txid);
2590+ }
2591+
25662592 TxValidationState state;
25672593
2568- CNodeState* nodestate = State (pfrom->GetId ());
2569- nodestate->m_tx_download .m_tx_announced .erase (txid);
2570- nodestate->m_tx_download .m_tx_in_flight .erase (txid);
2571- EraseTxRequest (txid);
2594+ nodestate->m_tx_download .m_tx_announced .erase (hash);
2595+ nodestate->m_tx_download .m_tx_in_flight .erase (hash);
2596+ EraseTxRequest (hash);
25722597
25732598 std::list<CTransactionRef> lRemovedTxn;
25742599
2575- if (!AlreadyHave (CInv (MSG_TX, txid), mempool) &&
2600+ // We do the AlreadyHave() check using wtxid, rather than txid - in the
2601+ // absence of witness malleation, this is strictly better, because the
2602+ // recent rejects filter may contain the wtxid but will never contain
2603+ // the txid of a segwit transaction that has been rejected.
2604+ // In the presence of witness malleation, it's possible that by only
2605+ // doing the check with wtxid, we could overlook a transaction which
2606+ // was confirmed with a different witness, or exists in our mempool
2607+ // with a different witness, but this has limited downside:
2608+ // mempool validation does its own lookup of whether we have the txid
2609+ // already; and an adversary can already relay us old transactions
2610+ // (older than our recency filter) if trying to DoS us, without any need
2611+ // for witness malleation.
2612+ if (!AlreadyHave (CInv (MSG_WTX, wtxid), mempool) &&
25762613 AcceptToMemoryPool (mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */ , 0 /* nAbsurdFee */ )) {
25772614 mempool.check (&::ChainstateActive ().CoinsTip ());
2578- RelayTransaction (tx.GetHash (), *connman);
2615+ RelayTransaction (tx.GetHash (), tx. GetWitnessHash (), *connman);
25792616 for (unsigned int i = 0 ; i < tx.vout .size (); i++) {
25802617 auto it_by_prev = mapOrphanTransactionsByPrev.find (COutPoint (txid, i));
25812618 if (it_by_prev != mapOrphanTransactionsByPrev.end ()) {
@@ -2608,10 +2645,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
26082645 uint32_t nFetchFlags = GetFetchFlags (pfrom);
26092646 const auto current_time = GetTime<std::chrono::microseconds>();
26102647
2611- for (const CTxIn& txin : tx.vin ) {
2612- CInv _inv (MSG_TX | nFetchFlags, txin.prevout .hash );
2613- pfrom->AddInventoryKnown (txin.prevout .hash );
2614- if (!AlreadyHave (_inv, mempool)) RequestTx (State (pfrom->GetId ()), _inv.hash , current_time);
2648+ if (!State (pfrom->GetId ())->m_wtxid_relay ) {
2649+ for (const CTxIn& txin : tx.vin ) {
2650+ // Here, we only have the txid (and not wtxid) of the
2651+ // inputs, so we only request parents from
2652+ // non-wtxid-relay peers.
2653+ // Eventually we should replace this with an improved
2654+ // protocol for getting all unconfirmed parents.
2655+ CInv _inv (MSG_TX | nFetchFlags, txin.prevout .hash );
2656+ pfrom->AddInventoryKnown (txin.prevout .hash );
2657+ if (!AlreadyHave (_inv, mempool)) RequestTx (State (pfrom->GetId ()), _inv.hash , current_time);
2658+ }
26152659 }
26162660 AddOrphanTx (ptx, pfrom->GetId ());
26172661
@@ -2672,7 +2716,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
26722716 LogPrintf (" Not relaying non-mempool transaction %s from whitelisted peer=%d\n " , tx.GetHash ().ToString (), pfrom->GetId ());
26732717 } else {
26742718 LogPrintf (" Force relaying tx %s from whitelisted peer=%d\n " , tx.GetHash ().ToString (), pfrom->GetId ());
2675- RelayTransaction (tx.GetHash (), *connman);
2719+ RelayTransaction (tx.GetHash (), tx. GetWitnessHash (), *connman);
26762720 }
26772721 }
26782722 }
@@ -3288,7 +3332,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
32883332 vRecv >> vInv;
32893333 if (vInv.size () <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
32903334 for (CInv &inv : vInv) {
3291- if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) {
3335+ if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX || inv. type == MSG_WTX ) {
32923336 // If we receive a NOTFOUND message for a txid we requested, erase
32933337 // it from our data structures for this peer.
32943338 auto in_flight_it = state->m_tx_download .m_tx_in_flight .find (inv.hash );
@@ -3580,17 +3624,19 @@ namespace {
35803624class CompareInvMempoolOrder
35813625{
35823626 CTxMemPool *mp;
3627+ bool m_wtxid_relay;
35833628public:
3584- explicit CompareInvMempoolOrder (CTxMemPool *_mempool)
3629+ explicit CompareInvMempoolOrder (CTxMemPool *_mempool, bool use_wtxid )
35853630 {
35863631 mp = _mempool;
3632+ m_wtxid_relay = use_wtxid;
35873633 }
35883634
35893635 bool operator ()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
35903636 {
35913637 /* As std::make_heap produces a max-heap, we want the entries with the
35923638 * fewest ancestors/highest fee to sort later. */
3593- return mp->CompareDepthAndScore (*b, *a);
3639+ return mp->CompareDepthAndScore (*b, *a, m_wtxid_relay );
35943640 }
35953641};
35963642}
@@ -3897,8 +3943,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
38973943 LOCK (pto->m_tx_relay ->cs_filter );
38983944
38993945 for (const auto & txinfo : vtxinfo) {
3900- const uint256& hash = txinfo.tx ->GetHash ();
3901- CInv inv (MSG_TX, hash);
3946+ const uint256& hash = state. m_wtxid_relay ? txinfo. tx -> GetWitnessHash () : txinfo.tx ->GetHash ();
3947+ CInv inv (state. m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
39023948 pto->m_tx_relay ->setInventoryTxToSend .erase (hash);
39033949 // Don't send transactions that peers will not put into their mempool
39043950 if (txinfo.fee < filterrate.GetFee (txinfo.vsize )) {
@@ -3932,7 +3978,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
39323978 }
39333979 // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
39343980 // A heap is used so that not all items need sorting if only a few are being sent.
3935- CompareInvMempoolOrder compareInvMempoolOrder (&m_mempool);
3981+ CompareInvMempoolOrder compareInvMempoolOrder (&m_mempool, state. m_wtxid_relay );
39363982 std::make_heap (vInvTx.begin (), vInvTx.end (), compareInvMempoolOrder);
39373983 // No reason to drain out at many times the network's capacity,
39383984 // especially since we have many peers and some will draw much shorter delays.
@@ -3951,17 +3997,19 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
39513997 continue ;
39523998 }
39533999 // Not in the mempool anymore? don't bother sending it.
3954- auto txinfo = m_mempool.info (hash);
4000+ auto txinfo = m_mempool.info (hash, state. m_wtxid_relay );
39554001 if (!txinfo.tx ) {
39564002 continue ;
39574003 }
4004+ auto txid = txinfo.tx ->GetHash ();
4005+ auto wtxid = txinfo.tx ->GetWitnessHash ();
39584006 // Peer told you to not send transactions at that feerate? Don't bother sending it.
39594007 if (txinfo.fee < filterrate.GetFee (txinfo.vsize )) {
39604008 continue ;
39614009 }
39624010 if (pto->m_tx_relay ->pfilter && !pto->m_tx_relay ->pfilter ->IsRelevantAndUpdate (*txinfo.tx )) continue ;
39634011 // Send
3964- vInv.push_back (CInv (MSG_TX, hash));
4012+ vInv.push_back (CInv (state. m_wtxid_relay ? MSG_WTX : MSG_TX, hash));
39654013 nRelayedTransactions++;
39664014 {
39674015 // Expire old relay messages
@@ -3971,12 +4019,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
39714019 vRelayExpiration.pop_front ();
39724020 }
39734021
3974- auto ret = mapRelay.insert ( std::make_pair (hash , std::move (txinfo.tx ) ));
4022+ auto ret = mapRelay.emplace (txid , std::move (txinfo.tx ));
39754023 if (ret.second ) {
3976- vRelayExpiration.push_back ( std::make_pair ( nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count (), ret.first ) );
4024+ vRelayExpiration.emplace_back ( nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count (), ret.first );
39774025 }
39784026 // Add wtxid-based lookup into mapRelay as well, so that peers can request by wtxid
3979- auto ret2 = mapRelay.emplace (ret. first -> second -> GetWitnessHash () , ret.first ->second );
4027+ auto ret2 = mapRelay.emplace (wtxid , ret.first ->second );
39804028 if (ret2.second ) {
39814029 vRelayExpiration.emplace_back (nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count (), ret2.first );
39824030 }
@@ -3986,6 +4034,14 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
39864034 vInv.clear ();
39874035 }
39884036 pto->m_tx_relay ->filterInventoryKnown .insert (hash);
4037+ if (hash != txid) {
4038+ // Insert txid into filterInventoryKnown, even for
4039+ // wtxidrelay peers. This prevents re-adding of
4040+ // unconfirmed parents to the recently_announced
4041+ // filter, when a child tx is requested. See
4042+ // ProcessGetData().
4043+ pto->m_tx_relay ->filterInventoryKnown .insert (txid);
4044+ }
39894045 }
39904046 }
39914047 }
@@ -4110,7 +4166,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
41104166 // Erase this entry from tx_process_time (it may be added back for
41114167 // processing at a later time, see below)
41124168 tx_process_time.erase (tx_process_time.begin ());
4113- CInv inv (MSG_TX | GetFetchFlags (pto), txid);
4169+ CInv inv (state. m_wtxid_relay ? MSG_WTX : ( MSG_TX | GetFetchFlags (pto) ), txid);
41144170 if (!AlreadyHave (inv, m_mempool)) {
41154171 // If this transaction was last requested more than 1 minute ago,
41164172 // then request.
0 commit comments