add general instruction data

This commit is contained in:
Sergio Chouhy 2025-08-10 18:51:55 -03:00
parent 48fe6f2740
commit 102d2e13f9
16 changed files with 83 additions and 57 deletions

View File

@ -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<u32>;
pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8];
pub fn read_nssa_inputs<T: DeserializeOwned>() -> (Vec<AccountWithMetadata>, T) {
let pre_states: Vec<AccountWithMetadata> = 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

View File

@ -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<AccountWithMetadata> = env::read();
let balance_to_move: u128 = env::read();
let (input_accounts, balance_to_move) = read_nssa_inputs::<u128>();
// Continue only if input_accounts is an array of two elements
let [sender, receiver] = match input_accounts.try_into() {

View File

@ -10,4 +10,7 @@ pub enum NssaError {
#[error("Program violated execution rules")]
InvalidProgramBehavior,
#[error("Serialization error: {0}")]
InstructionDataSerializationError(String),
}

View File

@ -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<T: Serialize>(
instruction_data: T,
) -> Result<InstructionData, NssaError> {
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<Vec<Account>, 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(())
}

View File

@ -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<Address>,
pub(crate) nonces: Vec<Nonce>,
// TODO: change to Vec<u8> 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<Address>,
nonces: Vec<Nonce>,
instruction_data: u128,
instruction_data: InstructionData,
) -> Self {
Self {
program_id,

View File

@ -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.

View File

@ -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)
}

View File

@ -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);

View File

@ -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<AccountWithMetadata> = env::read();
let balance_to_burn: u128 = env::read();
let (input_accounts, balance_to_burn) = read_nssa_inputs::<InstructionData>();
let [pre] = match input_accounts.try_into() {
Ok(array) => array,
@ -16,5 +17,3 @@ fn main() {
env::commit(&vec![account_post]);
}

View File

@ -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<AccountWithMetadata> = env::read();
let _instruction_data: u128 = env::read();
let (input_accounts, _) = read_nssa_inputs::<InstructionData>();
let [pre] = match input_accounts.try_into() {
Ok(array) => array,

View File

@ -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<AccountWithMetadata> = env::read();
let _instruction_data: u128 = env::read();
let (input_accounts, _) = read_nssa_inputs::<InstructionData>();
let [pre] = match input_accounts.try_into() {
Ok(array) => array,
@ -14,4 +15,3 @@ fn main() {
env::commit(&vec![account_pre, Account::default()]);
}

View File

@ -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<AccountWithMetadata> = env::read();
let _instruction_data: u128 = env::read();
let (input_accounts, _) = read_nssa_inputs::<InstructionData>();
let [pre] = match input_accounts.try_into() {
Ok(array) => array,

View File

@ -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<AccountWithMetadata> = env::read();
let _instruction_data: u128 = env::read();
let (input_accounts, _) = read_nssa_inputs::<InstructionData>();
let [pre1, _] = match input_accounts.try_into() {
Ok(array) => array,

View File

@ -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<AccountWithMetadata> = env::read();
let _instruction_data: u128 = env::read();
let (input_accounts, _) = read_nssa_inputs::<InstructionData>();
let [pre] = match input_accounts.try_into() {
Ok(array) => array,

View File

@ -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<AccountWithMetadata> = env::read();
let _instruction_data: u128 = env::read();
let (input_accounts, _) = read_nssa_inputs::<InstructionData>();
let [pre] = match input_accounts.try_into() {
Ok(array) => array,

View File

@ -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<AccountWithMetadata> = env::read();
let balance: u128 = env::read();
let (input_accounts, balance) = read_nssa_inputs::<InstructionData>();
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]);
}