Skip to content

Commit bf19342

Browse files
committed
Optimise coin-age priority by splitting up steps to calculate it
double priority = GetPriority(tx, view, height, ICIV); becomes const double coin_age = GetCoinAge(tx, view, height, ICIV); const auto mod_vsize = CalculateModifiedSize(tx); const double priority = ComputePriority2(coin_age, mod_vsize);
1 parent 0679e0b commit bf19342

File tree

10 files changed

+41
-34
lines changed

10 files changed

+41
-34
lines changed

src/bench/mempool_stress.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
static void AddTx(const CTransactionRef& tx, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
1717
{
1818
int64_t nTime = 0;
19-
const double coinage_priority = 10.0;
19+
constexpr double coin_age{10.0};
2020
unsigned int nHeight = 1;
2121
uint64_t sequence = 0;
2222
bool spendsCoinbase = false;
2323
unsigned int sigOpCost = 4;
2424
LockPoints lp;
25-
pool.addUnchecked(CTxMemPoolEntry(tx, 1000, nTime, coinage_priority, nHeight, sequence, tx->GetValueOut(), spendsCoinbase, sigOpCost, lp));
25+
pool.addUnchecked(CTxMemPoolEntry(tx, 1000, nTime, nHeight, sequence, /*entry_tx_inputs_coin_age=*/coin_age, tx->GetValueOut(), spendsCoinbase, sigOpCost, lp));
2626
}
2727

2828
struct Available {

src/bench/rpc_mempool.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
1818
{
1919
LockPoints lp;
20-
pool.addUnchecked(CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_priority=*/0, /*entry_height=*/0, /*entry_sequence=*/0, /*in_chain_input_value=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
20+
pool.addUnchecked(CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_height=*/0, /*entry_sequence=*/0, /*entry_tx_inputs_coin_age=*/0.0, /*in_chain_input_value=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
2121
}
2222

2323
static void RpcMempool(benchmark::Bench& bench)

src/kernel/mempool_entry.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,14 @@ class CTxMemPoolEntry
8686
const size_t nUsageSize; //!< ... and total memory usage
8787
const int64_t nTime; //!< Local time when entering the mempool
8888
const uint64_t entry_sequence; //!< Sequence number used to determine whether this transaction is too recent for relay
89+
const int64_t sigOpCost; //!< Total sigop cost
90+
const size_t nModSize; //!< Cached modified size for priority
8991
const double entryPriority; //!< Priority when entering the mempool
9092
const unsigned int entryHeight; //!< Chain height when entering the mempool
9193
double cachedPriority; //!< Last calculated priority
9294
unsigned int cachedHeight; //!< Height at which priority was last calculated
9395
CAmount inChainInputValue; //!< Sum of all txin values that are already in blockchain
9496
const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
95-
const int64_t sigOpCost; //!< Total sigop cost
96-
const size_t nModSize; //!< Cached modified size for priority
9797
CAmount m_modified_fee; //!< Used for determining the priority of the transaction for mining in a block
9898
mutable LockPoints lockPoints; //!< Track the height and time at which tx was final
9999

@@ -114,7 +114,8 @@ class CTxMemPoolEntry
114114

115115
public:
116116
CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
117-
int64_t time, double entry_priority, unsigned int entry_height, uint64_t entry_sequence,
117+
int64_t time, unsigned int entry_height, uint64_t entry_sequence,
118+
double entry_tx_inputs_coin_age,
118119
CAmount in_chain_input_value,
119120
bool spends_coinbase,
120121
int64_t sigops_cost, LockPoints lp)
@@ -124,15 +125,15 @@ class CTxMemPoolEntry
124125
nUsageSize{RecursiveDynamicUsage(tx)},
125126
nTime{time},
126127
entry_sequence{entry_sequence},
127-
entryPriority{entry_priority},
128+
sigOpCost{sigops_cost},
129+
nModSize{CalculateModifiedSize(*tx, GetTxSize())},
130+
entryPriority{ComputePriority2(entry_tx_inputs_coin_age, nModSize)},
128131
entryHeight{entry_height},
129-
cachedPriority{entry_priority},
132+
cachedPriority{entryPriority},
130133
// Since entries arrive *after* the tip's height, their entry priority is for the height+1
131134
cachedHeight{entry_height + 1},
132135
inChainInputValue{in_chain_input_value},
133136
spendsCoinbase{spends_coinbase},
134-
sigOpCost{sigops_cost},
135-
nModSize{CalculateModifiedSize(*tx, GetTxSize())},
136137
m_modified_fee{nFee},
137138
lockPoints{lp},
138139
nSizeWithDescendants{GetTxSize()},

src/policy/coin_age_priority.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <policy/policy.h>
1212
#include <primitives/transaction.h>
1313
#include <txmempool.h>
14+
#include <util/check.h>
1415
#include <validation.h>
1516

1617
using node::BlockAssembler;
@@ -22,8 +23,7 @@ unsigned int CalculateModifiedSize(const CTransaction& tx, unsigned int nTxSize)
2223
// is enough to cover a compressed pubkey p2sh redemption) for priority.
2324
// Providing any more cleanup incentive than making additional inputs free would
2425
// risk encouraging people to create junk outputs to redeem later.
25-
if (nTxSize == 0)
26-
nTxSize = (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
26+
Assert(nTxSize > 0);
2727
for (std::vector<CTxIn>::const_iterator it(tx.vin.begin()); it != tx.vin.end(); ++it)
2828
{
2929
unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size());
@@ -33,15 +33,14 @@ unsigned int CalculateModifiedSize(const CTransaction& tx, unsigned int nTxSize)
3333
return nTxSize;
3434
}
3535

36-
double ComputePriority(const CTransaction& tx, double dPriorityInputs, unsigned int nTxSize)
36+
double ComputePriority2(double inputs_coin_age, unsigned int mod_vsize)
3737
{
38-
nTxSize = CalculateModifiedSize(tx, nTxSize);
39-
if (nTxSize == 0) return 0.0;
38+
if (mod_vsize == 0) return 0.0;
4039

41-
return dPriorityInputs / nTxSize;
40+
return inputs_coin_age / mod_vsize;
4241
}
4342

44-
double GetPriority(const CTransaction &tx, const CCoinsViewCache& view, int nHeight, CAmount &inChainInputValue)
43+
double GetCoinAge(const CTransaction &tx, const CCoinsViewCache& view, int nHeight, CAmount &inChainInputValue)
4544
{
4645
inChainInputValue = 0;
4746
if (tx.IsCoinBase())
@@ -58,7 +57,7 @@ double GetPriority(const CTransaction &tx, const CCoinsViewCache& view, int nHei
5857
inChainInputValue += coin.out.nValue;
5958
}
6059
}
61-
return ComputePriority(tx, dResult);
60+
return dResult;
6261
}
6362

6463
void CTxMemPoolEntry::UpdateCachedPriority(unsigned int currentHeight, CAmount valueInCurrentBlock)

src/policy/coin_age_priority.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
class CCoinsViewCache;
1111
class CTransaction;
1212

13-
// Compute modified tx size for priority calculation (optionally given tx size)
14-
unsigned int CalculateModifiedSize(const CTransaction& tx, unsigned int nTxSize=0);
13+
// Compute modified tx vsize for priority calculation
14+
unsigned int CalculateModifiedSize(const CTransaction& tx, unsigned int nTxSize);
1515

16-
// Compute priority, given sum coin-age of inputs and (optionally) tx size
17-
double ComputePriority(const CTransaction& tx, double dPriorityInputs, unsigned int nTxSize=0);
16+
// Compute priority, given sum coin-age of inputs and modified tx vsize
17+
// CAUTION: Original ComputePriority accepted UNMODIFIED tx vsize and did the modification internally
18+
double ComputePriority2(double inputs_coin_age, unsigned int mod_vsize);
1819

1920
/**
20-
* Return priority of tx at height nHeight. Also calculate the sum of the values of the inputs
21+
* Return sum coin-age of tx inputs at height nHeight. Also calculate the sum of the values of the inputs
2122
* that are already in the chain. These are the inputs that will age and increase priority as
2223
* new blocks are added to the chain.
24+
* CAUTION: Original GetPriority also called ComputePriority and returned the final coin-age priority
2325
*/
24-
double GetPriority(const CTransaction &tx, const CCoinsViewCache& view, int nHeight, CAmount &inChainInputValue);
26+
double GetCoinAge(const CTransaction &tx, const CCoinsViewCache& view, int nHeight, CAmount &inChainInputValue);
2527

2628
#endif // BITCOIN_POLICY_COIN_AGE_PRIORITY_H

src/test/fuzz/util/mempool.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider,
3535
assert(MoneyRange(fee));
3636
const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
3737
const uint64_t entry_sequence{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
38-
const double coinage_priority = fuzzed_data_provider.ConsumeFloatingPoint<double>();
38+
const double coin_age = fuzzed_data_provider.ConsumeFloatingPoint<double>();
3939
const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, std::numeric_limits<unsigned int>::max() - 1);
4040
const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
4141
const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
42-
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, coinage_priority, entry_height, entry_sequence, tx.GetValueOut(), spends_coinbase, sig_op_cost, {}};
42+
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, entry_sequence, /*entry_tx_inputs_coin_age=*/coin_age, tx.GetValueOut(), spends_coinbase, sig_op_cost, {}};
4343
}

src/test/util/setup_common.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,9 +561,10 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex
561561
LOCK2(cs_main, m_node.mempool->cs);
562562
LockPoints lp;
563563
CAmount in_chain_input_value;
564-
double dPriority = GetPriority(*ptx, active_chainstate.CoinsTip(), height + 1, in_chain_input_value);
564+
const double coin_age = GetCoinAge(*ptx, active_chainstate.CoinsTip(), height + 1, in_chain_input_value);
565565
m_node.mempool->addUnchecked(CTxMemPoolEntry(ptx, /*fee=*/(total_in - num_outputs * amount_per_output),
566-
/*time=*/0, /*entry_priority=*/ dPriority, /*entry_height=*/ height, /*entry_sequence=*/0,
566+
/*time=*/0, /*entry_height=*/ height, /*entry_sequence=*/0,
567+
/*entry_tx_inputs_coin_age=*/coin_age,
567568
in_chain_input_value,
568569
/*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
569570
}
@@ -594,7 +595,8 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
594595
const auto tx_fee = target_feerate.GetFee(GetVirtualTransactionSize(*tx)) -
595596
m_node.mempool->m_opts.incremental_relay_feerate.GetFee(GetVirtualTransactionSize(*tx));
596597
m_node.mempool->addUnchecked(CTxMemPoolEntry(tx, /*fee=*/tx_fee,
597-
/*time=*/0, /*entry_priority=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
598+
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
599+
/*entry_tx_inputs_coin_age=*/0.0,
598600
/*in_chain_input_value=*/0,
599601
/*spends_coinbase=*/true, /*sigops_cost=*/1, lp));
600602
m_node.mempool->TrimToSize(0);

src/test/util/txmempool.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) co
3737

