Skip to content

Commit

Permalink
Merge pull request #18 from worldcoin/dzejkop/generating-proofs
Browse files Browse the repository at this point in the history
Parse external nullifier and validates its hash matches provided external nullifier hash
  • Loading branch information
Dzejkop authored Oct 2, 2024
2 parents c6ac267 + c74472a commit 25eecbc
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 82 deletions.
36 changes: 35 additions & 1 deletion world-chain-builder/Cargo.lock

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

8 changes: 7 additions & 1 deletion world-chain-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "world-chain-builder"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
default-run = "world-chain-builder"

[dependencies]
# reth
Expand Down Expand Up @@ -61,6 +61,7 @@ op-alloy-network = "0.2"
alloy-consensus = "0.3"
alloy-network = "0.3"
alloy-primitives = "0.8"
# alloy-provider = "0.3"
alloy-rpc-types-eth = "0.3"
alloy-rpc-types = "0.3"
alloy-rlp = "0.3"
Expand Down Expand Up @@ -92,6 +93,7 @@ tikv-jemallocator = { version = "0.6.0", optional = true }

[dev-dependencies]
tempfile = "3"
test-case = "3"
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
reth-provider = { git = "https://github.com/ewoolsey/reth", rev = "b2848f", features = [
"test-utils",
Expand All @@ -107,3 +109,7 @@ jemalloc = ["tikv-jemallocator"]
[[bin]]
name = "world-chain-builder"
path = "bin/world-chain-builder.rs"

[[bin]]
name = "toolkit"
path = "bin/toolkit.rs"
28 changes: 28 additions & 0 deletions world-chain-builder/bin/toolkit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use clap::Parser;

#[derive(Debug, Clone, Parser)]
struct Args {
#[clap(subcommand)]
cmd: Cmd,
}

#[derive(Debug, Clone, Parser)]
enum Cmd {
Prove(ProveArgs),
}

#[derive(Debug, Clone, Parser)]
struct ProveArgs {
#[clap(short, long)]
tx_index: usize,
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
dotenvy::dotenv().ok();

let args = Args::parse();
println!("{:?}", args);

Ok(())
}
188 changes: 187 additions & 1 deletion world-chain-builder/src/pbh/semaphore.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
use std::str::FromStr;

use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
use chrono::{Datelike, NaiveDate};
use semaphore::packed_proof::PackedProof;
use semaphore::Field;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use super::tx::Prefix;

pub const TREE_DEPTH: usize = 30;

const LEN: usize = 256;

Expand Down Expand Up @@ -50,11 +58,157 @@ pub struct SemaphoreProof {
pub proof: Proof,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExternalNullifier {
pub month: DateMarker,
pub nonce: u16,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DateMarker {
pub year: i32,
pub month: u32,
}

impl ExternalNullifier {
pub fn new(month: DateMarker, nonce: u16) -> Self {
Self { month, nonce }
}
}

impl std::fmt::Display for ExternalNullifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}-{}", Prefix::V1, self.month, self.nonce)
}
}

#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ExternalNullifierParsingError {
#[error("invalid format - expected a string of format `vv-mmyyyy-xxx...` got {actual}")]
InvaldFormat { actual: String },

#[error("error parsing prefix - {0}")]
InvalidPrefix(strum::ParseError),

#[error("error parsing month - {0}")]
InvalidMonth(MonthMarkerParsingError),

#[error("error parsing nonce - {0}")]
InvalidNonce(std::num::ParseIntError),

#[error("leading zeroes in nonce `{0}`")]
LeadingZeroes(String),
}

impl FromStr for ExternalNullifier {
type Err = ExternalNullifierParsingError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('-').collect();
if parts.len() != 3 {
return Err(ExternalNullifierParsingError::InvaldFormat {
actual: s.to_string(),
});
}

// no need to check the exact value since there's only one variant
let Prefix::V1 = parts[0]
.parse()
.map_err(ExternalNullifierParsingError::InvalidPrefix)?;

let month = parts[1]
.parse()
.map_err(ExternalNullifierParsingError::InvalidMonth)?;

let nonce_str = parts[2];
let nonce_str_trimmed = nonce_str.trim_start_matches('0');

if nonce_str != "0" && nonce_str != nonce_str_trimmed {
return Err(ExternalNullifierParsingError::LeadingZeroes(
nonce_str.to_string(),
));
}

let nonce = nonce_str
.parse()
.map_err(ExternalNullifierParsingError::InvalidNonce)?;

Ok(ExternalNullifier { month, nonce })
}
}

