Skip to content

Commit 44080a9

Browse files
committed
Merge #12118: Sort mempool by min(feerate, ancestor_feerate)
0a22a52 Use mempool's ancestor sort in transaction selection (Suhas Daftuar) 7abfa53 Add test for new ancestor feerate sort behavior (Suhas Daftuar) 9a51319 Sort mempool by min(feerate, ancestor_feerate) (Suhas Daftuar) 6773f92 Refactor CompareTxMemPoolEntryByDescendantScore (Suhas Daftuar) Pull request description: This more closely approximates the desirability of a given transaction for mining, and should result in less re-sorting when transactions get removed from the mempool after being mined. I measured this as approximately a 5% speedup in removeForBlock. Tree-SHA512: ffa36b567c5dfe3e8908c545a459b6a5ec0de26e7dc81b1050dd235cac9046564b4409a3f8c5ba97bd8b30526e8fec8f78480a912e317979467f32305c3dd37b
2 parents 4db16ec + 0a22a52 commit 44080a9

File tree

4 files changed

+71
-37
lines changed

4 files changed

+71
-37
lines changed

src/miner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
352352
// Try to compare the mapTx entry to the mapModifiedTx entry
353353
iter = mempool.mapTx.project<0>(mi);
354354
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
355-
CompareModifiedEntry()(*modit, CTxMemPoolModifiedEntry(iter))) {
355+
CompareTxMemPoolEntryByAncestorFee()(*modit, CTxMemPoolModifiedEntry(iter))) {
356356
// The best entry in mapModifiedTx has higher score
357357
// than the one from mapTx.
358358
// Switch which transaction (package) to consider

src/miner.h

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ struct CTxMemPoolModifiedEntry {
4141
nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors();
4242
}
4343

44+
int64_t GetModifiedFee() const { return iter->GetModifiedFee(); }
45+
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
46+
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
47+
size_t GetTxSize() const { return iter->GetTxSize(); }
48+
const CTransaction& GetTx() const { return iter->GetTx(); }
49+
4450
CTxMemPool::txiter iter;
4551
uint64_t nSizeWithAncestors;
4652
CAmount nModFeesWithAncestors;
@@ -67,21 +73,6 @@ struct modifiedentry_iter {
6773
}
6874
};
6975

70-
// This matches the calculation in CompareTxMemPoolEntryByAncestorFee,
71-
// except operating on CTxMemPoolModifiedEntry.
72-
// TODO: refactor to avoid duplication of this logic.
73-
struct CompareModifiedEntry {
74-
bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b) const
75-
{
76-
double f1 = (double)a.nModFeesWithAncestors * b.nSizeWithAncestors;
77-
double f2 = (double)b.nModFeesWithAncestors * a.nSizeWithAncestors;
78-
if (f1 == f2) {
79-
return CTxMemPool::CompareIteratorByHash()(a.iter, b.iter);
80-
}
81-
return f1 > f2;
82-
}
83-
};
84-
8576
// A comparator that sorts transactions based on number of ancestors.
8677
// This is sufficient to sort an ancestor package in an order that is valid
8778
// to appear in a block.
@@ -106,7 +97,7 @@ typedef boost::multi_index_container<
10697
// Reuse same tag from CTxMemPool's similar index
10798
boost::multi_index::tag<ancestor_score>,
10899
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
109-
CompareModifiedEntry
100+
CompareTxMemPoolEntryByAncestorFee
110101
>
111102
>
112103
> indexed_modified_transaction_set;

src/test/mempool_tests.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,23 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
398398
sortedOrder.erase(sortedOrder.end()-2);
399399
sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
400400
CheckSort<ancestor_score>(pool, sortedOrder);
401+
402+
// High-fee parent, low-fee child
403+
// tx7 -> tx8
404+
CMutableTransaction tx8 = CMutableTransaction();
405+
tx8.vin.resize(1);
406+
tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
407+
tx8.vin[0].scriptSig = CScript() << OP_11;
408+
tx8.vout.resize(1);
409+
tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
410+
tx8.vout[0].nValue = 10*COIN;
411+
412+
// Check that we sort by min(feerate, ancestor_feerate):
413+
// set the fee so that the ancestor feerate is above tx1/5,
414+
// but the transaction's own feerate is lower
415+
pool.addUnchecked(tx8.GetHash(), entry.Fee(5000LL).FromTx(tx8));
416+
sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString());
417+
CheckSort<ancestor_score>(pool, sortedOrder);
401418
}
402419

