Difficulty Algorithm & Fork-choice Rules

A precise walk-through of Parallax's difficulty adjustment history — BTC-style DAA from genesis, the ASERT upgrade at block 17,560 (PIP-0002), and the Nakamoto fork-choice rule based on cumulative work.

Consensus Parameters
ParameterSymbolValueNotes
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
Difficulty algorithm history
BTC-style DAA → ASERT
BTC-style 2016-block windows from genesis–17,559; ASERT (aserti3-2d) for blocks ≥ 17,560 (PIP-0002).
Overview
Parallax targets 10-minute blocks using XHash Proof of Work and a per-block ASERT difficulty algorithm, after an initial BTC-style DAA phase.
  • From genesis up to block 17,559 Parallax used a Bitcoin-style 2016-block difficulty adjustment window (BTC-style DAA).
  • PIP-0002 (Migration from BTC-Style Difficulty Adjustment Algorithm to ASERT) activated ASERT at block 17,560.
  • ASERT (aserti3-2d, as used by Bitcoin Cash) adjusts difficulty every block relative to a fixed anchor, converging smoothly toward the 600 s target under hashrate shocks.
  • Median-Time-Past (MTP, median of last 11) guards timestamp validity; nodes reject headers more than +300s in the future and verify PoW with target = ⌊(2^256−1)/D⌋.
Target ↔ Difficulty
Work threshold mapping used by XHash verification (common to both BTC-DAA and ASERT eras).
  • Let TWO256M1 = 2^256 − 1. The header is valid if XHash(header) ≤ target, where target = ⌊TWO256M1 / D⌋.
  • Higher difficulty D ⇒ smaller target ⇒ harder block.
  • Header.MixDigest must equal the computed digest from hashimoto (light/full paths).
  • Difficulty must be strictly positive; zero/negative is invalid.
Target calculation (conceptual)
pseudocode
// Given difficulty D (big integer)
TWO256M1 = (1n << 256n) - 1n
target   = TWO256M1 / D
valid    = BigIntFromBytes(result) <= target
Difficulty Adjustment: BTC-Style DAA → ASERT
Historical 2016-block windows, modern per-block ASERT relative to an anchor.
  • Genesis → block 17,559: CalcNakamotoDifficulty() implements a Bitcoin-style 2016-block window using epoch anchors.
  • Block 17,560 and later: CalcAsertDifficulty() implements aserti3-2d using a fixed anchor {anchorHeight, anchorParentTime, anchorTarget}.
  • ASERT uses the height offset Δh and elapsed time Δt relative to the anchor to push difficulty toward the 600 s target with a 2-day half-life.
  • Historical blocks keep their original BTC-style DAA difficulty and are still validated under those rules; forward-looking adjustment is entirely ASERT-based.
Height-dependent difficulty selection (conceptual)
pseudocode
// Difficulty selection by era
CalcDifficulty(config, anchor, parent, header):
  if height(header) < 17560:
    // BTC-style DAA: 2016-block window with epoch anchors
    return CalcNakamotoDifficulty(config, parent)
  else:
    // ASERT: per-block retarget relative to fixed anchor
    return CalcAsertDifficulty(config, anchor, parent, header)

// ASERT core idea (fixed-point, aserti3-2d)
CalcAsertDifficulty(config, anchor, parent, header):
  Δh = height(header) - anchor.height
  Δt = header.Time - anchor.parentTime   // seconds
  // τ = 600 s target, T_half = 172800 s (2 days)
  e  = ((Δt - Δh * τ) * RADIX) / T_half  // fixed-point exponent
  target = anchor.target * 2^e           // via cubic approximation in integer math
  target = clamp(target, 1, maxTarget)
  return DifficultyFromTarget(target)
Median-Time-Past (MTP)
Timestamp sanity for validity and time-warp resistance.
  • MTP(parent) = median of the last 11 block timestamps ending at parent.
  • Validity requires header.Time > MTP(parent) (strict inequality).
  • Future-drift bound: header.Time ≤ now + 300s.
  • Under ASERT, elapsed time relative to the anchor is driven by timestamps constrained by MTP, which prevents classic long-range time-warp attacks against the difficulty algorithm.
MTP computation
pseudocode
MTP(n):
  ts = timestamps(n, upTo=11) // back from n inclusive
  sort(ts)
  return ts[len(ts)//2]
Fork Choice Rule
Heaviest valid chain by cumulative work, independent of the active difficulty era.
  • Maintain ChainWork[tip] = ChainWork[parent] + Work(block).
  • Work(block) is a monotone function of target/difficulty; any consistent definition yields equivalent ordering.
  • Select the valid tip with greatest ChainWork; ties can be broken lexicographically by tip hash.
  • Invalid headers (time, PoW, BTC-DAA/ASERT rules) are excluded before fork choice.
Cumulative work (conceptual)
pseudocode
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])
Reorgs & Probabilistic Finality
Depth-based assurances instead of absolute finality.
  • Confirmation depth k lowers reorg probability exponentially with k.
  • Wallets/UIs choose k by value at risk (e.g., 6-conf defaults for high-value).
  • Nodes can implement practical guards (e.g., max reorg depth) to avoid DoS from pathological peers.
  • ASERT's smoother, per-block difficulty response reduces the incentive for oscillation-driven or opportunistic long reorgs compared to large-window BTC-style DAA.
Reorg handling (conceptual)
pseudocode
OnNewTip(candidate):
  if ChainWork[candidate] > ChainWork[currentTip]:
    currentTip = candidate
    ReorgTo(candidate)
Attacks & Mitigations
Key vectors relevant to the Parallax difficulty design.
  • Time-warp: strict MTP enforcement plus ASERT's per-block feedback (no long static windows) mitigate the classic BTC-style 2016-block time-warp manipulation.
  • Future timestamp skew: reject headers more than +300s ahead of local time.
  • MixDigest spoofing: header.MixDigest must match hashimoto output (light/full).
Header validity subset
pseudocode
ValidHeader(h, parent):
  require(len(h.Extra) <= MaximumExtraDataSize)
  require(h.Time <= now() + 300)
  require(h.Time > MTP(parent))
  require(h.Difficulty > 0)
  // difficulty rules depend on height:
  //   < 17560: BTC-style DAA epoch invariants
  //   ≥ 17560: ASERT anchor-based invariants
  // PoW: MixDigest match & XHash(h) <= targetFrom(D)
Pipeline

End-to-end selection flow

Headers are checked for Extra size, time (MTP & future drift), era-specific difficulty rules (BTC-style DAA for early blocks, ASERT for height ≥ 17,560), gas limits/EIPs, height increment, and PoW seal. Valid blocks extend ChainWork, and the heaviest tip is canonical.

1
Validate
2
Check Anchor / Era
3
Compute Difficulty (DAA/ASERT)
4
Accumulate Work
5
Select Heaviest