Nouns Vortex:
A privacy preserving voting for Nouns DAO
A privacy preserving voting for Nouns DAO
水 Homomorphic voting scheme 水
We assume that we have achieved the following state:
-
There is a Merkle tree with leaves
leaf[i]whereiruns through the set of all users who had non-zero voting balance at vote initialization block, in some canonical order. Leaf is calculated asleaf[i] = H(commit[i], nouns[i]), wherenounsis 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 keyDwhich is a sum ofauth_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.
Public inputs: voting_id, voting_merkle_root, null, enc_vote, which are subject to the following relations:
-
Public check:
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 asleaf, which decomposes (eventually) to private inputskey, 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_valueorforce[i] == (0, 0). -
If it was
(0, 0), there arekey.thresholdsignatures of the message(voting_id, H(vote_value, seed))with different public keys from an arraykey.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 = 0orYorN. -
The value
enc_voteis a homomorphic El Gamal encryption of the vote, namely a pair of points:(C, K), whereC = (vote_value * nouns) + (rand * D)andK = rand * Gfor some random scalarrand.
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_vote[i] as enc_result = (C_res, K_res).
Now, each decryption authourity i submits dec[i] = priv K_res with the proof that it was formed correctly with their private key priv satisfying priv*G = auth_pub[i]. If they fail to submit it, they are slashed and the voting goes into fallback mode.
Denote dec to be sum of dec[i].
The 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.