Inspiration: The Digital Governance Dilemma

My project was inspired by the core contradiction in building secure, transparent digital governance: the trade-off between privacy and integrity. Standard blockchain voting systems are transparent, which provides integrity (verifying who voted and when), but sacrifices privacy. Traditional voting (e.g., paper ballots) offers privacy but lacks verifiable integrity (easy to cheat). We asked: Can we achieve a verifiable, single-vote system while preserving absolute voter anonymity? The Midnight Network's focus on advanced cryptography provided the perfect answer: Zero-Knowledge Nullifiers. This mechanism allows a user to prove they are performing a unique action without revealing their identity or the token that enables the action. Our goal was to build the cleanest, most secure implementation of a ZK voting contract possible.

How We Built NulliVote I structured the project around the three pillars of a secure decentralized application: State, Witness, and Circuit Logic.

  1. The Contract (PrivateVote.compact) We defined the public ledger state to be as minimal and secure as possible: vote_counts: The public, immutable tally of votes. nullifiers: A Set to store the anonymous receipts (hashes) of all spent votes. This is the cryptographic security barrier.
  2. The ZK Transition (SubmitVote) The voting process hinges on the separation of public input and private witness: Witness (Private): The user's secret, one-time eligibility token, voter_id_secret. This never leaves the user's machine in a real ZK system. Input (Public): The candidate's name and the nullifier_hash (the anonymous receipt generated from the secret).
  3. The Trustless Verification (IsVoteValid Circuit) This is the core security mechanism. The circuit must succeed before the state can be updated. It checks: Integrity Check (The Nullifier): It asserts that the anonymous receipt has not been spent. This is the double-voting prevention mechanism: assert(¬prev.nullifiers.contains(witness.nullifier_hash)) Validity Check: It confirms the candidate exists and, critically, that the nullifier_hash was genuinely derived from the private voter_id_secret (the linkage proof, though mocked in the front-end, is a required property of the ZK circuit).
  4. The Front-End Simulation I built a responsive web interface (HTML/JS/Tailwind) that simulates the full ZK workflow. It uses a JavaScript hash function (generateNullifier) to create the anonymous receipt from the user's input and mocks the circuit's verification against the local USED_NULLIFIERS set, clearly demonstrating the successful vote consumption and the critical double-spend rejection.

Challenges I Faced

  1. ZK Toolchain Integration The most significant challenge was the integration of the ZK proving system within the limited hackathon timeline. I could not achieve end-to-end circuit compilation and proof generation. I chose to prioritize the correctness of the Compact contract logic and built a high-fidelity client-side simulator that perfectly mirrors the security assertions of the IsVoteValid circuit. This allowed us to effectively demonstrate the project's core value—the double-spend prevention—for the judges.

  2. The Eligibility Loop A secure system requires that the initial voter_id_secret be tied to a known group of eligible voters. We realized that simply having a user type a random secret is insecure. The next logical iteration requires integrating a Merkle Tree. A user's ZK proof would need to prove not only that their nullifier is new but also that their voter_id_secret is a leaf within a pre-registered Merkle Tree of eligible voters—all without revealing the secret itself.

What I Learned I gained a deep, practical understanding of how advanced cryptography translates into functional smart contract security primitives. Nullifiers as State Consumption: I learned that the nullifier isn't just a random hash; it's a state consumption token. Its inclusion in the public set is the irreversible action that proves a one-time secret has been spent, making it foundational for private, unique actions. The Circuit is the Law: I solidified the concept that in ZK systems, the contract's power lies not in the transition, but entirely in the circuit's assertions. If the circuit fails, the transition simply cannot occur—providing a powerful, trustless gatekeeper. Prioritizing the Core Value: In a tight timeline, we learned to focus the implementation on the single most valuable technical feature (the Nullifier check) to create the most impactful, demonstrable proof-of-concept.

Built With

  • css
  • front-end
  • html/javascript/tailwind
  • midnight-network-(target-platform)
  • nullifier
  • nullivote-was-built-with-compact-(for-zk-circuits-and-contracts)
  • simulation
  • zk
Share this project:

Updates