@@ -38,6 +38,7 @@ namespace 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);
4040 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);
4142 bool TestSequenceLocks (const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
4243 {
4344 CCoinsViewMemPool view_mempool (&m_node.chainman ->ActiveChainstate ().CoinsTip (), *m_node.mempool );
@@ -480,6 +481,81 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
480481 }
481482}
482483
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+
483559// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
484560BOOST_AUTO_TEST_CASE (CreateNewBlock_validity)
485561{
@@ -562,6 +638,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
562638 LOCK2 (cs_main, m_node.mempool ->cs );
563639 TestPackageSelection (chainparams, scriptPubKey, txFirst);
564640
641+ m_node.chainman ->ActiveChain ().Tip ()->nHeight --;
642+ SetMockTime (0 );
643+ m_node.mempool ->clear ();
644+
645+ TestPrioritisedMining (chainparams, scriptPubKey, txFirst);
646+
565647 fCheckpointsEnabled = true ;
566648}
567649
0 commit comments