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

View File

@ -6,8 +6,11 @@ edition = "2024"
[dependencies]
thiserror = "2.0.12"
risc0-zkvm = "2.3.1"
nssa-core = { path = "core"}
nssa-core = { path = "core" }
program-methods = { path = "program_methods" }
serde = "1.0.219"
serde_cbor = "0.11.2"
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
let [sender, receiver] = match input_accounts.try_into() {
Ok(array) => array,
Err(_) => return, // silently return on bad input
Err(_) => return,
};
// 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::Signature;
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;
pub struct Program {
id: ProgramId,
elf: &'static [u8],
pub(crate) id: ProgramId,
pub(crate) elf: &'static [u8],
}
pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program {
id: AUTHENTICATED_TRANSFER_ID,
elf: AUTHENTICATED_TRANSFER_ELF,
};
impl Program {
pub fn id(&self) -> ProgramId {
self.id
@ -69,4 +64,25 @@ impl Program {
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
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::{
address::Address,
error::NssaError,
program::{AUTHENTICATED_TRANSFER_PROGRAM, Program},
public_transaction::PublicTransaction,
address::Address, error::NssaError, program::Program, public_transaction::PublicTransaction,
};
use nssa_core::{
account::{Account, AccountWithMetadata},
@ -17,13 +14,14 @@ pub struct V01State {
impl V01State {
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
.iter()
.copied()
.map(|(address_value, balance)| {
let account = Account {
balance,
program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id(),
program_owner: authenticated_transfer_program.id(),
..Account::default()
};
let address = Address::new(address_value);
@ -32,8 +30,8 @@ impl V01State {
.collect();
let builtin_programs = HashMap::from([(
AUTHENTICATED_TRANSFER_PROGRAM.id(),
AUTHENTICATED_TRANSFER_PROGRAM,
authenticated_transfer_program.id(),
authenticated_transfer_program,
)]);
Self {
@ -140,118 +138,15 @@ impl V01State {
}
}
// Test utils
#[cfg(test)]
mod tests {
use super::*;
use crate::{public_transaction, signature::PrivateKey};
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 = 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,
impl V01State {
/// Include test programs in the builtin programs map
pub fn with_test_programs(mut self) -> Self {
self.builtin_programs.insert(
Program::nonce_changer_program().id(),
Program::nonce_changer_program(),
);
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);
self
}
}

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)
}
// 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 {
// 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)
}
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)
}

View File

@ -78,31 +78,7 @@ mod tests {
use super::*;
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]
fn test_get_transaction_by_hash() {
let temp_dir = tempdir().unwrap();

View File

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

View File

@ -86,7 +86,7 @@ impl NodeCore {
if let Some(account) = account {
let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)];
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(
program_id,
addresses,