From 3594c846dab7bb2557de260d3883f74f13468a20 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 10:19:59 -0300 Subject: [PATCH] add test program owner changer --- nssa/src/program.rs | 10 ++ nssa/src/state.rs | 29 ++++++ nssa/src/tests/valid_execution_tests.rs | 93 ++++++++++++++++++- .../guest/src/bin/program_owner_changer.rs | 18 ++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 nssa/test_program_methods/guest/src/bin/program_owner_changer.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 45a42b7..2307da2 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -105,4 +105,14 @@ impl Program { elf: MISSING_OUTPUT_ELF, } } + + /// A program that changes the program owner of an account to [0, 1, 2, 3, 4, 5, 6, 7] + pub fn program_owner_changer() -> Self { + use test_program_methods::{PROGRAM_OWNER_CHANGER_ELF, PROGRAM_OWNER_CHANGER_ID}; + + Program { + id: PROGRAM_OWNER_CHANGER_ID, + elf: PROGRAM_OWNER_CHANGER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 195727f..278fab4 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -149,6 +149,35 @@ impl V01State { self.insert_program(Program::nonce_changer_program()); self.insert_program(Program::extra_output_program()); self.insert_program(Program::missing_output_program()); + self.insert_program(Program::program_owner_changer()); + self + } + + pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self { + let account_with_default_values_except_balance = Account { + balance: 100, + ..Account::default() + }; + let account_with_default_values_except_nonce = Account { + nonce: 37, + ..Account::default() + }; + let account_with_default_values_except_data = Account { + data: vec![0xca, 0xfe], + ..Account::default() + }; + self.public_state.insert( + Address::new([255; 32]), + account_with_default_values_except_balance, + ); + self.public_state.insert( + Address::new([254; 32]), + account_with_default_values_except_nonce, + ); + self.public_state.insert( + Address::new([253; 32]), + account_with_default_values_except_data, + ); self } } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index f010c06..c2781a7 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -1,3 +1,5 @@ +use nssa_core::account::Account; + use crate::{ Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, }; @@ -34,7 +36,6 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } - #[test] fn test_program_should_fail_with_missing_output_accounts() { let initial_data = [([1; 32], 100)]; @@ -50,3 +51,93 @@ fn test_program_should_fail_with_missing_output_accounts() { assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } + +#[test] +fn test_program_should_fail_if_it_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]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in the program owner field + assert_ne!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + 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))); +} + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_balance() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([255; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_ne!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + 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))); +} + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_nonce() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([254; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_ne!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + 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))); +} + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_data() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([253; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_ne!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + 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/program_owner_changer.rs b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs new file mode 100644 index 0000000..156b4a4 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs @@ -0,0 +1,18 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + let mut account_post = account_pre.clone(); + account_post.program_owner = [0, 1, 2, 3, 4, 5, 6, 7]; + + env::commit(&vec![account_post]); +}