add chained call test

This commit is contained in:
Sergio Chouhy 2025-10-29 15:34:11 -03:00
parent 3a27719392
commit 48fc643952
8 changed files with 103 additions and 9 deletions

View File

@ -25,7 +25,7 @@ pub struct ChainedCall {
pub struct ProgramOutput {
pub pre_states: Vec<AccountWithMetadata>,
pub post_states: Vec<Account>,
pub chained_call: Option<ChainedCall>
pub chained_call: Option<ChainedCall>,
}
pub fn read_nssa_inputs<T: DeserializeOwned>() -> ProgramInput<T> {
@ -42,7 +42,20 @@ pub fn write_nssa_outputs(pre_states: Vec<AccountWithMetadata>, post_states: Vec
let output = ProgramOutput {
pre_states,
post_states,
chained_call: None
chained_call: None,
};
env::commit(&output);
}
pub fn write_nssa_outputs_with_chained_call(
pre_states: Vec<AccountWithMetadata>,
post_states: Vec<Account>,
chained_call: Option<ChainedCall>,
) {
let output = ProgramOutput {
pre_states,
post_states,
chained_call,
};
env::commit(&output);
}

View File

@ -30,9 +30,9 @@ fn main() {
chained_call,
} = program_output;
// TODO: implement tail calls for privacy preserving transactions
// TODO: implement chained calls for privacy preserving transactions
if chained_call.is_some() {
panic!("Privacy preserving transactions do not support yet tail calls.")
panic!("Privacy preserving transactions do not support yet chained calls.")
}
// Check that there are no repeated account ids

View File

@ -1,6 +1,6 @@
use crate::program_methods::{AUTHENTICATED_TRANSFER_ELF, PINATA_ELF, TOKEN_ELF};
use nssa_core::{
account::{Account, AccountWithMetadata},
account::AccountWithMetadata,
program::{InstructionData, ProgramId, ProgramOutput},
};
@ -107,11 +107,11 @@ impl Program {
#[cfg(test)]
mod tests {
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
use program_methods::{
use crate::program_methods::{
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF,
TOKEN_ID,
};
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
use crate::program::Program;
@ -195,6 +195,15 @@ mod tests {
elf: BURNER_ELF.to_vec(),
}
}
pub fn chain_caller() -> Self {
use test_program_methods::{CHAIN_CALLER_ELF, CHAIN_CALLER_ID};
Program {
id: CHAIN_CALLER_ID,
elf: CHAIN_CALLER_ELF.to_vec(),
}
}
}
#[test]

View File

@ -114,7 +114,7 @@ impl PublicTransaction {
account_indices: (0..pre_states.len()).collect(),
};
for _ in 0..MAX_NUMBER_CHAINED_CALLS {
for _i in 0..MAX_NUMBER_CHAINED_CALLS {
// Check the `program_id` corresponds to a deployed program
let Some(program) = state.programs().get(&chained_call.program_id) else {
return Err(NssaError::InvalidInput("Unknown program".into()));
@ -139,7 +139,7 @@ impl PublicTransaction {
if validate_execution(
&program_output.pre_states,
&program_output.post_states,
message.program_id,
chained_call.program_id,
) {
for (pre, post) in program_output
.pre_states

View File

@ -263,6 +263,7 @@ pub mod tests {
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
account::{Account, AccountId, AccountWithMetadata, Nonce},
encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar},
program::ProgramId,
};
fn transfer_transaction(
@ -475,6 +476,7 @@ pub mod tests {
self.insert_program(Program::data_changer());
self.insert_program(Program::minter());
self.insert_program(Program::burner());
self.insert_program(Program::chain_caller());
self
}
@ -2045,4 +2047,38 @@ pub mod tests {
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_chained_call() {
let program = Program::chain_caller();
let key = PrivateKey::try_new([1; 32]).unwrap();
let address = Address::from(&PublicKey::new_from_private_key(&key));
let initial_balance = 100;
let initial_data = [(address, initial_balance)];
let mut state =
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
let from = address;
let from_key = key;
let to = Address::new([2; 32]);
let amount: u128 = 37;
let instruction: (u128, ProgramId) =
(amount, Program::authenticated_transfer_program().id());
let message = public_transaction::Message::try_new(
program.id(),
vec![to, from],
vec![0],
instruction,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
let tx = PublicTransaction::new(message, witness_set);
state.transition_from_public_transaction(&tx).unwrap();
let from_post = state.get_account_by_address(&from);
let to_post = state.get_account_by_address(&to);
assert_eq!(from_post.balance, initial_balance - amount);
assert_eq!(to_post.balance, amount);
}
}

View File

@ -1824,6 +1824,8 @@ name = "programs"
version = "0.1.0"
dependencies = [
"nssa-core",
"risc0-zkvm",
"serde",
]
[[package]]

View File

@ -6,4 +6,6 @@ edition = "2024"
[workspace]
[dependencies]
risc0-zkvm = { version = "3.0.3", features = ['std'] }
nssa-core = { path = "../../core" }
serde = { version = "1.0.219", default-features = false }

View File

@ -0,0 +1,32 @@
use nssa_core::program::{
ChainedCall, ProgramId, ProgramInput, read_nssa_inputs, write_nssa_outputs_with_chained_call,
};
use risc0_zkvm::serde::to_vec;
type Instruction = (u128, ProgramId);
fn main() {
let ProgramInput {
pre_states,
instruction: (balance, program_id),
} = read_nssa_inputs::<Instruction>();
let [sender_pre, receiver_pre] = match pre_states.try_into() {
Ok(array) => array,
Err(_) => return,
};
let instruction_data = to_vec(&balance).unwrap();
let chained_call = Some(ChainedCall {
program_id,
instruction_data,
account_indices: vec![1, 0],
});
write_nssa_outputs_with_chained_call(
vec![sender_pre.clone(), receiver_pre.clone()],
vec![sender_pre.account, receiver_pre.account],
chained_call,
);
}