Skip to content

ODATANO/DAYZERO

Repository files navigation

@odatano/dayzero (DAYZERO)

Tests codecov npm Aiken License

Pure-TypeScript zero-knowledge toolkit for DAYPASS with Groth16 proving and verification plus Poseidon hashing on BLS12-381, with a hand-written Aiken on-chain verifier.

Features

  • Poseidon BLS12-381 (t=3, alpha=5, RF=8, RP=57): the Poseidon paper's Grain-LFSR instantiation, params machine-generated and regression-locked, plus a generic field->leaf Merkle tree (buildPoseidonFieldTree).
  • Groth16 verification on @noble/curves: batched Miller loops, the compressed ZCash point encodings (G1 48 B / G2 96 B) that blst and the Cardano Plutus builtins use.
  • Groth16 CRS setup + prover: radix-2 FFT domain (coset-FFT quotient, Z(x) = x^n - 1), secret-free proving keys (toxic waste generated and discarded), multi-core MSM prover via worker_threads.
  • Circuit toolkit: small R1CS CircuitBuilder, Poseidon hash/Merkle gadgets, bounded comparators, a reusable field-threshold predicate circuit.
  • Aiken on-chain verifier (contracts/daypass-predicate-policy/): Plutus V3 Groth16 verification with the VK applied as script params. The minting policy binds the token to its public inputs: asset name = blake2b-224 over the serialised datum, read from the output carrying the token. Measured on Preview: mem 173k / steps 2.71B per verify (~27 % of a tx budget), 1797 bytes with the VK applied.
  • DAYPASS sidecar:: a small HTTP server (/health, /commit, /prove, /validator).
  • Portable verification (scripts/verify-zk-mint.mjs): re-verifies a live zk mint from public chain data only by fetching proof/datum via Blockfrost, VK bound to the policyId via script hash, pairing re-executed locally.

Quick start

npm install
npm test            # 76 unit tests
npm run typecheck
import {
  POSEIDON_BLS12_381_T3 as P, poseidonHash,
  setupGroth16, createGroth16WorkerProver, verifyGroth16,
} from '@odatano/dayzero';

const leaf = poseidonHash(P, fieldKey, value);

const { provingKey, verificationKey } = setupGroth16(r1cs);    // toxic waste discarded
const prover = createGroth16WorkerProver(provingKey);          // multi-core MSM
const { proof, publicInputs } = await prover.prove(witness);
verifyGroth16(verificationKey, proof, publicInputs);           // true

DAYPASS sidecar

# generate proving keys + Aiken validator registry (NEW policyIds every run guard artifacts/*.json like trust roots)
node --import tsx scripts/build-daypass-validator-registry.mjs --crs

DAYZERO_DAYPASS_PROVING_KEYS=artifacts \
DAYZERO_DAYPASS_VALIDATOR_ARTIFACTS=artifacts/daypass-validator-registry.crs.json \
# 127.0.0.1:8799, DAYPASS_ZK_PROVER_URL points here
npm run daypass:server

Environment:

Variable Default Meaning
DAYPASS_PROVER_PORT 8799 listen port
DAYPASS_PROVER_HOST 127.0.0.1 bind address; set 0.0.0.0 explicitly to expose it (e.g. inside Docker)
DAYZERO_DAYPASS_PROVING_KEYS unset directory with daypass-groth16-setup.<op>.json
DAYZERO_DAYPASS_VALIDATOR_ARTIFACTS unset validator registry JSON served by /validator
DAYZERO_ALLOW_DEV_PROVER unset 1 opts into the INSECURE dev prover when no proving keys are configured
DAYZERO_PROVER_THREADS cores MSM worker count (1 = single-threaded)

