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

@@ -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",