diff --git a/contracts/Ballot.sol b/contracts/Ballot.sol index 8712aed..d965b35 100644 --- a/contracts/Ballot.sol +++ b/contracts/Ballot.sol @@ -2,6 +2,8 @@ pragma solidity >=0.7.0 <0.9.0; +import "hardhat/console.sol"; + contract Ballot { struct Voter { bool hasRightToVote; @@ -24,6 +26,7 @@ contract Ballot { uint256 public yesCount; uint256 public voteCount; address public verifierContractAddress; + uint256 public merkleRoot; constructor(address _verifierContractAddress) { chairperson = msg.sender; @@ -52,15 +55,27 @@ contract Ballot { commitList.push(commit); } + function setMerkleRoot(uint256 newMerkleRoot) public { + require( + msg.sender == chairperson, + "Only chairperson can set the merkle root!" + ); + merkleRoot = newMerkleRoot; + } + function revealVote( bool _vote, bytes32 serialNumber, - bytes32[10] memory commitsForProof, Verifier.Proof memory proof ) public { + require( + merkleRoot != 0, + "Merkle root has not been set by the chairperson!" + ); + // validate proof // call sokrates - uint256[85] memory proof_inputs; + uint256[13] memory proof_inputs; uint256 j = 0; if (_vote) { proof_inputs[j] = 1; @@ -77,28 +92,22 @@ contract Ballot { break; } } - for (uint256 c_index = 0; c_index < 10; c_index++) { - bytes32 commit = commitsForProof[c_index]; - for (uint256 i = 7; i >= 0; i--) { - proof_inputs[j] = uint256(commit >> (i * 32)) & 0xffffffff; - j += 1; - if (i == 0) { - break; - } + for (uint256 i = 7; i >= 0; --i) { + proof_inputs[j] = uint256(merkleRoot >> (i * 32)) & 0xffffffff; + j += 1; + if (i == 0) { + break; } } - Verifier v = Verifier(verifierContractAddress); - require(v.verifyTx(proof, proof_inputs)); - - // is commitsForProof subset of commits - for (uint256 i = 0; i < commitsForProof.length; ++i) { - bytes32 commitInProof = commitsForProof[i]; - if (commitInProof != 0) { - require(commits[commitInProof]); - } + console.log("Inputs:"); + for (uint256 i = 0; i < 13; i++) { + console.log(proof_inputs[i]); } + Verifier v = Verifier(verifierContractAddress); + require(v.verifyTx(proof, proof_inputs), "Proof does not verify!"); + require(!seenSerialNumbers[serialNumber], "Already revealed!"); seenSerialNumbers[serialNumber] = true; @@ -126,12 +135,7 @@ interface Verifier { G1Point c; } - function verifyTx(Proof memory proof, uint256[85] memory input) - external - view - returns (bool r); - - function testVerifyTx(Proof memory proof, uint256[85] memory input) + function verifyTx(Proof memory proof, uint256[13] memory input) external view returns (bool r); diff --git a/src/provers.py b/src/provers.py index de40282..f3a3960 100644 --- a/src/provers.py +++ b/src/provers.py @@ -1,13 +1,12 @@ -from abc import ABC, abstractmethod import hashlib -import shutil -import os -from pathlib import Path import json import math -from typing import List, Tuple +import os +import shutil +from abc import ABC, abstractmethod +from pathlib import Path from tempfile import TemporaryDirectory -import time +from typing import List, Tuple from zokrates_pycrypto.gadgets.pedersenHasher import PedersenHasher diff --git a/src/vote_cli.py b/src/vote_cli.py index c9fe6bf..c5914cc 100644 --- a/src/vote_cli.py +++ b/src/vote_cli.py @@ -9,7 +9,7 @@ from Crypto.Signature import PKCS1_v1_5 from web3 import Web3 -from provers import ZokratesProver +from provers import MERKLE_TREE_DEPTH, ZokratesProver, calculate_merkle_tree VOTE_SERVER = "http://0.0.0.0:5000" PROVER = ZokratesProver(Path(".")) @@ -131,6 +131,27 @@ def eth_give_right_to_vote(address: str): voting_contract = get_voting_contract(voting_contract_address, w3) send_transaction(w3, voting_contract.functions.giveRightToVote(address).build_transaction()) +@app.command() +def eth_set_merkle_root(): + w3 = Web3(Web3.HTTPProvider(HTTP_ENDPOINT_URL)) + voting_contract_address = get_deployed_contract_address() + voting_contract = get_voting_contract(voting_contract_address, w3) + + num_commits = voting_contract.functions.numCommits().call() + known_hashes = [] + for i in range(num_commits): + known_hashes.append(voting_contract.functions.commitList(i).call()) + + # This actually computes the Merkle proof for the first commit, but we're only interested in the merkle root + print("Computing Merkle root...") + merkle_root, _, _ = calculate_merkle_tree(known_hashes, 2 ** MERKLE_TREE_DEPTH, known_hashes[0]) + + print(f"Merkle root is: {merkle_root.hex()}") + send_transaction(w3, voting_contract.functions.setMerkleRoot(int(merkle_root.hex(), 16)).build_transaction({ + # Gas estimation fails for some reason, so set limit manually + "gas": 2000000 + })) + @app.command() def eth_vote(vote: bool): @@ -185,13 +206,18 @@ def eth_reveal(): for i in range(num_commits): known_hashes.append(voting_contract.functions.commitList(i).call()) - proof, known_hashes = PROVER.compute_proof(serial_number, secret, vote, known_hashes) + proof, merkle_root = PROVER.compute_proof(serial_number, secret, vote, known_hashes) + print(f"Merkle root is: {merkle_root.hex()}") + merkle_root_int = int(merkle_root.hex(), 16) + stored_merkle_root = voting_contract.functions.merkleRoot().call() + assert stored_merkle_root != 0, "The chairperson has not set the Merkle root yet!" + assert stored_merkle_root == merkle_root_int, "Merkle Root is different from what was set by the chairperson!" def to_ints(hex_str): return (int(hex_str[0], 16), int(hex_str[1], 16)) proof_abc = (to_ints(proof["proof"]["a"]), (to_ints(proof["proof"]["b"][0]), to_ints(proof["proof"]["b"][1])), to_ints(proof["proof"]["c"])) - tx = voting_contract.functions.revealVote(vote, serial_number, known_hashes, proof_abc).build_transaction() + tx = voting_contract.functions.revealVote(vote, serial_number, proof_abc).build_transaction() send_transaction(w3, tx) diff --git a/test_eth.sh b/test_eth.sh index 13d2996..8940794 100755 --- a/test_eth.sh +++ b/test_eth.sh @@ -5,5 +5,6 @@ python src/vote_cli.py eth-deploy-voting-contract # Right to vote for second hardhat account #python src/vote_cli.py eth-give-right-to-vote 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 python src/vote_cli.py eth-vote yes +python src/vote_cli.py eth-set-merkle-root python src/vote_cli.py eth-reveal python src/vote_cli.py eth-get-results \ No newline at end of file