diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 1690445..5ca3dab 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -791,6 +791,36 @@ pub async fn test_pinata() { info!("Success!"); } +pub async fn test_authenticated_transfer_initialize_function() { + info!("test initialize account for authenticated transfer"); + let command = Command::AuthenticatedTransferInitializePublicAccount {}; + + let SubcommandReturnValue::RegisterAccount { addr } = + wallet::execute_subcommand(command).await.unwrap() + else { + panic!("Error creating account"); + }; + + info!("Checking correct execution"); + let wallet_config = fetch_config().unwrap(); + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + let account = seq_client + .get_account(addr.to_string()) + .await + .unwrap() + .account; + + let expected_program_owner = Program::authenticated_transfer_program().id(); + let expected_nonce = 1; + let expected_balance = 0; + + assert_eq!(account.program_owner, expected_program_owner); + assert_eq!(account.balance, expected_balance); + assert_eq!(account.nonce, expected_nonce); + assert!(account.data.is_empty()); + info!("Success!"); +} + macro_rules! test_cleanup_wrap { ($home_dir:ident, $test_func:ident) => {{ let res = pre_test($home_dir.clone()).await.unwrap(); @@ -871,6 +901,9 @@ pub async fn main_tests_runner() -> Result<()> { "test_pinata" => { test_cleanup_wrap!(home_dir, test_pinata); } + "test_authenticated_transfer_initialize_function" => { + test_cleanup_wrap!(home_dir, test_authenticated_transfer_initialize_function); + } "all" => { test_cleanup_wrap!(home_dir, test_success_move_to_another_account); test_cleanup_wrap!(home_dir, test_success); @@ -902,32 +935,7 @@ pub async fn main_tests_runner() -> Result<()> { test_success_private_transfer_to_another_owned_account_claiming_path ); test_cleanup_wrap!(home_dir, test_pinata); - } - "all_private" => { - test_cleanup_wrap!( - home_dir, - test_success_private_transfer_to_another_owned_account - ); - test_cleanup_wrap!( - home_dir, - test_success_private_transfer_to_another_foreign_account - ); - test_cleanup_wrap!( - home_dir, - test_success_deshielded_transfer_to_another_account - ); - test_cleanup_wrap!( - home_dir, - test_success_shielded_transfer_to_another_owned_account - ); - test_cleanup_wrap!( - home_dir, - test_success_shielded_transfer_to_another_foreign_account - ); - test_cleanup_wrap!( - home_dir, - test_success_private_transfer_to_another_owned_account_claiming_path - ); + test_cleanup_wrap!(home_dir, test_authenticated_transfer_initialize_function); } _ => { anyhow::bail!("Unknown test name"); diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index 210e3f0..31d606f 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -1,15 +1,32 @@ -use nssa_core::program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}; +use nssa_core::{ + account::{Account, AccountWithMetadata}, + program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}, +}; -/// A transfer of balance program. -/// To be used both in public and private contexts. -fn main() { - // Read input accounts. - // It is expected to receive only two accounts: [sender_account, receiver_account] - let ProgramInput { - pre_states, - instruction: balance_to_move, - } = read_nssa_inputs(); +fn initialize_account(pre_states: Vec) { + // Continue only if input_accounts is an array of one element + let [pre_state] = match pre_states.try_into() { + Ok(array) => array, + Err(_) => return, + }; + let account_to_claim = pre_state.account.clone(); + let is_authorized = pre_state.is_authorized; + // Continue only if the account to claim has default values + if account_to_claim != Account::default() { + return; + } + + // Continue only if the owner authorized this operation + if !is_authorized { + return; + } + + // Noop will result in account being claimed for this program + write_nssa_outputs(vec![pre_state], vec![account_to_claim]); +} + +fn transfer(pre_states: Vec, balance_to_move: u128) { // Continue only if input_accounts is an array of two elements let [sender, receiver] = match pre_states.try_into() { Ok(array) => array, @@ -35,3 +52,19 @@ fn main() { write_nssa_outputs(vec![sender, receiver], vec![sender_post, receiver_post]); } +/// A transfer of balance program. +/// To be used both in public and private contexts. +fn main() { + // Read input accounts. + // It is expected to receive only two accounts: [sender_account, receiver_account] + let ProgramInput { + pre_states, + instruction: balance_to_move, + } = read_nssa_inputs(); + + match (pre_states.len(), balance_to_move) { + (1, 0) => initialize_account(pre_states), + (2, balance_to_move) => transfer(pre_states, balance_to_move), + _ => panic!("Invalid parameters"), + } +} diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 0e8b1cb..4a32ddc 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -377,6 +377,7 @@ pub enum Command { #[arg(long)] solution: u128, }, + AuthenticatedTransferInitializePublicAccount {}, } ///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config @@ -847,6 +848,25 @@ pub async fn execute_subcommand(command: Command) -> Result { + let addr = wallet_core.create_new_account_public(); + + println!("Generated new account with addr {addr}"); + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + let res = wallet_core + .register_account_under_authenticated_transfers_programs(addr) + .await?; + + println!("Results of tx send is {res:#?}"); + + let _transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + + SubcommandReturnValue::RegisterAccount { addr } + } }; Ok(subcommand_ret) diff --git a/wallet/src/token_transfers/public.rs b/wallet/src/token_transfers/public.rs index 22d50eb..3e6df44 100644 --- a/wallet/src/token_transfers/public.rs +++ b/wallet/src/token_transfers/public.rs @@ -42,4 +42,30 @@ impl WalletCore { Err(ExecutionFailureKind::InsufficientFundsError) } } + + pub async fn register_account_under_authenticated_transfers_programs( + &self, + from: Address, + ) -> Result { + let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + + let instruction: u128 = 0; + let addresses = vec![from]; + let program_id = Program::authenticated_transfer_program().id(); + let message = Message::try_new(program_id, addresses, nonces, instruction).unwrap(); + + let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); + + let Some(signing_key) = signing_key else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let witness_set = WitnessSet::for_message(&message, &[signing_key]); + + let tx = PublicTransaction::new(message, witness_set); + + Ok(self.sequencer_client.send_tx_public(tx).await?) + } }