Nobody sets Bitcoin's block rate. There is no scheduler and no feed telling the network how much hardware is plugged in. What exists is a number in every block header, a rule for recomputing it every 2,016 blocks, and the timestamps miners wrote themselves. This is that mechanism in the terms the code uses, quoting Bitcoin Core v31.1 throughout. If you have not met proof of work, what miners do is the version without the byte layout.
What a miner is actually searching for
A block header is 80 bytes: version, the hash of the previous
header, the merkle root of
the transactions, a timestamp, a four-byte field called nBits, and a 32-bit nonce.
Mining is hashing that header twice with SHA-256, reading the result as a 256-bit number,
and asking whether it is at or below the target encoded in nBits. If it is not, the
miner changes something (the nonce, the extra nonce in the coinbase, the timestamp, the
transaction set) and hashes again. There is no partial credit and no way to steer the
search, which is why the count of attempts is a measurable, purchasable quantity.
The check itself is four lines: CheckProofOfWorkImpl in
src/pow.cpp derives
the target from nBits, compares, returns. nBits is a compact encoding of one exponent
byte and three mantissa bytes, so the target carries only 24 bits of precision. A coarse
dial, and consensus checks nothing else about the work.
Difficulty is a convenience number
"Difficulty" appears nowhere in that check. It is a ratio computed for human consumption, and
GetDifficulty
lives in the RPC layer rather than in consensus code. It divides the difficulty-1 target,
which is what the compact form 0x1d00ffff decodes back to, by the current one.
That value is close to but not the same as mainnet's powLimit, declared in
chainparams.cpp
as 0x00000000ffffffff...ffff, because the compact encoding truncates the mantissa. A
difficulty of 127.48 trillion, where the network sat on 20 August 2026 according to
mempool.space, means the target in use was that many times
smaller than the ceiling. No node stores difficulty as consensus state, and every rule
below is written against the target.
The retarget: every 2,016 blocks, by at most a factor of four
Two constants set the pace, both in chainparams.cpp
(lines 96 to 98):
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // 1,209,600 seconds
consensus.nPowTargetSpacing = 10 * 60; // 600 seconds
The retarget interval is not a third constant.
DifficultyAdjustmentInterval()
is the first divided by the second: 2,016. Between retargets nothing moves:
GetNextWorkRequired returns the previous block's nBits unchanged unless the height
being built is a multiple of 2,016, so every block in a period is mined against an
identical target.
At a boundary the node measures how long the previous period actually took, and scales:
new_target = old_target * nActualTimespan / nPowTargetTimespan
Slower than expected means a larger nActualTimespan, a larger target, and easier blocks.
Faster means the reverse. Before that multiplication the measured span is clamped
(src/pow.cpp):
if (nActualTimespan < params.nPowTargetTimespan/4)
nActualTimespan = params.nPowTargetTimespan/4;
if (nActualTimespan > params.nPowTargetTimespan*4)
nActualTimespan = params.nPowTargetTimespan*4;
One retarget can move the target by a factor of four and no further, in either direction. This is routinely left out, and it decides how a shock plays out. Suppose nine tenths of the hashrate vanished overnight. Blocks would take ten times as long, so the period would run about 140 days instead of 14. The clamp caps the correction at four, which leaves the interval at roughly 25 minutes, still two and a half times target. Only the next period, another 35 days or so, finishes the job. The bound protects the chain from a wildly wrong or manipulated timespan, and pays for it by making a genuine collapse take two retargets to absorb.
The off-by-one that cannot be fixed
Finding the start of the period looks like this (src/pow.cpp):
// Go back by what we want to be 14 days worth of blocks
int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
pindexLast is the last block of the period just ending. Going back 2,015 blocks lands on
the first block of that same period, which is right as an index and wrong as a duration:
the gap between two blocks 2,015 apart contains 2,015 intervals, not 2,016. That figure is
then divided by 1,209,600 seconds, what 2,016 intervals were meant to take.
Work out the fixed point. With steady hashrate and honest timestamps the retarget stops
moving when nActualTimespan equals 1,209,600, and that span covers 2,015 intervals. So
the interval the code converges on is 1,209,600 / 2,015, which is 600.2978 seconds: ten
minutes and about three tenths of a second. The bias runs slow, not fast, by roughly 0.05
percent, and each two-week period overruns by about ten minutes, which is precisely the one
interval that was left out. No published source states this figure, so check the
arithmetic rather than taking it.
That is an idealised steady state, not a measurement. The mean interval from the genesis block to height 963,283 on 20 August 2026 works out at 577 seconds, nine minutes and 37 seconds. That says nothing about the off-by-one. It says hashrate has trended upward for seventeen years, so blocks inside a period keep arriving faster than the target set at its start, and a 0.05 percent bias the other way vanishes underneath it.
The drift is the harmless half. What matters is that the measurement windows do not overlap. A window runs from the first block of a period to the last, and the next starts at the block after that, so the interval between two periods is measured by nobody. A wrong or dishonest timestamp on the block that closes a window is never subtracted back out by the following one. That gap is the opening the time warp attack works through.
Fixing the off-by-one would take a hard fork, and the reason is one line in validation.cpp:
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.Invalid(..., "bad-diffbits", "incorrect proof of work");
A header's nBits must equal, exactly, what the validating node computes. There is no
close enough, and no direction that is more permissive, so changing the formula either way
means upgraded and non-upgraded nodes reject each other's blocks. A chain split, for 0.05
percent. Nobody has proposed paying that.
Timestamps are not a clock
The whole adjustment runs on numbers miners write into their own headers. Two consensus
rules in ContextualCheckBlockHeader
constrain them, and neither is as tight as readers assume.
A timestamp must be strictly greater than the median time past, the median of the previous eleven block timestamps (chain.h). Because that is a median of eleven rather than a comparison against the parent, a block's timestamp can legitimately be earlier than its parent's. Block timestamps are not monotonic, and code that assumes they are is wrong.
A timestamp must also be no more than MAX_FUTURE_BLOCK_TIME, defined as 2 * 60 * 60
(chain.h), ahead of
the validating node's own clock, not a consensus one: Bitcoin Core 27.0
removed network-adjusted time from consensus code,
so an operator whose clock drifts far enough falls out of consensus by themselves. Under
ordinary spacing the median time past sits about five blocks back, roughly fifty minutes
behind the tip, so the legal band for a new timestamp is close to three hours wide.
The retarget reads raw header timestamps, not median time past. So the miner of the last
block of a period, and the miner of the first, each hold a couple of hours of latitude over
a quantity nominally equal to 1,209,600 seconds. Two hours in 336 is about 0.6 percent at
each end: real, bounded, and not worth much once. Doing it every period is the time warp
attack, which needs sustained majority hashrate in public. The countermeasure exists: a
rule capping how far a retarget block's timestamp may fall behind its predecessor, at 600
seconds on testnet4 under BIP 94
and at 7,200 seconds in the mainnet proposal,
BIP 54. Neither applies here.
BIP 54 carries no activation parameters, and
chainparams.cpp
sets enforce_BIP94 = false for mainnet. And BIP 54 would not close the off-by-one: its
second rule is itself written in terms of the block 2,015 back.
When the hashrate leaves, the network waits
When China ordered its miners to stop in 2021 and roughly half the network's hashrate went dark, blocks slowed and stayed slow for weeks. On 3 July 2021 the difficulty fell 27.94 percent, the largest downward move on record, and the interval came back.
There was no faster path. The target is constant for 2,016 blocks by construction, the only input is elapsed timestamps, and the only moment they are read is the boundary. Reacting sooner would mean sampling shorter windows, and short windows are cheaper to move with exactly the latitude described above. Bitcoin buys robustness by refusing to be responsive, and the price is paid in confirmation times by whoever is transacting during the gap. It is uncomfortable, and it is the system working.
What it does not do
- It does not target a price. No price enters the calculation. The causation runs the other way: price moves hashrate, hashrate moves the next retarget.
- It does not respond to demand for block space. A full mempool changes fees, not difficulty.
- It does not adjust continuously. One step every 2,016 blocks, then flat.
- It does not keep a calendar. Halvings fire on block height, so a slow chain reaches them later in wall-clock terms and mints exactly the same number of coins.
- It does not guarantee ten minutes. It targets the mean of a memoryless process. Even in perfect equilibrium the intervals are exponentially distributed: about 63 percent of blocks arrive within ten minutes of the last, and roughly one in 400 takes over an hour. A single slow block is evidence of nothing.
The whole apparatus is two timestamps, one division and a clamp, evaluated identically by every node. Everything Bitcoin does about a hashrate shock, it does at that boundary and nowhere else.
