From 8b70da517ce0494eb7267d329b500986f09c8bb1 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 3 Oct 2025 18:31:56 -0300 Subject: [PATCH] add check and test --- nssa/core/src/account.rs | 2 +- .../src/bin/privacy_preserving_circuit.rs | 18 ++++ nssa/src/state.rs | 82 +++++++++++++------ 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/nssa/core/src/account.rs b/nssa/core/src/account.rs index ddae797..44f7514 100644 --- a/nssa/core/src/account.rs +++ b/nssa/core/src/account.rs @@ -16,7 +16,7 @@ pub struct Account { /// A fingerprint of the owner of an account. This can be, for example, an `Address` in case the account /// is public, or a `NullifierPublicKey` in case the account is private. -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] #[cfg_attr(any(feature = "host", test), derive(Debug))] pub struct AccountId(pub(super) [u8; 32]); impl AccountId { 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 235b5c8..4522ce5 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use risc0_zkvm::{guest::env, serde::to_vec}; use nssa_core::{ @@ -30,6 +32,11 @@ fn main() { post_states, } = program_output; + // Check that there are no repeated account ids + if !validate_uniqueness_of_account_ids(&pre_states) { + panic!("Repeated account ids found") + } + // Check that the program is well behaved. // See the # Programs section for the definition of the `validate_execution` method. if !validate_execution(&pre_states, &post_states, program_id) { @@ -161,3 +168,14 @@ fn main() { env::commit(&output); } + +fn validate_uniqueness_of_account_ids(pre_states: &[AccountWithMetadata]) -> bool { + let number_of_accounts = pre_states.len(); + let number_of_account_ids = pre_states + .iter() + .map(|account| account.account_id.clone()) + .collect::>() + .len(); + + number_of_accounts == number_of_account_ids +} diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 65b440f..6fe966e 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1397,10 +1397,10 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = - AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); // Setting only one nonce for an execution with two private accounts. let private_account_nonces = [0xdeadbeef1]; @@ -1437,7 +1437,7 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); @@ -1472,10 +1472,10 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = - AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); // Setting no auth key for an execution with one non default private accounts. let private_account_auth = []; @@ -1513,10 +1513,10 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = - AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); let private_account_keys = [ // First private account is the sender @@ -1561,7 +1561,7 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = AccountWithMetadata::new( Account { @@ -1570,7 +1570,7 @@ pub mod tests { ..Account::default() }, false, - AccountId::new([1; 32]), + &recipient_keys.npk(), ); let result = execute_and_prove( @@ -1608,7 +1608,7 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = AccountWithMetadata::new( Account { @@ -1617,7 +1617,7 @@ pub mod tests { ..Account::default() }, false, - AccountId::new([1; 32]), + &recipient_keys.npk(), ); let result = execute_and_prove( @@ -1654,7 +1654,7 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = AccountWithMetadata::new( Account { @@ -1663,7 +1663,7 @@ pub mod tests { ..Account::default() }, false, - AccountId::new([1; 32]), + &recipient_keys.npk(), ); let result = execute_and_prove( @@ -1700,7 +1700,7 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = AccountWithMetadata::new( Account { @@ -1709,7 +1709,7 @@ pub mod tests { ..Account::default() }, false, - AccountId::new([1; 32]), + &recipient_keys.npk(), ); let result = execute_and_prove( @@ -1747,13 +1747,13 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = AccountWithMetadata::new( Account::default(), // This should be set to false in normal circumstances true, - AccountId::new([1; 32]), + &recipient_keys.npk(), ); let result = execute_and_prove( @@ -1819,10 +1819,10 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = - AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); // Setting three new private account nonces for a circuit execution with only two private // accounts. @@ -1861,10 +1861,10 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = - AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); // Setting three private account keys for a circuit execution with only two private // accounts. @@ -1907,10 +1907,10 @@ pub mod tests { ..Account::default() }, true, - AccountId::new([0; 32]), + &sender_keys.npk(), ); let private_account_2 = - AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); // Setting two private account keys for a circuit execution with only one non default // private account (visibility mask equal to 1 means that auth keys are expected). @@ -1940,4 +1940,40 @@ pub mod tests { assert!(matches!(result, Err(NssaError::CircuitProvingError(_)))); } + + #[test] + fn test_circuit_should_fail_if_there_are_repeated_ids() { + let program = Program::simple_balance_transfer(); + let sender_keys = test_private_account_keys_1(); + let private_account_1 = AccountWithMetadata::new( + Account { + program_owner: program.id(), + balance: 100, + ..Account::default() + }, + true, + &sender_keys.npk(), + ); + + let visibility_mask = [1, 1]; + let private_account_auth = [ + (sender_keys.nsk, (1, vec![])), + (sender_keys.nsk, (1, vec![])), + ]; + let shared_secret = SharedSecretKey::new(&[55; 32], &sender_keys.ivk()); + let result = execute_and_prove( + &[private_account_1.clone(), private_account_1], + &Program::serialize_instruction(100u128).unwrap(), + &visibility_mask, + &[0xdeadbeef1, 0xdeadbeef2], + &[ + (sender_keys.npk(), shared_secret.clone()), + (sender_keys.npk(), shared_secret), + ], + &private_account_auth, + &program, + ); + + assert!(matches!(result, Err(NssaError::CircuitProvingError(_)))); + } }