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.
- 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 viaworker_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.
npm install
npm test # 76 unit tests
npm run typecheckimport {
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# 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:serverEnvironment:
| 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.
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.
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
# 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>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 |
⚠️ 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 ofcontracts/.../lib/daypass/groth16.akexternally 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.
| 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.
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.
Apache-2.0.