We assume that we have achieved the following state:
There is a Merkle tree with leaves
iruns through the set of all users who had non-zero voting balance at vote initialization block, in some canonical order. Leaf is calculated as
leaf[i] = H(commit[i], nouns[i]), where
nounsis voting power at the checkpoint. In what follows, we assume this root is correct; otherwise, voting will be challenged and cancelled.
There is a set of registered tally authorities, their decryption public keys live in the map
auth_pub. We assume that there is also a decyption public key
Dwhich is a sum of
auth_pub. It can be either calculated onchain, or also supplied by proposer and calculated optimistically.
We also assume we are given some independent generators of babyJubJub, denoted
G, Y, N.
Now, in order to vote, user needs to create the following proof.
voting_id, voting_merkle_root, null, enc_vote, which are subject to the following relations:
voting_idcoincides with the current public voting id,
voting_merkle_rootcoincides with supplied Merkle root.
There exists the leaf of the Merkle tree
voting_merkle_root, denoted further as
leaf, which decomposes (eventually) to private inputs
key, force, nouns, subject to some relations:
H(key.seed, voting_id) = null- unique nullifier to prevent double-voting
There is an elliptic curve point
vote_valuewhich is subject to some additional relations:
The leaf of the force Merkle tree
force[i] == vote_valueor
force[i] == (0, 0).
If it was
(0, 0), there are
key.thresholdsignatures of the message
(voting_id, H(vote_value, seed))with different public keys from an array
key.pubkeys. // here, additional hashing with seed done to prevent this signatures from revealing private information, so they can be exchanged over insecure channel
vote_value = 0or
enc_voteis a homomorphic El Gamal encryption of the vote, namely a pair of points:
(C, K), where
C = (vote_value * nouns) + (rand * D)and
K = rand * Gfor some random scalar
Now, these proofs should be relayed on-chain and put in the proof-checking pool. We suggest that community spins up few public relayers for this purpose. Submitting proofs from some external EOA is fine, too, but it will require some collateral (relayers will require collateral too, but relatively small because they will submit proof in a batch).
When the proof-checking delay has passed, the resulting values
enc_vote are submitted back and added up. This can also be done optimistically without much effort.
Let us denote the total sum of all valid
enc_result = (C_res, K_res).
Now, each decryption authourity
dec[i] = priv K_res with the proof that it was formed correctly with their private key
priv*G = auth_pub[i]. If they fail to submit it, they are slashed and the voting goes into fallback mode.
dec to be sum of
res_point = C_res - dec. Now, anyone can provide values
yay, nay such that
yay*Y + nay*N = res_points. The way to obtain these values is lookup. This lookup is quadratic in the amount of Nouns (which is fine on our scale), but if there will be more voting options, it is possible that the scheme will need to be altered a bit to instead send multiple points.