XHash

The memory-hard Proof-of-Work used by Parallax. Ethash-style Hashimoto with Parallax's ASERT-based difficulty, consensus rules, and epoch handling.

Consensus-Relevant Details
As reflected by xhash/consensus.go and Parallax chain config.
AspectValue / BehaviorNotes
Hash function (seal)
Legacy Keccak-256
RLP over specific header fields
Target mapping
target = ⌊(2^256−1)/D⌋
Result must be ≤ target
MixDigest
Must equal computed digest
Mismatch ⇒ invalid PoW
MTP
Median of last 11
Enforced: Time > MTP(parent); future drift ≤ 300s
Epoch length
Config-defined (e.g., 720 blocks)
Determines cache/DAG regeneration cadence
Difficulty algorithm
ASERT (per-block, anchor-based)
aserti3-2d; consensus computes D, XHash enforces it via PoW
Overview
XHash is Parallax's Proof-of-Work engine: Ethash-style Hashimoto (light/full) with Parallax-specific consensus wiring, ASERT-based 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).
  • Difficulty must match the consensus difficulty engine (ASERT) for the given parent and header.
  • 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 & Anchors
Interaction between PoW and the ASERT difficulty schedule.
  • Difficulty is computed by the ASERT scheduler; XHash simply enforces the chosen difficulty via the target check.
  • Consensus maintains an ASERT anchor (height, parentTime, target) and uses header metadata (e.g., EpochStartTime or dedicated fields) to reconstruct the anchor context.
  • CalcAsertDifficulty(config, anchor, parent, header) derives the expected difficulty for the header.
  • Difficulty and anchor invariants are enforced in header verification before fork-choice.
Difficulty invariants (conceptual)
pseudocode
verifyDifficulty(h, parent, anchor):
  expected = CalcAsertDifficulty(config, anchor, parent, h)
  require(h.Difficulty == expected)