Skip to content

Commit d9cc99d

Browse files
committed
[test] MiniMiner::Linearize and manual construction
1 parent dfd6a37 commit d9cc99d

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

src/test/miniminer_tests.cpp

+163
Original file line numberDiff line numberDiff line change
@@ -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

314373
BOOST_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
}
486589
BOOST_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+
542705
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)