@@ -37,6 +37,8 @@ using node::CBlockTemplate;
3737namespace miner_tests {
3838struct MinerTestingSetup : public TestingSetup {
3939 void TestPackageSelection (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
40+ void TestBasicMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
41+ void TestPrioritisedMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
4042 bool TestSequenceLocks (const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
4143 {
4244 CCoinsViewMemPool view_mempool (&m_node.chainman ->ActiveChainstate ().CoinsTip (), *m_node.mempool );
@@ -205,70 +207,16 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
205207 BOOST_CHECK (pblocktemplate->block .vtx [8 ]->GetHash () == hashLowFeeTx2);
206208}
207209
208- // NOTE: These tests rely on CreateNewBlock doing its own self-validation!
209- BOOST_AUTO_TEST_CASE (CreateNewBlock_validity)
210+ void MinerTestingSetup::TestBasicMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight)
210211{
211- const auto chainParams = CreateChainParams (*m_node.args , CBaseChainParams::MAIN);
212- const CChainParams& chainparams = *chainParams;
213- CScript scriptPubKey = CScript () << ParseHex (" 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" ) << OP_CHECKSIG;
214- std::unique_ptr<CBlockTemplate> pblocktemplate, pemptyblocktemplate;
215- CMutableTransaction tx;
216- CScript script;
217212 uint256 hash;
213+ CMutableTransaction tx;
218214 TestMemPoolEntryHelper entry;
219215 entry.nFee = 11 ;
220216 entry.nHeight = 11 ;
221-
222- fCheckpointsEnabled = false ;
223-
224- // Simple block creation, nothing special yet:
225- BOOST_CHECK (pemptyblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
226-
227- // We can't make transactions until we have inputs
228- // Therefore, load 100 blocks :)
229- int baseheight = 0 ;
230- std::vector<CTransactionRef> txFirst;
231-
232- auto createAndProcessEmptyBlock = [&]() {
233- int i = m_node.chainman ->ActiveChain ().Height () % blockinfo_size;
234- CBlock *pblock = &pemptyblocktemplate->block ; // pointer for convenience
235- {
236- LOCK (cs_main);
237- pblock->nVersion = VERSIONBITS_TOP_BITS;
238- pblock->nTime = m_node.chainman ->ActiveChain ().Tip ()->GetMedianTimePast ()+1 ;
239- CMutableTransaction txCoinbase (*pblock->vtx [0 ]);
240- txCoinbase.nVersion = 1 ;
241- txCoinbase.vin [0 ].scriptSig = CScript{} << (m_node.chainman ->ActiveChain ().Height () + 1 ) << BLOCKINFO[i].extranonce ;
242- txCoinbase.vout [0 ].scriptPubKey = CScript ();
243- pblock->vtx [0 ] = MakeTransactionRef (std::move (txCoinbase));
244- if (txFirst.size () == 0 )
245- baseheight = m_node.chainman ->ActiveChain ().Height ();
246- if (txFirst.size () < 4 )
247- txFirst.push_back (pblock->vtx [0 ]);
248- pblock->hashMerkleRoot = BlockMerkleRoot (*pblock);
249- pblock->nNonce = BLOCKINFO[i].nonce ;
250-
251- // This will usually succeed in the first round as we take the nonce from BLOCKINFO
252- // It's however useful when adding new blocks with unknown nonces (you should add the found block to BLOCKINFO)
253- while (!CheckProofOfWork (pblock->GetHash (), pblock->nBits , chainparams.GetConsensus ())) {
254- pblock->nNonce ++;
255- }
256- }
257- std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
258- BOOST_CHECK (Assert (m_node.chainman )->ProcessNewBlock (chainparams, shared_pblock, true , nullptr ));
259- pblock->hashPrevBlock = pblock->GetHash ();
260- };
261-
262- for ([[maybe_unused]] const auto & _ : BLOCKINFO) {
263- createAndProcessEmptyBlock ();
264- }
265-
266- {
267- LOCK (cs_main);
268- LOCK (m_node.mempool ->cs );
269-
270217 // Just to make sure we can still make simple blocks
271- BOOST_CHECK (pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
218+ auto pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey);
219+ BOOST_CHECK (pblocktemplate);
272220
273221 const CAmount BLOCKSUBSIDY = 500 *COIN;
274222 const CAmount LOWFEE = CENT;
@@ -410,7 +358,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
410358 tx.vin [0 ].prevout .n = 0 ;
411359 tx.vin [0 ].scriptSig = CScript () << OP_1;
412360 tx.vout [0 ].nValue = BLOCKSUBSIDY-LOWFEE;
413- script = CScript () << OP_0;
361+ CScript script = CScript () << OP_0;
414362 tx.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (script));
415363 hash = tx.GetHash ();
416364 m_node.mempool ->addUnchecked (entry.Fee (LOWFEE).Time (Now<NodeSeconds>()).SpendsCoinbase (true ).FromTx (tx));
@@ -530,7 +478,143 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
530478 for (int i = 0 ; i < CBlockIndex::nMedianTimeSpan; ++i) {
531479 CBlockIndex* ancestor{Assert (m_node.chainman ->ActiveChain ().Tip ()->GetAncestor (m_node.chainman ->ActiveChain ().Tip ()->nHeight - i))};
532480 ancestor->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
533- } // unlock cs_main while calling createAndProcessEmptyBlock
481+ }
482+ }
483+
484+ void MinerTestingSetup::TestPrioritisedMining (const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
485+ {
486+ TestMemPoolEntryHelper entry;
487+
488+ // Test that a tx below min fee but prioritised is included
489+ CMutableTransaction tx;
490+ tx.vin .resize (1 );
491+ tx.vin [0 ].prevout .hash = txFirst[0 ]->GetHash ();
492+ tx.vin [0 ].prevout .n = 0 ;
493+ tx.vin [0 ].scriptSig = CScript () << OP_1;
494+ tx.vout .resize (1 );
495+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
496+ uint256 hashFreePrioritisedTx = tx.GetHash ();
497+ m_node.mempool ->addUnchecked (entry.Fee (0 ).Time (Now<NodeSeconds>()).SpendsCoinbase (true ).FromTx (tx));
498+ m_node.mempool ->PrioritiseTransaction (hashFreePrioritisedTx, 5 * COIN);
499+
500+ tx.vin [0 ].prevout .hash = txFirst[1 ]->GetHash ();
501+ tx.vin [0 ].prevout .n = 0 ;
502+ tx.vout [0 ].nValue = 5000000000LL - 1000 ;
503+ // This tx has a low fee: 1000 satoshis
504+ uint256 hashParentTx = tx.GetHash (); // save this txid for later use
505+ m_node.mempool ->addUnchecked (entry.Fee (1000 ).Time (Now<NodeSeconds>()).SpendsCoinbase (true ).FromTx (tx));
506+
507+ // This tx has a medium fee: 10000 satoshis
508+ tx.vin [0 ].prevout .hash = txFirst[2 ]->GetHash ();
509+ tx.vout [0 ].nValue = 5000000000LL - 10000 ;
510+ uint256 hashMediumFeeTx = tx.GetHash ();
511+ m_node.mempool ->addUnchecked (entry.Fee (10000 ).Time (Now<NodeSeconds>()).SpendsCoinbase (true ).FromTx (tx));
512+ m_node.mempool ->PrioritiseTransaction (hashMediumFeeTx, -5 * COIN);
513+
514+ // This tx also has a low fee, but is prioritised
515+ tx.vin [0 ].prevout .hash = hashParentTx;
516+ tx.vout [0 ].nValue = 5000000000LL - 1000 - 1000 ; // 1000 satoshi fee
517+ uint256 hashPrioritsedChild = tx.GetHash ();
518+ m_node.mempool ->addUnchecked (entry.Fee (1000 ).Time (Now<NodeSeconds>()).SpendsCoinbase (false ).FromTx (tx));
519+ m_node.mempool ->PrioritiseTransaction (hashPrioritsedChild, 2 * COIN);
520+
521+ // Test that transaction selection properly updates ancestor fee calculations as prioritised
522+ // parents get included in a block. Create a transaction with two prioritised ancestors, each
523+ // included by itself: FreeParent <- FreeChild <- FreeGrandchild.
524+ // When FreeParent is added, a modified entry will be created for FreeChild + FreeGrandchild
525+ // FreeParent's prioritisation should not be included in that entry.
526+ // When FreeChild is included, FreeChild's prioritisation should also not be included.
527+ tx.vin [0 ].prevout .hash = txFirst[3 ]->GetHash ();
528+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
529+ uint256 hashFreeParent = tx.GetHash ();
530+ m_node.mempool ->addUnchecked (entry.Fee (0 ).SpendsCoinbase (true ).FromTx (tx));
531+ m_node.mempool ->PrioritiseTransaction (hashFreeParent, 10 * COIN);
532+
533+ tx.vin [0 ].prevout .hash = hashFreeParent;
534+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
535+ uint256 hashFreeChild = tx.GetHash ();
536+ m_node.mempool ->addUnchecked (entry.Fee (0 ).SpendsCoinbase (false ).FromTx (tx));
537+ m_node.mempool ->PrioritiseTransaction (hashFreeChild, 1 * COIN);
538+
539+ tx.vin [0 ].prevout .hash = hashFreeChild;
540+ tx.vout [0 ].nValue = 5000000000LL ; // 0 fee
541+ uint256 hashFreeGrandchild = tx.GetHash ();
542+ m_node.mempool ->addUnchecked (entry.Fee (0 ).SpendsCoinbase (false ).FromTx (tx));
543+
544+ auto pblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey);
545+ BOOST_REQUIRE_EQUAL (pblocktemplate->block .vtx .size (), 6U );
546+ BOOST_CHECK (pblocktemplate->block .vtx [1 ]->GetHash () == hashFreeParent);
547+ BOOST_CHECK (pblocktemplate->block .vtx [2 ]->GetHash () == hashFreePrioritisedTx);
548+ BOOST_CHECK (pblocktemplate->block .vtx [3 ]->GetHash () == hashParentTx);
549+ BOOST_CHECK (pblocktemplate->block .vtx [4 ]->GetHash () == hashPrioritsedChild);
550+ BOOST_CHECK (pblocktemplate->block .vtx [5 ]->GetHash () == hashFreeChild);
551+ for (size_t i=0 ; i<pblocktemplate->block .vtx .size (); ++i) {
552+ // The FreeParent and FreeChild's prioritisations should not impact the child.
553+ BOOST_CHECK (pblocktemplate->block .vtx [i]->GetHash () != hashFreeGrandchild);
554+ // De-prioritised transaction should not be included.
555+ BOOST_CHECK (pblocktemplate->block .vtx [i]->GetHash () != hashMediumFeeTx);
556+ }
557+ }
558+
559+ // NOTE: These tests rely on CreateNewBlock doing its own self-validation!
560+ BOOST_AUTO_TEST_CASE (CreateNewBlock_validity)
561+ {
562+ // Note that by default, these tests run with size accounting enabled.
563+ const auto chainParams = CreateChainParams (*m_node.args , CBaseChainParams::MAIN);
564+ const CChainParams& chainparams = *chainParams;
565+ CScript scriptPubKey = CScript () << ParseHex (" 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" ) << OP_CHECKSIG;
566+ std::unique_ptr<CBlockTemplate> pblocktemplate, pemptyblocktemplate;
567+
568+ fCheckpointsEnabled = false ;
569+
570+ // Simple block creation, nothing special yet:
571+ BOOST_CHECK (pemptyblocktemplate = AssemblerForTest (chainparams).CreateNewBlock (scriptPubKey));
572+
573+ // We can't make transactions until we have inputs
574+ // Therefore, load 100 blocks :)
575+ int baseheight = 0 ;
576+ std::vector<CTransactionRef> txFirst;
577+
578+ auto createAndProcessEmptyBlock = [&]() {
579+ int i = m_node.chainman ->ActiveChain ().Height () % blockinfo_size;
580+ CBlock *pblock = &pemptyblocktemplate->block ; // pointer for convenience
581+ {
582+ LOCK (cs_main);
583+ pblock->nVersion = VERSIONBITS_TOP_BITS;
584+ pblock->nTime = m_node.chainman ->ActiveChain ().Tip ()->GetMedianTimePast ()+1 ;
585+ CMutableTransaction txCoinbase (*pblock->vtx [0 ]);
586+ txCoinbase.nVersion = 1 ;
587+ txCoinbase.vin [0 ].scriptSig = CScript{} << (m_node.chainman ->ActiveChain ().Height () + 1 ) << BLOCKINFO[i].extranonce ;
588+ txCoinbase.vout [0 ].scriptPubKey = CScript ();
589+ pblock->vtx [0 ] = MakeTransactionRef (std::move (txCoinbase));
590+ if (txFirst.size () == 0 )
591+ baseheight = m_node.chainman ->ActiveChain ().Height ();
592+ if (txFirst.size () < 4 )
593+ txFirst.push_back (pblock->vtx [0 ]);
594+ pblock->hashMerkleRoot = BlockMerkleRoot (*pblock);
595+ pblock->nNonce = BLOCKINFO[i].nonce ;
596+
597+ // This will usually succeed in the first round as we take the nonce from BLOCKINFO
598+ // It's however useful when adding new blocks with unknown nonces (you should add the found block to BLOCKINFO)
599+ while (!CheckProofOfWork (pblock->GetHash (), pblock->nBits , chainparams.GetConsensus ())) {
600+ pblock->nNonce ++;
601+ }
602+ }
603+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
604+ BOOST_CHECK (Assert (m_node.chainman )->ProcessNewBlock (chainparams, shared_pblock, true , nullptr ));
605+ pblock->hashPrevBlock = pblock->GetHash ();
606+ };
607+
608+ for ([[maybe_unused]] const auto & _ : BLOCKINFO) {
609+ createAndProcessEmptyBlock ();
610+ }
611+
612+
613+ {
614+ LOCK (cs_main);
615+ LOCK (m_node.mempool ->cs );
616+
617+ TestBasicMining (chainparams, scriptPubKey, txFirst, baseheight);
534618 }
535619
536620 // Mine an empty block
@@ -554,6 +638,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
554638 LOCK2 (cs_main, m_node.mempool ->cs );
555639 TestPackageSelection (chainparams, scriptPubKey, txFirst);
556640
641+ m_node.chainman ->ActiveChain ().Tip ()->nHeight --;
642+ SetMockTime (0 );
643+ m_node.mempool ->clear ();
644+
645+ TestPrioritisedMining (chainparams, scriptPubKey, txFirst);
646+
557647 fCheckpointsEnabled = true ;
558648}
559649
0 commit comments