add max depth reached error for chained calls

This commit is contained in:
Sergio Chouhy 2025-11-12 19:55:02 -03:00
parent a94440fa1f
commit c7b415b2f4
4 changed files with 63 additions and 15 deletions

View File

@ -54,4 +54,7 @@ pub enum NssaError {
#[error("Program already exists")]
ProgramAlreadyExists,
#[error("Chain of calls too long")]
MaxChainedCallsDepthExceeded,
}

View File

@ -11,6 +11,7 @@ use crate::{
V02State,
error::NssaError,
public_transaction::{Message, WitnessSet},
state::MAX_NUMBER_CHAINED_CALLS,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@ -18,7 +19,6 @@ pub struct PublicTransaction {
message: Message,
witness_set: WitnessSet,
}
const MAX_NUMBER_CHAINED_CALLS: usize = 10;
impl PublicTransaction {
pub fn new(message: Message, witness_set: WitnessSet) -> Self {
@ -183,7 +183,11 @@ impl PublicTransaction {
};
}
Ok(state_diff)
if chained_calls.is_empty() {
Ok(state_diff)
} else {
Err(NssaError::MaxChainedCallsDepthExceeded)
}
}
}

View File

@ -10,6 +10,8 @@ use nssa_core::{
};
use std::collections::{HashMap, HashSet};
pub const MAX_NUMBER_CHAINED_CALLS: usize = 10;
pub(crate) struct CommitmentSet {
merkle_tree: MerkleTree,
commitments: HashMap<Commitment, usize>,
@ -251,6 +253,7 @@ pub mod tests {
program::Program,
public_transaction,
signature::PrivateKey,
state::MAX_NUMBER_CHAINED_CALLS,
};
use nssa_core::{
@ -2079,7 +2082,7 @@ pub mod tests {
}
#[test]
fn test_chained_call() {
fn test_chained_call_succeeds() {
let program = Program::chain_caller();
let key = PrivateKey::try_new([1; 32]).unwrap();
let address = Address::from(&PublicKey::new_from_private_key(&key));
@ -2091,8 +2094,8 @@ pub mod tests {
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 instruction: (u128, ProgramId, u32) =
(amount, Program::authenticated_transfer_program().id(), 2);
let expected_to_post = Account {
program_owner: Program::chain_caller().id(),
@ -2118,4 +2121,40 @@ pub mod tests {
assert_eq!(from_post.balance, initial_balance - 2 * amount);
assert_eq!(to_post, expected_to_post);
}
#[test]
fn test_execution_fails_if_chained_calls_exceeds_depth() {
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 = 0;
let instruction: (u128, ProgramId, u32) = (
amount,
Program::authenticated_transfer_program().id(),
MAX_NUMBER_CHAINED_CALLS as u32 + 1,
);
let message = public_transaction::Message::try_new(
program.id(),
vec![to, from], //The chain_caller program permutes the account order in the chain call
vec![0],
instruction,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
let tx = PublicTransaction::new(message, witness_set);
let result = state.transition_from_public_transaction(&tx);
assert!(matches!(
result,
Err(NssaError::MaxChainedCallsDepthExceeded)
));
}
}

View File

@ -3,14 +3,14 @@ use nssa_core::program::{
};
use risc0_zkvm::serde::to_vec;
type Instruction = (u128, ProgramId);
type Instruction = (u128, ProgramId, u32);
/// A program that calls another program twice.
/// A program that calls another program `num_chain_calls` times.
/// It permutes the order of the input accounts on the subsequent call
fn main() {
let ProgramInput {
pre_states,
instruction: (balance, program_id),
instruction: (balance, program_id, num_chain_calls),
} = read_nssa_inputs::<Instruction>();
let [sender_pre, receiver_pre] = match pre_states.try_into() {
@ -20,19 +20,21 @@ fn main() {
let instruction_data = to_vec(&balance).unwrap();
let chained_call = vec![
let mut chained_call = vec![
ChainedCall {
program_id,
instruction_data: instruction_data.clone(),
account_indices: vec![0, 1],
},
ChainedCall {
program_id,
instruction_data,
account_indices: vec![1, 0], // <- Account order permutation here
},
};
num_chain_calls as usize - 1
];
chained_call.push(ChainedCall {
program_id,
instruction_data,
account_indices: vec![1, 0], // <- Account order permutation here
});
write_nssa_outputs_with_chained_call(
vec![sender_pre.clone(), receiver_pre.clone()],
vec![sender_pre.account, receiver_pre.account],