Skip to content

mining: Increase minimum feerate as the block fills up - #338

Open
kwsantiago wants to merge 4 commits into
bitcoinknots:29.x-knotsfrom
privkeyio:block-min-feerate-ramp
Open

mining: Increase minimum feerate as the block fills up#338
kwsantiago wants to merge 4 commits into
bitcoinknots:29.x-knotsfrom
privkeyio:block-min-feerate-ramp

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Jul 19, 2026

Copy link
Copy Markdown

Closes #59.

Satoshi's client raised the fee a transaction had to pay as the block filled up, so a block was only extended when the space was paid for:

// Raise the price as the block approaches full
if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
{
    if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN)
        return MAX_MONEY;
    nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize);
}

This was never deliberately dropped. c555400 (2012) reworked CreateNewBlock to sort the paid area by fee-per-kb, and in the process replaced the size-dependent minimum with a flat one, which is the blockMinFeeRate check still in addPackageTxs today. The ramp survived unused in GetMinFee until 87cce04 (2013) removed it as dead code, correctly so by then: its only remaining caller passed a constant nBlockSize of 1000, far below the threshold that would trigger it.

Behavior

Past -blockmintxfeerampstart percent full, a package's minimum fee is scaled by (limit - ramp_start) / (limit - used), so it is unchanged at the ramp start, grows without bound as the block fills, and reaches MAX_MONEY when full. Fullness is measured against whichever of -blockmaxsize or -blockmaxweight is tighter, since only one of the two may be set.

The default is 50, where the original engaged. Setting it to 100 disables the scaling. It scales -blockmintxfee, so a -blockmintxfee of 0 also disables it. The option is also on the Mining tab, and can be overridden per getblocktemplate request alongside minfeerate.

Two things differ from the original. The multiplier is continuous instead of integer steps that jump straight to 2x at the halfway mark. And fullness excludes the candidate transaction: making the threshold depend on the candidate's own size would break the assumption in addPackageTxs that a package failing the check cannot be followed by one that passes, which is what lets it return instead of scanning the rest of the mempool. The threshold lags by at most one transaction as a result.

Coin-age priority transactions are exempt from -blockmintxfee and are selected first, so they advance the fullness counter and can start the fee pass partway up the ramp, as they did originally. At the defaults the priority area is 100000 of 300000 bytes, below the 50% start, so this only engages if -blockprioritysize is raised past -blockmintxfeerampstart percent of the block. Where it is, free transactions from aged UTXOs raise the floor for fee-paying ones.

The default

Turning the ramp on by default is the part worth arguing about. With -blockmintxfee=1000, a package paying 2 sat/vB is admitted while the block is under 75% full, so a mempool whose marginal transactions sit at that feerate leaves a quarter of the block empty; measured at a full 4M weight block, 75% against 99% with the ramp off. How much that costs depends on the mempool, since selection stops where the falling feerate curve meets the rising threshold: a steep fee distribution is barely affected, a flat low-fee one fully. 50 is what the original used, and shipping it off would leave the issue unaddressed for anyone who does not go looking for the option.

Testing

ScaledBlockMinFee is covered in miner_tests at every ramp value, including across the saturation boundary, asserting that the fee demanded never decreases as the block fills, which is the property the early return relies on. mining_blockmintxfeerampstart.py drives the option end to end against expected transaction counts, covering the size-limited path, -blockprioritysize, CPFP packages priced as whole ancestor sets, the getblocktemplate override, and startup validation, and mines a block to check its contents.

Enabling the ramp by default adds roughly 15% to functional test runtime, since smaller blocks mean the tests mine more of them.

GUI

Mining options tab

@kwsantiago
kwsantiago force-pushed the block-min-feerate-ramp branch from 3c4cb35 to 060a29c Compare July 19, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Block creator: Increase minimum feerate as the block fills up

3 participants