@proofchain/verify-core
v0.1.1
Published
Trustless browser verification for ProofChain events and Score Receipts — Merkle proofs + on-chain root checks, zero dependencies
Readme
@proofchain/verify-core
Trustless, browser-friendly verification for ProofChain events and Score Receipts.
Zero runtime dependencies — recomputes Merkle proofs and checks on-chain roots
using nothing but fetch and the Web Crypto API, so it can run entirely
client-side without trusting the ProofChain API.
Install
npm install @proofchain/verify-coreRequires Node 18+ (or any modern browser) for global fetch and crypto.subtle.
Usage
1. Verify a single event from the proof-bundle endpoint
import { verifyEvent } from '@proofchain/verify-core';
const bundle = await fetch(
`https://api.proofchain.co.za/verify/event/${eventId}/proof-bundle`,
).then((r) => r.json());
const result = await verifyEvent(bundle);
console.log(result.level); // 'invalid' | 'merkle_valid' | 'chain_confirmed'2. Verify a receipt fetched from /verify/receipt/{id}
import { verifyReceipt } from '@proofchain/verify-core';
const manifest = await fetch(
`https://api.proofchain.co.za/verify/receipt/${receiptId}`,
).then((r) => r.json());
const result = await verifyReceipt(manifest);
console.log(result.level); // overall verification level
console.log(result.events); // { total, merkleValid, chainConfirmed, unresolved }
console.log(result.scoreCheck); // advisory recomputed score sum3. Override the RPC endpoint (or skip chain checks entirely)
import { verifyReceipt } from '@proofchain/verify-core';
// Use your own Polygon RPC provider instead of the public default.
const result = await verifyReceipt(manifest, {
rpcUrl: 'https://polygon-mainnet.g.alchemy.com/v2/<key>',
timeoutMs: 5_000,
});
// Or verify Merkle inclusion only, with no network calls at all.
const offlineResult = await verifyReceipt(manifest, { skipChainCheck: true });Threat model
This package answers: "does this event/receipt data match what ProofChain committed on-chain?" It does not answer "is ProofChain trustworthy" or "is the API currently serving correct data" — it lets you check the API's claims independently.
Verified in v1:
- The leaf hash is recomputed from the event's own public fields
(
certificate_id,document_hash,event_id) — the API cannot silently substitute a different document and still pass. - Merkle inclusion — the leaf really is included under the claimed root, via an independently-walked proof (OpenZeppelin sorted-pair convention).
- On-chain confirmation — for a given
tx_hash, the transaction succeeded, was sent to the expected contract address, and itsattestBatch(...)calldata'smerkleRootargument equals the claimed root, via a direct Polygon RPC call the verifier controls.
NOT verified in v1 (explicitly deferred):
- The
attestBatchselector,batchId, andtenantIdcalldata arguments are not bound via keccak256 (that needs a vendored keccak implementation or an extra dependency); only the 256-bitmerkleRootequality is checked. - Block finality depth — a confirmed transaction could still be reorged on a very recent block; this package does not wait for or check confirmation depth.
- Epoch Merkle inclusion —
epochLeafValidrecomputes and compares the epoch leaf hash, but does not walk a Merkle proof against an epoch tree root (the manifest carries only the leaf fields in v1, not an inclusion proof).
Score-recompute caveat
scoreCheck in verifyReceipt's result is advisory, not proof. It sums
each event's contribution as a 4-decimal-place fixed-point BigInt (never
floating point, so no drift) and compares it to totals.total_score. When the
receipt is truncated, or any event is unresolved, the sum is reported as
an upper bound with exact: false and an explanatory note. Even when
exact is true, this is not a cryptographic guarantee: daily-cap-zeroed
events are not flagged in the v1 manifest, so a capped event's true
contribution to the score may differ from what's shown, and the recomputed
sum can only ever be checked against the manifest's own totals — not against
data the manifest doesn't carry.
API
See src/types.ts and src/receipt.ts
for full type definitions, including EventProofBundle, ReceiptManifest,
VerifyOptions, EventVerifyResult, and ReceiptVerifyResult.
