@@ -1004,18 +1004,39 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
1004
1004
// than the ones it replaces.
1005
1005
CAmount nConflictingFees = 0 ;
1006
1006
size_t nConflictingSize = 0 ;
1007
+ uint64_t nConflictingCount = 0 ;
1008
+ CTxMemPool::setEntries allConflicting;
1007
1009
if (setConflicts.size ())
1008
1010
{
1009
1011
LOCK (pool.cs );
1010
1012
1011
1013
CFeeRate newFeeRate (nFees, nSize);
1012
1014
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)
1014
1018
{
1015
1019
CTxMemPool::txiter mi = pool.mapTx .find (hashConflicting);
1016
1020
if (mi == pool.mapTx .end ())
1017
1021
continue ;
1018
1022
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
+
1019
1040
// Don't allow the replacement to reduce the feerate of the
1020
1041
// mempool.
1021
1042
//
@@ -1048,12 +1069,28 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
1048
1069
setConflictsParents.insert (txin.prevout .hash );
1049
1070
}
1050
1071
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" );
1057
1094
}
1058
1095
1059
1096
for (unsigned int j = 0 ; j < tx.vin .size (); j++)
@@ -1119,17 +1156,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
1119
1156
}
1120
1157
1121
1158
// 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)
1126
1160
{
1127
1161
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 (),
1129
1163
hash.ToString (),
1130
1164
FormatMoney (nFees - nConflictingFees),
1131
1165
(int )nSize - (int )nConflictingSize);
1132
1166
}
1167
+ pool.RemoveStaged (allConflicting);
1133
1168
1134
1169
// Store transaction in memory
1135
1170
pool.addUnchecked (hash, entry, setAncestors, !IsInitialBlockDownload ());
0 commit comments