circuits/)src/main.nr) proves simultaneously:blake2s(election_id) ties the leaf address to the voter's private key.nullifier = blake2s(sk || election_id) is unique per (voter, election) and cannot be computed without the private key.0 <= vote_choice < num_candidates <= 4.noir-bignum, which is incompatible with nargo 1.0.0-beta.13. The blake2s-based substitute has identical security properties for this use case. A migration path to PLUME is documented in circuits/src/main.nr and circuits/Nargo.toml.contracts/)HonkVerifier Auto-generated UltraHonk verifier (from bb write_solidity_verifier) ElectionManager Per-election contract: enforces time window, nullifier dedup, verifier call, tally ElectionFactory Deploys and tracks ElectionManager instancesElectionManager.castVote() (83 bytes32 values):merkle_root 1-32 nullifier bytes (one bytes32 per byte) 33 vote_choice 34-65 election_id bytes 66 num_candidates 67-82 UltraHonk pairing points (protocol-internal, supplied by prover)frontend/)@aztec/bb.js + @noir-lang/noir_js (~10-30 s)Barretenberg.poseidon2PermutationSharedArrayBuffer (configured in vite.config.ts)contracts/deployments/sepolia.json to frontend/src/assets/deployments.json.scripts/ with npx ts-node.DEPTH in main.nr and regenerate the verifier to support more. Candidate names off-chain ElectionManager stores only the Merkle root and candidate count. Names are stored in localStorage keyed by electionId. Private key in browser The demo VotePanel prompts for a private key to generate the ECDSA signature locally. The key never leaves the browser, but production would use a hardware-wallet signing flow instead.Posted Jun 26, 2026
Developed anonymous voting platform using zero-knowledge proofs for Ethereum Sepolia. ZK circuits were written in Noir.