Apply mempool changeset transactions directly into the mempool

Rather than individually calling addUnchecked for each transaction added in a
changeset (after removing all the to-be-removed transactions), instead we can
take advantage of boost::multi_index's splicing features to extract and insert
entries directly from the staging multi_index into mapTx.

This has the immediate advantage of saving allocation overhead for mempool
entries which have already been allocated once. This also means that the memory
locations of mempool entries will not change when transactions go from staging
to the main mempool.

Additionally, eliminate addUnchecked and require all new transactions to enter
the mempool via a CTxMemPoolChangeSet.
This commit is contained in:
Suhas Daftuar 2024-10-10 14:59:23 -04:00
parent 34b6c5833d
commit 7fb62f7db6
20 changed files with 268 additions and 209 deletions

View file

@ -11,6 +11,7 @@
#include <script/script.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
@ -28,7 +29,7 @@ static void AddTx(const CTransactionRef& tx, CTxMemPool& pool) EXCLUSIVE_LOCKS_R
unsigned int sigOpCost{4};
uint64_t fee{0};
LockPoints lp;
pool.addUnchecked(CTxMemPoolEntry(
AddToMempool(pool, CTxMemPoolEntry(
tx, fee, nTime, nHeight, sequence,
spendsCoinbase, sigOpCost, lp));
}

View file

@ -10,6 +10,7 @@
#include <script/script.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
@ -26,7 +27,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
bool spendsCoinbase = false;
unsigned int sigOpCost = 4;
LockPoints lp;
pool.addUnchecked(CTxMemPoolEntry(
AddToMempool(pool, CTxMemPoolEntry(
tx, nFee, nTime, nHeight, sequence,
spendsCoinbase, sigOpCost, lp));
}

View file

@ -10,6 +10,7 @@
#include <script/script.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <validation.h>
@ -28,7 +29,7 @@ static void AddTx(const CTransactionRef& tx, CTxMemPool& pool) EXCLUSIVE_LOCKS_R
bool spendsCoinbase = false;
unsigned int sigOpCost = 4;
LockPoints lp;
pool.addUnchecked(CTxMemPoolEntry(tx, 1000, nTime, nHeight, sequence, spendsCoinbase, sigOpCost, lp));
AddToMempool(pool, CTxMemPoolEntry(tx, 1000, nTime, nHeight, sequence, spendsCoinbase, sigOpCost, lp));
}
struct Available {

View file

@ -10,6 +10,7 @@
#include <script/script.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <univalue.h>
#include <util/check.h>
@ -21,7 +22,7 @@
static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
{
LockPoints lp;
pool.addUnchecked(CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
AddToMempool(pool, CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
}
static void RpcMempool(benchmark::Bench& bench)

View file

@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
CBlock block(BuildBlockTestCase(rand_ctx));
LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[2]));
AddToMempool(pool, entry.FromTx(block.vtx[2]));
BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
// Do a simple ShortTxIDs RT
@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
CBlock block(BuildBlockTestCase(rand_ctx));
LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[2]));
AddToMempool(pool, entry.FromTx(block.vtx[2]));
BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash;
@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
CBlock block(BuildBlockTestCase(rand_ctx));
LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[1]));
AddToMempool(pool, entry.FromTx(block.vtx[1]));
BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash;
@ -322,7 +322,7 @@ BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
extra_txn.resize(10);
LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[2]));
AddToMempool(pool, entry.FromTx(block.vtx[2]));
BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
// Ensure the non_block_tx is actually not in the block
for (const auto &block_tx : block.vtx) {

View file

@ -59,7 +59,7 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
TestMemPoolEntryHelper entry;
const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
assert(MoneyRange(fee));
pool.addUnchecked(entry.Fee(fee).FromTx(tx));
AddToMempool(pool, entry.Fee(fee).FromTx(tx));
// All outputs are available to spend
for (uint32_t n{0}; n < num_outputs; ++n) {
@ -154,7 +154,7 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
TestMemPoolEntryHelper entry;
const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
assert(MoneyRange(fee));
pool.addUnchecked(entry.Fee(fee).FromTx(tx));
AddToMempool(pool, entry.Fee(fee).FromTx(tx));
transactions.push_back(tx);
}
std::vector<COutPoint> outpoints;

View file

@ -78,7 +78,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
if (add_to_mempool && !pool.exists(GenTxid::Txid(tx->GetHash()))) {
LOCK2(cs_main, pool.cs);
pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, *tx));
AddToMempool(pool, ConsumeTxMemPoolEntry(fuzzed_data_provider, *tx));
available.insert(i);
}
}

View file

@ -73,12 +73,12 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
mtx->vin[0].prevout = COutPoint{another_tx.GetHash(), 0};
}
LOCK2(cs_main, pool.cs);
pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, another_tx));
AddToMempool(pool, ConsumeTxMemPoolEntry(fuzzed_data_provider, another_tx));
}
const CTransaction tx{*mtx};
if (fuzzed_data_provider.ConsumeBool()) {
LOCK2(cs_main, pool.cs);
pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
AddToMempool(pool, ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
}
{
LOCK(pool.cs);
@ -129,7 +129,7 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
mempool_txs.pop_back();
break;
}
pool.addUnchecked(parent_entry);
AddToMempool(pool, parent_entry);
if (fuzzed_data_provider.ConsumeBool()) {
child.vin[0].prevout = COutPoint{mempool_txs.back().GetHash(), 0};
}
@ -141,7 +141,7 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
mempool_txs.pop_back();
break;
}
pool.addUnchecked(child_entry);
AddToMempool(pool, child_entry);
if (fuzzed_data_provider.ConsumeBool()) {
pool.PrioritiseTransaction(mempool_txs.back().GetHash().ToUint256(), fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(-100000, 100000));

View file

@ -72,17 +72,17 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Just the parent:
testPool.addUnchecked(entry.FromTx(txParent));
AddToMempool(testPool, entry.FromTx(txParent));
poolSize = testPool.size();
testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
// Parent, children, grandchildren:
testPool.addUnchecked(entry.FromTx(txParent));
AddToMempool(testPool, entry.FromTx(txParent));
for (int i = 0; i < 3; i++)
{
testPool.addUnchecked(entry.FromTx(txChild[i]));
testPool.addUnchecked(entry.FromTx(txGrandChild[i]));
AddToMempool(testPool, entry.FromTx(txChild[i]));
AddToMempool(testPool, entry.FromTx(txGrandChild[i]));
}
// Remove Child[0], GrandChild[0] should be removed:
poolSize = testPool.size();
@ -104,8 +104,8 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
// Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
for (int i = 0; i < 3; i++)
{
testPool.addUnchecked(entry.FromTx(txChild[i]));
testPool.addUnchecked(entry.FromTx(txGrandChild[i]));
AddToMempool(testPool, entry.FromTx(txChild[i]));
AddToMempool(testPool, entry.FromTx(txGrandChild[i]));
}
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
// put into the mempool (maybe because it is non-standard):
@ -137,28 +137,28 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx1.vout.resize(1);
tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx1.vout[0].nValue = 10 * COIN;
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
/* highest fee */
CMutableTransaction tx2 = CMutableTransaction();
tx2.vout.resize(1);
tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx2.vout[0].nValue = 2 * COIN;
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
AddToMempool(pool, entry.Fee(20000LL).FromTx(tx2));
/* lowest fee */
CMutableTransaction tx3 = CMutableTransaction();
tx3.vout.resize(1);
tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx3.vout[0].nValue = 5 * COIN;
pool.addUnchecked(entry.Fee(0LL).FromTx(tx3));
AddToMempool(pool, entry.Fee(0LL).FromTx(tx3));
/* 2nd highest fee */
CMutableTransaction tx4 = CMutableTransaction();
tx4.vout.resize(1);
tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx4.vout[0].nValue = 6 * COIN;
pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4));
AddToMempool(pool, entry.Fee(15000LL).FromTx(tx4));
/* equal fee rate to tx1, but newer */
CMutableTransaction tx5 = CMutableTransaction();
@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx5.vout[0].nValue = 11 * COIN;
entry.time = NodeSeconds{1s};
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx5));
BOOST_CHECK_EQUAL(pool.size(), 5U);
std::vector<std::string> sortedOrder;
@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx6.vout.resize(1);
tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx6.vout[0].nValue = 20 * COIN;
pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
AddToMempool(pool, entry.Fee(0LL).FromTx(tx6));
BOOST_CHECK_EQUAL(pool.size(), 6U);
// Check that at this point, tx6 is sorted low
sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK(*ancestors_calculated == setAncestors);
}
pool.addUnchecked(entry.FromTx(tx7), setAncestors);
AddToMempool(pool, entry.FromTx(tx7));
BOOST_CHECK_EQUAL(pool.size(), 7U);
// Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ...
@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx8.vout[0].nValue = 10 * COIN;
setAncestors.insert(pool.GetIter(tx7.GetHash()).value());
pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{2s}).FromTx(tx8), setAncestors);
AddToMempool(pool, entry.Fee(0LL).Time(NodeSeconds{2s}).FromTx(tx8));
// Now tx8 should be sorted low, but tx6/tx both high
sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx9.vout.resize(1);
tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx9.vout[0].nValue = 1 * COIN;
pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{3s}).FromTx(tx9), setAncestors);
AddToMempool(pool, entry.Fee(0LL).Time(NodeSeconds{3s}).FromTx(tx9));
// tx9 should be sorted low
BOOST_CHECK_EQUAL(pool.size(), 9U);
@ -268,7 +268,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK(*ancestors_calculated == setAncestors);
}
pool.addUnchecked(entry.FromTx(tx10), setAncestors);
AddToMempool(pool, entry.FromTx(tx10));
/**
* tx8 and tx9 should both now be sorted higher
@ -313,14 +313,14 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx1.vout.resize(1);
tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx1.vout[0].nValue = 10 * COIN;
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
/* highest fee */
CMutableTransaction tx2 = CMutableTransaction();
tx2.vout.resize(1);
tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx2.vout[0].nValue = 2 * COIN;
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
AddToMempool(pool, entry.Fee(20000LL).FromTx(tx2));
uint64_t tx2Size = GetVirtualTransactionSize(CTransaction(tx2));
/* lowest fee */
@ -328,21 +328,21 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx3.vout.resize(1);
tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx3.vout[0].nValue = 5 * COIN;
pool.addUnchecked(entry.Fee(0LL).FromTx(tx3));
AddToMempool(pool, entry.Fee(0LL).FromTx(tx3));
/* 2nd highest fee */
CMutableTransaction tx4 = CMutableTransaction();
tx4.vout.resize(1);
tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx4.vout[0].nValue = 6 * COIN;
pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4));
AddToMempool(pool, entry.Fee(15000LL).FromTx(tx4));
/* equal fee rate to tx1, but newer */
CMutableTransaction tx5 = CMutableTransaction();
tx5.vout.resize(1);
tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx5.vout[0].nValue = 11 * COIN;
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx5));
BOOST_CHECK_EQUAL(pool.size(), 5U);
std::vector<std::string> sortedOrder;
@ -371,7 +371,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx6.vout[0].nValue = 20 * COIN;
uint64_t tx6Size = GetVirtualTransactionSize(CTransaction(tx6));
pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
AddToMempool(pool, entry.Fee(0LL).FromTx(tx6));
BOOST_CHECK_EQUAL(pool.size(), 6U);
// Ties are broken by hash
if (tx3.GetHash() < tx6.GetHash())
@ -393,7 +393,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
/* set the fee to just below tx2's feerate when including ancestor */
CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
pool.addUnchecked(entry.Fee(fee).FromTx(tx7));
AddToMempool(pool, entry.Fee(fee).FromTx(tx7));
BOOST_CHECK_EQUAL(pool.size(), 7U);
sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString());
CheckSort<ancestor_score>(pool, sortedOrder);
@ -425,7 +425,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
// Check that we sort by min(feerate, ancestor_feerate):
// set the fee so that the ancestor feerate is above tx1/5,
// but the transaction's own feerate is lower
pool.addUnchecked(entry.Fee(5000LL).FromTx(tx8));
AddToMempool(pool, entry.Fee(5000LL).FromTx(tx8));
sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString());
CheckSort<ancestor_score>(pool, sortedOrder);
}
@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
tx1.vout.resize(1);
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
tx1.vout[0].nValue = 10 * COIN;
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
CMutableTransaction tx2 = CMutableTransaction();
tx2.vin.resize(1);
@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
tx2.vout.resize(1);
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
tx2.vout[0].nValue = 10 * COIN;
pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2));
AddToMempool(pool, entry.Fee(5000LL).FromTx(tx2));
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
@ -461,7 +461,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
pool.addUnchecked(entry.FromTx(tx2));
AddToMempool(pool, entry.FromTx(tx2));
CMutableTransaction tx3 = CMutableTransaction();
tx3.vin.resize(1);
tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
@ -469,7 +469,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
tx3.vout.resize(1);
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
tx3.vout[0].nValue = 10 * COIN;
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3));
AddToMempool(pool, entry.Fee(20000LL).FromTx(tx3));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
@ -532,10 +532,10 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
tx7.vout[1].nValue = 10 * COIN;
pool.addUnchecked(entry.Fee(7000LL).FromTx(tx4));
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(1100LL).FromTx(tx6));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
AddToMempool(pool, entry.Fee(7000LL).FromTx(tx4));
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(1100LL).FromTx(tx6));
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
// we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
@ -544,8 +544,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
if (!pool.exists(GenTxid::Txid(tx5.GetHash())))
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
@ -553,8 +553,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
std::vector<CTransactionRef> vtx;
SetMockTime(42);
@ -613,7 +613,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
// [tx1]
//
CTransactionRef tx1 = make_tx(/*output_values=*/{10 * COIN});
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
// Ancestors / descendants should be 1 / 1 (itself / itself)
pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
@ -625,7 +625,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
// [tx1].0 <- [tx2]
//
CTransactionRef tx2 = make_tx(/*output_values=*/{495 * CENT, 5 * COIN}, /*inputs=*/{tx1});
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx2));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx2));
// Ancestors / descendants should be:
// transaction ancestors descendants
@ -644,7 +644,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
// [tx1].0 <- [tx2].0 <- [tx3]
//
CTransactionRef tx3 = make_tx(/*output_values=*/{290 * CENT, 200 * CENT}, /*inputs=*/{tx2});
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx3));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx3));
// Ancestors / descendants should be:
// transaction ancestors descendants
@ -669,7 +669,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
// \---1 <- [tx4]
//
CTransactionRef tx4 = make_tx(/*output_values=*/{290 * CENT, 250 * CENT}, /*inputs=*/{tx2}, /*input_indices=*/{1});
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx4));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx4));
// Ancestors / descendants should be:
// transaction ancestors descendants
@ -706,13 +706,13 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
CTransactionRef& tyi = *ty[i];
tyi = make_tx(/*output_values=*/{v}, /*inputs=*/i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{});
v -= 50 * CENT;
pool.addUnchecked(entry.Fee(10000LL).FromTx(tyi));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tyi));
pool.GetTransactionAncestry(tyi->GetHash(), ancestors, descendants);
BOOST_CHECK_EQUAL(ancestors, i+1);
BOOST_CHECK_EQUAL(descendants, i+1);
}
CTransactionRef ty6 = make_tx(/*output_values=*/{5 * COIN}, /*inputs=*/{tx3, ty5});
pool.addUnchecked(entry.Fee(10000LL).FromTx(ty6));
AddToMempool(pool, entry.Fee(10000LL).FromTx(ty6));
// Ancestors / descendants should be:
// transaction ancestors descendants
@ -778,10 +778,10 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTestsDiamond)
tb = make_tx(/*output_values=*/{5 * COIN, 3 * COIN}, /*inputs=*/ {ta});
tc = make_tx(/*output_values=*/{2 * COIN}, /*inputs=*/{tb}, /*input_indices=*/{1});
td = make_tx(/*output_values=*/{6 * COIN}, /*inputs=*/{tb, tc}, /*input_indices=*/{0, 0});
pool.addUnchecked(entry.Fee(10000LL).FromTx(ta));
pool.addUnchecked(entry.Fee(10000LL).FromTx(tb));
pool.addUnchecked(entry.Fee(10000LL).FromTx(tc));
pool.addUnchecked(entry.Fee(10000LL).FromTx(td));
AddToMempool(pool, entry.Fee(10000LL).FromTx(ta));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tb));
AddToMempool(pool, entry.Fee(10000LL).FromTx(tc));
AddToMempool(pool, entry.Fee(10000LL).FromTx(td));
// Ancestors / descendants should be:
// transaction ancestors descendants

View file

@ -123,19 +123,19 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
tx.vout[0].nValue = 5000000000LL - 1000;
// This tx has a low fee: 1000 satoshis
Txid hashParentTx = tx.GetHash(); // save this txid for later use
tx_mempool.addUnchecked(entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
// This tx has a medium fee: 10000 satoshis
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = 5000000000LL - 10000;
Txid hashMediumFeeTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
// This tx has a high fee, but depends on the first transaction
tx.vin[0].prevout.hash = hashParentTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
Txid hashHighFeeTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U);
@ -147,7 +147,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
tx.vin[0].prevout.hash = hashHighFeeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
Txid hashFreeTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(0).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(0).FromTx(tx));
size_t freeTxSize = ::GetSerializeSize(TX_WITH_WITNESS(tx));
// Calculate a fee on child transaction that will put the package just
@ -157,7 +157,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
tx.vin[0].prevout.hash = hashFreeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
Txid hashLowFeeTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(feeToUse).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(feeToUse).FromTx(tx));
pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@ -171,7 +171,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
tx_mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(feeToUse + 2).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(feeToUse + 2).FromTx(tx));
pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
@ -185,7 +185,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
tx.vout[0].nValue = 5000000000LL - 100000000;
tx.vout[1].nValue = 100000000; // 1BTC output
Txid hashFreeTx2 = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
// This tx can't be mined by itself
tx.vin[0].prevout.hash = hashFreeTx2;
@ -193,7 +193,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
Txid hashLowFeeTx2 = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
// Verify that this tx isn't selected.
@ -206,7 +206,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
// as well.
tx.vin[0].prevout.n = 1;
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
tx_mempool.addUnchecked(entry.Fee(10000).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(10000).FromTx(tx));
pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
@ -246,7 +246,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
@ -264,7 +264,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
@ -288,7 +288,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
@ -300,7 +300,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
// orphan in tx_mempool, template creation fails
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).FromTx(tx));
BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
}
@ -313,7 +313,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin.resize(2);
tx.vin[1].scriptSig = CScript() << OP_1;
@ -321,7 +321,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vin[1].prevout.n = 0;
tx.vout[0].nValue = tx.vout[0].nValue + BLOCKSUBSIDY - HIGHERFEE; // First txn output + fresh coinbase - new txn fee
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(HIGHERFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
}
@ -336,7 +336,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vout[0].nValue = 0;
hash = tx.GetHash();
// give it a fee so it'll get mined
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
// Should throw bad-cb-multiple
BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
}
@ -351,10 +351,10 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
tx.vout[0].scriptPubKey = CScript() << OP_1;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
}
@ -397,12 +397,12 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
CScript script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
// Should throw block-validation-failed
BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
@ -439,7 +439,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = 0;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(HIGHFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks fail
@ -453,7 +453,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Time(Now<NodeSeconds>()).FromTx(tx));
AddToMempool(tx_mempool, entry.Time(Now<NodeSeconds>()).FromTx(tx));
BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks fail
@ -476,7 +476,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
prevheights[0] = baseheight + 3;
tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Time(Now<NodeSeconds>()).FromTx(tx));
AddToMempool(tx_mempool, entry.Time(Now<NodeSeconds>()).FromTx(tx));
BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
@ -487,7 +487,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
tx_mempool.addUnchecked(entry.Time(Now<NodeSeconds>()).FromTx(tx));
AddToMempool(tx_mempool, entry.Time(Now<NodeSeconds>()).FromTx(tx));
BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
@ -542,7 +542,7 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
tx.vout.resize(1);
tx.vout[0].nValue = 5000000000LL; // 0 fee
uint256 hashFreePrioritisedTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(0).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(0).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
tx_mempool.PrioritiseTransaction(hashFreePrioritisedTx, 5 * COIN);
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
@ -550,20 +550,20 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
tx.vout[0].nValue = 5000000000LL - 1000;
// This tx has a low fee: 1000 satoshis
Txid hashParentTx = tx.GetHash(); // save this txid for later use
tx_mempool.addUnchecked(entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
// This tx has a medium fee: 10000 satoshis
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vout[0].nValue = 5000000000LL - 10000;
Txid hashMediumFeeTx = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
tx_mempool.PrioritiseTransaction(hashMediumFeeTx, -5 * COIN);
// This tx also has a low fee, but is prioritised
tx.vin[0].prevout.hash = hashParentTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 1000; // 1000 satoshi fee
Txid hashPrioritsedChild = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
tx_mempool.PrioritiseTransaction(hashPrioritsedChild, 2 * COIN);
// Test that transaction selection properly updates ancestor fee calculations as prioritised
@ -575,19 +575,19 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
tx.vout[0].nValue = 5000000000LL; // 0 fee
Txid hashFreeParent = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
tx_mempool.PrioritiseTransaction(hashFreeParent, 10 * COIN);
tx.vin[0].prevout.hash = hashFreeParent;
tx.vout[0].nValue = 5000000000LL; // 0 fee
Txid hashFreeChild = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
tx_mempool.PrioritiseTransaction(hashFreeChild, 1 * COIN);
tx.vin[0].prevout.hash = hashFreeChild;
tx.vout[0].nValue = 5000000000LL; // 0 fee
Txid hashFreeGrandchild = tx.GetHash();
tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
AddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
auto pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);

View file

@ -84,7 +84,7 @@ BOOST_FIXTURE_TEST_CASE(miniminer_negative, TestChain100Setup)
const CAmount negative_modified_fees{positive_base_fee + negative_fee_delta};
BOOST_CHECK(negative_modified_fees < 0);
const auto tx_mod_negative = make_tx({COutPoint{m_coinbase_txns[4]->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(positive_base_fee).FromTx(tx_mod_negative));
AddToMempool(pool, entry.Fee(positive_base_fee).FromTx(tx_mod_negative));
pool.PrioritiseTransaction(tx_mod_negative->GetHash(), negative_fee_delta);
const COutPoint only_outpoint{tx_mod_negative->GetHash(), 0};
@ -114,21 +114,21 @@ BOOST_FIXTURE_TEST_CASE(miniminer_1p1c, TestChain100Setup)
// Create a parent tx0 and child tx1 with normal fees:
const auto tx0 = make_tx({COutPoint{m_coinbase_txns[0]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(med_fee).FromTx(tx0));
AddToMempool(pool, entry.Fee(med_fee).FromTx(tx0));
const auto tx1 = make_tx({COutPoint{tx0->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(med_fee).FromTx(tx1));
AddToMempool(pool, entry.Fee(med_fee).FromTx(tx1));
// Create a low-feerate parent tx2 and high-feerate child tx3 (cpfp)
const auto tx2 = make_tx({COutPoint{m_coinbase_txns[1]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx2));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx2));
const auto tx3 = make_tx({COutPoint{tx2->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx3));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx3));
// Create a parent tx4 and child tx5 where both have low fees
const auto tx4 = make_tx({COutPoint{m_coinbase_txns[2]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx4));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx4));
const auto tx5 = make_tx({COutPoint{tx4->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx5));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx5));
const CAmount tx5_delta{CENT/100};
// Make tx5's modified fee much higher than its base fee. This should cause it to pass
// the fee-related checks despite being low-feerate.
@ -137,9 +137,9 @@ BOOST_FIXTURE_TEST_CASE(miniminer_1p1c, TestChain100Setup)
// Create a high-feerate parent tx6, low-feerate child tx7
const auto tx6 = make_tx({COutPoint{m_coinbase_txns[3]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx6));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx6));
const auto tx7 = make_tx({COutPoint{tx6->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx7));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx7));
std::vector<COutPoint> all_unspent_outpoints({
COutPoint{tx0->GetHash(), 1},
@ -405,23 +405,23 @@ BOOST_FIXTURE_TEST_CASE(miniminer_overlap, TestChain100Setup)
// Create 3 parents of different feerates, and 1 child spending outputs from all 3 parents.
const auto tx0 = make_tx({COutPoint{m_coinbase_txns[0]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx0));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx0));
const auto tx1 = make_tx({COutPoint{m_coinbase_txns[1]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(med_fee).FromTx(tx1));
AddToMempool(pool, entry.Fee(med_fee).FromTx(tx1));
const auto tx2 = make_tx({COutPoint{m_coinbase_txns[2]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx2));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx2));
const auto tx3 = make_tx({COutPoint{tx0->GetHash(), 0}, COutPoint{tx1->GetHash(), 0}, COutPoint{tx2->GetHash(), 0}}, /*num_outputs=*/3);
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx3));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx3));
// Create 1 grandparent and 1 parent, then 2 children.
const auto tx4 = make_tx({COutPoint{m_coinbase_txns[3]->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx4));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx4));
const auto tx5 = make_tx({COutPoint{tx4->GetHash(), 0}}, /*num_outputs=*/3);
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx5));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx5));
const auto tx6 = make_tx({COutPoint{tx5->GetHash(), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(med_fee).FromTx(tx6));
AddToMempool(pool, entry.Fee(med_fee).FromTx(tx6));
const auto tx7 = make_tx({COutPoint{tx5->GetHash(), 1}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx7));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx7));
std::vector<CTransactionRef> all_transactions{tx0, tx1, tx2, tx3, tx4, tx5, tx6, tx7};
std::vector<int64_t> tx_vsizes;
@ -604,7 +604,7 @@ BOOST_FIXTURE_TEST_CASE(calculate_cluster, TestChain100Setup)
auto& lasttx = m_coinbase_txns[0];
for (auto i{0}; i < 500; ++i) {
const auto tx = make_tx({COutPoint{lasttx->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(CENT).FromTx(tx));
AddToMempool(pool, entry.Fee(CENT).FromTx(tx));
chain_txids.push_back(tx->GetHash());
lasttx = tx;
}
@ -616,7 +616,7 @@ BOOST_FIXTURE_TEST_CASE(calculate_cluster, TestChain100Setup)
// GatherClusters stops at 500 transactions.
const auto tx_501 = make_tx({COutPoint{lasttx->GetHash(), 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(CENT).FromTx(tx_501));
AddToMempool(pool, entry.Fee(CENT).FromTx(tx_501));
const auto cluster_501 = pool.GatherClusters({tx_501->GetHash()});
BOOST_CHECK_EQUAL(cluster_501.size(), 0);
@ -629,12 +629,12 @@ BOOST_FIXTURE_TEST_CASE(calculate_cluster, TestChain100Setup)
std::vector<Txid> zigzag_txids;
for (auto p{0}; p < 50; ++p) {
const auto txp = make_tx({COutPoint{Txid::FromUint256(GetRandHash()), 0}}, /*num_outputs=*/2);
pool.addUnchecked(entry.Fee(CENT).FromTx(txp));
AddToMempool(pool, entry.Fee(CENT).FromTx(txp));
zigzag_txids.push_back(txp->GetHash());
}
for (auto c{0}; c < 49; ++c) {
const auto txc = make_tx({COutPoint{zigzag_txids[c], 1}, COutPoint{zigzag_txids[c+1], 0}}, /*num_outputs=*/1);
pool.addUnchecked(entry.Fee(CENT).FromTx(txc));
AddToMempool(pool, entry.Fee(CENT).FromTx(txc));
zigzag_txids.push_back(txc->GetHash());
}
const auto vec_iters_zigzag = pool.GetIterVec(convert_to_uint256_vec(zigzag_txids));

View file

@ -63,9 +63,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique
{
LOCK2(cs_main, mpool.cs);
mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
AddToMempool(mpool, entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
// Since TransactionAddedToMempool callbacks are generated in ATMP,
// not addUnchecked, we cheat and create one manually here
// not AddToMempool, we cheat and create one manually here
const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
feeV[j],
@ -164,9 +164,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
{
LOCK2(cs_main, mpool.cs);
mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
AddToMempool(mpool, entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
// Since TransactionAddedToMempool callbacks are generated in ATMP,
// not addUnchecked, we cheat and create one manually here
// not AddToMempool, we cheat and create one manually here
const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
feeV[j],
@ -228,9 +228,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
{
LOCK2(cs_main, mpool.cs);
mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
AddToMempool(mpool, entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
// Since TransactionAddedToMempool callbacks are generated in ATMP,
// not addUnchecked, we cheat and create one manually here
// not AddToMempool, we cheat and create one manually here
const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
feeV[j],

View file

@ -78,7 +78,7 @@ static CTransactionRef add_descendants(const CTransactionRef& tx, int32_t num_de
auto tx_to_spend = tx;
for (int32_t i{0}; i < num_descendants; ++i) {
auto next_tx = make_tx(/*inputs=*/{tx_to_spend}, /*output_values=*/{(50 - i) * CENT});
pool.addUnchecked(entry.FromTx(next_tx));
AddToMempool(pool, entry.FromTx(next_tx));
tx_to_spend = next_tx;
}
// Return last created tx
@ -93,7 +93,7 @@ static CTransactionRef add_descendant_to_parents(const std::vector<CTransactionR
TestMemPoolEntryHelper entry;
// Assumes this isn't already spent in mempool
auto child_tx = make_tx(/*inputs=*/parents, /*output_values=*/{50 * CENT});
pool.addUnchecked(entry.FromTx(child_tx));
AddToMempool(pool, entry.FromTx(child_tx));
// Return last created tx
return child_tx;
}
@ -107,8 +107,8 @@ static std::pair<CTransactionRef, CTransactionRef> add_children_to_parent(const
TestMemPoolEntryHelper entry;
// Assumes this isn't already spent in mempool
auto children_tx = make_two_siblings(/*parent=*/parent, /*output_values=*/{50 * CENT});
pool.addUnchecked(entry.FromTx(children_tx.first));
pool.addUnchecked(entry.FromTx(children_tx.second));
AddToMempool(pool, entry.FromTx(children_tx.first));
AddToMempool(pool, entry.FromTx(children_tx.second));
return children_tx;
}
@ -124,46 +124,46 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
// Create a parent tx1 and child tx2 with normal fees:
const auto tx1 = make_tx(/*inputs=*/ {m_coinbase_txns[0]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx1));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx1));
const auto tx2 = make_tx(/*inputs=*/ {tx1}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx2));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx2));
// Create a low-feerate parent tx3 and high-feerate child tx4 (cpfp)
const auto tx3 = make_tx(/*inputs=*/ {m_coinbase_txns[1]}, /*output_values=*/ {1099 * CENT});
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx3));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx3));
const auto tx4 = make_tx(/*inputs=*/ {tx3}, /*output_values=*/ {999 * CENT});
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx4));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx4));
// Create a parent tx5 and child tx6 where both have very low fees
const auto tx5 = make_tx(/*inputs=*/ {m_coinbase_txns[2]}, /*output_values=*/ {1099 * CENT});
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx5));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx5));
const auto tx6 = make_tx(/*inputs=*/ {tx5}, /*output_values=*/ {1098 * CENT});
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx6));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx6));
// Make tx6's modified fee much higher than its base fee. This should cause it to pass
// the fee-related checks despite being low-feerate.
pool.PrioritiseTransaction(tx6->GetHash(), 1 * COIN);
// Two independent high-feerate transactions, tx7 and tx8
const auto tx7 = make_tx(/*inputs=*/ {m_coinbase_txns[3]}, /*output_values=*/ {999 * CENT});
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx7));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx7));
const auto tx8 = make_tx(/*inputs=*/ {m_coinbase_txns[4]}, /*output_values=*/ {999 * CENT});
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx8));
AddToMempool(pool, entry.Fee(high_fee).FromTx(tx8));
// Normal txs, will chain txns right before CheckConflictTopology test
const auto tx9 = make_tx(/*inputs=*/ {m_coinbase_txns[5]}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx9));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx9));
const auto tx10 = make_tx(/*inputs=*/ {m_coinbase_txns[6]}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx10));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx10));
// Will make these two parents of single child
const auto tx11 = make_tx(/*inputs=*/ {m_coinbase_txns[7]}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx11));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx11));
const auto tx12 = make_tx(/*inputs=*/ {m_coinbase_txns[8]}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx12));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx12));
// Will make two children of this single parent
const auto tx13 = make_tx(/*inputs=*/ {m_coinbase_txns[9]}, /*output_values=*/ {995 * CENT, 995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx13));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx13));
const auto entry1_normal = pool.GetIter(tx1->GetHash()).value();
const auto entry2_normal = pool.GetIter(tx2->GetHash()).value();
@ -363,9 +363,9 @@ BOOST_FIXTURE_TEST_CASE(improves_feerate, TestChain100Setup)
// low feerate parent with normal feerate child
const auto tx1 = make_tx(/*inputs=*/ {m_coinbase_txns[0]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(low_fee).FromTx(tx1));
AddToMempool(pool, entry.Fee(low_fee).FromTx(tx1));
const auto tx2 = make_tx(/*inputs=*/ {tx1}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx2));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx2));
const auto entry1 = pool.GetIter(tx1->GetHash()).value();
const auto tx1_fee = entry1->GetModifiedFee();
@ -398,7 +398,7 @@ BOOST_FIXTURE_TEST_CASE(improves_feerate, TestChain100Setup)
// Adding a grandchild makes the cluster size 3, which is uncalculable
const auto tx3 = make_tx(/*inputs=*/ {tx2}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx3));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx3));
const auto res3 = ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1, tx1_size + tx2_size);
BOOST_CHECK(res3.has_value());
BOOST_CHECK(res3.value().first == DiagramCheckError::UNCALCULABLE);
@ -419,7 +419,7 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// low -> high -> medium fee transactions that would result in two chunks together since they
// are all same size
const auto low_tx = make_tx(/*inputs=*/ {m_coinbase_txns[0]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(low_fee).FromTx(low_tx));
AddToMempool(pool, entry.Fee(low_fee).FromTx(low_tx));
const auto entry_low = pool.GetIter(low_tx->GetHash()).value();
const auto low_size = entry_low->GetTxSize();
@ -446,7 +446,7 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// Add a second transaction to the cluster that will make a single chunk, to be evicted in the RBF
const auto high_tx = make_tx(/*inputs=*/ {low_tx}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(high_fee).FromTx(high_tx));
AddToMempool(pool, entry.Fee(high_fee).FromTx(high_tx));
const auto entry_high = pool.GetIter(high_tx->GetHash()).value();
const auto high_size = entry_high->GetTxSize();
@ -471,7 +471,7 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// third transaction causes the topology check to fail
const auto normal_tx = make_tx(/*inputs=*/ {high_tx}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(normal_fee).FromTx(normal_tx));
AddToMempool(pool, entry.Fee(normal_fee).FromTx(normal_tx));
const auto entry_normal = pool.GetIter(normal_tx->GetHash()).value();
const auto normal_size = entry_normal->GetTxSize();
@ -483,12 +483,12 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// Make a size 2 cluster that is itself two chunks; evict both txns
const auto high_tx_2 = make_tx(/*inputs=*/ {m_coinbase_txns[1]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(high_fee).FromTx(high_tx_2));
AddToMempool(pool, entry.Fee(high_fee).FromTx(high_tx_2));
const auto entry_high_2 = pool.GetIter(high_tx_2->GetHash()).value();
const auto high_size_2 = entry_high_2->GetTxSize();
const auto low_tx_2 = make_tx(/*inputs=*/ {high_tx_2}, /*output_values=*/ {9 * COIN});
pool.addUnchecked(entry.Fee(low_fee).FromTx(low_tx_2));
AddToMempool(pool, entry.Fee(low_fee).FromTx(low_tx_2));
const auto entry_low_2 = pool.GetIter(low_tx_2->GetHash()).value();
const auto low_size_2 = entry_low_2->GetTxSize();
@ -503,15 +503,15 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// You can have more than two direct conflicts if the there are multiple affected clusters, all of size 2 or less
const auto conflict_1 = make_tx(/*inputs=*/ {m_coinbase_txns[2]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_1));
AddToMempool(pool, entry.Fee(low_fee).FromTx(conflict_1));
const auto conflict_1_entry = pool.GetIter(conflict_1->GetHash()).value();
const auto conflict_2 = make_tx(/*inputs=*/ {m_coinbase_txns[3]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_2));
AddToMempool(pool, entry.Fee(low_fee).FromTx(conflict_2));
const auto conflict_2_entry = pool.GetIter(conflict_2->GetHash()).value();
const auto conflict_3 = make_tx(/*inputs=*/ {m_coinbase_txns[4]}, /*output_values=*/ {10 * COIN});
pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_3));
AddToMempool(pool, entry.Fee(low_fee).FromTx(conflict_3));
const auto conflict_3_entry = pool.GetIter(conflict_3->GetHash()).value();
{
@ -523,7 +523,7 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// Add a child transaction to conflict_1 and make it cluster size 2, two chunks due to same feerate
const auto conflict_1_child = make_tx(/*inputs=*/{conflict_1}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_1_child));
AddToMempool(pool, entry.Fee(low_fee).FromTx(conflict_1_child));
const auto conflict_1_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
{
@ -536,7 +536,7 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// Add another descendant to conflict_1, making the cluster size > 2 should fail at this point.
const auto conflict_1_grand_child = make_tx(/*inputs=*/{conflict_1_child}, /*output_values=*/ {995 * CENT});
pool.addUnchecked(entry.Fee(high_fee).FromTx(conflict_1_grand_child));
AddToMempool(pool, entry.Fee(high_fee).FromTx(conflict_1_grand_child));
const auto conflict_1_grand_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
{

View file

@ -180,7 +180,7 @@ BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1}, minrelay, pool).has_value());
// Add first grandparent to mempool and fetch entry
pool.addUnchecked(entry.FromTx(grandparent_tx_1));
AddToMempool(pool, entry.FromTx(grandparent_tx_1));
// Ignores ancestors that aren't direct parents
BOOST_CHECK(!CheckEphemeralSpends({child_no_dust}, minrelay, pool).has_value());
@ -194,7 +194,7 @@ BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_2, parent_with_dust}, minrelay, pool).has_value());
// Add second grandparent to mempool
pool.addUnchecked(entry.FromTx(grandparent_tx_2));
AddToMempool(pool, entry.FromTx(grandparent_tx_2));
// Only spends single dust out of two direct parents
BOOST_CHECK(CheckEphemeralSpends({dust_non_spend_both_parents}, minrelay, pool).has_value());
@ -203,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
BOOST_CHECK(!CheckEphemeralSpends({parent_with_dust}, minrelay, pool).has_value());
// Now add dusty parent to mempool
pool.addUnchecked(entry.FromTx(parent_with_dust));
AddToMempool(pool, entry.FromTx(parent_with_dust));
// Passes dust checks even with non-parent ancestors
BOOST_CHECK(!CheckEphemeralSpends({child_no_dust}, minrelay, pool).has_value());
@ -219,9 +219,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
CTxMemPool::setEntries empty_ancestors;
auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);
pool.addUnchecked(entry.FromTx(mempool_tx_v3));
AddToMempool(pool, entry.FromTx(mempool_tx_v3));
auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);
pool.addUnchecked(entry.FromTx(mempool_tx_v2));
AddToMempool(pool, entry.FromTx(mempool_tx_v2));
// Default values.
CTxMemPool::Limits m_limits{};
@ -331,7 +331,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
package_multi_parents.emplace_back(mempool_tx_v3);
for (size_t i{0}; i < 2; ++i) {
auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);
pool.addUnchecked(entry.FromTx(mempool_tx));
AddToMempool(pool, entry.FromTx(mempool_tx));
mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);
package_multi_parents.emplace_back(mempool_tx);
}
@ -356,7 +356,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
auto last_outpoint{random_outpoints(1)[0]};
for (size_t i{0}; i < 2; ++i) {
auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);
pool.addUnchecked(entry.FromTx(mempool_tx));
AddToMempool(pool, entry.FromTx(mempool_tx));
last_outpoint = COutPoint{mempool_tx->GetHash(), 0};
package_multi_gen.emplace_back(mempool_tx);
if (i == 1) middle_tx = mempool_tx;
@ -443,7 +443,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
BOOST_CHECK(GetTransactionWeight(*tx_mempool_v3_child) <= TRUC_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR);
auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_mempool_v3_child), m_limits)};
BOOST_CHECK(SingleTRUCChecks(tx_mempool_v3_child, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt);
pool.addUnchecked(entry.FromTx(tx_mempool_v3_child));
AddToMempool(pool, entry.FromTx(tx_mempool_v3_child));
Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child};
BOOST_CHECK(PackageTRUCChecks(tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_ancestors) == std::nullopt);
@ -471,7 +471,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
expected_error_str);
// Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg.
pool.addUnchecked(entry.FromTx(tx_v3_child2));
AddToMempool(pool, entry.FromTx(tx_v3_child2));
auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value();
BOOST_CHECK_EQUAL(entry_mempool_parent->GetCountWithDescendants(), 3);
@ -490,9 +490,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
pool.addUnchecked(entry.FromTx(tx_mempool_grandparent));
pool.addUnchecked(entry.FromTx(tx_mempool_sibling));
pool.addUnchecked(entry.FromTx(tx_mempool_nibling));
AddToMempool(pool, entry.FromTx(tx_mempool_grandparent));
AddToMempool(pool, entry.FromTx(tx_mempool_sibling));
AddToMempool(pool, entry.FromTx(tx_mempool_nibling));
auto ancestors_3gen{pool.CalculateMemPoolAncestors(entry.FromTx(tx_to_submit), m_limits)};
const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",

