Zero-Knowledge Proofs of Identity and Veracity of Transaction Receipts — A Deep Drive
Modern digital systems increasingly rely on transaction receipts — cryptographically-signed records that prove a payment, transfer, or state-change occurred. Receipts are used in finance, e-commerce, supply chains, blockchains, and auditing. Yet two conflicting requirements often arise:
Veracity: a verifier (customer, auditor, regulator, smart contract) must be able to trust that the receipt is real and corresponds to a genuine transaction.
Privacy & Minimal Disclosure: the prover (bank, custodian, merchant, or user) must not leak sensitive details (amounts, account numbers, customer PII) beyond what’s strictly necessary.
Zero-Knowledge Proofs (ZKPs) offer a principled way to reconcile these requirements: produce cryptographic evidence that a receipt is genuine and satisfies certain predicates — without revealing underlying secrets. This blog explains the problem, designs practical ZK architectures, presents example constructions, explores trade-offs and attack surfaces, and gives guidance for engineers and architects who want to build verifiable yet privacy-preserving receipt systems.
Why ZK for Receipts? Problem Statement
Imagine these scenarios:
A merchant wants to prove to a regulator that they processed $X in refunds this month without revealing individual customer names or order details.
A user needs to prove to a third party that they indeed received a payment from an escrow service, but they don’t want to disclose the sender identity or exact amount.
A blockchain custodian wishes to publish proofs that off-chain aggregation logic (fees, splits) matches on-chain payouts, without publishing user-level balances.
Traditional approaches:
Reveal the whole receipt (loses privacy).
Provide signed receipts with selective redaction (sometimes brittle; signatures over redacted text can be problematic).
Rely on trusted intermediaries/audits (trust and scalability issues).
Goal: allow a prover to demonstrate (a) identity/authenticity of the issuer, and (b) veracity of one or more predicates about the receipt (e.g., amount within bounds, destination in whitelist, timestamps valid), without revealing other private fields. ZKPs let us assert such facts succinctly and non-interactively (with Fiat–Shamir), and can be deployed both off-chain and on-chain.
High-Level Design Patterns
There are several composable patterns for ZK receipt systems. Pick based on threat model, performance needs, and whether on-chain verification is required.
Signed-Commitment + ZK Opening
Encrypt-Then-Proof
Accumulators / Merkle Trees + ZK
SNARK/PLONK on Full Receipt Logic
Proofs over Homomorphic Aggregation
Cryptographic Building Blocks
Digital Signatures (RSA, ECDSA, EdDSA): prove receipt issuer authenticity. Signatures should be over commitment roots or canonical receipt encodings.
Commitments (Pedersen, vector commitments, Merkle): hide receipt fields while committing to their values (binding + hiding).
Zero-Knowledge Proofs:
Encryption (Paillier, ElGamal, KEMs): useful when you want authorized parties to decrypt receipts but prove predicates publicly.
Merkle trees / Accumulators: membership and inclusion audits.
System Architectures — Three Concrete Designs
Minimal-Disclosure Receipt (Merchant → Customer → Auditor)
Actors: Merchant (issuer), Customer (holder), Auditor (verifier).
Data model:
Receipt fields {tx_id, merchant_id, customer_id, amount, timestamp, memo}.
Flow:
Merchant generates a Pedersen commitment C = g^{tx_id} h^{r1} · ... (vector commitment over fields) and signs C with its signing key sk_m.
Merchant issues the signed commitment σ = Sign_sk_m(C) to customer; customer stores C, σ plus the opening randomness.
To prove to an auditor that total refunds this month ≤ limit L without revealing individual receipts, the customer:
Auditor verifies merchant’s signature on commitment root, verifies the Bulletproof (range), and accepts.
Why this works: commitments bind the merchant, signatures prove authenticity; Bulletproofs show the relation about amounts without revealing per-receipt values.
Optimizations:
Use vector commitments to avoid many individual commitments.
Use aggregation-friendly proofs when proving large batches.
On-Chain Verifiable Receipts (Custodian → Smart Contract)
Actors: Custodian (prover), Smart Contract (verifier), User.
Requirements:
Smart contract must accept proofs cheaply and act (release funds, mark invoice paid).
Flow:
Custodian publishes root = MerkleRoot(receipts_batch) and signs it off-chain.
Smart contract stores root (or the custodian publishes a tx containing root).
To trigger on-chain settlement, the custodian submits:
Smart contract verifies π_zk (cheap) and π (cheap hash ops), then executes business logic.
Why this works: zkSNARKs make on-chain checks succinct; Merkle roots allow batching and auditability.
Tradeoffs:
Prover cost could be high (SNARK witness generation).
Need a SNARK system with acceptable trust assumptions (universal/setup).
Identity-Preserving Proof of Payment (User proves they received payment to third party)
Actors: Payer system (bank/escrow, issuer), Payee (receiver, prover), Verifier (third party).
Problem: Payee wants to prove “I received a payment ≥ $X from a whitelisted payer” without disclosing payer’s account or exact timestamp.
Construction:
Issuer signs a structured receipt R = {enc_fields, commitment, metadata} where enc_fields contains encrypted payer identity & amount under issuer’s audit key, and commitment commits to amount and payer_id.
Payee gets a transcript: signature σ, ciphertext C_enc, commitment Com.
Payee constructs a zero-knowledge proof that:
Verifier checks signature on commitment root (ensuring issuer issued it) and verifies ZK proof. No PII revealed.
Notes:
For p ∈ whitelist check, you can either use a Merkle tree or a public-key-style whitelist (e.g., set of public keys), proving membership via Merkle path or set membership circuit.
If verifier must check payer identity but payee must not reveal it publicly, use an interactive protocol where an auditor decrypts C_enc under threshold decryption.
Example Primitives & Small Protocols
Below are compact but concrete proofs useful in receipts:
A. Chaum–Pedersen style proof linking commitment to signed ciphertext
Prove: C = Enc(m; r) (Paillier) and Com = g^m h^s are consistent, and issuer signed Com.
Non-interactive approach:
Prover obtains m and randomness r, s.
Compute sigma-protocol to prove knowledge of m and r such that C = Enc(m; r) and Com = g^m h^s. Use Fiat–Shamir to make non-interactive.
Verifier checks the signature on Com and then the non-interactive proof.
This is ideal when ciphertexts must be published but you want to prove correctness of decryption without revealing m.
B. Bulletproofs for amount-in-range on committed receipts
For each receipt commitment Com(amount, r), build a Bulletproof range proof that 0 ≤ amount < 2^32.
For aggregated bounds (sum ≤ L), use inner-product proofs on vector commitments to show the sum is within bounds without revealing individual amounts.
Bulletproofs are attractive because they are short and trustless.
C. zkSNARK verifying full business logic + signature
Circuit takes as private inputs openings of the receipt (fields), recomputes canonical encoding & signature verification (if expensive, signature verification can be checked outside and signature root on chain).
Circuit checks predicates (fee calculations, tax rules).
Produce proof for on-chain contract to accept.
Threat Model, Attacks & Mitigations
Forgery of receipts: Prevented by signatures (issuer’s private key). Include canonical serialization to avoid malleability.
Prover data fabrication: ZK proofs ensure the prover knows a valid opening; commitments prevent semantic manipulation.
Replay / double-spend of receipts: Include unique tx_id, timestamps, and optional nonces or sequence numbers in commitments; verifiers should check freshness.
Side-channel leakage: Implement constant-time primitives and careful randomness generation for ZK proof witnesses.
Metadata leakage: Even if plaintexts are hidden, traffic patterns or proof timing might leak info — consider batching and padding.
Trusted setup risks (SNARKs): Prefer universal or trusted-setup-free proofs (Bulletproofs, STARKs, Halo2 with updatable setup) if you cannot accept toxic-waste risk.
Compromised issuer key: Use key rotation, multi-sig for issuer keys, or threshold signing for high-value issuers.
Prover collusion with issuer: Minimize need to trust issuer; require third-party auditors or threshold decryption keys split among auditors.
Performance & Practical Considerations
Choosing the ZK primitive:
Batching: Aggregate many receipts into a single proof to amortize prover cost.
Proof size vs prover time: SNARKs provide small proofs but heavy prover time; Bulletproofs are larger but no trusted setup.
Storage: Keep transcripts (commitment roots, signatures, ZK proofs) in immutable logs for audits.
Key management: Use HSMs or threshold signing for issuer private keys.
Audit mode: Allow an auditor with special decryption rights (threshold decryption) to reveal selected receipts with verifiable logs.
Real-World Use Cases & Scenarios
Regulatory reporting: Banks publish encrypted transaction aggregates with ZK proofs that totals obey constraints, enabling privacy-preserving regulatory oversight.
Payment confirmation without PII: Marketplace proves sellers got payouts meeting SLA, without exposing buyer or seller PII.
Supply chain proof of delivery: Logistics provider proves package transfer events occurred (signed commitments) and that delivery receipts meet contract rules (time windows) w/o exposing customer addresses.
Privacy-preserving refunds: Prove refund totals and compliance without publishing individual customer claims.
On-chain triggers: Off-chain systems produce zk proofs that certain off-chain events happened to trigger on-chain state changes (e.g., insurance payouts conditional on verified off-chain receipts).
Implementation Checklist & Starter Plan
Define the receipt schema and canonical serialization.
Choose commitment scheme (Pedersen or vector commitment) and signature algorithm. Ensure both are compatible with ZK tools chosen.
Identify predicates to prove (range, membership, equality, complex business logic).
Pick ZK primitive:
Design proof lifecycle:
Implement canonical proof generation and verification modules; integrate with signature verification and commitment checks.
Perform security review and cryptographic audit; test edge cases and replay scenarios.
Deploy with logging, monitoring, and a plan for key rotation and incident response.
Conclusion
Zero-knowledge proofs let us re-imagine receipts as verifiable but privacy-preserving artifacts. By combining commitments, signatures, ZK primitives (Bulletproofs, SNARKs, Sigma protocols), and accumulators, we can build systems where issuers and holders prove facts about transactions with mathematical certainty — without mass disclosure