Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ethereum #1

Merged
merged 36 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
0add013
Begining Ethereum integration
xcthulhu May 25, 2024
7730041
Adding executable bit
xcthulhu May 25, 2024
30f6933
Introducing VoteRecorder
xcthulhu May 25, 2024
5da74c7
Introducing .nvmrc
xcthulhu May 25, 2024
19845af
Introducing voting script
xcthulhu May 25, 2024
d85ddd5
flake/devShells: add kurtosis
marijanp May 25, 2024
8fa339e
Fixing formatting
xcthulhu May 25, 2024
31df1c2
Introducing scraping
xcthulhu May 25, 2024
2fe00ad
Making output of scrape script configurable
xcthulhu May 25, 2024
d80638d
Loading data from file in vote.ts
xcthulhu May 25, 2024
39e42b5
client: implement groth16-proof, extract-election-id
marijanp May 25, 2024
95ddc2c
format
marijanp May 25, 2024
bd70f61
add missing clap annotiation
marijanp May 25, 2024
df1328f
fix nix
marijanp May 25, 2024
2e4d3a3
fix nix
marijanp May 25, 2024
41f700b
Fixing prover
xcthulhu May 25, 2024
6dc8269
Introducing voting and auditing scripts
xcthulhu May 25, 2024
0cf326c
fix test
marijanp May 25, 2024
a83fe7a
flake: fix nix
marijanp May 25, 2024
561b754
removing trailing newline
xcthulhu May 25, 2024
cfed9fb
Updating vote.sh
xcthulhu May 25, 2024
26546be
dump
marijanp May 25, 2024
89c851f
prover: fix test
marijanp May 25, 2024
60ecd3c
add additional contrib section
marijanp May 25, 2024
f4ba7bc
flake: fix nix
marijanp May 25, 2024
e400369
update readme
jonas089 May 25, 2024
0e0d500
sign choice rather than gov id => protocol fix
jonas089 May 25, 2024
7e254cf
Updating README.md
xcthulhu May 25, 2024
6a825dc
client: implement groth16-proof, extract-election-id
marijanp May 25, 2024
d0ef7ff
fix nix
marijanp May 25, 2024
e053764
Updating Groth16 code
xcthulhu May 26, 2024
a1adaa6
Fixing prover
xcthulhu May 26, 2024
5b838b7
Merge remote-tracking branch 'origin/main' into ethereum
marijanp May 26, 2024
967c5c7
quick fix
May 26, 2024
78e15fe
Auditting votes
May 26, 2024
e5238c7
Merge pull request #2 from cspr-rad/main
jonas089 May 26, 2024
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ target/
result
api-config.toml
.env
election-*
election-*
artifacts
cache
node_modules
typechain-types
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions audit-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;