View file

@ -539,9 +539,11 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex
if (submit) {
LOCK2(cs_main, m_node.mempool->cs);
LockPoints lp;
m_node.mempool->addUnchecked(CTxMemPoolEntry(ptx, /*fee=*/(total_in - num_outputs * amount_per_output),
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
auto changeset = m_node.mempool->GetChangeSet();
changeset->StageAddition(ptx, /*fee=*/(total_in - num_outputs * amount_per_output),
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*spends_coinbase=*/false, /*sigops_cost=*/4, lp);
changeset->Apply();
}
--num_transactions;
}
@ -569,9 +571,13 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
// The new mempool min feerate is equal to the removed package's feerate + incremental feerate.
const auto tx_fee = target_feerate.GetFee(GetVirtualTransactionSize(*tx)) -
m_node.mempool->m_opts.incremental_relay_feerate.GetFee(GetVirtualTransactionSize(*tx));
m_node.mempool->addUnchecked(CTxMemPoolEntry(tx, /*fee=*/tx_fee,
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*spends_coinbase=*/true, /*sigops_cost=*/1, lp));
{
auto changeset = m_node.mempool->GetChangeSet();
changeset->StageAddition(tx, /*fee=*/tx_fee,
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*spends_coinbase=*/true, /*sigops_cost=*/1, lp);
changeset->Apply();
}
m_node.mempool->TrimToSize(0);
assert(m_node.mempool->GetMinFee() == target_feerate);
}

View file

@ -219,3 +219,13 @@ void CheckMempoolTRUCInvariants(const CTxMemPool& tx_pool)
}
}
}
void AddToMempool(CTxMemPool& tx_pool, const CTxMemPoolEntry& entry)
{
LOCK2(cs_main, tx_pool.cs);
auto changeset = tx_pool.GetChangeSet();
changeset->StageAddition(entry.GetSharedTx(), entry.GetFee(),
entry.GetTime().count(), entry.GetHeight(), entry.GetSequence(),
entry.GetSpendsCoinbase(), entry.GetSigOpCost(), entry.GetLockPoints());
changeset->Apply();
}

View file

@ -68,4 +68,8 @@ std::vector<uint32_t> GetDustIndexes(const CTransactionRef& tx_ref, CFeeRate dus
* */
void CheckMempoolTRUCInvariants(const CTxMemPool& tx_pool);
/** One-line wrapper for creating a mempool changeset with a single transaction
* and applying it. */
void AddToMempool(CTxMemPool& tx_pool, const CTxMemPoolEntry& entry);
#endif // BITCOIN_TEST_UTIL_TXMEMPOOL_H

View file

@ -357,7 +357,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// of ancestors reachable via GetMemPoolParents()/GetMemPoolChildren()
// will be the same as the set of ancestors whose packages include this
// transaction, because when we add a new transaction to the mempool in
// addUnchecked(), we assume it has no children, and in the case of a
// addNewTransaction(), we assume it has no children, and in the case of a
// reorg where that assumption is false, the in-mempool children aren't
// linked to the in-block tx's until UpdateTransactionsFromBlock() is
// called.
@ -432,18 +432,58 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n)
nTransactionsUpdated += n;
}
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors)
void CTxMemPool::Apply(ChangeSet* changeset)
{
// Add to memory pool without checking anything.
// Used by AcceptToMemoryPool(), which DOES do
// all the appropriate checks.
indexed_transaction_set::iterator newit = mapTx.emplace(CTxMemPoolEntry::ExplicitCopy, entry).first;
AssertLockHeld(cs);
RemoveStaged(changeset->m_to_remove, false, MemPoolRemovalReason::REPLACED);
for (size_t i=0; i<changeset->m_entry_vec.size(); ++i) {
auto tx_entry = changeset->m_entry_vec[i];
std::optional<CTxMemPool::setEntries> ancestors;
if (i == 0) {
// Note: ChangeSet::CalculateMemPoolAncestors() will return a
// cached value if mempool ancestors for this tranaction were
// previously calculated.
// We can only use a cached ancestor calculation for the first
// transaction in a package, because in-package parents won't be
// present in the cached ancestor sets of in-package children.
// We pass in Limits::NoLimits() to ensure that this function won't fail
// (we're going to be applying this set of transactions whether or
// not the mempool policy limits are being respected).
ancestors = *Assume(changeset->CalculateMemPoolAncestors(tx_entry, Limits::NoLimits()));
}
// First splice this entry into mapTx.
auto node_handle = changeset->m_to_add.extract(tx_entry);
auto result = mapTx.insert(std::move(node_handle));
Assume(result.inserted);
txiter it = result.position;
// Now update the entry for ancestors/descendants.
if (ancestors.has_value()) {
addNewTransaction(it, *ancestors);
} else {
addNewTransaction(it);
}
}
}
void CTxMemPool::addNewTransaction(CTxMemPool::txiter it)
{
auto ancestors{AssumeCalculateMemPoolAncestors(__func__, *it, Limits::NoLimits())};
return addNewTransaction(it, ancestors);
}
void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit, CTxMemPool::setEntries& setAncestors)
{
const CTxMemPoolEntry& entry = *newit;
// Update transaction for any feeDelta created by PrioritiseTransaction
// TODO: move this into the changeset instead.
CAmount delta{0};
ApplyDelta(entry.GetTx().GetHash(), delta);
ApplyDelta(newit->GetTx().GetHash(), delta);
// The following call to UpdateModifiedFee assumes no previous fee modifications
Assume(entry.GetFee() == entry.GetModifiedFee());
Assume(newit->GetFee() == newit->GetModifiedFee());
if (delta) {
mapTx.modify(newit, [&delta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(delta); });
}
@ -468,7 +508,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
// Update ancestors with information about this tx
for (const auto& pit : GetIterSet(setParentTransactions)) {
UpdateParent(newit, pit, true);
UpdateParent(newit, pit, true);
}
UpdateAncestorsOf(true, newit, setAncestors);
UpdateEntryForAncestors(newit, setAncestors);
@ -1067,12 +1107,6 @@ int CTxMemPool::Expire(std::chrono::seconds time)
return stage.size();
}
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry)
{
auto ancestors{AssumeCalculateMemPoolAncestors(__func__, entry, Limits::NoLimits())};
return addUnchecked(entry, ancestors);
}
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add)
{
AssertLockHeld(cs);
@ -1380,17 +1414,7 @@ CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTran
void CTxMemPool::ChangeSet::Apply()
{
LOCK(m_pool->cs);
m_pool->RemoveStaged(m_to_remove, false, MemPoolRemovalReason::REPLACED);
for (size_t i=0; i<m_entry_vec.size(); ++i) {
auto tx_entry = m_entry_vec[i];
if (i == 0 && m_ancestors.count(tx_entry)) {
m_pool->addUnchecked(*tx_entry, m_ancestors[tx_entry]);
} else {
// We always recalculate ancestors from scratch if we're dealing
// with transactions which may have parents in the same package.
m_pool->addUnchecked(*tx_entry);
}
}
m_pool->Apply(this);
m_to_add.clear();
m_to_remove.clear();
m_entry_vec.clear();

View file

@ -260,13 +260,13 @@ struct TxMempoolInfo
*
* Usually when a new transaction is added to the mempool, it has no in-mempool
* children (because any such children would be an orphan). So in
* addUnchecked(), we:
* - update a new entry's setMemPoolParents to include all in-mempool parents
* - update the new entry's direct parents to include the new tx as a child
* addNewTransaction(), we:
* - update a new entry's m_parents to include all in-mempool parents
* - update each of those parent entries to include the new tx as a child
* - update all ancestors of the transaction to include the new tx's size/fee
*
* When a transaction is removed from the mempool, we must:
* - update all in-mempool parents to not track the tx in setMemPoolChildren
* - update all in-mempool parents to not track the tx in their m_children
* - update all ancestors to not include the tx's size/fees in descendant state
* - update all in-mempool children to not include it as a parent
*
@ -283,7 +283,7 @@ struct TxMempoolInfo
* unreachable from just looking at transactions in the mempool (the linking
* transactions may also be in the disconnected block, waiting to be added).
* Because of this, there's not much benefit in trying to search for in-mempool
* children in addUnchecked(). Instead, in the special case of transactions
* children in addNewTransaction(). Instead, in the special case of transactions
* being added from a disconnected block, we require the caller to clean up the
* state, to account for in-mempool, out-of-block descendants for all the
* in-block transactions by calling UpdateTransactionsFromBlock(). Note that
@ -453,15 +453,6 @@ public:
*/
void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
// addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
// addUnchecked can be used to have it call CalculateMemPoolAncestors(), and
// then invoke the second version.
// Note that addUnchecked is ONLY called from ATMP outside of tests
// and any other callers may break wallet's in-mempool tracking (due to
// lack of CValidationInterface::TransactionAddedToMempool callbacks).
void addUnchecked(const CTxMemPoolEntry& entry) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** After reorg, filter the entries that would no longer be valid in the next block, and update
@ -791,7 +782,7 @@ private:
/** Before calling removeUnchecked for a given transaction,
* UpdateForRemoveFromMempool must be called on the entire (dependent) set
* of transactions being removed at the same time. We use each
* CTxMemPoolEntry's setMemPoolParents in order to walk ancestors of a
* CTxMemPoolEntry's m_parents in order to walk ancestors of a
* given transaction that is removed, so we can't remove intermediate
* transactions in a chain before we've updated all the state for the
* removal.
@ -865,9 +856,29 @@ public:
// map from the m_to_add index to the ancestors for the transaction
std::map<CTxMemPool::txiter, CTxMemPool::setEntries, CompareIteratorByHash> m_ancestors;
CTxMemPool::setEntries m_to_remove;
friend class CTxMemPool;
};
std::unique_ptr<ChangeSet> GetChangeSet() EXCLUSIVE_LOCKS_REQUIRED(cs) { return std::make_unique<ChangeSet>(this); }
friend class CTxMemPool::ChangeSet;
private:
// Apply the given changeset to the mempool, by removing transactions in
// the to_remove set and adding transactions in the to_add set.
void Apply(CTxMemPool::ChangeSet* changeset) EXCLUSIVE_LOCKS_REQUIRED(cs);
// addNewTransaction must update state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
// addNewTransaction can be used to have it call CalculateMemPoolAncestors(), and
// then invoke the second version.
// Note that addNewTransaction is ONLY called (via Apply()) from ATMP
// outside of tests and any other callers may break wallet's in-mempool
// tracking (due to lack of CValidationInterface::TransactionAddedToMempool
// callbacks).
void addNewTransaction(CTxMemPool::txiter it) EXCLUSIVE_LOCKS_REQUIRED(cs);
void addNewTransaction(CTxMemPool::txiter it, CTxMemPool::setEntries& setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs);
};
/**

View file

@ -324,7 +324,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
}
}
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
// AcceptToMemoryPool/addNewTransaction all assume that new mempool entries have
// no in-mempool children, which is generally not true when adding
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in