add test folder

This commit is contained in:
Sergio Chouhy 2025-08-10 00:53:53 -03:00
parent 4f7bde100c
commit bfdf039ef3
17 changed files with 225 additions and 200 deletions

View File

@ -31,7 +31,7 @@ pub fn produce_dummy_block(
} }
pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction {
let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); let program_id = nssa::program::Program::authenticated_transfer_program().id();
let addresses = vec![]; let addresses = vec![];
let nonces = vec![]; let nonces = vec![];
let instruction_data = 0; let instruction_data = 0;
@ -51,7 +51,7 @@ pub fn create_transaction_native_token_transfer(
) -> nssa::PublicTransaction { ) -> nssa::PublicTransaction {
let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)];
let nonces = vec![nonce]; let nonces = vec![nonce];
let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); let program_id = nssa::program::Program::authenticated_transfer_program().id();
let message = let message =
nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move);
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]);

View File

@ -6,8 +6,11 @@ edition = "2024"
[dependencies] [dependencies]
thiserror = "2.0.12" thiserror = "2.0.12"
risc0-zkvm = "2.3.1" risc0-zkvm = "2.3.1"
nssa-core = { path = "core"} nssa-core = { path = "core" }
program-methods = { path = "program_methods" } program-methods = { path = "program_methods" }
serde = "1.0.219" serde = "1.0.219"
serde_cbor = "0.11.2" serde_cbor = "0.11.2"
sha2 = "0.10.9" sha2 = "0.10.9"
[dev-dependencies]
test-program-methods = { path = "test_program_methods" }

View File

