@@ -1004,18 +1004,39 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
10041004 // than the ones it replaces.
10051005 CAmount nConflictingFees = 0 ;
10061006 size_t nConflictingSize = 0 ;
1007+ uint64_t nConflictingCount = 0 ;
1008+ CTxMemPool::setEntries allConflicting;
10071009 if (setConflicts.size ())
10081010 {
10091011 LOCK (pool.cs );
10101012
10111013 CFeeRate newFeeRate (nFees, nSize);
10121014 set<uint256> setConflictsParents;
1013- BOOST_FOREACH (const uint256 hashConflicting, setConflicts)
1015+ const int maxDescendantsToVisit = 100 ;
1016+ CTxMemPool::setEntries setIterConflicting;
1017+ BOOST_FOREACH (const uint256 &hashConflicting, setConflicts)
10141018 {
10151019 CTxMemPool::txiter mi = pool.mapTx .find (hashConflicting);
10161020 if (mi == pool.mapTx .end ())
10171021 continue ;
10181022
1023+ // Save these to avoid repeated lookups
1024+ setIterConflicting.insert (mi);
1025+
1026+ // If this entry is "dirty", then we don't have descendant
1027+ // state for this transaction, which means we probably have
1028+ // lots of in-mempool descendants.
1029+ // Don't allow replacements of dirty transactions, to ensure
1030+ // that we don't spend too much time walking descendants.
1031+ // This should be rare.
1032+ if (mi->IsDirty ()) {
1033+ return state.DoS (0 ,
1034+ error (" AcceptToMemoryPool: rejecting replacement %s; cannot replace tx %s with untracked descendants" ,
1035+ hash.ToString (),
1036+ mi->GetTx ().GetHash ().ToString ()),
1037+ REJECT_NONSTANDARD, " too many potential replacements" );
1038+ }
1039+
10191040 // Don't allow the replacement to reduce the feerate of the
10201041 // mempool.
10211042 //
@@ -1048,12 +1069,28 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
10481069 setConflictsParents.insert (txin.prevout .hash );
10491070 }
10501071
1051- // For efficiency we simply sum up the pre-calculated
1052- // fees/size-with-descendants values from the mempool package
1053- // tracking; this does mean the pathological case of diamond tx
1054- // graphs will be overcounted.
1055- nConflictingFees += mi->GetFeesWithDescendants ();
1056- nConflictingSize += mi->GetSizeWithDescendants ();
1072+ nConflictingCount += mi->GetCountWithDescendants ();
1073+ }
1074+ // This potentially overestimates the number of actual descendants
1075+ // but we just want to be conservative to avoid doing too much
1076+ // work.
1077+ if (nConflictingCount <= maxDescendantsToVisit) {
1078+ // If not too many to replace, then calculate the set of
1079+ // transactions that would have to be evicted
1080+ BOOST_FOREACH (CTxMemPool::txiter it, setIterConflicting) {
1081+ pool.CalculateDescendants (it, allConflicting);
1082+ }
1083+ BOOST_FOREACH (CTxMemPool::txiter it, allConflicting) {
1084+ nConflictingFees += it->GetFee ();
1085+ nConflictingSize += it->GetTxSize ();
1086+ }
1087+ } else {
1088+ return state.DoS (0 ,
1089+ error (" AcceptToMemoryPool: rejecting replacement %s; too many potential replacements (%d > %d)\n " ,
1090+ hash.ToString (),
1091+ nConflictingCount,
1092+ maxDescendantsToVisit),
1093+ REJECT_NONSTANDARD, " too many potential replacements" );
10571094 }
10581095
10591096 for (unsigned int j = 0 ; j < tx.vin .size (); j++)
@@ -1119,17 +1156,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
11191156 }
11201157
11211158 // Remove conflicting transactions from the mempool
1122- list<CTransaction> ltxConflicted;
1123- pool.removeConflicts (tx, ltxConflicted);
1124-
1125- BOOST_FOREACH (const CTransaction &txConflicted, ltxConflicted)
1159+ BOOST_FOREACH (const CTxMemPool::txiter it, allConflicting)
11261160 {
11271161 LogPrint (" mempool" , " replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n " ,
1128- txConflicted .GetHash ().ToString (),
1162+ it-> GetTx () .GetHash ().ToString (),
11291163 hash.ToString (),
11301164 FormatMoney (nFees - nConflictingFees),
11311165 (int )nSize - (int )nConflictingSize);
11321166 }
1167+ pool.RemoveStaged (allConflicting);
11331168
11341169 // Store transaction in memory
11351170 pool.addUnchecked (hash, entry, setAncestors, !IsInitialBlockDownload ());
0 commit comments