A complete Zero-Knowledge Proof based ranked voting system that ensures vote privacy, verifiability, and secure aggregation. This system allows voters to rank candidates privately while proving vote validity and calculating results without revealing individual choices. The demo video is attached in the github repo itself, and here is the link for the same: https://drive.google.com/file/d/17ldeaRUjjqomlNvkIGU0ia72M-XmkW_T/view?usp=sharing
This project implements a fully zero-knowledge ranked voting system with:
- Private Voting: Individual vote rankings remain completely private
- Zero-Knowledge Tallying: Aggregated results calculated without revealing individual votes
- Circuit Security Verification: Automated verification using Picus to ensure circuits are properly constrained
- Tie-Breaking: Deterministic tie-breaking mechanism for equal scores
- Modern Web Interface: Clean, responsive UI built with Next.js and Tailwind CSS
- Vote Privacy: Rankings are hashed using Poseidon commitments - never revealed
- ZK Proofs: Each vote generates a zero-knowledge proof of validity
- Double-Voting Prevention: Cryptographic nullifiers prevent duplicate votes
- Circuit Verification: Picus-verified circuits ensure no information leakage
- Ranked Voting: Voters rank candidates (1-4, no duplicates)
- Borda Count: Aggregated scoring using Borda count method
- ZK Tally Proof: Zero-knowledge proof of correct aggregation
- Tie-Breaking: Alphabetical ordering for deterministic tie resolution
- Picus Integration: Automated circuit verification for underconstrained signals
- Formal Verification: Circuits verified as "properly constrained" (safe)
- Transparent Process: All proofs are verifiable by anyone
- Circom 2.0: Zero-knowledge circuit language
- SnarkJS: ZK-SNARK proof generation and verification (Groth16)
- Picus: Formal verification tool for circuit security
- Next.js 14: React framework for web interface
- Tailwind CSS: Modern styling
- Poseidon Hash: Cryptographic hash function for commitments
- TypeScript: Type-safe development
Before you begin, ensure you have:
- Node.js (v18 or higher) and npm
- Circom 2.0 compiler (Installation Guide)
- SnarkJS (installed via npm)
- Git (for version control)
- Picus: For automated circuit security verification
- Racket: Required by Picus
- cvc5: SMT solver for Picus
git clone <your-repo-url>
cd zkp
npm installnpm run compileThis compiles both:
rankedVoting.circom- Individual vote verification circuitvoteTally.circom- Zero-knowledge vote aggregation circuit
cd circuits/artifacts
# Generate Powers of Tau
snarkjs powersoftau new bn128 14 pot14_0000.ptau -v
snarkjs powersoftau contribute pot14_0000.ptau pot14_0001.ptau --name="First contribution" -v
snarkjs powersoftau prepare phase2 pot14_0001.ptau pot14_final.ptau -v
# Generate zkey for rankedVoting
snarkjs groth16 setup rankedVoting.r1cs pot14_final.ptau rankedVoting_0000.zkey
snarkjs zkey contribute rankedVoting_0000.zkey rankedVoting_0001.zkey --name="1st Contributor" -v
snarkjs zkey export verificationkey rankedVoting_0001.zkey verification_key.json# Generate zkey for voteTally
npm run setup:tallyOr manually:
cd circuits/artifacts
snarkjs groth16 setup voteTally.r1cs pot14_final.ptau voteTally_0000.zkey
snarkjs zkey contribute voteTally_0000.zkey voteTally_0001.zkey --name="1st Contributor" -v
snarkjs zkey export verificationkey voteTally_0001.zkey voteTally_verification_key.json# Copy voting circuit files
copy circuits\artifacts\rankedVoting_js\rankedVoting.wasm public\circuits\
copy circuits\artifacts\rankedVoting_0001.zkey public\circuits\
copy circuits\artifacts\verification_key.json public\circuits\
# Copy tally circuit files
copy circuits\artifacts\voteTally_js\voteTally.wasm public\circuits\
copy circuits\artifacts\voteTally_0001.zkey public\circuits\
copy circuits\artifacts\voteTally_verification_key.json public\circuits\voteTally_verification_key.jsonnpm run devOpen http://localhost:3000 in your browser.
Before running locally, verify your circuits are secure:
# Using local Picus installation (Windows CMD)
npm run verify:local:cmd
# Using local Picus installation (Git Bash)
npm run verify:local:bashExpected Result: Both circuits should show "The circuit is properly constrained" (safe).
For detailed Picus setup, see the Picus documentation.
- Start the application:
npm run dev - Admin Setup: Set number of voters (1-10 for current circuit)
- Submit Votes:
- Enter voter IDs
- Rank candidates (1-4, no duplicates)
- Verify vote hash is generated (not rankings)
- Check Privacy:
- Open browser DevTools β Application β Local Storage
- Verify rankings are NOT stored in plain text
- Generate Tally:
- Wait for all votes to be collected
- Click "Generate Zero-Knowledge Tally"
- Verify aggregated scores are shown (not individual votes)
See TEST_ZERO_KNOWLEDGE.md for comprehensive testing guide.
zkp/
βββ circuits/
β βββ rankedVoting.circom # Vote verification circuit
β βββ voteTally.circom # Zero-knowledge tally circuit
β βββ artifacts/ # Compiled circuit files (gitignored)
βββ src/
β βββ components/
β β βββ AdminSetup.tsx # Election configuration
β β βββ VotingInterface.tsx # Voting UI
β β βββ ResultsDisplay.tsx # Results with ZK tally
β βββ lib/
β β βββ zkp.js # ZKP utilities (voting)
β β βββ tallyZKP.js # ZKP utilities (tallying)
β β βββ voteStorage.ts # Local storage management
β β βββ resultsCalculator.ts # Results calculation
β βββ pages/
β β βββ index.tsx # Main page
β β βββ _app.tsx # Next.js app wrapper
β βββ styles/
β βββ globals.css # Global styles
βββ scripts/
β βββ compile.js # Circuit compilation
β βββ setup-tally.js # Tally trusted setup
β βββ verify-picus-local.bat # Picus verification (Windows)
β βββ verify-picus-local.sh # Picus verification (Linux/Mac)
βββ public/
β βββ circuits/ # Circuit files for local execution
βββ package.json
βββ next.config.js
βββ tsconfig.json
βββ README.md
- Voter ranks candidates (1-4, no duplicates)
- System generates:
- Random secret
- Vote hash:
Poseidon(rankings[0], rankings[1], secret) - Nullifier:
Poseidon(voterId, secret)
- Circuit verifies (without revealing rankings):
- All rankings in valid range [1, 4]
- No duplicate rankings
- Hash commitment matches vote
- ZK Proof generated: Proves validity without revealing data
- All votes collected: System waits for admin-set number of voters
- ZK Tally Proof generated:
- Verifies all vote commitments are valid
- Calculates aggregated Borda scores
- Proves aggregation correctness
- Individual rankings remain private
- Results displayed: Only aggregated scores shown
When candidates have equal scores:
- Primary: Sort by Borda count score (descending)
- Tie-Breaker: Alphabetical order (deterministic)
- Visual Indicators: Tied candidates marked with π€ icon
See TIE_BREAKING.md for details.
The voteTally.circom circuit is currently compiled for 10 votes maximum. To support more votes:
- Edit
circuits/voteTally.circom(last line):component main = VoteTally(4, 20); // Change 10 to desired max
- Recompile:
npm run compile - Regenerate trusted setup:
npm run setup:tally - Copy files to
public/circuits/
See CIRCUIT_VOTE_LIMIT.md for details.
- TEST_ZERO_KNOWLEDGE.md - Comprehensive testing guide
- ZERO_KNOWLEDGE_ANALYSIS.md - Privacy analysis
- TIE_BREAKING.md - Tie-breaking mechanism
- CIRCUIT_VOTE_LIMIT.md - Circuit configuration
β
Vote Privacy: Rankings never revealed in stored data or proofs
β
Tally Privacy: Aggregated results calculated without revealing individual votes
β
Circuit Security: Picus verified circuits are properly constrained
β
Proof Validity: All proofs are cryptographically verifiable
- Encrypted Rankings: Temporarily stored for tally proof generation (cleared after tally)
- Base64 Encoding: Used for demo (production should use proper encryption)
- Local Storage: All data stored client-side (no server)
See ZERO_KNOWLEDGE_ANALYSIS.md for detailed analysis.
- Vote Hash: Cryptographic commitment (cannot be reversed)
- ZK Proof: Mathematical proof of validity (doesn't reveal rankings)
- Nullifier: Prevents double-voting (doesn't reveal identity)
- Aggregated Scores: Total Borda count points per candidate
- Tally Proof: ZK proof that aggregation is correct
- Individual Votes: Never revealed
Note: This is a proof-of-concept implementation. For production use, additional security measures, proper encryption, and server-side infrastructure would be required.