Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 171 additions & 14 deletions foundry-voting/.gitignore
Original file line number Diff line number Diff line change
@@ -1,20 +1,177 @@
# Compiler files
cache/
out/
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
# Logs

# Docs
docs/
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

circuits/target
# Caches

cache
.cache

# Diagnostic reports (https://nodejs.org/api/report.html)

report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# Runtime data

pids
_.pid
_.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover

lib-cov

# Coverage directory used by tools like istanbul

coverage
*.lcov

# nyc test coverage

.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)

.grunt

# Bower dependency directory (https://bower.io/)

bower_components

# node-waf configuration

.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)

build/Release

# Dependency directories

node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)

web_modules/

# TypeScript cache

*.tsbuildinfo

# Optional npm cache directory

.npm

# Optional eslint cache

.eslintcache

# Optional stylelint cache

.stylelintcache

# Microbundle cache

.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history

.node_repl_history

# Output of 'npm pack'

*.tgz

# Yarn Integrity file

.yarn-integrity

# dotenv environment variable files

# Dotenv file
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)

.parcel-cache

# Next.js build output

.next
out

# Nuxt.js build / generate output

.nuxt
dist

# Gatsby files

# Comment in the public line in if your project uses Gatsby and not Next.js

# https://nextjs.org/blog/next-9-1#public-directory-support

# public

# vuepress build output

.vuepress/dist

# vuepress v2.x temp and cache directory

.temp

# Docusaurus cache and generated files

.docusaurus

# Serverless directories

.serverless/

# FuseBox cache

.fusebox/

# DynamoDB Local files

.dynamodb/

# TernJS port file

.tern-port

# Stores VSCode versions used for testing VSCode extensions

.vscode-test

# yarn v2

.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

node_modules
crs
# IntelliJ based IDEs
.idea

circuits/contract
circuits/proofs
# Finder (MacOS) folder config
.DS_Store
Empty file added foundry-voting/.gitmodule
Empty file.
3 changes: 0 additions & 3 deletions foundry-voting/.gitmodules

This file was deleted.

123 changes: 91 additions & 32 deletions foundry-voting/README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,113 @@
# zk Voting with Foundry
# Foundry Voting Example

