Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251Txid hashLowFeeTx = tx.GetHash();252TryAddToMempool(tx_mempool, entry.Fee(feeToUse).FromTx(tx));254// waitNext() should return nullptr because there is no better template255should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1});256BOOST_REQUIRE(should_be_nullptr == nullptr);258block = block_template->getBlock();259// Verify that the free tx and the low fee tx didn't get selected260for (size_t i=0; i<block.vtx.size(); ++i) {261BOOST_CHECK(block.vtx[i]->GetHash() != hashFreeTx);262BOOST_CHECK(block.vtx[i]->GetHash() != hashLowFeeTx);263}265// Test that packages above the min relay fee do get included, even if one266// of the transactions is below the min relay fee267// Remove the low fee transaction and replace with a higher fee transaction268tx_mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);269tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee270hashLowFeeTx = tx.GetHash();271TryAddToMempool(tx_mempool, entry.Fee(feeToUse + 2).FromTx(tx));273// waitNext() should return if fees for the new template are at least 1 sat up274block_template = block_template->waitNext({.fee_threshold = 1});275BOOST_REQUIRE(block_template);276block = block_template->getBlock();277BOOST_REQUIRE_EQUAL(block.vtx.size(), 6U);278BOOST_CHECK(block.vtx[4]->GetHash() == hashFreeTx);279BOOST_CHECK(block.vtx[5]->GetHash() == hashLowFeeTx);281// Test that transaction selection properly updates ancestor fee282// calculations as ancestor transactions get included in a block.283// Add a 0-fee transaction that has 2 outputs.284tx.vin[0].prevout.hash = txFirst[2]->GetHash();285tx.vout.resize(2);286tx.vout[0].nValue = 5000000000LL - 100000000;287tx.vout[1].nValue = 100000000; // 1BTC output288// Increase size to avoid rounding errors: when the feerate is extremely small (i.e. 1sat/kvB), evaluating the fee289// at smaller sizes gives us rounded values that are equal to each other, which means we incorrectly include290// hashFreeTx2 + hashLowFeeTx2.291BulkTransaction(tx, 4000);292Txid hashFreeTx2 = tx.GetHash();293TryAddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(tx));295// This tx can't be mined by itself296tx.vin[0].prevout.hash = hashFreeTx2;297tx.vout.resize(1);298feeToUse = blockMinFeeRate.GetFee(freeTxSize);299tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;300Txid hashLowFeeTx2 = tx.GetHash();301TryAddToMempool(tx_mempool, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));302block_template = mining->createNewBlock(options, /*cooldown=*/false);303BOOST_REQUIRE(block_template);304block = block_template->getBlock();306// Verify that this tx isn't selected.307for (size_t i=0; i<block.vtx.size(); ++i) {308BOOST_CHECK(block.vtx[i]->GetHash() != hashFreeTx2);309BOOST_CHECK(block.vtx[i]->GetHash() != hashLowFeeTx2);310}312// This tx will be mineable, and should cause hashLowFeeTx2 to be selected313// as well.314tx.vin[0].prevout.n = 1;315tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee316TryAddToMempool(tx_mempool, entry.Fee(10000).FromTx(tx));317block_template = mining->createNewBlock(options, /*cooldown=*/false);318BOOST_REQUIRE(block_template);319block = block_template->getBlock();320BOOST_REQUIRE_EQUAL(block.vtx.size(), 9U);321BOOST_CHECK(block.vtx[8]->GetHash() == hashLowFeeTx2);322}324// One sigop-dense tx spending `input`: num_outputs bare CHECKMULTISIG outputs,325// i.e. 20 * num_outputs legacy sigops (OP_NOP forces the inaccurate max-20 count).326static CMutableTransaction CreateBigSigOpsTx(const COutPoint& input, unsigned int num_outputs)327{328CMutableTransaction tx;329tx.vin.resize(1);330tx.vin[0].prevout = input;331tx.vin[0].scriptSig = CScript() << OP_1;332tx.vout.resize(num_outputs);333for (auto& out : tx.vout) {334out.nValue = 0;335out.scriptPubKey = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP << OP_CHECKMULTISIG << OP_1;336}337return tx;338}340std::vector<CTransactionRef> CreateBigSigOpsCluster(const CTransactionRef& first_tx)341{342std::vector<CTransactionRef> ret;344CMutableTransaction tx;345// block sigops > limit: 1000 CHECKMULTISIG + 1346tx.vin.resize(1);347// NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG348tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_CHECKSIG << OP_1;349tx.vin[0].prevout.hash = first_tx->GetHash();350tx.vin[0].prevout.n = 0;351tx.vout.resize(50);352for (auto &out : tx.vout) {353out.nValue = first_tx->vout[0].nValue / 50;354out.scriptPubKey = CScript() << OP_1;355}357tx.vout[0].nValue -= CENT;358CTransactionRef parent_tx = MakeTransactionRef(tx);359ret.push_back(parent_tx);360assert(GetLegacySigOpCount(*parent_tx) == 1);362// Tx1 has 1 sigops, 1 input, 50 outputs.363// Tx2-51 has 400 sigops: 1 input, 20 CHECKMULTISIG outputs364// Total: 1000 CHECKMULTISIG + 1365for (unsigned int i = 0; i < 50; ++i) {366ret.push_back(MakeTransactionRef(CreateBigSigOpsTx(COutPoint{parent_tx->GetHash(), i}, /*num_outputs=*/20)));367}368return ret;369}371void MinerTestingSetup::TestSigOpsAdjustedWeightChunkLimit(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)372{373auto mining{MakeMining()};374BOOST_REQUIRE(mining);376CTxMemPool& tx_mempool{MakeMempool()};377LOCK(tx_mempool.cs);378TestMemPoolEntryHelper entry;380const auto tx{CreateBigSigOpsTx(COutPoint{txFirst[0]->GetHash(), 0}, /*num_outputs=*/50)};381const auto sigop_entry{entry.Fee(COIN).SpendsCoinbase(true).SigOpsCost(GetLegacySigOpCount(CTransaction(tx)) * WITNESS_SCALE_FACTOR).FromTx(tx)};382BOOST_REQUIRE(sigop_entry.GetAdjustedWeight() > sigop_entry.GetTxWeight());383BOOST_REQUIRE(sigop_entry.GetSigOpCost() < MAX_BLOCK_SIGOPS_COST);384TryAddToMempool(tx_mempool, sigop_entry);386BlockCreateOptions options{387// +1 because TestChunkBlockLimits rejects on >= (exact fit doesn't count).388.block_max_weight = DEFAULT_BLOCK_RESERVED_WEIGHT + sigop_entry.GetTxWeight() + 1,389.coinbase_output_script = scriptPubKey,390};391const CBlock block{mining->createNewBlock(options, /*cooldown=*/false)->getBlock()};392BOOST_CHECK_EQUAL(block.vtx.size(), 2U);393BOOST_CHECK(block.vtx[1]->GetHash() == tx.GetHash());394}396void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight)397{398FakeNodeClock clock{};400Txid hash;401CMutableTransaction tx;402TestMemPoolEntryHelper entry;403entry.nFee = 11;404entry.nHeight = 11;406const CAmount BLOCKSUBSIDY = 50 * COIN;407const CAmount LOWFEE = CENT;408const CAmount HIGHFEE = COIN;409const CAmount HIGHERFEE = 4 * COIN;411auto mining{MakeMining()};412BOOST_REQUIRE(mining);414BlockCreateOptions options{415.coinbase_output_script = scriptPubKey,416};418{419CTxMemPool& tx_mempool{MakeMempool()};420LOCK(tx_mempool.cs);422// Just to make sure we can still make simple blocks423auto block_template{mining->createNewBlock(options, /*cooldown=*/false)};424BOOST_REQUIRE(block_template);425CBlock block{block_template->getBlock()};427auto txs = CreateBigSigOpsCluster(txFirst[0]);429int64_t legacy_sigops = 0;430for (auto& t : txs) {431// If we don't set the number of sigops in the CTxMemPoolEntry,432// template creation fails during sanity checks.433TryAddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(t));434legacy_sigops += GetLegacySigOpCount(*t);435BOOST_CHECK(tx_mempool.GetIter(t->GetHash()).has_value());436}437assert(tx_mempool.mapTx.size() == 51);438assert(legacy_sigops == 20001);439BOOST_CHECK_EXCEPTION(mining->createNewBlock(options, /*cooldown=*/false), std::runtime_error, HasReason("bad-blk-sigops"));440}442{443CTxMemPool& tx_mempool{MakeMempool()};444LOCK(tx_mempool.cs);446// Check that the mempool is empty.447assert(tx_mempool.mapTx.empty());449// Just to make sure we can still make simple blocks450auto block_template{mining->createNewBlock(options, /*cooldown=*/false)};451BOOST_REQUIRE(block_template);452CBlock block{block_template->getBlock()};454auto txs = CreateBigSigOpsCluster(txFirst[0]);456int64_t legacy_sigops = 0;457for (auto& t : txs) {458TryAddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(true).SigOpsCost(GetLegacySigOpCount(*t)*WITNESS_SCALE_FACTOR).FromTx(t));459legacy_sigops += GetLegacySigOpCount(*t);460BOOST_CHECK(tx_mempool.GetIter(t->GetHash()).has_value());461}462assert(tx_mempool.mapTx.size() == 51);463assert(legacy_sigops == 20001);465BOOST_REQUIRE(mining->createNewBlock(options, /*cooldown=*/false));466}468{469CTxMemPool& tx_mempool{MakeMempool()};470LOCK(tx_mempool.cs);472// block size > limit473tx.vin.resize(1);474tx.vout.resize(1);475tx.vout[0].nValue = BLOCKSUBSIDY;476// 36 * (520char + DROP) + OP_1 = 18757 bytes477std::vector<unsigned char> vchData(520);478for (unsigned int i = 0; i < 18; ++i) {479tx.vin[0].scriptSig << vchData << OP_DROP;480tx.vout[0].scriptPubKey << vchData << OP_DROP;481}482tx.vin[0].scriptSig << OP_1;483tx.vout[0].scriptPubKey << OP_1;484tx.vin[0].prevout.hash = txFirst[0]->GetHash();485tx.vin[0].prevout.n = 0;486tx.vout[0].nValue = BLOCKSUBSIDY;487for (unsigned int i = 0; i < 63; ++i) {488tx.vout[0].nValue -= LOWFEE;489hash = tx.GetHash();490bool spendsCoinbase = i == 0; // only first tx spends coinbase491TryAddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now<NodeSeconds>()).SpendsCoinbase(spendsCoinbase).FromTx(tx));492BOOST_CHECK(tx_mempool.GetIter(hash).has_value());493tx.vin[0].prevout.hash = hash;494}495BOOST_REQUIRE(mining->createNewBlock(options, /*cooldown=*/false));496}498{499CTxMemPool& tx_mempool{MakeMempool()};500LOCK(tx_mempool.cs);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.