A linear pipeline from purchase event to published review. Six distinct cryptographic operations, five execution environments, three trust boundaries — designed so that no single point sees all the data.
Each step performs one cryptographic operation in one execution environment. The pipeline is linear — no step can be skipped, and each step's output is the next step's input.
The merchant cryptographically commits that a purchase occurred. This is a digital signature — it binds the merchant's identity to the purchase event.
The gateway computes a SNARK-friendly commitment over the purchase fields.
This att_hash is the bridge between the attestation layer and the ZK circuit —
it becomes the public input that the proof must bind to.
Only att_hash (a commitment) crosses the merchant→protocol boundary.
The raw purchase fields stay in the merchant zone.
No PII leaves the merchant's infrastructure.
The consumer's device derives a unique token and a nullifier from the purchase commitment plus private randomness. Domain-separated Poseidon tags ensure the token and nullifier are always distinct, even for the same inputs.
The consumer's device generates a Groth16 ZK-SNARK proving it knows a valid purchase witness — without revealing the purchase details. The verifier learns nothing except that the statement is true.
1,155 constraints · 8 security findings addressed · 8 negative test vectors
rejecting with CONSTRAINT_REJECTED · Commit 3c3017b
A rotating committee of validators independently verifies the proof and attestation, checks the nullifier hasn't been spent, and reaches Byzantine agreement. This is the most crypto-dense step — four distinct operations per validator.
The platform receives the verification result: a boolean and the quorum certificate. It verifies the QC signatures, then publishes the review with a "verified purchase" badge. The platform never sees the purchase data, the consumer's identity, or the ZK witness.
Only { valid: true/false } and the quorum certificate cross the
protocol→platform boundary. No purchase amount, no customer email,
no order ID, no merchant internal data. Commitments-only outside the merchant boundary.
The pipeline's security comes not just from the cryptographic operations, but from the strict separation of who can see what. Each zone has minimum necessary knowledge — nothing more.
att_hashatt_hash (commitment){ valid: true }| Data Element | Merchant | Consumer | Validators | Platform |
|---|---|---|---|---|
| Purchase details | ✓ | ✓ | ✗ | ✗ |
| Customer identity | ✓ | ✓ | ✗ | ✗ |
| att_hash (commitment) | ✓ | ✓ | COMMIT | ✗ |
| ZK proof π | ✗ | ✓ | ✓ | ✗ |
| Nullifier | ✗ | ✓ | ✓ | ✗ |
| Verification result | ✗ | ✓ | ✓ | ✓ |
| Review content | ✗ | ✓ | ✗ | ✓ |
The merchant does know which purchases generated attestations (because they signed them). Unlinkability is from the platform's and validators' perspective, not the merchant's. The merchant cannot see the review content or the consumer's identity on the platform, but they can correlate attestation timing with review publication. Full merchant unlinkability would require blind signatures — a future enhancement, not a Phase 1 property.
At Step 5, the nullifier-spend and review-publish are a single atomic state transition finalized by the quorum certificate. A validator cannot finalize one without the other.
NullifierImpliesReview: Every spent nullifier has a corresponding published review. 7 invariants · 269 states · 0 violations.
NoDoubleSpend: A token cannot be redeemed twice.
If nullifier ∈ NullifierSet, the transaction is rejected —
this is the one-purchase-one-review guarantee.
If consensus itself is compromised (> f Byzantine validators), atomicity guarantees degrade. Below the BFT threshold, this attack is impossible given the verified invariant. Above the threshold, conflicting QCs become possible. Detection: slashing evidence (two QCs for the same sequence number). This is documented in THREAT_MODEL.md §6.3.