DelegatorVote Descriptions

Each delegator vote contains an DelegatorVoteBody and a zk-SNARK delegator vote proof.

Invariants

The invariants that the DelegatorVote upholds are described below.

Local Invariants

  1. Available voting power for a proposal = total delegated stake to active validators when the proposal was created

    1.1 Voting power must have been present before the proposal was created.

    1.2 You can’t vote with a note that was spent prior to proposal start.

    1.3 That staked note must correspond to a validator that was in the active set when the proposal being voted on was created.

  2. You can’t use the same voting power twice on a proposal.

  3. You can’t vote on a proposal that is not votable, i.e. it has not been withdrawn or voting has finished.

  4. Invariants 1-3 with regards to spending a note apply to voting with it.

  5. The voting power (amount and asset type of the staked note used for voting), the vote, as well as the proposal being voted on, is revealed during a delegator vote. However, they are anonymous in that they do not reveal the address of the voter.

  6. Currently, votes across proposals can be linked if the same staked note is used.

Local Justification

  1. We check the available voting power for a proposal equals the total delegated stake to active validators when the proposal was created via:

    1.1 The circuit checks the age of the staked note, and the stateful check verifies that the claimed position matches that of the proposal.

    1.2 We check that the note was spent only after the block of the proposal.

    1.3 The stateful check for the exchange rate makes sure the validator was active at that time.

  2. We maintain a nullifier set for delegator votes and check for duplicate nullifiers in the stateful checks.

  3. The stateful check looks up the proposal ID and ensures it is votable.

  4. c.f. justification for spend circuit here

  5. A randomized verification key is used to prevent linkability of votes across the same spend authority. The spender demonstrates in zero-knowledge that this randomized verification key was derived from the spend authorization key given a witnessed spend authorization randomizer. The spender also demonstrates in zero-knowledge that the spend authorization key is associated with the address on the note being used for voting.

  6. The nullifier revealed in the DelegatorVote will be the same if the same staked note is used. Thus, the nullifier can be used to link votes across proposals. Clients can roll over a staked note that was used for voting for privacy (this is currently done in Planner::plan_with_spendable_and_votable_notes).

DelegatorVote zk-SNARK Statements

The delegator vote proof demonstrates the properties enumerated below for the following private witnesses known by the prover:

  • Note amount (interpreted as an and constrained to fit in 128 bits) and asset ID
  • Note blinding factor used to blind the note commitment
  • Address associated with the note being spent, consisting of diversified basepoint , transmission key , and clue key
  • Note commitment
  • Spend authorization randomizer used for generating the randomized spend authorization key
  • Spend authorization key
  • Nullifier deriving key
  • Merkle proof of inclusion for the note commitment, consisting of a position pos, constrained to fit in 48 bits, and an authentication path consisting of 72 elements (3 siblings each per 24 levels)

And the corresponding public inputs:

  • Merkle anchor of the state commitment tree
  • Balance commitment to the value balance
  • Nullifier of the note to be used for voting
  • Randomized verification key
  • The start position start_pos of the proposal being voted on, constrained to fit in 48 bits

Start Position Verification

The zk-SNARK certifies that the position of the staked note pos is less than the position of the proposal being voted on:

pos < start_pos

This demonstrates that the staked note used in voting existed prior to the proposal.

The zk-SNARK also certifies that the commitment index of the start position is zero.

Note Commitment Integrity

The zk-SNARK certifies that the note commitment was derived as:

.

using the above witnessed values and where ds is a constant domain separator:

ds = from_le_bytes(BLAKE2b-512(b"penumbra.notecommit")) mod q

Balance Commitment Integrity

The zk-SNARK certifies that the public input balance commitment was derived from the witnessed values as:

where is a constant generator and is an asset-specific generator point derived in-circuit as described in Assets and Values. For delegator votes, .

Nullifier Integrity

The zk-SNARK certifies that the revealed nullifier was derived as:

using the witnessed values above and where ds is a constant domain separator:

ds = from_le_bytes(BLAKE2b-512(b"penumbra.nullifier")) mod q

as described in Nullifiers.

Diversified Address Integrity

The zk-SNARK certifies that the diversified address associated with the note was derived as:

where is the witnessed diversified basepoint and is the incoming viewing key computed using a rate-2 Poseidon hash from the witnessed and as:

ivk = hash_2(from_le_bytes(b"penumbra.derive.ivk"), nk, decaf377_s(ak)) mod r

as described in Viewing Keys.

Spend Authority

The zk-SNARK certifies that for the randomized verification key was derived using the witnessed and spend auth randomizer as:

where is the conventional decaf377 basepoint as described in The Decaf377 Group.

Merkle Verification

The zk-SNARK certifies that the witnessed Merkle authentication path is a valid Merkle path to the provided public anchor.

Diversified Base is not Identity

The zk-SNARK certifies that the diversified basepoint associated with the address on the note is not identity.

Spend Authorization Key is not Identity

The zk-SNARK certifies that the spend authorization key is not identity.