v31.1-roots.1 · Commit 4: [PATCH 04/17] feat(mining): preserve Roots priority and template policies
src/test/miner_tests.cpp
Before: src/test/miner_tests.cpp · After: src/test/miner_tests.cpp
File metadata
index 6b4e85a59e45ad84c4a8c7d1e6df912580638723..032ccc35cec02d754efb4386c22dabbd763b628c 100644
Download original patch · Release notes
Diff rows 1–336 of 336. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/test/miner_tests.cpp
CHANGED
|
@@ -42,6 +42,7 @@ namespace miner_tests {
|
|
| 42 |
struct MinerTestingSetup : public TestingSetup {
|
| 43 |
void TestPackageSelection(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
| 44 |
void TestBasicMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
|
|
| 45 |
void TestPrioritisedMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
| 46 |
bool TestSequenceLocks(const CTransaction& tx, CTxMemPool& tx_mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
| 47 |
{
|
|
@@ -116,6 +117,8 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|
| 116 |
CTxMemPool& tx_mempool{MakeMempool()};
|
| 117 |
auto mining{MakeMining()};
|
| 118 |
BlockAssembler::Options options;
|
|
|
|
|
|
|
| 119 |
options.coinbase_output_script = scriptPubKey;
|
| 120 |
options.include_dummy_extranonce = true;
|
| 121 |
|
|
@@ -250,7 +253,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|
| 250 |
// This tx can't be mined by itself
|
| 251 |
tx.vin[0].prevout.hash = hashFreeTx2;
|
| 252 |
tx.vout.resize(1);
|
| 253 |
-
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
|
| 254 |
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
|
| 255 |
Txid hashLowFeeTx2 = tx.GetHash();
|
| 256 |
TryAddToMempool(tx_mempool, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
|
|
@@ -335,6 +338,8 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
|
|
| 335 |
BOOST_REQUIRE(mining);
|
| 336 |
|
| 337 |
BlockAssembler::Options options;
|
|
|
|
|
|
|
| 338 |
options.coinbase_output_script = scriptPubKey;
|
| 339 |
options.include_dummy_extranonce = true;
|
| 340 |
|
|
@@ -662,6 +667,8 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
|
|
| 662 |
BOOST_REQUIRE(mining);
|
| 663 |
|
| 664 |
BlockAssembler::Options options;
|
|
|
|
|
|
|
| 665 |
options.coinbase_output_script = scriptPubKey;
|
| 666 |
options.include_dummy_extranonce = true;
|
| 667 |
|
|
@@ -743,15 +750,290 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
|
|
| 743 |
}
|
| 744 |
}
|
| 745 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
|
| 747 |
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
| 748 |
{
|
|
|
|
|
|
|
| 749 |
auto mining{MakeMining()};
|
| 750 |
BOOST_REQUIRE(mining);
|
| 751 |
|
| 752 |
// Note that by default, these tests run with size accounting enabled.
|
| 753 |
CScript scriptPubKey = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex << OP_CHECKSIG;
|
| 754 |
BlockAssembler::Options options;
|
|
|
|
|
|
|
| 755 |
options.coinbase_output_script = scriptPubKey;
|
| 756 |
options.include_dummy_extranonce = true;
|
| 757 |
|
|
@@ -867,6 +1149,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|
| 867 |
SetMockTime(0);
|
| 868 |
|
| 869 |
TestPrioritisedMining(scriptPubKey, txFirst);
|
|
|
|
| 870 |
}
|
| 871 |
|
| 872 |
BOOST_AUTO_TEST_SUITE_END()
|
|
|
|
| 42 |
struct MinerTestingSetup : public TestingSetup {
|
| 43 |
void TestPackageSelection(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
| 44 |
void TestBasicMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
| 45 |
+
void TestPriorityPhaseStaleAncestors(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
| 46 |
void TestPrioritisedMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
| 47 |
bool TestSequenceLocks(const CTransaction& tx, CTxMemPool& tx_mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
| 48 |
{
|
|
|
|
| 117 |
CTxMemPool& tx_mempool{MakeMempool()};
|
| 118 |
auto mining{MakeMining()};
|
| 119 |
BlockAssembler::Options options;
|
| 120 |
+
options.nBlockPrioritySize = 0;
|
| 121 |
+
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
|
| 122 |
options.coinbase_output_script = scriptPubKey;
|
| 123 |
options.include_dummy_extranonce = true;
|
| 124 |
|
|
|
|
| 253 |
// This tx can't be mined by itself
|
| 254 |
tx.vin[0].prevout.hash = hashFreeTx2;
|
| 255 |
tx.vout.resize(1);
|
| 256 |
+
feeToUse = blockMinFeeRate.GetFee(GetVirtualTransactionSize(CTransaction{tx}));
|
| 257 |
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
|
| 258 |
Txid hashLowFeeTx2 = tx.GetHash();
|
| 259 |
TryAddToMempool(tx_mempool, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
|
|
|
|
| 338 |
BOOST_REQUIRE(mining);
|
| 339 |
|
| 340 |
BlockAssembler::Options options;
|
| 341 |
+
options.nBlockPrioritySize = 0;
|
| 342 |
+
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
|
| 343 |
options.coinbase_output_script = scriptPubKey;
|
| 344 |
options.include_dummy_extranonce = true;
|
| 345 |
|
|
|
|
| 667 |
BOOST_REQUIRE(mining);
|
| 668 |
|
| 669 |
BlockAssembler::Options options;
|
| 670 |
+
options.nBlockPrioritySize = 0;
|
| 671 |
+
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
|
| 672 |
options.coinbase_output_script = scriptPubKey;
|
| 673 |
options.include_dummy_extranonce = true;
|
| 674 |
|
|
|
|
| 750 |
}
|
| 751 |
}
|
| 752 |
|
| 753 |
+
void MinerTestingSetup::TestPriorityPhaseStaleAncestors(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
|
| 754 |
+
{
|
| 755 |
+
CTxMemPool& tx_mempool{MakeMempool()};
|
| 756 |
+
BlockAssembler::Options options;
|
| 757 |
+
options.coinbase_output_script = scriptPubKey;
|
| 758 |
+
options.include_dummy_extranonce = true;
|
| 759 |
+
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
|
| 760 |
+
|
| 761 |
+
LOCK(tx_mempool.cs);
|
| 762 |
+
TestMemPoolEntryHelper entry;
|
| 763 |
+
|
| 764 |
+
CMutableTransaction tx;
|
| 765 |
+
tx.vin.resize(1);
|
| 766 |
+
tx.vin[0].scriptSig = CScript() << OP_1;
|
| 767 |
+
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
| 768 |
+
tx.vin[0].prevout.n = 0;
|
| 769 |
+
tx.vout.resize(1);
|
| 770 |
+
tx.vout[0].nValue = 5000000000LL - 100000;
|
| 771 |
+
const Txid parent_txid = tx.GetHash();
|
| 772 |
+
TryAddToMempool(tx_mempool, entry.Fee(100000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
| 773 |
+
|
| 774 |
+
tx.vin[0].prevout.hash = parent_txid;
|
| 775 |
+
const Txid child_txid = tx.GetHash();
|
| 776 |
+
TryAddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
|
| 777 |
+
|
| 778 |
+
options.nBlockPrioritySize = DEFAULT_BLOCK_RESERVED_SIZE + GetSerializeSize(TX_WITH_WITNESS(tx_mempool.mapTx.find(parent_txid)->GetTx()));
|
| 779 |
+
const auto block_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 780 |
+
BOOST_REQUIRE(block_template);
|
| 781 |
+
const CBlock& block{block_template->block};
|
| 782 |
+
BOOST_REQUIRE_EQUAL(block.vtx.size(), 2U);
|
| 783 |
+
BOOST_CHECK(block.vtx[1]->GetHash() == parent_txid);
|
| 784 |
+
for (const auto& btx : block.vtx) BOOST_CHECK(btx->GetHash() != child_txid);
|
| 785 |
+
|
| 786 |
+
// Template policy must not make the otherwise-valid zero-fee child invalid.
|
| 787 |
+
CBlock block_with_child{block};
|
| 788 |
+
block_with_child.vtx.push_back(MakeTransactionRef(tx));
|
| 789 |
+
node::RegenerateCommitments(block_with_child, *m_node.chainman);
|
| 790 |
+
const BlockValidationState state{TestBlockValidity(m_node.chainman->ActiveChainstate(), block_with_child,
|
| 791 |
+
/*check_pow=*/false, /*check_merkle_root=*/true)};
|
| 792 |
+
BOOST_CHECK_MESSAGE(state.IsValid(), state.ToString());
|
| 793 |
+
|
| 794 |
+
// With the priority phase disabled, ordinary package selection must still
|
| 795 |
+
// include the parent and stop subsidising its zero-fee child afterwards.
|
| 796 |
+
options.nBlockPrioritySize = 0;
|
| 797 |
+
const auto no_priority_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 798 |
+
BOOST_REQUIRE(no_priority_template);
|
| 799 |
+
BOOST_REQUIRE_EQUAL(no_priority_template->block.vtx.size(), 2U);
|
| 800 |
+
BOOST_CHECK(no_priority_template->block.vtx[1]->GetHash() == parent_txid);
|
| 801 |
+
|
| 802 |
+
// A zero mining fee floor may still select the child.
|
| 803 |
+
options.blockMinFeeRate = CFeeRate{0};
|
| 804 |
+
const auto zero_floor_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 805 |
+
BOOST_REQUIRE(zero_floor_template);
|
| 806 |
+
BOOST_REQUIRE_EQUAL(zero_floor_template->block.vtx.size(), 3U);
|
| 807 |
+
BOOST_CHECK(zero_floor_template->block.vtx[2]->GetHash() == child_txid);
|
| 808 |
+
|
| 809 |
+
tx_mempool.removeRecursive(*block.vtx[1], MemPoolRemovalReason::REPLACED);
|
| 810 |
+
|
| 811 |
+
// Confirmed coin age selects zero-fee parents first. The child's own fee
|
| 812 |
+
// meets the mining floor, but its stale package including those parents
|
| 813 |
+
// does not. Exercise both one ancestor and a shared descendant of two
|
| 814 |
+
// ancestors, which must each be subtracted exactly once.
|
| 815 |
+
for (const size_t parent_count : {1U, 2U}) {
|
| 816 |
+
BOOST_TEST_CONTEXT("priority-selected parents: " << parent_count) {
|
| 817 |
+
options.blockMinFeeRate = CFeeRate{1000};
|
| 818 |
+
std::vector<CTransactionRef> parents;
|
| 819 |
+
size_t priority_tx_size{0};
|
| 820 |
+
for (size_t i{0}; i < parent_count; ++i) {
|
| 821 |
+
CMutableTransaction parent;
|
| 822 |
+
parent.vin.resize(1);
|
| 823 |
+
parent.vin[0].prevout = COutPoint{txFirst[i]->GetHash(), 0};
|
| 824 |
+
parent.vin[0].scriptSig = CScript() << OP_1;
|
| 825 |
+
parent.vout.emplace_back(50 * COIN, CScript{});
|
| 826 |
+
parents.push_back(MakeTransactionRef(parent));
|
| 827 |
+
TryAddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(parents.back()));
|
| 828 |
+
priority_tx_size += GetSerializeSize(TX_WITH_WITNESS(*parents.back()));
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
CMutableTransaction child;
|
| 832 |
+
for (const auto& parent : parents) {
|
| 833 |
+
child.vin.emplace_back(COutPoint{parent->GetHash(), 0}, CScript() << OP_1);
|
| 834 |
+
}
|
| 835 |
+
child.vout.emplace_back(parent_count * 50 * COIN, CScript{});
|
| 836 |
+
const CAmount child_fee = options.blockMinFeeRate.GetFee(GetVirtualTransactionSize(CTransaction{child}));
|
| 837 |
+
child.vout[0].nValue -= child_fee;
|
| 838 |
+
const auto child_ref = MakeTransactionRef(child);
|
| 839 |
+
TryAddToMempool(tx_mempool, entry.Fee(child_fee).SpendsCoinbase(false).FromTx(child_ref));
|
| 840 |
+
|
| 841 |
+
// Without priority selection the entire package is below the floor.
|
| 842 |
+
options.nBlockPrioritySize = 0;
|
| 843 |
+
const auto below_floor_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 844 |
+
BOOST_REQUIRE(below_floor_template);
|
| 845 |
+
BOOST_CHECK_EQUAL(below_floor_template->block.vtx.size(), 1U);
|
| 846 |
+
|
| 847 |
+
// The assembler reserves 1000 bytes before adding transactions;
|
| 848 |
+
// end the priority phase immediately after including the parents.
|
| 849 |
+
options.nBlockPrioritySize = DEFAULT_BLOCK_RESERVED_SIZE + priority_tx_size;
|
| 850 |
+
const auto priority_template = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 851 |
+
BOOST_REQUIRE(priority_template);
|
| 852 |
+
BOOST_REQUIRE_EQUAL(priority_template->block.vtx.size(), parent_count + 2);
|
| 853 |
+
BOOST_CHECK(priority_template->block.vtx.back()->GetHash() == child_ref->GetHash());
|
| 854 |
+
BOOST_CHECK_EQUAL(priority_template->vTxFees.back(), child_fee);
|
| 855 |
+
// Equality is excluded by Roots' conservative resource boundary.
|
| 856 |
+
const auto weight = *BlockAssembler::m_last_block_weight;
|
| 857 |
+
options.nBlockMaxWeight = weight;
|
| 858 |
+
const auto capped = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 859 |
+
BOOST_CHECK_EQUAL(capped->block.vtx.size(), parent_count + 1);
|
| 860 |
+
options.nBlockMaxWeight = weight + 1;
|
| 861 |
+
const auto fits = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 862 |
+
BOOST_CHECK_EQUAL(fits->block.vtx.size(), parent_count + 2);
|
| 863 |
+
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
|
| 864 |
+
options.nBlockMaxSize = DEFAULT_BLOCK_RESERVED_SIZE + priority_tx_size + GetSerializeSize(TX_WITH_WITNESS(*child_ref));
|
| 865 |
+
const auto size_capped = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 866 |
+
BOOST_CHECK_EQUAL(size_capped->block.vtx.size(), parent_count + 1);
|
| 867 |
+
++options.nBlockMaxSize;
|
| 868 |
+
const auto size_fits = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 869 |
+
BOOST_CHECK_EQUAL(size_fits->block.vtx.size(), parent_count + 2);
|
| 870 |
+
options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE;
|
| 871 |
+
for (const auto& parent : parents) tx_mempool.removeRecursive(*parent, MemPoolRemovalReason::REPLACED);
|
| 872 |
+
}
|
| 873 |
+
}
|
| 874 |
+
|
| 875 |
+
// A child can outrank its parent using a second, older confirmed input.
|
| 876 |
+
// It must wait, then be revisited exactly once after its parent is selected.
|
| 877 |
+
options.nBlockPrioritySize = DEFAULT_BLOCK_PRIORITY_SIZE;
|
| 878 |
+
options.blockMinFeeRate = CFeeRate{1'000'000};
|
| 879 |
+
CMutableTransaction waiting_parent;
|
| 880 |
+
waiting_parent.vin.emplace_back(COutPoint{txFirst[1]->GetHash(), 0}, CScript() << OP_1);
|
| 881 |
+
waiting_parent.vout.emplace_back(50 * COIN, CScript{});
|
| 882 |
+
const auto parent_ref = MakeTransactionRef(waiting_parent);
|
| 883 |
+
TryAddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(parent_ref));
|
| 884 |
+
CMutableTransaction waiting_child;
|
| 885 |
+
waiting_child.vin.emplace_back(COutPoint{parent_ref->GetHash(), 0}, CScript() << OP_1);
|
| 886 |
+
waiting_child.vin.emplace_back(COutPoint{txFirst[0]->GetHash(), 0}, CScript() << OP_1);
|
| 887 |
+
waiting_child.vout.emplace_back(100 * COIN - 500, CScript{});
|
| 888 |
+
const auto waiting_child_ref = MakeTransactionRef(waiting_child);
|
| 889 |
+
TryAddToMempool(tx_mempool, entry.Fee(500).SpendsCoinbase(true).FromTx(waiting_child_ref));
|
| 890 |
+
const auto pool_size = tx_mempool.size();
|
| 891 |
+
const auto pool_fee = tx_mempool.GetTotalFee();
|
| 892 |
+
for (int repeat = 0; repeat < 2; ++repeat) {
|
| 893 |
+
const auto ready = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 894 |
+
BOOST_REQUIRE_EQUAL(ready->block.vtx.size(), 3U);
|
| 895 |
+
BOOST_CHECK(ready->block.vtx[1]->GetHash() == parent_ref->GetHash());
|
| 896 |
+
BOOST_CHECK(ready->block.vtx[2]->GetHash() == waiting_child_ref->GetHash());
|
| 897 |
+
BOOST_CHECK_EQUAL(ready->vTxFees[1], 500);
|
| 898 |
+
BOOST_CHECK_EQUAL(tx_mempool.size(), pool_size);
|
| 899 |
+
BOOST_CHECK_EQUAL(tx_mempool.GetTotalFee(), pool_fee);
|
| 900 |
+
}
|
| 901 |
+
options.nBlockPrioritySize = 0;
|
| 902 |
+
const auto disabled = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock();
|
| 903 |
+
BOOST_CHECK_EQUAL(disabled->block.vtx.size(), 1U);
|
| 904 |
+
}
|
| 905 |
+
|
| 906 |
+
BOOST_AUTO_TEST_CASE(priority_tie_policy)
|
| 907 |
+
{
|
| 908 |
+
CTxMemPool& pool = MakeMempool();
|
| 909 |
+
LOCK2(cs_main, pool.cs);
|
| 910 |
+
BlockAssembler::Options options;
|
| 911 |
+
options.test_block_validity = false;
|
| 912 |
+
options.blockMinFeeRate = CFeeRate{1'000'000'000};
|
| 913 |
+
TestMemPoolEntryHelper entry;
|
| 914 |
+
CMutableTransaction high;
|
| 915 |
+
high.vin.emplace_back(COutPoint{Txid::FromUint256(uint256::ONE), 0});
|
| 916 |
+
high.vout.emplace_back(COIN, CScript{});
|
| 917 |
+
CMutableTransaction low = high;
|
| 918 |
+
low.vin[0].prevout.n = 1;
|
| 919 |
+
const auto high_ref = MakeTransactionRef(high);
|
| 920 |
+
const auto low_ref = MakeTransactionRef(low);
|
| 921 |
+
TryAddToMempool(pool, entry.Fee(100).FromTx(high_ref));
|
| 922 |
+
TryAddToMempool(pool, entry.Fee(10).FromTx(low_ref));
|
| 923 |
+
BOOST_REQUIRE(pool.PrioritiseTransaction(low_ref->GetHash(), 1'000'000));
|
| 924 |
+
// Both have zero coin age. Actual fee breaks the tie even when operator
|
| 925 |
+
// deltas reverse their modified-fee ordering. Threshold stops after one.
|
| 926 |
+
const auto actual_fee = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 927 |
+
BOOST_REQUIRE_EQUAL(actual_fee->block.vtx.size(), 2U);
|
| 928 |
+
BOOST_CHECK(actual_fee->block.vtx[1]->GetHash() == high_ref->GetHash());
|
| 929 |
+
pool.removeRecursive(*low_ref, MemPoolRemovalReason::REPLACED);
|
| 930 |
+
TryAddToMempool(pool, entry.Fee(100).FromTx(low_ref));
|
| 931 |
+
const auto hash_tie = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 932 |
+
BOOST_REQUIRE_EQUAL(hash_tie->block.vtx.size(), 2U);
|
| 933 |
+
BOOST_CHECK(hash_tie->block.vtx[1]->GetHash() == std::max(high_ref->GetHash(), low_ref->GetHash()));
|
| 934 |
+
options.nBlockPrioritySize = 0;
|
| 935 |
+
const auto disabled = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 936 |
+
BOOST_CHECK_EQUAL(disabled->block.vtx.size(), 1U);
|
| 937 |
+
}
|
| 938 |
+
|
| 939 |
+
BOOST_AUTO_TEST_CASE(residual_policy_accounting)
|
| 940 |
+
{
|
| 941 |
+
CTxMemPool& pool = MakeMempool();
|
| 942 |
+
LOCK2(cs_main, pool.cs);
|
| 943 |
+
BlockAssembler::Options options;
|
| 944 |
+
options.test_block_validity = false;
|
| 945 |
+
options.blockMinFeeRate = CFeeRate{1000};
|
| 946 |
+
CMutableTransaction parent;
|
| 947 |
+
parent.vin.emplace_back(COutPoint{Txid::FromUint256(uint256::ONE), 0});
|
| 948 |
+
parent.vout.emplace_back(COIN, CScript{});
|
| 949 |
+
const auto parent_ref = MakeTransactionRef(parent);
|
| 950 |
+
CMutableTransaction child;
|
| 951 |
+
child.vin.emplace_back(COutPoint{parent_ref->GetHash(), 0});
|
| 952 |
+
child.vout.emplace_back(COIN - 20000, CScript{});
|
| 953 |
+
const auto child_ref = MakeTransactionRef(child);
|
| 954 |
+
const int32_t extra_weight = 401;
|
| 955 |
+
const int64_t adjusted_weight = GetTransactionWeight(*child_ref) + extra_weight;
|
| 956 |
+
const CAmount policy_vsize = (adjusted_weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
|
| 957 |
+
{
|
| 958 |
+
auto stage = pool.GetChangeSet();
|
| 959 |
+
stage->StageAddition(parent_ref, 100000, 0, 1, 0, false, 0, {}, 0, 110000);
|
| 960 |
+
stage->StageAddition(child_ref, 20000, 0, 1, 0, false, 0, {}, extra_weight, 20000 - policy_vsize);
|
| 961 |
+
BOOST_REQUIRE(stage->CheckMemPoolPolicyLimits());
|
| 962 |
+
stage->Apply();
|
| 963 |
+
}
|
| 964 |
+
// Priority uses actual fees on this zero-age tie and selects the parent.
|
| 965 |
+
// Its negative modified fee disappears; the child meets the exact rounded
|
| 966 |
+
// policy-vsize fee floor without stale ancestor debt or carrier resources.
|
| 967 |
+
const auto selected = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 968 |
+
BOOST_REQUIRE_EQUAL(selected->block.vtx.size(), 3U);
|
| 969 |
+
BOOST_CHECK(selected->block.vtx[1]->GetHash() == parent_ref->GetHash());
|
| 970 |
+
BOOST_CHECK(selected->block.vtx[2]->GetHash() == child_ref->GetHash());
|
| 971 |
+
BOOST_CHECK_EQUAL(selected->vTxFees[0], 100000);
|
| 972 |
+
BOOST_CHECK_EQUAL(selected->vTxFees[1], 20000);
|
| 973 |
+
BOOST_CHECK_EQUAL(selected->block.vtx[0]->vout[0].nValue,
|
| 974 |
+
GetBlockSubsidy(1, m_node.chainman->GetParams().GetConsensus()) + 120000);
|
| 975 |
+
BOOST_REQUIRE_EQUAL(selected->m_package_feerates.size(), 1U);
|
| 976 |
+
BOOST_CHECK(selected->m_package_feerates[0] == FeePerVSize(policy_vsize, policy_vsize));
|
| 977 |
+
BOOST_REQUIRE(pool.PrioritiseTransaction(child_ref->GetHash(), -1));
|
| 978 |
+
const auto below = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 979 |
+
BOOST_CHECK_EQUAL(below->block.vtx.size(), 2U);
|
| 980 |
+
BOOST_REQUIRE(pool.PrioritiseTransaction(child_ref->GetHash(), 1));
|
| 981 |
+
options.nBlockMaxWeight = DEFAULT_BLOCK_RESERVED_WEIGHT + GetTransactionWeight(*parent_ref) + adjusted_weight;
|
| 982 |
+
const auto capped = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 983 |
+
BOOST_CHECK_EQUAL(capped->block.vtx.size(), 2U);
|
| 984 |
+
++options.nBlockMaxWeight;
|
| 985 |
+
const auto fits = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 986 |
+
BOOST_CHECK_EQUAL(fits->block.vtx.size(), 3U);
|
| 987 |
+
options.nBlockPrioritySize = 0;
|
| 988 |
+
const auto disabled = BlockAssembler{m_node.chainman->ActiveChainstate(), &pool, options}.CreateNewBlock();
|
| 989 |
+
BOOST_CHECK_EQUAL(disabled->block.vtx.size(), 1U);
|
| 990 |
+
}
|
| 991 |
+
|
| 992 |
+
BOOST_AUTO_TEST_CASE(template_option_normalization)
|
| 993 |
+
{
|
| 994 |
+
ArgsManager args;
|
| 995 |
+
BlockAssembler::Options options;
|
| 996 |
+
node::ApplyArgsManOptions(args, options);
|
| 997 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxWeight, 1'200'000U);
|
| 998 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxSize, MAX_BLOCK_SERIALIZED_SIZE);
|
| 999 |
+
BOOST_CHECK_EQUAL(options.nBlockPrioritySize, 100'000U);
|
| 1000 |
+
BOOST_CHECK_EQUAL(options.blockMinFeeRate.GetFeePerK(), 1000);
|
| 1001 |
+
args.ForceSetArg("-blockmaxsize", "120000");
|
| 1002 |
+
node::ApplyArgsManOptions(args, options);
|
| 1003 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxWeight, MAX_BLOCK_WEIGHT);
|
| 1004 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxSize, 120'000U);
|
| 1005 |
+
args.ForceSetArg("-blockmaxweight", "600000");
|
| 1006 |
+
args.ForceSetArg("-blockprioritysize", "0");
|
| 1007 |
+
args.ForceSetArg("-blockreservedweight", "9000");
|
| 1008 |
+
args.ForceSetArg("-blockmintxfee", "0.00002000");
|
| 1009 |
+
node::ApplyArgsManOptions(args, options);
|
| 1010 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxWeight, 600'000U);
|
| 1011 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxSize, 120'000U);
|
| 1012 |
+
BOOST_CHECK_EQUAL(options.nBlockPrioritySize, 0U);
|
| 1013 |
+
BOOST_REQUIRE(options.block_reserved_weight);
|
| 1014 |
+
BOOST_CHECK_EQUAL(*options.block_reserved_weight, 9000U);
|
| 1015 |
+
BOOST_CHECK_EQUAL(options.blockMinFeeRate.GetFeePerK(), 2000);
|
| 1016 |
+
ArgsManager weight_only;
|
| 1017 |
+
weight_only.ForceSetArg("-blockmaxweight", "700000");
|
| 1018 |
+
options = {};
|
| 1019 |
+
node::ApplyArgsManOptions(weight_only, options);
|
| 1020 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxWeight, 700'000U);
|
| 1021 |
+
BOOST_CHECK_EQUAL(options.nBlockMaxSize, MAX_BLOCK_SERIALIZED_SIZE);
|
| 1022 |
+
}
|
| 1023 |
+
|
| 1024 |
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
|
| 1025 |
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
| 1026 |
{
|
| 1027 |
+
m_node.args->ForceSetArg("-blockprioritysize", "0");
|
| 1028 |
+
m_node.args->ForceSetArg("-blockmaxweight", "4000000");
|
| 1029 |
auto mining{MakeMining()};
|
| 1030 |
BOOST_REQUIRE(mining);
|
| 1031 |
|
| 1032 |
// Note that by default, these tests run with size accounting enabled.
|
| 1033 |
CScript scriptPubKey = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex << OP_CHECKSIG;
|
| 1034 |
BlockAssembler::Options options;
|
| 1035 |
+
options.nBlockPrioritySize = 0;
|
| 1036 |
+
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
|
| 1037 |
options.coinbase_output_script = scriptPubKey;
|
| 1038 |
options.include_dummy_extranonce = true;
|
| 1039 |
|
|
|
|
| 1149 |
SetMockTime(0);
|
| 1150 |
|
| 1151 |
TestPrioritisedMining(scriptPubKey, txFirst);
|
| 1152 |
+
TestPriorityPhaseStaleAncestors(scriptPubKey, txFirst);
|
| 1153 |
}
|
| 1154 |
|
| 1155 |
BOOST_AUTO_TEST_SUITE_END()
|