Skip to content

Latest commit

 

History

History
142 lines (105 loc) · 6.15 KB

05_quadratic_vote_tallying_circuit.md

File metadata and controls

142 lines (105 loc) · 6.15 KB

The quadratic vote tallying circuit

Quadratic voting is one of many types of vote tallying mechanisms. We chose it for the first version of MACI due to the high amount of interest that the community has shown for it.

Quadratic voting allows users to express the strength of their preferences when they vote for options. Since users are allocated a limited number of voice credits, and the number of tallied votes per option is the square root of the number of voice credits spent on said option, quadratic voting over-privileges neither concentrated nor diffuse interests.

For instance, if a user has 99 voice credits, they may spend them this way (each row represents a command):

Option Voice credits spent
A 1
A 9
B 25
C 64

The outcome is as such:

Option Tallied votes
A 3.16
B 5
C 8

Even though the user has a disproportionate preference for option C (64 voice credits), their impact on the tallied vote (8 votes) is merely the square root of the voice credits they have spent. This prevents them from having an outsized influence on the results simply by virtue of their willingness to spend as many voice credits on that option as they had.

Additionally, we consider that votes are cumulative. This means that the user spent 10 voice credits on option A.

The MACI contract's quadraticVoteTally() function should verify a proof created using this circuit to compute the results of tallying a set of state leaves. This also proves that these state leaves have an intermediate root A, as well that A is part of the tree with final state root R. This allows the coordinator to prove the final tally in batches. The function keeps track of the index of each intermediate root to ensure that they are processed consecutively.

Inputs

Pseudocode name zk-SNARK input type Description Set by
fullStateRoot Public The final Merkle root of the state tree Contract
fullStateTreeDepth Hardcoded The depth of the state tree Contract
intermediateStateTreeDepth Hardcoded The depth of the intermediate state tree Contract
intermediateStateRoot Public The intermediate Merkle root generated by the given state leaves Contract
intermediatePathElements[k] Private The Merkle path elements from intermediateStateRoot to stateRoot. Coordinator
intermediatePathIndex Public The Merkle path index from intermediateStateRoot to stateRoot. Contract
currentResults[n] Private The vote tally of all prior batches of state leaves Coordinator
currentResultsSalt Private A random value to hash with the vote tally for state leaves up to the current batch Coordinator
currentResultsCommitment Public The salted commitment of the values in currentResults Contract
newResultsCommitment Public The salted commitment of the vote tally for this batch of leaves plus the vote tally from currentResults Contract
salt Private A random value to hash with the culmulate vote tally for this batch of state leaves Coordinator
stateLeaves[m][p] Private The batch of leaves of the state tree to tally. Coordinator
voteLeaves[m][n] Private The vote leaves for each user in this batch of state leaves. Coordinator

n is the number of options in voteOptionTree. m is the number of state leaves in this batch. k is fullStateTreeDepth - intermediateStateTreeDepth p is the message length

A result commitment is the hash of a Merkle root of all the vote leaves, and a salt. For instance:

root = genTree(results)
hash(root, salt)

Circuit pseudocode

// Alice votes for party A with 16 credits
// Bob votes for party A with 9 credits

// Party A gets 7 tallied votes. NOT 5 votes.

// Ensure via a constraint that the intermediate root is the 
// correct Merkle root of the stateLeaves passed into this 
// snark
assert(intermediateStateRoot == genTree(stateLeaves))

// Ensure via a constraint that the intermediate root is part of the full state tree
var x = generateMerkleRoot(
    intermediatePathElements,
    intermediatePathIndex,
    intermediateRoot
)

assert(x == stateRoot)

// This variable stores the sum of the square roots of each 
// user's voice credits per option.
var computedResults = currentResults

var start = 1
if intermediatePathIndex > 0:
    start = 0

// For each user
for i as start to m: // we ignore leaf 0 on purpose
    
    // Ensure via a constraint that the voteLeaves for this 
    // user is correct (such that when each vote leaf is 
    // inserted into an MT, the Merkle root matches
    // the `voteOptionTreeRoot` field of the state leaf)

    var computedVoteOptionTreeRoot = genTree(voteLeaves[i])
    assert(computedVoteOptionTreeRoot == stateLeaves[i].voteOptionTreeRoot)

    // Calculate the sum of votes for each option
    for j as 0 to n.
        // This adds to the subtotal from previous batches
        // of state leaves
        computedResults[j] += voteLeaves[i][j]
        
        
// Ensure via a constraint that the commitment to the current results is
// correct

assert(
    hash(genTree(currentResults), currentResultsSalt) == 
    currentResultsCommitment
)

// Ensure via a constraint that the final result
// is correct
assert(
    hash(genTree(computedResults), salt) == 
    newResultsCommitment
)

where genTree is pseudocode for a circuit which computes a Merkle root from a list of leaves.

Circuit failure modes

Condition Outcome
Invalid state leaves and/or intermediate state root No such proof can be generated
Invalid vote option leaves No such proof can be generated
Invalid Merkle path to the full state root from the intermediate state root for the batch of votes No such proof can be generated