ZeroPoll: Anonymous, Secure, and Verifiable Voting System
Project Background
ZeroPoll is an innovative, privacy-preserving voting system that leverages zero-knowledge proofs (zk-SNARKs) to ensure that votes remain anonymous while being verifiable. Built on the Mina ecosystem, ZeroPoll provides a secure voting solution for organizations, businesses, and social events, covering numerous applications such as voting on proposals, business management, surveys, elections and much more. Initially bootstrapped using ProtoKit’s starter kit, ZeroPoll features a user-friendly interface powered by Next.js, React, Zustand, and ShadcnUI.
The system allows participants to vote anonymously while verifying their eligibility through a commitment system. This ensures both privacy and result verifiability.
Proposal Overview
Problem
Traditional voting methods often struggle to balance user experience, privacy and security. Ensuring that votes are anonymous and verifiable without compromising the integrity of the voting process can be complex and inefficient. Many existing systems fail to scale effectively on conventional blockchains.
Solution
ZeroPoll addresses these challenges by using zk-SNARKs to provide anonymous and verifiable voting. Poll options are hashed with salts and stored on-chain, allowing poll privacy. A Merkle tree-based commitment system ensures only eligible voters can participate, while nullifiers prevent double-voting. Through o1js and ProtoKit, ZeroPoll leverages zk-rollups and Mina L1 for scalable implementation. The poll proofs can be verified on the client side. Yet, ZeroPoll is designed to be user-friendly and accessible, requiring no technical expertise. Our modern and intuitive interface, combined with the seamless integration of Auro Wallet, handles all the complexities, ensuring a secure, smooth and enjoyable user experience.
Impact
ZeroPoll enhances the Mina ecosystem by showcasing the real-world application of zk-SNARKs for secure voting. It promotes adoption among entities seeking privacy-focused solutions and stimulates developer interest through new SDKs and APIs. By facilitating anonymous polling, ZeroPoll extends Mina’s utility to various sectors, including business and community voting.
Audience
Target audiences include organizations, businesses, and communities where secure, private voting is crucial. Developers interested in zk-SNARKs and blockchain applications will also benefit from the proposed SDKs and APIs.
Architecture & Design
ZeroPoll consists of 3 parts:
- The Chain (Runtime Modules): Developed with ProtoKit and O1JS. The Poll Module is the core of the project, it’s designed to implement a secure and private voting system using Zero-Knowledge Proofs, Poseidon hashing, and Merkle trees. It ensures voter anonymity, verifies vote validity without revealing identities, and prevents double voting through nullifier checks. The system revolves around creating polls, handling votes, and verifying them using cryptographic techniques. It’s currently designed to work only with private polls, which requires the poll creator to restrict which public keys can vote through a commitment hash.
- The Web App: Built with Next.js, React, Zustand, and ShadcnUI, it provides an intuitive interface for interacting with the blockchain. It consumes poll data from the chain and from the persistence repository and verifies voting through zero-knowledge proofs.
- The Persistence Repository: Currently uses PostgreSQL with Prisma ORM for storing less sensitive data (poll metadata). It ensures that only authorized signed wallets can read the poll metadata (we authenticate users with Auro Wallet Sign Message. Future enhancements will include encrypted storage on IPFS for a more decentralized approach.
Poll creation diagram:
- Poll creator creates the poll, storing the offchain metadata, including the poll name, description, poll options, and public keys eligible for voting.
- Poll creator creates a commitment hash from the merkle map of the voter public keys hashed with Poseidon.
- Poll creator creates the hashes of each poll option with Poseidon + Salt.
- Poll creator calls the createPoll method, passing the commitment and optionsHashes as arguments. A new poll is created and can be shared with voters.
Vote cast diagram
- An eligible voter retrieves the poll’s offchain metadata and computes the hashes (commitment and optionsHashes).
- The eligible voter queries the chain to retrieve the poll’s commitment and optionsHashes to compare with the hashes of the offchain data, ensuring that the poll options are not tampered with and to verify which public keys are eligible to vote.
- The eligible voter generates a ZkProgram.Proof with a nullifier and casts his vote via the vote method. His poll state is updated, adding +1 to the hash of the voted option.
- The client-side vote count is recalculated.
Poll Module Detailed
The complete code for the poll module can be found here
Components
VoteOption:
- A struct representing each voting option. Each option has a hash (to identify the option) and a votesCount (tracking the number of votes it has received).
- Used to count votes for each option while ensuring privacy.
OptionHash:
- Extends the Field class and provides a mechanism to hash text inputs (poll options) into unique Poseidon-based hashes at client-side.
- Uses a combination of text and a salt to generate unique hashes, ensuring the integrity and uniqueness of poll options.
OptionsHashes:
- Struct that represents a group of 10 hashed poll options. It either allow client-side to converts text options into hashes (fromTexts) or directly accepts predefined hashes (fromHashes).
- Ensures that all polls always contain exactly 10 options, even if some are placeholders, using Poseidon hashes to make each option verifiable and unique.
VoteOptions:
- A struct that holds the set of VoteOption instances for a given poll.
- Responsible for casting votes using the cast method, which updates the vote count for the selected option while maintaining immutability.
- Initialized from OptionsHashes, it creates a mapping between hashed options and their corresponding vote counts.
PollPublicOutput:
- This struct holds the public output of the poll after a vote is cast, including the Merkle tree root and the nullifier (which prevents double voting).
- Used to verify that a vote has been correctly processed and recorded.
Key Functionalities
createPoll:
- This function creates a new poll with a unique poll ID and a commitment (representing the hash of the poll options).
- The commitment and poll options (in the form of OptionsHashes) are stored on-chain, and the poll ID is incremented.
- Maintains state using StateMap for commitments, poll options, and vote tallies.
vote:
- A voter can cast a vote by providing a pollId, the hash of their selected option, and a PollProof (ZKP).
- The method verifies the validity of the provided proof, checks that the poll exists and that the nullifier (used to prevent double voting) has not been used before.
- It then updates the poll’s vote counts based on the selected option and marks the nullifier as used.
Proof Verification:
- ZKPs are used to prove voter eligibility without revealing the voter’s identity. The canVote method calculates the root and verifies that the voter can vote based on their public key and the poll ID.
This allows trustless verification, ensuring a user can only vote once while keeping their identity private.
Zero-Knowledge Poll Program:
- The pollProgram is a ZK program that defines how the ZKP is generated and verified. It includes methods like canVote to ensure the integrity of the vote casting process.
- Ensures secure interactions between the voting module and the cryptographic constructs of the system.
State Management
- commitments: Maps poll IDs to their respective commitments (root of the Merkle tree containing the hashed participants public keys allowed to vote in the private poll).
- nullifiers: Maps nullifiers (used for preventing double voting) to booleans indicating whether the nullifier has already been used.
- votes: Stores the VoteOptions for each poll, keeping track of the votes for each option.
- lastPollId: Tracks the ID of the most recently created poll.
Reference / Inspirational Works
Private Airdrop - Module: Allow private and anonymous token airdrop. Explores the same concept of set a MerkleMap commitment to anonymously register eligible participants and use nullifier to prevent double-claim. Made with Protokit by maht0rz.
Katz - Ballot Vote: Vote for a candidate with greater anonymity. By leveraging recursive proofs with o1js on Mina, the vote is kept private from the rest of the public while the overall election results can be known by anyone.
Vision
Long-term goals for ZeroPoll include:
- Study extra voter invitation and authentication solutions based on identity systems such as zkid and/or social networks
- Becoming one of the main use cases of zkApps with Mina, o1JS and Protokit; contributing to the adoption of these technologies by the community.
- Becoming a collaborative project and well audited by community devs, thus providing a basis for other similar use cases.
- Offer public and private management solutions for different organizations. Examples: managing proposal votes, elections and surveys.
- Contribute to the creation of a culture of anonymous and verifiable voting systems based on zk-knowledge. This can affect the demand for such solutions in their most diverse use cases, such as public and private management, DAO proposals, surveys and even presidential elections…
- After covering general use cases related to polling/voting, study the main market demands for the project and, if necessary, update the project’s positioning, user experience and functionalities.
Existing Work
The project is functional as an alpha release.
Production Timeline
ZeroPoll is expected to deliver all features and improvements contained in this proposal within 3 months, with regular updates and feature additions separated by scopes 1, 2 and 3.
Budget & Milestones
Deliverables
- Development and release of
@zeropoll/sdk
, @zeropoll/chain
, and @zeropoll/ui
.
- User account features for managing and bookmarking polls.
- Enhanced UI/UX, including a new landing page and improved poll creation and voting experience.
- Downloadable proof files in JSON format for offline verification.
- Public anonymous polls without requiring voter public keys.
- Integration with Mina L1 via ProtoKit zk-rollup.
Mid-Point Milestones
- Scope 1:
- Complete development of the landing page and UI/UX improvements.
- Refactor code for better maintainability and performance.
- Enhance unit tests for both runtime modules and the web app.
- Update and improve the README and technical documentation.
- Scope 2:
- Release the @zeropoll/sdk, @zeropoll/chain, and @zeropoll/ui packages.
- Implement functionality for downloadable proofs in JSON format.
- Create and finalize technical documentation and user guides.
- Transition poll metadata storage to a decentralized storage solution like IPFS.
- Scope 3:
- Complete integration with Mina L1.
- Launch public anonymous polls which does not requires the poll creator to insert the voters public keys, but simply share a link that anyone with a wallet could vote - currently the polls are private and the poll creator is required to restrict who can vote or read the poll options. To achieve this, the current Merkle Map system can be - adapted or replaced, while preserving the Nullifier and other components necessary for the zk circuit. Note that the idea is to allow the poll creator to choose the restrictions, like choose between PRIVATE or PUBLIC during the creation of the poll.
- Release the stable version of ZeroPoll.
- Final:
- Conduct zk-SNARK proof validation.
- Execute bug bounties and security audits to ensure robustness and security.
Project Timeline
- Month 1: (Scope 1) Develop landing page, refactor code, and improve UI/UX (poll management, creation, and voting), enhanced unit tests for runtime modules and web app, improve README of the repo.
- Month 2: (Scope 2) Release
@zeropoll/sdk
, @zeropoll/chain
, and @zeropoll/ui
, implement downloadable proofs, creation of technical documentation and transition to decentralized data storage.
- Month 3: (Scope 3) Launch public anonymous polls, integrate with Mina L1, and release stable version.
Budget Requested
Total: 33,000 MINA
- Scope 1: 11,000 MINA
- Scope 2: 11,000 MINA
- Scope 3: 11,000 MINA
Wallet:
B62qisCaNyznz9VtEVStHKhzUsCCp9JaD1eTb8zUWKGdxfdhauoj8MU
Commitment to Long-Term Development
ZeroPoll will continue evolving beyond the initial funding period. Future plans include integrating ProtoKit’s full potential, creating public anonymous polls, enhancing user features, and developing SDKs and APIs. We will stay aligned with ProtoKit’s development to integrate our chain layer on Mina L1 as soon as possible. The project aims to explore further possibilities such as token-based incentives and decentralized governance features.
Team Info
Proposer GitHub
github.com/anarkrypto
Team Members
- Kaique Nunes (anarkrypto)
Proposer Experience and Achievements
I am a full-stack developer with 8 years of experience in TypeScript, React, blockchain, smart contracts, web3, and decentralized systems. My previous projects include:
- Bioca.eco - Decentralized Supply Chain System: EVM smart contracts for traceability via NFTs.
- NFT Minting Backend: Using EVM, IPFS, and RabbitMQ.
- NanoPay.me: Open source payment gateway for Nano cryptocurrency.
- NanoDrop.io: Popular nanocurrency faucet recommended by the Nano Foundation.
- Construbook.app: A social network for the construction industry.
Achievements include:
- Winner of NanoJam hackathon with P2PoW.
- Construbook’s integration with Hub da Construção Sinduscon-BA.
- Awarded for contributions to Fluence marketplace.
Risks & Mitigations
Risks
- Dependency on ProtoKit’s development for Mina L1 integration.
- Ensuring app and chain security against privacy breaches or vote manipulation.
Mitigations
- Monitor ProtoKit’s development and explore alternative zk-rollup solutions if needed.
- Implement security audits and bug bounty programs to secure ZeroPoll against vulnerabilities.