> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parallaxprotocol.org/llms.txt
> Use this file to discover all available pages before exploring further.

# XHash

> Formal specification of the XHash proof-of-work algorithm used by the Parallax protocol

**XHash** is the proof-of-work function of the Parallax protocol. It is a memory-hard algorithm in the Dagger-Hashimoto lineage, derived from [Ethash](./ethash) but re-specified with standardized cryptography, a corrected data-aggregation function, a network-unique seed chain, and epoch parameters matched to Parallax's 10-minute block interval. The combined effect is that every internal data structure of XHash (cache, dataset, and mix) is bit-for-bit incompatible with Ethash, rendering existing Ethash ASICs and shared DAG infrastructure useless against the Parallax network while preserving the [memory-hard](https://wikipedia.org/wiki/Memory-hard_function) properties that keep commodity GPUs competitive.

This page is the normative specification of the algorithm. Two implementations exist and are kept in strict agreement with it:

* **[`ParallaxProtocol/xhash`](https://github.com/ParallaxProtocol/xhash)** - the standalone C/C++ reference implementation (CMake build, Python bindings, and an `xhash-bench` microbenchmark tool). This is the recommended starting point for miner, pool, and tooling authors.
* **[`kernel/xhash`](https://github.com/ParallaxProtocol/parallax/tree/main/kernel/xhash)** - the production Go implementation inside the Parallax daemon, which drives consensus on mainnet. The algorithm core lives in [`algorithm.go`](https://github.com/ParallaxProtocol/parallax/blob/main/kernel/xhash/algorithm.go), seal verification in [`consensus.go`](https://github.com/ParallaxProtocol/parallax/blob/main/kernel/xhash/consensus.go), and difficulty adjustment in [`difficulty.go`](https://github.com/ParallaxProtocol/parallax/blob/main/kernel/xhash/difficulty.go) and [`asert.go`](https://github.com/ParallaxProtocol/parallax/blob/main/kernel/xhash/asert.go).

Readers new to memory-hard proof-of-work should read the [Dagger-Hashimoto](./dagger-hashimoto) and [Ethash](./ethash) pages first; this specification is self-contained but assumes familiarity with the general construction.

## 1. Design goals

XHash was designed under four constraints:

1. **Memory hardness.** The dominant cost of a mining attempt must be pseudorandom reads over a multi-gigabyte dataset, so that performance is bounded by memory bandwidth rather than by arithmetic throughput. This keeps the gap between commodity hardware and custom silicon small.
2. **Asymmetric verification.** Full nodes must be able to verify a block using only a small (megabyte-scale) cache and a few milliseconds of CPU time, without ever materializing the mining dataset.
3. **Standards-compliant cryptography.** All hashing inside the algorithm uses the finalized NIST FIPS 202 SHA-3 functions, not the pre-standardization Keccak variants Ethereum shipped with. Any off-the-shelf cryptographic library can implement XHash without carrying a non-standard Keccak.
4. **Network isolation.** The seed chain is domain-separated with a Parallax-specific constant, so no cache, dataset, or precomputed intermediate from any Ethash-family chain is reusable against Parallax, and vice versa.

## 2. Notation and conventions

* All multi-byte integers inside the algorithm are **little-endian** unless stated otherwise. Implementations on big-endian hosts must byte-swap 32-bit words at the storage boundary (the Go implementation marks on-disk files produced on big-endian machines with a `.be` suffix).
* A **word** is a 32-bit unsigned integer. A **node** (or item) is 64 bytes, i.e. 16 words.
* `sha3_256` and `sha3_512` denote **FIPS 202 SHA3-256 and SHA3-512** (standard padding `0x06`). `keccak256` denotes legacy Keccak-256 (padding `0x01`) and appears in exactly one place: the seal hash of the block header (Section 10).
* `||` denotes byte-string concatenation. `A[i:j]` denotes the half-open byte slice from `i` to `j`.
* All divisions on integers are floor divisions.
* Pseudocode is given in Python; it is executable against the prelude in the appendix of the [Ethash](./ethash) page after substituting standard SHA-3 for Keccak.

## 3. Constants

```python theme={null}
WORD_BYTES            = 4          # bytes per word
DATASET_BYTES_INIT    = 2**30      # dataset bytes at genesis (~1 GiB)
DATASET_BYTES_GROWTH  = 2**23      # dataset growth per epoch (8 MiB)
CACHE_BYTES_INIT      = 2**24      # cache bytes at genesis (~16 MiB)
CACHE_BYTES_GROWTH    = 2**17      # cache growth per epoch (128 KiB)
EPOCH_LENGTH          = 720        # blocks per epoch
MIX_BYTES             = 128        # width of the hashimoto mix
HASH_BYTES            = 64         # node size in bytes
DATASET_PARENTS       = 256        # parents per dataset node
CACHE_ROUNDS          = 3          # RandMemoHash rounds in cache generation
ACCESSES              = 64         # dataset accesses per hashimoto evaluation
FNV_PRIME             = 0x01000193 # 32-bit FNV prime

PARALLAX_CHAIN_MAGIC  = b"PARALLAX\x01"   # 9-byte domain-separation constant
```

The chain magic is the ASCII string `PARALLAX` followed by a single version byte `0x01`:

```
50 41 52 41 4C 4C 41 58 01
```

### 3.1 Epoch schedule

With Parallax's 600-second target block spacing, one epoch of 720 blocks spans exactly 432,000 seconds = **5.0 days**. This matches Ethash's original \~5-day cadence (30,000 blocks at 15 seconds), so operational assumptions about DAG regeneration frequency carry over unchanged. Approximately 73 epochs elapse per year, growing the dataset by about 612 MiB/year and the cache by about 9.6 MiB/year.

| Quantity     | Epoch 0                      | Growth per epoch | Growth per year (approx.) |
| ------------ | ---------------------------- | ---------------- | ------------------------- |
| Dataset size | 1,073,739,904 B (\~1.00 GiB) | 8 MiB            | 612 MiB                   |
| Cache size   | 16,776,896 B (\~16.0 MiB)    | 128 KiB          | 9.6 MiB                   |

### 3.2 Size derivation

Cache and dataset sizes grow linearly per epoch, but each is snapped down to the largest value whose node count is prime, eliminating the risk of short cycles in the pseudorandom parent-selection walk:

```python theme={null}
def get_cache_size(block_number):
    sz = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number // EPOCH_LENGTH)
    sz -= HASH_BYTES
    while not isprime(sz // HASH_BYTES):
        sz -= 2 * HASH_BYTES
    return sz

def get_full_size(block_number):
    sz = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number // EPOCH_LENGTH)
    sz -= MIX_BYTES
    while not isprime(sz // MIX_BYTES):
        sz -= 2 * MIX_BYTES
    return sz
```

Both implementations ship precomputed lookup tables for the first 2,048 epochs (about 28 years of chain time) and fall back to the derivation above beyond that; see `datasetSizes` and `cacheSizes` in [`algorithm.go`](https://github.com/ParallaxProtocol/parallax/blob/main/kernel/xhash/algorithm.go). Because the growth constants and primality rule are identical to Ethash's, the size tables coincide with Ethash's tables; only the *content* of the structures differs.

## 4. Cryptographic primitives

Ethereum's "sha3" is not SHA-3: it froze on a pre-standardization Keccak draft, and the final FIPS 202 standard changed the padding rule. XHash deliberately breaks with this legacy. **Every hash inside the seed chain, cache, dataset, and hashimoto loop is standard FIPS 202 SHA3-256 or SHA3-512.**

This has two consequences:

1. **Interoperability.** XHash can be implemented against any compliant SHA-3 library (OpenSSL, libsodium via sha3 add-ons, hardware SHA-3 units, the Go standard library) with no custom Keccak code.
2. **Divergence by construction.** Even for epoch 0, where the seed is all zeros in both algorithms (Section 5), the differing padding byte makes the very first cache node differ from Ethash's, and the divergence cascades through every downstream structure. Network isolation does not depend on the chain magic alone; it is enforced at two independent layers.

The single exception is the **seal hash** of the block header (Section 10), which uses legacy Keccak-256. This is intentional: Parallax block and transaction hashing follows the Ethereum wire conventions throughout the protocol, and the seal hash is a protocol-layer quantity, not part of the PoW core. The PoW core treats it as an opaque 32-byte input.

## 5. Seed chain

Each epoch has a 32-byte **seed** from which its cache (and hence its dataset) is derived deterministically. Epoch 0 uses the zero seed; each subsequent epoch hashes the previous seed prefixed with the chain magic:

```python theme={null}
def get_seedhash(block_number):
    s = b"\x00" * 32
    for _ in range(block_number // EPOCH_LENGTH):
        s = sha3_256(PARALLAX_CHAIN_MAGIC + s)
    return s
```

Formally, with `s_0 = 0^32`:

```
s_{e+1} = SHA3-256( PARALLAX_CHAIN_MAGIC || s_e )
```

Injecting the 9-byte magic into every iteration makes the entire seed chain, from epoch 1 onward, unique to Parallax. An Ethash-family chain (which computes `s_{e+1} = keccak256(s_e)`) shares no seed with Parallax at any epoch, so precomputed caches and DAGs cannot be transplanted in either direction.

Note that implementations key their per-epoch structures by `get_seedhash(epoch * EPOCH_LENGTH + 1)`, i.e. by any block strictly inside the epoch; all blocks of one epoch share one seed, cache, and dataset.

## 6. Cache generation

The per-epoch verification cache is produced in two phases: a sequential SHA3-512 chain fills the buffer, then three rounds of Sergio Demian Lerner's **RandMemoHash** construction (from [*Strict Memory Hard Hashing Functions*, 2014](http://www.hashcash.org/papers/memohash.pdf)) scramble it so that computing any node quickly requires holding essentially the whole cache:

```python theme={null}
def mkcache(cache_size, seed):
    n = cache_size // HASH_BYTES

    # Phase 1: sequentially fill the cache
    o = [sha3_512(seed)]
    for i in range(1, n):
        o.append(sha3_512(o[-1]))

    # Phase 2: low-round RandMemoHash
    for _ in range(CACHE_ROUNDS):
        for i in range(n):
            v    = word(o[i], 0) % n           # first 32-bit word of node i, LE
            o[i] = sha3_512(xor(o[(i - 1 + n) % n], o[v]))

    return o
```

where `word(node, k)` reads the k-th little-endian 32-bit word of a 64-byte node and `xor` is the bytewise XOR of two nodes. The output is `n` 64-byte nodes (`n` = 262,139 at epoch 0). Cache generation costs roughly `(CACHE_ROUNDS + 1) * n` SHA3-512 invocations and takes on the order of a second on a modern CPU.

## 7. Data aggregation: FNV-1

Ethash mixes data with a function *inspired by* [FNV](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) but deviating from it: it multiplies the prime with a full 32-bit word instead of one octet at a time. XHash instead implements **FNV-1 exactly as specified**, consuming the second operand byte by byte (little-endian), with the first operand acting as the running FNV state:

```python theme={null}
def fnv1(a, b):
    h = a
    for _ in range(4):            # consume the 4 bytes of b, little-endian
        h = (h * FNV_PRIME) % 2**32
        h ^= b & 0xFF
        b >>= 8
    return h
```

Two properties matter for the construction:

* **Non-associativity and non-linearity in the state.** Like Ethash's variant, `fnv1` is a cheap, order-sensitive combiner - a sequence of multiplies and XORs that does not commute and cannot be factored out of the access loop.
* **Full-width diffusion per octet.** Each of the four input bytes is folded in through its own multiply, giving byte-level diffusion identical to the canonical FNV-1 avalanche behavior, and making XHash mixing traces incompatible with any Ethash implementation or hardware pipeline.

Vectors of words are combined elementwise:

```python theme={null}
def fnv1_map(mix, data):          # both are equal-length word arrays
    return [fnv1(mix[i], data[i]) for i in range(len(mix))]
```

`fnv1` is also used as the *index generator* for pseudorandom parent and dataset-page selection (Sections 8 and 9), so the change affects both the mixing arithmetic and the memory access pattern.

## 8. Dataset generation

Each 64-byte dataset node is computed from the cache by aggregating `DATASET_PARENTS = 256` pseudorandomly selected cache nodes:

```python theme={null}
def calc_dataset_item(cache, i):
    n = len(cache)
    r = HASH_BYTES // WORD_BYTES          # 16 words per node

    # initialize the mix from cache node i mod n, perturbed by the index
    mix = copy(cache[i % n])
    mix[0] ^= i
    mix = sha3_512(mix)

    # walk 256 pseudorandom parents
    for j in range(DATASET_PARENTS):
        parent = fnv1(i ^ j, mix[j % r]) % n
        mix    = fnv1_map(mix, cache[parent])

    return sha3_512(mix)

def calc_dataset(full_size, cache):
    return [calc_dataset_item(cache, i) for i in range(full_size // HASH_BYTES)]
```

The parent index depends on the evolving mix, so the 256 cache reads per node are sequentially dependent: a device cannot know read `j+1` before finishing read `j`. Full dataset generation performs `full_size / 64` node computations and is embarrassingly parallel across nodes; the Go implementation generates it on all cores and typically completes in minutes, while miners are expected to pregenerate the next epoch's dataset in the background (Section 12).

## 9. The hashimoto loop

The core sealing function binds a header to a nonce by aggregating 64 pseudorandom 128-byte pages of the dataset:

```python theme={null}
def hashimoto(seal_hash, nonce, full_size, dataset_lookup):
    n         = full_size // HASH_BYTES
    w         = MIX_BYTES // WORD_BYTES     # 32 words of mix
    mixhashes = MIX_BYTES // HASH_BYTES     # 2 nodes per page

    # 40-byte input: 32-byte seal hash || 8-byte little-endian nonce
    s = sha3_512(seal_hash + nonce_le64(nonce))
    s_head = word(s, 0)

    # replicate the 64-byte s into a 128-byte mix
    mix = words(s) * mixhashes              # 32 words

    # 64 sequentially dependent dataset page reads
    for i in range(ACCESSES):
        p = fnv1(i ^ s_head, mix[i % w]) % (n // mixhashes)
        page = []
        for j in range(mixhashes):
            page.extend(dataset_lookup(2 * p + j))
        mix = fnv1_map(mix, page)

    # compress the 32-word mix to an 8-word digest
    cmix = []
    for i in range(0, w, 4):
        cmix.append(fnv1(fnv1(fnv1(mix[i], mix[i+1]), mix[i+2]), mix[i+3]))

    return {
        "mix_digest": serialize(cmix),              # 32 bytes -> header.mixHash
        "result":     sha3_256(s + serialize(cmix)) # 32 bytes, compared to target
    }

def hashimoto_light(full_size, cache, seal_hash, nonce):
    return hashimoto(seal_hash, nonce, full_size,
                     lambda x: calc_dataset_item(cache, x))

def hashimoto_full(full_size, dataset, seal_hash, nonce):
    return hashimoto(seal_hash, nonce, full_size, lambda x: dataset[x])
```

Design notes:

* Each access fetches a full aligned **128-byte page** (two adjacent nodes), sized to a DRAM burst so that every iteration performs one full-page read; this minimizes TLB-miss advantages that specialized hardware could otherwise exploit.
* The page index for iteration `i` depends on the mix produced by iteration `i-1`, so the 64 reads are strictly serial per attempt; throughput scales with memory bandwidth and parallel attempt count, not with faster arithmetic.
* The final outer `sha3_256(s || cmix)` serves two purposes: it makes `result` a statistically unbiased 256-bit value, and it creates a cheap outer PoW - a peer can hash `s || cmix` and compare against the target before doing any dataset or cache work, which is useful for DoS filtering.

## 10. Header binding: the seal hash

The 32-byte `seal_hash` input to hashimoto commits to the entire block header except the two seal fields themselves. It is the legacy Keccak-256 of the RLP encoding of the truncated header:

```
seal_hash = keccak256( RLP([
    parent_hash, coinbase, state_root, tx_root, receipt_root, logs_bloom,
    difficulty, number, gas_limit, gas_used, timestamp, extra_data,
    epoch_start_time,
    base_fee?          # present only when the header carries a base fee
]))
```

`mixHash` and `nonce` are excluded (they are outputs of sealing, not inputs). `epoch_start_time` is a Parallax-specific header field recording the timestamp of the most recent retarget boundary block; it participates in difficulty validation (Section 13) and is therefore sealed. The 64-bit nonce is stored big-endian in the header (Ethereum convention) but serialized **little-endian** into the 40-byte hashimoto input.

## 11. Mining and the target

A nonce is valid for difficulty `D` when the hashimoto result, interpreted as a big-endian 256-bit integer, satisfies:

```
result  <=  target  =  (2**256 - 1) // D
```

```python theme={null}
def mine(full_size, dataset, seal_hash, difficulty):
    target = (2**256 - 1) // difficulty
    nonce  = random_uint64()
    while decode_int_be(hashimoto_full(full_size, dataset, seal_hash, nonce)["result"]) > target:
        nonce = (nonce + 1) % 2**64
    return nonce
```

XHash defines the boundary as `(2^256 - 1) // D` rather than Ethash's `2^256 // D`. Using the maximum representable 256-bit value as the numerator keeps every quantity in the system - targets, difficulties, and the ASERT `max_target` bound (Section 13) - inside native 256-bit arithmetic with no 257-bit intermediate, which simplifies fixed-width implementations. At `D = 1` the target is `2^256 - 1` and every hash is valid; difficulty and target are related by `D = (2**256 - 1) // target` in the reverse direction.

Miners draw the starting nonce from a cryptographically seeded 64-bit source and scan linearly; the Go sealer distributes disjoint starting points across threads and re-randomizes per work package.

## 12. Verification

A block seal `(mixHash, nonce)` is verified as follows:

1. Recompute `seal_hash` from the received header (Section 10).
2. Evaluate hashimoto. A **full verifier** with the current DAG uses `hashimoto_full`; a **light verifier** uses `hashimoto_light`, which regenerates only the 128 dataset nodes actually touched (64 pages × 2 nodes), each costing 256 cache-node FNV walks and two SHA3-512 calls. Light verification needs only the \~16 MiB cache and runs in single-digit milliseconds.
3. Reject with `invalid mix digest` unless the computed `mix_digest` equals the header's `mixHash`. This check ensures the miner actually performed the memory-hard inner loop and lets verifiers reject forgeries before the target comparison.
4. Reject with `invalid proof-of-work` unless `result <= (2**256 - 1) // difficulty`.

The daemon verifies with the cache by default and uses the full DAG only when one is already generated (`fulldag` fast path), never blocking block import on DAG generation.

### 12.1 Operational notes (caches and DAGs on disk)

The Go implementation memory-maps caches and datasets from disk and maintains, for each, the current epoch plus a pregenerated **future epoch** so that the epoch transition at multiple-of-720 heights causes no mining stall. Files are named:

```
cache-R23-{seedhash[:8] hex}      # verification cache
full-R23-{seedhash[:8] hex}       # mining dataset (DAG)
```

`R23` is the data-structure revision (`algorithmRevision = 23`); a `.be` suffix marks big-endian hosts. Each file begins with the magic words `0xbaddcafe, 0xfee1dead` as a sanity check, and stale epochs beyond a configured retention count are deleted automatically. Remote miners and pools receive work packages containing the seal hash, the epoch seed hash, the 32-byte big-endian boundary target `(2^256 - 1) // D`, and the block number.

## 13. Difficulty adjustment

Difficulty adjustment is not part of the hash function proper, but it defines the target against which XHash results are judged, so it is specified here. Parallax uses two regimes, split at the chain-configured `asertActivationHeight` (17,560 on mainnet).

### 13.1 Nakamoto phase (heights below activation)

Blocks before activation retarget exactly like Bitcoin, adapted to header-carried state. Every header carries `epoch_start_time`: on a boundary height (`h % 2016 == 0`) it is set to that block's own timestamp; otherwise it is copied from the parent. At each boundary:

```python theme={null}
target_timespan = 2016 * 600                    # 14 days in seconds
actual = clamp(parent.timestamp - parent.epoch_start_time,
               target_timespan // 4, target_timespan * 4)
D_new  = max(1, D_old * target_timespan // actual)
```

Between boundaries difficulty is constant. Because `actual` spans the 2,015 intervals from the boundary block to the parent while being compared against a 2,016-interval budget, the measurement mirrors Bitcoin's historical off-by-one exactly.

### 13.2 ASERT phase (heights at or above activation)

From the activation height onward, Parallax switches to **aserti3-2d**, the absolutely scheduled exponentially rising targets algorithm adopted by Bitcoin Cash, computing every block's target directly from its schedule deviation against a fixed anchor:

```
target(next) = anchor_target * 2^( (time_delta - 600 * height_delta) / 172800 )
```

where, evaluating from parent block `p` against the anchor block `A` (the block at height `activation - 1`):

* `time_delta   = p.timestamp - timestamp(parent of A)`
* `height_delta = p.height - A.height`
* ideal spacing is **600 seconds**, and the **half-life is 172,800 seconds (2 days)**: sustained arrival one half-life ahead of schedule doubles difficulty; one half-life behind halves it.

The exponential is evaluated in integer-only fixed-point arithmetic with radix 2^16, using the cubic polynomial approximation of `2^x` from the BCH specification (coefficients `195766423245049`, `971821376`, `5127`), and the result is clamped to `max_target = 2^256 - 1`. The implementation caches the anchor and re-derives it if a reorg replaces the header at the anchor height. Difficulty and target convert through `D = (2**256 - 1) // T` as in Section 11.

## 14. Differences from Ethash

| Aspect               | Ethash                                                           | XHash                                                       |
| -------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------- |
| Epoch length         | 30,000 blocks (\~5 days at 15 s)                                 | 720 blocks (exactly 5 days at 600 s)                        |
| Hash primitives      | Pre-standard Keccak-256 / Keccak-512                             | NIST FIPS 202 SHA3-256 / SHA3-512                           |
| Aggregation function | FNV-inspired (word-wise multiply)                                | Canonical FNV-1 (octet-wise)                                |
| Seed chain           | `keccak256(s)` iterated                                          | `sha3_256(MAGIC \|\| s)` iterated, `MAGIC = "PARALLAX\x01"` |
| Mining target        | `2**256 // D`                                                    | `(2**256 - 1) // D`                                         |
| Difficulty algorithm | Ethereum homestead-family formulas                               | Bitcoin-style 2016-block retarget, then aserti3-2d          |
| Sizes and structure  | 1 GiB+ DAG, 16 MiB+ cache, 128 B pages, 64 accesses, 256 parents | identical parameters and size tables                        |

The structural parameters were retained deliberately: they are the well-studied, battle-tested part of Ethash. Every cryptographic and arithmetic ingredient feeding those structures was replaced or corrected.

## 15. Security considerations

**Memory hardness.** A single hashimoto evaluation performs 64 serial 128-byte reads at pseudorandom, data-dependent addresses over a dataset that exceeds on-chip SRAM budgets by orders of magnitude. Amortizing the dataset away (a "light evaluation" attack) costs 128 node regenerations, each requiring 256 sequentially dependent cache reads plus two SHA3-512 calls - roughly three orders of magnitude more work per attempt than a dataset read, which makes cache-only mining strictly uneconomical while keeping cache-only *verification* cheap (it runs once per block, not once per nonce).

**ASIC and infrastructure isolation.** Divergence from Ethash is enforced at three independent layers: (1) FIPS-202 padding changes every hash output even under identical inputs; (2) the chain magic makes every seed from epoch 1 onward Parallax-specific; (3) octet-wise FNV-1 changes both the mixing arithmetic and the pseudorandom address streams. Fixed-function Ethash hardware pipelines the Keccak permutation with draft padding and word-wise FNV in silicon; neither can be reconfigured to XHash, and no Ethash DAG, cache, or seed table is reusable.

**Verifier DoS resistance.** The outer `sha3_256(s || cmix)` allows a cheap plausibility check on received seals before any memory-hard work, and the mix-digest equality check (Section 12) forces attackers to perform the full inner loop to produce a header that survives past step 3.

**Epoch-0 caveat.** The zero seed of epoch 0 is shared with Ethash by construction, but layer (1) alone already guarantees the epoch-0 structures differ; network isolation never rests on the seed chain alone.

## 16. Calculations

This section quantifies the security and control-theoretic behavior of XHash under Parallax's parameters, in the spirit of Section 11 of the [Bitcoin whitepaper](https://bitcoin.org/bitcoin.pdf). Every number below was verified numerically against a bit-exact port of the consensus integer arithmetic in [`asert.go`](https://github.com/ParallaxProtocol/parallax/blob/main/kernel/xhash/asert.go) (including Go's truncating division, two's-complement `uint64` wraparound, and arithmetic shifts); the probabilistic results were evaluated in log-space to full float precision.

### 16.1 The double-spend race

Following Nakamoto, model the contest between the honest chain and an attacker's private chain as a binomial random walk: `p` is the probability the next block is honest, `q = 1 - p` the probability it is the attacker's. By the Gambler's Ruin argument, an attacker who is `z` blocks behind catches up with probability

```
q_z = 1                if p <= q
q_z = (q/p)^z          if p > q
```

The probability of catching up falls **geometrically** in `z`: each additional confirmation multiplies the attacker's odds by `q/p`. A recipient who waits for `z` confirmations must also account for the progress the attacker has already made during that wait, which is Poisson-distributed with expectation `lambda = z * q/p`. The overall success probability, rearranged as in the whitepaper to avoid summing the distribution's infinite tail, is:

```python theme={null}
def attacker_success(q, z):
    p, lam = 1 - q, z * q / (1 - q)
    return 1 - sum(poisson(lam, k) * (1 - (q/p)**(z - k)) for k in range(z + 1))
```

This analysis counts *blocks*, not seconds, so it applies to Parallax exactly as to Bitcoin; only the wall-clock interpretation changes. With Parallax's 600-second spacing, `z` confirmations correspond to an expected wait of `z x 10` minutes, the same as Bitcoin and roughly 40 times longer real-time exposure per confirmation than a 15-second chain - one Parallax confirmation embodies 40 times the expected work share of one 15-second-chain confirmation at equal network scale.

Evaluating for an attacker with 10% of the network:

| z | P         | z  | P         |
| - | --------- | -- | --------- |
| 0 | 1.0000000 | 6  | 0.0002428 |
| 1 | 0.2045873 | 7  | 0.0000647 |
| 2 | 0.0509779 | 8  | 0.0000173 |
| 3 | 0.0131722 | 9  | 0.0000046 |
| 4 | 0.0034552 | 10 | 0.0000012 |
| 5 | 0.0009137 |    |           |

and for an attacker with 30% of the network:

| z  | P         | z  | P         |
| -- | --------- | -- | --------- |
| 0  | 1.0000000 | 30 | 0.0001522 |
| 5  | 0.1773523 | 35 | 0.0000379 |
| 10 | 0.0416605 | 40 | 0.0000095 |
| 15 | 0.0101008 | 45 | 0.0000024 |
| 20 | 0.0024804 | 50 | 0.0000006 |
| 25 | 0.0006132 |    |           |

Solving for the confirmation depth that bounds the attacker's success below 0.1%:

| q | 0.05 | 0.10 | 0.15 | 0.20 | 0.25 | 0.30 | 0.35 | 0.40 | 0.45 |
| - | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| z | 4    | 5    | 8    | 11   | 15   | 24   | 41   | 89   | 340  |

These reproduce the whitepaper's figures exactly (as they must - the derivation is parameter-free), and the common "6 confirmations" heuristic corresponds to sub-0.1% risk against attackers controlling up to 12% of Parallax's hashrate (at 12%, P = 0.00072; at 13%, P = 0.00115).

### 16.2 Block arrival and work expectation

Block discovery is a Poisson process with rate `1/600 s^-1` at equilibrium. The probability that at least one block is found within `t` seconds is `1 - e^(-t/600)`:

| t            | 60 s   | 300 s  | 600 s  | 1200 s | 3600 s |
| ------------ | ------ | ------ | ------ | ------ | ------ |
| P(block ≤ t) | 0.0952 | 0.3935 | 0.6321 | 0.8647 | 0.9975 |

With the XHash boundary `target = (2^256 - 1) // D`, a single hash succeeds with probability `(target + 1) / 2^256`, so the expected number of hashimoto evaluations per block is `2^256 / (target + 1)`. Evaluated exactly in rational arithmetic, this deviates from `D` by less than one part in 10^66 for any realistic difficulty (verified at `D = 10^3` and `D = 10^12`), so the estimator

```
network_hashrate ~= difficulty / 600
```

holds without correction terms, and the choice of `2^256 - 1` as numerator (Section 11) costs nothing in work-accounting accuracy.

### 16.3 ASERT response analysis

ASERT (Section 13.2) is a proportional controller in log-target space: the target is an explicit exponential function of the chain's cumulative schedule deviation, `target = anchor_target * 2^((T - 600H)/172800)` for drift `T - 600H` seconds. Its behavior under Parallax's parameters:

**Fixed-point exactness.** The consensus code evaluates the exponential in 16.16 fixed point with a cubic approximation of `2^x` on the fractional interval. Exhaustive evaluation over all 65,536 fractional inputs gives a maximum relative error of **0.0117%** (worst case at `x = 7057/65536`), on top of an exponent quantization step of `2^-16` ≈ 0.0011%. Per-block target error from the approximation machinery is therefore bounded by about 1.2 parts in 10,000 and does not accumulate, because each block's target is recomputed from the absolute schedule, not from the previous target.

**Exact identities.** Verified bit-for-bit against the consensus integer code:

* A chain exactly on schedule (`timeDelta = 600 * numBlocks`) reproduces the anchor target *exactly* - the factor path yields precisely `2^16` and the shift path is idle.
* A cumulative drift of exactly one half-life (±172,800 s) exactly doubles (or halves) the target: the fractional part is zero and the entire adjustment travels the error-free bit-shift path.
* A single block arriving 600 s late moves the target by ×1.002411 against a theoretical `2^(600/172800)` = 1.002410.

**Step response.** Linearizing the controller: after a sudden hashrate change, the spacing error decays exponentially with time constant

```
tau = halflife / ln 2 = 172800 / 0.6931 = 249298 s = 69.2 h = 2.89 days
```

giving 90% adjustment after `tau * ln 10` = 159.5 h and 99% after 318.9 h, independent of the direction or (to first order) magnitude of the step. Simulating the exact consensus arithmetic with deterministic expected block times confirms the linearized prediction:

| Hashrate step | Within 10% of 600 s  | Within 1% of 600 s   |
| ------------- | -------------------- | -------------------- |
| ×0.5 (half)   | 708 blocks, 159.4 h  | 1630 blocks, 319.0 h |
| ×2 (double)   | 914 blocks, 111.6 h  | 1908 blocks, 270.7 h |
| ×0.25         | 876 blocks, 235.4 h  | 1798 blocks, 395.0 h |
| ×4            | 1370 blocks, 139.6 h | 2364 blocks, 298.7 h |

(The simulated 159.4 h for a hashrate halving matches the theoretical 159.5 h to 0.1%; upward steps complete in less wall-clock time because blocks arrive faster than 600 s throughout the transient.) Because scheduling is absolute, there is no steady-state error: once hashrate stabilizes, the expected spacing returns to exactly 600 s and any residual calendar drift stops growing, in contrast to window-based retargeting, which permanently forgets past drift.

### 16.4 Nakamoto-phase bounds

During the pre-ASERT phase (Section 13.1), a single retarget is clamped by the timespan bounds to the interval:

```
0.25 <= D_new / D_old <= 4
```

verified over the full clamp range (an instant interval clamps to ×4, an arbitrarily slow one to ×0.25, an on-schedule one to ×1). Difficulty can therefore change by at most a factor of 4 per 2,016 blocks (14 days) before activation height 17,560, after which the ASERT dynamics of Section 16.3 govern.

## 17. Reference implementations and further reading

* C/C++ reference implementation, Python bindings, benchmarks: **[github.com/ParallaxProtocol/xhash](https://github.com/ParallaxProtocol/xhash)**
* Production Go implementation (consensus engine of the Parallax daemon): **[github.com/ParallaxProtocol/parallax - `kernel/xhash`](https://github.com/ParallaxProtocol/parallax/tree/main/kernel/xhash)**
* Test vectors: `kernel/xhash/testdata` and the test suites of both repositories (`algorithm_test.go`, `xhash_test.go`, and the C++ `test/` directory) pin cache, dataset, and hashimoto outputs across implementations.
* Background: [Dagger-Hashimoto](./dagger-hashimoto), [Ethash](./ethash), Lerner's [*Strict Memory Hard Hashing Functions* (2014)](http://www.hashcash.org/papers/memohash.pdf), [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf), the [FNV specification](https://datatracker.ietf.org/doc/html/draft-eastlake-fnv), and the [aserti3-2d specification](https://upgradespecs.bitcoincashnode.org/2020-11-15-asert/).
