Verifying a Peaknode attestation (bundle format 1)
This procedure lets anyone check a published Peaknode proof of reserves from evidence.zip alone. Every check below uses a field in the evidence files and a command you run yourself, so you do not need to trust the Peaknode API's own verdicts. Unzip evidence.zip and run the commands from that folder.
1. What you need
evidence.zipfrom the publication page, unzipped.jq,openssl3.x,sha256sum(orshasum -a 256),base64,xxdandpython3.
2. Check the bundle hash
sha256sum bundle.json equals jq -r .bundle_hash bundle.sig.
sha256sum bundle.json
jq -r .bundle_hash bundle.sig
3. Check the key
jq -r .key_id bundle.sig appears in attestation-keys.json and in https://api.peaknode.ar/.well-known/peaknode-attestation.json. The key's valid_from is before the publication's published_at, and its valid_to is null or after it.
4. Check the signature
KEY=$(jq -r --arg k "$(jq -r .key_id bundle.sig)" '.keys[] | select(.key_id==$k) | .public_key' attestation-keys.json)
{ printf '302a300506032b6570032100' | xxd -r -p; echo "$KEY" | base64 -d; } > pub.der
openssl pkey -pubin -inform DER -in pub.der -out pub.pem
jq -r .bundle_hash bundle.sig | xxd -r -p > hash.bin
jq -r .signature bundle.sig | base64 -d > sig.bin
openssl pkeyutl -verify -pubin -inkey pub.pem -rawin -in hash.bin -sigfile sig.bin
Expected output: Signature Verified Successfully.
5. Check the reserves manifest
sha256sum reserves-manifest.json equals jq -r .proof_of_reserves.manifest_hash bundle.json. Verify reserves-manifest.sig with step 4's commands, using manifest_hash from reserves-manifest.sig in place of bundle_hash and its own key_id and signature.
sha256sum reserves-manifest.json
jq -r .proof_of_reserves.manifest_hash bundle.json
jq -r .manifest_hash reserves-manifest.sig
6. Check the liabilities commitment
Do the same over liabilities-manifest.json: sha256sum liabilities-manifest.json equals jq -r .proof_of_liabilities.manifest_hash bundle.json and jq -r .manifest_hash liabilities-manifest.sig, and liabilities-manifest.sig verifies with step 4's commands. Then the reserves manifest must name this commitment: jq -r .liabilities_manifest_hash reserves-manifest.json equals that hash.
7. Check the funds and valuation digests
jq -r .funds.document_b64 bundle.json | base64 -d | sha256sum
jq -r .funds_digest reserves-manifest.json
jq -r .valuation.document_b64 bundle.json | base64 -d | sha256sum
jq -r .valuation_digest reserves-manifest.json
Each pair must match. Run the valuation pair only when valuation_digest is not null. The funds document lists every wallet's balance in minor units at the anchored block.
8. Check per-asset liabilities against the commitment
Each manifest line's liabilities (whole units) times 10^decimals equals the partition total (minor units):
python3 - <<'PY'
import json, decimal
decimal.getcontext().prec = 100
b = json.load(open("bundle.json")); m = json.load(open("reserves-manifest.json")); l = json.load(open("liabilities-manifest.json"))
dec = {a["catalog_entry_id"]: a["decimals"] for a in b["assets"]}
parts = {p["catalog_entry_id"]: int(p["total"]) for p in l["partitions"]}
for line in m["lines"]:
minor = decimal.Decimal(line["liabilities"]) * (10 ** dec[line["catalog_entry_id"]])
assert minor == parts.get(line["catalog_entry_id"], 0), line["symbol"]
print("totals match")
PY
9. Check a customer balance proof
Follow verify-liabilities.md, which is not inside evidence.zip. It is published at https://docs.peaknode.ar/verify-liabilities.md (the peaknode verify command or the Go SDK sdk/go/verify). The verifying key is at https://api.peaknode.ar/verify/vk?version=1.
The download after a balance check (on the publication page or the balance page) is peaknode-balance-<code>.json, a JSON object {format, version, code, manifest_hash, bundles} with format set to peaknode-balance-proof and version to 1. Its manifest_hash is the commitment to check ($MH in verify-liabilities.md), and bundles holds the same bundles the inclusion route returns, so the jq '.bundles[]' commands there apply to this file in place of inclusion.json:
export MH=$(jq -r .manifest_hash peaknode-balance-<code>.json)
jq -c '.bundles[] | {symbol: .asset.symbol, balance}' peaknode-balance-<code>.json
10. Check block anchors
Decode the funds document with jq -r .funds.document_b64 bundle.json | base64 -d | jq .anchors. For each entry in anchors[], open the chain's public explorer at number and compare hash and block_time.
11. What this does not prove
Per-user proofs are not in the evidence, and auditors sample users with nonces obtained from the exchange.