Skip to content

Commit 29fac19

Browse files
committed
Add unit tests for ancestor feerate mining
1 parent c82a4e9 commit 29fac19

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed

src/test/miner_tests.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,113 @@ bool TestSequenceLocks(const CTransaction &tx, int flags)
7171
return CheckSequenceLocks(tx, flags);
7272
}
7373

74+
// Test suite for ancestor feerate transaction selection.
75+
// Implemented as an additional function, rather than a separate test case,
76+
// to allow reusing the blockchain created in CreateNewBlock_validity.
77+
// Note that this test assumes blockprioritysize is 0.
78+
void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransaction *>& txFirst)
79+
{
80+
// Test the ancestor feerate transaction selection.
81+
TestMemPoolEntryHelper entry;
82+
83+
// Test that a medium fee transaction will be selected after a higher fee
84+
// rate package with a low fee rate parent.
85+
CMutableTransaction tx;
86+
tx.vin.resize(1);
87+
tx.vin[0].scriptSig = CScript() << OP_1;
88+
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
89+
tx.vin[0].prevout.n = 0;
90+
tx.vout.resize(1);
91+
tx.vout[0].nValue = 5000000000LL - 1000;
92+
// This tx has a low fee: 1000 satoshis
93+
uint256 hashParentTx = tx.GetHash(); // save this txid for later use
94+
mempool.addUnchecked(hashParentTx, entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
95+
96+
// This tx has a medium fee: 10000 satoshis
97+
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
98+
tx.vout[0].nValue = 5000000000LL - 10000;
99+
uint256 hashMediumFeeTx = tx.GetHash();
100+
mempool.addUnchecked(hashMediumFeeTx, entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
101+
102+
// This tx has a high fee, but depends on the first transaction
103+
tx.vin[0].prevout.hash = hashParentTx;
104+
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
105+
uint256 hashHighFeeTx = tx.GetHash();
106+
mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
107+
108+
CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
109+
BOOST_CHECK(pblocktemplate->block.vtx[1].GetHash() == hashParentTx);
110+
BOOST_CHECK(pblocktemplate->block.vtx[2].GetHash() == hashHighFeeTx);
111+
BOOST_CHECK(pblocktemplate->block.vtx[3].GetHash() == hashMediumFeeTx);
112+
113+
// Test that a package below the min relay fee doesn't get included
114+
tx.vin[0].prevout.hash = hashHighFeeTx;
115+
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
116+
uint256 hashFreeTx = tx.GetHash();
117+
mempool.addUnchecked(hashFreeTx, entry.Fee(0).FromTx(tx));
118+
size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
119+
120+
// Calculate a fee on child transaction that will put the package just
121+
// below the min relay fee (assuming 1 child tx of the same size).
122+
CAmount feeToUse = minRelayTxFee.GetFee(2*freeTxSize) - 1;
123+
124+
tx.vin[0].prevout.hash = hashFreeTx;
125+
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
126+
uint256 hashLowFeeTx = tx.GetHash();
127+
mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse).FromTx(tx));
128+
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
129+
// Verify that the free tx and the low fee tx didn't get selected
130+
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
131+
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx);
132+
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx);
133+
}
134+
135+
// Test that packages above the min relay fee do get included, even if one
136+
// of the transactions is below the min relay fee
137+
// Remove the low fee transaction and replace with a higher fee transaction
138+
std::list<CTransaction> dummy;
139+
mempool.removeRecursive(tx, dummy);
140+
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
141+
hashLowFeeTx = tx.GetHash();
142+
mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx));
143+
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
144+
BOOST_CHECK(pblocktemplate->block.vtx[4].GetHash() == hashFreeTx);
145+
BOOST_CHECK(pblocktemplate->block.vtx[5].GetHash() == hashLowFeeTx);
146+
147+
// Test that transaction selection properly updates ancestor fee
148+
// calculations as ancestor transactions get included in a block.
149+
// Add a 0-fee transaction that has 2 outputs.
150+
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
151+
tx.vout.resize(2);
152+
tx.vout[0].nValue = 5000000000LL - 100000000;
153+
tx.vout[1].nValue = 100000000; // 1BTC output
154+
uint256 hashFreeTx2 = tx.GetHash();
155+
mempool.addUnchecked(hashFreeTx2, entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
156+
157+
// This tx can't be mined by itself
158+
tx.vin[0].prevout.hash = hashFreeTx2;
159+
tx.vout.resize(1);
160+
feeToUse = minRelayTxFee.GetFee(freeTxSize);
161+
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
162+
uint256 hashLowFeeTx2 = tx.GetHash();
163+
mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
164+
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
165+
166+
// Verify that this tx isn't selected.
167+
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
168+
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx2);
169+
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx2);
170+
}
171+
172+
// This tx will be mineable, and should cause hashLowFeeTx2 to be selected
173+
// as well.
174+
tx.vin[0].prevout.n = 1;
175+
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
176+
mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx));
177+
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
178+
BOOST_CHECK(pblocktemplate->block.vtx[8].GetHash() == hashLowFeeTx2);
179+
}
180+
74181
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
75182
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
76183
{
@@ -385,6 +492,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
385492
SetMockTime(0);
386493
mempool.clear();
387494

495+
TestPackageSelection(chainparams, scriptPubKey, txFirst);
496+
388497
BOOST_FOREACH(CTransaction *_tx, txFirst)
389498
delete _tx;
390499

0 commit comments

Comments
 (0)