@ -12,7 +12,7 @@ fn main() {
// Continue only if input_accounts is an array of two elements // Continue only if input_accounts is an array of two elements
let [sender, receiver] = match input_accounts.try_into() { let [sender, receiver] = match input_accounts.try_into() {
Ok(array) => array, Ok(array) => array,
Err(_) => return, // silently return on bad input Err(_) => return,
}; };
// Continue only if the sender has authorized this operation // Continue only if the sender has authorized this operation

View File

@ -11,3 +11,6 @@ pub use signature::PrivateKey;
pub use signature::PublicKey; pub use signature::PublicKey;
pub use signature::Signature; pub use signature::Signature;
pub use state::V01State; pub use state::V01State;
#[cfg(test)]
mod tests;

View File

@ -8,15 +8,10 @@ use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor};
use crate::error::NssaError; use crate::error::NssaError;
pub struct Program { pub struct Program {
id: ProgramId, pub(crate) id: ProgramId,
elf: &'static [u8], pub(crate) elf: &'static [u8],
} }
pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program {
id: AUTHENTICATED_TRANSFER_ID,
elf: AUTHENTICATED_TRANSFER_ELF,
};
impl Program { impl Program {
pub fn id(&self) -> ProgramId { pub fn id(&self) -> ProgramId {
self.id self.id
@ -69,4 +64,25 @@ impl Program {
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
Ok(()) Ok(())
} }
pub fn authenticated_transfer_program() -> Self {
Self {
id: AUTHENTICATED_TRANSFER_ID,
elf: AUTHENTICATED_TRANSFER_ELF,
}
}
}
// Test utils
#[cfg(test)]
impl Program {
/// A program that changes the nonce of an account
pub fn nonce_changer_program() -> Self {
use test_program_methods::{NONCE_CHANGER_ELF, NONCE_CHANGER_ID};
Program {
id: NONCE_CHANGER_ID,
elf: NONCE_CHANGER_ELF,
}
}
} }

View File

@ -1,8 +1,5 @@
use crate::{ use crate::{
address::Address, address::Address, error::NssaError, program::Program, public_transaction::PublicTransaction,
error::NssaError,
program::{AUTHENTICATED_TRANSFER_PROGRAM, Program},
public_transaction::PublicTransaction,
}; };
use nssa_core::{ use nssa_core::{
account::{Account, AccountWithMetadata}, account::{Account, AccountWithMetadata},
@ -17,13 +14,14 @@ pub struct V01State {
impl V01State { impl V01State {
pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self { pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self {
let authenticated_transfer_program = Program::authenticated_transfer_program();
let public_state = initial_data let public_state = initial_data
.iter() .iter()
.copied() .copied()
.map(|(address_value, balance)| { .map(|(address_value, balance)| {
let account = Account { let account = Account {
balance, balance,
program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id(), program_owner: authenticated_transfer_program.id(),
..Account::default() ..Account::default()
}; };
let address = Address::new(address_value); let address = Address::new(address_value);
@ -32,8 +30,8 @@ impl V01State {
.collect(); .collect();
let builtin_programs = HashMap::from([( let builtin_programs = HashMap::from([(
AUTHENTICATED_TRANSFER_PROGRAM.id(), authenticated_transfer_program.id(),
AUTHENTICATED_TRANSFER_PROGRAM, authenticated_transfer_program,
)]); )]);
Self { Self {
@ -140,118 +138,15 @@ impl V01State {
} }
} }
// Test utils
#[cfg(test)] #[cfg(test)]
mod tests { impl V01State {
use super::*; /// Include test programs in the builtin programs map
use crate::{public_transaction, signature::PrivateKey}; pub fn with_test_programs(mut self) -> Self {
self.builtin_programs.insert(
fn transfer_transaction( Program::nonce_changer_program().id(),
from: Address, Program::nonce_changer_program(),
from_key: PrivateKey,
nonce: u128,
to: Address,
balance: u128,
) -> PublicTransaction {
let addresses = vec![from, to];
let nonces = vec![nonce];
let program_id = AUTHENTICATED_TRANSFER_PROGRAM.id();
let message = public_transaction::Message::new(program_id, addresses, nonces, balance);
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
PublicTransaction::new(message, witness_set)
}
#[test]
fn transition_from_authenticated_transfer_program_invocation_default_account_destination() {
let initial_data = [([1; 32], 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = Address::new(initial_data[0].0);
let from_key = PrivateKey(1);
let to = Address::new([2; 32]);
assert_eq!(state.get_account_by_address(&to), Account::default());
let balance_to_move = 5;
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&from).balance, 95);
assert_eq!(state.get_account_by_address(&to).balance, 5);
assert_eq!(state.get_account_by_address(&from).nonce, 1);
assert_eq!(state.get_account_by_address(&to).nonce, 0);
}
#[test]
fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() {
let initial_data = [([1; 32], 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = Address::new(initial_data[0].0);
let from_key = PrivateKey(1);
let to = Address::new([2; 32]);
let balance_to_move = 101;
assert!(state.get_account_by_address(&from).balance < balance_to_move);
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
let result = state.transition_from_public_transaction(&tx);
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
assert_eq!(state.get_account_by_address(&from).balance, 100);
assert_eq!(state.get_account_by_address(&to).balance, 0);
assert_eq!(state.get_account_by_address(&from).nonce, 0);
assert_eq!(state.get_account_by_address(&to).nonce, 0);
}
#[test]
fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() {
let initial_data = [([1; 32], 100), ([99; 32], 200)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = Address::new(initial_data[1].0);
let from_key = PrivateKey(99);
let to = Address::new(initial_data[0].0);
assert_ne!(state.get_account_by_address(&to), Account::default());
let balance_to_move = 8;
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&from).balance, 192);
assert_eq!(state.get_account_by_address(&to).balance, 108);
assert_eq!(state.get_account_by_address(&from).nonce, 1);
assert_eq!(state.get_account_by_address(&to).nonce, 0);
}
#[test]
fn transition_from_chained_authenticated_transfer_program_invocations() {
let initial_data = [([1; 32], 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let address_1 = Address::new(initial_data[0].0);
let key_1 = PrivateKey(1);
let address_2 = Address::new([2; 32]);
let key_2 = PrivateKey(2);
let address_3 = Address::new([3; 32]);
let balance_to_move = 5;
let tx = transfer_transaction(
address_1.clone(),
key_1,
0,
address_2.clone(),
balance_to_move,
); );
state.transition_from_public_transaction(&tx).unwrap(); self
let balance_to_move = 3;
let tx = transfer_transaction(
address_2.clone(),
key_2,
0,
address_3.clone(),
balance_to_move,
);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&address_1).balance, 95);
assert_eq!(state.get_account_by_address(&address_2).balance, 2);
assert_eq!(state.get_account_by_address(&address_3).balance, 3);
assert_eq!(state.get_account_by_address(&address_1).nonce, 1);
assert_eq!(state.get_account_by_address(&address_2).nonce, 1);
assert_eq!(state.get_account_by_address(&address_3).nonce, 0);
} }
} }

1
nssa/src/tests/mod.rs Normal file
View File

@ -0,0 +1 @@
mod state_tests;

View File

@ -0,0 +1,131 @@
use crate::{
Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction,
signature::PrivateKey,
};
use nssa_core::account::Account;
#[test]
fn test_programs_cant_change_account_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, 5);
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)));
}
fn transfer_transaction(
from: Address,
from_key: PrivateKey,
nonce: u128,
to: Address,
balance: u128,
) -> PublicTransaction {
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 witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
PublicTransaction::new(message, witness_set)
}
#[test]
fn transition_from_authenticated_transfer_program_invocation_default_account_destination() {
let initial_data = [([1; 32], 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = Address::new(initial_data[0].0);
let from_key = PrivateKey(1);
let to = Address::new([2; 32]);
assert_eq!(state.get_account_by_address(&to), Account::default());
let balance_to_move = 5;
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&from).balance, 95);
assert_eq!(state.get_account_by_address(&to).balance, 5);
assert_eq!(state.get_account_by_address(&from).nonce, 1);
assert_eq!(state.get_account_by_address(&to).nonce, 0);
}
#[test]
fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() {
let initial_data = [([1; 32], 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = Address::new(initial_data[0].0);
let from_key = PrivateKey(1);
let to = Address::new([2; 32]);
let balance_to_move = 101;
assert!(state.get_account_by_address(&from).balance < balance_to_move);
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
let result = state.transition_from_public_transaction(&tx);
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
assert_eq!(state.get_account_by_address(&from).balance, 100);
assert_eq!(state.get_account_by_address(&to).balance, 0);
assert_eq!(state.get_account_by_address(&from).nonce, 0);
assert_eq!(state.get_account_by_address(&to).nonce, 0);
}
#[test]
fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() {
let initial_data = [([1; 32], 100), ([99; 32], 200)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = Address::new(initial_data[1].0);
let from_key = PrivateKey(99);
let to = Address::new(initial_data[0].0);
assert_ne!(state.get_account_by_address(&to), Account::default());
let balance_to_move = 8;
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&from).balance, 192);
assert_eq!(state.get_account_by_address(&to).balance, 108);
assert_eq!(state.get_account_by_address(&from).nonce, 1);
assert_eq!(state.get_account_by_address(&to).nonce, 0);
}
#[test]
fn transition_from_chained_authenticated_transfer_program_invocations() {
let initial_data = [([1; 32], 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let address_1 = Address::new(initial_data[0].0);
let key_1 = PrivateKey(1);
let address_2 = Address::new([2; 32]);
let key_2 = PrivateKey(2);
let address_3 = Address::new([3; 32]);
let balance_to_move = 5;
let tx = transfer_transaction(
address_1.clone(),
key_1,
0,
address_2.clone(),
balance_to_move,
);
state.transition_from_public_transaction(&tx).unwrap();
let balance_to_move = 3;
let tx = transfer_transaction(
address_2.clone(),
key_2,
0,
address_3.clone(),
balance_to_move,
);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&address_1).balance, 95);
assert_eq!(state.get_account_by_address(&address_2).balance, 2);
assert_eq!(state.get_account_by_address(&address_3).balance, 3);
assert_eq!(state.get_account_by_address(&address_1).nonce, 1);
assert_eq!(state.get_account_by_address(&address_2).nonce, 1);
assert_eq!(state.get_account_by_address(&address_3).nonce, 0);
}

View File

@ -0,0 +1,11 @@
[package]
name = "test-program-methods"
version = "0.1.0"
edition = "2021"
[build-dependencies]
risc0-build = { version = "2.3.1" }
[package.metadata.risc0]
methods = ["guest"]

View File

@ -0,0 +1,3 @@
fn main() {
risc0_build::embed_methods();
}

View File

@ -0,0 +1,11 @@
[package]
name = "programs"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] }
nssa-core = {path = "../../core"}

View File

@ -0,0 +1,18 @@
use nssa_core::account::AccountWithMetadata;
use risc0_zkvm::guest::env;
fn main() {
let input_accounts: Vec<AccountWithMetadata> = 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.nonce += 1;
env::commit(&vec![account_post]);
}

View File

@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/methods.rs"));

View File

@ -188,55 +188,11 @@ mod tests {
setup_sequencer_config_variable_initial_accounts(initial_accounts) setup_sequencer_config_variable_initial_accounts(initial_accounts)
} }
// fn create_dummy_transaction() -> nssa::PublicTransaction {
// let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id;
// let addresses = vec![];
// let nonces = vec![];
// let instruction_data = 0;
// let message =
// nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data);
// let private_key = nssa::PrivateKey::new(1);
// let witness_set =
// nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]);
// nssa::PublicTransaction::new(message, witness_set)
// }
//
// fn create_dummy_transaction_native_token_transfer(
// from: [u8; 32],
// nonce: u128,
// to: [u8; 32],
// balance_to_move: u128,
// signing_key: nssa::PrivateKey,
// ) -> nssa::PublicTransaction {
// let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)];
// let nonces = vec![nonce];
// let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id;
// let message =
// nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move);
// let witness_set =
// nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]);
// nssa::PublicTransaction::new(message, witness_set)
// }
fn create_signing_key_for_account1() -> nssa::PrivateKey { fn create_signing_key_for_account1() -> nssa::PrivateKey {
// let pub_sign_key_acc1 = [
// 133, 143, 177, 187, 252, 66, 237, 236, 234, 252, 244, 138, 5, 151, 3, 99, 217, 231,
// 112, 217, 77, 211, 58, 218, 176, 68, 99, 53, 152, 228, 198, 190,
// ];
//
// let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc1);
// SigningKey::from_bytes(field_bytes).unwrap()
nssa::PrivateKey::new(1) nssa::PrivateKey::new(1)
} }
fn create_signing_key_for_account2() -> nssa::PrivateKey { fn create_signing_key_for_account2() -> nssa::PrivateKey {
// let pub_sign_key_acc2 = [
// 54, 90, 62, 225, 71, 225, 228, 148, 143, 53, 210, 23, 137, 158, 171, 156, 48, 7, 139,
// 52, 117, 242, 214, 7, 99, 29, 122, 184, 59, 116, 144, 107,
// ];
//
// let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc2);
// SigningKey::from_bytes(field_bytes).unwrap()
nssa::PrivateKey::new(2) nssa::PrivateKey::new(2)
} }

View File

@ -78,31 +78,7 @@ mod tests {
use super::*; use super::*;
use tempfile::tempdir; use tempfile::tempdir;
//
// fn create_dummy_block_with_transaction(block_id: u64) -> (Block, nssa::PublicTransaction) {
// let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id;
// let addresses = vec![];
// let nonces = vec![];
// let instruction_data = 0;
// let message =
// nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data);
// let private_key = nssa::PrivateKey::new(1);
// let witness_set =
// nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]);
// let tx = nssa::PublicTransaction::new(message, witness_set);
// (
// Block {
// block_id,
// prev_block_id: block_id - 1,
// prev_block_hash: [0; 32],
// hash: [1; 32],
// transactions: vec![tx.clone()],
// data: vec![],
// },
// tx,
// )
// }
//
#[test] #[test]
fn test_get_transaction_by_hash() { fn test_get_transaction_by_hash() {
let temp_dir = tempdir().unwrap(); let temp_dir = tempdir().unwrap();

View File

@ -498,7 +498,7 @@ mod tests {
], ],
"instruction_data": 10, "instruction_data": 10,
"nonces": [0], "nonces": [0],
"program_id": nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(), "program_id": nssa::program::Program::authenticated_transfer_program().id(),
}, },
"witness_set": { "witness_set": {
"signatures_and_public_keys": [ "signatures_and_public_keys": [

View File

@ -86,7 +86,7 @@ impl NodeCore {
if let Some(account) = account { if let Some(account) = account {
let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)];
let nonces = vec![nonce]; let nonces = vec![nonce];
let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); let program_id = nssa::program::Program::authenticated_transfer_program().id();
let message = nssa::public_transaction::Message::new( let message = nssa::public_transaction::Message::new(
program_id, program_id,
addresses, addresses,