case file w-0001mainnet slot———————
file w-0005 docs · methods & sources

How it
works.

Every number on this site comes from the code described here. If something looks too good, this page tells you exactly what was run.

Groth16 circuits & setup

Two circom circuits, compiled with circom 2.2.3 (--O2):

CircuitProvesConstraintsPublic signals
preimageMiMC7(secret) = fingerprint3641
allowlistPoseidon(secret) is a leaf of a depth-12 tree with this root; nullifier = Poseidon(secret, scope); recipient bound3,3704

Phase 1 is the Privacy & Scaling Explorations Perpetual Powers of Tau, the file published after contribution 80, cut to 2¹² powers (ppot_0080_12.ptau). Phase 2 is a single contribution made by our build script with 64 bytes of OS randomness that were never written down. Each proving key was then verified against its circuit and the phase-1 file. That's a demo-grade setup: anyone who kept the phase-2 randomness could forge proofs for these circuits. Real deployments run a phase-2 ceremony with many contributors.

Phase-2 contribution hashes, so you can check the keys you download from /zk/:

  • preimage: 466d63a0dd7a6b7a024ff9977af14378…
  • allowlist: b28a8b15fafd1f8577d3ce540a2dd287…

Proofs are made with snarkjs in the browser and checked twice: by snarkjs, and by an independent pairing check (noble-curves) on the 256-byte encoding a Solana program receives. That encoding is −A (64 bytes), B with imaginary parts first (128), and C (64), all big-endian, which is the layout groth16-solana takes.

The STARK

Written for Witness in TypeScript (src/lib/stark) to prove the same statement as the preimage circuit with nothing but hashes. Parameters:

StatementI know s with MiMC7(s, k = 0) = h
FieldBN254 scalar field (the same one circom uses), 2-adicity 28
Trace128 rows, one column: the 92 MiMC states, then padding rounds
Constraintstransition T(gx) = (T(x) + c(x))⁷ on all rows but the last; boundary T(g⁹¹) = h
Zero-knowledgetrace masked by Z_H(x)·R(x), deg R < 128; random masking polynomial added to FRI; 16-byte salts on every trace and composition leaf
Low-degree testFRI, degree bound 2,048, blowup 16, domain 32,768, folding factor 2, 7 committed layers, final polynomial of 8 coefficients
Out-of-domain checkDEEP-ALI: trace opened at z and g·z, composition recomputed at z by the verifier
Commitments & Fiat–ShamirSHA-256 Merkle trees (domain-separated leaves and nodes) and a SHA-256 transcript
Queries26, plus 16 bits of proof-of-work grinding
Proof size99,629 bytes, fixed for these parameters
Security120 bits conjectured (26 × log₂ 16 + 16). Provable bounds are lower; field and hash sizes are not the bottleneck.

The test suite proves and verifies, flips bits throughout the proof, tries the wrong fingerprint, and feeds the verifier a prover that cheats on a single trace row. All of those are rejected. It is teaching code: constant-time behaviour, side channels and formal soundness analysis are out of scope.

Encrypted balances

Twisted ElGamal on ristretto255 (noble-curves), built the way Solana's zk-sdk builds it: H is the ristretto point derived from SHA3-512 of the compressed base point (8c9240b456a9e6dc65c377a1…), the public key is P = s⁻¹·H, and a ciphertext is C = x·G + r·H with D = r·P. Decryption computes C − s·D = x·G and runs baby-step giant-step for x. The lab keeps amounts to 16 bits and decrypts sums up to 20 bits, the same reason Solana splits amounts into 16- and 32-bit pieces.

The Q-day rehearsal generates a key whose s⁻¹ is only 24 bits, so the same baby-step giant-step search recovers it from the public key alone. Nothing about that run is simulated. What it stands in for is Shor's algorithm, which does the full-size version on a large enough quantum computer.

Transaction sizes

Sizes come from real transactions built and signed with @solana/web3.js against a placeholder program id (no Witness program is deployed). A one-signer legacy transaction has 204 bytes of fixed cost (signature, header, three account keys, blockhash, instruction framing), leaving 1,028 bytes of the 1,232 for data. v1 transactions raise the limit to 4,096 bytes. We estimate their room as 3,892 bytes with the same overhead. On-chain verification costs come from groth16-solana's benchmarks: 78,293 CU with one public input and 91,448 with four.

Wallet check

Our server reads public data, so no browser keys or connections are involved:

  • Solana RPC: the account, up to 3,000 signatures, the first transaction and the funder's recent history, a sample of recent transactions, the SOL balance and token accounts.
  • Jupiter's price API for USD values, and its token list for names. Only verified tokens are shown by name.
  • The SNS proxy for a primary name.

The heuristics, plainly:

  • Busy hub: the funder has 1,000 transactions within three days. Exchanges, bridges and bots look like this. People don't.
  • Linked wallet: another wallet (on-curve key, so not a program address) on the other side of a plain SOL or token transfer, meaning a transaction that uses only the System, Token, Token-2022, ATA, Memo or Compute Budget programs. Jito tip accounts are ignored.
  • Time zone: the quietest six hours of the day, assuming sleep around 01:00–07:00 local time. This needs at least 40 transactions over 5 or more days, and gets low confidence when activity is spread evenly.

On the public RPC the scan reads 12 recent transactions, paced to stay inside its limits. With a dedicated RPC it reads 40. The score is the sum of the findings' points, out of 100.

FAQ

+Is this an audit or a security certification?

No. Witness is a place to learn and measure. The STARK prover was written for this site and is tested here, but nobody has audited it. The Groth16 setup has a single phase-2 contributor. Neither should protect money.

+Does anything I type leave my browser?

Not in the lab. Secrets, keys and proofs are made and checked in the page, and the heavy work runs in Web Workers in the same tab. The wallet check sends the address you paste to our server, which reads public mainnet data and keeps the result for ten minutes so a refresh is free. It is not logged.

+Why is the STARK proof so big?

It commits with Merkle trees and proves low degree with FRI, so it has to open 26 random positions, each with Merkle paths through several layers. That's the price of using only hashes. Production STARKs are in the same range: Winterfell reports 51–252 KB depending on the security level.

+Could Solana verify the STARK?

Not in one transaction. It's 99,629 bytes, and a transaction carries at most 1,028 bytes of payload (about 3,892 in the new v1 format). A program could collect it into an account over 97 transactions (about 26 with v1) and then check it in steps. Nobody has deployed such a verifier.

+Is Groth16 broken today?

No. Breaking it needs discrete logarithms on BN254, which no classical computer can compute at this size. BN254's estimated security is about 100 bits, below the 128 its old name promised but still far out of reach. The risk is a future quantum computer, and only for new forgeries.

+What happens to a confidential balance on Q-day?

Whoever saved its ciphertexts can read them. The ElGamal public key is on chain, Shor's algorithm turns it into the decryption key, and every amount ever encrypted to it opens. The lab rehearses this with a 24-bit key that a laptop can break.

+Why MiMC7 for the secret experiment?

It's a hash built from field arithmetic: 91 rounds of x ↦ (x + c)⁷. That makes it cheap in a circuit (364 constraints) and easy to express as a one-column STARK, so both proof systems can prove exactly the same statement over the same field.

+What is $WITNESS?

The project's token. It has not launched. When it does, the address will appear in the token panel on the home page first. Treat any address you see elsewhere as fake.

Background reading: the primer, five chapters with sources.