mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-24 18:23:26 -03:00
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:
parent
34b6c5833d
commit
7fb62f7db6
20 changed files with 268 additions and 209 deletions
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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();
|
||||
|
||||
{
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue