We implement a private voting zkapp. The idea was to try to implement single stage private voting, say like on Twitter- one where participants do not need to register to vote first, as was done in Minataur. In a voting procedure with a registration step, users are required to make sure that sufficiently many people have signed up for voting before them but not voted and also that sufficient time has passed between their registration and voting transactions, so that their vote may be private. This can be difficult. Further, it seemed like bad UX. We also allow for the aggregation of user votes off-chain, which ensures a high voting throughput for the users.
In our implementation, a centralized party (let's assume honest for now) sets up the voting contract (called secretBallot
), which includes as state variables:
- a randomly chosen
ballot_ID
- the root of the Merkle tree containing the hash of public keys contained in the list of voters
- the root of a Merkle tree storing the votes for different options
- a MerkleMap storing nullifiers
(hash(private_key, ballot_ID))
, which record whether a key has been used for voting or not.
In order, to vote, the user controlling the key pair (sk, pk)
(secret/private key, public key) proves to the contract that:
hash(pk)
belongs in the voter listpk
is derived fromsk
- the
hash(sk, ballot_ID)
has not been used to vote before:nullifierMap[hash(sk, ballot_ID)] = 0
.
Together these three imply that the user controls a sk
which is eligible to vote but has not been used to vote yet (Note that we are placing some assumption on the amount of information that is leaked by these hashes, especially if the same sk
is used for multiple votes; also see this for more information on nullifiers).
The user also needs to provide the correct witness and the number of votes for the option, he chooses to vote for.
The zkapp is implemented in secretBallot.ts and an example program using the vote function described above is in singleVote-main.ts.
We also provide the functionality to aggregate the votes of multiple voters into a single proof and use that to modify the on-chain state. This is achieved using the recursion feature of zk proofs on MINA. We follow the idea for implementing rollups described here.
The Structs and ZKPrograms required for aggregation are implemented and described in voteAggregation.ts. An example program, which generates multiple votes and aggregates them is presented in voteAggr-main.ts.
-
Set the inputs in inputs.ts.
-
Build the project
npm run build
- Create new keys
node build/src/createKeys.js
- Run the desired example file
node build/src/singleVote-main.js
or
node build/src/voteAggr-main.js