fn parse_receipts_file(path: PathBuf) -> Vec<Receipt> {
pub fn parse_receipts_file(path: PathBuf) -> Vec<Receipt> {
let file = File::open(path).expect("Failed to read receipts file");
let reader = BufReader::new(file);
let mut result: Vec<Receipt> = Vec::new();
for line in reader.lines() {
match line {
Ok(hex_encoded_receipt) => {
let hex_encoded_receipt = hex_encoded_receipt.strip_prefix("0x").unwrap_or(&hex_encoded_receipt);
let receipt_bytes: Vec<u8> =
hex::decode(hex_encoded_receipt).expect("Failed to decode receipt hex");
let receipt: Receipt =
Expand All @@ -35,9 +36,10 @@ fn verify_receipt_vec(receipts: Vec<Receipt>, gov_pub_key: String) -> HashMap<St
let mut valid_votes: Vec<CircuitOutputs> = Vec::new();
for receipt in receipts {
let journal: CircuitOutputs = receipt.journal.decode().expect("Failed to decode journal");
let journal_gov_pub: String = hex::encode(&journal.government_public_key);
let journal_gov_pub: String = format!("0x{}", hex::encode(&journal.government_public_key));
let voter_identity: Signature = journal.public_identity;
if journal_gov_pub != gov_pub_key {
eprintln!("Expected gov key: {}, got: {}", &gov_pub_key, &journal_gov_pub);
continue;
};
if identities.contains(&voter_identity) {
Expand Down Expand Up @@ -81,3 +83,7 @@ pub fn audit_data(path: PathBuf, gov_pub_key: String) {
let receipts = parse_receipts_file(path);
verify_receipt_vec(receipts, gov_pub_key);
}

pub fn serialize_receipt(receipt: Receipt) -> Vec<u8> {
bincode::serialize(&receipt).expect("Failed to serialize receipt")
}
4 changes: 3 additions & 1 deletion client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ version = "0.1.0"
edition = "2021"

[dependencies]
bincode = "1.3"
clap = { version = "4", features = ["derive"] }
methods = { path = "../methods" }
elliptic-curve = "0.13"
hex = "0.4"
risc0-zkvm = { version = "0.21", features = ["prove"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
serde = "1.0"
Expand All @@ -19,4 +21,4 @@ risc0-groth16 = { version = "0.21.0" }
audit-utils = {path="../audit-utils"}

[features]
groth16 = []
groth16 = []
Binary file added client/receipt
Binary file not shown.
57 changes: 46 additions & 11 deletions client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use k256::{
};
use rand_core::OsRng;
use reqwest::blocking::Client;
use risc0_types::CircuitOutputs;
use risc0_zkvm::Receipt;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
Expand All @@ -24,6 +26,11 @@ pub enum Command {
user_id_path: PathBuf,
#[arg(short, long)]
vote: String,
#[arg(short, long)]
receipt_out_path: Option<PathBuf>,
#[cfg(feature = "groth16")]
#[cfg_attr(feature = "groth16", arg(short, long))]
groth16_receipt_out_path: Option<PathBuf>,
},
GenerateKeyPair {
#[arg(short, long)]
Expand All @@ -43,6 +50,10 @@ pub enum Command {
#[arg(short, long)]
gov_key_hex: String,
},
ExtractElectionId {
#[arg(short, long)]
receipt_path: PathBuf,
},
}

#[derive(Serialize, Deserialize)]
Expand All @@ -53,7 +64,13 @@ pub struct VerifiedUser {

pub fn run(cli: Cli) {
match cli.command {
Command::Vote { user_id_path, vote } => {
Command::Vote {
user_id_path,
vote,
receipt_out_path,
#[cfg(feature = "groth16")]
groth16_receipt_out_path,
} => {
let user_secret_key =
SigningKey::from_slice(&fs::read(user_id_path.join("secret_key")).expect(""))
.expect("");
Expand All @@ -67,29 +84,39 @@ pub fn run(cli: Cli) {
.expect("");
let public_identity = Signature::from_slice(&verified_user.public_identity).expect("");

#[cfg(feature = "groth16")]
let SUBMIT_TO_LAYER_ONE = "CALL_TO_GROTH_16";
// todo: generate an optimized groth16 wrapper proof and submit it to ETH
/*

...
*/

// generate a regular risc0 proof and submit it to the API server
let receipt = prover::prove(
&vote,
&user_secret_key,
&government_public_key,
&public_identity,
None,
);

if let Some(receipt_out_path) = receipt_out_path {
let serialized_receipt = bincode::serialize(&receipt).expect("");
fs::write(receipt_out_path, serialized_receipt).expect("");
};

let client: Client = Client::new();
let response = client
.post("http://127.0.0.1:8080/submit_receipt")
.json(&receipt)
.send()
.expect("Failed to submit proof to server");
assert!(response.status().is_success());

#[cfg(feature = "groth16")]
if let Some(groth16_receipt_out_path) = groth16_receipt_out_path {
let groth16_receipt = prover::prove_groth16(receipt);
fs::write(
groth16_receipt_out_path,
format!(
"0x{}",
hex::encode(bincode::serialize(&groth16_receipt).expect(""))
),
)
.expect("");
}
}

Command::GenerateKeyPair {
Expand Down Expand Up @@ -138,12 +165,20 @@ pub fn run(cli: Cli) {
)
.expect("");
}

Command::Audit {
audit_file_path,
gov_key_hex,
} => {
audit_data(audit_file_path, gov_key_hex);
}
Command::ExtractElectionId { receipt_path } => {
let receipt_bytes = fs::read(receipt_path).expect("");
let receipt: Receipt = bincode::deserialize(&receipt_bytes).expect("");
let data: CircuitOutputs = receipt
.journal
.decode()
.expect("Failed to extract public journal from receipt");
println!("0x{}", hex::encode(data.government_public_key))
}
}
}
21 changes: 4 additions & 17 deletions client/src/prover.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::{fs, path::PathBuf};
// These constants represent the RISC-V ELF and the image ID generated by risc0-build.
// The ELF is used for proving and the ID is used for verification.
use k256::ecdsa::{signature::Signer, Signature, SigningKey, VerifyingKey};
Expand All @@ -11,7 +10,6 @@ pub fn prove(
user_secret_key: &SigningKey,
government_public_key: &VerifyingKey,
public_identity: &Signature,
dump_path: Option<PathBuf>,
) -> Receipt {
let user_public_key_serialized: Vec<u8> = user_secret_key
.verifying_key()
Expand Down Expand Up @@ -39,24 +37,15 @@ pub fn prove(
.unwrap();

let prover = default_prover();
let receipt = prover.prove(env, ACROPOLIS_ELF).unwrap();
// dump receipt
if let Some(path) = dump_path {
fs::write(
path,
serde_json::to_vec(&receipt).expect("Failed to serialize receipt"),
)
.expect("Failed to dump receipt");
}
receipt
prover.prove(env, ACROPOLIS_ELF).unwrap()
}

#[cfg(feature = "groth16")]
pub fn prove_groth16(receipt: Receipt) -> Receipt {
use risc0_groth16::docker::stark_to_snark;
use risc0_zkvm::{
get_prover_server, recursion::identity_p254, CompactReceipt, ExecutorEnv, InnerReceipt,
ProverOpts, Receipt,
get_prover_server, recursion::identity_p254, CompactReceipt, InnerReceipt, ProverOpts,
Receipt,
};
let opts = ProverOpts::default();
let prover = get_prover_server(&opts).unwrap();
Expand Down Expand Up @@ -99,7 +88,6 @@ fn generate_proof() {
&signing_key,
government_signing_key.verifying_key(),
&public_identity,
None,
);
}

Expand Down Expand Up @@ -130,9 +118,8 @@ fn optimize_groth16_proof() {
&signing_key,
government_signing_key.verifying_key(),
&public_identity,
None,
);
let optimized_receipt = prove_groth16(receipt);
let optimized_receipt = prove_groth16(&receipt);
println!(
"Optimized Receipt size: {:?}",
serde_json::to_vec(&optimized_receipt).unwrap().len()
Expand Down
18 changes: 18 additions & 0 deletions ethereum/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
node_modules
.env

# Hardhat files
/cache
/artifacts

# TypeChain files
/typechain
/typechain-types

# solidity-coverage files
/coverage
/coverage.json

# Environment variables set by package.json scripts
/PORT
/CONTRACT_ADDRESS
1 change: 1 addition & 0 deletions ethereum/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.13.1
13 changes: 13 additions & 0 deletions ethereum/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Sample Hardhat Project

This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract.

Try running some of the following tasks:

```shell
npx hardhat help
npx hardhat test
REPORT_GAS=true npx hardhat test
npx hardhat node
npx hardhat ignition deploy ./ignition/modules/Lock.ts
```
31 changes: 31 additions & 0 deletions ethereum/contracts/VoteRecorder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;


contract VoteRecorder {

event VoteRecorded (
bytes indexed electionId,
uint256 voteIndex,
bytes voteReceipt
);

mapping(bytes electionId => uint256 votesIndex) public votesIndices;
mapping(bytes electionId => mapping(uint256 voteCounter => bytes voteReceipt)) public voteReceipts;

constructor() {}

function vote(bytes calldata electionId, bytes calldata voteReceipt) external {
voteReceipts[electionId][votesIndices[electionId]] = voteReceipt;
emit VoteRecorded(electionId, votesIndices[electionId], voteReceipt);
votesIndices[electionId] += 1;
}

function getVoteReceipt(bytes calldata electionId, uint256 voteIndex) external view returns (bytes memory) {
return voteReceipts[electionId][voteIndex];
}

function getVotesTallied(bytes calldata electionId) external view returns (uint256) {
return votesIndices[electionId];
}
}
66 changes: 66 additions & 0 deletions ethereum/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { HardhatUserConfig, task } from "hardhat/config";
import fs from "fs";
import path from "path";
import "@nomicfoundation/hardhat-toolbox";

task(
"balances",
"Prints the list of ETH account balances",
async (_args, hre): Promise<void> => {
// @ts-ignore
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
// @ts-ignore
const balance = await hre.ethers.provider.getBalance(account.address);
console.log(`${account.address} has balance ${balance.toString()}`);
}
},
);

const config: HardhatUserConfig = {
solidity: "0.8.24",

networks: {
localnet: {
url: `http://127.0.0.1:${fs.readFileSync(path.resolve(__dirname, 'PORT'), 'utf8')}`,
// These are private keys associated with prefunded test accounts created by the ethereum-package
// https://github.com/kurtosis-tech/ethereum-package/blob/main/src/prelaunch_data_generator/genesis_constants/genesis_constants.star
accounts: [
// 0x8943545177806ED17B9F23F0a21ee5948eCaa776
"bcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31",

// 0xE25583099BA105D9ec0A67f5Ae86D90e50036425
"39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d",

// 0x614561D2d143621E126e87831AEF287678B442b8
"53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710",

// 0xf93Ee4Cf8c6c40b329b0c0626F28333c132CF241
"ab63b23eb7941c1251757e24b3d2350d2bc05c3c388d06f8fe6feafefb1e8c70",

// 0x802dCbE1B1A97554B4F50DB5119E37E8e7336417
"5d2344259f42259f82d2c140aa66102ba89b57b4883ee441a8b312622bd42491",

// 0xAe95d8DA9244C37CaC0a3e16BA966a8e852Bb6D6
"27515f805127bebad2fb9b183508bdacb8c763da16f54e0678b16e8f28ef3fff",

// 0x2c57d1CFC6d5f8E4182a56b4cf75421472eBAEa4
"7ff1a4c1d57e5e784d327c4c7651e952350bc271f156afb3d00d20f5ef924856",

// 0x741bFE4802cE1C4b5b00F9Df2F5f179A1C89171A
"3a91003acaf4c21b3953d94fa4a6db694fa69e5242b2e37be05dd82761058899",

// 0xc3913d4D8bAb4914328651C2EAE817C8b78E1f4c
"bb1d0f125b4fb2bb173c318cdead45468474ca71474e2247776b2b4c0fa2d3f5",

// 0x65D08a056c17Ae13370565B04cF77D2AfA1cB9FA
"850643a0224065ecce3882673c21f56bcf6eef86274cc21cadff15930b59fc8c",

// 0x3e95dFbBaF6B348396E6674C7871546dCC568e56
"94eb3102993b41ec55c241060f47daa0f6372e2e3ad7e91612ae36c364042e44",
],
},
},
};

export default config;
Loading
Loading