@@ -133,6 +133,7 @@ BOOST_FIXTURE_TEST_CASE(miniminer_1p1c, TestChain100Setup)
133133 // Make tx5's modified fee much higher than its base fee. This should cause it to pass
134134 // the fee-related checks despite being low-feerate.
135135 pool.PrioritiseTransaction (tx5->GetHash (), tx5_delta);
136+ const CAmount tx5_mod_fee{low_fee + tx5_delta};
136137
137138 // Create a high-feerate parent tx6, low-feerate child tx7
138139 const auto tx6 = make_tx ({COutPoint{m_coinbase_txns[3 ]->GetHash (), 0 }}, /* num_outputs=*/ 2 );
@@ -309,6 +310,64 @@ BOOST_FIXTURE_TEST_CASE(miniminer_1p1c, TestChain100Setup)
309310 }
310311 }
311312 }
313+
314+ // Check m_inclusion_order for equivalent mempool- and manually-constructed MiniMiners.
315+ // (We cannot check bump fees in manually-constructed MiniMiners because it doesn't know what
316+ // outpoints are requested).
317+ std::vector<node::MiniMinerMempoolEntry> miniminer_info;
318+ {
319+ const int32_t tx0_vsize{tx_dims.at (tx0->GetHash ()).vsize };
320+ const int32_t tx1_vsize{tx_dims.at (tx1->GetHash ()).vsize };
321+ const int32_t tx2_vsize{tx_dims.at (tx2->GetHash ()).vsize };
322+ const int32_t tx3_vsize{tx_dims.at (tx3->GetHash ()).vsize };
323+ const int32_t tx4_vsize{tx_dims.at (tx4->GetHash ()).vsize };
324+ const int32_t tx5_vsize{tx_dims.at (tx5->GetHash ()).vsize };
325+ const int32_t tx6_vsize{tx_dims.at (tx6->GetHash ()).vsize };
326+ const int32_t tx7_vsize{tx_dims.at (tx7->GetHash ()).vsize };
327+
328+ miniminer_info.emplace_back (/* fee_self=*/ med_fee,/* fee_ancestor=*/ med_fee,/* vsize_self=*/ tx0_vsize,/* vsize_ancestor=*/ tx0_vsize, tx0);
329+ miniminer_info.emplace_back ( med_fee, 2 *med_fee, tx1_vsize, tx0_vsize + tx1_vsize, tx1);
330+ miniminer_info.emplace_back ( low_fee, low_fee, tx2_vsize, tx2_vsize, tx2);
331+ miniminer_info.emplace_back ( high_fee, low_fee + high_fee, tx3_vsize, tx2_vsize + tx3_vsize, tx3);
332+ miniminer_info.emplace_back ( low_fee, low_fee, tx4_vsize, tx4_vsize, tx4);
333+ miniminer_info.emplace_back ( tx5_mod_fee, low_fee + tx5_mod_fee, tx5_vsize, tx4_vsize + tx5_vsize, tx5);
334+ miniminer_info.emplace_back ( high_fee, high_fee, tx6_vsize, tx6_vsize, tx6);
335+ miniminer_info.emplace_back ( low_fee, high_fee + low_fee, tx7_vsize, tx6_vsize + tx7_vsize, tx7);
336+ }
337+ std::map<Txid, std::set<Txid>> descendant_caches;
338+ descendant_caches.emplace (tx0->GetHash (), std::set<Txid>{tx0->GetHash (), tx1->GetHash ()});
339+ descendant_caches.emplace (tx1->GetHash (), std::set<Txid>{tx1->GetHash ()});
340+ descendant_caches.emplace (tx2->GetHash (), std::set<Txid>{tx2->GetHash (), tx3->GetHash ()});
341+ descendant_caches.emplace (tx3->GetHash (), std::set<Txid>{tx3->GetHash ()});
342+ descendant_caches.emplace (tx4->GetHash (), std::set<Txid>{tx4->GetHash (), tx5->GetHash ()});
343+ descendant_caches.emplace (tx5->GetHash (), std::set<Txid>{tx5->GetHash ()});
344+ descendant_caches.emplace (tx6->GetHash (), std::set<Txid>{tx6->GetHash (), tx7->GetHash ()});
345+ descendant_caches.emplace (tx7->GetHash (), std::set<Txid>{tx7->GetHash ()});
346+
347+ node::MiniMiner miniminer_manual (miniminer_info, descendant_caches);
348+ // Use unspent outpoints to avoid entries being omitted.
349+ node::MiniMiner miniminer_pool (pool, all_unspent_outpoints);
350+ BOOST_CHECK (miniminer_manual.IsReadyToCalculate ());
351+ BOOST_CHECK (miniminer_pool.IsReadyToCalculate ());
352+ for (const auto & sequences : {miniminer_manual.Linearize (), miniminer_pool.Linearize ()}) {
353+ // tx6 is selected first: high feerate with no parents to bump
354+ BOOST_CHECK_EQUAL (Find (sequences, tx6->GetHash ()), 0 );
355+
356+ // tx2 + tx3 CPFP are selected next
357+ BOOST_CHECK_EQUAL (Find (sequences, tx2->GetHash ()), 1 );
358+ BOOST_CHECK_EQUAL (Find (sequences, tx3->GetHash ()), 1 );
359+
360+ // tx4 + prioritised tx5 CPFP
361+ BOOST_CHECK_EQUAL (Find (sequences, tx4->GetHash ()), 2 );
362+ BOOST_CHECK_EQUAL (Find (sequences, tx5->GetHash ()), 2 );
363+
364+ BOOST_CHECK_EQUAL (Find (sequences, tx0->GetHash ()), 3 );
365+ BOOST_CHECK_EQUAL (Find (sequences, tx1->GetHash ()), 3 );
366+
367+
368+ // tx7 is selected last: low feerate with no children
369+ BOOST_CHECK_EQUAL (Find (sequences, tx7->GetHash ()), 4 );
370+ }
312371}
313372
314373BOOST_FIXTURE_TEST_CASE (miniminer_overlap, TestChain100Setup)
@@ -482,6 +541,50 @@ BOOST_FIXTURE_TEST_CASE(miniminer_overlap, TestChain100Setup)
482541 BOOST_CHECK (tx7_bumpfee != bump_fees.end ());
483542 BOOST_CHECK_EQUAL (tx7_bumpfee->second , 0 );
484543 }
544+ // Check linearization order
545+ std::vector<node::MiniMinerMempoolEntry> miniminer_info;
546+ miniminer_info.emplace_back (/* fee_self=*/ low_fee, /* fee_ancestor=*/ low_fee,/* vsize_self=*/ tx_vsizes[0 ], /* vsize_ancestor=*/ tx_vsizes[0 ], tx0);
547+ miniminer_info.emplace_back ( med_fee, med_fee, tx_vsizes[1 ], tx_vsizes[1 ], tx1);
548+ miniminer_info.emplace_back ( high_fee, high_fee, tx_vsizes[2 ], tx_vsizes[2 ], tx2);
549+ miniminer_info.emplace_back ( high_fee, low_fee+med_fee+2 *high_fee, tx_vsizes[3 ], tx_vsizes[0 ]+tx_vsizes[1 ]+tx_vsizes[2 ]+tx_vsizes[3 ], tx3);
550+
551+ miniminer_info.emplace_back ( high_fee, high_fee, tx_vsizes[4 ], tx_vsizes[4 ], tx4);
552+ miniminer_info.emplace_back ( low_fee, low_fee + high_fee, tx_vsizes[5 ], tx_vsizes[4 ]+tx_vsizes[5 ], tx5);
553+ miniminer_info.emplace_back ( med_fee, high_fee+low_fee+med_fee, tx_vsizes[6 ], tx_vsizes[4 ]+tx_vsizes[5 ]+tx_vsizes[6 ], tx6);
554+ miniminer_info.emplace_back ( high_fee, high_fee+low_fee+high_fee, tx_vsizes[7 ], tx_vsizes[4 ]+tx_vsizes[5 ]+tx_vsizes[7 ], tx7);
555+
556+ std::map<Txid, std::set<Txid>> descendant_caches;
557+ descendant_caches.emplace (tx0->GetHash (), std::set<Txid>{tx0->GetHash (), tx3->GetHash ()});
558+ descendant_caches.emplace (tx1->GetHash (), std::set<Txid>{tx1->GetHash (), tx3->GetHash ()});
559+ descendant_caches.emplace (tx2->GetHash (), std::set<Txid>{tx2->GetHash (), tx3->GetHash ()});
560+ descendant_caches.emplace (tx3->GetHash (), std::set<Txid>{tx3->GetHash ()});
561+ descendant_caches.emplace (tx4->GetHash (), std::set<Txid>{tx4->GetHash (), tx5->GetHash (), tx6->GetHash (), tx7->GetHash ()});
562+ descendant_caches.emplace (tx5->GetHash (), std::set<Txid>{tx5->GetHash (), tx6->GetHash (), tx7->GetHash ()});
563+ descendant_caches.emplace (tx6->GetHash (), std::set<Txid>{tx6->GetHash ()});
564+ descendant_caches.emplace (tx7->GetHash (), std::set<Txid>{tx7->GetHash ()});
565+
566+ node::MiniMiner miniminer_manual (miniminer_info, descendant_caches);
567+ // Use unspent outpoints to avoid entries being omitted.
568+ node::MiniMiner miniminer_pool (pool, all_unspent_outpoints);
569+ BOOST_CHECK (miniminer_manual.IsReadyToCalculate ());
570+ BOOST_CHECK (miniminer_pool.IsReadyToCalculate ());
571+ for (const auto & sequences : {miniminer_manual.Linearize (), miniminer_pool.Linearize ()}) {
572+ // tx2 and tx4 selected first: high feerate with nothing to bump
573+ BOOST_CHECK_EQUAL (Find (sequences, tx4->GetHash ()), 0 );
574+ BOOST_CHECK_EQUAL (Find (sequences, tx2->GetHash ()), 1 );
575+
576+ // tx5 + tx7 CPFP
577+ BOOST_CHECK_EQUAL (Find (sequences, tx5->GetHash ()), 2 );
578+ BOOST_CHECK_EQUAL (Find (sequences, tx7->GetHash ()), 2 );
579+
580+ // tx0 and tx1 CPFP'd by tx3
581+ BOOST_CHECK_EQUAL (Find (sequences, tx0->GetHash ()), 3 );
582+ BOOST_CHECK_EQUAL (Find (sequences, tx1->GetHash ()), 3 );
583+ BOOST_CHECK_EQUAL (Find (sequences, tx3->GetHash ()), 3 );
584+
585+ // tx6 at medium feerate
586+ BOOST_CHECK_EQUAL (Find (sequences, tx6->GetHash ()), 4 );
587+ }
485588}
486589BOOST_FIXTURE_TEST_CASE (calculate_cluster, TestChain100Setup)
487590{
@@ -539,4 +642,64 @@ BOOST_FIXTURE_TEST_CASE(calculate_cluster, TestChain100Setup)
539642 }
540643}
541644
645+ BOOST_FIXTURE_TEST_CASE (manual_ctor, TestChain100Setup)
646+ {
647+ CTxMemPool& pool = *Assert (m_node.mempool );
648+ LOCK2 (cs_main, pool.cs );
649+ {
650+ // 3 pairs of fee-bumping grandparent + parent, plus 1 low-feerate child.
651+ // 0 fee + high fee
652+ auto grandparent_zero_fee = make_tx ({{m_coinbase_txns.at (0 )->GetHash (), 0 }}, 1 );
653+ auto parent_high_feerate = make_tx ({{grandparent_zero_fee->GetHash (), 0 }}, 1 );
654+ // double low fee + med fee
655+ auto grandparent_double_low_feerate = make_tx ({{m_coinbase_txns.at (2 )->GetHash (), 0 }}, 1 );
656+ auto parent_med_feerate = make_tx ({{grandparent_double_low_feerate->GetHash (), 0 }}, 1 );
657+ // low fee + double low fee
658+ auto grandparent_low_feerate = make_tx ({{m_coinbase_txns.at (1 )->GetHash (), 0 }}, 1 );
659+ auto parent_double_low_feerate = make_tx ({{grandparent_low_feerate->GetHash (), 0 }}, 1 );
660+ // child is below the cpfp package feerates because it is larger than everything else
661+ auto child = make_tx ({{parent_high_feerate->GetHash (), 0 }, {parent_double_low_feerate->GetHash (), 0 }, {parent_med_feerate->GetHash (), 0 }}, 1 );
662+
663+ // We artificially record each transaction (except the child) with a uniform vsize of 100vB.
664+ const int64_t tx_vsize{100 };
665+ const int64_t child_vsize{1000 };
666+
667+ std::vector<node::MiniMinerMempoolEntry> miniminer_info;
668+ miniminer_info.emplace_back (/* fee_self=*/ 0 , /* fee_ancestor=*/ 0 ,/* vsize_self=*/ tx_vsize,/* vsize_ancestor=*/ tx_vsize, grandparent_zero_fee);
669+ miniminer_info.emplace_back ( high_fee, high_fee, tx_vsize, 2 *tx_vsize, parent_high_feerate);
670+ miniminer_info.emplace_back ( 2 *low_fee, 2 *low_fee, tx_vsize, tx_vsize, grandparent_double_low_feerate);
671+ miniminer_info.emplace_back ( med_fee, 2 *low_fee+med_fee, tx_vsize, 2 *tx_vsize, parent_med_feerate);
672+ miniminer_info.emplace_back ( low_fee, low_fee, tx_vsize, tx_vsize, grandparent_low_feerate);
673+ miniminer_info.emplace_back ( 2 *low_fee, 3 *low_fee, tx_vsize, 2 *tx_vsize, parent_double_low_feerate);
674+ miniminer_info.emplace_back ( low_fee, high_fee+med_fee+6 *low_fee, child_vsize, 6 *tx_vsize+child_vsize, child);
675+ std::map<Txid, std::set<Txid>> descendant_caches;
676+ descendant_caches.emplace (grandparent_zero_fee->GetHash (), std::set<Txid>{grandparent_zero_fee->GetHash (), parent_high_feerate->GetHash (), child->GetHash ()});
677+ descendant_caches.emplace (grandparent_low_feerate->GetHash (), std::set<Txid>{grandparent_low_feerate->GetHash (), parent_double_low_feerate->GetHash (), child->GetHash ()});
678+ descendant_caches.emplace (grandparent_double_low_feerate->GetHash (), std::set<Txid>{grandparent_double_low_feerate->GetHash (), parent_med_feerate->GetHash (), child->GetHash ()});
679+ descendant_caches.emplace (parent_high_feerate->GetHash (), std::set<Txid>{parent_high_feerate->GetHash (), child->GetHash ()});
680+ descendant_caches.emplace (parent_med_feerate->GetHash (), std::set<Txid>{parent_med_feerate->GetHash (), child->GetHash ()});
681+ descendant_caches.emplace (parent_double_low_feerate->GetHash (), std::set<Txid>{parent_double_low_feerate->GetHash (), child->GetHash ()});
682+ descendant_caches.emplace (child->GetHash (), std::set<Txid>{child->GetHash ()});
683+
684+ node::MiniMiner miniminer_manual (miniminer_info, descendant_caches);
685+ BOOST_CHECK (miniminer_manual.IsReadyToCalculate ());
686+ const auto sequences{miniminer_manual.Linearize ()};
687+
688+ // CPFP zero + high
689+ BOOST_CHECK_EQUAL (sequences.at (grandparent_zero_fee->GetHash ()), 0 );
690+ BOOST_CHECK_EQUAL (sequences.at (parent_high_feerate->GetHash ()), 0 );
691+
692+ // CPFP double low + med
693+ BOOST_CHECK_EQUAL (sequences.at (grandparent_double_low_feerate->GetHash ()), 1 );
694+ BOOST_CHECK_EQUAL (sequences.at (parent_med_feerate->GetHash ()), 1 );
695+
696+ // CPFP low + med
697+ BOOST_CHECK_EQUAL (sequences.at (grandparent_low_feerate->GetHash ()), 2 );
698+ BOOST_CHECK_EQUAL (sequences.at (parent_double_low_feerate->GetHash ()), 2 );
699+
700+ // Child at the end
701+ BOOST_CHECK_EQUAL (sequences.at (child->GetHash ()), 3 );
702+ }
703+ }
704+
542705BOOST_AUTO_TEST_SUITE_END ()
0 commit comments