mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-18 11:30:44 +01:00
Move CalculateChunksForRBF() to the mempool changeset
This commit is contained in:
@@ -369,44 +369,79 @@ BOOST_FIXTURE_TEST_CASE(improves_feerate, TestChain100Setup)
|
||||
|
||||
const auto entry1 = pool.GetIter(tx1->GetHash()).value();
|
||||
const auto tx1_fee = entry1->GetModifiedFee();
|
||||
const auto tx1_size = entry1->GetTxSize();
|
||||
const auto entry2 = pool.GetIter(tx2->GetHash()).value();
|
||||
const auto tx2_fee = entry2->GetModifiedFee();
|
||||
const auto tx2_size = entry2->GetTxSize();
|
||||
|
||||
// conflicting transactions
|
||||
const auto tx1_conflict = make_tx(/*inputs=*/ {m_coinbase_txns[0], m_coinbase_txns[2]}, /*output_values=*/ {10 * COIN});
|
||||
const auto tx3 = make_tx(/*inputs=*/ {tx1_conflict}, /*output_values=*/ {995 * CENT});
|
||||
auto entry3 = entry.FromTx(tx3);
|
||||
|
||||
// Now test ImprovesFeerateDiagram with various levels of "package rbf" feerates
|
||||
|
||||
// It doesn't improve itself
|
||||
const auto res1 = ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee, tx1_size + tx2_size);
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry1);
|
||||
changeset->StageRemoval(entry2);
|
||||
changeset->StageAddition(tx1_conflict, tx1_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
changeset->StageAddition(tx3, tx2_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto res1 = ImprovesFeerateDiagram(*changeset);
|
||||
BOOST_CHECK(res1.has_value());
|
||||
BOOST_CHECK(res1.value().first == DiagramCheckError::FAILURE);
|
||||
BOOST_CHECK(res1.value().second == "insufficient feerate: does not improve feerate diagram");
|
||||
|
||||
// With one more satoshi it does
|
||||
BOOST_CHECK(ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1, tx1_size + tx2_size) == std::nullopt);
|
||||
changeset.reset();
|
||||
changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry1);
|
||||
changeset->StageRemoval(entry2);
|
||||
changeset->StageAddition(tx1_conflict, tx1_fee+1, 0, 1, 0, false, 4, LockPoints());
|
||||
changeset->StageAddition(tx3, tx2_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
BOOST_CHECK(ImprovesFeerateDiagram(*changeset) == std::nullopt);
|
||||
|
||||
changeset.reset();
|
||||
// With prioritisation of in-mempool conflicts, it affects the results of the comparison using the same args as just above
|
||||
pool.PrioritiseTransaction(entry1->GetSharedTx()->GetHash(), /*nFeeDelta=*/1);
|
||||
const auto res2 = ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1, tx1_size + tx2_size);
|
||||
changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry1);
|
||||
changeset->StageRemoval(entry2);
|
||||
changeset->StageAddition(tx1_conflict, tx1_fee+1, 0, 1, 0, false, 4, LockPoints());
|
||||
changeset->StageAddition(tx3, tx2_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto res2 = ImprovesFeerateDiagram(*changeset);
|
||||
BOOST_CHECK(res2.has_value());
|
||||
BOOST_CHECK(res2.value().first == DiagramCheckError::FAILURE);
|
||||
BOOST_CHECK(res2.value().second == "insufficient feerate: does not improve feerate diagram");
|
||||
changeset.reset();
|
||||
|
||||
pool.PrioritiseTransaction(entry1->GetSharedTx()->GetHash(), /*nFeeDelta=*/-1);
|
||||
|
||||
// With one less vB it does
|
||||
BOOST_CHECK(ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee, tx1_size + tx2_size - 1) == std::nullopt);
|
||||
// With fewer vbytes it does
|
||||
CMutableTransaction tx4{entry3.GetTx()};
|
||||
tx4.vin[0].scriptWitness = CScriptWitness(); // Clear out the witness, to reduce size
|
||||
auto entry4 = entry.FromTx(MakeTransactionRef(tx4));
|
||||
changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry1);
|
||||
changeset->StageRemoval(entry2);
|
||||
changeset->StageAddition(tx1_conflict, tx1_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
changeset->StageAddition(entry4.GetSharedTx(), tx2_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
BOOST_CHECK(ImprovesFeerateDiagram(*changeset) == std::nullopt);
|
||||
changeset.reset();
|
||||
|
||||
// Adding a grandchild makes the cluster size 3, which is uncalculable
|
||||
const auto tx3 = make_tx(/*inputs=*/ {tx2}, /*output_values=*/ {995 * CENT});
|
||||
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);
|
||||
const auto tx5 = make_tx(/*inputs=*/ {tx2}, /*output_values=*/ {995 * CENT});
|
||||
AddToMempool(pool, entry.Fee(normal_fee).FromTx(tx5));
|
||||
const auto entry5 = pool.GetIter(tx5->GetHash()).value();
|
||||
|
||||
changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry1);
|
||||
changeset->StageRemoval(entry2);
|
||||
changeset->StageRemoval(entry5);
|
||||
changeset->StageAddition(tx1_conflict, tx1_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
changeset->StageAddition(entry4.GetSharedTx(), tx2_fee + entry5->GetModifiedFee() + 1, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto res3 = ImprovesFeerateDiagram(*changeset);
|
||||
BOOST_CHECK(res3.has_value());
|
||||
BOOST_CHECK(res3.value().first == DiagramCheckError::UNCALCULABLE);
|
||||
BOOST_CHECK(res3.value().second == strprintf("%s has both ancestor and descendant, exceeding cluster limit of 2", tx2->GetHash().GetHex()));
|
||||
|
||||
BOOST_CHECK(res3.value().second == strprintf("%s has 2 ancestors, max 1 allowed", tx5->GetHash().GetHex()));
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
@@ -427,19 +462,28 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto entry_low = pool.GetIter(low_tx->GetHash()).value();
|
||||
const auto low_size = entry_low->GetTxSize();
|
||||
|
||||
const auto replacement_tx = make_tx(/*inputs=*/ {m_coinbase_txns[0]}, /*output_values=*/ {9 * COIN});
|
||||
auto entry_replacement = entry.FromTx(replacement_tx);
|
||||
|
||||
// Replacement of size 1
|
||||
{
|
||||
const auto replace_one{pool.CalculateChunksForRBF(/*replacement_fees=*/0, /*replacement_vsize=*/1, {entry_low}, {entry_low})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry_low);
|
||||
changeset->StageAddition(replacement_tx, 0, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_one{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(replace_one.has_value());
|
||||
std::vector<FeeFrac> expected_old_chunks{{low_fee, low_size}};
|
||||
BOOST_CHECK(replace_one->first == expected_old_chunks);
|
||||
std::vector<FeeFrac> expected_new_chunks{{0, 1}};
|
||||
std::vector<FeeFrac> expected_new_chunks{{0, int32_t(entry_replacement.GetTxSize())}};
|
||||
BOOST_CHECK(replace_one->second == expected_new_chunks);
|
||||
}
|
||||
|
||||
// Non-zero replacement fee/size
|
||||
{
|
||||
const auto replace_one_fee{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry_low);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_one_fee{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(replace_one_fee.has_value());
|
||||
std::vector<FeeFrac> expected_old_diagram{{low_fee, low_size}};
|
||||
BOOST_CHECK(replace_one_fee->first == expected_old_diagram);
|
||||
@@ -454,7 +498,11 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto high_size = entry_high->GetTxSize();
|
||||
|
||||
{
|
||||
const auto replace_single_chunk{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low, entry_high})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry_low);
|
||||
changeset->StageRemoval(entry_high);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_single_chunk{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(replace_single_chunk.has_value());
|
||||
std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
|
||||
BOOST_CHECK(replace_single_chunk->first == expected_old_chunks);
|
||||
@@ -464,7 +512,10 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
|
||||
// Conflict with the 2nd tx, resulting in new diagram with three entries
|
||||
{
|
||||
const auto replace_cpfp_child{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high}, {entry_high})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry_high);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_cpfp_child{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(replace_cpfp_child.has_value());
|
||||
std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
|
||||
BOOST_CHECK(replace_cpfp_child->first == expected_old_chunks);
|
||||
@@ -476,12 +527,16 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto normal_tx = make_tx(/*inputs=*/ {high_tx}, /*output_values=*/ {995 * CENT});
|
||||
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();
|
||||
|
||||
{
|
||||
const auto replace_too_large{pool.CalculateChunksForRBF(/*replacement_fees=*/normal_fee, /*replacement_vsize=*/normal_size, {entry_low}, {entry_low, entry_high, entry_normal})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry_low);
|
||||
changeset->StageRemoval(entry_high);
|
||||
changeset->StageRemoval(entry_normal);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_too_large{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(!replace_too_large.has_value());
|
||||
BOOST_CHECK_EQUAL(util::ErrorString(replace_too_large).original, strprintf("%s has 2 descendants, max 1 allowed", low_tx->GetHash().GetHex()));
|
||||
BOOST_CHECK_EQUAL(util::ErrorString(replace_too_large).original, strprintf("%s has 2 ancestors, max 1 allowed", normal_tx->GetHash().GetHex()));
|
||||
}
|
||||
|
||||
// Make a size 2 cluster that is itself two chunks; evict both txns
|
||||
@@ -496,7 +551,11 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto low_size_2 = entry_low_2->GetTxSize();
|
||||
|
||||
{
|
||||
const auto replace_two_chunks_single_cluster{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high_2}, {entry_high_2, entry_low_2})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(entry_high_2);
|
||||
changeset->StageRemoval(entry_low_2);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_two_chunks_single_cluster{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(replace_two_chunks_single_cluster.has_value());
|
||||
std::vector<FeeFrac> expected_old_chunks{{high_fee, high_size_2}, {low_fee, low_size_2}};
|
||||
BOOST_CHECK(replace_two_chunks_single_cluster->first == expected_old_chunks);
|
||||
@@ -518,7 +577,12 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto conflict_3_entry = pool.GetIter(conflict_3->GetHash()).value();
|
||||
|
||||
{
|
||||
const auto replace_multiple_clusters{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(conflict_1_entry);
|
||||
changeset->StageRemoval(conflict_2_entry);
|
||||
changeset->StageRemoval(conflict_3_entry);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_multiple_clusters{changeset->CalculateChunksForRBF()};
|
||||
BOOST_CHECK(replace_multiple_clusters.has_value());
|
||||
BOOST_CHECK(replace_multiple_clusters->first.size() == 3);
|
||||
BOOST_CHECK(replace_multiple_clusters->second.size() == 1);
|
||||
@@ -530,7 +594,13 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto conflict_1_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
|
||||
|
||||
{
|
||||
const auto replace_multiple_clusters_2{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(conflict_1_entry);
|
||||
changeset->StageRemoval(conflict_2_entry);
|
||||
changeset->StageRemoval(conflict_3_entry);
|
||||
changeset->StageRemoval(conflict_1_child_entry);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_multiple_clusters_2{changeset->CalculateChunksForRBF()};
|
||||
|
||||
BOOST_CHECK(replace_multiple_clusters_2.has_value());
|
||||
BOOST_CHECK(replace_multiple_clusters_2->first.size() == 4);
|
||||
@@ -543,10 +613,17 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
|
||||
const auto conflict_1_grand_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
|
||||
|
||||
{
|
||||
const auto replace_cluster_size_3{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry, conflict_1_grand_child_entry})};
|
||||
auto changeset = pool.GetChangeSet();
|
||||
changeset->StageRemoval(conflict_1_entry);
|
||||
changeset->StageRemoval(conflict_2_entry);
|
||||
changeset->StageRemoval(conflict_3_entry);
|
||||
changeset->StageRemoval(conflict_1_child_entry);
|
||||
changeset->StageRemoval(conflict_1_grand_child_entry);
|
||||
changeset->StageAddition(replacement_tx, high_fee, 0, 1, 0, false, 4, LockPoints());
|
||||
const auto replace_cluster_size_3{changeset->CalculateChunksForRBF()};
|
||||
|
||||
BOOST_CHECK(!replace_cluster_size_3.has_value());
|
||||
BOOST_CHECK_EQUAL(util::ErrorString(replace_cluster_size_3).original, strprintf("%s has 2 descendants, max 1 allowed", conflict_1->GetHash().GetHex()));
|
||||
BOOST_CHECK_EQUAL(util::ErrorString(replace_cluster_size_3).original, strprintf("%s has both ancestor and descendant, exceeding cluster limit of 2", conflict_1_child->GetHash().GetHex()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user