Skip to article
Browse documentation +
BUILD ON IT / ELPIS

Canonicalization

The exact procedure behind commitmentHash and attestationReason (elpis-canon/1), so any implementation — yours, an auditor's — produces the same bytes.

Why this page exists

If you cannot recompute our hashes, you cannot check our decisions — only trust them.

Two hashes matter. commitmentHash(condition) fixes what “done” means before the work. attestationReason(verdict) is the decision and its rationale, recorded as the ERC-8183 reason. Both are 0x-prefixed lowercase hex SHA-256 digests over UTF-8. The version is elpis-canon/1; a change to any rule below is a new version.

commitmentHash(condition)

Input: an ordered array of acceptance checks.

  • For each check, drop the label field entirely — it is presentation only and never affects the commitment.
  • Emit the remaining fields with keys sorted ascending. Array-valued fields keep their order; an omitted optional field is simply absent (never null).
  • JSON.stringify the array with no insignificant whitespace.
  • "0x" + hex(SHA-256(utf8(json))).
worked example
// condition
[ { "kind": "deliverable_hash", "hash": "0xDEED", "label": "the report" } ]

// canonical (label dropped, keys sorted)
[{"hash":"0xDEED","kind":"deliverable_hash"}]

// commitmentHash
0x0ce2db10fdbfe486bbea23d6e8236bb75b3edbf8c3935227564cf2389bfc2a52

attestationReason(verdict)

Input: { decision, rationale }, where decision is complete, reject or hold, and rationale is an ordered array of strings.

  • JSON.stringify({ decision, rationale }) — exactly those two keys, in that order, with the rationale in the order produced.
  • "0x" + hex(SHA-256(utf8(json))).
  • The rationale strings are byte-significant: change a word and the reason changes.
Settlement decisionVerdict decision
releasecomplete
refundreject
holdhold

What a replay must reproduce

POST /api/v1/attestations/{id}/replay re-runs the pure decision over the stored condition and proof, recomputes both hashes, and reports matches: true only when the recomputed attestation equals the recorded one. The Python SDK is a second implementation of this page, and its tests reproduce the reference hashes from the TypeScript one byte for byte.

No floating point, no reliance on a serializer's default key order, and the proof is not hashed into the commitment — the commitment fixes the criteria, the proof is judged against them.