From 102d2e13f98d585843dfb46fd6e7042a870ce94a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 18:51:55 -0300 Subject: [PATCH] add general instruction data --- nssa/core/src/program.rs | 9 +++++++ .../guest/src/bin/authenticated_transfer.rs | 5 ++-- nssa/src/error.rs | 3 +++ nssa/src/program.rs | 21 +++++++++------ nssa/src/public_transaction/message.rs | 10 ++++--- nssa/src/public_transaction/mod.rs | 2 +- nssa/src/tests/state_tests.rs | 3 ++- nssa/src/tests/valid_execution_tests.rs | 27 ++++++++++--------- .../guest/src/bin/burner.rs | 9 +++---- .../guest/src/bin/data_changer.rs | 7 ++--- .../guest/src/bin/extra_output.rs | 8 +++--- .../guest/src/bin/minter.rs | 7 ++--- .../guest/src/bin/missing_output.rs | 7 ++--- .../guest/src/bin/nonce_changer.rs | 7 ++--- .../guest/src/bin/program_owner_changer.rs | 7 ++--- .../guest/src/bin/simple_balance_transfer.rs | 8 +++--- 16 files changed, 83 insertions(+), 57 deletions(-) diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index d3d253ae..9b99a61a 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -1,8 +1,17 @@ use crate::account::{Account, AccountWithMetadata}; +use risc0_zkvm::serde::Deserializer; +use risc0_zkvm::{DeserializeOwned, guest::env}; pub type ProgramId = [u32; 8]; +pub type InstructionData = Vec; pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; +pub fn read_nssa_inputs() -> (Vec, T) { + let pre_states: Vec = env::read(); + let words: InstructionData = env::read(); + let instruction_data = T::deserialize(&mut Deserializer::new(words.as_ref())).unwrap(); + (pre_states, instruction_data) +} /// Validates well-behaved program execution /// /// # Parameters diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index 8ed3fc4f..e6adbb1f 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -1,4 +1,4 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; /// A transfer of balance program. @@ -6,8 +6,7 @@ use risc0_zkvm::guest::env; fn main() { // Read input accounts. // It is expected to receive only two accounts: [sender_account, receiver_account] - let input_accounts: Vec = env::read(); - let balance_to_move: u128 = env::read(); + let (input_accounts, balance_to_move) = read_nssa_inputs::(); // Continue only if input_accounts is an array of two elements let [sender, receiver] = match input_accounts.try_into() { diff --git a/nssa/src/error.rs b/nssa/src/error.rs index decc248f..eefbc886 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -10,4 +10,7 @@ pub enum NssaError { #[error("Program violated execution rules")] InvalidProgramBehavior, + + #[error("Serialization error: {0}")] + InstructionDataSerializationError(String), } diff --git a/nssa/src/program.rs b/nssa/src/program.rs index d1ec4c3b..055b7798 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -1,9 +1,10 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{DEFAULT_PROGRAM_ID, ProgramId}, + program::{DEFAULT_PROGRAM_ID, InstructionData, ProgramId}, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; -use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; +use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec}; +use serde::{Deserialize, Serialize}; use crate::error::NssaError; @@ -17,10 +18,17 @@ impl Program { self.id } + pub fn serialize_instruction_data( + instruction_data: T, + ) -> Result { + to_vec(&instruction_data) + .map_err(|e| NssaError::InstructionDataSerializationError(e.to_string())) + } + pub(crate) fn execute( &self, pre_states: &[AccountWithMetadata], - instruction_data: u128, + instruction_data: &InstructionData, ) -> Result, NssaError> { // Write inputs to the program let mut env_builder = ExecutorEnv::builder(); @@ -52,15 +60,12 @@ impl Program { /// Writes inputs to `env_builder` in the order expected by the programs fn write_inputs( pre_states: &[AccountWithMetadata], - instruction_data: u128, + instruction_data: &[u32], env_builder: &mut ExecutorEnvBuilder, ) -> Result<(), NssaError> { let pre_states = pre_states.to_vec(); env_builder - .write(&pre_states) - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - env_builder - .write(&instruction_data) + .write(&(pre_states, instruction_data)) .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; Ok(()) } diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 9dd7f2e8..b62e3ad4 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -1,4 +1,7 @@ -use nssa_core::{account::Nonce, program::ProgramId}; +use nssa_core::{ + account::Nonce, + program::{InstructionData, ProgramId}, +}; use serde::{Deserialize, Serialize}; use crate::Address; @@ -8,8 +11,7 @@ pub struct Message { pub(crate) program_id: ProgramId, pub(crate) addresses: Vec
, pub(crate) nonces: Vec, - // TODO: change to Vec for general programs - pub(crate) instruction_data: u128, + pub(crate) instruction_data: InstructionData, } impl Message { @@ -17,7 +19,7 @@ impl Message { program_id: ProgramId, addresses: Vec
, nonces: Vec, - instruction_data: u128, + instruction_data: InstructionData, ) -> Self { Self { program_id, diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 0ff56a9e..64e051b4 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -108,7 +108,7 @@ impl PublicTransaction { }; // // Execute program - let post_states = program.execute(&pre_states, message.instruction_data)?; + let post_states = program.execute(&pre_states, &message.instruction_data)?; // Verify execution corresponds to a well-behaved program. // See the # Programs section for the definition of the `validate_execution` method. diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index 853b684f..c90f8e94 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -14,7 +14,8 @@ fn transfer_transaction( let addresses = vec![from, to]; let nonces = vec![nonce]; let program_id = Program::authenticated_transfer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, balance); + let instruction_data = Program::serialize_instruction_data(&balance).unwrap(); + let message = public_transaction::Message::new(program_id, addresses, nonces, instruction_data); let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 306ca836..6ef32d95 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -10,7 +10,7 @@ fn test_program_should_fail_if_modifies_nonces() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -25,7 +25,7 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -40,7 +40,7 @@ fn test_program_should_fail_with_missing_output_accounts() { 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 program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -61,7 +61,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_prog 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 message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -84,7 +84,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_bala 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 message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -107,7 +107,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonc 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 message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -130,7 +130,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data 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 message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -145,8 +145,9 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { 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 balance_to_move: u128 = 1; let program_id = Program::simple_balance_transfer().id(); + let instruction_data = Program::serialize_instruction_data(balance_to_move).unwrap(); assert_ne!( state.get_account_by_address(&sender_address).program_owner, program_id @@ -155,12 +156,13 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { program_id, vec![sender_address, receiver_address], vec![], - balance_to_move, + instruction_data, ); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); + println!("{:?}", result); assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } @@ -178,7 +180,7 @@ fn test_program_should_fail_if_modifies_data_of_non_owned_account() { state.get_account_by_address(&address).program_owner, program_id ); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -194,7 +196,7 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { let address = Address::new([1; 32]); let program_id = Program::minter().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -218,8 +220,9 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { let balance_to_burn = 1; assert!(state.get_account_by_address(&address).balance > balance_to_burn); + let instruction_data = Program::serialize_instruction_data(balance_to_burn).unwrap(); let message = - public_transaction::Message::new(program_id, vec![address], vec![], balance_to_burn); + public_transaction::Message::new(program_id, vec![address], vec![], instruction_data); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); diff --git a/nssa/test_program_methods/guest/src/bin/burner.rs b/nssa/test_program_methods/guest/src/bin/burner.rs index 8ce1fb63..4d89bffb 100644 --- a/nssa/test_program_methods/guest/src/bin/burner.rs +++ b/nssa/test_program_methods/guest/src/bin/burner.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = u128; + fn main() { - let input_accounts: Vec = env::read(); - let balance_to_burn: u128 = env::read(); + let (input_accounts, balance_to_burn) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, @@ -16,5 +17,3 @@ fn main() { env::commit(&vec![account_post]); } - - diff --git a/nssa/test_program_methods/guest/src/bin/data_changer.rs b/nssa/test_program_methods/guest/src/bin/data_changer.rs index 9fd38508..bdafe32c 100644 --- a/nssa/test_program_methods/guest/src/bin/data_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/data_changer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/extra_output.rs b/nssa/test_program_methods/guest/src/bin/extra_output.rs index f15c0976..815758c7 100644 --- a/nssa/test_program_methods/guest/src/bin/extra_output.rs +++ b/nssa/test_program_methods/guest/src/bin/extra_output.rs @@ -1,9 +1,10 @@ -use nssa_core::account::{Account, AccountWithMetadata}; +use nssa_core::{account::Account, program::read_nssa_inputs}; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, @@ -14,4 +15,3 @@ fn main() { env::commit(&vec![account_pre, Account::default()]); } - diff --git a/nssa/test_program_methods/guest/src/bin/minter.rs b/nssa/test_program_methods/guest/src/bin/minter.rs index 7c4ce453..c3ca9973 100644 --- a/nssa/test_program_methods/guest/src/bin/minter.rs +++ b/nssa/test_program_methods/guest/src/bin/minter.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::{account::Account, program::read_nssa_inputs}; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/missing_output.rs b/nssa/test_program_methods/guest/src/bin/missing_output.rs index 7857ea55..a24b2b98 100644 --- a/nssa/test_program_methods/guest/src/bin/missing_output.rs +++ b/nssa/test_program_methods/guest/src/bin/missing_output.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre1, _] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs index 08007f6e..e2b8f14d 100644 --- a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, 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 index 156b4a4e..8e04e530 100644 --- a/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, 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 index 1fe73ed8..7f6dbe87 100644 --- a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs +++ b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = u128; + fn main() { - let input_accounts: Vec = env::read(); - let balance: u128 = env::read(); + let (input_accounts, balance) = read_nssa_inputs::(); let [sender_pre, receiver_pre] = match input_accounts.try_into() { Ok(array) => array, @@ -17,4 +18,3 @@ fn main() { env::commit(&vec![sender_post, receiver_post]); } -