Proof of Reserves

Get an inclusion proof for a participant

Changed on

Return the Merkle inclusion proof for one participant in your platform's tree for an asset: the leaf preimage, the sibling path, and the signed root the path resolves to.

This is a POST because the participant code is enumerable and sensitive; keeping it in the body keeps it out of URLs, proxy access logs, and browser history. The response is per-participant and is not cacheable.

The tree is always your own — the platform comes from your authenticated identity. Only participant_code is read from the body. Send nothing else: a stray field is normally rejected with 400, and where request validation runs at the edge instead it is ignored — but a platform identifier that is not your own is denied with 403 rather than honoured.

To verify: recompute the leaf hash from leaf, walk proof_path applying each sibling on the side given by direction and adding sums with checked u128 arithmetic, then require the resulting hash and sum to equal the values inside the verified commitment_cbor of root. Bind the check to the expected platform, asset, and spec_version — a mathematically valid root for the wrong tree still fails the assertion you care about.

This document describes the wire contract, not the hash construction. The exact preimage bytes — hash function, domain separation, field encoding and ordering, the participant-slot derivation, sentinel padding, the internal-node preimage, and the full set of signed CBOR entries — are defined in the versioned Proof of Reserves verification spec issued with the reference verifier at onboarding. You need that document to implement a verifier; this one is not sufficient on its own. If the two ever disagree, treat it as a defect on our side and raise it — do not pick one and proceed.

Note that a Merkle Sum Tree proof conceals which participants the siblings represent, but not their aggregated balances: each sibling's subtree sum is disclosed.

No rate limit is applied on these routes by this API. Edge limits may still apply, so handle 429 and honour Retry-After rather than assuming an unlimited budget.

post/por/proofs/{asset}/{snapshot}

Request

  • Base URL
    https://api.cert.zerohash.com
  • URL
    https://api.cert.zerohash.com/por/proofs/{asset}/{snapshot}
  • Auth
    API key in header X-SCX-API-KEY and API key in header X-SCX-PASSPHRASE

Path parameters

assetstring required
Example:ETH

Chain-qualified asset identifier. A bare symbol denotes the asset on its home chain; a dotted form denotes the bridged asset on that chain.

snapshotstring required
Example:latest

Which tree to prove against. Either the literal latest or a db_snapshot_id hex string to pin a specific historical snapshot.

latest resolves to the commitment with the highest publication_seq for this tree. To pin: take db_snapshot_id from a prior roots response and pass it here — the proof is built against that exact snapshot.

Headers

X-SCX-SIGNEDstring required

HMAC-SHA256 signature of the request, base64-encoded. See the Authentication guide for the exact signing formula.

X-SCX-TIMESTAMPstring required
Example:1678901234

Current Unix timestamp in seconds. Must be within 60 seconds of server time or the request is rejected.

X-Request-Idstring uuid

Optional client-supplied request ID used for tracing and idempotency. Must be a UUID v4. When omitted, zerohash generates one and returns it in the response X-Request-Id header.

Request body

participant_codestring required

Participant code whose inclusion proof is requested, within your own platform tree. This is the only field read. Send nothing else: a stray field is normally rejected with 400, and where request validation is handled at the edge instead it is ignored — but supplying a platform identifier that is not your own is denied with 403 rather than honoured.

Example request

{
  "participant_code": "NHQAYQ"
}

Response

Inclusion proof and the signed root it resolves to

Example response

{
  "leaf": {
    "platform_code": "FP2F20",
    "asset": "ETH",
    "participant_code": "NHQAYQ",
    "balance": "1000000000000000000"
  },
  "proof_path": [
    {
      "sibling_hash": "fabbcb492818c929538f98f3df7c8db3c7d56a05197a73dbfd460c577fac29c5",
      "sibling_sum": "6836672574609458722427808",
      "direction": "LEFT"
    }
  ],
  "root": {
    "platform_code": "FP2F20",
    "asset": "ETH",
    "spec_version": "1.0.0",
    "publication_seq": "1",
    "db_snapshot_id": "10edfd6c58bcae73b5c6bf9f9e88b69cb47173271cb5fc1362121b1544d04f3c",
    "snapshot_ts": "1970-01-01T00:00:00Z",
    "root_hash": "b72605925891e8d8f9c1a9b28f662f34b66f7f9ee9ece8c187cc206c06ac49c4",
    "root_sum": "000000000005a9d6eb0ecc04a85ed3a0",
    "commitment_cbor": "rGhhc3NldF9pZGNFVEhocm9vdF9zdW1QAAAAAAAFqdbrDswEqF7ToGlyb290X2hhc2hYILcmBZJYkejY+cGpso9mLzS2b3+e6ezowYfMIGwGrEnEa3BsYXRmb3JtX2lkZkZQMkYyMGtzbmFwc2hvdF90cwBsYWNjb3VudF90eXBlaWF2YWlsYWJsZWxzcGVjX3ZlcnNpb25lMS4wLjBuZGJfc25hcHNob3RfaWRYQDEwZWRmZDZjNThiY2FlNzNiNWM2YmY5ZjllODhiNjljYjQ3MTczMjcxY2I1ZmMxMzYyMTIxYjE1NDRkMDRmM2NvYXNzZXRfcHJlY2lzaW9uAG9wdWJsaWNhdGlvbl9zZXEAc2V2aWRlbmNlX3BrZ19zaGEyNTZYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGFzc2V0X2NvbmZpZ192ZXJzaW9uYA==",
    "signature": "vcJ7xd0OSR75rkBKW/yc5pO0n3FYdjjExFz+8CqgHvIPtZlQ9vf37wkntHgVCTDFNBttRJCfnNct9sI7W4DxDQ==",
    "signing_key_id": "Wallet:3b779fac-a9c2-4bef-a7b2-05ee8807af96"
  }
}

Changes