@@ -31,6 +31,7 @@ namespace 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);
3333 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);
3435 bool TestSequenceLocks (const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
3536 {
3637 CCoinsViewMemPool view_mempool (&m_node.chainman ->ActiveChainstate ().CoinsTip (), *m_node.mempool );
@@ -468,6 +469,81 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
468469 BOOST_CHECK_EQUAL (pblocktemplate->block .vtx .size (), 5U );
469470}
470471
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+
471547// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
472548BOOST_AUTO_TEST_CASE (CreateNewBlock_validity)
473549{
@@ -522,6 +598,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
522598
523599 TestPackageSelection (chainparams, scriptPubKey, txFirst);
524600
601+ m_node.chainman ->ActiveChain ().Tip ()->nHeight --;
602+ SetMockTime (0 );
603+ m_node.mempool ->clear ();
604+
605+ TestPrioritisedMining (chainparams, scriptPubKey, txFirst);
606+
525607 fCheckpointsEnabled = true ;
526608}
527609
0 commit comments