From d623812c2455508697b306ef985e43a1bae53af7 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 25 Aug 2025 14:51:46 -0300 Subject: [PATCH 1/9] remove shared secrete derivation from r0 --- nssa/Cargo.toml | 1 + nssa/core/Cargo.toml | 1 - nssa/core/src/lib.rs | 168 +++++------------ nssa/program_methods/guest/Cargo.lock | 175 ------------------ nssa/program_methods/guest/Cargo.toml | 4 - .../src/bin/privacy_preserving_circuit.rs | 21 ++- .../privacy_preserving_transaction/circuit.rs | 54 ++++-- .../encoding.rs | 37 +++- .../privacy_preserving_transaction/message.rs | 128 ++++++++++--- .../src/privacy_preserving_transaction/mod.rs | 2 +- .../transaction.rs | 9 +- nssa/src/state.rs | 63 ++++--- 12 files changed, 271 insertions(+), 392 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index c050c65..aad78af 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -11,6 +11,7 @@ program-methods = { path = "program_methods" } serde = "1.0.219" sha2 = "0.10.9" secp256k1 = "0.31.1" +k256 = "0.13.3" rand = "0.8" borsh = "1.5.7" bytemuck = "1.13" diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index de7e4d6..a68a3f2 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -9,7 +9,6 @@ serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } chacha20 = { version = "0.9", default-features = false } -k256 = "0.13.3" [features] default = [] diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index e523f6f..2856d18 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -2,6 +2,7 @@ use chacha20::{ ChaCha20, cipher::{KeyIvInit, StreamCipher}, }; + use risc0_zkvm::{ serde::to_vec, sha::{Impl, Sha256}, @@ -25,14 +26,6 @@ use std::io::{Cursor, Read}; pub mod account; pub mod program; -use k256::{ - AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, PublicKey, Scalar, - elliptic_curve::{ - PrimeField, - sec1::{FromEncodedPoint, ToEncodedPoint}, - }, -}; - #[cfg(feature = "host")] pub mod error; @@ -66,56 +59,23 @@ pub fn compute_root_associated_to_path( result } -pub type EphemeralPublicKey = Secp256k1Point; -pub type IncomingViewingPublicKey = Secp256k1Point; - -pub type EphemeralSecretKey = [u8; 32]; - -impl From<&EphemeralSecretKey> for EphemeralPublicKey { - fn from(value: &EphemeralSecretKey) -> Self { - Secp256k1Point::from_scalar(*value) - } -} - -#[derive(Serialize, Deserialize, Clone)] -#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] -pub struct Secp256k1Point(pub Vec); -impl Secp256k1Point { - pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point { - let x_bytes: FieldBytes = value.into(); - let x = Scalar::from_repr(x_bytes).unwrap(); - - let p = ProjectivePoint::GENERATOR * x; - let q = AffinePoint::from(p); - let enc = q.to_encoded_point(true); - - Self(enc.as_bytes().to_vec()) - } -} +pub type SharedSecretKey = [u8; 32]; #[derive(Serialize, Deserialize)] #[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))] -pub struct EncryptedAccountData { - ciphertext: Vec, - epk: EphemeralPublicKey, - view_tag: u8, -} +pub struct Ciphertext(Vec); -impl EncryptedAccountData { +impl Ciphertext { #[cfg(feature = "host")] - pub fn decrypt(self, isk: &[u8; 32], output_index: u32) -> Option { - let ss_bytes = Self::ecdh(isk, &self.epk.0.clone().try_into().unwrap()); - let ipk = IncomingViewingPublicKey::from_scalar(*isk); - + pub fn decrypt(self, shared_secret: &[u8; 32], output_index: u32) -> Option { let key = Self::kdf( - ss_bytes, - &self.epk, - &ipk, + &shared_secret, + // &ipk, // &commitment.to_byte_array(), output_index, ); let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); - let mut buffer = self.ciphertext; + let mut buffer = self.0; cipher.apply_keystream(&mut buffer); let mut cursor = Cursor::new(buffer.as_slice()); @@ -124,71 +84,45 @@ impl EncryptedAccountData { pub fn new( account: &Account, - // commitment: &Commitment, - esk: &EphemeralSecretKey, - npk: &NullifierPublicKey, - ipk: &IncomingViewingPublicKey, + shared_secret: &[u8; 32], + // npk: &NullifierPublicKey, + // ipk: &IncomingViewingPublicKey, output_index: u32, ) -> Self { let mut buffer = account.to_bytes().to_vec(); - let ss_bytes = Self::ecdh(esk, &ipk.0.clone().try_into().unwrap()); - let epk = EphemeralPublicKey::from(esk); - let key = Self::kdf( - ss_bytes, - &epk, - ipk, + shared_secret, + // ipk, // &commitment.to_byte_array(), output_index, ); let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); cipher.apply_keystream(&mut buffer); - let view_tag = Self::view_tag(&npk, &ipk); - Self { - ciphertext: buffer, - epk, - view_tag, - } + // let view_tag = Self::view_tag(&npk, &ipk); + Self(buffer) } pub fn kdf( - ss_bytes: [u8; 32], - epk: &EphemeralPublicKey, - ipk: &IncomingViewingPublicKey, + ss_bytes: &[u8; 32], + // epk: &EphemeralPublicKey, + // ipk: &IncomingViewingPublicKey, // commitment: &[u8; 32], output_index: u32, ) -> [u8; 32] { let mut bytes = Vec::new(); bytes.extend_from_slice(b"NSSA/v0.1/KDF-SHA256"); - bytes.extend_from_slice(&ss_bytes); - bytes.extend_from_slice(&epk.0[..]); - bytes.extend_from_slice(&ipk.0[..]); + bytes.extend_from_slice(ss_bytes); + // bytes.extend_from_slice(&epk.0[..]); + // bytes.extend_from_slice(&ipk.0[..]); // bytes.extend_from_slice(&commitment[..]); bytes.extend_from_slice(&output_index.to_le_bytes()); Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap() } - pub fn ecdh(scalar: &[u8; 32], point: &[u8; 33]) -> [u8; 32] { - let scalar = Scalar::from_repr((*scalar).into()).unwrap(); - - let encoded = EncodedPoint::from_bytes(point).unwrap(); - let pubkey_affine = AffinePoint::from_encoded_point(&encoded).unwrap(); - - let shared = ProjectivePoint::from(pubkey_affine) * scalar; - let shared_affine = shared.to_affine(); - - let encoded = shared_affine.to_encoded_point(false); - let x_bytes_slice = encoded.x().unwrap(); - let mut x_bytes = [0u8; 32]; - x_bytes.copy_from_slice(x_bytes_slice); - - x_bytes - } - #[cfg(feature = "host")] pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut u32_bytes = [0; 4]; @@ -198,33 +132,24 @@ impl EncryptedAccountData { let mut ciphertext = vec![0; ciphertext_lenght as usize]; cursor.read_exact(&mut ciphertext)?; - let mut epk_bytes = vec![0; 33]; - cursor.read_exact(&mut epk_bytes)?; + // let mut epk_bytes = vec![0; 33]; + // cursor.read_exact(&mut epk_bytes)?; + // + // let mut tag_bytes = [0; 1]; + // cursor.read_exact(&mut tag_bytes)?; - let mut tag_bytes = [0; 1]; - cursor.read_exact(&mut tag_bytes)?; - - Ok(Self { - ciphertext, - epk: Secp256k1Point(epk_bytes), - view_tag: tag_bytes[0], - }) - } - - fn view_tag(npk: &NullifierPublicKey, ipk: &&IncomingViewingPublicKey) -> u8 { - // TODO: implement - 0 + Ok(Self(ciphertext)) } } -impl EncryptedAccountData { +impl Ciphertext { pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); - let ciphertext_length: u32 = self.ciphertext.len() as u32; + let ciphertext_length: u32 = self.0.len() as u32; bytes.extend_from_slice(&ciphertext_length.to_le_bytes()); - bytes.extend_from_slice(&self.ciphertext); - bytes.extend_from_slice(&self.epk.0); - bytes.push(self.view_tag); + bytes.extend_from_slice(&self.0); + // bytes.extend_from_slice(&self.epk.0); + // bytes.push(self.view_tag); bytes } @@ -237,8 +162,9 @@ pub struct PrivacyPreservingCircuitInput { pub private_account_nonces: Vec, pub private_account_keys: Vec<( NullifierPublicKey, - IncomingViewingPublicKey, - EphemeralSecretKey, + SharedSecretKey, + // IncomingViewingPublicKey, + // EphemeralSecretKey, )>, pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>, pub program_id: ProgramId, @@ -249,7 +175,7 @@ pub struct PrivacyPreservingCircuitInput { pub struct PrivacyPreservingCircuitOutput { pub public_pre_states: Vec, pub public_post_states: Vec, - pub encrypted_private_post_states: Vec, + pub ciphertexts: Vec, pub new_commitments: Vec, pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, } @@ -268,7 +194,7 @@ mod tests { use risc0_zkvm::serde::from_slice; use crate::{ - EncryptedAccountData, EphemeralPublicKey, PrivacyPreservingCircuitOutput, Secp256k1Point, + Ciphertext, PrivacyPreservingCircuitOutput, account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey}, }; @@ -301,11 +227,10 @@ mod tests { data: b"post state data".to_vec(), nonce: 18446744073709551615, }], - encrypted_private_post_states: vec![EncryptedAccountData { - ciphertext: vec![255, 255, 1, 1, 2, 2], - epk: EphemeralPublicKey::from_scalar([123; 32]), - view_tag: 1, - }], + ciphertexts: vec![ + Ciphertext(vec![255, 255, 1, 1, 2, 2]), // epk: EphemeralPublicKey::from_scalar([123; 32]), + // view_tag: 1, + ], new_commitments: vec![Commitment::new( &NullifierPublicKey::from(&[1; 32]), &Account::default(), @@ -324,15 +249,12 @@ mod tests { } #[test] - fn test_encrypted_account_data_to_bytes_roundtrip() { - let data = EncryptedAccountData { - ciphertext: vec![255, 255, 1, 1, 2, 2], - epk: EphemeralPublicKey::from_scalar([123; 32]), - view_tag: 95, - }; + fn test_ciphertext_to_bytes_roundtrip() { + let data = Ciphertext(vec![255, 255, 1, 1, 2, 2]); + let bytes = data.to_bytes(); let mut cursor = Cursor::new(bytes.as_slice()); - let data_from_cursor = EncryptedAccountData::from_cursor(&mut cursor).unwrap(); + let data_from_cursor = Ciphertext::from_cursor(&mut cursor).unwrap(); assert_eq!(data, data_from_cursor); } } diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index 380583f..b37fcb9 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -297,24 +297,12 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - [[package]] name = "bincode" version = "1.3.3" @@ -568,18 +556,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -625,16 +601,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - [[package]] name = "derivative" version = "2.2.0" @@ -764,20 +730,6 @@ dependencies = [ "proc-macro-error", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - [[package]] name = "educe" version = "0.6.0" @@ -802,25 +754,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "embedded-io" version = "0.4.0" @@ -884,16 +817,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1006,7 +929,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1042,17 +964,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -1104,15 +1015,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "http" version = "1.3.1" @@ -1403,20 +1305,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - [[package]] name = "keccak" version = "0.1.5" @@ -1588,7 +1476,6 @@ name = "nssa-core" version = "0.1.0" dependencies = [ "chacha20", - "k256", "risc0-zkvm", "serde", ] @@ -1675,16 +1562,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "postcard" version = "1.1.3" @@ -1903,9 +1780,6 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] [[package]] name = "rand_core" @@ -1998,16 +1872,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "ring" version = "0.17.14" @@ -2329,20 +2193,6 @@ dependencies = [ "yaml-rust2", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - [[package]] name = "semver" version = "1.0.26" @@ -2422,16 +2272,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - [[package]] name = "slab" version = "0.4.10" @@ -2470,16 +2310,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stability" version = "0.2.1" @@ -3394,8 +3224,3 @@ dependencies = [ "quote", "syn 2.0.104", ] - -[[patch.unused]] -name = "k256" -version = "0.13.3" -source = "git+https://github.com/risc0/RustCrypto-elliptic-curves?tag=k256%2Fv0.13.3-risczero.1#ff5d67b095cfcc2569b7789f2079ed87ef2c7756" diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index 79a0c1a..4b377c8 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -8,7 +8,3 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } nssa-core = { path = "../../core" } - -[patch.crates-io] -k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.1" } - diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index 51108af..44071f4 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -4,8 +4,7 @@ use nssa_core::{ account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey}, compute_root_associated_to_path, program::{validate_execution, ProgramOutput, DEFAULT_PROGRAM_ID}, - CommitmentSetDigest, EncryptedAccountData, EphemeralPublicKey, EphemeralSecretKey, - IncomingViewingPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, + Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, }; fn main() { @@ -42,7 +41,7 @@ fn main() { // and will be populated next. let mut public_pre_states: Vec = Vec::new(); let mut public_post_states: Vec = Vec::new(); - let mut encrypted_private_post_states: Vec = Vec::new(); + let mut ciphertexts: Vec = Vec::new(); let mut new_commitments: Vec = Vec::new(); let mut new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)> = Vec::new(); @@ -67,7 +66,8 @@ fn main() { } 1 | 2 => { let new_nonce = private_nonces_iter.next().expect("Missing private nonce"); - let (Npk, Ipk, esk) = private_keys_iter.next().expect("Missing private keys"); + // let (Npk, Ipk, esk) = private_keys_iter.next().expect("Missing keys"); + let (Npk, shared_secret) = private_keys_iter.next().expect("Missing keys"); if visibility_mask[i] == 1 { // Private account with authentication @@ -116,17 +116,18 @@ fn main() { let commitment_post = Commitment::new(Npk, &post_with_updated_values); // Encrypt and push post state - let encrypted_account = EncryptedAccountData::new( + let encrypted_account = Ciphertext::new( &post_with_updated_values, + shared_secret, // &commitment_post, - esk, - Npk, - Ipk, + // esk, + // Npk, + // Ipk, output_index, ); new_commitments.push(commitment_post); - encrypted_private_post_states.push(encrypted_account); + ciphertexts.push(encrypted_account); output_index += 1; } _ => panic!("Invalid visibility mask value"), @@ -148,7 +149,7 @@ fn main() { let output = PrivacyPreservingCircuitOutput { public_pre_states, public_post_states, - encrypted_private_post_states, + ciphertexts, new_commitments, new_nullifiers, }; diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 031790a..01eb144 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -1,6 +1,6 @@ use nssa_core::{ - CommitmentSetDigest, EphemeralSecretKey, IncomingViewingPublicKey, MembershipProof, - PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, + CommitmentSetDigest, MembershipProof, PrivacyPreservingCircuitInput, + PrivacyPreservingCircuitOutput, SharedSecretKey, account::{Account, AccountWithMetadata, Nonce, NullifierPublicKey, NullifierSecretKey}, program::{InstructionData, ProgramId, ProgramOutput}, }; @@ -28,8 +28,9 @@ pub fn execute_and_prove( private_account_nonces: &[u128], private_account_keys: &[( NullifierPublicKey, - IncomingViewingPublicKey, - EphemeralSecretKey, + SharedSecretKey, + // IncomingViewingPublicKey, + // EphemeralSecretKey, )], private_account_auth: &[(NullifierSecretKey, MembershipProof)], program: &Program, @@ -90,7 +91,7 @@ fn execute_and_prove_program( #[cfg(test)] mod tests { use nssa_core::{ - EncryptedAccountData, + Ciphertext, account::{ Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, @@ -101,7 +102,10 @@ mod tests { use crate::{ Address, V01State, merkle_tree::MerkleTree, - privacy_preserving_transaction::circuit::{Proof, execute_and_prove}, + privacy_preserving_transaction::{ + circuit::{Proof, execute_and_prove}, + message::{EncryptedAccountData, EphemeralPublicKey}, + }, program::Program, state::{ CommitmentSet, @@ -147,12 +151,18 @@ mod tests { let expected_sender_pre = sender.clone(); let recipient_keys = test_private_account_keys_1(); + + let esk = [3; 32]; + let shared_secret = + EncryptedAccountData::compute_shared_secret(&esk, &recipient_keys.ivk()); + let epk = EphemeralPublicKey::from_scalar(esk); + let (output, proof) = execute_and_prove( &[sender, recipient], &Program::serialize_instruction(balance_to_move).unwrap(), &[0, 2], &[0xdeadbeef], - &[(recipient_keys.npk(), recipient_keys.ivk(), [3; 32])], + &[(recipient_keys.npk(), shared_secret)], &[], &Program::authenticated_transfer_program(), ) @@ -166,11 +176,11 @@ mod tests { assert_eq!(sender_post, expected_sender_post); assert_eq!(output.new_commitments.len(), 1); assert_eq!(output.new_nullifiers.len(), 0); - assert_eq!(output.encrypted_private_post_states.len(), 1); + assert_eq!(output.ciphertexts.len(), 1); - let recipient_post = output.encrypted_private_post_states[0] + let recipient_post = output.ciphertexts[0] .clone() - .decrypt(&recipient_keys.isk, 0) + .decrypt(&shared_secret, 0) .unwrap(); assert_eq!(recipient_post, expected_recipient_post); } @@ -222,14 +232,24 @@ mod tests { Commitment::new(&recipient_keys.npk(), &expected_private_account_2), ]; + let esk_1 = [3; 32]; + let shared_secret_1 = + EncryptedAccountData::compute_shared_secret(&esk_1, &sender_keys.ivk()); + let epk_1 = EphemeralPublicKey::from_scalar(esk_1); + + let esk_2 = [5; 32]; + let shared_secret_2 = + EncryptedAccountData::compute_shared_secret(&esk_2, &recipient_keys.ivk()); + let epk_2 = EphemeralPublicKey::from_scalar(esk_2); + let (output, proof) = execute_and_prove( &[sender_pre.clone(), recipient], &Program::serialize_instruction(balance_to_move).unwrap(), &[1, 2], &[0xdeadbeef1, 0xdeadbeef2], &[ - (sender_keys.npk(), sender_keys.ivk(), [3; 32]), - (recipient_keys.npk(), recipient_keys.ivk(), [5; 32]), + (sender_keys.npk(), shared_secret_1), + (recipient_keys.npk(), shared_secret_2), ], &[( sender_keys.nsk, @@ -244,17 +264,17 @@ mod tests { assert!(output.public_post_states.is_empty()); assert_eq!(output.new_commitments, expected_new_commitments); assert_eq!(output.new_nullifiers, expected_new_nullifiers); - assert_eq!(output.encrypted_private_post_states.len(), 2); + assert_eq!(output.ciphertexts.len(), 2); - let recipient_post_1 = output.encrypted_private_post_states[0] + let recipient_post_1 = output.ciphertexts[0] .clone() - .decrypt(&sender_keys.isk, 0) + .decrypt(&shared_secret_1, 0) .unwrap(); assert_eq!(recipient_post_1, expected_private_account_1); - let recipient_post_2 = output.encrypted_private_post_states[1] + let recipient_post_2 = output.ciphertexts[1] .clone() - .decrypt(&recipient_keys.isk, 1) + .decrypt(&shared_secret_2, 1) .unwrap(); assert_eq!(recipient_post_2, expected_private_account_2); } diff --git a/nssa/src/privacy_preserving_transaction/encoding.rs b/nssa/src/privacy_preserving_transaction/encoding.rs index 0ab2db8..fd818e2 100644 --- a/nssa/src/privacy_preserving_transaction/encoding.rs +++ b/nssa/src/privacy_preserving_transaction/encoding.rs @@ -3,17 +3,50 @@ use std::io::{Cursor, Read}; use nssa_core::{ - EncryptedAccountData, + Ciphertext, account::{Account, Commitment, Nullifier}, }; -use crate::{Address, error::NssaError}; +use crate::{ + Address, + error::NssaError, + privacy_preserving_transaction::message::{ + EncryptedAccountData, EphemeralPublicKey, Secp256k1Point, + }, +}; use super::message::Message; const MESSAGE_ENCODING_PREFIX_LEN: usize = 22; const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"\x01/NSSA/v0.1/TxMessage/"; +impl EncryptedAccountData { + pub(crate) fn to_bytes(&self) -> Vec { + let mut bytes = self.ciphertext.to_bytes(); + bytes.extend_from_slice(&self.epk.0); + bytes.push(self.view_tag); + bytes + } + + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let ciphertext = Ciphertext::from_cursor(cursor)?; + + let mut epk_bytes = vec![0; 33]; + cursor.read_exact(&mut epk_bytes)?; + let epk = Secp256k1Point(epk_bytes); + + let mut tag_bytes = [0; 1]; + cursor.read_exact(&mut tag_bytes)?; + let view_tag = tag_bytes[0]; + + Ok(Self { + ciphertext, + epk, + view_tag, + }) + } +} + impl Message { pub(crate) fn to_bytes(&self) -> Vec { let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index b4de4a7..8d31bc9 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -1,9 +1,80 @@ +use std::io::Cursor; + +use k256::{ + AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, PublicKey, Scalar, + elliptic_curve::{ + PrimeField, + sec1::{FromEncodedPoint, ToEncodedPoint}, + }, +}; use nssa_core::{ - CommitmentSetDigest, EncryptedAccountData, + Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitOutput, SharedSecretKey, account::{Account, Commitment, Nonce, Nullifier}, }; +use serde::{Deserialize, Serialize}; -use crate::Address; +use crate::{Address, error::NssaError}; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Secp256k1Point(pub(crate) Vec); +impl Secp256k1Point { + pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point { + let x_bytes: FieldBytes = value.into(); + let x = Scalar::from_repr(x_bytes).unwrap(); + + let p = ProjectivePoint::GENERATOR * x; + let q = AffinePoint::from(p); + let enc = q.to_encoded_point(true); + + Self(enc.as_bytes().to_vec()) + } +} + +pub type EphemeralSecretKey = [u8; 32]; +pub type EphemeralPublicKey = Secp256k1Point; +pub type IncomingViewingPublicKey = Secp256k1Point; +impl From<&EphemeralSecretKey> for EphemeralPublicKey { + fn from(value: &EphemeralSecretKey) -> Self { + Secp256k1Point::from_scalar(*value) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EncryptedAccountData { + pub(crate) ciphertext: Ciphertext, + pub(crate) epk: EphemeralPublicKey, + pub(crate) view_tag: u8, +} + +impl EncryptedAccountData { + pub fn decrypt( + self, + isk: &[u8; 32], + epk: &EphemeralPublicKey, + output_index: u32, + ) -> Option { + let shared_secret = Self::compute_shared_secret(isk, &epk); + self.ciphertext.decrypt(&shared_secret, output_index) + } + + pub fn compute_shared_secret(scalar: &[u8; 32], point: &Secp256k1Point) -> SharedSecretKey { + let scalar = Scalar::from_repr((*scalar).into()).unwrap(); + let point: [u8; 33] = point.0.clone().try_into().unwrap(); + + let encoded = EncodedPoint::from_bytes(point).unwrap(); + let pubkey_affine = AffinePoint::from_encoded_point(&encoded).unwrap(); + + let shared = ProjectivePoint::from(pubkey_affine) * scalar; + let shared_affine = shared.to_affine(); + + let encoded = shared_affine.to_encoded_point(false); + let x_bytes_slice = encoded.x().unwrap(); + let mut x_bytes = [0u8; 32]; + x_bytes.copy_from_slice(x_bytes_slice); + + x_bytes + } +} #[derive(Debug, Clone, PartialEq, Eq)] pub struct Message { @@ -16,22 +87,36 @@ pub struct Message { } impl Message { - pub fn new( + pub fn try_from_circuit_output( public_addresses: Vec
, nonces: Vec, - public_post_states: Vec, - encrypted_private_post_states: Vec, - new_commitments: Vec, - new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, - ) -> Self { - Self { + ephemeral_public_keys: Vec, + output: PrivacyPreservingCircuitOutput, + ) -> Result { + if ephemeral_public_keys.len() != output.ciphertexts.len() { + return Err(NssaError::InvalidInput( + "Ephemeral public keys and ciphertexts length mismatch".into(), + )); + } + + let encrypted_private_post_states = output + .ciphertexts + .into_iter() + .zip(ephemeral_public_keys) + .map(|(ciphertext, epk)| EncryptedAccountData { + ciphertext, + epk, + view_tag: 0, // TODO: implement + }) + .collect(); + Ok(Self { public_addresses, nonces, - public_post_states, + public_post_states: output.public_post_states, encrypted_private_post_states, - new_commitments, - new_nullifiers, - } + new_commitments: output.new_commitments, + new_nullifiers: output.new_nullifiers, + }) } } @@ -78,23 +163,6 @@ pub mod tests { } } - #[test] - fn test_constructor() { - let message = message_for_tests(); - let expected_message = message.clone(); - - let message = Message::new( - message.public_addresses, - message.nonces, - message.public_post_states, - message.encrypted_private_post_states, - message.new_commitments, - message.new_nullifiers, - ); - - assert_eq!(message, expected_message); - } - #[test] fn test_message_serialization_roundtrip() { let message = message_for_tests(); diff --git a/nssa/src/privacy_preserving_transaction/mod.rs b/nssa/src/privacy_preserving_transaction/mod.rs index 1d36118..d773395 100644 --- a/nssa/src/privacy_preserving_transaction/mod.rs +++ b/nssa/src/privacy_preserving_transaction/mod.rs @@ -5,6 +5,6 @@ mod witness_set; pub mod circuit; -pub use message::Message; +pub use message::{Message, IncomingViewingPublicKey, EphemeralPublicKey, EphemeralSecretKey, EncryptedAccountData}; pub use transaction::PrivacyPreservingTransaction; pub use witness_set::WitnessSet; diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index 49f3d8d..adb0c66 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -1,10 +1,11 @@ use std::collections::{HashMap, HashSet}; use nssa_core::account::{Account, AccountWithMetadata, Commitment, Nullifier}; -use nssa_core::{CommitmentSetDigest, EncryptedAccountData, PrivacyPreservingCircuitOutput}; +use nssa_core::{Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitOutput}; use crate::error::NssaError; use crate::privacy_preserving_transaction::circuit::Proof; +use crate::privacy_preserving_transaction::message::EncryptedAccountData; use crate::{Address, V01State}; use super::message::Message; @@ -145,7 +146,11 @@ fn check_privacy_preserving_circuit_proof_is_valid( let output = PrivacyPreservingCircuitOutput { public_pre_states: public_pre_states.to_vec(), public_post_states: public_post_states.to_vec(), - encrypted_private_post_states: encrypted_private_post_states.to_vec(), + ciphertexts: encrypted_private_post_states + .iter() + .cloned() + .map(|value| value.ciphertext) + .collect(), new_commitments: new_commitments.to_vec(), new_nullifiers: new_nullifiers.to_vec(), }; diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 3ac1e15..35f9719 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -222,14 +222,15 @@ pub mod tests { Address, PublicKey, PublicTransaction, V01State, error::NssaError, privacy_preserving_transaction::{ - Message, PrivacyPreservingTransaction, WitnessSet, circuit, + EncryptedAccountData, EphemeralPublicKey, IncomingViewingPublicKey, Message, + PrivacyPreservingTransaction, WitnessSet, circuit, }, program::Program, public_transaction, signature::PrivateKey, }; use nssa_core::{ - IncomingViewingPublicKey, + Ciphertext, account::{ Account, AccountWithMetadata, Commitment, Nonce, Nullifier, NullifierPublicKey, NullifierSecretKey, @@ -780,11 +781,11 @@ pub mod tests { balance_to_move: u128, state: &V01State, ) -> PrivacyPreservingTransaction { - let esk = [3; 32]; let sender = AccountWithMetadata { account: state.get_account_by_address(&sender_keys.address()), is_authorized: true, }; + let sender_nonce = sender.account.nonce; let recipient = AccountWithMetadata { @@ -792,25 +793,28 @@ pub mod tests { is_authorized: false, }; + let esk = [3; 32]; + let shared_secret = + EncryptedAccountData::compute_shared_secret(&esk, &recipient_keys.ivk()); + let epk = EphemeralPublicKey::from_scalar(esk); + let (output, proof) = circuit::execute_and_prove( &[sender, recipient], &Program::serialize_instruction(balance_to_move).unwrap(), &[0, 2], &[0xdeadbeef], - &[(recipient_keys.npk(), recipient_keys.ivk(), esk)], + &[(recipient_keys.npk(), shared_secret)], &[], &Program::authenticated_transfer_program(), ) .unwrap(); - let message = Message::new( + let message = Message::try_from_circuit_output( vec![sender_keys.address()], vec![sender_nonce], - output.public_post_states, - output.encrypted_private_post_states, - output.new_commitments.clone(), - output.new_nullifiers, - ); + vec![epk], + output, + ).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&sender_keys.signing_key]); PrivacyPreservingTransaction::new(message, witness_set) @@ -835,14 +839,24 @@ pub mod tests { is_authorized: false, }; + let esk_1 = [3; 32]; + let shared_secret_1 = + EncryptedAccountData::compute_shared_secret(&esk_1, &sender_keys.ivk()); + let epk_1 = EphemeralPublicKey::from_scalar(esk_1); + + let esk_2 = [3; 32]; + let shared_secret_2 = + EncryptedAccountData::compute_shared_secret(&esk_2, &recipient_keys.ivk()); + let epk_2 = EphemeralPublicKey::from_scalar(esk_2); + let (output, proof) = circuit::execute_and_prove( &[sender_pre, recipient_pre], &Program::serialize_instruction(balance_to_move).unwrap(), &[1, 2], &new_nonces, &[ - (sender_keys.npk(), sender_keys.ivk(), [3; 32]), - (recipient_keys.npk(), recipient_keys.ivk(), [4; 32]), + (sender_keys.npk(), shared_secret_1), + (recipient_keys.npk(), shared_secret_2), ], &[( sender_keys.nsk, @@ -856,14 +870,7 @@ pub mod tests { ) .unwrap(); - let message = Message::new( - vec![], - vec![], - output.public_post_states, - output.encrypted_private_post_states, - output.new_commitments.clone(), - output.new_nullifiers, - ); + let message = Message::try_from_circuit_output(vec![], vec![], vec![epk_1, epk_2], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); @@ -889,12 +896,16 @@ pub mod tests { is_authorized: false, }; + let esk = [3; 32]; + let shared_secret = EncryptedAccountData::compute_shared_secret(&esk, &sender_keys.ivk()); + let epk = EphemeralPublicKey::from_scalar(esk); + let (output, proof) = circuit::execute_and_prove( &[sender_pre, recipient_pre], &Program::serialize_instruction(balance_to_move).unwrap(), &[1, 0], &[new_nonce], - &[(sender_keys.npk(), sender_keys.ivk(), [3; 32])], + &[(sender_keys.npk(), shared_secret)], &[( sender_keys.nsk, state @@ -907,14 +918,12 @@ pub mod tests { ) .unwrap(); - let message = Message::new( + let message = Message::try_from_circuit_output( vec![recipient_address.clone()], vec![], - output.public_post_states, - output.encrypted_private_post_states, - output.new_commitments.clone(), - output.new_nullifiers, - ); + vec![epk], + output, + ).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); From e5085479143564c40b057bfb808d25b2044686d3 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 26 Aug 2025 13:50:52 -0300 Subject: [PATCH 2/9] add npk to kdf --- nssa/core/src/lib.rs | 13 +++++++++++-- .../guest/src/bin/privacy_preserving_circuit.rs | 2 +- nssa/src/privacy_preserving_transaction/circuit.rs | 14 +++++++------- nssa/src/privacy_preserving_transaction/message.rs | 5 +++-- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index 2856d18..aec9ab9 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -67,9 +67,15 @@ pub struct Ciphertext(Vec); impl Ciphertext { #[cfg(feature = "host")] - pub fn decrypt(self, shared_secret: &[u8; 32], output_index: u32) -> Option { + pub fn decrypt( + self, + shared_secret: &[u8; 32], + npk: &NullifierPublicKey, + output_index: u32, + ) -> Option { let key = Self::kdf( &shared_secret, + npk, // &ipk, // &commitment.to_byte_array(), output_index, @@ -85,7 +91,7 @@ impl Ciphertext { pub fn new( account: &Account, shared_secret: &[u8; 32], - // npk: &NullifierPublicKey, + npk: &NullifierPublicKey, // ipk: &IncomingViewingPublicKey, output_index: u32, ) -> Self { @@ -93,6 +99,7 @@ impl Ciphertext { let key = Self::kdf( shared_secret, + npk, // ipk, // &commitment.to_byte_array(), output_index, @@ -106,6 +113,7 @@ impl Ciphertext { pub fn kdf( ss_bytes: &[u8; 32], + npk: &NullifierPublicKey, // epk: &EphemeralPublicKey, // ipk: &IncomingViewingPublicKey, // commitment: &[u8; 32], @@ -115,6 +123,7 @@ impl Ciphertext { bytes.extend_from_slice(b"NSSA/v0.1/KDF-SHA256"); bytes.extend_from_slice(ss_bytes); + bytes.extend_from_slice(&npk.to_byte_array()); // bytes.extend_from_slice(&epk.0[..]); // bytes.extend_from_slice(&ipk.0[..]); // bytes.extend_from_slice(&commitment[..]); diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index 44071f4..cda5053 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -121,7 +121,7 @@ fn main() { shared_secret, // &commitment_post, // esk, - // Npk, + Npk, // Ipk, output_index, ); diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 01eb144..38407ce 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -180,7 +180,7 @@ mod tests { let recipient_post = output.ciphertexts[0] .clone() - .decrypt(&shared_secret, 0) + .decrypt(&shared_secret, &recipient_keys.npk(), 0) .unwrap(); assert_eq!(recipient_post, expected_recipient_post); } @@ -266,16 +266,16 @@ mod tests { assert_eq!(output.new_nullifiers, expected_new_nullifiers); assert_eq!(output.ciphertexts.len(), 2); - let recipient_post_1 = output.ciphertexts[0] + let sender_post = output.ciphertexts[0] .clone() - .decrypt(&shared_secret_1, 0) + .decrypt(&shared_secret_1, &sender_keys.npk(), 0) .unwrap(); - assert_eq!(recipient_post_1, expected_private_account_1); + assert_eq!(sender_post, expected_private_account_1); - let recipient_post_2 = output.ciphertexts[1] + let recipient_post = output.ciphertexts[1] .clone() - .decrypt(&shared_secret_2, 1) + .decrypt(&shared_secret_2, &recipient_keys.npk(), 1) .unwrap(); - assert_eq!(recipient_post_2, expected_private_account_2); + assert_eq!(recipient_post, expected_private_account_2); } } diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index 8d31bc9..245297f 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -9,7 +9,7 @@ use k256::{ }; use nssa_core::{ Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitOutput, SharedSecretKey, - account::{Account, Commitment, Nonce, Nullifier}, + account::{Account, Commitment, Nonce, Nullifier, NullifierPublicKey}, }; use serde::{Deserialize, Serialize}; @@ -51,10 +51,11 @@ impl EncryptedAccountData { self, isk: &[u8; 32], epk: &EphemeralPublicKey, + npk: &NullifierPublicKey, output_index: u32, ) -> Option { let shared_secret = Self::compute_shared_secret(isk, &epk); - self.ciphertext.decrypt(&shared_secret, output_index) + self.ciphertext.decrypt(&shared_secret, npk, output_index) } pub fn compute_shared_secret(scalar: &[u8; 32], point: &Secp256k1Point) -> SharedSecretKey { From fd7421624970c2e09b7f264b490042f2b6718711 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 26 Aug 2025 14:14:08 -0300 Subject: [PATCH 3/9] add commit to kdf --- nssa/core/src/lib.rs | 48 ++++--------------- .../src/bin/privacy_preserving_circuit.rs | 4 +- .../privacy_preserving_transaction/circuit.rs | 21 ++++++-- .../privacy_preserving_transaction/message.rs | 3 +- 4 files changed, 29 insertions(+), 47 deletions(-) diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index aec9ab9..89dbeac 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -71,15 +71,10 @@ impl Ciphertext { self, shared_secret: &[u8; 32], npk: &NullifierPublicKey, + commitment: &Commitment, output_index: u32, ) -> Option { - let key = Self::kdf( - &shared_secret, - npk, - // &ipk, - // &commitment.to_byte_array(), - output_index, - ); + let key = Self::kdf(&shared_secret, npk, commitment, output_index); let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); let mut buffer = self.0; @@ -92,31 +87,22 @@ impl Ciphertext { account: &Account, shared_secret: &[u8; 32], npk: &NullifierPublicKey, - // ipk: &IncomingViewingPublicKey, + commitment: &Commitment, output_index: u32, ) -> Self { let mut buffer = account.to_bytes().to_vec(); - let key = Self::kdf( - shared_secret, - npk, - // ipk, - // &commitment.to_byte_array(), - output_index, - ); + let key = Self::kdf(shared_secret, npk, commitment, output_index); let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); cipher.apply_keystream(&mut buffer); - // let view_tag = Self::view_tag(&npk, &ipk); Self(buffer) } pub fn kdf( ss_bytes: &[u8; 32], npk: &NullifierPublicKey, - // epk: &EphemeralPublicKey, - // ipk: &IncomingViewingPublicKey, - // commitment: &[u8; 32], + commitment: &Commitment, output_index: u32, ) -> [u8; 32] { let mut bytes = Vec::new(); @@ -124,9 +110,7 @@ impl Ciphertext { bytes.extend_from_slice(b"NSSA/v0.1/KDF-SHA256"); bytes.extend_from_slice(ss_bytes); bytes.extend_from_slice(&npk.to_byte_array()); - // bytes.extend_from_slice(&epk.0[..]); - // bytes.extend_from_slice(&ipk.0[..]); - // bytes.extend_from_slice(&commitment[..]); + bytes.extend_from_slice(&commitment.to_byte_array()); bytes.extend_from_slice(&output_index.to_le_bytes()); Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap() @@ -141,12 +125,6 @@ impl Ciphertext { let mut ciphertext = vec![0; ciphertext_lenght as usize]; cursor.read_exact(&mut ciphertext)?; - // let mut epk_bytes = vec![0; 33]; - // cursor.read_exact(&mut epk_bytes)?; - // - // let mut tag_bytes = [0; 1]; - // cursor.read_exact(&mut tag_bytes)?; - Ok(Self(ciphertext)) } } @@ -157,8 +135,6 @@ impl Ciphertext { let ciphertext_length: u32 = self.0.len() as u32; bytes.extend_from_slice(&ciphertext_length.to_le_bytes()); bytes.extend_from_slice(&self.0); - // bytes.extend_from_slice(&self.epk.0); - // bytes.push(self.view_tag); bytes } @@ -169,12 +145,7 @@ pub struct PrivacyPreservingCircuitInput { pub program_output: ProgramOutput, pub visibility_mask: Vec, pub private_account_nonces: Vec, - pub private_account_keys: Vec<( - NullifierPublicKey, - SharedSecretKey, - // IncomingViewingPublicKey, - // EphemeralSecretKey, - )>, + pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>, pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>, pub program_id: ProgramId, } @@ -236,10 +207,7 @@ mod tests { data: b"post state data".to_vec(), nonce: 18446744073709551615, }], - ciphertexts: vec![ - Ciphertext(vec![255, 255, 1, 1, 2, 2]), // epk: EphemeralPublicKey::from_scalar([123; 32]), - // view_tag: 1, - ], + ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])], new_commitments: vec![Commitment::new( &NullifierPublicKey::from(&[1; 32]), &Account::default(), diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index cda5053..fb4a41b 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -119,10 +119,8 @@ fn main() { let encrypted_account = Ciphertext::new( &post_with_updated_values, shared_secret, - // &commitment_post, - // esk, Npk, - // Ipk, + &commitment_post, output_index, ); diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 38407ce..49dd8f3 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -180,7 +180,12 @@ mod tests { let recipient_post = output.ciphertexts[0] .clone() - .decrypt(&shared_secret, &recipient_keys.npk(), 0) + .decrypt( + &shared_secret, + &recipient_keys.npk(), + &output.new_commitments[0], + 0, + ) .unwrap(); assert_eq!(recipient_post, expected_recipient_post); } @@ -268,13 +273,23 @@ mod tests { let sender_post = output.ciphertexts[0] .clone() - .decrypt(&shared_secret_1, &sender_keys.npk(), 0) + .decrypt( + &shared_secret_1, + &sender_keys.npk(), + &expected_new_commitments[0], + 0, + ) .unwrap(); assert_eq!(sender_post, expected_private_account_1); let recipient_post = output.ciphertexts[1] .clone() - .decrypt(&shared_secret_2, &recipient_keys.npk(), 1) + .decrypt( + &shared_secret_2, + &recipient_keys.npk(), + &expected_new_commitments[1], + 1, + ) .unwrap(); assert_eq!(recipient_post, expected_private_account_2); } diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index 245297f..a56ed7d 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -52,10 +52,11 @@ impl EncryptedAccountData { isk: &[u8; 32], epk: &EphemeralPublicKey, npk: &NullifierPublicKey, + commitment: &Commitment, output_index: u32, ) -> Option { let shared_secret = Self::compute_shared_secret(isk, &epk); - self.ciphertext.decrypt(&shared_secret, npk, output_index) + self.ciphertext.decrypt(&shared_secret, npk, commitment, output_index) } pub fn compute_shared_secret(scalar: &[u8; 32], point: &Secp256k1Point) -> SharedSecretKey { From 66b5efaacb0061ed7f232d27f089f1fa98c7108d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 26 Aug 2025 14:53:02 -0300 Subject: [PATCH 4/9] file refactor --- nssa/core/Cargo.toml | 3 +- nssa/core/src/{account/mod.rs => account.rs} | 9 - nssa/core/src/account/commitment.rs | 34 --- nssa/core/src/circuit_io.rs | 93 +++++++ nssa/core/src/commitment.rs | 63 +++++ nssa/core/src/{account => }/encoding.rs | 52 +++- nssa/core/src/encryption/mod.rs | 77 ++++++ .../src/encryption/shared_key_derivation.rs | 56 ++++ nssa/core/src/lib.rs | 242 +----------------- nssa/core/src/{account => }/nullifier.rs | 6 +- nssa/core/src/program.rs | 3 - .../src/bin/privacy_preserving_circuit.rs | 14 +- nssa/src/merkle_tree/mod.rs | 1 - .../privacy_preserving_transaction/circuit.rs | 79 +++--- .../encoding.rs | 20 +- .../privacy_preserving_transaction/message.rs | 72 +----- .../src/privacy_preserving_transaction/mod.rs | 2 +- .../transaction.rs | 7 +- nssa/src/state.rs | 34 ++- 19 files changed, 424 insertions(+), 443 deletions(-) rename nssa/core/src/{account/mod.rs => account.rs} (85%) delete mode 100644 nssa/core/src/account/commitment.rs create mode 100644 nssa/core/src/circuit_io.rs create mode 100644 nssa/core/src/commitment.rs rename nssa/core/src/{account => }/encoding.rs (80%) create mode 100644 nssa/core/src/encryption/mod.rs create mode 100644 nssa/core/src/encryption/shared_key_derivation.rs rename nssa/core/src/{account => }/nullifier.rs (95%) diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index a68a3f2..97d7558 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -9,7 +9,8 @@ serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } chacha20 = { version = "0.9", default-features = false } +k256 = { version = "0.13.3", optional = true} [features] default = [] -host = ["thiserror", "bytemuck"] +host = ["thiserror", "bytemuck", "k256"] diff --git a/nssa/core/src/account/mod.rs b/nssa/core/src/account.rs similarity index 85% rename from nssa/core/src/account/mod.rs rename to nssa/core/src/account.rs index 6f5e017..4b2f51d 100644 --- a/nssa/core/src/account/mod.rs +++ b/nssa/core/src/account.rs @@ -1,16 +1,7 @@ use serde::{Deserialize, Serialize}; - use crate::program::ProgramId; -mod commitment; -mod encoding; -mod nullifier; - -pub use commitment::Commitment; -pub use nullifier::{Nullifier, NullifierPublicKey, NullifierSecretKey}; - pub type Nonce = u128; -// TODO: Consider changing `Data` to `Vec` for r0 friendlinenss type Data = Vec; /// Account to be used both in public and private contexts diff --git a/nssa/core/src/account/commitment.rs b/nssa/core/src/account/commitment.rs deleted file mode 100644 index 487789f..0000000 --- a/nssa/core/src/account/commitment.rs +++ /dev/null @@ -1,34 +0,0 @@ -use risc0_zkvm::{ - serde::to_vec, - sha::{Impl, Sha256}, -}; -use serde::{Deserialize, Serialize}; - -use crate::account::{Account, NullifierPublicKey}; - -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq, Hash))] -pub struct Commitment(pub(super) [u8; 32]); - -impl Commitment { - pub fn new(Npk: &NullifierPublicKey, account: &Account) -> Self { - let mut bytes = Vec::new(); - bytes.extend_from_slice(&Npk.to_byte_array()); - let account_bytes_with_hashed_data = { - let mut this = Vec::new(); - for word in &account.program_owner { - this.extend_from_slice(&word.to_le_bytes()); - } - this.extend_from_slice(&account.balance.to_le_bytes()); - this.extend_from_slice(&account.nonce.to_le_bytes()); - let hashed_data: [u8; 32] = Impl::hash_bytes(&account.data) - .as_bytes() - .try_into() - .unwrap(); - this.extend_from_slice(&hashed_data); - this - }; - bytes.extend_from_slice(&account_bytes_with_hashed_data); - Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap()) - } -} diff --git a/nssa/core/src/circuit_io.rs b/nssa/core/src/circuit_io.rs new file mode 100644 index 0000000..f16d010 --- /dev/null +++ b/nssa/core/src/circuit_io.rs @@ -0,0 +1,93 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + Commitment, CommitmentSetDigest, MembershipProof, Nullifier, NullifierPublicKey, + NullifierSecretKey, SharedSecretKey, + account::{Account, AccountWithMetadata, Nonce}, + encryption::Ciphertext, + program::{ProgramId, ProgramOutput}, +}; + +#[derive(Serialize, Deserialize)] +pub struct PrivacyPreservingCircuitInput { + pub program_output: ProgramOutput, + pub visibility_mask: Vec, + pub private_account_nonces: Vec, + pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>, + pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>, + pub program_id: ProgramId, +} + +#[derive(Serialize, Deserialize)] +#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] +pub struct PrivacyPreservingCircuitOutput { + pub public_pre_states: Vec, + pub public_post_states: Vec, + pub ciphertexts: Vec, + pub new_commitments: Vec, + pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, +} + +#[cfg(feature = "host")] +impl PrivacyPreservingCircuitOutput { + pub fn to_bytes(&self) -> Vec { + bytemuck::cast_slice(&risc0_zkvm::serde::to_vec(&self).unwrap()).to_vec() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + Commitment, Nullifier, NullifierPublicKey, + account::{Account, AccountWithMetadata}, + }; + use risc0_zkvm::serde::from_slice; + + #[test] + fn test_privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() { + let output = PrivacyPreservingCircuitOutput { + public_pre_states: vec![ + AccountWithMetadata { + account: Account { + program_owner: [1, 2, 3, 4, 5, 6, 7, 8], + balance: 12345678901234567890, + data: b"test data".to_vec(), + nonce: 18446744073709551614, + }, + is_authorized: true, + }, + AccountWithMetadata { + account: Account { + program_owner: [9, 9, 9, 8, 8, 8, 7, 7], + balance: 123123123456456567112, + data: b"test data".to_vec(), + nonce: 9999999999999999999999, + }, + is_authorized: false, + }, + ], + public_post_states: vec![Account { + program_owner: [1, 2, 3, 4, 5, 6, 7, 8], + balance: 100, + data: b"post state data".to_vec(), + nonce: 18446744073709551615, + }], + ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])], + new_commitments: vec![Commitment::new( + &NullifierPublicKey::from(&[1; 32]), + &Account::default(), + )], + new_nullifiers: vec![( + Nullifier::new( + &Commitment::new(&NullifierPublicKey::from(&[2; 32]), &Account::default()), + &[1; 32], + ), + [0xab; 32], + )], + }; + let bytes = output.to_bytes(); + let output_from_slice: PrivacyPreservingCircuitOutput = from_slice(&bytes).unwrap(); + assert_eq!(output, output_from_slice); + } +} diff --git a/nssa/core/src/commitment.rs b/nssa/core/src/commitment.rs new file mode 100644 index 0000000..a4a4efb --- /dev/null +++ b/nssa/core/src/commitment.rs @@ -0,0 +1,63 @@ +use risc0_zkvm::sha::{Impl, Sha256}; +use serde::{Deserialize, Serialize}; + +use crate::{account::Account, NullifierPublicKey}; + +#[derive(Serialize, Deserialize)] +#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq, Hash))] +pub struct Commitment(pub(super) [u8; 32]); + +impl Commitment { + pub fn new(npk: &NullifierPublicKey, account: &Account) -> Self { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&npk.to_byte_array()); + let account_bytes_with_hashed_data = { + let mut this = Vec::new(); + for word in &account.program_owner { + this.extend_from_slice(&word.to_le_bytes()); + } + this.extend_from_slice(&account.balance.to_le_bytes()); + this.extend_from_slice(&account.nonce.to_le_bytes()); + let hashed_data: [u8; 32] = Impl::hash_bytes(&account.data) + .as_bytes() + .try_into() + .unwrap(); + this.extend_from_slice(&hashed_data); + this + }; + bytes.extend_from_slice(&account_bytes_with_hashed_data); + Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap()) + } +} + +pub type CommitmentSetDigest = [u8; 32]; + +pub type MembershipProof = (usize, Vec<[u8; 32]>); + +pub fn compute_digest_for_path( + commitment: &Commitment, + proof: &MembershipProof, +) -> CommitmentSetDigest { + let value_bytes = commitment.to_byte_array(); + let mut result: [u8; 32] = Impl::hash_bytes(&value_bytes) + .as_bytes() + .try_into() + .unwrap(); + let mut level_index = proof.0; + for node in &proof.1 { + let is_left_child = level_index & 1 == 0; + if is_left_child { + let mut bytes = [0u8; 64]; + bytes[..32].copy_from_slice(&result); + bytes[32..].copy_from_slice(node); + result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap(); + } else { + let mut bytes = [0u8; 64]; + bytes[..32].copy_from_slice(node); + bytes[32..].copy_from_slice(&result); + result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap(); + } + level_index >>= 1; + } + result +} diff --git a/nssa/core/src/account/encoding.rs b/nssa/core/src/encoding.rs similarity index 80% rename from nssa/core/src/account/encoding.rs rename to nssa/core/src/encoding.rs index 5b1dd0e..0bf2383 100644 --- a/nssa/core/src/account/encoding.rs +++ b/nssa/core/src/encoding.rs @@ -1,17 +1,25 @@ // TODO: Consider switching to deriving Borsh - -use risc0_zkvm::sha::{Impl, Sha256}; - #[cfg(feature = "host")] use std::io::Cursor; #[cfg(feature = "host")] use std::io::Read; -use crate::account::{Account, Commitment, Nullifier, NullifierPublicKey}; +use crate::account::Account; + +#[cfg(feature = "host")] +use crate::encryption::shared_key_derivation::Secp256k1Point; + +use crate::encryption::Ciphertext; + #[cfg(feature = "host")] use crate::error::NssaCoreError; +use crate::Commitment; +#[cfg(feature = "host")] +use crate::Nullifier; +use crate::NullifierPublicKey; + impl Account { pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); @@ -93,6 +101,42 @@ impl Nullifier { } } +impl Ciphertext { + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + let ciphertext_length: u32 = self.0.len() as u32; + bytes.extend_from_slice(&ciphertext_length.to_le_bytes()); + bytes.extend_from_slice(&self.0); + + bytes + } + + #[cfg(feature = "host")] + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let mut u32_bytes = [0; 4]; + + cursor.read_exact(&mut u32_bytes)?; + let ciphertext_lenght = u32::from_le_bytes(u32_bytes); + let mut ciphertext = vec![0; ciphertext_lenght as usize]; + cursor.read_exact(&mut ciphertext)?; + + Ok(Self(ciphertext)) + } +} + +#[cfg(feature = "host")] +impl Secp256k1Point { + pub fn to_bytes(&self) -> [u8; 33] { + self.0.clone().try_into().unwrap() + } + + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let mut value = vec![0; 33]; + cursor.read_exact(&mut value)?; + Ok(Self(value)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/nssa/core/src/encryption/mod.rs b/nssa/core/src/encryption/mod.rs new file mode 100644 index 0000000..8fc45bd --- /dev/null +++ b/nssa/core/src/encryption/mod.rs @@ -0,0 +1,77 @@ +use chacha20::{ + ChaCha20, + cipher::{KeyIvInit, StreamCipher}, +}; +use risc0_zkvm::sha::{Impl, Sha256}; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "host")] +pub(crate) mod shared_key_derivation; + +#[cfg(feature = "host")] +pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey}; + +use crate::{Commitment, NullifierPublicKey, account::Account}; + +#[derive(Serialize, Deserialize, Clone)] +pub struct SharedSecretKey([u8; 32]); + +pub struct EncryptionScheme; + +#[derive(Serialize, Deserialize)] +#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))] +pub struct Ciphertext(pub(crate) Vec); + +impl EncryptionScheme { + pub fn encrypt( + account: &Account, + shared_secret: &SharedSecretKey, + commitment: &Commitment, + output_index: u32, + ) -> Ciphertext { + let mut buffer = account.to_bytes().to_vec(); + Self::symmetric_transform(&mut buffer, shared_secret, commitment, output_index); + Ciphertext(buffer) + } + + fn symmetric_transform( + buffer: &mut [u8], + shared_secret: &SharedSecretKey, + commitment: &Commitment, + output_index: u32, + ) { + let key = Self::kdf(shared_secret, commitment, output_index); + let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); + cipher.apply_keystream(buffer); + } + + fn kdf( + shared_secret: &SharedSecretKey, + commitment: &Commitment, + output_index: u32, + ) -> [u8; 32] { + let mut bytes = Vec::new(); + + bytes.extend_from_slice(b"NSSA/v0.1/KDF-SHA256"); + bytes.extend_from_slice(&shared_secret.0); + bytes.extend_from_slice(&commitment.to_byte_array()); + bytes.extend_from_slice(&output_index.to_le_bytes()); + + Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap() + } + + #[cfg(feature = "host")] + pub fn decrypt( + ciphertext: &Ciphertext, + shared_secret: &SharedSecretKey, + commitment: &Commitment, + output_index: u32, + ) -> Option { + use std::io::Cursor; + let mut buffer = ciphertext.0.to_owned(); + Self::symmetric_transform(&mut buffer, shared_secret, commitment, output_index); + + let mut cursor = Cursor::new(buffer.as_slice()); + Account::from_cursor(&mut cursor).ok() + } +} diff --git a/nssa/core/src/encryption/shared_key_derivation.rs b/nssa/core/src/encryption/shared_key_derivation.rs new file mode 100644 index 0000000..c735105 --- /dev/null +++ b/nssa/core/src/encryption/shared_key_derivation.rs @@ -0,0 +1,56 @@ +use serde::{Deserialize, Serialize}; + +use k256::{ + AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, Scalar, + elliptic_curve::{ + PrimeField, + sec1::{FromEncodedPoint, ToEncodedPoint}, + }, +}; + +use crate::SharedSecretKey; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Secp256k1Point(pub(crate) Vec); + +impl Secp256k1Point { + pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point { + let x_bytes: FieldBytes = value.into(); + let x = Scalar::from_repr(x_bytes).unwrap(); + + let p = ProjectivePoint::GENERATOR * x; + let q = AffinePoint::from(p); + let enc = q.to_encoded_point(true); + + Self(enc.as_bytes().to_vec()) + } +} + +pub type EphemeralSecretKey = [u8; 32]; +pub type EphemeralPublicKey = Secp256k1Point; +pub type IncomingViewingPublicKey = Secp256k1Point; +impl From<&EphemeralSecretKey> for EphemeralPublicKey { + fn from(value: &EphemeralSecretKey) -> Self { + Secp256k1Point::from_scalar(*value) + } +} + +impl SharedSecretKey { + pub fn new(scalar: &[u8; 32], point: &Secp256k1Point) -> Self { + let scalar = Scalar::from_repr((*scalar).into()).unwrap(); + let point: [u8; 33] = point.0.clone().try_into().unwrap(); + + let encoded = EncodedPoint::from_bytes(point).unwrap(); + let pubkey_affine = AffinePoint::from_encoded_point(&encoded).unwrap(); + + let shared = ProjectivePoint::from(pubkey_affine) * scalar; + let shared_affine = shared.to_affine(); + + let encoded = shared_affine.to_encoded_point(false); + let x_bytes_slice = encoded.x().unwrap(); + let mut x_bytes = [0u8; 32]; + x_bytes.copy_from_slice(x_bytes_slice); + + Self(x_bytes) + } +} diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index 89dbeac..2275e31 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -1,237 +1,15 @@ -use chacha20::{ - ChaCha20, - cipher::{KeyIvInit, StreamCipher}, -}; - -use risc0_zkvm::{ - serde::to_vec, - sha::{Impl, Sha256}, -}; -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "host")] -use crate::error::NssaCoreError; - -use crate::{ - account::{ - Account, AccountWithMetadata, Commitment, Nonce, Nullifier, NullifierPublicKey, - NullifierSecretKey, - }, - program::{ProgramId, ProgramOutput}, -}; - -#[cfg(feature = "host")] -use std::io::{Cursor, Read}; - pub mod account; +mod circuit_io; +mod commitment; +mod encoding; +pub mod encryption; +mod nullifier; pub mod program; +pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput}; +pub use commitment::{Commitment, CommitmentSetDigest, MembershipProof, compute_digest_for_path}; +pub use encryption::{EncryptionScheme, SharedSecretKey}; +pub use nullifier::{Nullifier, NullifierPublicKey, NullifierSecretKey}; + #[cfg(feature = "host")] pub mod error; - -pub type CommitmentSetDigest = [u8; 32]; -pub type MembershipProof = (usize, Vec<[u8; 32]>); -pub fn compute_root_associated_to_path( - commitment: &Commitment, - proof: &MembershipProof, -) -> CommitmentSetDigest { - let value_bytes = commitment.to_byte_array(); - let mut result: [u8; 32] = Impl::hash_bytes(&value_bytes) - .as_bytes() - .try_into() - .unwrap(); - let mut level_index = proof.0; - for node in &proof.1 { - let is_left_child = level_index & 1 == 0; - if is_left_child { - let mut bytes = [0u8; 64]; - bytes[..32].copy_from_slice(&result); - bytes[32..].copy_from_slice(node); - result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap(); - } else { - let mut bytes = [0u8; 64]; - bytes[..32].copy_from_slice(node); - bytes[32..].copy_from_slice(&result); - result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap(); - } - level_index >>= 1; - } - result -} - -pub type SharedSecretKey = [u8; 32]; - -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))] -pub struct Ciphertext(Vec); - -impl Ciphertext { - #[cfg(feature = "host")] - pub fn decrypt( - self, - shared_secret: &[u8; 32], - npk: &NullifierPublicKey, - commitment: &Commitment, - output_index: u32, - ) -> Option { - let key = Self::kdf(&shared_secret, npk, commitment, output_index); - let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); - let mut buffer = self.0; - - cipher.apply_keystream(&mut buffer); - let mut cursor = Cursor::new(buffer.as_slice()); - Account::from_cursor(&mut cursor).ok() - } - - pub fn new( - account: &Account, - shared_secret: &[u8; 32], - npk: &NullifierPublicKey, - commitment: &Commitment, - output_index: u32, - ) -> Self { - let mut buffer = account.to_bytes().to_vec(); - - let key = Self::kdf(shared_secret, npk, commitment, output_index); - let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); - cipher.apply_keystream(&mut buffer); - - Self(buffer) - } - - pub fn kdf( - ss_bytes: &[u8; 32], - npk: &NullifierPublicKey, - commitment: &Commitment, - output_index: u32, - ) -> [u8; 32] { - let mut bytes = Vec::new(); - - bytes.extend_from_slice(b"NSSA/v0.1/KDF-SHA256"); - bytes.extend_from_slice(ss_bytes); - bytes.extend_from_slice(&npk.to_byte_array()); - bytes.extend_from_slice(&commitment.to_byte_array()); - bytes.extend_from_slice(&output_index.to_le_bytes()); - - Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap() - } - - #[cfg(feature = "host")] - pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { - let mut u32_bytes = [0; 4]; - - cursor.read_exact(&mut u32_bytes)?; - let ciphertext_lenght = u32::from_le_bytes(u32_bytes); - let mut ciphertext = vec![0; ciphertext_lenght as usize]; - cursor.read_exact(&mut ciphertext)?; - - Ok(Self(ciphertext)) - } -} - -impl Ciphertext { - pub fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let ciphertext_length: u32 = self.0.len() as u32; - bytes.extend_from_slice(&ciphertext_length.to_le_bytes()); - bytes.extend_from_slice(&self.0); - - bytes - } -} - -#[derive(Serialize, Deserialize)] -pub struct PrivacyPreservingCircuitInput { - pub program_output: ProgramOutput, - pub visibility_mask: Vec, - pub private_account_nonces: Vec, - pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>, - pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>, - pub program_id: ProgramId, -} - -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] -pub struct PrivacyPreservingCircuitOutput { - pub public_pre_states: Vec, - pub public_post_states: Vec, - pub ciphertexts: Vec, - pub new_commitments: Vec, - pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, -} - -#[cfg(feature = "host")] -impl PrivacyPreservingCircuitOutput { - pub fn to_bytes(&self) -> Vec { - bytemuck::cast_slice(&to_vec(&self).unwrap()).to_vec() - } -} - -#[cfg(test)] -mod tests { - use std::io::Cursor; - - use risc0_zkvm::serde::from_slice; - - use crate::{ - Ciphertext, PrivacyPreservingCircuitOutput, - account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey}, - }; - - #[test] - fn test_privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() { - let output = PrivacyPreservingCircuitOutput { - public_pre_states: vec![ - AccountWithMetadata { - account: Account { - program_owner: [1, 2, 3, 4, 5, 6, 7, 8], - balance: 12345678901234567890, - data: b"test data".to_vec(), - nonce: 18446744073709551614, - }, - is_authorized: true, - }, - AccountWithMetadata { - account: Account { - program_owner: [9, 9, 9, 8, 8, 8, 7, 7], - balance: 123123123456456567112, - data: b"test data".to_vec(), - nonce: 9999999999999999999999, - }, - is_authorized: false, - }, - ], - public_post_states: vec![Account { - program_owner: [1, 2, 3, 4, 5, 6, 7, 8], - balance: 100, - data: b"post state data".to_vec(), - nonce: 18446744073709551615, - }], - ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])], - new_commitments: vec![Commitment::new( - &NullifierPublicKey::from(&[1; 32]), - &Account::default(), - )], - new_nullifiers: vec![( - Nullifier::new( - &Commitment::new(&NullifierPublicKey::from(&[2; 32]), &Account::default()), - &[1; 32], - ), - [0xab; 32], - )], - }; - let bytes = output.to_bytes(); - let output_from_slice: PrivacyPreservingCircuitOutput = from_slice(&bytes).unwrap(); - assert_eq!(output, output_from_slice); - } - - #[test] - fn test_ciphertext_to_bytes_roundtrip() { - let data = Ciphertext(vec![255, 255, 1, 1, 2, 2]); - - let bytes = data.to_bytes(); - let mut cursor = Cursor::new(bytes.as_slice()); - let data_from_cursor = Ciphertext::from_cursor(&mut cursor).unwrap(); - assert_eq!(data, data_from_cursor); - } -} diff --git a/nssa/core/src/account/nullifier.rs b/nssa/core/src/nullifier.rs similarity index 95% rename from nssa/core/src/account/nullifier.rs rename to nssa/core/src/nullifier.rs index 8faef0b..346a815 100644 --- a/nssa/core/src/account/nullifier.rs +++ b/nssa/core/src/nullifier.rs @@ -1,7 +1,7 @@ use risc0_zkvm::sha::{Impl, Sha256}; use serde::{Deserialize, Serialize}; -use crate::account::Commitment; +use crate::Commitment; #[derive(Serialize, Deserialize, PartialEq, Eq)] #[cfg_attr(any(feature = "host", test), derive(Debug, Clone, Hash))] @@ -58,11 +58,11 @@ mod tests { 57, 5, 64, 115, 153, 56, 184, 51, 207, 238, 99, 165, 147, 214, 213, 151, 30, 251, 30, 196, 134, 22, 224, 211, 237, 120, 136, 225, 188, 220, 249, 28, ]; - let expected_Npk = NullifierPublicKey([ + let expected_npk = NullifierPublicKey([ 202, 120, 42, 189, 194, 218, 78, 244, 31, 6, 108, 169, 29, 61, 22, 221, 69, 138, 197, 161, 241, 39, 142, 242, 242, 50, 188, 201, 99, 28, 176, 238, ]); let Npk = NullifierPublicKey::from(&nsk); - assert_eq!(Npk, expected_Npk); + assert_eq!(Npk, expected_npk); } } diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index f84bd61..d284bbc 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -3,9 +3,6 @@ use risc0_zkvm::serde::Deserializer; use risc0_zkvm::{DeserializeOwned, guest::env}; use serde::{Deserialize, Serialize}; -#[cfg(feature = "host")] -use crate::error::NssaCoreError; - pub type ProgramId = [u32; 8]; pub type InstructionData = Vec; pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index fb4a41b..d43f4d3 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -1,10 +1,12 @@ use risc0_zkvm::{guest::env, serde::to_vec}; use nssa_core::{ - account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey}, - compute_root_associated_to_path, + account::{Account, AccountWithMetadata}, + compute_digest_for_path, + encryption::Ciphertext, program::{validate_execution, ProgramOutput, DEFAULT_PROGRAM_ID}, - Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, + Commitment, CommitmentSetDigest, EncryptionScheme, Nullifier, NullifierPublicKey, + PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, }; fn main() { @@ -82,8 +84,7 @@ fn main() { // Compute commitment set digest associated with provided auth path let commitment_pre = Commitment::new(Npk, &pre_states[i].account); - let set_digest = - compute_root_associated_to_path(&commitment_pre, membership_proof); + let set_digest = compute_digest_for_path(&commitment_pre, membership_proof); // Check pre_state authorization if !pre_states[i].is_authorized { @@ -116,10 +117,9 @@ fn main() { let commitment_post = Commitment::new(Npk, &post_with_updated_values); // Encrypt and push post state - let encrypted_account = Ciphertext::new( + let encrypted_account = EncryptionScheme::encrypt( &post_with_updated_values, shared_secret, - Npk, &commitment_post, output_index, ); diff --git a/nssa/src/merkle_tree/mod.rs b/nssa/src/merkle_tree/mod.rs index 4b0b06f..a242379 100644 --- a/nssa/src/merkle_tree/mod.rs +++ b/nssa/src/merkle_tree/mod.rs @@ -181,7 +181,6 @@ fn prev_power_of_two(x: usize) -> usize { #[cfg(test)] mod tests { use hex_literal::hex; - use nssa_core::account::{Account, NullifierPublicKey}; use super::*; diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 49dd8f3..ad4b03c 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -1,7 +1,7 @@ use nssa_core::{ - CommitmentSetDigest, MembershipProof, PrivacyPreservingCircuitInput, - PrivacyPreservingCircuitOutput, SharedSecretKey, - account::{Account, AccountWithMetadata, Nonce, NullifierPublicKey, NullifierSecretKey}, + CommitmentSetDigest, MembershipProof, NullifierPublicKey, NullifierSecretKey, + PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, SharedSecretKey, + account::{Account, AccountWithMetadata, Nonce}, program::{InstructionData, ProgramId, ProgramOutput}, }; use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover}; @@ -91,11 +91,9 @@ fn execute_and_prove_program( #[cfg(test)] mod tests { use nssa_core::{ - Ciphertext, - account::{ - Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey, - NullifierSecretKey, - }, + Commitment, EncryptionScheme, Nullifier, NullifierPublicKey, NullifierSecretKey, + account::{Account, AccountWithMetadata}, + encryption::EphemeralPublicKey, }; use risc0_zkvm::{InnerReceipt, Journal, Receipt}; @@ -104,7 +102,7 @@ mod tests { merkle_tree::MerkleTree, privacy_preserving_transaction::{ circuit::{Proof, execute_and_prove}, - message::{EncryptedAccountData, EphemeralPublicKey}, + message::EncryptedAccountData, }, program::Program, state::{ @@ -153,8 +151,7 @@ mod tests { let recipient_keys = test_private_account_keys_1(); let esk = [3; 32]; - let shared_secret = - EncryptedAccountData::compute_shared_secret(&esk, &recipient_keys.ivk()); + let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); let epk = EphemeralPublicKey::from_scalar(esk); let (output, proof) = execute_and_prove( @@ -162,7 +159,7 @@ mod tests { &Program::serialize_instruction(balance_to_move).unwrap(), &[0, 2], &[0xdeadbeef], - &[(recipient_keys.npk(), shared_secret)], + &[(recipient_keys.npk(), shared_secret.clone())], &[], &Program::authenticated_transfer_program(), ) @@ -178,15 +175,13 @@ mod tests { assert_eq!(output.new_nullifiers.len(), 0); assert_eq!(output.ciphertexts.len(), 1); - let recipient_post = output.ciphertexts[0] - .clone() - .decrypt( - &shared_secret, - &recipient_keys.npk(), - &output.new_commitments[0], - 0, - ) - .unwrap(); + let recipient_post = EncryptionScheme::decrypt( + &output.ciphertexts[0], + &shared_secret, + &output.new_commitments[0], + 0, + ) + .unwrap(); assert_eq!(recipient_post, expected_recipient_post); } @@ -238,13 +233,11 @@ mod tests { ]; let esk_1 = [3; 32]; - let shared_secret_1 = - EncryptedAccountData::compute_shared_secret(&esk_1, &sender_keys.ivk()); + let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk()); let epk_1 = EphemeralPublicKey::from_scalar(esk_1); let esk_2 = [5; 32]; - let shared_secret_2 = - EncryptedAccountData::compute_shared_secret(&esk_2, &recipient_keys.ivk()); + let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk()); let epk_2 = EphemeralPublicKey::from_scalar(esk_2); let (output, proof) = execute_and_prove( @@ -253,8 +246,8 @@ mod tests { &[1, 2], &[0xdeadbeef1, 0xdeadbeef2], &[ - (sender_keys.npk(), shared_secret_1), - (recipient_keys.npk(), shared_secret_2), + (sender_keys.npk(), shared_secret_1.clone()), + (recipient_keys.npk(), shared_secret_2.clone()), ], &[( sender_keys.nsk, @@ -271,26 +264,22 @@ mod tests { assert_eq!(output.new_nullifiers, expected_new_nullifiers); assert_eq!(output.ciphertexts.len(), 2); - let sender_post = output.ciphertexts[0] - .clone() - .decrypt( - &shared_secret_1, - &sender_keys.npk(), - &expected_new_commitments[0], - 0, - ) - .unwrap(); + let sender_post = EncryptionScheme::decrypt( + &output.ciphertexts[0], + &shared_secret_1, + &expected_new_commitments[0], + 0, + ) + .unwrap(); assert_eq!(sender_post, expected_private_account_1); - let recipient_post = output.ciphertexts[1] - .clone() - .decrypt( - &shared_secret_2, - &recipient_keys.npk(), - &expected_new_commitments[1], - 1, - ) - .unwrap(); + let recipient_post = EncryptionScheme::decrypt( + &output.ciphertexts[1], + &shared_secret_2, + &expected_new_commitments[1], + 1, + ) + .unwrap(); assert_eq!(recipient_post, expected_private_account_2); } } diff --git a/nssa/src/privacy_preserving_transaction/encoding.rs b/nssa/src/privacy_preserving_transaction/encoding.rs index fd818e2..bfaf4ff 100644 --- a/nssa/src/privacy_preserving_transaction/encoding.rs +++ b/nssa/src/privacy_preserving_transaction/encoding.rs @@ -1,18 +1,13 @@ -// TODO: Consider switching to deriving Borsh - use std::io::{Cursor, Read}; use nssa_core::{ - Ciphertext, - account::{Account, Commitment, Nullifier}, + Commitment, Nullifier, + account::Account, + encryption::{Ciphertext, EphemeralPublicKey}, }; use crate::{ - Address, - error::NssaError, - privacy_preserving_transaction::message::{ - EncryptedAccountData, EphemeralPublicKey, Secp256k1Point, - }, + Address, error::NssaError, privacy_preserving_transaction::message::EncryptedAccountData, }; use super::message::Message; @@ -23,17 +18,14 @@ const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"\x01/NSSA/ impl EncryptedAccountData { pub(crate) fn to_bytes(&self) -> Vec { let mut bytes = self.ciphertext.to_bytes(); - bytes.extend_from_slice(&self.epk.0); + bytes.extend_from_slice(&self.epk.to_bytes()); bytes.push(self.view_tag); bytes } pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let ciphertext = Ciphertext::from_cursor(cursor)?; - - let mut epk_bytes = vec![0; 33]; - cursor.read_exact(&mut epk_bytes)?; - let epk = Secp256k1Point(epk_bytes); + let epk = EphemeralPublicKey::from_cursor(cursor)?; let mut tag_bytes = [0; 1]; cursor.read_exact(&mut tag_bytes)?; diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index a56ed7d..b2f8247 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -1,44 +1,14 @@ use std::io::Cursor; -use k256::{ - AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, PublicKey, Scalar, - elliptic_curve::{ - PrimeField, - sec1::{FromEncodedPoint, ToEncodedPoint}, - }, -}; use nssa_core::{ - Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitOutput, SharedSecretKey, - account::{Account, Commitment, Nonce, Nullifier, NullifierPublicKey}, + Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput, + account::{Account, Nonce}, + encryption::{Ciphertext, EphemeralPublicKey}, }; use serde::{Deserialize, Serialize}; use crate::{Address, error::NssaError}; -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct Secp256k1Point(pub(crate) Vec); -impl Secp256k1Point { - pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point { - let x_bytes: FieldBytes = value.into(); - let x = Scalar::from_repr(x_bytes).unwrap(); - - let p = ProjectivePoint::GENERATOR * x; - let q = AffinePoint::from(p); - let enc = q.to_encoded_point(true); - - Self(enc.as_bytes().to_vec()) - } -} - -pub type EphemeralSecretKey = [u8; 32]; -pub type EphemeralPublicKey = Secp256k1Point; -pub type IncomingViewingPublicKey = Secp256k1Point; -impl From<&EphemeralSecretKey> for EphemeralPublicKey { - fn from(value: &EphemeralSecretKey) -> Self { - Secp256k1Point::from_scalar(*value) - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct EncryptedAccountData { pub(crate) ciphertext: Ciphertext, @@ -46,38 +16,6 @@ pub struct EncryptedAccountData { pub(crate) view_tag: u8, } -impl EncryptedAccountData { - pub fn decrypt( - self, - isk: &[u8; 32], - epk: &EphemeralPublicKey, - npk: &NullifierPublicKey, - commitment: &Commitment, - output_index: u32, - ) -> Option { - let shared_secret = Self::compute_shared_secret(isk, &epk); - self.ciphertext.decrypt(&shared_secret, npk, commitment, output_index) - } - - pub fn compute_shared_secret(scalar: &[u8; 32], point: &Secp256k1Point) -> SharedSecretKey { - let scalar = Scalar::from_repr((*scalar).into()).unwrap(); - let point: [u8; 33] = point.0.clone().try_into().unwrap(); - - let encoded = EncodedPoint::from_bytes(point).unwrap(); - let pubkey_affine = AffinePoint::from_encoded_point(&encoded).unwrap(); - - let shared = ProjectivePoint::from(pubkey_affine) * scalar; - let shared_affine = shared.to_affine(); - - let encoded = shared_affine.to_encoded_point(false); - let x_bytes_slice = encoded.x().unwrap(); - let mut x_bytes = [0u8; 32]; - x_bytes.copy_from_slice(x_bytes_slice); - - x_bytes - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct Message { pub(crate) public_addresses: Vec
, @@ -126,9 +64,7 @@ impl Message { pub mod tests { use std::io::Cursor; - use nssa_core::account::{ - Account, Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, - }; + use nssa_core::{account::Account, Commitment, Nullifier, NullifierPublicKey}; use crate::{Address, privacy_preserving_transaction::message::Message}; diff --git a/nssa/src/privacy_preserving_transaction/mod.rs b/nssa/src/privacy_preserving_transaction/mod.rs index d773395..448a488 100644 --- a/nssa/src/privacy_preserving_transaction/mod.rs +++ b/nssa/src/privacy_preserving_transaction/mod.rs @@ -5,6 +5,6 @@ mod witness_set; pub mod circuit; -pub use message::{Message, IncomingViewingPublicKey, EphemeralPublicKey, EphemeralSecretKey, EncryptedAccountData}; +pub use message::{Message, EncryptedAccountData}; pub use transaction::PrivacyPreservingTransaction; pub use witness_set::WitnessSet; diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index adb0c66..632aec9 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -1,7 +1,10 @@ use std::collections::{HashMap, HashSet}; -use nssa_core::account::{Account, AccountWithMetadata, Commitment, Nullifier}; -use nssa_core::{Ciphertext, CommitmentSetDigest, PrivacyPreservingCircuitOutput}; +use nssa_core::{ + Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput, + account::{Account, AccountWithMetadata}, + encryption::Ciphertext, +}; use crate::error::NssaError; use crate::privacy_preserving_transaction::circuit::Proof; diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 35f9719..81d8746 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -4,9 +4,7 @@ use crate::{ public_transaction::PublicTransaction, }; use nssa_core::{ - CommitmentSetDigest, MembershipProof, - account::{Account, Commitment, Nullifier}, - program::{DEFAULT_PROGRAM_ID, ProgramId}, + account::Account, program::{ProgramId, DEFAULT_PROGRAM_ID}, Commitment, CommitmentSetDigest, MembershipProof, Nullifier }; use std::collections::{HashMap, HashSet}; @@ -222,19 +220,17 @@ pub mod tests { Address, PublicKey, PublicTransaction, V01State, error::NssaError, privacy_preserving_transaction::{ - EncryptedAccountData, EphemeralPublicKey, IncomingViewingPublicKey, Message, - PrivacyPreservingTransaction, WitnessSet, circuit, + EncryptedAccountData, Message, PrivacyPreservingTransaction, WitnessSet, circuit, }, program::Program, public_transaction, signature::PrivateKey, }; + use nssa_core::{ - Ciphertext, account::{ - Account, AccountWithMetadata, Commitment, Nonce, Nullifier, NullifierPublicKey, - NullifierSecretKey, - }, + Account, AccountWithMetadata, Nonce, + }, encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey }; use program_methods::AUTHENTICATED_TRANSFER_ID; @@ -794,8 +790,7 @@ pub mod tests { }; let esk = [3; 32]; - let shared_secret = - EncryptedAccountData::compute_shared_secret(&esk, &recipient_keys.ivk()); + let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); let epk = EphemeralPublicKey::from_scalar(esk); let (output, proof) = circuit::execute_and_prove( @@ -814,7 +809,8 @@ pub mod tests { vec![sender_nonce], vec![epk], output, - ).unwrap(); + ) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&sender_keys.signing_key]); PrivacyPreservingTransaction::new(message, witness_set) @@ -840,13 +836,11 @@ pub mod tests { }; let esk_1 = [3; 32]; - let shared_secret_1 = - EncryptedAccountData::compute_shared_secret(&esk_1, &sender_keys.ivk()); + let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk()); let epk_1 = EphemeralPublicKey::from_scalar(esk_1); let esk_2 = [3; 32]; - let shared_secret_2 = - EncryptedAccountData::compute_shared_secret(&esk_2, &recipient_keys.ivk()); + let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk()); let epk_2 = EphemeralPublicKey::from_scalar(esk_2); let (output, proof) = circuit::execute_and_prove( @@ -870,7 +864,8 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output(vec![], vec![], vec![epk_1, epk_2], output).unwrap(); + let message = + Message::try_from_circuit_output(vec![], vec![], vec![epk_1, epk_2], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); @@ -897,7 +892,7 @@ pub mod tests { }; let esk = [3; 32]; - let shared_secret = EncryptedAccountData::compute_shared_secret(&esk, &sender_keys.ivk()); + let shared_secret = SharedSecretKey::new(&esk, &sender_keys.ivk()); let epk = EphemeralPublicKey::from_scalar(esk); let (output, proof) = circuit::execute_and_prove( @@ -923,7 +918,8 @@ pub mod tests { vec![], vec![epk], output, - ).unwrap(); + ) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); From 0a3f093d6083fb970798570c1bd61c351909a985 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 27 Aug 2025 16:24:20 -0300 Subject: [PATCH 5/9] fmt and clippy --- nssa/Cargo.toml | 2 +- nssa/core/Cargo.toml | 4 +- nssa/core/src/account.rs | 2 +- nssa/core/src/circuit_io.rs | 1 + nssa/core/src/commitment.rs | 2 +- nssa/core/src/encoding.rs | 1 + nssa/core/src/encryption/mod.rs | 2 +- nssa/core/src/nullifier.rs | 4 +- nssa/program_methods/Cargo.toml | 1 - nssa/src/lib.rs | 6 +- nssa/src/merkle_tree/mod.rs | 57 ++++++++-------- .../privacy_preserving_transaction/circuit.rs | 26 ++----- .../encoding.rs | 5 +- .../privacy_preserving_transaction/message.rs | 13 ++-- .../src/privacy_preserving_transaction/mod.rs | 8 +-- .../transaction.rs | 1 - nssa/src/program.rs | 14 ++-- nssa/src/public_transaction/encoding.rs | 3 +- nssa/src/state.rs | 68 +++++++------------ 19 files changed, 90 insertions(+), 130 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index aad78af..5162167 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] thiserror = "2.0.12" risc0-zkvm = "2.3.1" -nssa-core = { path = "core", features=["host"]} +nssa-core = { path = "core", features = ["host"] } program-methods = { path = "program_methods" } serde = "1.0.219" sha2 = "0.10.9" diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 97d7558..44491de 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -4,12 +4,12 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = { version = "2.3.1"} +risc0-zkvm = { version = "2.3.1" } serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } chacha20 = { version = "0.9", default-features = false } -k256 = { version = "0.13.3", optional = true} +k256 = { version = "0.13.3", optional = true } [features] default = [] diff --git a/nssa/core/src/account.rs b/nssa/core/src/account.rs index 4b2f51d..688611e 100644 --- a/nssa/core/src/account.rs +++ b/nssa/core/src/account.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::program::ProgramId; +use serde::{Deserialize, Serialize}; pub type Nonce = u128; type Data = Vec; diff --git a/nssa/core/src/circuit_io.rs b/nssa/core/src/circuit_io.rs index f16d010..e619b2d 100644 --- a/nssa/core/src/circuit_io.rs +++ b/nssa/core/src/circuit_io.rs @@ -35,6 +35,7 @@ impl PrivacyPreservingCircuitOutput { } } +#[cfg(feature = "host")] #[cfg(test)] mod tests { use super::*; diff --git a/nssa/core/src/commitment.rs b/nssa/core/src/commitment.rs index a4a4efb..bc22c8f 100644 --- a/nssa/core/src/commitment.rs +++ b/nssa/core/src/commitment.rs @@ -1,7 +1,7 @@ use risc0_zkvm::sha::{Impl, Sha256}; use serde::{Deserialize, Serialize}; -use crate::{account::Account, NullifierPublicKey}; +use crate::{NullifierPublicKey, account::Account}; #[derive(Serialize, Deserialize)] #[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq, Hash))] diff --git a/nssa/core/src/encoding.rs b/nssa/core/src/encoding.rs index 0bf2383..6f7e36f 100644 --- a/nssa/core/src/encoding.rs +++ b/nssa/core/src/encoding.rs @@ -171,6 +171,7 @@ mod tests { assert_eq!(expected_bytes, bytes); } + #[cfg(feature = "host")] #[test] fn test_nullifier_to_bytes() { let nullifier = Nullifier((0..32).collect::>().try_into().unwrap()); diff --git a/nssa/core/src/encryption/mod.rs b/nssa/core/src/encryption/mod.rs index 8fc45bd..b79e75c 100644 --- a/nssa/core/src/encryption/mod.rs +++ b/nssa/core/src/encryption/mod.rs @@ -11,7 +11,7 @@ pub(crate) mod shared_key_derivation; #[cfg(feature = "host")] pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey}; -use crate::{Commitment, NullifierPublicKey, account::Account}; +use crate::{Commitment, account::Account}; #[derive(Serialize, Deserialize, Clone)] pub struct SharedSecretKey([u8; 32]); diff --git a/nssa/core/src/nullifier.rs b/nssa/core/src/nullifier.rs index 346a815..d1410de 100644 --- a/nssa/core/src/nullifier.rs +++ b/nssa/core/src/nullifier.rs @@ -62,7 +62,7 @@ mod tests { 202, 120, 42, 189, 194, 218, 78, 244, 31, 6, 108, 169, 29, 61, 22, 221, 69, 138, 197, 161, 241, 39, 142, 242, 242, 50, 188, 201, 99, 28, 176, 238, ]); - let Npk = NullifierPublicKey::from(&nsk); - assert_eq!(Npk, expected_npk); + let npk = NullifierPublicKey::from(&nsk); + assert_eq!(npk, expected_npk); } } diff --git a/nssa/program_methods/Cargo.toml b/nssa/program_methods/Cargo.toml index 0f31c9b..ea7e36b 100644 --- a/nssa/program_methods/Cargo.toml +++ b/nssa/program_methods/Cargo.toml @@ -8,4 +8,3 @@ risc0-build = { version = "2.3.1" } [package.metadata.risc0] methods = ["guest"] - diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index ac94f1e..ed88047 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,16 +1,18 @@ pub mod address; pub mod error; +mod merkle_tree; mod privacy_preserving_transaction; pub mod program; pub mod public_transaction; mod signature; mod state; -mod merkle_tree; pub use address::Address; +pub use privacy_preserving_transaction::{ + PrivacyPreservingTransaction, circuit::execute_and_prove, +}; pub use public_transaction::PublicTransaction; pub use signature::PrivateKey; pub use signature::PublicKey; pub use signature::Signature; pub use state::V01State; - diff --git a/nssa/src/merkle_tree/mod.rs b/nssa/src/merkle_tree/mod.rs index a242379..2306efd 100644 --- a/nssa/src/merkle_tree/mod.rs +++ b/nssa/src/merkle_tree/mod.rs @@ -1,5 +1,3 @@ -use std::collections::{HashMap, HashSet}; - use sha2::{Digest, Sha256}; mod default_values; @@ -47,8 +45,7 @@ impl MerkleTree { /// Number of levels required to hold all values fn depth(&self) -> usize { - let result = self.length.next_power_of_two().trailing_zeros() as usize; - result + self.length.next_power_of_two().trailing_zeros() as usize } fn get_node(&self, index: usize) -> &Node { @@ -108,7 +105,6 @@ impl MerkleTree { self.set_node(node_index, node_hash); self.length += 1; - let root_index = self.root_index(); for _ in 0..self.depth() { let parent_index = (node_index - 1) >> 1; let left_child = self.get_node((parent_index << 1) + 1); @@ -121,14 +117,6 @@ impl MerkleTree { new_index } - pub fn new(values: &[Value]) -> Self { - let mut this = Self::with_capacity(values.len()); - for value in values.iter().cloned() { - this.insert(value); - } - this - } - pub fn get_authentication_path_for(&self, index: usize) -> Option> { if index >= self.length { return None; @@ -155,22 +143,6 @@ impl MerkleTree { } } -// Reference implementation -fn verify_authentication_path(value: &Value, index: usize, path: &[Node], root: &Node) -> bool { - let mut result = hash_value(value); - let mut level_index = index; - for node in path { - let is_left_child = level_index & 1 == 0; - if is_left_child { - result = hash_two(&result, node); - } else { - result = hash_two(node, &result); - } - level_index >>= 1; - } - &result == root -} - fn prev_power_of_two(x: usize) -> usize { if x == 0 { return 0; @@ -180,10 +152,19 @@ fn prev_power_of_two(x: usize) -> usize { #[cfg(test)] mod tests { + impl MerkleTree { + pub fn new(values: &[Value]) -> Self { + let mut this = Self::with_capacity(values.len()); + for value in values.iter().cloned() { + this.insert(value); + } + this + } + } + use hex_literal::hex; use super::*; - #[test] fn test_empty_merkle_tree() { let tree = MerkleTree::with_capacity(4); @@ -398,6 +379,22 @@ mod tests { assert_eq!(expected_tree, tree); } + // Reference implementation + fn verify_authentication_path(value: &Value, index: usize, path: &[Node], root: &Node) -> bool { + let mut result = hash_value(value); + let mut level_index = index; + for node in path { + let is_left_child = level_index & 1 == 0; + if is_left_child { + result = hash_two(&result, node); + } else { + result = hash_two(node, &result); + } + level_index >>= 1; + } + &result == root + } + #[test] fn test_authentication_path_1() { let values = [[1; 32], [2; 32], [3; 32], [4; 32]]; diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index ad4b03c..687e214 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -1,8 +1,8 @@ use nssa_core::{ - CommitmentSetDigest, MembershipProof, NullifierPublicKey, NullifierSecretKey, - PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, SharedSecretKey, - account::{Account, AccountWithMetadata, Nonce}, - program::{InstructionData, ProgramId, ProgramOutput}, + MembershipProof, NullifierPublicKey, NullifierSecretKey, PrivacyPreservingCircuitInput, + PrivacyPreservingCircuitOutput, SharedSecretKey, + account::AccountWithMetadata, + program::{InstructionData, ProgramOutput}, }; use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover}; @@ -91,19 +91,12 @@ fn execute_and_prove_program( #[cfg(test)] mod tests { use nssa_core::{ - Commitment, EncryptionScheme, Nullifier, NullifierPublicKey, NullifierSecretKey, + Commitment, EncryptionScheme, Nullifier, account::{Account, AccountWithMetadata}, - encryption::EphemeralPublicKey, }; - use risc0_zkvm::{InnerReceipt, Journal, Receipt}; use crate::{ - Address, V01State, - merkle_tree::MerkleTree, - privacy_preserving_transaction::{ - circuit::{Proof, execute_and_prove}, - message::EncryptedAccountData, - }, + privacy_preserving_transaction::circuit::execute_and_prove, program::Program, state::{ CommitmentSet, @@ -111,8 +104,6 @@ mod tests { }, }; - use rand::{Rng, RngCore, rngs::OsRng}; - use super::*; #[test] @@ -152,7 +143,6 @@ mod tests { let esk = [3; 32]; let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); - let epk = EphemeralPublicKey::from_scalar(esk); let (output, proof) = execute_and_prove( &[sender, recipient], @@ -206,7 +196,7 @@ mod tests { let balance_to_move: u128 = 37; let mut commitment_set = CommitmentSet::with_capacity(2); - commitment_set.extend(&[commitment_sender.clone()]); + commitment_set.extend(std::slice::from_ref(&commitment_sender)); let expected_new_nullifiers = vec![( Nullifier::new(&commitment_sender, &sender_keys.nsk), @@ -234,11 +224,9 @@ mod tests { let esk_1 = [3; 32]; let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk()); - let epk_1 = EphemeralPublicKey::from_scalar(esk_1); let esk_2 = [5; 32]; let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk()); - let epk_2 = EphemeralPublicKey::from_scalar(esk_2); let (output, proof) = execute_and_prove( &[sender_pre.clone(), recipient], diff --git a/nssa/src/privacy_preserving_transaction/encoding.rs b/nssa/src/privacy_preserving_transaction/encoding.rs index bfaf4ff..b5d4950 100644 --- a/nssa/src/privacy_preserving_transaction/encoding.rs +++ b/nssa/src/privacy_preserving_transaction/encoding.rs @@ -88,6 +88,7 @@ impl Message { bytes } + #[allow(unused)] pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let prefix = { let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN]; @@ -115,7 +116,7 @@ impl Message { // Nonces cursor.read_exact(&mut len_bytes)?; let nonces_len = u32::from_le_bytes(len_bytes) as usize; - let mut nonces = Vec::with_capacity(nonces_len as usize); + let mut nonces = Vec::with_capacity(nonces_len); for _ in 0..nonces_len { let mut buf = [0u8; 16]; cursor.read_exact(&mut buf)?; @@ -153,7 +154,7 @@ impl Message { for _ in 0..new_nullifiers_len { let nullifier = Nullifier::from_cursor(cursor)?; let mut commitment_set_digest = [0; 32]; - cursor.read_exact(&mut commitment_set_digest); + cursor.read_exact(&mut commitment_set_digest)?; new_nullifiers.push((nullifier, commitment_set_digest)); } diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index b2f8247..4877e3d 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -1,11 +1,8 @@ -use std::io::Cursor; - use nssa_core::{ Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput, account::{Account, Nonce}, encryption::{Ciphertext, EphemeralPublicKey}, }; -use serde::{Deserialize, Serialize}; use crate::{Address, error::NssaError}; @@ -64,7 +61,7 @@ impl Message { pub mod tests { use std::io::Cursor; - use nssa_core::{account::Account, Commitment, Nullifier, NullifierPublicKey}; + use nssa_core::{Commitment, Nullifier, NullifierPublicKey, account::Account}; use crate::{Address, privacy_preserving_transaction::message::Message}; @@ -75,8 +72,8 @@ pub mod tests { let nsk1 = [11; 32]; let nsk2 = [12; 32]; - let Npk1 = NullifierPublicKey::from(&nsk1); - let Npk2 = NullifierPublicKey::from(&nsk2); + let npk1 = NullifierPublicKey::from(&nsk1); + let npk2 = NullifierPublicKey::from(&nsk2); let public_addresses = vec![Address::new([1; 32])]; @@ -86,9 +83,9 @@ pub mod tests { let encrypted_private_post_states = Vec::new(); - let new_commitments = vec![Commitment::new(&Npk2, &account2)]; + let new_commitments = vec![Commitment::new(&npk2, &account2)]; - let old_commitment = Commitment::new(&Npk1, &account1); + let old_commitment = Commitment::new(&npk1, &account1); let new_nullifiers = vec![(Nullifier::new(&old_commitment, &nsk1), [0; 32])]; Message { diff --git a/nssa/src/privacy_preserving_transaction/mod.rs b/nssa/src/privacy_preserving_transaction/mod.rs index 448a488..54fc94b 100644 --- a/nssa/src/privacy_preserving_transaction/mod.rs +++ b/nssa/src/privacy_preserving_transaction/mod.rs @@ -1,10 +1,8 @@ mod encoding; -mod message; -mod transaction; -mod witness_set; +pub mod message; +pub mod transaction; +pub mod witness_set; pub mod circuit; -pub use message::{Message, EncryptedAccountData}; pub use transaction::PrivacyPreservingTransaction; -pub use witness_set::WitnessSet; diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index 632aec9..9aac54e 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -3,7 +3,6 @@ use std::collections::{HashMap, HashSet}; use nssa_core::{ Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput, account::{Account, AccountWithMetadata}, - encryption::Ciphertext, }; use crate::error::NssaError; diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 410d674..d504313 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -1,11 +1,10 @@ -use borsh::{BorshDeserialize, BorshSerialize}; use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{DEFAULT_PROGRAM_ID, InstructionData, ProgramId, ProgramOutput}, + program::{InstructionData, ProgramId, ProgramOutput}, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ - ExecutorEnv, ExecutorEnvBuilder, Journal, Receipt, default_executor, default_prover, + ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec, }; use serde::Serialize; @@ -51,7 +50,7 @@ impl Program { // Get outputs let ProgramOutput { - mut post_states, .. + post_states, .. } = session_info .journal .decode() @@ -83,11 +82,8 @@ impl Program { #[cfg(test)] mod tests { - use nssa_core::{ - account::{Account, AccountWithMetadata}, - program::ProgramOutput, - }; - use risc0_zkvm::{InnerReceipt, Receipt, serde::to_vec}; + use nssa_core::account::{Account, AccountWithMetadata}; + use crate::program::Program; diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 70b5288..e8890de 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -11,8 +11,7 @@ use crate::{ }; const MESSAGE_ENCODING_PREFIX_LEN: usize = 22; -const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = - b"\x00/NSSA/v0.1/TxMessage/"; +const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"\x00/NSSA/v0.1/TxMessage/"; impl Message { /// Serializes a `Message` into bytes in the following layout: diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 81d8746..93ee06c 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -4,14 +4,16 @@ use crate::{ public_transaction::PublicTransaction, }; use nssa_core::{ - account::Account, program::{ProgramId, DEFAULT_PROGRAM_ID}, Commitment, CommitmentSetDigest, MembershipProof, Nullifier + Commitment, CommitmentSetDigest, MembershipProof, Nullifier, + account::Account, + program::{DEFAULT_PROGRAM_ID, ProgramId}, }; use std::collections::{HashMap, HashSet}; pub(crate) struct CommitmentSet { merkle_tree: MerkleTree, commitments: HashMap, - pub root_history: HashSet, + root_history: HashSet, } impl CommitmentSet { @@ -19,13 +21,12 @@ impl CommitmentSet { self.merkle_tree.root() } - pub(crate) fn get_proof_for(&self, commitment: &Commitment) -> Option { + pub fn get_proof_for(&self, commitment: &Commitment) -> Option { let index = *self.commitments.get(commitment)?; - let proof = self - .merkle_tree + + self.merkle_tree .get_authentication_path_for(index) - .map(|path| (index, path)); - proof + .map(|path| (index, path)) } pub(crate) fn extend(&mut self, commitments: &[Commitment]) { @@ -53,7 +54,7 @@ type NullifierSet = HashSet; pub struct V01State { public_state: HashMap, - pub private_state: (CommitmentSet, NullifierSet), + private_state: (CommitmentSet, NullifierSet), builtin_programs: HashMap, } @@ -159,6 +160,10 @@ impl V01State { .unwrap_or(Account::default()) } + pub fn get_proof_for_commitment(&self, commitment: &Commitment) -> Option { + self.private_state.0.get_proof_for(commitment) + } + pub(crate) fn builtin_programs(&self) -> &HashMap { &self.builtin_programs } @@ -199,16 +204,6 @@ impl V01State { } Ok(()) } - - pub(crate) fn check_commitment_set_digest_is_valid( - &self, - commitment_set_digest: &CommitmentSetDigest, - ) -> bool { - self.private_state - .0 - .root_history - .contains(commitment_set_digest) - } } #[cfg(test)] @@ -220,7 +215,7 @@ pub mod tests { Address, PublicKey, PublicTransaction, V01State, error::NssaError, privacy_preserving_transaction::{ - EncryptedAccountData, Message, PrivacyPreservingTransaction, WitnessSet, circuit, + PrivacyPreservingTransaction, circuit, message::Message, witness_set::WitnessSet, }, program::Program, public_transaction, @@ -228,11 +223,10 @@ pub mod tests { }; use nssa_core::{ - account::{ - Account, AccountWithMetadata, Nonce, - }, encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey + Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, + account::{Account, AccountWithMetadata, Nonce}, + encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, }; - use program_methods::AUTHENTICATED_TRANSFER_ID; fn transfer_transaction( from: Address, @@ -482,7 +476,7 @@ pub mod tests { } pub fn with_private_account(mut self, keys: &TestPrivateKeys, account: &Account) -> Self { - let commitment = Commitment::new(&keys.npk(), &account); + let commitment = Commitment::new(&keys.npk(), account); self.private_state.0.extend(&[commitment]); self } @@ -825,7 +819,7 @@ pub mod tests { state: &V01State, ) -> PrivacyPreservingTransaction { let program = Program::authenticated_transfer_program(); - let sender_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account); + let sender_commitment = Commitment::new(&sender_keys.npk(), sender_private_account); let sender_pre = AccountWithMetadata { account: sender_private_account.clone(), is_authorized: true, @@ -854,11 +848,7 @@ pub mod tests { ], &[( sender_keys.nsk, - state - .private_state - .0 - .get_proof_for(&sender_commitment) - .unwrap(), + state.get_proof_for_commitment(&sender_commitment).unwrap(), )], &program, ) @@ -881,7 +871,7 @@ pub mod tests { state: &V01State, ) -> PrivacyPreservingTransaction { let program = Program::authenticated_transfer_program(); - let sender_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account); + let sender_commitment = Commitment::new(&sender_keys.npk(), sender_private_account); let sender_pre = AccountWithMetadata { account: sender_private_account.clone(), is_authorized: true, @@ -903,23 +893,15 @@ pub mod tests { &[(sender_keys.npk(), shared_secret)], &[( sender_keys.nsk, - state - .private_state - .0 - .get_proof_for(&sender_commitment) - .unwrap(), + state.get_proof_for_commitment(&sender_commitment).unwrap(), )], &program, ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![recipient_address.clone()], - vec![], - vec![epk], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![*recipient_address], vec![], vec![epk], output) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); From 8dbb636a14a60d301ac37d06fc8740fa88744bea Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 27 Aug 2025 17:24:59 -0300 Subject: [PATCH 6/9] add dev mode to ci rust tests --- ci_scripts/test-ubuntu.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci_scripts/test-ubuntu.sh b/ci_scripts/test-ubuntu.sh index db14402..5b7afcf 100644 --- a/ci_scripts/test-ubuntu.sh +++ b/ci_scripts/test-ubuntu.sh @@ -4,7 +4,7 @@ curl -L https://risczero.com/install | bash /home/runner/.risc0/bin/rzup install source env.sh -cargo test --release +RISC0_DEV_MODE=1 cargo test --release cd integration_tests export NSSA_WALLET_HOME_DIR=$(pwd)/configs/debug/wallet/ -cargo run $(pwd)/configs/debug all \ No newline at end of file +cargo run $(pwd)/configs/debug all From 5bd45b685fe4e3c459cdc07729514f8c13644d97 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 27 Aug 2025 18:02:10 -0300 Subject: [PATCH 7/9] bump r0 version --- nssa/Cargo.toml | 2 +- nssa/core/Cargo.toml | 2 +- nssa/core/src/encoding.rs | 2 +- nssa/program_methods/Cargo.toml | 2 +- nssa/program_methods/guest/Cargo.lock | 716 +++++++++++++----- nssa/program_methods/guest/Cargo.toml | 2 +- .../src/bin/privacy_preserving_circuit.rs | 13 +- nssa/test_program_methods/Cargo.toml | 2 +- nssa/test_program_methods/guest/Cargo.toml | 2 +- wallet/Cargo.toml | 2 +- 10 files changed, 551 insertions(+), 194 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 5162167..8ada541 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] thiserror = "2.0.12" -risc0-zkvm = "2.3.1" +risc0-zkvm = "3.0.3" nssa-core = { path = "core", features = ["host"] } program-methods = { path = "program_methods" } serde = "1.0.219" diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 44491de..2eb0ce2 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = { version = "2.3.1" } +risc0-zkvm = { version = "3.0.3" } serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } diff --git a/nssa/core/src/encoding.rs b/nssa/core/src/encoding.rs index 6f7e36f..dd586de 100644 --- a/nssa/core/src/encoding.rs +++ b/nssa/core/src/encoding.rs @@ -150,7 +150,7 @@ mod tests { data: b"hola mundo".to_vec(), }; - // program owner || balance || nonce || hash(data) + // program owner || balance || nonce || data_len || data let expected_bytes = [ 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 192, 186, 220, 114, 113, 65, 236, 234, 222, 15, 215, 191, 227, 198, 23, 0, 42, diff --git a/nssa/program_methods/Cargo.toml b/nssa/program_methods/Cargo.toml index ea7e36b..af52307 100644 --- a/nssa/program_methods/Cargo.toml +++ b/nssa/program_methods/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [build-dependencies] -risc0-build = { version = "2.3.1" } +risc0-build = { version = "3.0.3" } [package.metadata.risc0] methods = ["guest"] diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index b37fcb9..eb5eb30 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -44,6 +44,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.98" @@ -303,6 +318,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bincode" version = "1.3.3" @@ -356,15 +377,15 @@ dependencies = [ [[package]] name = "bonsai-sdk" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bce8d6acc5286a16e94c29e9c885d1869358885e08a6feeb6bc54e36fe20055" +checksum = "21055e2f49cbbdbfe9f8f96d597c5527b0c6ab7933341fdc2f147180e48a988e" dependencies = [ "duplicate", "maybe-async", "reqwest", "serde", - "thiserror 1.0.69", + "thiserror", ] [[package]] @@ -460,7 +481,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror", ] [[package]] @@ -495,6 +516,19 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + [[package]] name = "cipher" version = "0.4.4" @@ -511,7 +545,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.12", + "thiserror", ] [[package]] @@ -601,6 +635,27 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -678,23 +733,23 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.60.2", ] [[package]] @@ -722,14 +777,21 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "duplicate" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" dependencies = [ - "heck 0.4.1", - "proc-macro-error", + "heck", + "proc-macro2", + "proc-macro2-diagnostics", ] +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "educe" version = "0.6.0" @@ -823,6 +885,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.5.0" @@ -966,12 +1034,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" @@ -980,23 +1045,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", + "foldhash", ] [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.4", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1115,6 +1175,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.0.0" @@ -1234,6 +1318,17 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.10.0" @@ -1242,6 +1337,7 @@ checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.4", + "serde", ] [[package]] @@ -1490,6 +1586,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -1499,6 +1618,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1506,6 +1636,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -1544,6 +1696,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1562,6 +1723,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "postcard" version = "1.1.3" @@ -1583,6 +1765,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1601,30 +1789,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -1634,6 +1798,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "version_check", + "yansi", +] + [[package]] name = "programs" version = "0.1.0" @@ -1642,6 +1819,20 @@ dependencies = [ "risc0-zkvm", ] +[[package]] +name = "proptest" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +dependencies = [ + "bitflags 2.9.1", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "unarray", +] + [[package]] name = "prost" version = "0.13.5" @@ -1679,7 +1870,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror 2.0.12", + "thiserror", "tokio", "tracing", "web-time", @@ -1700,7 +1891,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror", "tinyvec", "tracing", "web-time", @@ -1780,6 +1971,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] [[package]] name = "rand_core" @@ -1791,14 +1985,43 @@ dependencies = [ ] [[package]] -name = "redox_users" -version = "0.4.6" +name = "rand_xorshift" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 1.0.69", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -1888,18 +2111,21 @@ dependencies = [ [[package]] name = "risc0-binfmt" -version = "2.0.2" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62eb7025356a233c1bc267c458a2ce56fcfc89b136d813c8a77be14ef1eaf2b1" +checksum = "1c8f97f81bcdead4101bca06469ecef481a2695cd04e7e877b49dea56a7f6f2a" dependencies = [ "anyhow", "borsh", + "bytemuck", "derive_more", "elf", "lazy_static", "postcard", + "rand 0.9.2", "risc0-zkp", "risc0-zkvm-platform", + "ruint", "semver", "serde", "tracing", @@ -1907,9 +2133,9 @@ dependencies = [ [[package]] name = "risc0-build" -version = "2.3.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ffc0f135e6c1e9851e7e19438d03ff41a9d49199ee4f6c17b8bb30b4f83910" +checksum = "1bbb512d728e011d03ce0958ca7954624ee13a215bcafd859623b3c63b2a3f60" dependencies = [ "anyhow", "cargo_metadata", @@ -1931,9 +2157,9 @@ dependencies = [ [[package]] name = "risc0-circuit-keccak" -version = "3.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0094af5a57b020388a03bdd3834959c7d62723f1687be81414ade25104d93263" +checksum = "5f195f865ac1afdc21a172d7756fdcc21be18e13eb01d78d3d7f2b128fa881ba" dependencies = [ "anyhow", "bytemuck", @@ -1947,9 +2173,9 @@ dependencies = [ [[package]] name = "risc0-circuit-recursion" -version = "3.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ebded45c902c2b6939924a1cddd1d06b5d1d4ad1531e8798ebfee78f9c038d" +checksum = "dca8f15c8abc0fd8c097aa7459879110334d191c63dd51d4c28881c4a497279e" dependencies = [ "anyhow", "bytemuck", @@ -1962,9 +2188,9 @@ dependencies = [ [[package]] name = "risc0-circuit-rv32im" -version = "3.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15030849f8356f01f23c74b37dbfa4283100b594eb634109993e9e005ef45f64" +checksum = "ae1b0689f4a270a2f247b04397ebb431b8f64fe5170e98ee4f9d71bd04825205" dependencies = [ "anyhow", "bit-vec", @@ -1980,24 +2206,24 @@ dependencies = [ [[package]] name = "risc0-core" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317bbf70a8750b64d4fd7a2bdc9d7d5f30d8bb305cae486962c797ef35c8d08e" +checksum = "80f2723fedace48c6c5a505bd8f97ac4e1712bc4cb769083e10536d862b66987" dependencies = [ "bytemuck", - "bytemuck_derive", - "rand_core 0.6.4", + "rand_core 0.9.3", ] [[package]] name = "risc0-groth16" -version = "2.0.2" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf5d0b673d5fc67a89147c2e9c53134707dcc8137a43d1ef06b4ff68e99b74f" +checksum = "724285dc79604abfb2d40feaefe3e335420a6b293511661f77d6af62f1f5fae9" dependencies = [ "anyhow", "ark-bn254", "ark-ec", + "ark-ff", "ark-groth16", "ark-serialize", "bytemuck", @@ -2007,24 +2233,24 @@ dependencies = [ "risc0-binfmt", "risc0-zkp", "serde", - "stability", ] [[package]] name = "risc0-zkos-v1compat" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76c479b69d1987cb54ac72dcc017197296fdcd6daf78fafc10cbbd3a167a7de" +checksum = "840c2228803557a8b7dc035a8f196516b6fd68c9dc6ac092f0c86241b5b1bafb" dependencies = [ "include_bytes_aligned", "no_std_strings", + "risc0-zkvm-platform", ] [[package]] name = "risc0-zkp" -version = "2.0.2" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a287e9cd6d7b3b38eeb49c62090c46a1935922309fbd997a9143ed8c43c8f3cb" +checksum = "ffb6bf356f469bb8744f72a07a37134c5812c1d55d6271bba80e87bdb7a58c8e" dependencies = [ "anyhow", "blake2", @@ -2036,7 +2262,7 @@ dependencies = [ "hex-literal", "metal", "paste", - "rand_core 0.6.4", + "rand_core 0.9.3", "risc0-core", "risc0-zkvm-platform", "serde", @@ -2047,9 +2273,9 @@ dependencies = [ [[package]] name = "risc0-zkvm" -version = "2.3.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9684b333c1c5d83f29ce2a92314ccfafd9d8cdfa6c4e19c07b97015d2f1eb9d0" +checksum = "3fcce11648a9ff60b8e7af2f0ce7fbf8d25275ab6d414cc91b9da69ee75bc978" dependencies = [ "anyhow", "bincode", @@ -2058,7 +2284,6 @@ dependencies = [ "bytemuck", "bytes", "derive_more", - "getrandom 0.2.16", "hex", "lazy-regex", "prost", @@ -2084,15 +2309,17 @@ dependencies = [ [[package]] name = "risc0-zkvm-platform" -version = "2.0.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae9cb2c2f6cab2dfa395ea6e2576713929040c7fb0c5f4150d13e1119d18686" +checksum = "5c826f09626ab2ae76671673e5a232548ddd95a34eece2ea4ced5f010383f95b" dependencies = [ "bytemuck", "cfg-if", "getrandom 0.2.16", "getrandom 0.3.3", "libm", + "num_enum", + "paste", "stability", ] @@ -2106,6 +2333,48 @@ dependencies = [ "paste", ] +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "ruint" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" +dependencies = [ + "borsh", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -2180,19 +2449,47 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "rzup" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "400558bf12d4292a7804093b60a437ba8b0219ea7d53716b2c010a0d31e5f4a8" +checksum = "5d2aed296f203fa64bcb4b52069356dd86d6ec578593985b919b6995bee1f0ae" dependencies = [ + "hex", + "rsa", "semver", "serde", + "serde_with", + "sha2", "strum", "tempfile", - "thiserror 2.0.12", + "thiserror", "toml", "yaml-rust2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "semver" version = "1.0.26" @@ -2255,6 +2552,38 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "sha2" version = "0.10.9" @@ -2272,6 +2601,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "slab" version = "0.4.10" @@ -2310,6 +2649,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stability" version = "0.2.1" @@ -2334,23 +2683,22 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "rustversion", "syn 2.0.104", ] @@ -2415,33 +2763,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.12", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", + "thiserror-impl", ] [[package]] @@ -2455,6 +2783,37 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -2547,7 +2906,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -2660,6 +3019,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -2844,6 +3209,41 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "windows-link" version = "0.1.3" @@ -2851,12 +3251,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-result" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.48.5", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", ] [[package]] @@ -2886,21 +3295,6 @@ dependencies = [ "windows-targets 0.53.3", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -2934,12 +3328,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -2952,12 +3340,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -2970,12 +3352,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3000,12 +3376,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3018,12 +3388,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3036,12 +3400,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3054,12 +3412,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3098,15 +3450,21 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yaml-rust2" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1a1c0bc9823338a3bdf8c61f994f23ac004c6fa32c08cd152984499b445e8d" +checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" dependencies = [ "arraydeque", "encoding_rs", "hashlink", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yoke" version = "0.8.0" diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index 4b377c8..0d47ccd 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" [workspace] [dependencies] -risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } +risc0-zkvm = { version = "3.0.3", default-features = false, features = ['std'] } nssa-core = { path = "../../core" } diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index d43f4d3..b21cfde 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -68,8 +68,7 @@ fn main() { } 1 | 2 => { let new_nonce = private_nonces_iter.next().expect("Missing private nonce"); - // let (Npk, Ipk, esk) = private_keys_iter.next().expect("Missing keys"); - let (Npk, shared_secret) = private_keys_iter.next().expect("Missing keys"); + let (npk, shared_secret) = private_keys_iter.next().expect("Missing keys"); if visibility_mask[i] == 1 { // Private account with authentication @@ -77,13 +76,13 @@ fn main() { private_auth_iter.next().expect("Missing private auth"); // Verify Npk - let expected_Npk = NullifierPublicKey::from(nsk); - if &expected_Npk != Npk { - panic!("Npk mismatch"); + let expected_npk = NullifierPublicKey::from(nsk); + if &expected_npk != npk { + panic!("Nullifier public key mismatch"); } // Compute commitment set digest associated with provided auth path - let commitment_pre = Commitment::new(Npk, &pre_states[i].account); + let commitment_pre = Commitment::new(npk, &pre_states[i].account); let set_digest = compute_digest_for_path(&commitment_pre, membership_proof); // Check pre_state authorization @@ -114,7 +113,7 @@ fn main() { } // Compute commitment - let commitment_post = Commitment::new(Npk, &post_with_updated_values); + let commitment_post = Commitment::new(npk, &post_with_updated_values); // Encrypt and push post state let encrypted_account = EncryptionScheme::encrypt( diff --git a/nssa/test_program_methods/Cargo.toml b/nssa/test_program_methods/Cargo.toml index dded508..50d6ca2 100644 --- a/nssa/test_program_methods/Cargo.toml +++ b/nssa/test_program_methods/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [build-dependencies] -risc0-build = { version = "2.3.1" } +risc0-build = { version = "3.0.3" } [package.metadata.risc0] methods = ["guest"] diff --git a/nssa/test_program_methods/guest/Cargo.toml b/nssa/test_program_methods/guest/Cargo.toml index 4b377c8..0d47ccd 100644 --- a/nssa/test_program_methods/guest/Cargo.toml +++ b/nssa/test_program_methods/guest/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" [workspace] [dependencies] -risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } +risc0-zkvm = { version = "3.0.3", default-features = false, features = ['std'] } nssa-core = { path = "../../core" } diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index e5b01b7..db6d6e9 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -18,7 +18,7 @@ reqwest.workspace = true thiserror.workspace = true tokio.workspace = true tempfile.workspace = true -risc0-zkvm = "2.3.1" +risc0-zkvm = "3.0.3" hex.workspace = true actix-rt.workspace = true clap.workspace = true From 87577726ec816f226364a17531f7084b0a866b74 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 27 Aug 2025 18:23:56 -0300 Subject: [PATCH 8/9] add program_id check --- .../guest/src/bin/privacy_preserving_circuit.rs | 9 ++++++--- nssa/src/privacy_preserving_transaction/circuit.rs | 10 ++++------ nssa/src/program.rs | 10 ++-------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index b21cfde..a07fdd7 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -9,6 +9,8 @@ use nssa_core::{ PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, }; +const AUTHENTICATED_TRANSFER_PROGRAM_ID: [u32; 8] = [4009390658, 295818534, 2287042879, 2991817555, 298425691, 3426172222, 3671663086, 1858988641]; + fn main() { let PrivacyPreservingCircuitInput { program_output, @@ -19,8 +21,9 @@ fn main() { program_id, } = env::read(); - // TODO: Check that `program_execution_proof` is one of the allowed built-in programs - // assert!(BUILTIN_PROGRAM_IDS.contains(executing_program_id)); + // Check that `program_execution_proof` is one of the allowed built-in programs + // TODO: Adapt when more builtin programs are added + assert_eq!(program_id, AUTHENTICATED_TRANSFER_PROGRAM_ID); // Check that `program_output` is consistent with the execution of the corresponding program. env::verify(program_id, &to_vec(&program_output).unwrap()).unwrap(); @@ -75,7 +78,7 @@ fn main() { let (nsk, membership_proof) = private_auth_iter.next().expect("Missing private auth"); - // Verify Npk + // Verify the nullifier public key let expected_npk = NullifierPublicKey::from(nsk); if &expected_npk != npk { panic!("Nullifier public key mismatch"); diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 687e214..ed32f98 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -10,6 +10,7 @@ use crate::{error::NssaError, program::Program}; use program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT_ID}; +/// Proof of the privacy preserving execution circuit #[derive(Debug, Clone, PartialEq, Eq)] pub struct Proof(Vec); @@ -21,17 +22,14 @@ impl Proof { } } +/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution +/// circuit pub fn execute_and_prove( pre_states: &[AccountWithMetadata], instruction_data: &InstructionData, visibility_mask: &[u8], private_account_nonces: &[u128], - private_account_keys: &[( - NullifierPublicKey, - SharedSecretKey, - // IncomingViewingPublicKey, - // EphemeralSecretKey, - )], + private_account_keys: &[(NullifierPublicKey, SharedSecretKey)], private_account_auth: &[(NullifierSecretKey, MembershipProof)], program: &Program, ) -> Result<(PrivacyPreservingCircuitOutput, Proof), NssaError> { diff --git a/nssa/src/program.rs b/nssa/src/program.rs index d504313..66358e9 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -3,10 +3,7 @@ use nssa_core::{ program::{InstructionData, ProgramId, ProgramOutput}, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; -use risc0_zkvm::{ - ExecutorEnv, ExecutorEnvBuilder, default_executor, - serde::to_vec, -}; +use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec}; use serde::Serialize; use crate::error::NssaError; @@ -49,9 +46,7 @@ impl Program { .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; // Get outputs - let ProgramOutput { - post_states, .. - } = session_info + let ProgramOutput { post_states, .. } = session_info .journal .decode() .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; @@ -83,7 +78,6 @@ impl Program { #[cfg(test)] mod tests { use nssa_core::account::{Account, AccountWithMetadata}; - use crate::program::Program; From a36592485cee72a87082e4f6c6263ea85c0ec934 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 27 Aug 2025 19:25:03 -0300 Subject: [PATCH 9/9] remove builtin programs check --- nssa/Cargo.toml | 1 - .../guest/src/bin/privacy_preserving_circuit.rs | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 8ada541..98a0adf 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -11,7 +11,6 @@ program-methods = { path = "program_methods" } serde = "1.0.219" sha2 = "0.10.9" secp256k1 = "0.31.1" -k256 = "0.13.3" rand = "0.8" borsh = "1.5.7" bytemuck = "1.13" diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index a07fdd7..83f593a 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -9,8 +9,6 @@ use nssa_core::{ PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, }; -const AUTHENTICATED_TRANSFER_PROGRAM_ID: [u32; 8] = [4009390658, 295818534, 2287042879, 2991817555, 298425691, 3426172222, 3671663086, 1858988641]; - fn main() { let PrivacyPreservingCircuitInput { program_output, @@ -21,9 +19,8 @@ fn main() { program_id, } = env::read(); - // Check that `program_execution_proof` is one of the allowed built-in programs - // TODO: Adapt when more builtin programs are added - assert_eq!(program_id, AUTHENTICATED_TRANSFER_PROGRAM_ID); + // TODO: Check that `program_execution_proof` is one of the allowed built-in programs + // assert_eq!(program_id, AUTHENTICATED_TRANSFER_PROGRAM_ID); // Check that `program_output` is consistent with the execution of the corresponding program. env::verify(program_id, &to_vec(&program_output).unwrap()).unwrap();