Trust Model Audit
Groth16 · BLS12-381 · Stellar TestnetFull breakdown of what is private, what is publicly verifiable on-chain, and what is enforced off-chain. Judges and auditors should read this page alongside the circuit code.
AidShield is not anonymous settlement. It is private eligibility with public accountability: the chain receives enough data to verify payment and block replay, while the credential witness and beneficiary identity stay off-chain.
Delivered inside the signed credential so the beneficiary can prove locally. During claim it is not sent on-chain or to the verifier.
Stored in the disbursement contract and checked against the proof's public inputs on every claim.
The contract persists every spent nullifier (Poseidon(secret, disbursement_id, claimant_address, 1)) and rejects duplicates.
Verified by the Groth16 verifier contract using native BLS12-381 pairing_check on Soroban. Cannot pass without a valid witness.
Phase 4: the Merkle leaf is Poseidon(secret, disbursement_id, claimant_address, expires_at, issuer_key_id). The wallet, expiry, and issuer are committed into the tree at campaign-generation time. A stolen secret cannot generate a valid proof for a different wallet or later expiry.
The operator signs credentials with an Ed25519 key. Signature verification happens client-side before proof generation. The operator must vet eligibility off-chain.
The expiry timestamp is a public circuit input bound into the Merkle leaf and checked against ledger time by the disbursement contract.
The issuer_key_id is committed into each Merkle leaf. The contract accepts claims only when that issuer key is active and rejects claims after revocation.
Fixed in the disbursement contract at initialisation. Cannot be changed without a contract upgrade.
Held in XLM inside the contract. Released directly to the claimant's wallet upon a valid proof submission.
Names, IDs, aid-list membership, and Merkle witness stay off-chain. The claimant wallet is public settlement data and can be linked like any Stellar address.
Includes nullifier, amount, and claimant wallet. Voucher events also include vendor wallet. These are public accountability data, not anonymous payout data.
Nullifier stored permanently after first claim. Contract rejects any reuse.
Groth16 soundness — computationally infeasible to produce a valid proof without the witness. Verified via native BLS12-381 pairing.
Public input checked against on-chain root. Proof for a different root fails verification.
Secret is a private input. Groth16 zero-knowledge property prevents extraction from proof bytes.
Phase 4: leaf = Poseidon(secret, disbursement_id, claimant_address, expires_at, issuer_key_id). A different wallet generates a different leaf, so no Merkle proof exists for it. The nullifier remains wallet-bound.
ISSUER_PUBLIC_KEY is hardcoded in the claim frontend. Ed25519 signature verified before proof generation begins.
The issuer key id is a public proof input and must be active in the contract registry when the claim is submitted.
Voucher redemption checks the vendor allowlist before proof execution and payout transfer.
AidShield is not an open mixer. Eligibility is campaign-scoped, issuer-bound, expiry-bound, wallet-bound, and funded from a controlled escrow.
- Credential expiry: Now bound into the ZK leaf and enforced against ledger time by the disbursement contract.
- Issuer governance: The upgraded contract supports issuer add/revoke plus threshold governor co-signers for sensitive admin operations. Production should add per-issuer limits and a guided multi-party signing UX.
- Trusted setup (Groth16): The proving key was generated with a single-contributor ceremony (demo only). Production requires a multi-party trusted setup to eliminate the toxic waste risk.
- Issuance uniqueness: Production should configure Upstash Redis REST variables so credential issuance reserves campaign slot and wallet keys with SET NX. Without Redis, the app uses a locked local-file fallback for demos.
- Browser proving: A compromised frontend could read a loaded credential witness. Use the official deployment, keep CSP enabled, rotate credentials after suspected compromise, and prefer short expiries.
- Public settlement: Stellar settlement remains public: payout wallet or vendor, route, amount, timing, contract ids, root, and nullifier are visible by design.