From c772f1b0fe67c56e60739a5a89290f6959e5a880 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 10 Oct 2025 17:47:23 -0300 Subject: [PATCH 1/4] add init function --- integration_tests/src/lib.rs | 60 +++++++++++-------- .../guest/src/bin/authenticated_transfer.rs | 53 ++++++++++++---- wallet/src/lib.rs | 20 +++++++ wallet/src/token_transfers/public.rs | 26 ++++++++ 4 files changed, 123 insertions(+), 36 deletions(-) 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?) + } } From a2b9aef1527aaf25db99a54d803ff75b36d17e40 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 13 Oct 2025 17:45:03 -0300 Subject: [PATCH 2/4] minor refactor --- .../guest/src/bin/authenticated_transfer.rs | 32 +++++++------------ wallet/src/lib.rs | 3 +- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index 31d606f..ccd0bec 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -3,12 +3,7 @@ use nssa_core::{ program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}, }; -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, - }; +fn initialize_account(pre_state: AccountWithMetadata) { let account_to_claim = pre_state.account.clone(); let is_authorized = pre_state.is_authorized; @@ -26,13 +21,7 @@ fn initialize_account(pre_states: Vec) { 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, - Err(_) => return, - }; - +fn transfer(sender: AccountWithMetadata, recipient: AccountWithMetadata, balance_to_move: u128) { // Continue only if the sender has authorized this operation if !sender.is_authorized { return; @@ -45,26 +34,27 @@ fn transfer(pre_states: Vec, balance_to_move: u128) { // Create accounts post states, with updated balances let mut sender_post = sender.account.clone(); - let mut receiver_post = receiver.account.clone(); + let mut recipient_post = recipient.account.clone(); sender_post.balance -= balance_to_move; - receiver_post.balance += balance_to_move; + recipient_post.balance += balance_to_move; - write_nssa_outputs(vec![sender, receiver], vec![sender_post, receiver_post]); + write_nssa_outputs(vec![sender, recipient], vec![sender_post, recipient_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"), + match (pre_states.as_slice(), balance_to_move) { + ([account_to_claim], 0) => initialize_account(account_to_claim.clone()), + ([sender, recipient], balance_to_move) => { + transfer(sender.clone(), recipient.clone(), balance_to_move) + } + _ => panic!("invalid params"), } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 4a32ddc..e5472bc 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -789,7 +789,8 @@ pub async fn execute_subcommand(command: Command) -> Result { let addr: Address = addr.parse()?; if let Some(account) = wallet_core.get_account_private(&addr) { - println!("{}", serde_json::to_string(&account).unwrap()); + let account_hr: HumanReadableAccount = account.into(); + println!("{}", serde_json::to_string(&account_hr).unwrap()); } else { println!("Private account not found."); } From fb4bb02f1a29b6e7172b6a1432ada26bb63b3f30 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 13 Oct 2025 18:06:02 -0300 Subject: [PATCH 3/4] add docstrings --- nssa/program_methods/guest/src/bin/authenticated_transfer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index ccd0bec..df8a38e 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -3,6 +3,8 @@ use nssa_core::{ program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}, }; +/// Initializes a default account under the ownership of this program. +/// This is achieved by a noop. fn initialize_account(pre_state: AccountWithMetadata) { let account_to_claim = pre_state.account.clone(); let is_authorized = pre_state.is_authorized; @@ -21,6 +23,7 @@ fn initialize_account(pre_state: AccountWithMetadata) { write_nssa_outputs(vec![pre_state], vec![account_to_claim]); } +/// Transfers `balance_to_move` native balance from `sender` to `recipient`. fn transfer(sender: AccountWithMetadata, recipient: AccountWithMetadata, balance_to_move: u128) { // Continue only if the sender has authorized this operation if !sender.is_authorized { From 9559fb4bf0332da4db64fa3f39e8254f63fd1cc5 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 13 Oct 2025 19:05:08 -0300 Subject: [PATCH 4/4] remove unused Nonce type alias --- common/src/transaction.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 3a2bda1..5332e5c 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -5,12 +5,6 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; -use elliptic_curve::{ - consts::{B0, B1}, - generic_array::GenericArray, -}; -use sha2::digest::typenum::{UInt, UTerm}; - #[derive(Debug, Clone, PartialEq, Eq)] pub enum NSSATransaction { Public(nssa::PublicTransaction), @@ -32,7 +26,6 @@ impl From for NSSATransaction { use crate::TreeHashType; pub type CipherText = Vec; -pub type Nonce = GenericArray, B1>, B0>, B0>>; pub type Tag = u8; #[derive(