Adding Private Voting to the Opinion Market System

We are currently investigating ways in which we can integrate private voting into our Opinion Market system. In this post, I will detail our system requirements, initial implementation approach, as well as future improvements we intend to make to the first working version of this private voting system.

Requirements

Our existing system requirements are as follows:

  1. We need to enable users to submit private votes onchain and have the opinion market contract store these encrypted votes as ciphertext in an appropriate data structure.
  2. We need a way for users to reliably encrypt their votes client-side before submitting them to the contract. Votes values need to remain hidden for the duration of the market such that they can’t be tallied while the market is live.
  3. We need a way to trustlessly decrypt votes offchain when the market closes and submit the vote tally to the contract to settle the market.
  4. Finally, we must be able to tie a user’s vote back to them upon market settlement so that we can accurately update the internal reputation / scoring system.

Implementation Details

Initial Implementation Flow

  1. Drand publishes a public key for a future round.
  2. Voters encrypt their votes offchain with the Drand public key & a noise value.
  3. The user submits their encrypted vote as ciphertext to the opinion market contract.
  4. After the voting period ends, the corresponding Drand private key is released.
  5. The settlement process follows these key steps:
  • The aggregator script performs three main operations offchain:

    • Collects and decrypts all user votes from the encrypted onchain submissions
    • Generates a merkle tree from the decrypted votes for efficient verification
    • Computes the final vote tally
  • These results are then atomically submitted to the opinion market contract through a single settle() function call, which includes the following as calldata:

    • Merkle root - enables efficient verification of vote integrity
    • Decrypted vote data - allows for vote verification
    • Final calculated tally - the ultimate outcome of the vote
  1. The Opinion Market contract will then fetch a random value from the randomness oracle and decrypt a predefined threshold of randomly selected encrypted votes onchain. Provided the randomly selected votes that were decrypted onchain match the corresponding decrypted votes in the Merkle tree that was constructed offchain, the tally will be accepted by the contract and the market will settle successfully. Otherwise, the contract will reject the tally and the function will fail to settle the market.

For this initial implementation, all private votes submitted to the contract will be made public after the market ends and the Drand private key is released. For full vote privacy in perpetuity, we would require the use of more advanced cryptographic techniques such as additive homomorphic encryption. This is something we are mindful of and intend to iterate on this in the second version of this system.

System Component Details

Cairo Contracts

  • Store encrypted votes as ciphertext in an appropriate data structure. This needs to include the user’s address, noise, and the vote values.
  • Decrypt a predetermined threshold of randomly selected encrypted votes to ensure statistical confidence in the offchain decryption process. In other words, we need to verify randomly selected votes from the merkle tree constructed offchain. We will need to create an algorithm to choose random votes from the list of encrypted votes.
  • The randomness oracle is queried by the contract to determine which votes to decrypt onchain.

Client-side Libraries

  • Encryption Process:
    • Ensure users correctly encrypt their votes with the Drand Public Key (Dpk) prior to submitting them to the Opinion Market contract.
      • This is what the structure of the vote should look like before encrypting:
        • <key:value pair>
        • <user’s address: boolean answer to question Q, boolean answer to question P, noise>
    • Correctly format each vote during the encryption process such that questions Q and P are formatted consistently and the final ciphertext output can be stored correctly in a cairo contract.
    • Introduce a noise value to the encryption process to ensure that it would be impossible to guess the values of encrypted votes from the ciphertext stored onchain.
  • Decryption Process:
    • Fetch Drand Private Key (DprivK) for the respective round from the Drand API.
    • Fetch all encrypted votes from the Opinion Market contract.
    • Decrypt all votes offchain and construct the merkle tree or merkle patricia trie with the decrypted votes.
    • Store the merkle tree off-chain (e.g. IPFS) or publish it to the Opinion Market contract.

Oracle

Future Improvements

Private Voting

  • Leverage Homomorphic Encryption to tally encrypted votes onchain without the need to ever reveal them.
  • Explore the use of pallier & exponential ELGamal implementations which allow for additive homomorphic encryption.
  • TSS (Threshold Signature Scheme) to maintain a group which can decrypt HME.
  • Explore the use of SNARK/STARK proofs to verify the entire computation from decrypting the votes to the production of the final merkle root and vote tally that are submitted to the contract.

Anonymity

  • Utilise client side proving tools such as Noir.Js that enable users to generate proofs locally on their device without revealing any sensitive information. These proofs would be used to verify the validity of a given user’s vote on submission as well as verify a user’s proof of personhood (PoP).
  • Client side proofs would be verified directly on Starknet via Garaga verifier contracts.

Additional Resources

5 Likes

This is a good initial approach for us, as it allows us to keep votes and running tally private - essentially achieving full market privacy while market is open.

By using Drand Network for TLE (Time-Lock Encryption), we ensure encrypting data now prevents it from being decrypted until a specified time in future. Also, Drand’s processes are completely decentralized and publicly verifiable :+1:

I do have an open question. For a first iteration, this approach is good to test out private voting. However, we will run into issues when scaling this to a lot of voters.

If we’ve thousands of voters, who vote directly on-chain via the Cairo contract, even decrypting a small percentage of these as part of the on-chain randomness decryption verification could become computationally intensive. We’d need to test this approach and take into account gas costs and Starknet transaction limits

To address above, we would need to batch the on-chain decryption process into smaller chunks, finally allowing us to verify the Merkle root on-chain.

This is such a cool approach, especially the way you’re thinking through leveraging homomorphic encryption. I’ve always wondered about the trade-offs with on-chain decryption at scale-seems like a tricky balance between efficiency and cost. But do you think batching could introduce any risks in terms of delays or verification issues?

Hey Kris, thanks for this detailed overview.
It’s great to see Drand TLE in play for encrypting votes before the key is publicly revealed. We’ve been exploring various randomization solutions ourselves—Pragma’s VRF, Cartridge VRF, and Drand’s beacon—and ended up leaning away from Drand’s randomness for on-chain selection. Since Drand publishes randomness off-chain, it becomes challenging to prove on-chain that a given round is both fresh and unmodified; someone could theoretically reuse an older Drand output. Meanwhile, VRF-based solutions (like Pragma’s) produce a cryptographic proof the contract can directly verify, making them much simpler for on-chain randomness. We did a small proof of concept with Pragma VRF, and it worked out well.