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
|
|