@@ -369,44 +369,79 @@ BOOST_FIXTURE_TEST_CASE(improves_feerate, TestChain100Setup)
369369
370370 const auto entry1 = pool.GetIter (tx1->GetHash ()).value ();
371371 const auto tx1_fee = entry1->GetModifiedFee ();
372- const auto tx1_size = entry1->GetTxSize ();
373372 const auto entry2 = pool.GetIter (tx2->GetHash ()).value ();
374373 const auto tx2_fee = entry2->GetModifiedFee ();
375- const auto tx2_size = entry2->GetTxSize ();
376374
377375 // conflicting transactions
378376 const auto tx1_conflict = make_tx (/* inputs=*/ {m_coinbase_txns[0 ], m_coinbase_txns[2 ]}, /* output_values=*/ {10 * COIN});
377+ const auto tx3 = make_tx (/* inputs=*/ {tx1_conflict}, /* output_values=*/ {995 * CENT});
378+ auto entry3 = entry.FromTx (tx3);
379379
380380 // Now test ImprovesFeerateDiagram with various levels of "package rbf" feerates
381381
382382 // It doesn't improve itself
383- const auto res1 = ImprovesFeerateDiagram (pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee, tx1_size + tx2_size);
383+ auto changeset = pool.GetChangeSet ();
384+ changeset->StageRemoval (entry1);
385+ changeset->StageRemoval (entry2);
386+ changeset->StageAddition (tx1_conflict, tx1_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
387+ changeset->StageAddition (tx3, tx2_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
388+ const auto res1 = ImprovesFeerateDiagram (*changeset);
384389 BOOST_CHECK (res1.has_value ());
385390 BOOST_CHECK (res1.value ().first == DiagramCheckError::FAILURE);
386391 BOOST_CHECK (res1.value ().second == " insufficient feerate: does not improve feerate diagram" );
387392
388393 // With one more satoshi it does
389- BOOST_CHECK (ImprovesFeerateDiagram (pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1 , tx1_size + tx2_size) == std::nullopt );
390-
394+ changeset.reset ();
395+ changeset = pool.GetChangeSet ();
396+ changeset->StageRemoval (entry1);
397+ changeset->StageRemoval (entry2);
398+ changeset->StageAddition (tx1_conflict, tx1_fee+1 , 0 , 1 , 0 , false , 4 , LockPoints ());
399+ changeset->StageAddition (tx3, tx2_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
400+ BOOST_CHECK (ImprovesFeerateDiagram (*changeset) == std::nullopt );
401+
402+ changeset.reset ();
391403 // With prioritisation of in-mempool conflicts, it affects the results of the comparison using the same args as just above
392404 pool.PrioritiseTransaction (entry1->GetSharedTx ()->GetHash (), /* nFeeDelta=*/ 1 );
393- const auto res2 = ImprovesFeerateDiagram (pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1 , tx1_size + tx2_size);
405+ changeset = pool.GetChangeSet ();
406+ changeset->StageRemoval (entry1);
407+ changeset->StageRemoval (entry2);
408+ changeset->StageAddition (tx1_conflict, tx1_fee+1 , 0 , 1 , 0 , false , 4 , LockPoints ());
409+ changeset->StageAddition (tx3, tx2_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
410+ const auto res2 = ImprovesFeerateDiagram (*changeset);
394411 BOOST_CHECK (res2.has_value ());
395412 BOOST_CHECK (res2.value ().first == DiagramCheckError::FAILURE);
396413 BOOST_CHECK (res2.value ().second == " insufficient feerate: does not improve feerate diagram" );
414+ changeset.reset ();
415+
397416 pool.PrioritiseTransaction (entry1->GetSharedTx ()->GetHash (), /* nFeeDelta=*/ -1 );
398417
399- // With one less vB it does
400- BOOST_CHECK (ImprovesFeerateDiagram (pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee, tx1_size + tx2_size - 1 ) == std::nullopt );
418+ // With fewer vbytes it does
419+ CMutableTransaction tx4{entry3.GetTx ()};
420+ tx4.vin [0 ].scriptWitness = CScriptWitness (); // Clear out the witness, to reduce size
421+ auto entry4 = entry.FromTx (MakeTransactionRef (tx4));
422+ changeset = pool.GetChangeSet ();
423+ changeset->StageRemoval (entry1);
424+ changeset->StageRemoval (entry2);
425+ changeset->StageAddition (tx1_conflict, tx1_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
426+ changeset->StageAddition (entry4.GetSharedTx (), tx2_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
427+ BOOST_CHECK (ImprovesFeerateDiagram (*changeset) == std::nullopt );
428+ changeset.reset ();
401429
402430 // Adding a grandchild makes the cluster size 3, which is uncalculable
403- const auto tx3 = make_tx (/* inputs=*/ {tx2}, /* output_values=*/ {995 * CENT});
404- AddToMempool (pool, entry.Fee (normal_fee).FromTx (tx3));
405- const auto res3 = ImprovesFeerateDiagram (pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1 , tx1_size + tx2_size);
431+ const auto tx5 = make_tx (/* inputs=*/ {tx2}, /* output_values=*/ {995 * CENT});
432+ AddToMempool (pool, entry.Fee (normal_fee).FromTx (tx5));
433+ const auto entry5 = pool.GetIter (tx5->GetHash ()).value ();
434+
435+ changeset = pool.GetChangeSet ();
436+ changeset->StageRemoval (entry1);
437+ changeset->StageRemoval (entry2);
438+ changeset->StageRemoval (entry5);
439+ changeset->StageAddition (tx1_conflict, tx1_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
440+ changeset->StageAddition (entry4.GetSharedTx (), tx2_fee + entry5->GetModifiedFee () + 1 , 0 , 1 , 0 , false , 4 , LockPoints ());
441+ const auto res3 = ImprovesFeerateDiagram (*changeset);
406442 BOOST_CHECK (res3.has_value ());
407443 BOOST_CHECK (res3.value ().first == DiagramCheckError::UNCALCULABLE);
408- BOOST_CHECK (res3.value ().second == strprintf (" %s has both ancestor and descendant, exceeding cluster limit of 2" , tx2->GetHash ().GetHex ()));
409-
444+ BOOST_CHECK (res3.value ().second == strprintf (" %s has 2 ancestors, max 1 allowed" , tx5->GetHash ().GetHex ()));
410445}
411446
412447BOOST_FIXTURE_TEST_CASE (calc_feerate_diagram_rbf, TestChain100Setup)
@@ -427,19 +462,28 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
427462 const auto entry_low = pool.GetIter (low_tx->GetHash ()).value ();
428463 const auto low_size = entry_low->GetTxSize ();
429464
465+ const auto replacement_tx = make_tx (/* inputs=*/ {m_coinbase_txns[0 ]}, /* output_values=*/ {9 * COIN});
466+ auto entry_replacement = entry.FromTx (replacement_tx);
467+
430468 // Replacement of size 1
431469 {
432- const auto replace_one{pool.CalculateChunksForRBF (/* replacement_fees=*/ 0 , /* replacement_vsize=*/ 1 , {entry_low}, {entry_low})};
470+ auto changeset = pool.GetChangeSet ();
471+ changeset->StageRemoval (entry_low);
472+ changeset->StageAddition (replacement_tx, 0 , 0 , 1 , 0 , false , 4 , LockPoints ());
473+ const auto replace_one{changeset->CalculateChunksForRBF ()};
433474 BOOST_CHECK (replace_one.has_value ());
434475 std::vector<FeeFrac> expected_old_chunks{{low_fee, low_size}};
435476 BOOST_CHECK (replace_one->first == expected_old_chunks);
436- std::vector<FeeFrac> expected_new_chunks{{0 , 1 }};
477+ std::vector<FeeFrac> expected_new_chunks{{0 , int32_t (entry_replacement. GetTxSize ()) }};
437478 BOOST_CHECK (replace_one->second == expected_new_chunks);
438479 }
439480
440481 // Non-zero replacement fee/size
441482 {
442- const auto replace_one_fee{pool.CalculateChunksForRBF (/* replacement_fees=*/ high_fee, /* replacement_vsize=*/ low_size, {entry_low}, {entry_low})};
483+ auto changeset = pool.GetChangeSet ();
484+ changeset->StageRemoval (entry_low);
485+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
486+ const auto replace_one_fee{changeset->CalculateChunksForRBF ()};
443487 BOOST_CHECK (replace_one_fee.has_value ());
444488 std::vector<FeeFrac> expected_old_diagram{{low_fee, low_size}};
445489 BOOST_CHECK (replace_one_fee->first == expected_old_diagram);
@@ -454,7 +498,11 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
454498 const auto high_size = entry_high->GetTxSize ();
455499
456500 {
457- const auto replace_single_chunk{pool.CalculateChunksForRBF (/* replacement_fees=*/ high_fee, /* replacement_vsize=*/ low_size, {entry_low}, {entry_low, entry_high})};
501+ auto changeset = pool.GetChangeSet ();
502+ changeset->StageRemoval (entry_low);
503+ changeset->StageRemoval (entry_high);
504+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
505+ const auto replace_single_chunk{changeset->CalculateChunksForRBF ()};
458506 BOOST_CHECK (replace_single_chunk.has_value ());
459507 std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
460508 BOOST_CHECK (replace_single_chunk->first == expected_old_chunks);
@@ -464,7 +512,10 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
464512
465513 // Conflict with the 2nd tx, resulting in new diagram with three entries
466514 {
467- const auto replace_cpfp_child{pool.CalculateChunksForRBF (/* replacement_fees=*/ high_fee, /* replacement_vsize=*/ low_size, {entry_high}, {entry_high})};
515+ auto changeset = pool.GetChangeSet ();
516+ changeset->StageRemoval (entry_high);
517+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
518+ const auto replace_cpfp_child{changeset->CalculateChunksForRBF ()};
468519 BOOST_CHECK (replace_cpfp_child.has_value ());
469520 std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
470521 BOOST_CHECK (replace_cpfp_child->first == expected_old_chunks);
@@ -476,12 +527,16 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
476527 const auto normal_tx = make_tx (/* inputs=*/ {high_tx}, /* output_values=*/ {995 * CENT});
477528 AddToMempool (pool, entry.Fee (normal_fee).FromTx (normal_tx));
478529 const auto entry_normal = pool.GetIter (normal_tx->GetHash ()).value ();
479- const auto normal_size = entry_normal->GetTxSize ();
480530
481531 {
482- const auto replace_too_large{pool.CalculateChunksForRBF (/* replacement_fees=*/ normal_fee, /* replacement_vsize=*/ normal_size, {entry_low}, {entry_low, entry_high, entry_normal})};
532+ auto changeset = pool.GetChangeSet ();
533+ changeset->StageRemoval (entry_low);
534+ changeset->StageRemoval (entry_high);
535+ changeset->StageRemoval (entry_normal);
536+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
537+ const auto replace_too_large{changeset->CalculateChunksForRBF ()};
483538 BOOST_CHECK (!replace_too_large.has_value ());
484- BOOST_CHECK_EQUAL (util::ErrorString (replace_too_large).original , strprintf (" %s has 2 descendants , max 1 allowed" , low_tx ->GetHash ().GetHex ()));
539+ BOOST_CHECK_EQUAL (util::ErrorString (replace_too_large).original , strprintf (" %s has 2 ancestors , max 1 allowed" , normal_tx ->GetHash ().GetHex ()));
485540 }
486541
487542 // 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)
496551 const auto low_size_2 = entry_low_2->GetTxSize ();
497552
498553 {
499- 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})};
554+ auto changeset = pool.GetChangeSet ();
555+ changeset->StageRemoval (entry_high_2);
556+ changeset->StageRemoval (entry_low_2);
557+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
558+ const auto replace_two_chunks_single_cluster{changeset->CalculateChunksForRBF ()};
500559 BOOST_CHECK (replace_two_chunks_single_cluster.has_value ());
501560 std::vector<FeeFrac> expected_old_chunks{{high_fee, high_size_2}, {low_fee, low_size_2}};
502561 BOOST_CHECK (replace_two_chunks_single_cluster->first == expected_old_chunks);
@@ -518,7 +577,12 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
518577 const auto conflict_3_entry = pool.GetIter (conflict_3->GetHash ()).value ();
519578
520579 {
521- 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})};
580+ auto changeset = pool.GetChangeSet ();
581+ changeset->StageRemoval (conflict_1_entry);
582+ changeset->StageRemoval (conflict_2_entry);
583+ changeset->StageRemoval (conflict_3_entry);
584+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
585+ const auto replace_multiple_clusters{changeset->CalculateChunksForRBF ()};
522586 BOOST_CHECK (replace_multiple_clusters.has_value ());
523587 BOOST_CHECK (replace_multiple_clusters->first .size () == 3 );
524588 BOOST_CHECK (replace_multiple_clusters->second .size () == 1 );
@@ -530,7 +594,13 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
530594 const auto conflict_1_child_entry = pool.GetIter (conflict_1_child->GetHash ()).value ();
531595
532596 {
533- 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})};
597+ auto changeset = pool.GetChangeSet ();
598+ changeset->StageRemoval (conflict_1_entry);
599+ changeset->StageRemoval (conflict_2_entry);
600+ changeset->StageRemoval (conflict_3_entry);
601+ changeset->StageRemoval (conflict_1_child_entry);
602+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
603+ const auto replace_multiple_clusters_2{changeset->CalculateChunksForRBF ()};
534604
535605 BOOST_CHECK (replace_multiple_clusters_2.has_value ());
536606 BOOST_CHECK (replace_multiple_clusters_2->first .size () == 4 );
@@ -543,10 +613,17 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
543613 const auto conflict_1_grand_child_entry = pool.GetIter (conflict_1_child->GetHash ()).value ();
544614
545615 {
546- 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})};
616+ auto changeset = pool.GetChangeSet ();
617+ changeset->StageRemoval (conflict_1_entry);
618+ changeset->StageRemoval (conflict_2_entry);
619+ changeset->StageRemoval (conflict_3_entry);
620+ changeset->StageRemoval (conflict_1_child_entry);
621+ changeset->StageRemoval (conflict_1_grand_child_entry);
622+ changeset->StageAddition (replacement_tx, high_fee, 0 , 1 , 0 , false , 4 , LockPoints ());
623+ const auto replace_cluster_size_3{changeset->CalculateChunksForRBF ()};
547624
548625 BOOST_CHECK (!replace_cluster_size_3.has_value ());
549- BOOST_CHECK_EQUAL (util::ErrorString (replace_cluster_size_3).original , strprintf (" %s has 2 descendants, max 1 allowed " , conflict_1 ->GetHash ().GetHex ()));
626+ 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 ()));
550627 }
551628}
552629
0 commit comments