From 2ef0ba373a7468880bd1044949f5dd5e72cf82fa Mon Sep 17 00:00:00 2001 From: pdtfh <149602456+pdtfh@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:00:49 +0100 Subject: [PATCH] pt 2 --- Cargo.lock | 6 ++- walletkit-core/Cargo.toml | 2 + walletkit-core/src/field.rs | 28 ----------- walletkit-core/src/identity.rs | 11 ++--- walletkit-core/src/lib.rs | 4 +- walletkit-core/src/proof.rs | 4 +- walletkit-core/src/u256.rs | 85 ++++++++++++++++++++++++++++++++++ walletkit-core/src/utils.rs | 84 --------------------------------- 8 files changed, 97 insertions(+), 127 deletions(-) delete mode 100644 walletkit-core/src/field.rs create mode 100644 walletkit-core/src/u256.rs delete mode 100644 walletkit-core/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index ba239108..a20bb4a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3903,9 +3903,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -5087,8 +5087,10 @@ dependencies = [ name = "walletkit-core" version = "0.0.6" dependencies = [ + "hex", "ruint", "semaphore", + "serde_json", "thiserror 2.0.3", "uniffi", ] diff --git a/walletkit-core/Cargo.toml b/walletkit-core/Cargo.toml index 1fe1d3b2..ac40014d 100644 --- a/walletkit-core/Cargo.toml +++ b/walletkit-core/Cargo.toml @@ -15,7 +15,9 @@ crate-type = ["lib", "staticlib", "cdylib"] name = "walletkit_core" [dependencies] +hex = "0.4.3" ruint = { version = "1.12.3", default-features = false, features = ["alloc"] } semaphore = { git = "https://github.com/worldcoin/semaphore-rs", rev = "accb14b", features = ["depth_30"] } +serde_json = "1.0.133" thiserror = "2.0.3" uniffi = { workspace = true, features = ["build"] } diff --git a/walletkit-core/src/field.rs b/walletkit-core/src/field.rs deleted file mode 100644 index 41e4b90a..00000000 --- a/walletkit-core/src/field.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[derive(uniffi::Object, Debug, PartialEq, Eq, Clone)] -/// A field element in the Semaphore protocol. -pub struct Field(pub semaphore::Field); - -impl Field { - #[must_use] - pub fn to_hex_string(&self) -> String { - format!("{:#04x}", self.0) - } -} - -impl From<&Field> for semaphore::Field { - fn from(val: &Field) -> Self { - val.0 - } -} - -impl From for semaphore::Field { - fn from(val: Field) -> Self { - val.0 - } -} - -impl std::fmt::Display for Field { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.to_hex_string()) - } -} diff --git a/walletkit-core/src/identity.rs b/walletkit-core/src/identity.rs index 039123bb..429418c3 100644 --- a/walletkit-core/src/identity.rs +++ b/walletkit-core/src/identity.rs @@ -1,7 +1,6 @@ -use ruint::aliases::U256; use semaphore::protocol::generate_nullifier_hash; -use crate::proof::Context; +use crate::{proof::Context, u256::U256Wrapper}; #[derive(Clone, PartialEq, Eq, Debug, uniffi::Object)] pub struct Identity(pub semaphore::identity::Identity); @@ -25,18 +24,14 @@ impl Identity { /// More information can be found [here](https://docs.world.org/world-id/concepts#vocabulary) /// /// [Protocol Reference](https://docs.semaphore.pse.dev/V2/technical-reference/circuits#nullifier-hash). - /// - /// # Result - /// Outputs a Field element as a `U256` value #[must_use] - pub fn generate_nullifier_hash(&self, context: &Context) -> U256 { - generate_nullifier_hash(&self.0, context.external_nullifier) + pub fn generate_nullifier_hash(&self, context: &Context) -> U256Wrapper { + generate_nullifier_hash(&self.0, context.external_nullifier).into() } } #[cfg(test)] mod tests { - use crate::utils::HexNumber; use super::*; #[test] diff --git a/walletkit-core/src/lib.rs b/walletkit-core/src/lib.rs index 9694d55d..80089f1f 100644 --- a/walletkit-core/src/lib.rs +++ b/walletkit-core/src/lib.rs @@ -1,10 +1,8 @@ #![deny(clippy::all, clippy::pedantic, clippy::nursery)] pub mod error; -pub mod field; pub mod identity; pub mod proof; -mod utils; -pub use utils::*; +pub mod u256; uniffi::setup_scaffolding!("walletkit_core"); diff --git a/walletkit-core/src/proof.rs b/walletkit-core/src/proof.rs index da002a6b..1185f6b8 100644 --- a/walletkit-core/src/proof.rs +++ b/walletkit-core/src/proof.rs @@ -7,9 +7,9 @@ pub struct Context { impl Context { #[must_use] - pub fn new(app_id: &[u8], action: &[u8]) -> Self { + pub fn new(app_id: &[u8], _action: &[u8]) -> Self { let external_nullifier = hash_to_field(app_id); // TODO: handle action properly Self { external_nullifier } } -} \ No newline at end of file +} diff --git a/walletkit-core/src/u256.rs b/walletkit-core/src/u256.rs new file mode 100644 index 00000000..0198544a --- /dev/null +++ b/walletkit-core/src/u256.rs @@ -0,0 +1,85 @@ +use ruint::aliases::U256; + +/// A wrapper around `U256` to represent a field element in the protocol. Wrapper enables FFI interoperability. +/// +/// Most inputs and outputs from the zero-knowledge proofs are `U256` values. +/// While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust, +/// it is not a user-friendly type for interactions or communications in other languages / systems. +/// +/// Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD +/// be represented as padded hex strings from Big Endian bytes. +#[allow(clippy::module_name_repetitions)] +#[derive(uniffi::Object, Debug, PartialEq, Eq, Clone)] +pub struct U256Wrapper(pub U256); + +#[uniffi::export] +impl U256Wrapper { + #[must_use] + pub fn to_hex_string(&self) -> String { + format!("0x{}", hex::encode(self.0.to_be_bytes::<32>())) + } +} + +impl U256Wrapper { + #[must_use] + pub const fn as_inner(&self) -> U256 { + self.0 + } +} + +impl From for U256 { + fn from(val: U256Wrapper) -> Self { + val.0 + } +} + +impl From for U256Wrapper { + fn from(val: U256) -> Self { + Self(val) + } +} + +impl std::fmt::Display for U256Wrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_hex_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ruint::uint; + + #[test] + fn test_to_hex_string_for_u256() { + assert_eq!( + U256Wrapper(U256::from(1)).to_hex_string(), + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_eq!( + U256Wrapper(U256::from(42)).to_hex_string(), + "0x000000000000000000000000000000000000000000000000000000000000002a" + ); + + assert_eq!( + U256Wrapper(uint!(999999_U256)).to_hex_string(), + "0x00000000000000000000000000000000000000000000000000000000000f423f" + ); + + assert_eq!( + U256Wrapper(uint!( + 80084422859880547211683076133703299733277748156566366325829078699459944778998_U256 + )) + .to_hex_string(), + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" + ); + + assert_eq!( + U256Wrapper(uint!( + 0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0_U256 + )) + .to_hex_string(), + "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0" + ); + } +} diff --git a/walletkit-core/src/utils.rs b/walletkit-core/src/utils.rs deleted file mode 100644 index bac72b5b..00000000 --- a/walletkit-core/src/utils.rs +++ /dev/null @@ -1,84 +0,0 @@ -use ruint::aliases::U256; - -use crate::error::Error; - -/// A trait for types that can be represented as a hex string. -/// -/// This trait is implemented for `U256` and can be used to convert between hex strings and numbers. -/// Most inputs and outputs from the zero-knowledge proofs are `U256` values. -/// While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust, -/// it is not a user-friendly type for interactions or communications in other languages / systems. -/// -/// Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD be represented as hex strings. -pub trait HexNumber { - /// Convert the value to a hex string. - fn to_hex_string(&self) -> String; - /// Convert a hex string to a `U256`. - /// - /// # Errors - /// - /// Returns an error if the hex string is invalid as a `U256`. - fn try_from_hex_string(hex: &str) -> Result - where - Self: std::marker::Sized; -} - -impl HexNumber for U256 { - fn to_hex_string(&self) -> String { - format!("{self:#066x}") - } - - fn try_from_hex_string(hex: &str) -> Result { - Self::from_str_radix(hex, 16).map_err(Error::U256ParsingError) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ruint::uint; - - #[test] - fn test_to_hex_string_for_u256() { - assert_eq!( - U256::from(1).to_hex_string(), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - U256::from(42).to_hex_string(), - "0x000000000000000000000000000000000000000000000000000000000000002a" - ); - - assert_eq!( - uint!(999999_U256).to_hex_string(), - "0x00000000000000000000000000000000000000000000000000000000000f423f" - ); - - assert_eq!( - uint!(80084422859880547211683076133703299733277748156566366325829078699459944778998_U256).to_hex_string(), - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" - ); - - assert_eq!( - uint!(0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0_U256).to_hex_string(), - "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0" - ); - } - - #[test] - fn test_try_from_hex_string() { - // Test valid hex strings - assert_eq!(U256::try_from_hex_string("0x2a").unwrap(), U256::from(42)); - assert_eq!(U256::try_from_hex_string("2a").unwrap(), U256::from(42)); - - // Test larger numbers - assert_eq!(U256::try_from_hex_string("0xf423f").unwrap(), U256::from(999999)); - - // Test zero - assert_eq!(U256::try_from_hex_string("0x0").unwrap(), U256::from(0)); - - // Test invalid hex strings - assert!(U256::try_from_hex_string("0xg").is_err()); - assert!(U256::try_from_hex_string("not hex").is_err()); - } -}