You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+45-13Lines changed: 45 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -962,23 +962,40 @@ Inside the `vote` function, voters will send their:
962
962
-**proof** → the cryptographic proof (later built in the frontend)
963
963
-**public inputs** → `nullifierHash`, `root`, `vote`, and `tree depth`
964
964
965
-
Before counting votes, we enforce some **rules**:
965
+
Before counting votes, we enforce some **rules** in the following order:
966
966
967
-
#### 1. Prevent Double-Voting 🛑
967
+
#### 1. Validate the Root 🔐
968
968
969
-
- Check if the `_nullifierHash` has already been used.
970
-
- If yes → revert the transaction.
971
-
- Track used nullifiers with a mapping: `s_nullifierHashes`.
969
+
-**Check for empty tree root**: this prevents voting when no one has registered yet.
970
+
-**Validate root matches current root state** (`s_tree.root()`): This ensures the proof was generated against the **actual on-chain Merkle tree**, not some arbitrary tree.
972
971
973
-
👉 Without this safeguard, anyone could replay the same proof/inputs over and over to vote multiple times.
974
-
That’s why **nullifiers are the cornerstone** of privacy-preserving voting.
972
+
**Why this matters:** Without root validation, an attacker could:
973
+
974
+
-**Create their own fake Merkle tree** with commitments they control
975
+
- Generate a mathematically valid ZK proof against that fake tree
976
+
- Submit the proof with their fake root to the contract
977
+
- The verifier would accept it (the proof is valid for that root!)
978
+
-**Result**: They vote without ever registering on-chain, completely bypassing the allowlist system
979
+
980
+
> 👉 The root check is what **binds the proof to the actual on-chain registration tree**. Without it, anyone could vote by creating fake trees. This is your **first line of defense**!
975
981
976
982
#### 2. Verify the Proof 🔒
977
983
978
984
- Call the `verify()` function on `i_verifier` and pass in the proof + public inputs.
979
985
- The verifier expects **public inputs as a `bytes32[]` array**, in **exactly the same order** as in your circuit file.
980
986
- If verification fails → revert the transaction.
981
987
988
+
#### 3. Prevent Double-Voting 🛑
989
+
990
+
- Check if the `_nullifierHash` has already been used.
991
+
- If yes → revert the transaction.
992
+
- Track used nullifiers with a mapping: `s_nullifierHashes`.
993
+
994
+
👉 Without this safeguard, anyone could replay the same proof/inputs over and over to vote multiple times.
995
+
That's why **nullifiers are the cornerstone** of privacy-preserving voting.
996
+
997
+
👉 **Important**: This check happens **after** proof verification. If the proof is invalid, we want to fail fast without wasting gas on state changes.
998
+
982
999
✅ Once both checks pass:
983
1000
984
1001
- Increment `s_yesVotes` or `s_noVotes` accordingly
@@ -990,27 +1007,34 @@ That’s why **nullifiers are the cornerstone** of privacy-preserving voting.
990
1007
<details>
991
1008
<summary>❓ Question 1</summary>
992
1009
1010
+
What two root validation checks must you perform **before** any other logic, and why are they essential for security?
1011
+
1012
+
</details>
1013
+
1014
+
<details>
1015
+
<summary>❓ Question 2</summary>
1016
+
993
1017
Before writing the voting logic, how can you stop a `_nullifierHash` from being reused so no one can vote twice?
994
1018
Make sure to revert with the correct error.
995
1019
996
1020
</details>
997
1021
998
1022
<details>
999
-
<summary>❓ Question 2</summary>
1023
+
<summary>❓ Question 3</summary>
1000
1024
1001
1025
When passing inputs to the verifier, how do you build the `bytes32[]` array and in what order should you place `_nullifierHash`, `_root`, `_vote`, and `_depth`?
1002
1026
1003
1027
</details>
1004
1028
1005
1029
<details>
1006
-
<summary>❓ Question 3</summary>
1030
+
<summary>❓ Question 4</summary>
1007
1031
1008
1032
After calling `i_verifier.verify(_proof, publicInputs)`, what condition should you check, and what should happen if it fails?
1009
1033
1010
1034
</details>
1011
1035
1012
1036
<details>
1013
-
<summary>❓ Question 4</summary>
1037
+
<summary>❓ Question 5</summary>
1014
1038
1015
1039
Once the proof is verified, how do you decide whether to increment `s_yesVotes` or `s_noVotes` and then emit the `VoteCast` event?
Copy file name to clipboardExpand all lines: extension/README.md.args.mjs
+45-13Lines changed: 45 additions & 13 deletions
Original file line number
Diff line number
Diff line change
@@ -967,23 +967,40 @@ Inside the \`vote\` function, voters will send their:
967
967
- **proof** → the cryptographic proof (later built in the frontend)
968
968
- **public inputs** → \`nullifierHash\`, \`root\`, \`vote\`, and \`tree depth\`
969
969
970
-
Before counting votes, we enforce some **rules**:
970
+
Before counting votes, we enforce some **rules** in the following order:
971
971
972
-
#### 1. Prevent Double-Voting 🛑
972
+
#### 1. Validate the Root 🔐
973
973
974
-
- Check if the \`_nullifierHash\` has already been used.
975
-
- If yes → revert the transaction.
976
-
- Track used nullifiers with a mapping: \`s_nullifierHashes\`.
974
+
- **Check for empty tree root**: this prevents voting when no one has registered yet.
975
+
- **Validate root matches current root state** (\`s_tree.root()\`): This ensures the proof was generated against the **actual on-chain Merkle tree**, not some arbitrary tree.
977
976
978
-
👉 Without this safeguard, anyone could replay the same proof/inputs over and over to vote multiple times.
979
-
That’s why **nullifiers are the cornerstone** of privacy-preserving voting.
977
+
**Why this matters:** Without root validation, an attacker could:
978
+
979
+
- **Create their own fake Merkle tree** with commitments they control
980
+
- Generate a mathematically valid ZK proof against that fake tree
981
+
- Submit the proof with their fake root to the contract
982
+
- The verifier would accept it (the proof is valid for that root!)
983
+
- **Result**: They vote without ever registering on-chain, completely bypassing the allowlist system
984
+
985
+
> 👉 The root check is what **binds the proof to the actual on-chain registration tree**. Without it, anyone could vote by creating fake trees. This is your **first line of defense**!
980
986
981
987
#### 2. Verify the Proof 🔒
982
988
983
989
- Call the \`verify()\` function on \`i_verifier\` and pass in the proof + public inputs.
984
990
- The verifier expects **public inputs as a \`bytes32[]\` array**, in **exactly the same order** as in your circuit file.
985
991
- If verification fails → revert the transaction.
986
992
993
+
#### 3. Prevent Double-Voting 🛑
994
+
995
+
- Check if the \`_nullifierHash\` has already been used.
996
+
- If yes → revert the transaction.
997
+
- Track used nullifiers with a mapping: \`s_nullifierHashes\`.
998
+
999
+
👉 Without this safeguard, anyone could replay the same proof/inputs over and over to vote multiple times.
1000
+
That's why **nullifiers are the cornerstone** of privacy-preserving voting.
1001
+
1002
+
👉 **Important**: This check happens **after** proof verification. If the proof is invalid, we want to fail fast without wasting gas on state changes.
1003
+
987
1004
✅ Once both checks pass:
988
1005
989
1006
- Increment \`s_yesVotes\` or \`s_noVotes\` accordingly
@@ -995,27 +1012,34 @@ That’s why **nullifiers are the cornerstone** of privacy-preserving voting.
995
1012
<details>
996
1013
<summary>❓ Question 1</summary>
997
1014
1015
+
What two root validation checks must you perform **before** any other logic, and why are they essential for security?
1016
+
1017
+
</details>
1018
+
1019
+
<details>
1020
+
<summary>❓ Question 2</summary>
1021
+
998
1022
Before writing the voting logic, how can you stop a \`_nullifierHash\` from being reused so no one can vote twice?
999
1023
Make sure to revert with the correct error.
1000
1024
1001
1025
</details>
1002
1026
1003
1027
<details>
1004
-
<summary>❓ Question 2</summary>
1028
+
<summary>❓ Question 3</summary>
1005
1029
1006
1030
When passing inputs to the verifier, how do you build the \`bytes32[]\` array and in what order should you place \`_nullifierHash\`, \`_root\`, \`_vote\`, and \`_depth\`?
1007
1031
1008
1032
</details>
1009
1033
1010
1034
<details>
1011
-
<summary>❓ Question 3</summary>
1035
+
<summary>❓ Question 4</summary>
1012
1036
1013
1037
After calling \`i_verifier.verify(_proof, publicInputs)\`, what condition should you check, and what should happen if it fails?
1014
1038
1015
1039
</details>
1016
1040
1017
1041
<details>
1018
-
<summary>❓ Question 4</summary>
1042
+
<summary>❓ Question 5</summary>
1019
1043
1020
1044
Once the proof is verified, how do you decide whether to increment \`s_yesVotes\` or \`s_noVotes\` and then emit the \`VoteCast\` event?
0 commit comments