impl DateMarker {
pub fn new(year: i32, month: u32) -> Self {
Self { year, month }
}
}

impl<T> From<T> for DateMarker
where
T: Datelike,
{
fn from(value: T) -> Self {
Self {
year: value.year(),
month: value.month(),
}
}
}

impl From<DateMarker> for NaiveDate {
fn from(value: DateMarker) -> Self {
NaiveDate::from_ymd_opt(value.year, value.month, 1).unwrap()
}
}

#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum MonthMarkerParsingError {
#[error("invalid length - expected 6 characters got {actual}")]
InvaldLength { actual: usize },
#[error("error parsing month - {0}")]
InvalidMonth(std::num::ParseIntError),
#[error("month out of range - expected 01-12 got {month}")]
MonthOutOfRange { month: u32 },
#[error("error parsing year - {0}")]
InvalidYear(std::num::ParseIntError),
}

impl FromStr for DateMarker {
type Err = MonthMarkerParsingError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 6 {
return Err(MonthMarkerParsingError::InvaldLength { actual: s.len() });
}

let month = &s[..2];
let year = &s[2..];

let month = month
.parse()
.map_err(MonthMarkerParsingError::InvalidMonth)?;
let year = year.parse().map_err(MonthMarkerParsingError::InvalidYear)?;

if month < 1 || month > 12 {
return Err(MonthMarkerParsingError::MonthOutOfRange { month });
}

Ok(DateMarker { year, month })
}
}

impl std::fmt::Display for DateMarker {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:02}{:04}", self.month, self.year)
}
}

#[cfg(test)]
mod test {
use ethers_core::types::U256;
use test_case::test_case;

use super::*;
use ethers_core::types::U256;

#[test]
fn encode_decode() {
Expand All @@ -79,4 +233,36 @@ mod test {
let decoded = SemaphoreProof::decode(&mut buf).unwrap();
assert_eq!(semaphore_proof, decoded);
}

#[test_case("v1-012025-11")]
#[test_case("v1-012025-19")]
fn parse_external_nulliifer_roundtrip(s: &str) {
let e: ExternalNullifier = s.parse().unwrap();

assert_eq!(e.to_string(), s);
}

#[test_case("v2-012025-11")]
#[test_case("v1-012025-011")]
fn parse_external_nulliifer_invalid(s: &str) {
s.parse::<ExternalNullifier>().unwrap_err();
}

#[test_case("012024")]
#[test_case("022024")]
#[test_case("022025")]
fn parse_month_marker_roundtrip(s: &str) {
let m: DateMarker = s.parse().unwrap();

assert_eq!(m.to_string(), s);
}

#[test_case("132024" ; "invalid month")]
#[test_case("12024" ; "too short")]
#[test_case("003024" ; "zero month")]
#[test_case("" ; "empty")]
#[test_case("23012024" ; "too long")]
fn parse_month_marker_invalid(s: &str) {
s.parse::<DateMarker>().unwrap_err();
}
}
12 changes: 8 additions & 4 deletions world-chain-builder/src/pool/error.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use reth_provider::ProviderError;

use reth_db::{DatabaseError, DatabaseWriteOperation};
use reth_provider::ProviderError;
use reth_transaction_pool::error::{InvalidPoolTransactionError, PoolTransactionError};
use reth_transaction_pool::{PoolTransaction, TransactionValidationOutcome};
use semaphore::Field;

use crate::pbh::semaphore::ExternalNullifierParsingError;

#[derive(Debug, thiserror::Error)]
pub enum WorldChainTransactionPoolInvalid {
#[error("nullifier has already been seen")]
NullifierAlreadyExists,
#[error("invalid external nullifier")]
InvalidExternalNullifier,
#[error("invalid external nullifier - {0}")]
InvalidExternalNullifier(ExternalNullifierParsingError),
#[error("invalid external nullifier hash - expected {expected:?} got {actual:?}")]
InvalidExternalNullifierHash { expected: Field, actual: Field },
#[error("invalid external nullifier prefix")]
InvalidExternalNullifierPrefix,
#[error("invalid external nullifier period")]
Expand Down
Loading

0 comments on commit 25eecbc

Please sign in to comment.