[test] check miner doesn't select 0fee transactions
What changed, and why it matters
This commit only adds a new test to Bitcoin Core's test suite. It checks that when miners set a minimum transaction fee above zero, the block template does not include completely free (zero-fee) transactions. There is no change to production code, no bug fix, and no security patch.
No action required. Review as ordinary test coverage improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies test/functional/mining_basic.py to expand the blockmintxfee test matrix and add an assertion verifying that every transaction in a getblocktemplate result pays a positive base fee whenever -blockmintxfee is greater than zero. It also uses confirmed_only=True for self-transfers to avoid spending unconfirmed outputs. This is a regression test strengthening existing behavior, not a code change to the miner or mempool.
Changed components
test/functional/mining_basic.pyInspect captured patch +14 / −5
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index a4e482f6..6707f4b0 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -39,6 +39,7 @@ from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than,
assert_greater_than_or_equal,
assert_raises_rpc_error,
get_fee,
@@ -143,7 +144,7 @@ class MiningTest(BitcoinTestFramework):
node = self.nodes[0]
# test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
- for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
+ for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 1, 5, 10, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
if blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
@@ -154,19 +155,27 @@ class MiningTest(BitcoinTestFramework):
self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart
# submit one tx with exactly the blockmintxfee rate, and one slightly below
- tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
+ tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
- if blockmintxfee_btc_kvb > 0:
+ if blockmintxfee_sat_kvb > 5:
lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN # 0.01 sat/vbyte lower
- tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb)
+ tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb, confirmed_only=True)
assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
else: # go below zero fee by using modified fees
- tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
+ tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1)
# check that tx below specified fee-rate is neither in template nor in the actual block
block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
block_template_txids = [tx['txid'] for tx in block_template['transactions']]
+
+ # Unless blockmintxfee is 0, the template shouldn't contain free transactions.
+ # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate.
+ if blockmintxfee_btc_kvb > 0:
+ for txid in block_template_txids:
+ tx = node.getmempoolentry(txid)
+ assert_greater_than(tx['fees']['base'], 0)
+
self.generate(self.wallet, 1, sync_fun=self.no_op)
block = node.getblock(node.getbestblockhash(), verbosity=2)
block_txids = [tx['txid'] for tx in block['tx']]
Why this scored 14/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.