The process of anonymous voting for Ethereum DAOs using Vocdoni involves the use of an identity commitment registry, which securely stores a secret owned by a Vocdoni user in a merkle tree. This secret is generated through a cryptographic process that employs a Hash function with at least one parameter considered secret for the user.
The specific formula for generating the commitment key, known as the secretIdentityKey, is as follows:
SecretIdentityKey = Hash(address + signature + password)
To gain a deeper understanding of the components involved, let's examine each one:
The secret identities are stored in a merkle tree as part of the Vochain state. The blockchain logic ensures that only one key is registered per account address and that the key has been created by the owner of that particular address. The blockchain verifies the ECDSA signature before adding the commitment identity to maintain the integrity of the system.
The secret identity plays a crucial role in proving ownership of an address anonymously within a zkSnark. To construct the proof, the user must possess the deterministic signature and the password, which are used as private inputs for the zk-SNARK circuit.
To prevent double voting and uphold the integrity of the voting process, a nullifier is computed by hashing the secret components together with the election identifier. The nullifier calculation follows this formula:
nullifier = hash(signature + password + electionID)
By incorporating the election identifier into the nullifier, each user can reproduce their own nullifier. However, any attempt to produce a second zkSnark proof for the same identity will result in the same nullifier. This mechanism effectively prevents double voting and ensures the authenticity of the voting process.
The secret identity key (SIK) merkle tree is a global data structure living inside the Vocdoni Vochain that stores a relation between an Ethereum address and its SIK. For each user only one secret identity can exist.
The tree is composed by:
The address (index) routes the path through the tree until its SIK (value). This mechansim guarantees the uniqueness of a SIK per address.
The Vochain transaction for adding a new identity proves the ownership of the address (by signing the transaction content) and provides the secret identity key.
The Census merkle tree is the census constructed by the Vocdoni's Census3 service out of the Ethereum data by quering a Web3 endpoint.
The purpose of the Census Merkle Tree is to create an immutable and verifiable representation of the token holders within the DAO on a specific block. It enables efficient and secure verification of an address's presence in the DAO's token holder set.
The Vocdoni Vochain serves as a verifier for the SIK merkle tree construction, ensuring its correctness.
Additionally, the Vochain verifies the voting proof submitted by DAO users by:
By conducting these verifications, the Vochain maintains the integrity and reliability of the voting process within the Vocdoni ecosystem.
The circuit has been designed to be zkSnark friendly and reduce the number of constraints to the minimum. To this end we do not require any secp256k1 cryptography operation.
The zk-SNARK circuit proves that the voter is eligible to vote (its Ethereum address is included in the Census Merkle Tree), that they know the secret inputs (signature and password) corresponding to their CIK, and that they have not voted more than their balance allows. It also ensures that the voter cannot vote more than once in the same election by generating a unique nullifier for each vote. All of this is done without revealing the voter's Ethereum address, preserving the anonymity of the vote.
The state database needs to be modified in the following way.
The Secret Identity Key (SIK) can be managed through the SetAccount transaction. There are three transaction types (txTypes) that allow for modifications to the SIK:
CREATE_ACCOUNT
DEL_ACCOUNT_SIK
[28 bytes of 0's] + [4 bytes blockchain Height LittleEndian]
SET_ACCOUNT_SIK
height < current_height
.The term "Height Hysteresis" is defined as the number of blocks for which the SIK Merkle root remains valid relative to the current height.
For example, Height Hysteresis could be defined as a constant value of 360. So at 10 seconds/blocks, it implies a time window of 1h.
The hysteresis mechanism is used to provide a buffer or grace period during which the SIK Merkle root remains valid even after a change in the blockchain height. This is important because it allows for a smoother transaction processing experience.
On the stateDB extra tree we store a new leaf:
When a user deletes his or her SIK, the leaf of the SIKMerkleTree identified by the user address will contain the height when it was deleted.
In this way, this SIK only can be setted again if the height that the leaf contains is lower than any ongoin process startBlock (plus the hysteresis*):
* Hysteresis: the number of past blocks on which we allow a SIK root to be valid when sending votes
To achieve this, we need to store at nostate database the list of current ongoing processes and its startBlock:
To keep this list updated:
startBlock=0
and status=PAUSED
, we add it to the list with current height (we need to check code for this casuistic)The hysteresis is the number of past blocks on which we allow a SIK root to be valid when sending votes. Required to verify proofs of vote generated with old but valid SIK merkle tree roots.
To achieve this, we need to store at nostate database the list of valid SIK roots:
and define the number of blocks the hysteresis means:
To keep this list updated:
sikRoot
is generated, the sikRoot
's from an older block than the current block minus the hysteresis
blocks will be deleted:
sikRoot
for the minimun hysteresis block number (currentBlock - hysteresis
), just remove all the roots with a lower block number.sikRoot
. It is becouse it still being validate for a period.Example: