Skip to content

Testnet difficulty adjustment: future-dated minimum-difficulty blocks crash difficulty (~12.7 s block time) #1321

Description

@daira

Summary

On Testnet, the ZIP 205 minimum-difficulty rule interacts with difficulty adjustment so that difficulty is pinned far below equilibrium: measured mean block time is around 12.7 s against the 75 s target (around 5.9× too fast). Two things combine:

(a) the minimum-difficulty trigger is driven almost entirely by future-dated timestamps, not real pauses; and
(b) because the averaging is over targets, each such block crashes difficulty rather than nudging it.

This makes Testnet block production erratic, and the estimation of NU activation heights hard.

Testnet-only (the minimum-difficulty rule does not apply to Mainnet), but it degrades Testnet usability.

Measured evidence (last 48 h of Testnet, 13650 blocks, heights 4105654–4119303)

  • mean block time 12.67 s (median gap 7 s; p90 = 22 s; p99 = 124 s) vs 75 s target → 5.92× too fast.
  • 41 minimum-difficulty blocks (0.30%). Classified: 40 future-dated timestamp artifacts, 0 real pauses, 1 ambiguous. The artifacts have gap_prev ≈ 451 s (just over the 450 s threshold), gap_next negative (~−130 s, i.e. the next block's nTime steps back), and sit ~475–950 s ahead of median-time-past.
  • The near-constant gap_prev = 451 s suggests these blocks are future-dated just past the threshold (for an unknown reason), resulting in minimum-difficulty (cheap) blocks.
  • 223 blocks are timestamped > 450 s ahead of median-time-past (max future-skew 949 s; validity bound is MTP + 90 min = 5400 s) — each such block can create a spurious trigger.
  • Difficulty sawtooths: min 1, median 242, max 2.28e6 (equilibrium for 75 s would be ~1400), pinned at 1 for the 41 minimum-difficulty blocks.

Note the count (41) is only ~1.2× the naive Poisson-tail estimate e^(−6)·13650 ≈ 34, so a count comparison alone looks unremarkable — but the classification shows these are not steady-state tail events at all; they are timestamp artifacts. That is why "the tail of the Poisson distribution" does not explain the behaviour.

Mechanism

From the Protocol Specification "Difficulty adjustment", with ZIP 205 and ZIP 208 (post-Blossom threshold):

  • A block N is a minimum-difficulty block iff nTime(N) − nTime(N−1) > 6 × PoWTargetSpacing (post-Blossom = 450 s), using raw consecutive timestamps (not median-time-past), which need only satisfy nTime > MTP and nTime ≤ MTP + 90 min and need not be monotonic. Its nBits is set to ToCompact(PoWLimit) (Testnet PoWLimit = 2^251 − 1).
  • MeanTarget(N) is the arithmetic mean of ToTarget(nBits(i)) over the trailing PoWAveragingWindow = 17 blocks, including minimum-difficulty blocks.

Because the average is over targets, one minimum-difficulty block dominates the mean. With 16 normal targets R and one PoWLimit:

MeanTarget ≈ (16·R + PoWLimit) / 17 ≈ PoWLimit / 17     (since PoWLimit ≫ R)

so the next target jumps from ≈ R to ≈ PoWLimit/17, and since difficulty ∝ 1/target, difficulty collapses from D to ≈ 17 (≈ PoWAveragingWindow) in one block — a factor (16 + D)/17 ≈ D/17. This is specific to averaging targets: because PoWLimit is ~D× a normal target, a single minimum-difficulty entry dominates the arithmetic mean. Averaging over difficulties or log-targets, or bounding the per-block change of MeanTarget, would avoid it.

The per-block ±16% / −32% limit does not prevent this: it bounds only the ActualTimespanBounded (median-time) factor, not the MeanTarget change. The minimum-difficulty block then persists in the window for 17 blocks, and recovery is bounded by the 16%-per-block factor, so returning to equilibrium D takes ~17 + log₁.₁₉(D/17) ≈ tens of uninterrupted blocks — during which the chain runs fast.

Proposed direction

To be specified in draft-valargroup-blocktime-reduction.md:

  • Trigger side: the trigger on raw consecutive nTime, with a 90-minute future allowance and no monotonicity requirement, is trivially exploitable (or can be triggered accidentally) to cause minimum-difficulty blocks. Consider triggering on median-time-past rather than raw nTime, and/or tightening the future-timestamp allowance, and/or requiring the gap to be robust to non-monotonic timestamps.
  • Averaging side: so that a trigger does not crash difficulty — exclude minimum-difficulty blocks' nBits from MeanTarget (e.g. carry forward the last non-minimum-difficulty target, cf. Bitcoin testnet's reset-to-prior behaviour), and/or average over difficulties / log-targets, and/or bound the per-block MeanTarget change.

🤖 Claude Opus 4.8

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions