Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 33: [PATCH 33/68] test(mining): cover priority ancestor accounting boundaries

src/test/miner_tests.cpp

Before: src/test/miner_tests.cpp · After: src/test/miner_tests.cpp

File metadata
index 3869aa2debba0b5d8aa563127fb0d31020fb38dd..383c87bc0fbd91e1baa798c7ba44c652469b2d44 100644

Download original patch · Release notes

Diff rows 1–115 of 115. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.

src/test/miner_tests.cpp CHANGED
@@ -10,6 +10,7 @@
10
10
  #include <consensus/tx_verify.h>
11
11
  #include <interfaces/mining.h>
12
12
  #include <node/miner.h>
13
+ #include <policy/coin_age_priority.h>
13
14
  #include <policy/policy.h>
14
15
  #include <test/util/random.h>
15
16
  #include <test/util/transaction_utils.h>
@@ -28,6 +29,7 @@
28
29
  #include <test/util/setup_common.h>
29
30
 
30
31
  #include <memory>
32
+ #include <string>
31
33
  #include <vector>
32
34
 
33
35
  #include <boost/test/unit_test.hpp>
@@ -696,7 +698,7 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
696
698
  // A transaction added by the coin-age priority phase must not keep subsidising
697
699
  // its descendants in the ancestor-feerate phase.
698
700
  //
699
- // Test entries have no coin-age priority, so the priority phase ranks them by
701
+ // The first scenario's entries have no coin-age priority, so it ranks them by
700
702
  // fee score, adds the fee-paying parent (the child still depends on it), and
701
703
  // then stops at MINIMUM_TX_PRIORITY. The child pays no fee and must not be
702
704
  // selected on the strength of its already-included parent's fee.
