A precise walk‑through of Parallax’s difficulty retargeting (XHash) and the Nakamoto fork‑choice rule based on cumulative work.
| Parameter | Symbol | Value | Notes |
|---|---|---|---|
| Target block interval | τ | 600 s | Bitcoin‑like target |
| Future time drift bound | — | 300 s | 5 minutes |
| MTP sample size | — | 11 blocks | Median of last 11 timestamps |
| Retarget interval | R | 2016 blocks | Bitcoin-like retarget |
// Given difficulty D (big integer)
TWO256M1 = (1n << 256n) - 1n
target = TWO256M1 / D
valid = BigIntFromBytes(result) <= target
// Prepare (when building a block)
if height % R == 0:
header.EpochStartTime = header.Time
else:
header.EpochStartTime = parent.EpochStartTime
header.Difficulty = CalcNakamotoDifficulty(config, parent)
// Verify (when receiving a header)
if height % R == 0:
require(header.EpochStartTime == header.Time)
else:
require(header.EpochStartTime == parent.EpochStartTime)
MTP(n):
ts = timestamps(n, upTo=11) // back from n inclusive
sort(ts)
return ts[len(ts)//2]
Work(block):
target = TWO256M1 / Difficulty(block)
// Use an approximation that preserves ordering; e.g.,
return TWO256M1 / (target + 1)
SelectBest(tips):
return argmax(tips, ChainWork[tip])
OnNewTip(candidate):
if ChainWork[candidate] > ChainWork[currentTip]:
currentTip = candidate
ReorgTo(candidate)
ValidHeader(h, parent):
require(len(h.Extra) <= MaximumExtraDataSize)
require(h.Time <= now() + 300)
require(h.Time > MTP(parent))
require(h.Difficulty > 0)
// epoch anchor invariants per height % R
// PoW: MixDigest match & XHash(h) <= targetFrom(D)
Headers are checked for Extra size, time (MTP & future drift), epoch‑anchor invariants, exact difficulty, gas limits/EIPs, height increment, and PoW seal. Valid blocks extend ChainWork, and the heaviest tip is canonical.