src/test/miner_tests.cpp
void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
{
    // Test the ancestor feerate transaction selection.
    TestMemPoolEntryHelper entry;

    // Test that a medium fee transaction will be selected after a higher fee
    // rate package with a low fee rate parent.
    CMutableTransaction tx;
    tx.vin.resize(1);
    tx.vin[0].scriptSig = CScript() << OP_1;
    tx.vin[0].prevout.hash = txFirst[0]->GetHash();
    tx.vin[0].prevout.n = 0;
    tx.vout.resize(1);
    tx.vout[0].nValue = 5000000000LL - 400000;
    // This tx has a low fee: 1000 satoshis
    uint256 hashParentTx = tx.GetHash(); // save this txid for later use
    m_node.mempool->addUnchecked(entry.Fee(400000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));

    // This tx has a medium fee: 10000 satoshis
    tx.vin[0].prevout.hash = txFirst[1]->GetHash();
    tx.vout[0].nValue = 5000000000LL - 4000000;
    uint256 hashMediumFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(4000000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));

    // This tx has a high fee, but depends on the first transaction
    tx.vin[0].prevout.hash = hashParentTx;
    tx.vout[0].nValue = 5000000000LL - 10000 - 400000 * 50; // 50k satoshi fee
    uint256 hashHighFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(400000 * 50).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));

    std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
    BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
    BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);

    // Test that a package below the block min tx fee doesn't get included
    tx.vin[0].prevout.hash = hashHighFeeTx;
    tx.vout[0].nValue = 5000000000LL - 10000 - 20000000; // 0 fee
    uint256 hashFreeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx));
    size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);

    // Calculate a fee on child transaction that will put the package just
    // below the block min tx fee (assuming 1 child tx of the same size).
    CAmount feeToUse = blockMinFeeRate.GetFee(2*freeTxSize) - 1;

    tx.vin[0].prevout.hash = hashFreeTx;
    tx.vout[0].nValue = 5000000000LL - 10000 - 20000000 - feeToUse;
    uint256 hashLowFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    // Verify that the free tx and the low fee tx didn't get selected
    for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx);
    }

    // Test that packages above the min relay fee do get included, even if one
    // of the transactions is below the min relay fee
    // Remove the low fee transaction and replace with a higher fee transaction
    m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
    tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
    hashLowFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
    BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);

    // Test that transaction selection properly updates ancestor fee
    // calculations as ancestor transactions get included in a block.
    // Add a 0-fee transaction that has 2 outputs.
    tx.vin[0].prevout.hash = txFirst[2]->GetHash();
    tx.vout.resize(2);
    tx.vout[0].nValue = 5000000000LL - 100000000;
    tx.vout[1].nValue = 100000000; // 1BTC output
    uint256 hashFreeTx2 = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));

    // This tx can't be mined by itself
    tx.vin[0].prevout.hash = hashFreeTx2;
    tx.vout.resize(1);
    feeToUse = blockMinFeeRate.GetFee(freeTxSize);
    tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
    uint256 hashLowFeeTx2 = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);

    // Verify that this tx isn't selected.
    for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx2);
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx2);
    }

    // This tx will be mineable, and should cause hashLowFeeTx2 to be selected
    // as well.
    tx.vin[0].prevout.n = 1;
    tx.vout[0].nValue = 100000000 - 4000000; // 10k satoshi fee
    m_node.mempool->addUnchecked(entry.Fee(4000000).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
Test suite for ancestor feerate transaction selection.
Implemented as an additional function, rather than a separate test case,
to allow reusing the blockchain created in CreateNewBlock_validity.
Test suite for ancestor feerate transaction selection . Implemented as an additional function rather than a separate test case to allow reusing the blockchain created in CreateNewBlock_validity .
void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
{
    TestMemPoolEntryHelper entry;
    CMutableTransaction tx;
    tx.vin.resize(1);
    tx.vin[0].scriptSig = CScript() << OP_1;
    tx.vin[0].prevout.hash = txFirst[0]->GetHash();
    tx.vin[0].prevout.n = 0;
    tx.vout.resize(1);
    tx.vout[0].nValue = 5000000000LL - 400000;
    uint256 hashParentTx = tx.GetHash(); 
    m_node.mempool->addUnchecked(entry.Fee(400000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    tx.vin[0].prevout.hash = txFirst[1]->GetHash();
    tx.vout[0].nValue = 5000000000LL - 4000000;
    uint256 hashMediumFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(4000000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    tx.vin[0].prevout.hash = hashParentTx;
    tx.vout[0].nValue = 5000000000LL - 10000 - 400000 * 50; 
    uint256 hashHighFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(400000 * 50).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
    std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
    BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
    BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);
    tx.vin[0].prevout.hash = hashHighFeeTx;
    tx.vout[0].nValue = 5000000000LL - 10000 - 20000000; 
    uint256 hashFreeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx));
    size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);
    CAmount feeToUse = blockMinFeeRate.GetFee(2*freeTxSize) - 1;
    tx.vin[0].prevout.hash = hashFreeTx;
    tx.vout[0].nValue = 5000000000LL - 10000 - 20000000 - feeToUse;
    uint256 hashLowFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx);
    }
    m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
    tx.vout[0].nValue -= 2; 
    hashLowFeeTx = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
    BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
    tx.vin[0].prevout.hash = txFirst[2]->GetHash();
    tx.vout.resize(2);
    tx.vout[0].nValue = 5000000000LL - 100000000;
    tx.vout[1].nValue = 100000000; 
    uint256 hashFreeTx2 = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
    tx.vin[0].prevout.hash = hashFreeTx2;
    tx.vout.resize(1);
    feeToUse = blockMinFeeRate.GetFee(freeTxSize);
    tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
    uint256 hashLowFeeTx2 = tx.GetHash();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx2);
        BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx2);
    }
    tx.vin[0].prevout.n = 1;
    tx.vout[0].nValue = 100000000 - 4000000; 
    m_node.mempool->addUnchecked(entry.Fee(4000000).FromTx(tx));
    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
void MinerTestingSetup :: TestPackageSelection ( const CChainParams & chainparams , const CScript & scriptPubKey , const std :: vector < CTransactionRef > & txFirst ) { TestMemPoolEntryHelper entry ; CMutableTransaction tx ; tx . vin . resize ( 1 ) ; tx . vin [ 0 ] . scriptSig = CScript ( ) << OP_1 ; tx . vin [ 0 ] . prevout . hash = txFirst [ 0 ] -> GetHash ( ) ; tx . vin [ 0 ] . prevout . n = 0 ; tx . vout . resize ( 1 ) ; tx . vout [ 0 ] . nValue = 5000000000LL - 400000 ; uint256 hashParentTx = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( 400000 ) . Time ( GetTime ( ) ) . SpendsCoinbase ( true ) . FromTx ( tx ) ) ; tx . vin [ 0 ] . prevout . hash = txFirst [ 1 ] -> GetHash ( ) ; tx . vout [ 0 ] . nValue = 5000000000LL - 4000000 ; uint256 hashMediumFeeTx = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( 4000000 ) . Time ( GetTime ( ) ) . SpendsCoinbase ( true ) . FromTx ( tx ) ) ; tx . vin [ 0 ] . prevout . hash = hashParentTx ; tx . vout [ 0 ] . nValue = 5000000000LL - 10000 - 400000 * 50 ; uint256 hashHighFeeTx = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( 400000 * 50 ) . Time ( GetTime ( ) ) . SpendsCoinbase ( false ) . FromTx ( tx ) ) ; std :: unique_ptr < CBlockTemplate > pblocktemplate = AssemblerForTest ( chainparams ) . CreateNewBlock ( scriptPubKey ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ 1 ] -> GetHash ( ) == hashParentTx ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ 2 ] -> GetHash ( ) == hashHighFeeTx ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ 3 ] -> GetHash ( ) == hashMediumFeeTx ) ; tx . vin [ 0 ] . prevout . hash = hashHighFeeTx ; tx . vout [ 0 ] . nValue = 5000000000LL - 10000 - 20000000 ; uint256 hashFreeTx = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( 0 ) . FromTx ( tx ) ) ; size_t freeTxSize = :: GetSerializeSize ( tx , PROTOCOL_VERSION ) ; CAmount feeToUse = blockMinFeeRate . GetFee ( 2 * freeTxSize ) - 1 ; tx . vin [ 0 ] . prevout . hash = hashFreeTx ; tx . vout [ 0 ] . nValue = 5000000000LL - 10000 - 20000000 - feeToUse ; uint256 hashLowFeeTx = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( feeToUse ) . FromTx ( tx ) ) ; pblocktemplate = AssemblerForTest ( chainparams ) . CreateNewBlock ( scriptPubKey ) ; for ( size_t i = 0 ; i < pblocktemplate -> block . vtx . size ( ) ; ++ i ) { BOOST_CHECK ( pblocktemplate -> block . vtx [ i ] -> GetHash ( ) != hashFreeTx ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ i ] -> GetHash ( ) != hashLowFeeTx ) ; } m_node . mempool -> removeRecursive ( CTransaction ( tx ) , MemPoolRemovalReason :: REPLACED ) ; tx . vout [ 0 ] . nValue -= 2 ; hashLowFeeTx = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( feeToUse + 2 ) . FromTx ( tx ) ) ; pblocktemplate = AssemblerForTest ( chainparams ) . CreateNewBlock ( scriptPubKey ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ 4 ] -> GetHash ( ) == hashFreeTx ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ 5 ] -> GetHash ( ) == hashLowFeeTx ) ; tx . vin [ 0 ] . prevout . hash = txFirst [ 2 ] -> GetHash ( ) ; tx . vout . resize ( 2 ) ; tx . vout [ 0 ] . nValue = 5000000000LL - 100000000 ; tx . vout [ 1 ] . nValue = 100000000 ; uint256 hashFreeTx2 = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( 0 ) . SpendsCoinbase ( true ) . FromTx ( tx ) ) ; tx . vin [ 0 ] . prevout . hash = hashFreeTx2 ; tx . vout . resize ( 1 ) ; feeToUse = blockMinFeeRate . GetFee ( freeTxSize ) ; tx . vout [ 0 ] . nValue = 5000000000LL - 100000000 - feeToUse ; uint256 hashLowFeeTx2 = tx . GetHash ( ) ; m_node . mempool -> addUnchecked ( entry . Fee ( feeToUse ) . SpendsCoinbase ( false ) . FromTx ( tx ) ) ; pblocktemplate = AssemblerForTest ( chainparams ) . CreateNewBlock ( scriptPubKey ) ; for ( size_t i = 0 ; i < pblocktemplate -> block . vtx . size ( ) ; ++ i ) { BOOST_CHECK ( pblocktemplate -> block . vtx [ i ] -> GetHash ( ) != hashFreeTx2 ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ i ] -> GetHash ( ) != hashLowFeeTx2 ) ; } tx . vin [ 0 ] . prevout . n = 1 ; tx . vout [ 0 ] . nValue = 100000000 - 4000000 ; m_node . mempool -> addUnchecked ( entry . Fee ( 4000000 ) . FromTx ( tx ) ) ; pblocktemplate = AssemblerForTest ( chainparams ) . CreateNewBlock ( scriptPubKey ) ; BOOST_CHECK ( pblocktemplate -> block . vtx [ 8 ] -> GetHash ( ) == hashLowFeeTx2 ) ; }
Test suite for ancestor feerate transaction selection.
Test suite for ancestor feerate transaction selection .
// Test the ancestor feerate transaction selection. // Test that a medium fee transaction will be selected after a higher fee // rate package with a low fee rate parent. // This tx has a low fee: 1000 satoshis // save this txid for later use // This tx has a medium fee: 10000 satoshis // This tx has a high fee, but depends on the first transaction // 50k satoshi fee // Test that a package below the block min tx fee doesn't get included // 0 fee // Calculate a fee on child transaction that will put the package just // below the block min tx fee (assuming 1 child tx of the same size). // Verify that the free tx and the low fee tx didn't get selected // Test that packages above the min relay fee do get included, even if one // of the transactions is below the min relay fee // Remove the low fee transaction and replace with a higher fee transaction // Now we should be just over the min relay fee // Test that transaction selection properly updates ancestor fee // calculations as ancestor transactions get included in a block. // Add a 0-fee transaction that has 2 outputs. // 1BTC output // This tx can't be mined by itself // Verify that this tx isn't selected. // This tx will be mineable, and should cause hashLowFeeTx2 to be selected // as well. // 10k satoshi fee