diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 8874773..744c1dc 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -34,6 +34,7 @@ impl AccountPostState { claim: false, } } + pub fn new_claimed(account: Account) -> Self { Self { account, @@ -191,5 +192,4 @@ mod tests { assert_eq!(account, account_post_state.account); assert!(!account_post_state.requires_claim()); } - } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index cef7791..4c3f8ac 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -2162,4 +2162,56 @@ pub mod tests { Err(NssaError::MaxChainedCallsDepthExceeded) )); } + + #[test] + fn test_claiming_mechanism_within_chain_call() { + // This test calls the authenticated transfer program through the chain_caller program. + // The transfer is made from an initialized sender to an uninitialized recipient. And + // it is expected that the recipient account is claimed by the authenticated transfer + // program and not the chained_caller program. + let chain_caller = Program::chain_caller(); + let auth_transfer = Program::authenticated_transfer_program(); + let key = PrivateKey::try_new([1; 32]).unwrap(); + let account_id = AccountId::from(&PublicKey::new_from_private_key(&key)); + let initial_balance = 100; + let initial_data = [(account_id, initial_balance)]; + let mut state = + V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); + let from = account_id; + let from_key = key; + let to = AccountId::new([2; 32]); + let amount: u128 = 37; + + // Check the recipient is an uninitialized account + assert_eq!(state.get_account_by_id(&to), Account::default()); + + let expected_to_post = Account { + // The expected program owner is the authenticated transfer program + program_owner: auth_transfer.id(), + balance: amount, + ..Account::default() + }; + + // The transaction executes the chain_caller program, which internally calls the + // authenticated_transfer program + let instruction: (u128, ProgramId, u32) = + (amount, Program::authenticated_transfer_program().id(), 1); + let message = public_transaction::Message::try_new( + chain_caller.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); + + state.transition_from_public_transaction(&tx).unwrap(); + + let from_post = state.get_account_by_id(&from); + let to_post = state.get_account_by_id(&to); + assert_eq!(from_post.balance, initial_balance - amount); + assert_eq!(to_post, expected_to_post); + } }