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]
wherei
runs 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])
, wherenouns
is 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 keyD
which 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_id
coincides with the current public voting id,voting_merkle_root
coincides 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_value
which is subject to some additional relations:-
The leaf of the force Merkle tree
force[i] == vote_value
orforce[i] == (0, 0)
. -
If it was
(0, 0)
, there arekey.threshold
signatures 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 = 0
orY
orN
. -
The value
enc_vote
is a homomorphic El Gamal encryption of the vote, namely a pair of points:(C, K)
, whereC = (vote_value * nouns) + (rand * D)
andK = rand * G
for 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.