XHash

The memory‑hard Proof‑of‑Work used by Parallax. Ethash‑style Hashimoto with Parallax’s consensus rules, difficulty mapping, and epoch handling.

Consensus‑Relevant Details

As reflected by xhash/consensus.go and Parallax chain config.

AspectValue / BehaviorNotes
Hash function (seal)Legacy Keccak‑256RLP over specific header fields
Target mappingtarget = ⌊(2^256−1)/D⌋Result must be ≤ target
MixDigestMust equal computed digestMismatch ⇒ invalid PoW
MTPMedian of last 11Enforced: Time > MTP(parent); Future drift ≤ 300s
Epoch lengthConfig‑defined (e.g., 720 blocks)Determines cache/DAG regeneration cadence
Retarget interval2016 blocksBitcoin-like retarget interval
Overview

XHash is Parallax’s Proof‑of‑Work engine: Ethash‑style Hashimoto (light/full) with Parallax‑specific consensus wiring, difficulty mapping, and epoch handling.

  • Mining evaluates Hashimoto over a header seal hash and 64‑bit nonce, using either a cache (light) or dataset (full).
  • Verification checks MixDigest equality and compares the result to a target derived from difficulty: target = ⌊(2^256−1)/D⌋.
  • Seal hash uses Legacy Keccak‑256 over an RLP list of specific header fields.
SealHash
pseudocode
SealHash(header):
  enc = [
    header.ParentHash,
    header.Coinbase,
    header.Root,
    header.TxHash,
    header.ReceiptHash,
    header.Bloom,
    header.Difficulty,
    header.Number,
    header.GasLimit,
    header.GasUsed,
    header.Time,
    header.Extra,
    header.EpochStartTime,
  ]
  if header.BaseFee != nil:
    enc.append(header.BaseFee)
  return keccak256(rlp.encode(enc))
Mining Loop

Hashimoto‑light/full with 64‑bit nonce search and little‑endian target compare in the inner loop.

  • Miners iterate nonce ∈ [0..2^64−1], computing (digest, result) = hashimoto(cache/dataset, sealHash, nonce).
  • MixDigest must match the header field exactly; result must be ≤ target.
  • Full miners use the per‑epoch dataset; light verifiers/nodes can validate with caches (no full DAG).
  • Dataset/cache regenerate on epoch boundaries (e.g., every 720 blocks)
Nonce search (pseudocode)
pseudocode
mine(header, cacheOrDataset):
  target = floor((2^256 - 1) / header.Difficulty)
  nonce  = random64()
  loop:
    (mix, res) = hashimoto(cacheOrDataset, SealHash(header), nonce)
    if mix == header.MixDigest and Big(res) <= target:
      return nonce
    nonce = (nonce + 1) mod 2^64
Header Verification

What a node checks before accepting a PoW header.

  • Size of Extra ≤ MaximumExtraDataSize.
  • Time ≤ now + 300s and Time > MTP(parent) (median of last 11).
  • EpochStartTime invariants: set on retarget boundary; otherwise equal to parent’s EpochStartTime.
  • Exact difficulty match: CalcNakamotoDifficulty(config, parent).
  • PoW seal: MixDigest equality and XHash(header) ≤ target(two256m1 / D).
verifySeal (conceptual)
pseudocode
verifySeal(h):
  require(h.Difficulty > 0)
  if fulldag:
    (mix, res) = hashimotoFull(dataset(epoch(h.Number)), SealHash(h), h.Nonce)
  else:
    (mix, res) = hashimotoLight(datasetSize(h.Number), cache(epoch(h.Number)), SealHash(h), h.Nonce)
  require(mix == h.MixDigest)
  target = floor((2^256 - 1) / h.Difficulty)
  require(Big(res) <= target)
Epochs, Cache & Dataset (DAG)

Regeneration cadence and miner compatibility notes.

  • Epoch number is derived from block height; cache/dataset sizes depend on epoch via datasetSize(height).
  • Parallax uses shorter epochs than legacy Ethash (e.g., 720‑block epochs) to fit ~5‑day regeneration at 10‑minute blocks.
  • Nodes keep caches to make verification light; full DAG not required to validate headers.
Regeneration trigger
pseudocode
if newEpoch(height):
  regenerate cache
  if mining full: regenerate dataset
Difficulty & Epoch Anchors

Interaction between PoW and the difficulty schedule.

  • Retarget every 2016 blocks.
  • On a boundary: header.EpochStartTime = header.Time; otherwise, propagate parent’s value.
  • CalcNakamotoDifficulty(config, parent) derives the next difficulty; PoW simply enforces it via the target check.
  • These rules are enforced in header verification before fork‑choice.
Anchor invariants
pseudocode
if height % R == 0:
  require(h.EpochStartTime == h.Time)
else:
  require(h.EpochStartTime == parent.EpochStartTime)