# 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.zip` from the publication page, unzipped. - `jq`, `openssl` 3.x, `sha256sum` (or `shasum -a 256`), `base64`, `xxd` and `python3`. ## 2. Check the bundle hash `sha256sum bundle.json` equals `jq -r .bundle_hash bundle.sig`. ```bash 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 ```bash 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`. ```bash 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 ```bash 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): ```bash 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-.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`: ```bash export MH=$(jq -r .manifest_hash peaknode-balance-.json) jq -c '.bundles[] | {symbol: .asset.symbol, balance}' peaknode-balance-.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.