> For the complete documentation index, see [llms.txt](https://crypta.gitbook.io/crypta/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://crypta.gitbook.io/crypta/probability-exchange/verifyingresults.md).

# Verifying Results

Every tick Crypta publishes is independently verifiable. Nothing needs to be taken on trust, and nothing needs to wait — each tick arrives with everything required to check it.

To verify every price movement visit: <https://verify.crypta.io/>

Anyone can independently verify:

### 1) T**hat the Secret Hash is Part of the Original Committed On-Chain Merkle Root:**

This is the step that proves Crypta did not choose the tick's secret after seeing the block hash.

First hash the Secret Hash to get the Merkle Leaf:

$$
Merkle : Leaf : Hash =SHA256(Secret : Hash)
$$

Then follow the published Merkle Proof by hashing up the tree until the Merkle Root.

$$
N\_{parent} = SHA256(N\_{left}||N\_{right})
$$

Confirm the final hash matches the Merkle Root commited on-chain before the epoch started.

### 2) The Independent Solana Block Hash:

Use any public Solana block explorer to confirm the block hash that was used matches the Solana block chain.

Top explorers:

* <https://solscan.io/>
* <https://explorer.solana.com/>

### 3) T**he PFE Hash:**

The committed secret is the key; the public block hash is the message:

$$
PFE\_{asset,t} = HMAC-SHA256(key=Secret :Hash, : msg=Block : Hash)
$$

This can be verified using the following independent hashing tool by inputting the secret hash as the key and the block hash as the input message:

{% embed url="<https://gchq.github.io/CyberChef/#recipe=HMAC(%7B'option':'Hex','string':''%7D,'SHA256>')" %}

{% hint style="info" %}
*Note: The inputs are hashed using the raw bytes.*
{% endhint %}

### 4) T**he Price Movements from the PFE Hash:**

Use the Javascript code below to derive the price movements generated from any PFE Hash but inputting the hash and the market variables.

```javascript
/**
 * Crypta price movement verifier script
 *
 * Check for yourself that a tick's entropy hash, combined with the market's
 * published variables, produces the price movement.
 *
 * Fill in the values below, then run the file. Nothing to install, nothing to
 * configure, no internet connection needed.
 *
 *   Node:     node verify-price.js
 *   Browser:  paste the whole file into the developer console
 */

// ===========================================================================
//  YOUR INPUTS - edit these, then run
// ===========================================================================

/** The tick's entropy hash (PFE), as published with the tick. */
const ENTROPY_HASH = '51fd9a1fbd6b14ed4194e479d96fdc43';

/** Drift per tick (mu). */
const DRIFT = 0.0;

/** Diffusion scale (sigma) - the size of a one-sigma move, in price units. */
const SIGMA = 0.02;

/** Jump probability per tick (p). */
const JUMP_PROBABILITY = 0.01;

/** Largest move allowed in one tick, if the market sets one. Otherwise null. */
const MAX_MOVE_PER_TICK = null;

// ===========================================================================
//  The calculation - you should not need to change anything below this line
// ===========================================================================

/** Turn the hex hash into raw bytes. */
function hexToBytes(hex) {
  const s = hex.trim().toLowerCase().replace(/^0x/, '');
  if (!/^[0-9a-f]+$/.test(s) || s.length % 2 !== 0) {
    throw new Error('ENTROPY_HASH is not valid hex');
  }
  if (s.length < 16) {
    throw new Error('ENTROPY_HASH must be at least 8 bytes (16 hex characters)');
  }
  const out = new Uint8Array(s.length / 2);
  for (let i = 0; i < out.length; i++) out[i] = parseInt(s.substr(i * 2, 2), 16);
  return out;
}

/** Read 8 bytes as a big-endian unsigned 64-bit integer. */
function readUint64(bytes, offset) {
  let n = 0n;
  for (let i = offset; i < offset + 8; i++) n = (n << 8n) | BigInt(bytes[i]);
  return n;
}

/**
 * Round to 4 decimal places, half away from zero.
 *
 * JavaScript's Math.round is half-up, which rounds negative halves the wrong
 * way. Movements are negative as often as positive, so this matters.
 */
function round4(v) {
  const x = v * 10000;
  if (x === 0) return 0;
  const mag = Math.abs(x);
  let whole = Math.floor(mag);
  if (mag - whole >= 0.5) whole += 1;
  return (x < 0 ? -whole : whole) / 10000;
}

/**
 * Inverse normal CDF (Acklam's approximation) - turns a uniform value in
 * (0,1) into a standard normal deviate.
 */
function inverseNormalCdf(p) {
  const a = [-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2,
             1.38357751867269e2, -3.066479806614716e1, 2.506628277459239];
  const b = [-5.447609879822406e1, 1.615858368580409e2, -1.556989798598866e2,
             6.680131188771972e1, -1.328068155288572e1];
  const c = [-7.784894002430293e-3, -3.223964580411365e-1, -2.400758277161838,
             -2.549732539343734, 4.374664141464968, 2.938163982698783];
  const d = [7.784695709041462e-3, 3.224671290700398e-1, 2.445134137142996,
             3.754408661907416];
  const LOW = 0.02425;

  if (p < LOW) {
    const q = Math.sqrt(-2 * Math.log(p));
    return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
           ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
  }
  if (p <= 1 - LOW) {
    const q = p - 0.5, r = q * q;
    return ((((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q) /
           (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
  }
  const q = Math.sqrt(-2 * Math.log(1 - p));
  return -((((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
           ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1));
}

function verify() {
  if (JUMP_PROBABILITY <= 0) throw new Error('JUMP_PROBABILITY must be greater than 0');

  const pfe = hexToBytes(ENTROPY_HASH);

  // Step 1 - the random part, from the first 8 bytes of the hash.
  const uRaw = readUint64(pfe, 0);
  let u = Number(uRaw) / 2 ** 64;
  u = Math.min(Math.max(u, 1e-10), 1 - 1e-10);
  const zt = inverseNormalCdf(u);

  // Step 2 - does this tick jump? Decided by the next 8 bytes of the hash.
  // A hash shorter than 16 bytes has no jump window, so it cannot jump.
  let jumpRaw = null, threshold = null, jumped = 0;
  if (pfe.length >= 16) {
    jumpRaw = readUint64(pfe, 8);
    const scaled = JUMP_PROBABILITY * 2 ** 64;
    threshold = scaled >= 2 ** 64 ? 2n ** 64n - 1n : BigInt(Math.trunc(scaled));
    jumped = jumpRaw < threshold ? 1 : 0;
  }

  // Step 3 - add up the three parts of the movement.
  const drift = DRIFT;
  const jump = -(DRIFT / JUMP_PROBABILITY) * jumped;
  const noise = SIGMA * zt;

  let movement = drift + jump + noise;
  let capped = false;
  if (MAX_MOVE_PER_TICK !== null && Math.abs(movement) > MAX_MOVE_PER_TICK) {
    movement = movement < 0 ? -MAX_MOVE_PER_TICK : MAX_MOVE_PER_TICK;
    capped = true;
  }

  const out = [];
  out.push('');
  out.push('  Entropy hash   ' + ENTROPY_HASH);
  out.push('');
  out.push('  Settings used');
  out.push('    drift (mu)         ' + DRIFT);
  out.push('    sigma              ' + SIGMA);
  out.push('    jump probability   ' + JUMP_PROBABILITY);
  out.push('    max move per tick  ' + (MAX_MOVE_PER_TICK === null ? 'none' : MAX_MOVE_PER_TICK));
  out.push('');
  out.push('  Step 1  random value from bytes 1-8');
  out.push('    number read      ' + uRaw);
  out.push('    as a fraction    ' + u);
  out.push('    normal deviate   ' + zt);
  out.push('');
  out.push('  Step 2  jump check from bytes 9-16');
  if (threshold === null) {
    out.push('    hash too short for a jump window, so no jump');
  } else {
    out.push('    number read      ' + jumpRaw);
    out.push('    jump threshold   ' + threshold);
    out.push('    jumped?          ' + (jumped ? 'YES' : 'no'));
  }
  out.push('');
  out.push('  Step 3  the movement');
  out.push('    drift            ' + drift);
  out.push('    jump             ' + jump);
  out.push('    random part      ' + noise);
  if (capped) out.push('    (capped at the market limit)');
  out.push('');
  out.push('    PRICE MOVEMENT   ' + (movement >= 0 ? '+' : '') + round4(movement).toFixed(4));
  out.push('    full precision   ' + movement);
  out.push('');
  out.push('  Compare PRICE MOVEMENT against the movement Crypta published');
  out.push('  for this tick. If they match, the tick is genuine.');
  out.push('');
  console.log(out.join('\n'));

  return movement;
}

verify();


```

{% hint style="info" %}
*Note: Crypta's pricing engine runs on Go instead of Javascript.*
{% endhint %}

### Summary

| Check             | Confirm                                                                                                                            |                                                                                |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Commitment        | `SHA256(secret hash) == Merkle Leaf` and that leaf rebuilds to the published Merkle Root                                           | The secret was fixed before the block hash existed                             |
| Independent Input | The tick's Solana block hash is the same as independent block explorers show                                                       | The other half of the mix is an independent public value Crypta did not author |
| Randomness        | The HMAC-SHA256 of the block hash, keyed with the tick secret hash, equals the final entropy hash                                  | The two halves were combined honestly, with no substitution                    |
| Price Movement    | The first 8 bytes of the PFE give the normal distribution Z<sub>t</sub> value and the next 8 give the tick's Bernoulli probability | The movement follows deterministically from the entropy                        |