403420

src/txmempool.h

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -206,31 +206,36 @@ class CompareTxMemPoolEntryByDescendantScore
206206
public:
207207
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
208208
{
209-
bool fUseADescendants = UseDescendantScore(a);
210-
bool fUseBDescendants = UseDescendantScore(b);
209+
double a_mod_fee, a_size, b_mod_fee, b_size;
211210

212-
double aModFee = fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee();
213-
double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize();
214-
215-
double bModFee = fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee();
216-
double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize();
211+
GetModFeeAndSize(a, a_mod_fee, a_size);
212+
GetModFeeAndSize(b, b_mod_fee, b_size);
217213

218214
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
219-
double f1 = aModFee * bSize;
220-
double f2 = aSize * bModFee;
215+
double f1 = a_mod_fee * b_size;
216+
double f2 = a_size * b_mod_fee;
221217

222218
if (f1 == f2) {
223219
return a.GetTime() >= b.GetTime();
224220
}
225221
return f1 < f2;
226222
}
227223

228-
// Calculate which score to use for an entry (avoiding division).
229-
bool UseDescendantScore(const CTxMemPoolEntry &a) const
224+
// Return the fee/size we're using for sorting this entry.
225+
void GetModFeeAndSize(const CTxMemPoolEntry &a, double &mod_fee, double &size) const
230226
{
227+
// Compare feerate with descendants to feerate of the transaction, and
228+
// return the fee/size for the max.
231229
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants();
232230
double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize();
233-
return f2 > f1;
231+
232+
if (f2 > f1) {
233+
mod_fee = a.GetModFeesWithDescendants();
234+
size = a.GetSizeWithDescendants();
235+
} else {
236+
mod_fee = a.GetModifiedFee();
237+
size = a.GetTxSize();
238+
}
234239
}
235240
};
236241

@@ -261,27 +266,48 @@ class CompareTxMemPoolEntryByEntryTime
261266
}
262267
};
263268

269+
/** \class CompareTxMemPoolEntryByAncestorScore
270+
*
271+
* Sort an entry by min(score/size of entry's tx, score/size with all ancestors).
272+
*/
264273
class CompareTxMemPoolEntryByAncestorFee
265274
{
266275
public:
267-
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
276+
template<typename T>
277+
bool operator()(const T& a, const T& b) const
268278
{
269-
double aFees = a.GetModFeesWithAncestors();
270-
double aSize = a.GetSizeWithAncestors();
279+
double a_mod_fee, a_size, b_mod_fee, b_size;
271280

272-
double bFees = b.GetModFeesWithAncestors();
273-
double bSize = b.GetSizeWithAncestors();
281+
GetModFeeAndSize(a, a_mod_fee, a_size);
282+
GetModFeeAndSize(b, b_mod_fee, b_size);
274283

275284
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
276-
double f1 = aFees * bSize;
277-
double f2 = aSize * bFees;
285+
double f1 = a_mod_fee * b_size;
286+
double f2 = a_size * b_mod_fee;
278287

279288
if (f1 == f2) {
280289
return a.GetTx().GetHash() < b.GetTx().GetHash();
281290
}
282-
283291
return f1 > f2;
284292
}
293+
294+
// Return the fee/size we're using for sorting this entry.
295+
template <typename T>
296+
void GetModFeeAndSize(const T &a, double &mod_fee, double &size) const
297+
{
298+
// Compare feerate with ancestors to feerate of the transaction, and
299+
// return the fee/size for the min.
300+
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithAncestors();
301+
double f2 = (double)a.GetModFeesWithAncestors() * a.GetTxSize();
302+
303+
if (f1 > f2) {
304+
mod_fee = a.GetModFeesWithAncestors();
305+
size = a.GetSizeWithAncestors();
306+
} else {
307+
mod_fee = a.GetModifiedFee();
308+
size = a.GetTxSize();
309+
}
310+
}
285311
};
286312

287313
// Multi_index tag names

0 commit comments

Comments
 (0)