Skip to content

Commit 09721d8

Browse files
authored
Merge pull request #365 from phipsae/challenge-zk-voting
Challenge zk voting
2 parents 51fee28 + b43c561 commit 09721d8

40 files changed

+6448
-62
lines changed

README.md

Lines changed: 1889 additions & 4 deletions
Large diffs are not rendered by default.

extension/README.md.args.mjs

Lines changed: 1866 additions & 37 deletions
Large diffs are not rendered by default.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "circuits"
3+
type = "bin"
4+
authors = [""]
5+
6+
[dependencies]
7+
binary_merkle_root = { git = "https://github.com/privacy-scaling-explorations/zk-kit.noir", tag = "binary-merkle-root-v0.0.1", directory = "packages/binary-merkle-root" }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use std::hash::poseidon::bn254::hash_1;
2+
use std::hash::poseidon::bn254::hash_2;
3+
////// Checkpoint 4 //////
4+
// use binary_merkle_root::binary_merkle_root;
5+
6+
fn main(
7+
// public inputs
8+
nullifier_hash: pub Field,
9+
// private inputs
10+
nullifier: Field,
11+
secret: Field,
12+
////// Checkpoint 4 //////
13+
// // public inputs
14+
// root: pub Field,
15+
// vote: pub bool,
16+
// depth: pub u32,
17+
// // private inputs
18+
// index: Field,
19+
// siblings: [Field; 16],
20+
) {
21+
////// Checkpoint 3 //////
22+
23+
////// Checkpoint 4 //////
24+
25+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
//SPDX-License-Identifier: MIT
2+
pragma solidity >=0.8.0 <0.9.0;
3+
4+
import { LeanIMT, LeanIMTData } from "@zk-kit/lean-imt.sol/LeanIMT.sol";
5+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
6+
/// Checkpoint 6 //////
7+
// import {IVerifier} from "./Verifier.sol";
8+
9+
contract Voting is Ownable {
10+
using LeanIMT for LeanIMTData;
11+
12+
//////////////////
13+
/// Errors //////
14+
/////////////////
15+
16+
error Voting__CommitmentAlreadyAdded(uint256 commitment);
17+
error Voting__NullifierHashAlreadyUsed(bytes32 nullifierHash);
18+
error Voting__InvalidProof();
19+
error Voting__NotAllowedToVote();
20+
21+
///////////////////////
22+
/// State Variables ///
23+
///////////////////////
24+
25+
string private s_question;
26+
mapping(address => bool) private s_voters;
27+
uint256 private s_yesVotes;
28+
uint256 private s_noVotes;
29+
30+
/// Checkpoint 2 //////
31+
32+
/// Checkpoint 6 //////
33+
34+
//////////////
35+
/// Events ///
36+
//////////////
37+
38+
event VoterAdded(address indexed voter);
39+
event NewLeaf(uint256 index, uint256 value);
40+
event VoteCast(
41+
bytes32 indexed nullifierHash,
42+
address indexed voter,
43+
bool vote,
44+
uint256 timestamp,
45+
uint256 totalYes,
46+
uint256 totalNo
47+
);
48+
49+
//////////////////
50+
////Constructor///
51+
//////////////////
52+
53+
constructor(address _owner, address _verifier, string memory _question) Ownable(_owner) {
54+
s_question = _question;
55+
/// Checkpoint 6 //////
56+
}
57+
58+
//////////////////
59+
/// Functions ///
60+
//////////////////
61+
62+
/**
63+
* @notice Batch updates the allowlist of voter EOAs
64+
* @dev Only the contract owner can call this function. Emits `VoterAdded` for each updated entry.
65+
* @param voters Addresses to update in the allowlist
66+
* @param statuses True to allow, false to revoke
67+
*/
68+
function addVoters(address[] calldata voters, bool[] calldata statuses) public onlyOwner {
69+
require(voters.length == statuses.length, "Voters and statuses length mismatch");
70+
71+
for (uint256 i = 0; i < voters.length; i++) {
72+
s_voters[voters[i]] = statuses[i];
73+
emit VoterAdded(voters[i]);
74+
}
75+
}
76+
77+
/**
78+
* @notice Registers a commitment leaf for an allowlisted address
79+
* @dev A given allowlisted address can register exactly once. Reverts if
80+
* the caller is not allowlisted or has already registered, or if the
81+
* same commitment has been previously inserted. Emits `NewLeaf`.
82+
* @param _commitment The Poseidon-based commitment to insert into the IMT
83+
*/
84+
function register(uint256 _commitment) public {
85+
/// Checkpoint 2 //////
86+
}
87+
88+
/**
89+
* @notice Casts a vote using a zero-knowledge proof
90+
* @dev Enforces single-use via `s_nullifierHashes`. Public inputs order must
91+
* match the circuit: root, nullifierHash, vote, depth. The `_vote`
92+
* value is interpreted as: 1 => yes, any other value => no. Emits `VoteCast`.
93+
* @param _proof Ultra Honk proof bytes
94+
* @param _root Merkle root corresponding to the registered commitments tree
95+
* @param _nullifierHash Unique nullifier to prevent double voting
96+
* @param _vote Encoded vote: 1 for yes, otherwise counted as no
97+
* @param _depth Tree depth used by the circuit
98+
*/
99+
function vote(bytes memory _proof, bytes32 _nullifierHash, bytes32 _root, bytes32 _vote, bytes32 _depth) public {
100+
/// Checkpoint 6 //////
101+
}
102+
103+
/////////////////////////
104+
/// Getter Functions ///
105+
////////////////////////
106+
107+
function getVotingData()
108+
public
109+
view
110+
returns (
111+
string memory question,
112+
address contractOwner,
113+
uint256 yesVotes,
114+
uint256 noVotes,
115+
uint256 size,
116+
uint256 depth,
117+
uint256 root
118+
)
119+
{
120+
question = s_question;
121+
contractOwner = owner();
122+
yesVotes = s_yesVotes;
123+
noVotes = s_noVotes;
124+
/// Checkpoint 2 //////
125+
// size = s_tree.size;
126+
// depth = s_tree.depth;
127+
// root = s_tree.root();
128+
}
129+
130+
function getVoterData(address _voter) public view returns (bool voter, bool registered) {
131+
voter = s_voters[_voter];
132+
// /// Checkpoint 2 //////
133+
// registered = s_hasRegistered[_voter];
134+
}
135+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//SPDX-License-Identifier: MIT
2+
pragma solidity >=0.8.0 <0.9.0;
3+
4+
/// Checkpoint 6 //////
5+
// import {IVerifier} from "../Verifier.sol";
6+
7+
// contract VerifierMock is IVerifier {
8+
// bool public shouldVerify = true;
9+
10+
// // Optional: enforce an exact public inputs set and order for tests
11+
// bool public enforceExpectedInputs;
12+
// bytes32 public expectedNullifier;
13+
// bytes32 public expectedRoot;
14+
// bytes32 public expectedVote;
15+
// bytes32 public expectedDepth;
16+
17+
// function setShouldVerify(bool _shouldVerify) external {
18+
// shouldVerify = _shouldVerify;
19+
// }
20+
21+
// function setExpectedInputs(bytes32 _nullifier, bytes32 _root, bytes32 _vote, bytes32 _depth) external {
22+
// enforceExpectedInputs = true;
23+
// expectedNullifier = _nullifier;
24+
// expectedRoot = _root;
25+
// expectedVote = _vote;
26+
// expectedDepth = _depth;
27+
// }
28+
29+
// function clearExpectedInputs() external {
30+
// enforceExpectedInputs = false;
31+
// expectedNullifier = 0x0;
32+
// expectedRoot = 0x0;
33+
// expectedVote = 0x0;
34+
// expectedDepth = 0x0;
35+
// }
36+
37+
// function verify(bytes calldata, bytes32[] calldata _publicInputs) external view returns (bool) {
38+
// if (!shouldVerify) {
39+
// return false;
40+
// }
41+
// if (enforceExpectedInputs) {
42+
// if (
43+
// _publicInputs.length != 4 || _publicInputs[0] != expectedNullifier || _publicInputs[1] != expectedRoot
44+
// || _publicInputs[2] != expectedVote || _publicInputs[3] != expectedDepth
45+
// ) {
46+
// return false;
47+
// }
48+
// }
49+
// return true;
50+
// }
51+
// }
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { HardhatRuntimeEnvironment } from "hardhat/types";
2+
import { DeployFunction } from "hardhat-deploy/types";
3+
4+
/**
5+
* Deploys a contract named "YourContract" using the deployer account and
6+
* constructor arguments set to the deployer address
7+
*
8+
* @param hre HardhatRuntimeEnvironment object.
9+
*/
10+
const deployYourVotingContract: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
11+
/*
12+
On localhost, the deployer account is the one that comes with Hardhat, which is already funded.
13+
14+
When deploying to live networks (e.g `yarn deploy --network sepolia`), the deployer account
15+
should have sufficient balance to pay for the gas fees for contract creation.
16+
17+
You can generate a random account with `yarn generate` or `yarn account:import` to import your
18+
existing PK which will fill DEPLOYER_PRIVATE_KEY_ENCRYPTED in the .env file (then used on hardhat.config.ts)
19+
You can run the `yarn account` command to check your balance in every network.
20+
*/
21+
const { deployer } = await hre.getNamedAccounts();
22+
const { deploy } = hre.deployments;
23+
24+
const ownerAddress = "0x0000000000000000000000000000000000000001";
25+
26+
/// checkpoint 6 //////
27+
const verifierAddress = "0x0000000000000000000000000000000000000002"; // placeholder
28+
// const verifier = await deploy("HonkVerifier", {
29+
// from: deployer,
30+
// log: true,
31+
// autoMine: true,
32+
// });
33+
34+
/// checkpoint 2 //////
35+
const leanIMTAddress = "0x0000000000000000000000000000000000000003"; // placeholder
36+
// const poseidon3 = await deploy("PoseidonT3", {
37+
// from: deployer,
38+
// log: true,
39+
// autoMine: true,
40+
// });
41+
42+
// const leanIMT = await deploy("LeanIMT", {
43+
// from: deployer,
44+
// log: true,
45+
// autoMine: true,
46+
// libraries: {
47+
// PoseidonT3: poseidon3.address,
48+
// },
49+
// });
50+
51+
await deploy("Voting", {
52+
from: deployer,
53+
// Contract constructor arguments
54+
/// checkpoint 6 //////
55+
args: [ownerAddress, verifierAddress, "Should we build zk apps?"],
56+
log: true,
57+
// autoMine: can be passed to the deploy function to make the deployment process faster on local networks by
58+
// automatically mining the contract deployment transaction. There is no effect on live networks.
59+
autoMine: true,
60+
libraries: {
61+
/// checkpoint 2 //////
62+
LeanIMT: leanIMTAddress,
63+
},
64+
});
65+
};
66+
67+
export default deployYourVotingContract;
68+
69+
// Tags are useful if you have multiple deploy files and only want to run one of them.
70+
// e.g. yarn deploy --tags YourContract
71+
deployYourVotingContract.tags = ["YourVotingContract"];
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export const configOverrides = {
2+
solidity: {
3+
compilers: [
4+
{
5+
version: "0.8.27",
6+
settings: {
7+
optimizer: {
8+
enabled: true,
9+
// https://docs.soliditylang.org/en/latest/using-the-compiler.html#optimizer-options
10+
runs: 200,
11+
},
12+
},
13+
},
14+
],
15+
},
16+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"@zk-kit/lean-imt.sol": "^2.0.1"
4+
}
5+
}

0 commit comments

Comments
 (0)