Try   HackMD

zk Voting with ECDSA sig verification and storage proofs

WIP circuit here

Membership inclusion

Storage Proofs

  • verifying token ownership requires storage proofs
    • snapshot at a given block w/ specific block header
    • zk program verifies a storage proof on that header
struct Proposal {
    uint snapshotBlock;
    bytes32 blockHeader;
    bytes32 ipfsHash;
    uint deadline;
    uint forVotes;
    uint againstVotes;
}

mapping (uint proposalId => Proposal) public proposals;
  • use this library to build a merkle tree on chain with sybil protection provided by token ownership

Manual setup

  • otherwise there could be a setup phase where a user generates a private key by signing a message with their ethereum account. for this, there would also need to be a secret value added to prevent links between accounts used in multiple

Voting

Signature verification

  • public key
  • signature
  • message_hash
  • proposal_id
  • vote
  • nullifier is a hash of:
    • proposal id
    • public_key_x
    • returned by circuit (must be provided for valid proof)
    • vote is excluded otherwise people could vote in both directions

Circuit

fn main(pub_key_x : [u8; 32], pub_key_y : [u8; 32], signature: [u8; 64], message_hash: pub [u8; 32], proposalId: Field, vote: Field, block_header : [u8; 32]) {
    let isValid = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, message_hash);
    assert(isValid == 1);

    // https://github.com/colinnielsen/ecrecover-noir/blob/main/src/secp256k1/helpers.nr
    let eth_address = ecrecover(pub_key_x, pub_key_y, signature, message_hash);

    // storage proof; TBD
    storage_proof(block_header, eth_address, token_contract, storage_slot);
    
    let mut new_array: [Field; 33] = [0;33];
    new_array[0] = proposalId;
    for i in 1..new_array.len() {
        new_array[i] = pub_key_x[i] as Field;
    }
    let nullifier = std::hash::pedersen(new_array);
}