@@ -586,7 +586,7 @@ class PeerManagerImpl final : public PeerManager
586586 * @param[in] maybe_add_extra_compact_tx Whether this tx should be added to vExtraTxnForCompact.
587587 * Set to false if the tx has already been rejected before,
588588 * e.g. is an orphan, to avoid adding duplicate entries.
589- * Updates m_txrequest, m_recent_rejects, m_orphanage, and vExtraTxnForCompact. */
589+ * Updates m_txrequest, m_recent_rejects, m_recent_rejects_reconsiderable, m_orphanage, and vExtraTxnForCompact. */
590590 void ProcessInvalidTx (NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
591591 bool maybe_add_extra_compact_tx)
592592 EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
@@ -806,7 +806,16 @@ class PeerManagerImpl final : public PeerManager
806806 /* * Stalling timeout for blocks in IBD */
807807 std::atomic<std::chrono::seconds> m_block_stalling_timeout{BLOCK_STALLING_TIMEOUT_DEFAULT};
808808
809- bool AlreadyHaveTx (const GenTxid& gtxid)
809+ /* * Check whether we already have this gtxid in:
810+ * - mempool
811+ * - orphanage
812+ * - m_recent_rejects
813+ * - m_recent_rejects_reconsiderable (if include_reconsiderable = true)
814+ * - m_recent_confirmed_transactions
815+ * Also responsible for resetting m_recent_rejects and m_recent_rejects_reconsiderable if the
816+ * chain tip has changed.
817+ * */
818+ bool AlreadyHaveTx (const GenTxid& gtxid, bool include_reconsiderable)
810819 EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
811820
812821 /* *
@@ -844,8 +853,32 @@ class PeerManagerImpl final : public PeerManager
844853 * Memory used: 1.3 MB
845854 */
846855 CRollingBloomFilter m_recent_rejects GUARDED_BY (::cs_main){120'000 , 0.000'001 };
856+ /* * Block hash of chain tip the last time we reset m_recent_rejects and
857+ * m_recent_rejects_reconsiderable. */
847858 uint256 hashRecentRejectsChainTip GUARDED_BY (cs_main);
848859
860+ /* *
861+ * Filter for:
862+ * (1) wtxids of transactions that were recently rejected by the mempool but are
863+ * eligible for reconsideration if submitted with other transactions.
864+ * (2) packages (see GetPackageHash) we have already rejected before and should not retry.
865+ *
866+ * Similar to m_recent_rejects, this filter is used to save bandwidth when e.g. all of our peers
867+ * have larger mempools and thus lower minimum feerates than us.
868+ *
869+ * When a transaction's error is TxValidationResult::TX_RECONSIDERABLE (in a package or by
870+ * itself), add its wtxid to this filter. When a package fails for any reason, add the combined
871+ * hash to this filter.
872+ *
873+ * Upon receiving an announcement for a transaction, if it exists in this filter, do not
874+ * download the txdata. When considering packages, if it exists in this filter, drop it.
875+ *
876+ * Reset this filter when the chain tip changes.
877+ *
878+ * Parameters are picked to be the same as m_recent_rejects, with the same rationale.
879+ */
880+ CRollingBloomFilter m_recent_rejects_reconsiderable GUARDED_BY (::cs_main){120'000 , 0.000'001 };
881+
849882 /*
850883 * Filter for transactions that have been recently confirmed.
851884 * We use this to avoid requesting transactions that have already been
@@ -2194,7 +2227,7 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
21942227//
21952228
21962229
2197- bool PeerManagerImpl::AlreadyHaveTx (const GenTxid& gtxid)
2230+ bool PeerManagerImpl::AlreadyHaveTx (const GenTxid& gtxid, bool include_reconsiderable )
21982231{
21992232 if (m_chainman.ActiveChain ().Tip ()->GetBlockHash () != hashRecentRejectsChainTip) {
22002233 // If the chain tip has changed previously rejected transactions
@@ -2203,12 +2236,15 @@ bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid)
22032236 // txs a second chance.
22042237 hashRecentRejectsChainTip = m_chainman.ActiveChain ().Tip ()->GetBlockHash ();
22052238 m_recent_rejects.reset ();
2239+ m_recent_rejects_reconsiderable.reset ();
22062240 }
22072241
22082242 const uint256& hash = gtxid.GetHash ();
22092243
22102244 if (m_orphanage.HaveTx (gtxid)) return true ;
22112245
2246+ if (include_reconsiderable && m_recent_rejects_reconsiderable.contains (hash)) return true ;
2247+
22122248 {
22132249 LOCK (m_recent_confirmed_transactions_mutex);
22142250 if (m_recent_confirmed_transactions.contains (hash)) return true ;
@@ -3097,7 +3133,14 @@ void PeerManagerImpl::ProcessInvalidTx(NodeId nodeid, const CTransactionRef& ptx
30973133 // See also comments in https://github.com/bitcoin/bitcoin/pull/18044#discussion_r443419034
30983134 // for concerns around weakening security of unupgraded nodes
30993135 // if we start doing this too early.
3100- m_recent_rejects.insert (ptx->GetWitnessHash ().ToUint256 ());
3136+ if (state.GetResult () == TxValidationResult::TX_RECONSIDERABLE) {
3137+ // If the result is TX_RECONSIDERABLE, add it to m_recent_rejects_reconsiderable
3138+ // because we should not download or submit this transaction by itself again, but may
3139+ // submit it as part of a package later.
3140+ m_recent_rejects_reconsiderable.insert (ptx->GetWitnessHash ().ToUint256 ());
3141+ } else {
3142+ m_recent_rejects.insert (ptx->GetWitnessHash ().ToUint256 ());
3143+ }
31013144 m_txrequest.ForgetTxHash (ptx->GetWitnessHash ());
31023145 // If the transaction failed for TX_INPUTS_NOT_STANDARD,
31033146 // then we know that the witness was irrelevant to the policy
@@ -4015,7 +4058,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
40154058 return ;
40164059 }
40174060 const GenTxid gtxid = ToGenTxid (inv);
4018- const bool fAlreadyHave = AlreadyHaveTx (gtxid);
4061+ const bool fAlreadyHave = AlreadyHaveTx (gtxid, /* include_reconsiderable= */ true );
40194062 LogPrint (BCLog::NET, " got inv: %s %s peer=%d\n " , inv.ToString (), fAlreadyHave ? " have" : " new" , pfrom.GetId ());
40204063
40214064 AddKnownTx (*peer, inv.hash );
@@ -4320,7 +4363,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43204363 // already; and an adversary can already relay us old transactions
43214364 // (older than our recency filter) if trying to DoS us, without any need
43224365 // for witness malleation.
4323- if (AlreadyHaveTx (GenTxid::Wtxid (wtxid))) {
4366+ if (AlreadyHaveTx (GenTxid::Wtxid (wtxid), /* include_reconsiderable= */ true )) {
43244367 if (pfrom.HasPermission (NetPermissionFlags::ForceRelay)) {
43254368 // Always relay transactions received from peers with forcerelay
43264369 // permission, even if they were already in the mempool, allowing
@@ -4392,7 +4435,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43924435 // protocol for getting all unconfirmed parents.
43934436 const auto gtxid{GenTxid::Txid (parent_txid)};
43944437 AddKnownTx (*peer, parent_txid);
4395- if (!AlreadyHaveTx (gtxid)) AddTxAnnouncement (pfrom, gtxid, current_time);
4438+ if (!AlreadyHaveTx (gtxid, /* include_reconsiderable= */ true )) AddTxAnnouncement (pfrom, gtxid, current_time);
43964439 }
43974440
43984441 if (m_orphanage.AddTx (ptx, pfrom.GetId ())) {
@@ -6033,7 +6076,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
60336076 entry.second .GetHash ().ToString (), entry.first );
60346077 }
60356078 for (const GenTxid& gtxid : requestable) {
6036- if (!AlreadyHaveTx (gtxid)) {
6079+ if (!AlreadyHaveTx (gtxid, /* include_reconsiderable= */ true )) {
60376080 LogPrint (BCLog::NET, " Requesting %s %s peer=%d\n " , gtxid.IsWtxid () ? " wtx" : " tx" ,
60386081 gtxid.GetHash ().ToString (), pto->GetId ());
60396082 vGetData.emplace_back (gtxid.IsWtxid () ? MSG_WTX : (MSG_TX | GetFetchFlags (*peer)), gtxid.GetHash ());
0 commit comments