3838
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) const
3939
{
40-
const double dPriority = 0;
40+
constexpr double coin_age{0};
4141
const CAmount inChainValue = 0;
42-
return CTxMemPoolEntry{tx, nFee, TicksSinceEpoch<std::chrono::seconds>(time), dPriority, nHeight, m_sequence, inChainValue, spendsCoinbase, sigOpCost, lp};
42+
return CTxMemPoolEntry{tx, nFee, TicksSinceEpoch<std::chrono::seconds>(time), nHeight, m_sequence, /*entry_tx_inputs_coin_age=*/coin_age, inChainValue, spendsCoinbase, sigOpCost, lp};
4343
}
4444

4545
std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,

src/txmempool.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,9 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
677677
for (const auto& it : GetSortedDepthAndScore()) {
678678
checkTotal += it->GetTxSize();
679679
CAmount dummyValue;
680-
double freshPriority = GetPriority(it->GetTx(), active_coins_tip, spendheight, dummyValue);
680+
const double fresh_coin_age = GetCoinAge(it->GetTx(), active_coins_tip, spendheight, dummyValue);
681+
const auto fresh_mod_vsize = CalculateModifiedSize(it->GetTx(), it->GetTxSize());
682+
const double freshPriority = ComputePriority2(fresh_coin_age, fresh_mod_vsize);
681683
double cachePriority = it->GetPriority(spendheight);
682684
double priDiff = cachePriority > freshPriority ? cachePriority - freshPriority : freshPriority - cachePriority;
683685
// Verify that the difference between the on the fly calculation and a fresh calculation

src/validation.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
938938

939939
CAmount inChainInputValue;
940940
// Since entries arrive *after* the tip's height, their priority is for the height+1
941-
double dPriority = GetPriority(tx, m_view, m_active_chainstate.m_chain.Height() + 1, inChainInputValue);
941+
const double coin_age = GetCoinAge(tx, m_view, m_active_chainstate.m_chain.Height() + 1, inChainInputValue);
942942

943943
// Keep track of transactions that spend a coinbase, which we re-scan
944944
// during reorgs to ensure COINBASE_MATURITY is still met.
@@ -954,7 +954,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
954954
// Set entry_sequence to 0 when rejectmsg_zero_mempool_entry_seq is used; this allows txs from a block
955955
// reorg to be marked earlier than any child txs that were already in the mempool.
956956
const uint64_t entry_sequence = args.m_ignore_rejects.count(rejectmsg_zero_mempool_entry_seq) ? 0 : m_pool.GetSequence();
957-
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, dPriority, m_active_chainstate.m_chain.Height(), entry_sequence,
957+
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(), entry_sequence,
958+
/*entry_tx_inputs_coin_age=*/coin_age,
958959
inChainInputValue,
959960
fSpendsCoinbase, nSigOpsCost, lock_points.value()));
960961
ws.m_vsize = entry->GetTxSize();

0 commit comments

Comments
 (0)