@@ -30,6 +30,8 @@ using node::CBlockTemplate;
3030namespace miner_tests {
3131struct MinerTestingSetup : public TestingSetup {
3232 void TestPackageSelection (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
33+ void TestBasicMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
34+ void TestPrioritisedMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
3335 bool TestSequenceLocks (const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
3436 {
3537 CCoinsViewMemPool view_mempool (&m_node.chainman ->ActiveChainstate ().CoinsTip (), *m_node.mempool );
@@ -191,60 +193,17 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
191193 BOOST_CHECK (pblocktemplate->block .vtx [8 ]->GetHash () == hashLowFeeTx2);
192194}
193195
194- // NOTE: These tests rely on CreateNewBlock doing its own self-validation!
195- BOOST_AUTO_TEST_CASE (CreateNewBlock_validity)
196+ void MinerTestingSetup::TestBasicMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight)
196197{
197- // Note that by default, these tests run with size accounting enabled.
198- const auto chainParams = CreateChainParams (*m_node.args , CBaseChainParams::MAIN);
199- const CChainParams& chainparams = *chainParams;
200- CScript scriptPubKey = CScript () << ParseHex (" 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" ) << OP_CHECKSIG;
201- std::unique_ptr<CBlockTemplate> pblocktemplate;
202- CMutableTransaction tx;
203- CScript script;
204198 uint256 hash;
199+ CMutableTransaction tx;
205200 TestMemPoolEntryHelper entry;
206201 entry.nFee = 11 ;
207202 entry.nHeight = 11 ;
208203
209- fCheckpointsEnabled = false ;
210-
211- // Simple block creation, nothing special yet:
212- BOOST_CHECK (pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
213-
214- // We can't make transactions until we have inputs
215- // Therefore, load 110 blocks :)
216- static_assert (std::size (BLOCKINFO) == 110 , " Should have 110 blocks to import" );
217- int baseheight = 0 ;
218- std::vector<CTransactionRef> txFirst;
219- for (const auto & bi : BLOCKINFO) {
220- CBlock *pblock = &pblocktemplate->block ; // pointer for convenience
221- {
222- LOCK (cs_main);
223- pblock->nVersion = VERSIONBITS_TOP_BITS;
224- pblock->nTime = m_node.chainman ->ActiveChain ().Tip ()->GetMedianTimePast ()+1 ;
225- CMutableTransaction txCoinbase (*pblock->vtx [0 ]);
226- txCoinbase.nVersion = 1 ;
227- txCoinbase.vin [0 ].scriptSig = CScript{} << (m_node.chainman ->ActiveChain ().Height () + 1 ) << bi.extranonce ;
228- txCoinbase.vout .resize (1 ); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
229- txCoinbase.vout [0 ].scriptPubKey = CScript ();
230- pblock->vtx [0 ] = MakeTransactionRef (std::move (txCoinbase));
231- if (txFirst.size () == 0 )
232- baseheight = m_node.chainman ->ActiveChain ().Height ();
233- if (txFirst.size () < 4 )
234- txFirst.push_back (pblock->vtx [0 ]);
235- pblock->hashMerkleRoot = BlockMerkleRoot (*pblock);
236- pblock->nNonce = bi.nonce ;
237- }
238- std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
239- BOOST_CHECK (Assert (m_node.chainman )->ProcessNewBlock (chainparams, shared_pblock, true , nullptr ));
240- pblock->hashPrevBlock = pblock->GetHash ();
241- }
242-
243- LOCK (cs_main);
244- LOCK (m_node.mempool ->cs );
245-
246204 // Just to make sure we can still make simple blocks
247- BOOST_CHECK (pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
205+ auto pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey);
206+ BOOST_CHECK (pblocktemplate);
248207
249208 const CAmount BLOCKSUBSIDY = 50 *COIN;
250209 const CAmount LOWFEE = CENT;
@@ -386,7 +345,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
386345 tx.vin [0 ].prevout .n = 0 ;
387346 tx.vin [0 ].scriptSig = CScript () << OP_1;
388347 tx.vout [0 ].nValue = BLOCKSUBSIDY-LOWFEE;
389- script = CScript () << OP_0;
348+ CScript script = CScript () << OP_0;
390349 tx.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (script));
391350 hash = tx.GetHash ();
392351 m_node.mempool ->addUnchecked (entry.Fee (LOWFEE).Time (GetTime ()).SpendsCoinbase (true ).FromTx (tx));
@@ -508,13 +467,143 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
508467
509468 BOOST_CHECK (pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
510469 BOOST_CHECK_EQUAL (pblocktemplate->block .vtx .size (), 5U );
470+ }
471+
472+ void MinerTestingSetup::TestPrioritisedMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
473+ {
474+ TestMemPoolEntryHelper entry;
475+
476+ // Test that a tx below min fee but prioritised is included
477+ CMutableTransaction tx;
478+ tx.vin .resize (1 );
479+ tx.vin [0 ].prevout .hash = txFirst[0 ]->GetHash ();
480+ tx.vin [0 ].prevout .n = 0 ;
481+ tx.vin [0 ].scriptSig = CScript () << OP_1;
482+ tx.vout .resize (1 );
483+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
484+ uint256 hashFreePrioritisedTx = tx.GetHash ();
485+ m_node.mempool ->addUnchecked (entry.Fee (0 ).Time (GetTime ()).SpendsCoinbase (true ).FromTx (tx));
486+ m_node.mempool ->PrioritiseTransaction (hashFreePrioritisedTx, 5 * COIN);
487+
488+ tx.vin [0 ].prevout .hash = txFirst[1 ]->GetHash ();
489+ tx.vin [0 ].prevout .n = 0 ;
490+ tx.vout [0 ].nValue = 5000000000LL - 1000 ;
491+ // This tx has a low fee: 1000 satoshis
492+ uint256 hashParentTx = tx.GetHash (); // save this txid for later use
493+ m_node.mempool ->addUnchecked (entry.Fee (1000 ).Time (GetTime ()).SpendsCoinbase (true ).FromTx (tx));
494+
495+ // This tx has a medium fee: 10000 satoshis
496+ tx.vin [0 ].prevout .hash = txFirst[2 ]->GetHash ();
497+ tx.vout [0 ].nValue = 5000000000LL - 10000 ;
498+ uint256 hashMediumFeeTx = tx.GetHash ();
499+ m_node.mempool ->addUnchecked (entry.Fee (10000 ).Time (GetTime ()).SpendsCoinbase (true ).FromTx (tx));
500+ m_node.mempool ->PrioritiseTransaction (hashMediumFeeTx, -5 * COIN);
501+
502+ // This tx also has a low fee, but is prioritised
503+ tx.vin [0 ].prevout .hash = hashParentTx;
504+ tx.vout [0 ].nValue = 5000000000LL - 1000 - 1000 ; // 1000 satoshi fee
505+ uint256 hashPrioritsedChild = tx.GetHash ();
506+ m_node.mempool ->addUnchecked (entry.Fee (1000 ).Time (GetTime ()).SpendsCoinbase (false ).FromTx (tx));
507+ m_node.mempool ->PrioritiseTransaction (hashPrioritsedChild, 2 * COIN);
508+
509+ // Test that transaction selection properly updates ancestor fee calculations as prioritised
510+ // parents get included in a block. Create a transaction with two prioritised ancestors, each
511+ // included by itself: FreeParent <- FreeChild <- FreeGrandchild.
512+ // When FreeParent is added, a modified entry will be created for FreeChild + FreeGrandchild
513+ // FreeParent's prioritisation should not be included in that entry.
514+ // When FreeChild is included, FreeChild's prioritisation should also not be included.
515+ tx.vin [0 ].prevout .hash = txFirst[3 ]->GetHash ();
516+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
517+ uint256 hashFreeParent = tx.GetHash ();
518+ m_node.mempool ->addUnchecked (entry.Fee (0 ).SpendsCoinbase (true ).FromTx (tx));
519+ m_node.mempool ->PrioritiseTransaction (hashFreeParent, 10 * COIN);
520+
521+ tx.vin [0 ].prevout .hash = hashFreeParent;
522+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
523+ uint256 hashFreeChild = tx.GetHash ();
524+ m_node.mempool ->addUnchecked (entry.Fee (0 ).SpendsCoinbase (false ).FromTx (tx));
525+ m_node.mempool ->PrioritiseTransaction (hashFreeChild, 1 * COIN);
526+
527+ tx.vin [0 ].prevout .hash = hashFreeChild;
528+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
529+ uint256 hashFreeGrandchild = tx.GetHash ();
530+ m_node.mempool ->addUnchecked (entry.Fee (0 ).SpendsCoinbase (false ).FromTx (tx));
531+
532+ auto pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey);
533+ BOOST_REQUIRE_EQUAL (pblocktemplate->block .vtx .size (), 6U );
534+ BOOST_CHECK (pblocktemplate->block .vtx [1 ]->GetHash () == hashFreeParent);
535+ BOOST_CHECK (pblocktemplate->block .vtx [2 ]->GetHash () == hashFreePrioritisedTx);
536+ BOOST_CHECK (pblocktemplate->block .vtx [3 ]->GetHash () == hashParentTx);
537+ BOOST_CHECK (pblocktemplate->block .vtx [4 ]->GetHash () == hashPrioritsedChild);
538+ BOOST_CHECK (pblocktemplate->block .vtx [5 ]->GetHash () == hashFreeChild);
539+ for (size_t i=0 ; i<pblocktemplate->block .vtx .size (); ++i) {
540+ // The FreeParent and FreeChild's prioritisations should not impact the child.
541+ BOOST_CHECK (pblocktemplate->block .vtx [i]->GetHash () != hashFreeGrandchild);
542+ // De-prioritised transaction should not be included.
543+ BOOST_CHECK (pblocktemplate->block .vtx [i]->GetHash () != hashMediumFeeTx);
544+ }
545+ }
546+
547+ // NOTE: These tests rely on CreateNewBlock doing its own self-validation!
548+ BOOST_AUTO_TEST_CASE (CreateNewBlock_validity)
549+ {
550+ // Note that by default, these tests run with size accounting enabled.
551+ const auto chainParams = CreateChainParams (*m_node.args , CBaseChainParams::MAIN);
552+ const CChainParams& chainparams = *chainParams;
553+ CScript scriptPubKey = CScript () << ParseHex (" 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" ) << OP_CHECKSIG;
554+ std::unique_ptr<CBlockTemplate> pblocktemplate;
555+
556+ fCheckpointsEnabled = false ;
557+
558+ // Simple block creation, nothing special yet:
559+ BOOST_CHECK (pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
560+
561+ // We can't make transactions until we have inputs
562+ // Therefore, load 110 blocks :)
563+ static_assert (std::size (BLOCKINFO) == 110 , " Should have 110 blocks to import" );
564+ int baseheight = 0 ;
565+ std::vector<CTransactionRef> txFirst;
566+ for (const auto & bi : BLOCKINFO) {
567+ CBlock *pblock = &pblocktemplate->block ; // pointer for convenience
568+ {
569+ LOCK (cs_main);
570+ pblock->nVersion = VERSIONBITS_TOP_BITS;
571+ pblock->nTime = m_node.chainman ->ActiveChain ().Tip ()->GetMedianTimePast ()+1 ;
572+ CMutableTransaction txCoinbase (*pblock->vtx [0 ]);
573+ txCoinbase.nVersion = 1 ;
574+ txCoinbase.vin [0 ].scriptSig = CScript{} << (m_node.chainman ->ActiveChain ().Height () + 1 ) << bi.extranonce ;
575+ txCoinbase.vout .resize (1 ); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
576+ txCoinbase.vout [0 ].scriptPubKey = CScript ();
577+ pblock->vtx [0 ] = MakeTransactionRef (std::move (txCoinbase));
578+ if (txFirst.size () == 0 )
579+ baseheight = m_node.chainman ->ActiveChain ().Height ();
580+ if (txFirst.size () < 4 )
581+ txFirst.push_back (pblock->vtx [0 ]);
582+ pblock->hashMerkleRoot = BlockMerkleRoot (*pblock);
583+ pblock->nNonce = bi.nonce ;
584+ }
585+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
586+ BOOST_CHECK (Assert (m_node.chainman )->ProcessNewBlock (chainparams, shared_pblock, true , nullptr ));
587+ pblock->hashPrevBlock = pblock->GetHash ();
588+ }
589+
590+ LOCK (cs_main);
591+ LOCK (m_node.mempool ->cs );
592+
593+ TestBasicMining (chainparams, scriptPubKey, txFirst, baseheight);
511594
512595 m_node.chainman ->ActiveChain ().Tip ()->nHeight --;
513596 SetMockTime (0 );
514597 m_node.mempool ->clear ();
515598
516599 TestPackageSelection (chainparams, scriptPubKey, txFirst);
517600
601+ m_node.chainman ->ActiveChain ().Tip ()->nHeight --;
602+ SetMockTime (0 );
603+ m_node.mempool ->clear ();
604+
605+ TestPrioritisedMining (chainparams, scriptPubKey, txFirst);
606+
518607 fCheckpointsEnabled = true ;
519608}
520609
0 commit comments