[![Run Tests on PR](https://github.com/noir-lang/noir-starter/actions/workflows/foundry-voting.yml/badge.svg)](https://github.com/noir-lang/noir-starter/actions/workflows/foundry-voting.yml)
This project demonstrates a voting system implementation using Noir circuits and Foundry.

This example project shows how to create a simple zk voting circuit in Noir with a corresponding Solidity contract to track eligible voters, proposals and votes.
## Quick Start

This example was last tested with Noir version 0.28.0. You can install it with [noirup](https://noir-lang.org/docs/getting_started/installation/#installing-noirup) using
Install dependencies:
```bash
bun install
```

## Development Guide

### Interactive Build Process

Run all steps interactively with status updates:
```bash
noirup -v 0.28.0
bun run build:ultraplonk
```

## Overview
This will guide you through all the steps with prompts and status updates.

### Manual Steps

This is the model used for creating the [circuit](circuits/src/main.nr) and the [zkVote contract](src/zkVote.sol) to manage private voting.
#### 1. Circuit Testing

1. Create a set of voters. A merkle root is stored in the zkVote Solidity contract that voters will use to verify membership against. In this example, there are 4 accounts in the set of voters. The private keys are 0, 1, 2, 3 and the secret value to create the commitment is 9.
Run the test suite:
```bash
bun run circuits:test
```

#### 2. Circuit Execution Flow

| Private Key | Commitment = pedersen(private key, secret) |
| ----------- | ------------------------------------------------------------------ |
| 1 | 0x03542cb720369f19a74fd05b4edfbedb27a78514ad3283f1b3270a1656cced8e |
| 2 | 0x1efa9d6bb4dfdf86063cc77efdec90eb9262079230f1898049efad264835b6c8 |
| 3 | 0x24013340c052ebf847e0d7081f84e6a8e92f54e2e1726a1e559ac46a8f242007 |
| 4 | 0x04fd3da9756f25c72ca8990437b7f7b58e7ca48bfc21e65e7978320db8b1e5c5 |
Execute the circuit (generates witness and circuit JSON file):
```bash
bun run circuits:execute
```

This gives intermediate hashes of `0x046394ae1ebbf494f2cd2c2d37171099510d099489c9accef59f90512d5f0477` (`pedersen(commitment0, commitment1)`) and `0x2a653551d87767c545a2a11b29f0581a392b4e177a87c8e3eb425c51a26a8c77` (`pedersen(commitment2, commitment3)`) and a root hash of `0x215597bacd9c7e977dfc170f320074155de974be494579d2586e5b268fa3b629`.
#### 3. Proof Generation

2. Users will input their information into the circuit and generate a proof (see example inputs in [Prover.toml](./circuits/Prover.toml) and run `nargo prove` to generate the proof.)
1. Public inputs and outputs are printed in [Verifier.toml](./circuits/Verifier.toml).
2. The proof is saved to `./proofs/foundry_voting.proof`.
3. The generated proof + the contents of Verifier.toml are sent in a transaction to the `castVote` function in the [zkVote](./src/zkVote.sol) contract. The function verifies that the sender is authorized to vote on the proposal, that they haven't already voted and tallies their vote.
Generate UltraPlonk proof:
```bash
bun run circuits:ultraplonk:generate-proof
```

## Testing
Generate verification key:
```bash
bun run circuits:ultraplonk:generate-vk
```

You can run the Noir tests (also defined in main.nr) with `nargo test`. To print test output, use `nargo test --show-output`.
#### 4. Contract Generation

See the test file [here](./test/zkVote.t.sol). Run tests with `forge test`.
Generate the Solidity verifier contract:
```bash
bun run circuits:contract
```

1. Run `nargo compile` to compile the circuit.
2. Run `nargo prove` to generate the proof (with the inputs in Prover.toml).
3. Run `nargo codegen-verifier` to generate the solidity verifier contract.
4. Run `yarn test` to run the Foundry test the Solidity verifier contract at `./test/zkVote.t.sol`.
#### 5. Proof Processing

## Development
Clean and format the proof for contract verification:
```bash
bun run ultraplonk:clean-proof
```

If you change the circuit at `./circuits/src/main.nr` you will need to recompile (`nargo compile`) the circuit and regenerate the Solidity verifier (saved to `./circuits/contract/plonk_vk.sol`).
#### 6. Contract Testing

The merkle tree will need to be recalculated whenever there are users added to the set or if there are any changes to the voters secrets (secrets are the input to the merkle membership commitment, so changing a key changes the corresponding leaf in the merkle tree, which changes the root). See `test_valid_build_merkle_tree` for an example calculation.
Run Forge tests:
```bash
forge test
```

Run `nargo test --show-output` in `./circuits` to print the example merkle tree.
### Advanced Usage

## Contributions
#### Manual Circuit Commands

Thanks to the folks at zkCamp modifying the original example and adding tests. You can see their repo [here](https://github.com/ZKCamp/noir-voting/tree/6-security).
If you need to run circuit commands directly:

1. Navigate to circuits directory:
```bash
cd circuits
```

2. Run Noir tests with output:
```bash
nargo test --show-output
```

3. Generate Prover.toml file:
```bash
nargo check
```

4. Execute circuit (after updating Prover.toml with inputs):
```bash
nargo execute
```
This generates:
- Circuit JSON in `target/circuits.json`
- Witness file in `target/circuits.gz`

#### Manual Proof Generation

Generate proof using UltraPlonk:
```bash
bb prove -b ./target/circuits.json -w ./target/circuits.gz -o ./target/proof
```

Generate proof string manually:
**Number of character to tail is counted with formula, 32 * NUMBER_OF_PUBLIC_INPUTS + 1**
**Number of public inputs also includes return values, that's why we have 4 public input for our circuit.**
```bash
tail -c +129 ./target/proof | od -An -v -t x1 | tr -d $' \n'
```
Binary file added foundry-voting/bun.lockb
Binary file not shown.
7 changes: 3 additions & 4 deletions foundry-voting/circuits/Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[package]
authors = [""]
compiler_version = ">=0.22.0"
name="foundry_voting"
type="bin"
name = "circuits"
type = "bin"
authors = ["Satyam Bansal"]

[dependencies]
4 changes: 2 additions & 2 deletions foundry-voting/circuits/Prover.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
hash_path = [
"0x1efa9d6bb4dfdf86063cc77efdec90eb9262079230f1898049efad264835b6c8",
"0x2a653551d87767c545a2a11b29f0581a392b4e177a87c8e3eb425c51a26a8c77"
"0x2a653551d87767c545a2a11b29f0581a392b4e177a87c8e3eb425c51a26a8c77",
]
index = "0"
proposalId = "0"
root = "0x215597bacd9c7e977dfc170f320074155de974be494579d2586e5b268fa3b629"
secret = "1"
vote = "1"
vote = "1"
4 changes: 0 additions & 4 deletions foundry-voting/circuits/Verifier.toml

This file was deleted.

Loading