Skip to content

Add ZK email offchain token transfer example#35

Open
critesjosh wants to merge 11 commits intonextfrom
jc/add-zkemail-verification-example
Open

Add ZK email offchain token transfer example#35
critesjosh wants to merge 11 commits intonextfrom
jc/add-zkemail-verification-example

Conversation

@critesjosh
Copy link
Copy Markdown
Contributor

@critesjosh critesjosh commented Apr 15, 2026

Summary

  • Adds an example of offchain-authorized token transfers using DKIM-signed emails on Aztec, following the offchain transfer tutorial pattern
  • Bob deposits tokens into an EmailClaim contract mapped to his email address hash. Carol creates a partial note, gets Bob's email authorization, generates a zkEmail proof offchain, and submits it to claim her tokens
  • Noir circuit verifies RSA-2048 DKIM signatures from icloud.com, extracts sender/recipient addresses, subject, and timestamp, and outputs 7 public fields
  • Aztec contract verifies the UltraHonk proof via verify_honk_proof, enforces a 6-point security model (DKIM key binding, sender identification, recipient binding, intent binding, single-use nullifier, freshness), deducts from the depositor's balance, and completes the recipient's partial note
  • Includes local and testnet deployment scripts, proof generation, and Vitest integration tests
  • Built with zkemail.nr on Aztec 4.2.0

Security model

  1. DKIM key binding -- proof must use the trusted mail server key
  2. Sender identification -- from-address hash identifies the depositor
  3. Recipient binding -- email must be sent to the expected address
  4. Intent binding -- email subject must match the expected action
  5. Single use -- email nullifier prevents replay
  6. Freshness -- DKIM timestamp must be recent

Test plan

  • yarn install and nargo compile in circuit/
  • yarn ccc to compile contract and generate bindings
  • yarn data to generate proof data
  • yarn test against local network (deploys token + EmailClaim, deposits, creates claim, verifies proof, checks balances)
  • yarn testnet for testnet deployment

🤖 Generated with Claude Code

critesjosh and others added 2 commits April 15, 2026 10:02
Noir circuit verifies DKIM-signed emails from icloud.com and an Aztec
contract verifies the proof on-chain using verify_honk_proof. Includes
testnet deployment scripts, integration tests, and proof generation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
critesjosh and others added 5 commits April 15, 2026 10:16
…nullifier, and freshness checks

Rework the circuit and contract from a simple domain-verification demo into a
full email-based authorization flow: recipient binding via to-address hash,
intent binding via subject hash, single-use nullifiers from the DKIM signature,
and timestamp freshness checks. Update README to document the security model,
contract API (including view functions), and fix Poseidon2 references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The contract now stores trusted DKIM public key hashes and verifies them
against proof outputs, preventing forgery with self-generated keypairs.
Update the security model (4 → 5 constraints), constructor signature
(3 → 5 params), verify_email steps, diagram, and troubleshooting to
match the actual contract code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Store trusted DKIM public key hashes at deployment and verify them
against proof outputs in verify_email, preventing forgery with
self-generated keypairs. Tighten timestamp extraction to ensure the
t= tag falls strictly within the DKIM-Signature header field. Make
header field parsing case-insensitive per RFC 5322.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address review comments:

[P2] Revert case-insensitive header field matching in generate_data.ts.
The header input is DKIM-canonicalized (c=relaxed), so field names are
always lowercase — case-insensitive matching was unnecessary and masked
the real requirement. Update circuit and script comments to state the
canonicalization requirement definitively.

[P3] Rewrite DKIM key mismatch troubleshooting to direct operators to a
trusted DNS source rather than the rejected proof's own public inputs,
which are prover-controlled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@critesjosh critesjosh marked this pull request as ready for review April 15, 2026 18:19
… partial notes

Renames ZKEmailVerifier to EmailClaim and integrates the offchain transfer
tutorial pattern: Bob deposits tokens mapped to his email address hash, Carol
creates a partial note and completes it by submitting a verified zkEmail proof.
The circuit now outputs 7 public inputs (adds from_address_hash for sender
identification), the contract manages token balances and partial note completion,
and the security model expands from 5 to 6 points. Also hardens timestamp
parsing to reject empty t= values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@critesjosh critesjosh changed the title Add ZK email verification example Add ZK email offchain token transfer example Apr 15, 2026
critesjosh and others added 3 commits April 15, 2026 15:20
Replace testWallet.setPublicAuthWit().send() with the explicit
SetPublicAuthwitContractInteraction.create() API, which is the
correct way to set public authwits on EmbeddedWallet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clarify what stays private (email content, Carol's identity) vs. what
is public (deposit amounts, email address hash linkability, claim
amounts, deposit-to-claim linkage).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…artial notes

Replaces UintNote::partial() with manual partial note creation that
accepts a randomness parameter, allowing Carol to compute the commitment
offchain before the transaction lands. Updates scripts and tests to
compute commitments client-side via poseidon2HashWithSeparator and pass
{ commitment } to claim_with_email.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant