@@ -32,7 +32,6 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
32
32
nTxSize = ::GetSerializeSize (tx, SER_NETWORK, PROTOCOL_VERSION);
33
33
nModSize = tx.CalculateModifiedSize (nTxSize);
34
34
nUsageSize = tx.DynamicMemoryUsage ();
35
- feeRate = CFeeRate (nFee, nTxSize);
36
35
}
37
36
38
37
CTxMemPoolEntry::CTxMemPoolEntry (const CTxMemPoolEntry& other)
@@ -49,9 +48,10 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
49
48
return dResult;
50
49
}
51
50
52
- CTxMemPool::CTxMemPool (const CFeeRate& _minRelayFee) :
53
- nTransactionsUpdated(0 )
51
+ CTxMemPool::CTxMemPool (const CFeeRate& _minRelayFee)
54
52
{
53
+ clear ();
54
+
55
55
// Sanity checks off by default for performance, because otherwise
56
56
// accepting transactions becomes O(N^2) where N is the number
57
57
// of transactions in the pool
@@ -109,6 +109,19 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
109
109
return true ;
110
110
}
111
111
112
+ void CTxMemPool::removeUnchecked (const uint256& hash)
113
+ {
114
+ indexed_transaction_set::iterator it = mapTx.find (hash);
115
+
116
+ BOOST_FOREACH (const CTxIn& txin, it->GetTx ().vin )
117
+ mapNextTx.erase (txin.prevout );
118
+
119
+ totalTxSize -= it->GetTxSize ();
120
+ cachedInnerUsage -= it->DynamicMemoryUsage ();
121
+ mapTx.erase (it);
122
+ nTransactionsUpdated++;
123
+ minerPolicyEstimator->removeTx (hash);
124
+ }
112
125
113
126
void CTxMemPool::remove (const CTransaction &origTx, std::list<CTransaction>& removed, bool fRecursive )
114
127
{
@@ -144,15 +157,8 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
144
157
txToRemove.push_back (it->second .ptx ->GetHash ());
145
158
}
146
159
}
147
- BOOST_FOREACH (const CTxIn& txin, tx.vin )
148
- mapNextTx.erase (txin.prevout );
149
-
150
160
removed.push_back (tx);
151
- totalTxSize -= mapTx.find (hash)->GetTxSize ();
152
- cachedInnerUsage -= mapTx.find (hash)->DynamicMemoryUsage ();
153
- mapTx.erase (hash);
154
- nTransactionsUpdated++;
155
- minerPolicyEstimator->removeTx (hash);
161
+ removeUnchecked (hash);
156
162
}
157
163
}
158
164
}
@@ -435,3 +441,104 @@ size_t CTxMemPool::DynamicMemoryUsage() const {
435
441
// Estimate the overhead of mapTx to be 6 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
436
442
return memusage::MallocUsage (sizeof (CTxMemPoolEntry) + 6 * sizeof (void *)) * mapTx.size () + memusage::DynamicUsage (mapNextTx) + memusage::DynamicUsage (mapDeltas) + cachedInnerUsage;
437
443
}
444
+
445
+ size_t CTxMemPool::GuessDynamicMemoryUsage (const CTxMemPoolEntry& entry) const {
446
+ return memusage::MallocUsage (sizeof (CTxMemPoolEntry) + 5 * sizeof (void *)) + entry.DynamicMemoryUsage () + memusage::IncrementalDynamicUsage (mapNextTx) * entry.GetTx ().vin .size ();
447
+ }
448
+
449
+ bool CTxMemPool::StageTrimToSize (size_t sizelimit, const CTxMemPoolEntry& toadd, std::set<uint256>& stage, CAmount& nFeesRemoved) {
450
+ size_t nSizeRemoved = 0 ;
451
+ std::set<uint256> protect;
452
+ BOOST_FOREACH (const CTxIn& in, toadd.GetTx ().vin ) {
453
+ protect.insert (in.prevout .hash );
454
+ }
455
+
456
+ size_t expsize = DynamicMemoryUsage () + GuessDynamicMemoryUsage (toadd); // Track the expected resulting memory usage of the mempool.
457
+ indexed_transaction_set::nth_index<1 >::type::reverse_iterator it = mapTx.get <1 >().rbegin ();
458
+ int fails = 0 ; // Number of mempool transactions iterated over that were not included in the stage.
459
+ // Iterate from lowest feerate to highest feerate in the mempool:
460
+ while (expsize > sizelimit && it != mapTx.get <1 >().rend ()) {
461
+ const uint256& hash = it->GetTx ().GetHash ();
462
+ if (stage.count (hash)) {
463
+ // If the transaction is already staged for deletion, we know its descendants are already processed, so skip it.
464
+ it++;
465
+ continue ;
466
+ }
467
+ if (GetRand (10 )) {
468
+ // Only try 1/10 of the transactions, in order to have some chance to avoid very big chains.
469
+ it++;
470
+ continue ;
471
+ }
472
+ if (CompareTxMemPoolEntryByFeeRate ()(*it, toadd)) {
473
+ // If the transaction's feerate is worse than what we're looking for, we have processed everything in the mempool
474
+ // that could improve the staged set. If we don't have an acceptable solution by now, bail out.
475
+ return false ;
476
+ }
477
+ std::deque<uint256> todo; // List of hashes that we still need to process (descendants of 'hash').
478
+ std::set<uint256> now; // Set of hashes that will need to be added to stage if 'hash' is included.
479
+ CAmount nowfee = 0 ; // Sum of the fees in 'now'.
480
+ size_t nowsize = 0 ; // Sum of the tx sizes in 'now'.
481
+ size_t nowusage = 0 ; // Sum of the memory usages of transactions in 'now'.
482
+ int iternow = 0 ; // Transactions we've inspected so far while determining whether 'hash' is acceptable.
483
+ todo.push_back (it->GetTx ().GetHash ()); // Add 'hash' to the todo list, to initiate processing its children.
484
+ bool good = true ; // Whether including 'hash' (and all its descendants) is a good idea.
485
+ // Iterate breadth-first over all descendants of transaction with hash 'hash'.
486
+ while (!todo.empty ()) {
487
+ uint256 hashnow = todo.front ();
488
+ if (protect.count (hashnow)) {
489
+ // If this transaction is in the protected set, we're done with 'hash'.
490
+ good = false ;
491
+ break ;
492
+ }
493
+ iternow++; // We only count transactions we actually had to go find in the mempool.
494
+ if (iternow + fails > 20 ) {
495
+ return false ;
496
+ }
497
+ const CTxMemPoolEntry* origTx = &*mapTx.find (hashnow);
498
+ nowfee += origTx->GetFee ();
499
+ if (nFeesRemoved + nowfee > toadd.GetFee ()) {
500
+ // If this pushes up to the total fees deleted too high, we're done with 'hash'.
501
+ good = false ;
502
+ break ;
503
+ }
504
+ todo.pop_front ();
505
+ // Add 'hashnow' to the 'now' set, and update its statistics.
506
+ now.insert (hashnow);
507
+ nowusage += GuessDynamicMemoryUsage (*origTx);
508
+ nowsize += origTx->GetTxSize ();
509
+ // Find dependencies of 'hashnow' and them to todo.
510
+ std::map<COutPoint, CInPoint>::iterator iter = mapNextTx.lower_bound (COutPoint (hashnow, 0 ));
511
+ while (iter != mapNextTx.end () && iter->first .hash == hashnow) {
512
+ const uint256& nexthash = iter->second .ptx ->GetHash ();
513
+ if (!(stage.count (nexthash) || now.count (nexthash))) {
514
+ todo.push_back (nexthash);
515
+ }
516
+ iter++;
517
+ }
518
+ }
519
+ if (good && (double )nowfee * toadd.GetTxSize () > (double )toadd.GetFee () * nowsize) {
520
+ // The new transaction's feerate is below that of the set we're removing.
521
+ good = false ;
522
+ }
523
+ if (good) {
524
+ stage.insert (now.begin (), now.end ());
525
+ nFeesRemoved += nowfee;
526
+ nSizeRemoved += nowsize;
527
+ expsize -= nowusage;
528
+ } else {
529
+ fails += iternow;
530
+ if (fails > 10 ) {
531
+ // Bail out after traversing 32 transactions that are not acceptable.
532
+ return false ;
533
+ }
534
+ }
535
+ it++;
536
+ }
537
+ return true ;
538
+ }
539
+
540
+ void CTxMemPool::RemoveStaged (std::set<uint256>& stage) {
541
+ BOOST_FOREACH (const uint256& hash, stage) {
542
+ removeUnchecked (hash);
543
+ }
544
+ }
0 commit comments