Startup refuses footgun configurations: without proving keys the sidecar only starts with the explicit DAYZERO_ALLOW_DEV_PROVER=1 opt-in (never in NODE_ENV=production; the dev setup's toxic waste is public, its proofs are forgeable), and when proving keys AND a registry are configured their verification keys must belong to the same trusted setup. Request hardening: bodies over 256 KiB get 413, non-JSON content types 415, more than 2 concurrent proofs 429. API details: docs/daypass-sidecar-contract.md.

Trust roots and packaging

The npm package deliberately ships code and the Aiken contracts only, NOT artifacts/: the proving keys and the validator registry are ACTIVE trust roots, and consumers must pin the exact set their on-chain policies were built from (DAYPASS commits them in its own repo and mounts them into the sidecar container). Regenerating them silently changes the VK and the policyIds; distribute them out of band (consumer repo, release assets with checksums), never implicitly through npm.

Layout

src/poseidon/        hash, params (generated), field tree
src/groth16/         verify, CRS setup/prover, FFT, worker pool, circuit builder, gadgets
src/daypass/         DAYPASS preset: field registry, predicate circuit, sidecar
contracts/           Aiken Groth16 verifier + predicate minting policy (34 tests incl. fuzz)
artifacts/           proving keys + validator registry (the active trust roots)
scripts/             generators, registry builder, benchmarks, portable verifier
docs/                sidecar contract, dev-prover notes, MPC ceremony plan

Development commands

# embed fresh dev proofs + fuzz suite
node --import tsx scripts/gen-aiken-test-vectors.mjs
 # on-chain verifier tests (aiken v1.1.21)       
cd contracts/daypass-predicate-policy && aiken check
 # time setup/prove/verify      
node --import tsx scripts/bench-crs.mjs                  
BLOCKFROST_API_KEY=... node --import tsx scripts/verify-zk-mint.mjs \
  <mintTx> artifacts/daypass-validator-registry.crs.json <anchorTx> <sourceField>

Compatibility contract

Invariants the on-chain data model depends on, all covered by tests:

Invariant Where
Poseidon params (prime, t, alpha, RF/RP, C, M) Grain-LFSR generated; locked fixtures
hashN left-fold; Merkle empty leaf = Poseidon(0,0); direct path bits src/poseidon/
Point encoding: compressed ZCash, G1 48 B / G2 96 B src/groth16/points.ts
Verify equation + public-input commitment src/groth16/verify.ts, mirrored in groth16.ak
Sidecar HTTP contract (request/response shapes) src/daypass/sidecar.ts

Security status

  • ⚠️ Work in progress, unaudited, do not use on mainnet
  • The dev setup is deterministic with PUBLIC toxic waste: anyone can forge proofs for dev verification keys. Tests and vector generation only.
  • The CRS setup discards its toxic waste, but it still exists in one process's memory during generation. Before any production use: run the MPC trusted-setup ceremony (docs/mpc-ceremony-plan.md) and get the ~40 soundness-critical lines of contracts/.../lib/daypass/groth16.ak externally audited.
  • Regenerating a setup changes the VK and therefore the on-chain policyId — guard the artifacts/ files like trust roots; transactions minted under a discarded registry can no longer be portably re-verified.

Live evidence (Cardano Preview)

What Tx
mint ae0d4c...05af8f
zkProve 5a5c0a...57d52c

The zkProve mint runs the full Groth16 pairing check on-chain AND enforces the v2 token binding: the minted asset name is the blake2b-224 commitment to the datum's public inputs.

Acknowledgements

Parts of DAYZERO were conceptually derived from prior work:

  • ZeroJ: the Java ZK library whose prover sidecar DAYZERO replaces. The on-chain verifier semantics (canonicality checks, pairing arrangement, redeemer/datum layout) and the Poseidon tree conventions were adopted from it to stay drop-in compatible with the existing DAYPASS implementation.
  • hadeshash: reference implementation of the Poseidon paper; the BLS12-381 parameters are its Grain-LFSR output.
  • @noble/curves: and @noble/hashes: (Paul Miller) audited pure-JS cryptography DAYZERO builds on.

License

Apache-2.0.

About

Pure-TypeScript zero-knowledge toolkit for DAYPASS with Groth16 proving and verification plus Poseidon hashing on BLS12-381

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors