check a proof

A grade's or a call's, here in your browser. Nothing is sent anywhere, and a saved copy works without us.

or drop it here, or paste it below, or a card's link

How it checks a grade
  1. The claim is the proof without signature and anchor, as canonical JSON: keys sorted, no spaces, empty fields left out.
  2. The signature is Ed25519 over the claim's UTF-8 bytes, in base64, by the key in its key (crayons.gg's are below). Each key's SHA-256 is also on Midnight, as a line of crayons.gg's log (below), its challengeKey SHA-256 of crayons.gg proof key <key>.
  3. The leaf is SHA-256, in hex, of the canonical JSON of {"kind":"crayons.gg grade record","v":1,"playerId","period","grade","reachedAt","rating","calls","entryId"}, valued from the claim (playerId is its player's id).
  4. The path leads from the leaf to the day's root: at each step, SHA-256 of the two 32-byte hashes joined, the sibling on the side given.
  5. The root is a line of crayons.gg's log once that UTC day is over: {"circuit":"sealChallenge","challengeKey","root","prev","at"} as JSON, its keys in that order, its challengeKey SHA-256 of crayons.gg grades <day>.
  6. The batch path leads from the line's SHA-256 to its batch's root, as a grade's path does. The log goes on the crayons.gg contract on Midnight in batches: each batch's root is in the contract's sealedRoots under SHA-256 of crayons.gg ledger batch <first>-<last>, its first and last line. Any Midnight indexer can confirm it. From 21 Oct the log goes on the log contract instead, whose state doesn't grow: it keeps one running head, SHA-256 of the head before a batch, its first and last line (8 bytes each, little endian) and its root, and the batches must tile the log. The proof gives the head before and after its batch; the contract's state as of the batch's block holds the head after, which any Midnight indexer can show.
How it checks a call
  1. The fingerprint is SHA-256 of the canonical JSON of {"challengeId","playerId","drawing","salt","stake","sentAt"} (stake left out when 0; sentAt, the call's submittedAt, only for rounds from 20 Oct 2026, whose id ends at 1792454400 or later). It's what was sealed when the round closed.
  2. The round's key is SHA-256 of its id (challengeId).
  3. The seal path leads from the fingerprint to the round's sealed root, as a grade's path does.
  4. The score's leaf is SHA-256 of the canonical JSON of {"challengeId","call","score"} (call the fingerprint); the results path leads from it to the scores' root.
  5. The results root is SHA-256 of the canonical JSON of {"v":2,"challengeId","pricesHash","scores","stakes","prize"} (scores the scores' root; stakes and prize left out when there are none).
  6. Both roots are lines of crayons.gg's log, as a grade day's root is: the sealed root's circuit is sealChallenge, the results root's publishResults, each with the round's key. Each line goes on the crayons.gg contract on Midnight in a batch, its path to the batch's root in the proof, the batch's root in sealedRoots under its own key, as above. The player's id is sealed; their name isn't.
  7. Before 17 Oct, on Midnight Preprod, a root went on the contract on its own, under the round's key (a results root with publishResults). The whole record moved to Mainnet on 21 Oct, in two batches; a proof keeps its Preprod receipt as earlier, which shows when the root first reached Midnight.

crayons.gg's signing keys:

crayons.gg's contracts on Midnight: