From ad47f48a79541356ae5fb0a87ec0ce4cb561381b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 11:02:59 -0300 Subject: [PATCH] add test for invalid balance transfers --- nssa/src/program.rs | 10 +++++ nssa/src/state.rs | 1 + nssa/src/tests/valid_execution_tests.rs | 45 ++++++++++++++----- .../guest/src/bin/simple_balance_transfer.rs | 20 +++++++++ 4 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 2307da2..ba239e9 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -115,4 +115,14 @@ impl Program { elf: PROGRAM_OWNER_CHANGER_ELF, } } + + /// A program that transfers balance without caring about authorizations + pub fn simple_balance_transfer() -> Self { + use test_program_methods::{SIMPLE_BALANCE_TRANSFER_ELF, SIMPLE_BALANCE_TRANSFER_ID}; + + Program { + id: SIMPLE_BALANCE_TRANSFER_ID, + elf: SIMPLE_BALANCE_TRANSFER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 278fab4..7b48c08 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -150,6 +150,7 @@ impl V01State { self.insert_program(Program::extra_output_program()); self.insert_program(Program::missing_output_program()); self.insert_program(Program::program_owner_changer()); + self.insert_program(Program::simple_balance_transfer()); self } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index c2781a7..7ae5144 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -5,13 +5,12 @@ use crate::{ }; #[test] -fn test_program_should_fail_if_it_modifies_nonces() { +fn test_program_should_fail_if_modifies_nonces() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; - let nonces = vec![]; let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -25,9 +24,8 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; - let nonces = vec![]; let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -41,9 +39,8 @@ fn test_program_should_fail_with_missing_output_accounts() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; - let nonces = vec![]; let program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -53,7 +50,7 @@ fn test_program_should_fail_with_missing_output_accounts() { } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_program_owner() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() { let initial_data = [([1; 32], 0)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let address = Address::new([1; 32]); @@ -74,7 +71,7 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_p } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_balance() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_balance() { let initial_data = []; let mut state = V01State::new_with_genesis_accounts(&initial_data) .with_test_programs() @@ -97,7 +94,7 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_b } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_nonce() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonce() { let initial_data = []; let mut state = V01State::new_with_genesis_accounts(&initial_data) .with_test_programs() @@ -120,7 +117,7 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_n } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_data() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data() { let initial_data = []; let mut state = V01State::new_with_genesis_accounts(&initial_data) .with_test_programs() @@ -141,3 +138,29 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_d assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } + +#[test] +fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let sender_address = Address::new([1; 32]); + let receiver_address = Address::new([2; 32]); + let balance_to_move = 1; + let program_id = Program::simple_balance_transfer().id(); + assert_ne!( + state.get_account_by_address(&sender_address).program_owner, + program_id + ); + let message = public_transaction::Message::new( + program_id, + vec![sender_address, receiver_address], + vec![], + balance_to_move, + ); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} diff --git a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs new file mode 100644 index 0000000..1fe73ed --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs @@ -0,0 +1,20 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let balance: u128 = env::read(); + + let [sender_pre, receiver_pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let mut sender_post = sender_pre.account.clone(); + let mut receiver_post = receiver_pre.account.clone(); + sender_post.balance -= balance; + receiver_post.balance += balance; + + env::commit(&vec![sender_post, receiver_post]); +} +