@@ -727,10 +729,92 @@ void MinerTestingSetup::TestPriorityPhaseStaleAncestors(const CScript& scriptPub
727
729
  const auto block_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
728
730
  BOOST_REQUIRE(block_template);
729
731
  const CBlock& block{block_template->block};
730
- BOOST_CHECK_EQUAL(block.vtx.size(), 2U);
732
+ BOOST_REQUIRE_EQUAL(block.vtx.size(), 2U);
731
733
  BOOST_CHECK(block.vtx[1]->GetHash() == parent_txid);
732
734
  for (const auto& btx : block.vtx) BOOST_CHECK(btx->GetHash() != child_txid);
733
735
 
736
+ // Template policy must not make the otherwise-valid zero-fee child invalid.
737
+ CBlock block_with_child{block};
738
+ block_with_child.vtx.push_back(MakeTransactionRef(tx));
739
+ node::RegenerateCommitments(block_with_child, *m_node.chainman);
740
+ const BlockValidationState state{TestBlockValidity(m_node.chainman->ActiveChainstate(), block_with_child,
741
+ /*check_pow=*/false, /*check_merkle_root=*/true)};
742
+ BOOST_CHECK_MESSAGE(state.IsValid(), state.ToString());
743
+
744
+ // With the priority phase disabled, ordinary package selection must still
745
+ // include the parent and stop subsidising its zero-fee child afterwards.
746
+ gArgs.ForceSetArg("-blockprioritysize", "0");
747
+ const auto no_priority_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
748
+ BOOST_REQUIRE(no_priority_template);
749
+ BOOST_REQUIRE_EQUAL(no_priority_template->block.vtx.size(), 2U);
750
+ BOOST_CHECK(no_priority_template->block.vtx[1]->GetHash() == parent_txid);
751
+
752
+ // A zero mining fee floor may still select the child.
753
+ options.blockMinFeeRate = CFeeRate{0};
754
+ const auto zero_floor_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
755
+ BOOST_REQUIRE(zero_floor_template);
756
+ BOOST_REQUIRE_EQUAL(zero_floor_template->block.vtx.size(), 3U);
757
+ BOOST_CHECK(zero_floor_template->block.vtx[2]->GetHash() == child_txid);
758
+
759
+ tx_mempool.removeRecursive(*block.vtx[1], MemPoolRemovalReason::REPLACED);
760
+
761
+ // Confirmed coin age selects zero-fee parents first. The child's own fee
762
+ // meets the mining floor, but its stale package including those parents
763
+ // does not. Exercise both one ancestor and a shared descendant of two
764
+ // ancestors, which must each be subtracted exactly once.
765
+ for (const size_t parent_count : {1U, 2U}) {
766
+ BOOST_TEST_CONTEXT("priority-selected parents: " << parent_count) {
767
+ options.blockMinFeeRate = CFeeRate{1000};
768
+ const unsigned int height = m_node.chainman->ActiveChain().Height();
769
+ std::vector<CTransactionRef> parents;
770
+ size_t priority_tx_size{0};
771
+ for (size_t i{0}; i < parent_count; ++i) {
772
+ CMutableTransaction parent;
773
+ parent.vin.resize(1);
774
+ parent.vin[0].prevout = COutPoint{txFirst[i]->GetHash(), 0};
775
+ parent.vin[0].scriptSig = CScript() << OP_1;
776
+ parent.vout.emplace_back(50 * COIN, CScript{});
777
+ parents.push_back(MakeTransactionRef(parent));
778
+ const auto coin_age = GetCoinAge(*parents.back(), m_node.chainman->ActiveChainstate().CoinsTip(), height + 1);
779
+ // AddToMempool does not preserve the coin-age cache.
780
+ auto changeset = tx_mempool.GetChangeSet();
781
+ changeset->StageAddition(parents.back(), /*fee=*/0, /*time=*/0, height, /*entry_sequence=*/0,
782
+ coin_age, /*spends_coinbase=*/true, /*extra_weight=*/0, /*sigops_cost=*/4, LockPoints{});
783
+ changeset->Apply();
784
+ const auto parent_iter = tx_mempool.mapTx.find(parents.back()->GetHash());
785
+ BOOST_REQUIRE(parent_iter != tx_mempool.mapTx.end());
786
+ BOOST_REQUIRE(parent_iter->GetPriority(height + 1) > MINIMUM_TX_PRIORITY);
787
+ priority_tx_size += GetSerializeSize(TX_WITH_WITNESS(*parents.back()));
788
+ }
789
+
790
+ CMutableTransaction child;
791
+ for (const auto& parent : parents) {
792
+ child.vin.emplace_back(COutPoint{parent->GetHash(), 0}, CScript() << OP_1);
793
+ }
794
+ child.vout.emplace_back(parent_count * 50 * COIN, CScript{});
795
+ const CAmount child_fee = options.blockMinFeeRate.GetFee(GetVirtualTransactionSize(CTransaction{child}));
796
+ child.vout[0].nValue -= child_fee;
797
+ const auto child_ref = MakeTransactionRef(child);
798
+ AddToMempool(tx_mempool, entry.Fee(child_fee).SpendsCoinbase(false).FromTx(child_ref));
799
+
800
+ // Without priority selection the entire package is below the floor.
801
+ gArgs.ForceSetArg("-blockprioritysize", "0");
802
+ const auto below_floor_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
803
+ BOOST_REQUIRE(below_floor_template);
804
+ BOOST_CHECK_EQUAL(below_floor_template->block.vtx.size(), 1U);
805
+
806
+ // The assembler reserves 1000 bytes before adding transactions;
807
+ // end the priority phase immediately after including the parents.
808
+ gArgs.ForceSetArg("-blockprioritysize", std::to_string(1000 + priority_tx_size));
809
+ const auto priority_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
810
+ BOOST_REQUIRE(priority_template);
811
+ BOOST_REQUIRE_EQUAL(priority_template->block.vtx.size(), parent_count + 2);
812
+ BOOST_CHECK(priority_template->block.vtx.back()->GetHash() == child_ref->GetHash());
813
+ BOOST_CHECK_EQUAL(priority_template->vTxFees.back(), child_fee);
814
+ for (const auto& parent : parents) tx_mempool.removeRecursive(*parent, MemPoolRemovalReason::REPLACED);
815
+ }
816
+ }
817
+
734
818
  gArgs.ForceSetArg("-blockprioritysize", "0");
735
819
  }
736
820