From c39c9ad4ca4145a434ce9b42caed2b88dd7aebf6 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Tue, 14 Oct 2025 15:29:18 +0300 Subject: [PATCH 1/6] feat: cli refactor --- integration_tests/src/lib.rs | 303 ++++++---- wallet/src/cli/chain.rs | 253 ++++++++ wallet/src/cli/mod.rs | 3 + .../src/cli/native_token_transfer_program.rs | 342 +++++++++++ wallet/src/cli/pinata_program.rs | 157 +++++ wallet/src/lib.rs | 552 +----------------- 6 files changed, 966 insertions(+), 644 deletions(-) create mode 100644 wallet/src/cli/chain.rs create mode 100644 wallet/src/cli/native_token_transfer_program.rs create mode 100644 wallet/src/cli/pinata_program.rs diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index abe1c76..a560e9f 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -19,8 +19,18 @@ use tempfile::TempDir; use tokio::task::JoinHandle; use wallet::{ Command, SubcommandReturnValue, WalletCore, - cli::token_program::{ - TokenProgramSubcommand, TokenProgramSubcommandPrivate, TokenProgramSubcommandPublic, + cli::{ + chain::{ChainSubcommand, FetchSubcommand, RegisterSubcommand}, + native_token_transfer_program::{ + NativeTokenTransferProgramSubcommand, NativeTokenTransferProgramSubcommandPrivate, + NativeTokenTransferProgramSubcommandShielded, + }, + pinata_program::{ + PinataProgramSubcommand, PinataProgramSubcommandPrivate, PinataProgramSubcommandPublic, + }, + token_program::{ + TokenProgramSubcommand, TokenProgramSubcommandPrivate, TokenProgramSubcommandPublic, + }, }, config::PersistentAccountData, helperfunctions::{fetch_config, fetch_persistent_accounts}, @@ -100,11 +110,13 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) pub async fn test_success() { info!("test_success"); - let command = Command::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 100, - }; + let command = Command::Transfer( + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }, + ); let wallet_config = fetch_config().unwrap(); @@ -136,7 +148,9 @@ pub async fn test_success() { pub async fn test_success_move_to_another_account() { info!("test_success_move_to_another_account"); - let command = Command::RegisterAccountPublic {}; + let command = Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + )); let wallet_config = fetch_config().unwrap(); @@ -160,11 +174,13 @@ pub async fn test_success_move_to_another_account() { panic!("Failed to produce new account, not present in persistent accounts"); } - let command = Command::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: new_persistent_account_addr.clone(), - amount: 100, - }; + let command = Command::Transfer( + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { + from: ACC_SENDER.to_string(), + to: new_persistent_account_addr.clone(), + amount: 100, + }, + ); wallet::execute_subcommand(command).await.unwrap(); @@ -192,11 +208,13 @@ pub async fn test_success_move_to_another_account() { pub async fn test_failure() { info!("test_failure"); - let command = Command::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 1000000, - }; + let command = Command::Transfer( + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 1000000, + }, + ); let wallet_config = fetch_config().unwrap(); @@ -230,11 +248,13 @@ pub async fn test_failure() { pub async fn test_success_two_transactions() { info!("test_success_two_transactions"); - let command = Command::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 100, - }; + let command = Command::Transfer( + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }, + ); let wallet_config = fetch_config().unwrap(); @@ -263,11 +283,13 @@ pub async fn test_success_two_transactions() { info!("First TX Success!"); - let command = Command::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 100, - }; + let command = Command::Transfer( + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }, + ); wallet::execute_subcommand(command).await.unwrap(); @@ -319,17 +341,23 @@ pub async fn test_success_token_program() { let wallet_config = fetch_config().unwrap(); // Create new account for the token definition - wallet::execute_subcommand(Command::RegisterAccountPublic {}) - .await - .unwrap(); + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap(); // Create new account for the token supply holder - wallet::execute_subcommand(Command::RegisterAccountPublic {}) - .await - .unwrap(); + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap(); // Create new account for receiving a token transaction - wallet::execute_subcommand(Command::RegisterAccountPublic {}) - .await - .unwrap(); + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap(); let persistent_accounts = fetch_persistent_accounts().unwrap(); @@ -462,26 +490,32 @@ pub async fn test_success_token_program_private_owned() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::RegisterAccountPublic {}) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::RegisterAccountPrivate {}) - .await - .unwrap() + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::RegisterAccountPrivate {}) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; @@ -595,26 +629,32 @@ pub async fn test_success_token_program_private_claiming_path() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::RegisterAccountPublic {}) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::RegisterAccountPrivate {}) - .await - .unwrap() + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::RegisterAccountPrivate {}) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; @@ -690,11 +730,13 @@ pub async fn test_success_token_program_private_claiming_path() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let command = Command::FetchPrivateAccount { - tx_hash, - acc_addr: recipient_addr.to_string(), - output_id: 1, - }; + let command = Command::Chain(ChainSubcommand::Fetch( + FetchSubcommand::FetchPrivateAccount { + tx_hash, + acc_addr: recipient_addr.to_string(), + output_id: 1, + }, + )); wallet::execute_subcommand(command).await.unwrap(); @@ -717,11 +759,13 @@ pub async fn test_success_private_transfer_to_another_owned_account() { let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap(); - let command = Command::SendNativeTokenTransferPrivateOwnedAccount { - from: from.to_string(), - to: to.to_string(), - amount: 100, - }; + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( + NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateOwnedAccount { + from: from.to_string(), + to: to.to_string(), + amount: 100, + }, + )); wallet::execute_subcommand(command).await.unwrap(); @@ -750,12 +794,14 @@ pub async fn test_success_private_transfer_to_another_foreign_account() { let to_npk_string = hex::encode(to_npk.0); let to_ipk = Secp256k1Point::from_scalar(to_npk.0); - let command = Command::SendNativeTokenTransferPrivateForeignAccount { - from: from.to_string(), - to_npk: to_npk_string, - to_ipk: hex::encode(to_ipk.0), - amount: 100, - }; + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( + NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + from: from.to_string(), + to_npk: to_npk_string, + to_ipk: hex::encode(to_ipk.0), + amount: 100, + }, + )); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = wallet::execute_subcommand(command).await.unwrap() @@ -789,7 +835,9 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat info!("test_success_private_transfer_to_another_owned_account_claiming_path"); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); - let command = Command::RegisterAccountPrivate {}; + let command = Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + )); let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { @@ -808,12 +856,14 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat .cloned() .unwrap(); - let command = Command::SendNativeTokenTransferPrivateForeignAccount { - from: from.to_string(), - to_npk: hex::encode(to_keys.nullifer_public_key.0), - to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), - amount: 100, - }; + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( + NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + from: from.to_string(), + to_npk: hex::encode(to_keys.nullifer_public_key.0), + to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), + amount: 100, + }, + )); let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { @@ -822,11 +872,13 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - let command = Command::FetchPrivateAccount { - tx_hash, - acc_addr: to_addr.to_string(), - output_id: 1, - }; + let command = Command::Chain(ChainSubcommand::Fetch( + FetchSubcommand::FetchPrivateAccount { + tx_hash, + acc_addr: to_addr.to_string(), + output_id: 1, + }, + )); wallet::execute_subcommand(command).await.unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); @@ -851,11 +903,13 @@ pub async fn test_success_deshielded_transfer_to_another_account() { info!("test_success_deshielded_transfer_to_another_account"); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); let to: Address = ACC_RECEIVER.parse().unwrap(); - let command = Command::SendNativeTokenTransferDeshielded { - from: from.to_string(), - to: to.to_string(), - amount: 100, - }; + let command = Command::Transfer( + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferDeshielded { + from: from.to_string(), + to: to.to_string(), + amount: 100, + }, + ); let wallet_config = fetch_config().unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -892,11 +946,13 @@ pub async fn test_success_shielded_transfer_to_another_owned_account() { info!("test_success_shielded_transfer_to_another_owned_account"); let from: Address = ACC_SENDER.parse().unwrap(); let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap(); - let command = Command::SendNativeTokenTransferShielded { - from: from.to_string(), - to: to.to_string(), - amount: 100, - }; + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Shielded( + NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShielded { + from: from.to_string(), + to: to.to_string(), + amount: 100, + }, + )); let wallet_config = fetch_config().unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -931,12 +987,13 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() { let to_ipk = Secp256k1Point::from_scalar(to_npk.0); let from: Address = ACC_SENDER.parse().unwrap(); - let command = Command::SendNativeTokenTransferShieldedForeignAccount { + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Shielded( + NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShieldedForeignAccount { from: from.to_string(), to_npk: to_npk_string, to_ipk: hex::encode(to_ipk.0), amount: 100, - }; + })); let wallet_config = fetch_config().unwrap(); @@ -972,11 +1029,13 @@ pub async fn test_pinata() { let pinata_addr = "cafe".repeat(16); let pinata_prize = 150; let solution = 989106; - let command = Command::ClaimPinata { - pinata_addr: pinata_addr.clone(), - winner_addr: ACC_SENDER.to_string(), - solution, - }; + let command = Command::PinataProgram(PinataProgramSubcommand::Public( + PinataProgramSubcommandPublic::ClaimPinata { + pinata_addr: pinata_addr.clone(), + winner_addr: ACC_SENDER.to_string(), + solution, + }, + )); let wallet_config = fetch_config().unwrap(); @@ -1018,11 +1077,13 @@ pub async fn test_pinata_private_receiver() { let pinata_prize = 150; let solution = 989106; - let command = Command::ClaimPinataPrivateReceiverOwned { - pinata_addr: pinata_addr.clone(), - winner_addr: ACC_SENDER_PRIVATE.to_string(), - solution, - }; + let command = Command::PinataProgram(PinataProgramSubcommand::Private( + PinataProgramSubcommandPrivate::ClaimPinataPrivateReceiverOwned { + pinata_addr: pinata_addr.clone(), + winner_addr: ACC_SENDER_PRIVATE.to_string(), + solution, + }, + )); let wallet_config = fetch_config().unwrap(); @@ -1050,11 +1111,13 @@ pub async fn test_pinata_private_receiver() { .unwrap() .balance; - let command = Command::FetchPrivateAccount { - tx_hash: tx_hash.clone(), - acc_addr: ACC_SENDER_PRIVATE.to_string(), - output_id: 0, - }; + let command = Command::Chain(ChainSubcommand::Fetch( + FetchSubcommand::FetchPrivateAccount { + tx_hash: tx_hash.clone(), + acc_addr: ACC_SENDER_PRIVATE.to_string(), + output_id: 0, + }, + )); wallet::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().unwrap(); @@ -1079,18 +1142,22 @@ pub async fn test_pinata_private_receiver_new_account() { // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: winner_addr } = - wallet::execute_subcommand(Command::RegisterAccountPrivate {}) - .await - .unwrap() + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; - let command = Command::ClaimPinataPrivateReceiverOwned { - pinata_addr: pinata_addr.clone(), - winner_addr: winner_addr.to_string(), - solution, - }; + let command = Command::PinataProgram(PinataProgramSubcommand::Private( + PinataProgramSubcommandPrivate::ClaimPinataPrivateReceiverOwned { + pinata_addr: pinata_addr.clone(), + winner_addr: winner_addr.to_string(), + solution, + }, + )); let wallet_config = fetch_config().unwrap(); diff --git a/wallet/src/cli/chain.rs b/wallet/src/cli/chain.rs new file mode 100644 index 0000000..ad58301 --- /dev/null +++ b/wallet/src/cli/chain.rs @@ -0,0 +1,253 @@ +use std::str::FromStr; + +use anyhow::Result; +use clap::Subcommand; +use common::transaction::NSSATransaction; +use nssa::Address; + +use crate::{ + SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::HumanReadableAccount, +}; + +///Represents generic chain CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum ChainSubcommand { + ///Get + #[command(subcommand)] + Get(GetSubcommand), + ///Fetch + #[command(subcommand)] + Fetch(FetchSubcommand), + ///Register + #[command(subcommand)] + Register(RegisterSubcommand), +} + +///Represents generic getter CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum GetSubcommand { + ///Get account `addr` balance + GetPublicAccountBalance { + #[arg(short, long)] + addr: String, + }, + ///Get account `addr` nonce + GetPublicAccountNonce { + #[arg(short, long)] + addr: String, + }, + ///Get account at address `addr` + GetPublicAccount { + #[arg(short, long)] + addr: String, + }, + ///Get private account with `addr` from storage + GetPrivateAccount { + #[arg(short, long)] + addr: String, + }, +} + +///Represents generic getter CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum FetchSubcommand { + ///Fetch transaction by `hash` + FetchTx { + #[arg(short, long)] + tx_hash: String, + }, + ///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id` + FetchPrivateAccount { + ///tx_hash - valid 32 byte hex string + #[arg(long)] + tx_hash: String, + ///acc_addr - valid 32 byte hex string + #[arg(long)] + acc_addr: String, + ///output_id - id of the output in the transaction + #[arg(long)] + output_id: usize, + }, +} + +///Represents generic register CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum RegisterSubcommand { + ///Register new public account + RegisterAccountPublic {}, + ///Register new private account + RegisterAccountPrivate {}, +} + +impl WalletSubcommand for GetSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + GetSubcommand::GetPublicAccountBalance { addr } => { + let addr = Address::from_str(&addr)?; + + let balance = wallet_core.get_account_balance(addr).await?; + println!("Accounts {addr} balance is {balance}"); + + Ok(SubcommandReturnValue::Empty) + } + GetSubcommand::GetPublicAccountNonce { addr } => { + let addr = Address::from_str(&addr)?; + + let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; + println!("Accounts {addr} nonce is {nonce}"); + + Ok(SubcommandReturnValue::Empty) + } + GetSubcommand::GetPublicAccount { addr } => { + let addr: Address = addr.parse()?; + let account = wallet_core.get_account_public(addr).await?; + let account_hr: HumanReadableAccount = account.clone().into(); + println!("{}", serde_json::to_string(&account_hr).unwrap()); + + Ok(SubcommandReturnValue::Account(account)) + } + GetSubcommand::GetPrivateAccount { addr } => { + let addr: Address = addr.parse()?; + if let Some(account) = wallet_core.get_account_private(&addr) { + println!("{}", serde_json::to_string(&account).unwrap()); + } else { + println!("Private account not found."); + } + Ok(SubcommandReturnValue::Empty) + } + } + } +} + +impl WalletSubcommand for FetchSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + FetchSubcommand::FetchTx { tx_hash } => { + let tx_obj = wallet_core + .sequencer_client + .get_transaction_by_hash(tx_hash) + .await?; + + println!("Transaction object {tx_obj:#?}"); + + Ok(SubcommandReturnValue::Empty) + } + FetchSubcommand::FetchPrivateAccount { + tx_hash, + acc_addr, + output_id: ciph_id, + } => { + let acc_addr: Address = acc_addr.parse().unwrap(); + + let account_key_chain = wallet_core + .storage + .user_data + .user_private_accounts + .get(&acc_addr); + + let Some((account_key_chain, _)) = account_key_chain else { + anyhow::bail!("Account not found"); + }; + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone(); + let to_comm = tx.message.new_commitments[ciph_id].clone(); + let shared_secret = + account_key_chain.calculate_shared_secret_receiver(to_ebc.epk); + + let res_acc_to = nssa_core::EncryptionScheme::decrypt( + &to_ebc.ciphertext, + &shared_secret, + &to_comm, + ciph_id as u32, + ) + .unwrap(); + + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + + wallet_core + .storage + .insert_private_account_data(acc_addr, res_acc_to); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::Empty) + } + } + } +} + +impl WalletSubcommand for RegisterSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + RegisterSubcommand::RegisterAccountPublic {} => { + 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:#?}"); + + Ok(SubcommandReturnValue::RegisterAccount { addr }) + } + RegisterSubcommand::RegisterAccountPrivate {} => { + let addr = wallet_core.create_new_account_private(); + + let (key, _) = wallet_core + .storage + .user_data + .get_private_account(&addr) + .unwrap(); + + println!("Generated new account with addr {addr}"); + println!("With npk {}", hex::encode(&key.nullifer_public_key)); + println!( + "With ipk {}", + hex::encode(key.incoming_viewing_public_key.to_bytes()) + ); + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::RegisterAccount { addr }) + } + } + } +} + +impl WalletSubcommand for ChainSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + ChainSubcommand::Get(get_subcommand) => { + get_subcommand.handle_subcommand(wallet_core).await + } + ChainSubcommand::Fetch(fetch_subcommand) => { + fetch_subcommand.handle_subcommand(wallet_core).await + } + ChainSubcommand::Register(register_subcommand) => { + register_subcommand.handle_subcommand(wallet_core).await + } + } + } +} diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index 5b277e0..093a501 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -2,6 +2,9 @@ use anyhow::Result; use crate::{SubcommandReturnValue, WalletCore}; +pub mod chain; +pub mod native_token_transfer_program; +pub mod pinata_program; pub mod token_program; pub(crate) trait WalletSubcommand { diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/native_token_transfer_program.rs new file mode 100644 index 0000000..355c7f3 --- /dev/null +++ b/wallet/src/cli/native_token_transfer_program.rs @@ -0,0 +1,342 @@ +use anyhow::Result; +use clap::Subcommand; +use common::transaction::NSSATransaction; +use nssa::Address; + +use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; + +///Represents generic CLI subcommand for a wallet working with native token transfer program +#[derive(Subcommand, Debug, Clone)] +pub enum NativeTokenTransferProgramSubcommand { + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Public operation + SendNativeTokenTransferPublic { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Private execution + #[command(subcommand)] + Private(NativeTokenTransferProgramSubcommandPrivate), + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Deshielded operation + SendNativeTokenTransferDeshielded { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Shielded execution + #[command(subcommand)] + Shielded(NativeTokenTransferProgramSubcommandShielded), +} + +///Represents generic shielded CLI subcommand for a wallet working with native token transfer program +#[derive(Subcommand, Debug, Clone)] +pub enum NativeTokenTransferProgramSubcommandShielded { + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Shielded operation + SendNativeTokenTransferShielded { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Shielded operation + SendNativeTokenTransferShieldedForeignAccount { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: String, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, +} + +///Represents generic private CLI subcommand for a wallet working with native token transfer program +#[derive(Subcommand, Debug, Clone)] +pub enum NativeTokenTransferProgramSubcommandPrivate { + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Private operation + SendNativeTokenTransferPrivateOwnedAccount { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Private operation + SendNativeTokenTransferPrivateForeignAccount { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: String, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, +} + +impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateOwnedAccount { from, to, amount } => { + let from: Address = from.parse().unwrap(); + let to: Address = to.parse().unwrap(); + + let (res, [secret_from, secret_to]) = wallet_core + .send_private_native_token_transfer_owned_account(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret_from, from), (secret_to, to)]; + + wallet_core + .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + from, + to_npk, + to_ipk, + amount, + } => { + let from: Address = from.parse().unwrap(); + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); + + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + + let (res, [secret_from, _]) = wallet_core + .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret_from, from)]; + + wallet_core + .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + } + } +} + +impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShielded { from, to, amount } => { + let from: Address = from.parse().unwrap(); + let to: Address = to.parse().unwrap(); + + let (res, secret) = wallet_core + .send_shielded_native_token_transfer(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret, to)]; + + wallet_core + .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShieldedForeignAccount { + from, + to_npk, + to_ipk, + amount, + } => { + let from: Address = from.parse().unwrap(); + + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); + + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + + let (res, _) = wallet_core + .send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + } + } +} + +impl WalletSubcommand for NativeTokenTransferProgramSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + NativeTokenTransferProgramSubcommand::Private(private_subcommand) => { + private_subcommand.handle_subcommand(wallet_core).await + } + NativeTokenTransferProgramSubcommand::Shielded(shielded_subcommand) => { + shielded_subcommand.handle_subcommand(wallet_core).await + } + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferDeshielded { + from, + to, + amount, + } => { + let from: Address = from.parse().unwrap(); + let to: Address = to.parse().unwrap(); + + let (res, secret) = wallet_core + .send_deshielded_native_token_transfer(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret, from)]; + + wallet_core.decode_insert_privacy_preserving_transaction_results( + tx, + &acc_decode_data, + )?; + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { + from, + to, + amount, + } => { + let from: Address = from.parse().unwrap(); + let to: Address = to.parse().unwrap(); + + let res = wallet_core + .send_public_native_token_transfer(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + + println!("Transaction data is {transfer_tx:?}"); + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::Empty) + } + } + } +} diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/pinata_program.rs new file mode 100644 index 0000000..2a001b8 --- /dev/null +++ b/wallet/src/cli/pinata_program.rs @@ -0,0 +1,157 @@ +use anyhow::Result; +use clap::Subcommand; +use common::transaction::NSSATransaction; +use log::info; + +use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; + +///Represents generic CLI subcommand for a wallet working with pinata program +#[derive(Subcommand, Debug, Clone)] +pub enum PinataProgramSubcommand { + ///Public execution + #[command(subcommand)] + Public(PinataProgramSubcommandPublic), + ///Private execution + #[command(subcommand)] + Private(PinataProgramSubcommandPrivate), +} + +///Represents generic public CLI subcommand for a wallet working with pinata program +#[derive(Subcommand, Debug, Clone)] +pub enum PinataProgramSubcommandPublic { + // TODO: Testnet only. Refactor to prevent compilation on mainnet. + // Claim piñata prize + ClaimPinata { + ///pinata_addr - valid 32 byte hex string + #[arg(long)] + pinata_addr: String, + ///winner_addr - valid 32 byte hex string + #[arg(long)] + winner_addr: String, + ///solution - solution to pinata challenge + #[arg(long)] + solution: u128, + }, +} + +///Represents generic private CLI subcommand for a wallet working with pinata program +#[derive(Subcommand, Debug, Clone)] +pub enum PinataProgramSubcommandPrivate { + // TODO: Testnet only. Refactor to prevent compilation on mainnet. + // Claim piñata prize + ClaimPinataPrivateReceiverOwned { + ///pinata_addr - valid 32 byte hex string + #[arg(long)] + pinata_addr: String, + ///winner_addr - valid 32 byte hex string + #[arg(long)] + winner_addr: String, + ///solution - solution to pinata challenge + #[arg(long)] + solution: u128, + }, +} + +impl WalletSubcommand for PinataProgramSubcommandPublic { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + PinataProgramSubcommandPublic::ClaimPinata { + pinata_addr, + winner_addr, + solution, + } => { + let res = wallet_core + .claim_pinata( + pinata_addr.parse().unwrap(), + winner_addr.parse().unwrap(), + solution, + ) + .await?; + info!("Results of tx send is {res:#?}"); + + Ok(SubcommandReturnValue::Empty) + } + } + } +} + +impl WalletSubcommand for PinataProgramSubcommandPrivate { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + PinataProgramSubcommandPrivate::ClaimPinataPrivateReceiverOwned { + pinata_addr, + winner_addr, + solution, + } => { + let pinata_addr = pinata_addr.parse().unwrap(); + let winner_addr = winner_addr.parse().unwrap(); + + let winner_intialized = wallet_core + .check_private_account_initialized(&winner_addr) + .await; + + let (res, [secret_winner]) = if winner_intialized { + wallet_core + .claim_pinata_private_owned_account_already_initialized( + pinata_addr, + winner_addr, + solution, + ) + .await? + } else { + wallet_core + .claim_pinata_private_owned_account_not_initialized( + pinata_addr, + winner_addr, + solution, + ) + .await? + }; + + info!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret_winner, winner_addr)]; + + wallet_core.decode_insert_privacy_preserving_transaction_results( + tx, + &acc_decode_data, + )?; + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + } + } +} + +impl WalletSubcommand for PinataProgramSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + PinataProgramSubcommand::Private(private_subcommand) => { + private_subcommand.handle_subcommand(wallet_core).await + } + PinataProgramSubcommand::Public(public_subcommand) => { + public_subcommand.handle_subcommand(wallet_core).await + } + } + } +} diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index ac4cf6d..65eca41 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -1,4 +1,4 @@ -use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc}; +use std::{fs::File, io::Write, path::PathBuf, sync::Arc}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use common::{ @@ -15,12 +15,15 @@ use nssa::{Account, Address}; use clap::{Parser, Subcommand}; use nssa_core::Commitment; -use crate::cli::WalletSubcommand; +use crate::cli::{ + WalletSubcommand, chain::ChainSubcommand, + native_token_transfer_program::NativeTokenTransferProgramSubcommand, + pinata_program::PinataProgramSubcommand, +}; use crate::{ cli::token_program::TokenProgramSubcommand, helperfunctions::{ - HumanReadableAccount, fetch_config, fetch_persistent_accounts, get_home, - produce_data_for_storage, + fetch_config, fetch_persistent_accounts, get_home, produce_data_for_storage, }, poller::TxPoller, }; @@ -181,163 +184,15 @@ impl WalletCore { #[derive(Subcommand, Debug, Clone)] #[clap(about)] pub enum Command { - ///Send native token transfer from `from` to `to` for `amount` - /// - /// Public operation - SendNativeTokenTransferPublic { - ///from - valid 32 byte hex string - #[arg(long)] - from: String, - ///to - valid 32 byte hex string - #[arg(long)] - to: String, - ///amount - amount of balance to move - #[arg(long)] - amount: u128, - }, - ///Send native token transfer from `from` to `to` for `amount` - /// - /// Private operation - SendNativeTokenTransferPrivateOwnedAccount { - ///from - valid 32 byte hex string - #[arg(long)] - from: String, - ///to - valid 32 byte hex string - #[arg(long)] - to: String, - ///amount - amount of balance to move - #[arg(long)] - amount: u128, - }, - ///Send native token transfer from `from` to `to` for `amount` - /// - /// Private operation - SendNativeTokenTransferPrivateForeignAccount { - ///from - valid 32 byte hex string - #[arg(long)] - from: String, - ///to_npk - valid 32 byte hex string - #[arg(long)] - to_npk: String, - ///to_ipk - valid 33 byte hex string - #[arg(long)] - to_ipk: String, - ///amount - amount of balance to move - #[arg(long)] - amount: u128, - }, - ///Send native token transfer from `from` to `to` for `amount` - /// - /// Deshielded operation - SendNativeTokenTransferDeshielded { - ///from - valid 32 byte hex string - #[arg(long)] - from: String, - ///to - valid 32 byte hex string - #[arg(long)] - to: String, - ///amount - amount of balance to move - #[arg(long)] - amount: u128, - }, - ///Send native token transfer from `from` to `to` for `amount` - /// - /// Shielded operation - SendNativeTokenTransferShielded { - ///from - valid 32 byte hex string - #[arg(long)] - from: String, - ///to - valid 32 byte hex string - #[arg(long)] - to: String, - ///amount - amount of balance to move - #[arg(long)] - amount: u128, - }, - ///Send native token transfer from `from` to `to` for `amount` - /// - /// Shielded operation - SendNativeTokenTransferShieldedForeignAccount { - ///from - valid 32 byte hex string - #[arg(long)] - from: String, - ///to_npk - valid 32 byte hex string - #[arg(long)] - to_npk: String, - ///to_ipk - valid 33 byte hex string - #[arg(long)] - to_ipk: String, - ///amount - amount of balance to move - #[arg(long)] - amount: u128, - }, - ///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id` - FetchPrivateAccount { - ///tx_hash - valid 32 byte hex string - #[arg(long)] - tx_hash: String, - ///acc_addr - valid 32 byte hex string - #[arg(long)] - acc_addr: String, - ///output_id - id of the output in the transaction - #[arg(long)] - output_id: usize, - }, - ///Get private account with `addr` from storage - GetPrivateAccount { - #[arg(short, long)] - addr: String, - }, - ///Register new public account - RegisterAccountPublic {}, - ///Register new private account - RegisterAccountPrivate {}, - ///Fetch transaction by `hash` - FetchTx { - #[arg(short, long)] - tx_hash: String, - }, - ///Get account `addr` balance - GetPublicAccountBalance { - #[arg(short, long)] - addr: String, - }, - ///Get account `addr` nonce - GetPublicAccountNonce { - #[arg(short, long)] - addr: String, - }, - ///Get account at address `addr` - GetPublicAccount { - #[arg(short, long)] - addr: String, - }, - // TODO: Testnet only. Refactor to prevent compilation on mainnet. - // Claim piñata prize - ClaimPinata { - ///pinata_addr - valid 32 byte hex string - #[arg(long)] - pinata_addr: String, - ///winner_addr - valid 32 byte hex string - #[arg(long)] - winner_addr: String, - ///solution - solution to pinata challenge - #[arg(long)] - solution: u128, - }, - // TODO: Testnet only. Refactor to prevent compilation on mainnet. - // Claim piñata prize - ClaimPinataPrivateReceiverOwned { - ///pinata_addr - valid 32 byte hex string - #[arg(long)] - pinata_addr: String, - ///winner_addr - valid 32 byte hex string - #[arg(long)] - winner_addr: String, - ///solution - solution to pinata challenge - #[arg(long)] - solution: u128, - }, + ///Transfer command + #[command(subcommand)] + Transfer(NativeTokenTransferProgramSubcommand), + ///Chain command + #[command(subcommand)] + Chain(ChainSubcommand), + ///Pinata command + #[command(subcommand)] + PinataProgram(PinataProgramSubcommand), ///Token command #[command(subcommand)] TokenProgram(TokenProgramSubcommand), @@ -365,373 +220,18 @@ pub async fn execute_subcommand(command: Command) -> Result { - let from: Address = from.parse().unwrap(); - let to: Address = to.parse().unwrap(); - - let res = wallet_core - .send_public_native_token_transfer(from, to, amount) - .await?; - - println!("Results of tx send is {res:#?}"); - - let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; - - println!("Transaction data is {transfer_tx:?}"); - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::Empty + Command::Transfer(transfer_subcommand) => { + transfer_subcommand + .handle_subcommand(&mut wallet_core) + .await? } - Command::SendNativeTokenTransferPrivateOwnedAccount { from, to, amount } => { - let from: Address = from.parse().unwrap(); - let to: Address = to.parse().unwrap(); - - let (res, [secret_from, secret_to]) = wallet_core - .send_private_native_token_transfer_owned_account(from, to, amount) - .await?; - - println!("Results of tx send is {res:#?}"); - - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_from, from), (secret_to, to)]; - - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; - } - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + Command::Chain(chain_subcommand) => { + chain_subcommand.handle_subcommand(&mut wallet_core).await? } - Command::SendNativeTokenTransferPrivateForeignAccount { - from, - to_npk, - to_ipk, - amount, - } => { - let from: Address = from.parse().unwrap(); - let to_npk_res = hex::decode(to_npk)?; - let mut to_npk = [0; 32]; - to_npk.copy_from_slice(&to_npk_res); - let to_npk = nssa_core::NullifierPublicKey(to_npk); - - let to_ipk_res = hex::decode(to_ipk)?; - let mut to_ipk = [0u8; 33]; - to_ipk.copy_from_slice(&to_ipk_res); - let to_ipk = - nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); - - let (res, [secret_from, _]) = wallet_core - .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) - .await?; - - println!("Results of tx send is {res:#?}"); - - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_from, from)]; - - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; - } - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } - } - Command::SendNativeTokenTransferDeshielded { from, to, amount } => { - let from: Address = from.parse().unwrap(); - let to: Address = to.parse().unwrap(); - - let (res, secret) = wallet_core - .send_deshielded_native_token_transfer(from, to, amount) - .await?; - - println!("Results of tx send is {res:#?}"); - - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret, from)]; - - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; - } - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } - } - Command::SendNativeTokenTransferShielded { from, to, amount } => { - let from: Address = from.parse().unwrap(); - let to: Address = to.parse().unwrap(); - - let (res, secret) = wallet_core - .send_shielded_native_token_transfer(from, to, amount) - .await?; - - println!("Results of tx send is {res:#?}"); - - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret, to)]; - - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; - } - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } - } - Command::SendNativeTokenTransferShieldedForeignAccount { - from, - to_npk, - to_ipk, - amount, - } => { - let from: Address = from.parse().unwrap(); - - let to_npk_res = hex::decode(to_npk)?; - let mut to_npk = [0; 32]; - to_npk.copy_from_slice(&to_npk_res); - let to_npk = nssa_core::NullifierPublicKey(to_npk); - - let to_ipk_res = hex::decode(to_ipk)?; - let mut to_ipk = [0u8; 33]; - to_ipk.copy_from_slice(&to_ipk_res); - let to_ipk = - nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); - - let (res, _) = wallet_core - .send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) - .await?; - - println!("Results of tx send is {res:#?}"); - - let tx_hash = res.tx_hash; - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } - } - Command::FetchPrivateAccount { - tx_hash, - acc_addr, - output_id: ciph_id, - } => { - let acc_addr: Address = acc_addr.parse().unwrap(); - - let account_key_chain = wallet_core - .storage - .user_data - .user_private_accounts - .get(&acc_addr); - - let Some((account_key_chain, _)) = account_key_chain else { - anyhow::bail!("Account not found"); - }; - - let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone(); - let to_comm = tx.message.new_commitments[ciph_id].clone(); - let shared_secret = account_key_chain.calculate_shared_secret_receiver(to_ebc.epk); - - let res_acc_to = nssa_core::EncryptionScheme::decrypt( - &to_ebc.ciphertext, - &shared_secret, - &to_comm, - ciph_id as u32, - ) - .unwrap(); - - println!("RES acc to {res_acc_to:#?}"); - - println!("Transaction data is {:?}", tx.message); - - wallet_core - .storage - .insert_private_account_data(acc_addr, res_acc_to); - } - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::Empty - } - Command::RegisterAccountPublic {} => { - 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:#?}"); - - SubcommandReturnValue::RegisterAccount { addr } - } - Command::RegisterAccountPrivate {} => { - let addr = wallet_core.create_new_account_private(); - - let (key, _) = wallet_core - .storage - .user_data - .get_private_account(&addr) - .unwrap(); - - println!("Generated new account with addr {addr}"); - println!("With npk {}", hex::encode(&key.nullifer_public_key)); - println!( - "With ipk {}", - hex::encode(key.incoming_viewing_public_key.to_bytes()) - ); - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::RegisterAccount { addr } - } - Command::FetchTx { tx_hash } => { - let tx_obj = wallet_core - .sequencer_client - .get_transaction_by_hash(tx_hash) - .await?; - - println!("Transaction object {tx_obj:#?}"); - - SubcommandReturnValue::Empty - } - Command::GetPublicAccountBalance { addr } => { - let addr = Address::from_str(&addr)?; - - let balance = wallet_core.get_account_balance(addr).await?; - println!("Accounts {addr} balance is {balance}"); - - SubcommandReturnValue::Empty - } - Command::GetPublicAccountNonce { addr } => { - let addr = Address::from_str(&addr)?; - - let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; - println!("Accounts {addr} nonce is {nonce}"); - - SubcommandReturnValue::Empty - } - Command::GetPublicAccount { addr } => { - let addr: Address = addr.parse()?; - let account = wallet_core.get_account_public(addr).await?; - let account_hr: HumanReadableAccount = account.clone().into(); - println!("{}", serde_json::to_string(&account_hr).unwrap()); - - SubcommandReturnValue::Account(account) - } - Command::GetPrivateAccount { addr } => { - let addr: Address = addr.parse()?; - if let Some(account) = wallet_core.get_account_private(&addr) { - println!("{}", serde_json::to_string(&account).unwrap()); - } else { - println!("Private account not found."); - } - SubcommandReturnValue::Empty - } - Command::ClaimPinata { - pinata_addr, - winner_addr, - solution, - } => { - let res = wallet_core - .claim_pinata( - pinata_addr.parse().unwrap(), - winner_addr.parse().unwrap(), - solution, - ) - .await?; - info!("Results of tx send is {res:#?}"); - - SubcommandReturnValue::Empty - } - Command::ClaimPinataPrivateReceiverOwned { - pinata_addr, - winner_addr, - solution, - } => { - let pinata_addr = pinata_addr.parse().unwrap(); - let winner_addr = winner_addr.parse().unwrap(); - - let winner_intialized = wallet_core - .check_private_account_initialized(&winner_addr) - .await; - - let (res, [secret_winner]) = if winner_intialized { - wallet_core - .claim_pinata_private_owned_account_already_initialized( - pinata_addr, - winner_addr, - solution, - ) - .await? - } else { - wallet_core - .claim_pinata_private_owned_account_not_initialized( - pinata_addr, - winner_addr, - solution, - ) - .await? - }; - - info!("Results of tx send is {res:#?}"); - - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_winner, winner_addr)]; - - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; - } - - let path = wallet_core.store_persistent_accounts()?; - - println!("Stored persistent accounts at {path:#?}"); - - SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + Command::PinataProgram(pinata_subcommand) => { + pinata_subcommand + .handle_subcommand(&mut wallet_core) + .await? } Command::TokenProgram(token_subcommand) => { token_subcommand.handle_subcommand(&mut wallet_core).await? From 88446b17f9f735fcae4c768061ff9744c3f475c5 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Wed, 15 Oct 2025 15:17:30 +0300 Subject: [PATCH 2/6] feat: automatic mode added --- common/src/sequencer_client/mod.rs | 17 +++++- wallet/src/lib.rs | 93 +++++++++++++++++++++++++++++- wallet/src/main.rs | 10 +++- 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index 1aec903..3c27cae 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -9,8 +9,8 @@ use serde_json::Value; use crate::rpc_primitives::requests::{ GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse, - GetProofForCommitmentRequest, GetProofForCommitmentResponse, GetTransactionByHashRequest, - GetTransactionByHashResponse, + GetLastBlockRequest, GetLastBlockResponse, GetProofForCommitmentRequest, + GetProofForCommitmentResponse, GetTransactionByHashRequest, GetTransactionByHashResponse, }; use crate::sequencer_client::json::AccountInitialData; use crate::transaction::{EncodedTransaction, NSSATransaction}; @@ -74,6 +74,19 @@ impl SequencerClient { Ok(resp_deser) } + ///Get last known `blokc_id` from sequencer + pub async fn get_last_block(&self) -> Result { + let block_req = GetLastBlockRequest {}; + + let req = serde_json::to_value(block_req)?; + + let resp = self.call_method_with_payload("get_last_block", req).await?; + + let resp_deser = serde_json::from_value(resp)?; + + Ok(resp_deser) + } + ///Get account public balance for `address`. `address` must be a valid hex-string for 32 bytes. pub async fn get_account_balance( &self, diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 65eca41..36e78e6 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -2,6 +2,7 @@ use std::{fs::File, io::Write, path::PathBuf, sync::Arc}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use common::{ + block::HashableBlockData, sequencer_client::SequencerClient, transaction::{EncodedTransaction, NSSATransaction}, }; @@ -10,7 +11,7 @@ use anyhow::Result; use chain_storage::WalletChainStore; use config::WalletConfig; use log::info; -use nssa::{Account, Address}; +use nssa::{Account, Address, privacy_preserving_transaction::message::EncryptedAccountData}; use clap::{Parser, Subcommand}; use nssa_core::Commitment; @@ -202,9 +203,12 @@ pub enum Command { #[derive(Parser, Debug)] #[clap(version, about)] pub struct Args { + /// Continious run flag + #[arg(short, long)] + pub continious_run: bool, /// Wallet command #[command(subcommand)] - pub command: Command, + pub command: Option, } #[derive(Debug, Clone)] @@ -240,3 +244,88 @@ pub async fn execute_subcommand(command: Command) -> Result Result<()> { + let config = fetch_config()?; + let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); + let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone())?; + + let mut latest_block_num = seq_client.get_last_block().await?.last_block; + let mut curr_last_block = latest_block_num; + + loop { + for block_id in curr_last_block..(latest_block_num + 1) { + let block = borsh::from_slice::( + &seq_client.get_block(block_id).await?.block, + )?; + + for tx in block.transactions { + let nssa_tx = NSSATransaction::try_from(&tx)?; + + if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx { + let mut affected_accounts = vec![]; + + for (acc_addr, (key_chain, _)) in + &wallet_core.storage.user_data.user_private_accounts + { + let view_tag = EncryptedAccountData::compute_view_tag( + key_chain.nullifer_public_key.clone(), + key_chain.incoming_viewing_public_key.clone(), + ); + + for (ciph_id, encrypted_data) in tx + .message() + .encrypted_private_post_states + .iter() + .enumerate() + { + if encrypted_data.view_tag == view_tag { + let ciphertext = &encrypted_data.ciphertext; + let commitment = &tx.message.new_commitments[ciph_id]; + let shared_secret = key_chain + .calculate_shared_secret_receiver(encrypted_data.epk.clone()); + + let res_acc = nssa_core::EncryptionScheme::decrypt( + ciphertext, + &shared_secret, + commitment, + ciph_id as u32, + ); + + if let Some(res_acc) = res_acc { + println!( + "Received new account for addr {acc_addr:#?} with account object {res_acc:#?}" + ); + + affected_accounts.push((*acc_addr, res_acc)); + } + } + } + } + + for (affected_addr, new_acc) in affected_accounts { + wallet_core + .storage + .insert_private_account_data(affected_addr, new_acc); + } + } + } + + wallet_core.store_persistent_accounts()?; + + println!( + "Block at id {block_id} with timestamp {} parsed", + block.timestamp + ); + } + + curr_last_block = latest_block_num + 1; + + tokio::time::sleep(std::time::Duration::from_millis( + config.seq_poll_timeout_millis, + )) + .await; + + latest_block_num = seq_client.get_last_block().await?.last_block; + } +} diff --git a/wallet/src/main.rs b/wallet/src/main.rs index b296400..9cab532 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Parser; use tokio::runtime::Builder; -use wallet::{Args, execute_subcommand}; +use wallet::{Args, execute_continious_run, execute_subcommand}; pub const NUM_THREADS: usize = 2; @@ -17,7 +17,13 @@ fn main() -> Result<()> { env_logger::init(); runtime.block_on(async move { - execute_subcommand(args.command).await.unwrap(); + if let Some(command) = args.command { + execute_subcommand(command).await.unwrap(); + } else if args.continious_run { + execute_continious_run().await.unwrap(); + } else { + println!("NOTHING TO DO"); + } }); Ok(()) From 28fe9e2113b3bdb083d5121c28fd70deab6ed32b Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Wed, 15 Oct 2025 15:44:52 +0300 Subject: [PATCH 3/6] fix: merge updates --- .../src/cli/native_token_transfer_program.rs | 28 ++++- wallet/src/cli/pinata_program.rs | 2 +- wallet/src/token_transfers/private.rs | 103 ++++++++++++++++-- wallet/src/token_transfers/shielded.rs | 89 ++++++++++++--- 4 files changed, 190 insertions(+), 32 deletions(-) diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/native_token_transfer_program.rs index 355c7f3..6d43d64 100644 --- a/wallet/src/cli/native_token_transfer_program.rs +++ b/wallet/src/cli/native_token_transfer_program.rs @@ -126,9 +126,17 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { let from: Address = from.parse().unwrap(); let to: Address = to.parse().unwrap(); - let (res, [secret_from, secret_to]) = wallet_core - .send_private_native_token_transfer_owned_account(from, to, amount) - .await?; + let to_initialization = wallet_core.check_private_account_initialized(&to).await?; + + let (res, [secret_from, secret_to]) = if let Some(to_proof) = to_initialization { + wallet_core + .send_private_native_token_transfer_owned_account_already_initialized(from, to, amount, to_proof) + .await? + } else { + wallet_core + .send_private_native_token_transfer_owned_account_not_initialized(from, to, amount) + .await? + }; println!("Results of tx send is {res:#?}"); @@ -206,9 +214,17 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let from: Address = from.parse().unwrap(); let to: Address = to.parse().unwrap(); - let (res, secret) = wallet_core - .send_shielded_native_token_transfer(from, to, amount) - .await?; + let to_initialization = wallet_core.check_private_account_initialized(&to).await?; + + let (res, secret) = if let Some(to_proof) = to_initialization { + wallet_core + .send_shielded_native_token_transfer_already_initialized(from, to, amount, to_proof) + .await? + } else { + wallet_core + .send_shielded_native_token_transfer_not_initialized(from, to, amount) + .await? + }; println!("Results of tx send is {res:#?}"); diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/pinata_program.rs index eb5c716..b197ae0 100644 --- a/wallet/src/cli/pinata_program.rs +++ b/wallet/src/cli/pinata_program.rs @@ -102,7 +102,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { pinata_addr, winner_addr, solution, - winner_proof + winner_proof, ) .await? } else { diff --git a/wallet/src/token_transfers/private.rs b/wallet/src/token_transfers/private.rs index 5253acd..0c6751b 100644 --- a/wallet/src/token_transfers/private.rs +++ b/wallet/src/token_transfers/private.rs @@ -6,7 +6,7 @@ use nssa::{ program::Program, }; use nssa_core::{ - Commitment, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata, + Commitment, MembershipProof, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata, encryption::IncomingViewingPublicKey, }; @@ -98,11 +98,12 @@ impl WalletCore { } } - pub async fn send_private_native_token_transfer_owned_account( + pub async fn send_private_native_token_transfer_owned_account_already_initialized( &self, from: Address, to: Address, balance_to_move: u128, + to_proof: MembershipProof, ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { let Some((from_keys, from_acc)) = self.storage.user_data.get_private_account(&from).cloned() @@ -124,7 +125,6 @@ impl WalletCore { let program = Program::authenticated_transfer_program(); let sender_commitment = Commitment::new(&from_npk, &from_acc); - let receiver_commitment = Commitment::new(&to_npk, &to_acc); let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk); let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk); @@ -153,14 +153,7 @@ impl WalletCore { .unwrap() .unwrap(), ), - ( - to_keys.private_key_holder.nullifier_secret_key, - self.sequencer_client - .get_proof_for_commitment(receiver_commitment) - .await - .unwrap() - .unwrap(), - ), + (to_keys.private_key_holder.nullifier_secret_key, to_proof), ], &program, ) @@ -196,4 +189,92 @@ impl WalletCore { Err(ExecutionFailureKind::InsufficientFundsError) } } + + pub async fn send_private_native_token_transfer_owned_account_not_initialized( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { + let Some((from_keys, from_acc)) = + self.storage.user_data.get_private_account(&from).cloned() + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, to_acc)) = self.storage.user_data.get_private_account(&to).cloned() + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let from_npk = from_keys.nullifer_public_key; + let from_ipk = from_keys.incoming_viewing_public_key; + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = Program::authenticated_transfer_program(); + + let sender_commitment = Commitment::new(&from_npk, &from_acc); + + let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk); + let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk); + + let eph_holder_from = EphemeralKeyHolder::new(&from_npk); + let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk); + + let eph_holder_to = EphemeralKeyHolder::new(&to_npk); + let shared_secret_to = eph_holder_to.calculate_shared_secret_sender(&to_ipk); + + let (output, proof) = circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 2], + &produce_random_nonces(2), + &[ + (from_npk.clone(), shared_secret_from.clone()), + (to_npk.clone(), shared_secret_to.clone()), + ], + &[( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = Message::try_from_circuit_output( + vec![], + vec![], + vec![ + ( + from_npk.clone(), + from_ipk.clone(), + eph_holder_from.generate_ephemeral_public_key(), + ), + ( + to_npk.clone(), + to_ipk.clone(), + eph_holder_to.generate_ephemeral_public_key(), + ), + ], + output, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, proof, &[]); + let tx = PrivacyPreservingTransaction::new(message, witness_set); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + [shared_secret_from, shared_secret_to], + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } } diff --git a/wallet/src/token_transfers/shielded.rs b/wallet/src/token_transfers/shielded.rs index 1cf1cb7..8fabedd 100644 --- a/wallet/src/token_transfers/shielded.rs +++ b/wallet/src/token_transfers/shielded.rs @@ -6,14 +6,84 @@ use nssa::{ program::Program, }; use nssa_core::{ - Commitment, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata, + MembershipProof, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata, encryption::IncomingViewingPublicKey, }; use crate::{WalletCore, helperfunctions::produce_random_nonces}; impl WalletCore { - pub async fn send_shielded_native_token_transfer( + pub async fn send_shielded_native_token_transfer_already_initialized( + &self, + from: Address, + to: Address, + balance_to_move: u128, + to_proof: MembershipProof, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let Ok(from_acc) = self.get_account_public(from).await else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, to_acc)) = self.storage.user_data.get_private_account(&to).cloned() + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = Program::authenticated_transfer_program(); + + let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from); + let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk); + + let eph_holder = EphemeralKeyHolder::new(&to_npk); + let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk); + + let (output, proof) = circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[0, 1], + &produce_random_nonces(1), + &[(to_npk.clone(), shared_secret.clone())], + &[(to_keys.private_key_holder.nullifier_secret_key, to_proof)], + &program, + ) + .unwrap(); + + let message = Message::try_from_circuit_output( + vec![from], + vec![from_acc.nonce], + vec![( + to_npk.clone(), + to_ipk.clone(), + eph_holder.generate_ephemeral_public_key(), + )], + output, + ) + .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, proof, &[signing_key]); + + let tx = PrivacyPreservingTransaction::new(message, witness_set); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secret, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } + + pub async fn send_shielded_native_token_transfer_not_initialized( &self, from: Address, to: Address, @@ -34,10 +104,8 @@ impl WalletCore { if from_acc.balance >= balance_to_move { let program = Program::authenticated_transfer_program(); - let receiver_commitment = Commitment::new(&to_npk, &to_acc); - let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from); - let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk); + let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk); let eph_holder = EphemeralKeyHolder::new(&to_npk); let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk); @@ -45,17 +113,10 @@ impl WalletCore { let (output, proof) = circuit::execute_and_prove( &[sender_pre, recipient_pre], &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), - &[0, 1], + &[0, 2], &produce_random_nonces(1), &[(to_npk.clone(), shared_secret.clone())], - &[( - to_keys.private_key_holder.nullifier_secret_key, - self.sequencer_client - .get_proof_for_commitment(receiver_commitment) - .await - .unwrap() - .unwrap(), - )], + &[], &program, ) .unwrap(); From 9614c3143bd7c305546b4c203a4501f54f4e1c69 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Thu, 16 Oct 2025 15:58:35 +0300 Subject: [PATCH 4/6] feat: integration test and async fs --- integration_tests/src/lib.rs | 196 ++++++++++++++---- wallet/src/cli/chain.rs | 6 +- .../src/cli/native_token_transfer_program.rs | 12 +- wallet/src/cli/pinata_program.rs | 2 +- wallet/src/cli/token_program.rs | 6 +- wallet/src/helperfunctions.rs | 21 +- wallet/src/lib.rs | 23 +- 7 files changed, 191 insertions(+), 75 deletions(-) diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index a560e9f..a1aa3e0 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -118,7 +118,7 @@ pub async fn test_success() { }, ); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -152,13 +152,13 @@ pub async fn test_success_move_to_another_account() { RegisterSubcommand::RegisterAccountPublic {}, )); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); wallet::execute_subcommand(command).await.unwrap(); - let persistent_accounts = fetch_persistent_accounts().unwrap(); + let persistent_accounts = fetch_persistent_accounts().await.unwrap(); let mut new_persistent_account_addr = String::new(); @@ -216,7 +216,7 @@ pub async fn test_failure() { }, ); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -256,7 +256,7 @@ pub async fn test_success_two_transactions() { }, ); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -317,7 +317,7 @@ pub async fn test_success_two_transactions() { pub async fn test_get_account() { info!("test_get_account"); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let account = seq_client @@ -338,7 +338,7 @@ pub async fn test_get_account() { /// This test creates a new token using the token program. After creating the token, the test executes a /// token transfer to a new account. pub async fn test_success_token_program() { - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); // Create new account for the token definition wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( @@ -359,7 +359,7 @@ pub async fn test_success_token_program() { .await .unwrap(); - let persistent_accounts = fetch_persistent_accounts().unwrap(); + let persistent_accounts = fetch_persistent_accounts().await.unwrap(); let mut new_persistent_accounts_addr = Vec::new(); @@ -485,7 +485,7 @@ pub async fn test_success_token_program() { /// This test creates a new private token using the token program. After creating the token, the test executes a /// private token transfer to a new account. All accounts are owned except definition. pub async fn test_success_token_program_private_owned() { - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { @@ -556,8 +556,10 @@ pub async fn test_success_token_program_private_owned() { ] ); - let wallet_config = fetch_config().unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_config = fetch_config().await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -579,8 +581,10 @@ pub async fn test_success_token_program_private_owned() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let wallet_config = fetch_config().unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_config = fetch_config().await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -607,8 +611,10 @@ pub async fn test_success_token_program_private_owned() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let wallet_config = fetch_config().unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_config = fetch_config().await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -624,7 +630,7 @@ pub async fn test_success_token_program_private_owned() { /// This test creates a new private token using the token program. After creating the token, the test executes a /// private token transfer to a new account. pub async fn test_success_token_program_private_claiming_path() { - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { @@ -695,8 +701,10 @@ pub async fn test_success_token_program_private_claiming_path() { ] ); - let wallet_config = fetch_config().unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_config = fetch_config().await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -740,8 +748,10 @@ pub async fn test_success_token_program_private_claiming_path() { wallet::execute_subcommand(command).await.unwrap(); - let wallet_config = fetch_config().unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_config = fetch_config().await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -772,9 +782,11 @@ pub async fn test_success_private_transfer_to_another_owned_account() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&from) @@ -812,9 +824,11 @@ pub async fn test_success_private_transfer_to_another_foreign_account() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&from) @@ -844,9 +858,11 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat panic!("FAILED TO REGISTER ACCOUNT"); }; - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) + .await + .unwrap(); let (to_keys, _) = wallet_storage .storage @@ -880,7 +896,9 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat }, )); wallet::execute_subcommand(command).await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&from) @@ -899,6 +917,78 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat info!("Success!"); } +pub async fn test_success_private_transfer_to_another_owned_account_cont_run_path() { + info!("test_success_private_transfer_to_another_owned_account_cont_run_path"); + let continious_run_handle = tokio::spawn(wallet::execute_continious_run()); + + let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); + + let command = Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + )); + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { + panic!("FAILED TO REGISTER ACCOUNT"); + }; + + let wallet_config = fetch_config().await.unwrap(); + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) + .await + .unwrap(); + + let (to_keys, _) = wallet_storage + .storage + .user_data + .user_private_accounts + .get(&to_addr) + .cloned() + .unwrap(); + + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( + NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + from: from.to_string(), + to_npk: hex::encode(to_keys.nullifer_public_key.0), + to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), + amount: 100, + }, + )); + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + panic!("FAILED TO SEND TX"); + }; + + let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; + + println!("Waiting for next blocks to check if continoius run fetch account"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); + + let new_commitment1 = wallet_storage + .get_private_account_commitment(&from) + .unwrap(); + assert_eq!(tx.message.new_commitments[0], new_commitment1); + + assert_eq!(tx.message.new_commitments.len(), 2); + for commitment in tx.message.new_commitments.into_iter() { + assert!(verify_commitment_is_in_state(commitment, &seq_client).await); + } + + let to_res_acc = wallet_storage.get_account_private(&to_addr).unwrap(); + + assert_eq!(to_res_acc.balance, 100); + + continious_run_handle.abort(); + + info!("Success!"); +} + pub async fn test_success_deshielded_transfer_to_another_account() { info!("test_success_deshielded_transfer_to_another_account"); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); @@ -911,9 +1001,11 @@ pub async fn test_success_deshielded_transfer_to_another_account() { }, ); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) + .await + .unwrap(); let from_acc = wallet_storage.get_account_private(&from).unwrap(); assert_eq!(from_acc.balance, 10000); @@ -923,7 +1015,9 @@ pub async fn test_success_deshielded_transfer_to_another_account() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let from_acc = wallet_storage.get_account_private(&from).unwrap(); let new_commitment = wallet_storage @@ -954,7 +1048,7 @@ pub async fn test_success_shielded_transfer_to_another_owned_account() { }, )); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); wallet::execute_subcommand(command).await.unwrap(); @@ -962,8 +1056,10 @@ pub async fn test_success_shielded_transfer_to_another_owned_account() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let wallet_config = fetch_config().unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_config = fetch_config().await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let acc_to = wallet_storage.get_account_private(&to).unwrap(); let new_commitment = wallet_storage.get_private_account_commitment(&to).unwrap(); @@ -995,7 +1091,7 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() { amount: 100, })); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1037,7 +1133,7 @@ pub async fn test_pinata() { }, )); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1085,7 +1181,7 @@ pub async fn test_pinata_private_receiver() { }, )); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1120,9 +1216,11 @@ pub async fn test_pinata_private_receiver() { )); wallet::execute_subcommand(command).await.unwrap(); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&ACC_SENDER_PRIVATE.parse().unwrap()) @@ -1159,7 +1257,7 @@ pub async fn test_pinata_private_receiver_new_account() { }, )); - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1181,9 +1279,11 @@ pub async fn test_pinata_private_receiver_new_account() { .unwrap() .balance; - let wallet_config = fetch_config().unwrap(); + let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&winner_addr) @@ -1287,6 +1387,12 @@ pub async fn main_tests_runner() -> Result<()> { "test_pinata_private_receiver_new_account" => { test_cleanup_wrap!(home_dir, test_pinata_private_receiver_new_account); } + "test_success_private_transfer_to_another_owned_account_cont_run_path" => { + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account_cont_run_path + ); + } "all" => { test_cleanup_wrap!(home_dir, test_success_move_to_another_account); test_cleanup_wrap!(home_dir, test_success); @@ -1322,6 +1428,10 @@ pub async fn main_tests_runner() -> Result<()> { test_cleanup_wrap!(home_dir, test_success_token_program_private_owned); test_cleanup_wrap!(home_dir, test_success_token_program_private_claiming_path); test_cleanup_wrap!(home_dir, test_pinata_private_receiver_new_account); + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account_cont_run_path + ); } "all_private" => { test_cleanup_wrap!( @@ -1352,6 +1462,10 @@ pub async fn main_tests_runner() -> Result<()> { test_cleanup_wrap!(home_dir, test_success_token_program_private_owned); test_cleanup_wrap!(home_dir, test_success_token_program_private_claiming_path); test_cleanup_wrap!(home_dir, test_pinata_private_receiver_new_account); + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account_cont_run_path + ); } _ => { anyhow::bail!("Unknown test name"); diff --git a/wallet/src/cli/chain.rs b/wallet/src/cli/chain.rs index ad58301..258c545 100644 --- a/wallet/src/cli/chain.rs +++ b/wallet/src/cli/chain.rs @@ -180,7 +180,7 @@ impl WalletSubcommand for FetchSubcommand { .insert_private_account_data(acc_addr, res_acc_to); } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -201,7 +201,7 @@ impl WalletSubcommand for RegisterSubcommand { println!("Generated new account with addr {addr}"); - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -223,7 +223,7 @@ impl WalletSubcommand for RegisterSubcommand { hex::encode(key.incoming_viewing_public_key.to_bytes()) ); - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/native_token_transfer_program.rs index 6d43d64..eb6310c 100644 --- a/wallet/src/cli/native_token_transfer_program.rs +++ b/wallet/src/cli/native_token_transfer_program.rs @@ -152,7 +152,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -194,7 +194,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -240,7 +240,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -273,7 +273,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let tx_hash = res.tx_hash; - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -323,7 +323,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { )?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -347,7 +347,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { println!("Transaction data is {transfer_tx:?}"); - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/pinata_program.rs index b197ae0..6ccfb97 100644 --- a/wallet/src/cli/pinata_program.rs +++ b/wallet/src/cli/pinata_program.rs @@ -131,7 +131,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/token_program.rs b/wallet/src/cli/token_program.rs index 7a17730..7bd3e88 100644 --- a/wallet/src/cli/token_program.rs +++ b/wallet/src/cli/token_program.rs @@ -174,7 +174,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -231,7 +231,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); @@ -281,7 +281,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts()?; + let path = wallet_core.store_persistent_accounts().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 20f4eec..a67b8ec 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -1,7 +1,8 @@ use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use nssa_core::account::Nonce; use rand::{RngCore, rngs::OsRng}; -use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr}; +use std::{path::PathBuf, str::FromStr}; +use tokio::io::AsyncReadExt; use anyhow::Result; use key_protocol::key_protocol_core::NSSAUserData; @@ -22,25 +23,25 @@ pub fn get_home() -> Result { } /// Fetch config from `NSSA_WALLET_HOME_DIR` -pub fn fetch_config() -> Result { +pub async fn fetch_config() -> Result { let config_home = get_home()?; - let file = File::open(config_home.join("wallet_config.json"))?; - let reader = BufReader::new(file); + let config_contents = tokio::fs::read(config_home.join("wallet_config.json")).await?; - Ok(serde_json::from_reader(reader)?) + Ok(serde_json::from_slice(&config_contents)?) } /// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json` /// /// If file not present, it is considered as empty list of persistent accounts -pub fn fetch_persistent_accounts() -> Result> { +pub async fn fetch_persistent_accounts() -> Result> { let home = get_home()?; let accs_path = home.join("curr_accounts.json"); + let mut persistent_accounts_content = vec![]; - match File::open(accs_path) { - Ok(file) => { - let reader = BufReader::new(file); - Ok(serde_json::from_reader(reader)?) + match tokio::fs::File::open(accs_path).await { + Ok(mut file) => { + file.read_to_end(&mut persistent_accounts_content).await?; + Ok(serde_json::from_slice(&persistent_accounts_content)?) } Err(err) => match err.kind() { std::io::ErrorKind::NotFound => Ok(vec![]), diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index ff17a10..ae96064 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -1,4 +1,4 @@ -use std::{fs::File, io::Write, path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use common::{ @@ -15,6 +15,7 @@ use nssa::{Account, Address, privacy_preserving_transaction::message::EncryptedA use clap::{Parser, Subcommand}; use nssa_core::{Commitment, MembershipProof}; +use tokio::io::AsyncWriteExt; use crate::cli::{ WalletSubcommand, chain::ChainSubcommand, @@ -47,13 +48,13 @@ pub struct WalletCore { } impl WalletCore { - pub fn start_from_config_update_chain(config: WalletConfig) -> Result { + pub async fn start_from_config_update_chain(config: WalletConfig) -> Result { let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); let tx_poller = TxPoller::new(config.clone(), client.clone()); let mut storage = WalletChainStore::new(config)?; - let persistent_accounts = fetch_persistent_accounts()?; + let persistent_accounts = fetch_persistent_accounts().await?; for pers_acc_data in persistent_accounts { storage.insert_account_data(pers_acc_data); } @@ -66,15 +67,15 @@ impl WalletCore { } ///Store persistent accounts at home - pub fn store_persistent_accounts(&self) -> Result { + pub async fn store_persistent_accounts(&self) -> Result { let home = get_home()?; let accs_path = home.join("curr_accounts.json"); let data = produce_data_for_storage(&self.storage.user_data); let accs = serde_json::to_vec_pretty(&data)?; - let mut accs_file = File::create(accs_path.as_path())?; - accs_file.write_all(&accs)?; + let mut accs_file = tokio::fs::File::create(accs_path.as_path()).await?; + accs_file.write_all(&accs).await?; info!("Stored accounts data at {accs_path:#?}"); @@ -221,8 +222,8 @@ pub enum SubcommandReturnValue { } pub async fn execute_subcommand(command: Command) -> Result { - let wallet_config = fetch_config()?; - let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?; + let wallet_config = fetch_config().await?; + let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?; let subcommand_ret = match command { Command::Transfer(transfer_subcommand) => { @@ -247,9 +248,9 @@ pub async fn execute_subcommand(command: Command) -> Result Result<()> { - let config = fetch_config()?; + let config = fetch_config().await?; let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); - let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone())?; + let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?; let mut latest_block_num = seq_client.get_last_block().await?.last_block; let mut curr_last_block = latest_block_num; @@ -312,7 +313,7 @@ pub async fn execute_continious_run() -> Result<()> { } } - wallet_core.store_persistent_accounts()?; + wallet_core.store_persistent_accounts().await?; println!( "Block at id {block_id} with timestamp {} parsed", From ff448cbb3ca824fe79d78d39558d0f220caf77b9 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Mon, 20 Oct 2025 09:20:22 +0300 Subject: [PATCH 5/6] fix: fmt --- integration_tests/src/lib.rs | 77 ++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index f86553a..bf919da 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -18,6 +18,7 @@ use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; use wallet::{ + Command, SubcommandReturnValue, WalletCore, cli::{ chain::{ChainSubcommand, FetchSubcommand, RegisterSubcommand}, native_token_transfer_program::{ @@ -28,9 +29,13 @@ use wallet::{ PinataProgramSubcommand, PinataProgramSubcommandPrivate, PinataProgramSubcommandPublic, }, token_program::{ - TokenProgramSubcommand, TokenProgramSubcommandDeshielded, TokenProgramSubcommandPrivate, TokenProgramSubcommandPublic, TokenProgramSubcommandShielded + TokenProgramSubcommand, TokenProgramSubcommandDeshielded, + TokenProgramSubcommandPrivate, TokenProgramSubcommandPublic, + TokenProgramSubcommandShielded, }, - }, config::PersistentAccountData, helperfunctions::{fetch_config, fetch_persistent_accounts}, Command, SubcommandReturnValue, WalletCore + }, + config::PersistentAccountData, + helperfunctions::{fetch_config, fetch_persistent_accounts}, }; #[derive(Parser, Debug)] @@ -769,26 +774,32 @@ pub async fn test_success_token_program_shielded_owned() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register(RegisterSubcommand::RegisterAccountPublic {}))) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register(RegisterSubcommand::RegisterAccountPublic {}))) - .await - .unwrap() + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register(RegisterSubcommand::RegisterAccountPrivate {}))) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; @@ -844,7 +855,9 @@ pub async fn test_success_token_program_shielded_owned() { tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment2 = wallet_storage .get_private_account_commitment(&recipient_addr) @@ -868,7 +881,9 @@ pub async fn test_success_token_program_shielded_owned() { tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment2 = wallet_storage .get_private_account_commitment(&recipient_addr) @@ -884,26 +899,32 @@ pub async fn test_success_token_program_deshielded_owned() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register(RegisterSubcommand::RegisterAccountPublic {}))) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register(RegisterSubcommand::RegisterAccountPrivate {}))) - .await - .unwrap() + wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPrivate {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register(RegisterSubcommand::RegisterAccountPublic {}))) - .await - .unwrap() + } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( + RegisterSubcommand::RegisterAccountPublic {}, + ))) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; @@ -945,7 +966,9 @@ pub async fn test_success_token_program_deshielded_owned() { ); let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -969,7 +992,9 @@ pub async fn test_success_token_program_deshielded_owned() { tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) @@ -993,7 +1018,9 @@ pub async fn test_success_token_program_deshielded_owned() { tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).await.unwrap(); + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); let new_commitment1 = wallet_storage .get_private_account_commitment(&supply_addr) From 28c1fd39a47d95297172e23326f2107f1c8dcf7c Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Mon, 20 Oct 2025 10:01:54 +0300 Subject: [PATCH 6/6] fix: suggestions added 1 --- integration_tests/src/lib.rs | 257 ++++++++--------- wallet/src/cli/account.rs | 253 +++++++++++++++++ wallet/src/cli/chain.rs | 250 ++-------------- wallet/src/cli/mod.rs | 1 + .../src/cli/native_token_transfer_program.rs | 266 +++++++++--------- wallet/src/cli/pinata_program.rs | 8 +- wallet/src/lib.rs | 10 +- wallet/src/main.rs | 5 +- 8 files changed, 539 insertions(+), 511 deletions(-) create mode 100644 wallet/src/cli/account.rs diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index bf919da..2c89fbd 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -20,7 +20,7 @@ use tokio::task::JoinHandle; use wallet::{ Command, SubcommandReturnValue, WalletCore, cli::{ - chain::{ChainSubcommand, FetchSubcommand, RegisterSubcommand}, + account::{AccountSubcommand, FetchSubcommand, RegisterSubcommand}, native_token_transfer_program::{ NativeTokenTransferProgramSubcommand, NativeTokenTransferProgramSubcommandPrivate, NativeTokenTransferProgramSubcommandShielded, @@ -112,13 +112,11 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) pub async fn test_success() { info!("test_success"); - let command = Command::Transfer( - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 100, - }, - ); + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }); let wallet_config = fetch_config().await.unwrap(); @@ -150,9 +148,7 @@ pub async fn test_success() { pub async fn test_success_move_to_another_account() { info!("test_success_move_to_another_account"); - let command = Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, - )); + let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Public {})); let wallet_config = fetch_config().await.unwrap(); @@ -176,13 +172,11 @@ pub async fn test_success_move_to_another_account() { panic!("Failed to produce new account, not present in persistent accounts"); } - let command = Command::Transfer( - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: new_persistent_account_addr.clone(), - amount: 100, - }, - ); + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { + from: ACC_SENDER.to_string(), + to: new_persistent_account_addr.clone(), + amount: 100, + }); wallet::execute_subcommand(command).await.unwrap(); @@ -210,13 +204,11 @@ pub async fn test_success_move_to_another_account() { pub async fn test_failure() { info!("test_failure"); - let command = Command::Transfer( - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 1000000, - }, - ); + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 1000000, + }); let wallet_config = fetch_config().await.unwrap(); @@ -250,13 +242,11 @@ pub async fn test_failure() { pub async fn test_success_two_transactions() { info!("test_success_two_transactions"); - let command = Command::Transfer( - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 100, - }, - ); + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }); let wallet_config = fetch_config().await.unwrap(); @@ -285,13 +275,11 @@ pub async fn test_success_two_transactions() { info!("First TX Success!"); - let command = Command::Transfer( - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), - amount: 100, - }, - ); + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }); wallet::execute_subcommand(command).await.unwrap(); @@ -343,20 +331,20 @@ pub async fn test_success_token_program() { let wallet_config = fetch_config().await.unwrap(); // Create new account for the token definition - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap(); // Create new account for the token supply holder - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap(); // Create new account for receiving a token transaction - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap(); @@ -492,8 +480,8 @@ pub async fn test_success_token_program_private_owned() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap() @@ -501,20 +489,18 @@ pub async fn test_success_token_program_private_owned() { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, - ))) - .await - .unwrap() - else { + let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand( + Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})), + ) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Private {}, ))) .await .unwrap() @@ -637,8 +623,8 @@ pub async fn test_success_token_program_private_claiming_path() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap() @@ -646,20 +632,18 @@ pub async fn test_success_token_program_private_claiming_path() { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, - ))) - .await - .unwrap() - else { + let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand( + Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})), + ) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Private {}, ))) .await .unwrap() @@ -740,13 +724,11 @@ pub async fn test_success_token_program_private_claiming_path() { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let command = Command::Chain(ChainSubcommand::Fetch( - FetchSubcommand::FetchPrivateAccount { - tx_hash, - acc_addr: recipient_addr.to_string(), - output_id: 1, - }, - )); + let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { + tx_hash, + acc_addr: recipient_addr.to_string(), + output_id: 1, + })); wallet::execute_subcommand(command).await.unwrap(); @@ -774,29 +756,27 @@ pub async fn test_success_token_program_shielded_owned() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap() else { panic!("invalid subcommand return value"); }; - // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, - ))) - .await - .unwrap() - else { + // Create new account for the token supply holder (public) + let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand( + Command::Account(AccountSubcommand::Register(RegisterSubcommand::Public {})), + ) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Private {}, ))) .await .unwrap() @@ -899,8 +879,8 @@ pub async fn test_success_token_program_deshielded_owned() { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { addr: definition_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap() @@ -908,20 +888,18 @@ pub async fn test_success_token_program_deshielded_owned() { panic!("invalid subcommand return value"); }; // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, - ))) - .await - .unwrap() - else { + let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand( + Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})), + ) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPublic {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( + RegisterSubcommand::Public {}, ))) .await .unwrap() @@ -1034,7 +1012,7 @@ pub async fn test_success_private_transfer_to_another_owned_account() { let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap(); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( - NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateOwnedAccount { + NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { from: from.to_string(), to: to.to_string(), amount: 100, @@ -1071,7 +1049,7 @@ pub async fn test_success_private_transfer_to_another_foreign_account() { let to_ipk = Secp256k1Point::from_scalar(to_npk.0); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( - NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + NativeTokenTransferProgramSubcommandPrivate::PrivateForeign { from: from.to_string(), to_npk: to_npk_string, to_ipk: hex::encode(to_ipk.0), @@ -1113,9 +1091,7 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat info!("test_success_private_transfer_to_another_owned_account_claiming_path"); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); - let command = Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, - )); + let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})); let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { @@ -1137,7 +1113,7 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat .unwrap(); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( - NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + NativeTokenTransferProgramSubcommandPrivate::PrivateForeign { from: from.to_string(), to_npk: hex::encode(to_keys.nullifer_public_key.0), to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), @@ -1152,13 +1128,11 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - let command = Command::Chain(ChainSubcommand::Fetch( - FetchSubcommand::FetchPrivateAccount { - tx_hash, - acc_addr: to_addr.to_string(), - output_id: 1, - }, - )); + let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { + tx_hash, + acc_addr: to_addr.to_string(), + output_id: 1, + })); wallet::execute_subcommand(command).await.unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) .await @@ -1187,9 +1161,7 @@ pub async fn test_success_private_transfer_to_another_owned_account_cont_run_pat let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); - let command = Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, - )); + let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})); let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { @@ -1211,7 +1183,7 @@ pub async fn test_success_private_transfer_to_another_owned_account_cont_run_pat .unwrap(); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private( - NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { + NativeTokenTransferProgramSubcommandPrivate::PrivateForeign { from: from.to_string(), to_npk: hex::encode(to_keys.nullifer_public_key.0), to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), @@ -1257,13 +1229,11 @@ pub async fn test_success_deshielded_transfer_to_another_account() { info!("test_success_deshielded_transfer_to_another_account"); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); let to: Address = ACC_RECEIVER.parse().unwrap(); - let command = Command::Transfer( - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferDeshielded { - from: from.to_string(), - to: to.to_string(), - amount: 100, - }, - ); + let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Deshielded { + from: from.to_string(), + to: to.to_string(), + amount: 100, + }); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1305,7 +1275,7 @@ pub async fn test_success_shielded_transfer_to_another_owned_account() { let from: Address = ACC_SENDER.parse().unwrap(); let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap(); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Shielded( - NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShielded { + NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { from: from.to_string(), to: to.to_string(), amount: 100, @@ -1348,12 +1318,13 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() { let from: Address = ACC_SENDER.parse().unwrap(); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Shielded( - NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShieldedForeignAccount { - from: from.to_string(), - to_npk: to_npk_string, - to_ipk: hex::encode(to_ipk.0), - amount: 100, - })); + NativeTokenTransferProgramSubcommandShielded::ShieldedForeign { + from: from.to_string(), + to_npk: to_npk_string, + to_ipk: hex::encode(to_ipk.0), + amount: 100, + }, + )); let wallet_config = fetch_config().await.unwrap(); @@ -1390,7 +1361,7 @@ pub async fn test_pinata() { let pinata_prize = 150; let solution = 989106; let command = Command::PinataProgram(PinataProgramSubcommand::Public( - PinataProgramSubcommandPublic::ClaimPinata { + PinataProgramSubcommandPublic::Claim { pinata_addr: pinata_addr.clone(), winner_addr: ACC_SENDER.to_string(), solution, @@ -1438,7 +1409,7 @@ pub async fn test_pinata_private_receiver() { let solution = 989106; let command = Command::PinataProgram(PinataProgramSubcommand::Private( - PinataProgramSubcommandPrivate::ClaimPinataPrivateReceiverOwned { + PinataProgramSubcommandPrivate::ClaimPrivateOwned { pinata_addr: pinata_addr.clone(), winner_addr: ACC_SENDER_PRIVATE.to_string(), solution, @@ -1471,13 +1442,11 @@ pub async fn test_pinata_private_receiver() { .unwrap() .balance; - let command = Command::Chain(ChainSubcommand::Fetch( - FetchSubcommand::FetchPrivateAccount { - tx_hash: tx_hash.clone(), - acc_addr: ACC_SENDER_PRIVATE.to_string(), - output_id: 0, - }, - )); + let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { + tx_hash: tx_hash.clone(), + acc_addr: ACC_SENDER_PRIVATE.to_string(), + output_id: 0, + })); wallet::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().await.unwrap(); @@ -1503,18 +1472,16 @@ pub async fn test_pinata_private_receiver_new_account() { let solution = 989106; // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { addr: winner_addr } = - wallet::execute_subcommand(Command::Chain(ChainSubcommand::Register( - RegisterSubcommand::RegisterAccountPrivate {}, - ))) - .await - .unwrap() - else { + let SubcommandReturnValue::RegisterAccount { addr: winner_addr } = wallet::execute_subcommand( + Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})), + ) + .await + .unwrap() else { panic!("invalid subcommand return value"); }; let command = Command::PinataProgram(PinataProgramSubcommand::Private( - PinataProgramSubcommandPrivate::ClaimPinataPrivateReceiverOwned { + PinataProgramSubcommandPrivate::ClaimPrivateOwned { pinata_addr: pinata_addr.clone(), winner_addr: winner_addr.to_string(), solution, diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs new file mode 100644 index 0000000..9ec4b20 --- /dev/null +++ b/wallet/src/cli/account.rs @@ -0,0 +1,253 @@ +use std::str::FromStr; + +use anyhow::Result; +use clap::Subcommand; +use common::transaction::NSSATransaction; +use nssa::Address; + +use crate::{ + SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::HumanReadableAccount, +}; + +///Represents generic chain CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum AccountSubcommand { + ///Get + #[command(subcommand)] + Get(GetSubcommand), + ///Fetch + #[command(subcommand)] + Fetch(FetchSubcommand), + ///Register + #[command(subcommand)] + Register(RegisterSubcommand), +} + +///Represents generic getter CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum GetSubcommand { + ///Get account `addr` balance + PublicAccountBalance { + #[arg(short, long)] + addr: String, + }, + ///Get account `addr` nonce + PublicAccountNonce { + #[arg(short, long)] + addr: String, + }, + ///Get account at address `addr` + PublicAccount { + #[arg(short, long)] + addr: String, + }, + ///Get private account with `addr` from storage + PrivateAccount { + #[arg(short, long)] + addr: String, + }, +} + +///Represents generic getter CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum FetchSubcommand { + ///Fetch transaction by `hash` + Tx { + #[arg(short, long)] + tx_hash: String, + }, + ///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id` + PrivateAccount { + ///tx_hash - valid 32 byte hex string + #[arg(long)] + tx_hash: String, + ///acc_addr - valid 32 byte hex string + #[arg(long)] + acc_addr: String, + ///output_id - id of the output in the transaction + #[arg(long)] + output_id: usize, + }, +} + +///Represents generic register CLI subcommand +#[derive(Subcommand, Debug, Clone)] +pub enum RegisterSubcommand { + ///Register new public account + Public {}, + ///Register new private account + Private {}, +} + +impl WalletSubcommand for GetSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + GetSubcommand::PublicAccountBalance { addr } => { + let addr = Address::from_str(&addr)?; + + let balance = wallet_core.get_account_balance(addr).await?; + println!("Accounts {addr} balance is {balance}"); + + Ok(SubcommandReturnValue::Empty) + } + GetSubcommand::PublicAccountNonce { addr } => { + let addr = Address::from_str(&addr)?; + + let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; + println!("Accounts {addr} nonce is {nonce}"); + + Ok(SubcommandReturnValue::Empty) + } + GetSubcommand::PublicAccount { addr } => { + let addr: Address = addr.parse()?; + let account = wallet_core.get_account_public(addr).await?; + let account_hr: HumanReadableAccount = account.clone().into(); + println!("{}", serde_json::to_string(&account_hr).unwrap()); + + Ok(SubcommandReturnValue::Account(account)) + } + GetSubcommand::PrivateAccount { addr } => { + let addr: Address = addr.parse()?; + if let Some(account) = wallet_core.get_account_private(&addr) { + println!("{}", serde_json::to_string(&account).unwrap()); + } else { + println!("Private account not found."); + } + Ok(SubcommandReturnValue::Empty) + } + } + } +} + +impl WalletSubcommand for FetchSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + FetchSubcommand::Tx { tx_hash } => { + let tx_obj = wallet_core + .sequencer_client + .get_transaction_by_hash(tx_hash) + .await?; + + println!("Transaction object {tx_obj:#?}"); + + Ok(SubcommandReturnValue::Empty) + } + FetchSubcommand::PrivateAccount { + tx_hash, + acc_addr, + output_id: ciph_id, + } => { + let acc_addr: Address = acc_addr.parse().unwrap(); + + let account_key_chain = wallet_core + .storage + .user_data + .user_private_accounts + .get(&acc_addr); + + let Some((account_key_chain, _)) = account_key_chain else { + anyhow::bail!("Account not found"); + }; + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone(); + let to_comm = tx.message.new_commitments[ciph_id].clone(); + let shared_secret = + account_key_chain.calculate_shared_secret_receiver(to_ebc.epk); + + let res_acc_to = nssa_core::EncryptionScheme::decrypt( + &to_ebc.ciphertext, + &shared_secret, + &to_comm, + ciph_id as u32, + ) + .unwrap(); + + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + + wallet_core + .storage + .insert_private_account_data(acc_addr, res_acc_to); + } + + let path = wallet_core.store_persistent_accounts().await?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::Empty) + } + } + } +} + +impl WalletSubcommand for RegisterSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + RegisterSubcommand::Public {} => { + let addr = wallet_core.create_new_account_public(); + + println!("Generated new account with addr {addr}"); + + let path = wallet_core.store_persistent_accounts().await?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::RegisterAccount { addr }) + } + RegisterSubcommand::Private {} => { + let addr = wallet_core.create_new_account_private(); + + let (key, _) = wallet_core + .storage + .user_data + .get_private_account(&addr) + .unwrap(); + + println!("Generated new account with addr {addr}"); + println!("With npk {}", hex::encode(&key.nullifer_public_key)); + println!( + "With ipk {}", + hex::encode(key.incoming_viewing_public_key.to_bytes()) + ); + + let path = wallet_core.store_persistent_accounts().await?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::RegisterAccount { addr }) + } + } + } +} + +impl WalletSubcommand for AccountSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + AccountSubcommand::Get(get_subcommand) => { + get_subcommand.handle_subcommand(wallet_core).await + } + AccountSubcommand::Fetch(fetch_subcommand) => { + fetch_subcommand.handle_subcommand(wallet_core).await + } + AccountSubcommand::Register(register_subcommand) => { + register_subcommand.handle_subcommand(wallet_core).await + } + } + } +} diff --git a/wallet/src/cli/chain.rs b/wallet/src/cli/chain.rs index 258c545..4db18fc 100644 --- a/wallet/src/cli/chain.rs +++ b/wallet/src/cli/chain.rs @@ -1,236 +1,20 @@ -use std::str::FromStr; - use anyhow::Result; use clap::Subcommand; -use common::transaction::NSSATransaction; -use nssa::Address; -use crate::{ - SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::HumanReadableAccount, -}; +use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; ///Represents generic chain CLI subcommand #[derive(Subcommand, Debug, Clone)] pub enum ChainSubcommand { - ///Get - #[command(subcommand)] - Get(GetSubcommand), - ///Fetch - #[command(subcommand)] - Fetch(FetchSubcommand), - ///Register - #[command(subcommand)] - Register(RegisterSubcommand), -} - -///Represents generic getter CLI subcommand -#[derive(Subcommand, Debug, Clone)] -pub enum GetSubcommand { - ///Get account `addr` balance - GetPublicAccountBalance { + GetLatestBlockId {}, + GetBlockAtId { #[arg(short, long)] - addr: String, + id: u64, }, - ///Get account `addr` nonce - GetPublicAccountNonce { + GetTransactionAtHash { #[arg(short, long)] - addr: String, + hash: String, }, - ///Get account at address `addr` - GetPublicAccount { - #[arg(short, long)] - addr: String, - }, - ///Get private account with `addr` from storage - GetPrivateAccount { - #[arg(short, long)] - addr: String, - }, -} - -///Represents generic getter CLI subcommand -#[derive(Subcommand, Debug, Clone)] -pub enum FetchSubcommand { - ///Fetch transaction by `hash` - FetchTx { - #[arg(short, long)] - tx_hash: String, - }, - ///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id` - FetchPrivateAccount { - ///tx_hash - valid 32 byte hex string - #[arg(long)] - tx_hash: String, - ///acc_addr - valid 32 byte hex string - #[arg(long)] - acc_addr: String, - ///output_id - id of the output in the transaction - #[arg(long)] - output_id: usize, - }, -} - -///Represents generic register CLI subcommand -#[derive(Subcommand, Debug, Clone)] -pub enum RegisterSubcommand { - ///Register new public account - RegisterAccountPublic {}, - ///Register new private account - RegisterAccountPrivate {}, -} - -impl WalletSubcommand for GetSubcommand { - async fn handle_subcommand( - self, - wallet_core: &mut WalletCore, - ) -> Result { - match self { - GetSubcommand::GetPublicAccountBalance { addr } => { - let addr = Address::from_str(&addr)?; - - let balance = wallet_core.get_account_balance(addr).await?; - println!("Accounts {addr} balance is {balance}"); - - Ok(SubcommandReturnValue::Empty) - } - GetSubcommand::GetPublicAccountNonce { addr } => { - let addr = Address::from_str(&addr)?; - - let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; - println!("Accounts {addr} nonce is {nonce}"); - - Ok(SubcommandReturnValue::Empty) - } - GetSubcommand::GetPublicAccount { addr } => { - let addr: Address = addr.parse()?; - let account = wallet_core.get_account_public(addr).await?; - let account_hr: HumanReadableAccount = account.clone().into(); - println!("{}", serde_json::to_string(&account_hr).unwrap()); - - Ok(SubcommandReturnValue::Account(account)) - } - GetSubcommand::GetPrivateAccount { addr } => { - let addr: Address = addr.parse()?; - if let Some(account) = wallet_core.get_account_private(&addr) { - println!("{}", serde_json::to_string(&account).unwrap()); - } else { - println!("Private account not found."); - } - Ok(SubcommandReturnValue::Empty) - } - } - } -} - -impl WalletSubcommand for FetchSubcommand { - async fn handle_subcommand( - self, - wallet_core: &mut WalletCore, - ) -> Result { - match self { - FetchSubcommand::FetchTx { tx_hash } => { - let tx_obj = wallet_core - .sequencer_client - .get_transaction_by_hash(tx_hash) - .await?; - - println!("Transaction object {tx_obj:#?}"); - - Ok(SubcommandReturnValue::Empty) - } - FetchSubcommand::FetchPrivateAccount { - tx_hash, - acc_addr, - output_id: ciph_id, - } => { - let acc_addr: Address = acc_addr.parse().unwrap(); - - let account_key_chain = wallet_core - .storage - .user_data - .user_private_accounts - .get(&acc_addr); - - let Some((account_key_chain, _)) = account_key_chain else { - anyhow::bail!("Account not found"); - }; - - let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; - - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone(); - let to_comm = tx.message.new_commitments[ciph_id].clone(); - let shared_secret = - account_key_chain.calculate_shared_secret_receiver(to_ebc.epk); - - let res_acc_to = nssa_core::EncryptionScheme::decrypt( - &to_ebc.ciphertext, - &shared_secret, - &to_comm, - ciph_id as u32, - ) - .unwrap(); - - println!("RES acc to {res_acc_to:#?}"); - - println!("Transaction data is {:?}", tx.message); - - wallet_core - .storage - .insert_private_account_data(acc_addr, res_acc_to); - } - - let path = wallet_core.store_persistent_accounts().await?; - - println!("Stored persistent accounts at {path:#?}"); - - Ok(SubcommandReturnValue::Empty) - } - } - } -} - -impl WalletSubcommand for RegisterSubcommand { - async fn handle_subcommand( - self, - wallet_core: &mut WalletCore, - ) -> Result { - match self { - RegisterSubcommand::RegisterAccountPublic {} => { - let addr = wallet_core.create_new_account_public(); - - println!("Generated new account with addr {addr}"); - - let path = wallet_core.store_persistent_accounts().await?; - - println!("Stored persistent accounts at {path:#?}"); - - Ok(SubcommandReturnValue::RegisterAccount { addr }) - } - RegisterSubcommand::RegisterAccountPrivate {} => { - let addr = wallet_core.create_new_account_private(); - - let (key, _) = wallet_core - .storage - .user_data - .get_private_account(&addr) - .unwrap(); - - println!("Generated new account with addr {addr}"); - println!("With npk {}", hex::encode(&key.nullifer_public_key)); - println!( - "With ipk {}", - hex::encode(key.incoming_viewing_public_key.to_bytes()) - ); - - let path = wallet_core.store_persistent_accounts().await?; - - println!("Stored persistent accounts at {path:#?}"); - - Ok(SubcommandReturnValue::RegisterAccount { addr }) - } - } - } } impl WalletSubcommand for ChainSubcommand { @@ -239,15 +23,25 @@ impl WalletSubcommand for ChainSubcommand { wallet_core: &mut WalletCore, ) -> Result { match self { - ChainSubcommand::Get(get_subcommand) => { - get_subcommand.handle_subcommand(wallet_core).await + ChainSubcommand::GetLatestBlockId {} => { + let latest_block_res = wallet_core.sequencer_client.get_last_block().await?; + + println!("Last block id is {}", latest_block_res.last_block); } - ChainSubcommand::Fetch(fetch_subcommand) => { - fetch_subcommand.handle_subcommand(wallet_core).await + ChainSubcommand::GetBlockAtId { id } => { + let block_res = wallet_core.sequencer_client.get_block(id).await?; + + println!("Last block id is {:#?}", block_res.block); } - ChainSubcommand::Register(register_subcommand) => { - register_subcommand.handle_subcommand(wallet_core).await + ChainSubcommand::GetTransactionAtHash { hash } => { + let tx_res = wallet_core + .sequencer_client + .get_transaction_by_hash(hash) + .await?; + + println!("Last block id is {:#?}", tx_res.transaction); } } + Ok(SubcommandReturnValue::Empty) } } diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index 093a501..3aa1b7f 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -2,6 +2,7 @@ use anyhow::Result; use crate::{SubcommandReturnValue, WalletCore}; +pub mod account; pub mod chain; pub mod native_token_transfer_program; pub mod pinata_program; diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/native_token_transfer_program.rs index eb6310c..666ea68 100644 --- a/wallet/src/cli/native_token_transfer_program.rs +++ b/wallet/src/cli/native_token_transfer_program.rs @@ -11,7 +11,7 @@ pub enum NativeTokenTransferProgramSubcommand { ///Send native token transfer from `from` to `to` for `amount` /// /// Public operation - SendNativeTokenTransferPublic { + Public { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -28,7 +28,7 @@ pub enum NativeTokenTransferProgramSubcommand { ///Send native token transfer from `from` to `to` for `amount` /// /// Deshielded operation - SendNativeTokenTransferDeshielded { + Deshielded { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -50,7 +50,7 @@ pub enum NativeTokenTransferProgramSubcommandShielded { ///Send native token transfer from `from` to `to` for `amount` /// /// Shielded operation - SendNativeTokenTransferShielded { + ShieldedOwned { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -64,7 +64,7 @@ pub enum NativeTokenTransferProgramSubcommandShielded { ///Send native token transfer from `from` to `to` for `amount` /// /// Shielded operation - SendNativeTokenTransferShieldedForeignAccount { + ShieldedForeign { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -86,7 +86,7 @@ pub enum NativeTokenTransferProgramSubcommandPrivate { ///Send native token transfer from `from` to `to` for `amount` /// /// Private operation - SendNativeTokenTransferPrivateOwnedAccount { + PrivateOwned { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -100,7 +100,7 @@ pub enum NativeTokenTransferProgramSubcommandPrivate { ///Send native token transfer from `from` to `to` for `amount` /// /// Private operation - SendNativeTokenTransferPrivateForeignAccount { + PrivateForeign { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -122,84 +122,92 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { wallet_core: &mut WalletCore, ) -> Result { match self { - NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateOwnedAccount { from, to, amount } => { - let from: Address = from.parse().unwrap(); - let to: Address = to.parse().unwrap(); + NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { from, to, amount } => { + let from: Address = from.parse().unwrap(); + let to: Address = to.parse().unwrap(); - let to_initialization = wallet_core.check_private_account_initialized(&to).await?; + let to_initialization = wallet_core.check_private_account_initialized(&to).await?; - let (res, [secret_from, secret_to]) = if let Some(to_proof) = to_initialization { - wallet_core - .send_private_native_token_transfer_owned_account_already_initialized(from, to, amount, to_proof) - .await? - } else { - wallet_core - .send_private_native_token_transfer_owned_account_not_initialized(from, to, amount) - .await? - }; + let (res, [secret_from, secret_to]) = if let Some(to_proof) = to_initialization { + wallet_core + .send_private_native_token_transfer_owned_account_already_initialized( + from, to, amount, to_proof, + ) + .await? + } else { + wallet_core + .send_private_native_token_transfer_owned_account_not_initialized( + from, to, amount, + ) + .await? + }; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send is {res:#?}"); - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_from, from), (secret_to, to)]; + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret_from, from), (secret_to, to)]; - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; + wallet_core.decode_insert_privacy_preserving_transaction_results( + tx, + &acc_decode_data, + )?; + } + + let path = wallet_core.store_persistent_accounts().await?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } + NativeTokenTransferProgramSubcommandPrivate::PrivateForeign { + from, + to_npk, + to_ipk, + amount, + } => { + let from: Address = from.parse().unwrap(); + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); - let path = wallet_core.store_persistent_accounts().await?; + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); - println!("Stored persistent accounts at {path:#?}"); + let (res, [secret_from, _]) = wallet_core + .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + .await?; - Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) - } - NativeTokenTransferProgramSubcommandPrivate::SendNativeTokenTransferPrivateForeignAccount { - from, - to_npk, - to_ipk, - amount, - } => { - let from: Address = from.parse().unwrap(); - let to_npk_res = hex::decode(to_npk)?; - let mut to_npk = [0; 32]; - to_npk.copy_from_slice(&to_npk_res); - let to_npk = nssa_core::NullifierPublicKey(to_npk); + println!("Results of tx send is {res:#?}"); - let to_ipk_res = hex::decode(to_ipk)?; - let mut to_ipk = [0u8; 33]; - to_ipk.copy_from_slice(&to_ipk_res); - let to_ipk = - nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; - let (res, [secret_from, _]) = wallet_core - .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) - .await?; + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret_from, from)]; - println!("Results of tx send is {res:#?}"); + wallet_core.decode_insert_privacy_preserving_transaction_results( + tx, + &acc_decode_data, + )?; + } - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; + let path = wallet_core.store_persistent_accounts().await?; - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_from, from)]; + println!("Stored persistent accounts at {path:#?}"); - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } - - let path = wallet_core.store_persistent_accounts().await?; - - println!("Stored persistent accounts at {path:#?}"); - - Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) - } } } } @@ -210,75 +218,79 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { wallet_core: &mut WalletCore, ) -> Result { match self { - NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShielded { from, to, amount } => { - let from: Address = from.parse().unwrap(); - let to: Address = to.parse().unwrap(); + NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { from, to, amount } => { + let from: Address = from.parse().unwrap(); + let to: Address = to.parse().unwrap(); - let to_initialization = wallet_core.check_private_account_initialized(&to).await?; + let to_initialization = wallet_core.check_private_account_initialized(&to).await?; - let (res, secret) = if let Some(to_proof) = to_initialization { - wallet_core - .send_shielded_native_token_transfer_already_initialized(from, to, amount, to_proof) - .await? - } else { - wallet_core - .send_shielded_native_token_transfer_not_initialized(from, to, amount) - .await? - }; + let (res, secret) = if let Some(to_proof) = to_initialization { + wallet_core + .send_shielded_native_token_transfer_already_initialized( + from, to, amount, to_proof, + ) + .await? + } else { + wallet_core + .send_shielded_native_token_transfer_not_initialized(from, to, amount) + .await? + }; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send is {res:#?}"); - let tx_hash = res.tx_hash; - let transfer_tx = wallet_core - .poll_native_token_transfer(tx_hash.clone()) - .await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; - if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret, to)]; + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let acc_decode_data = vec![(secret, to)]; - wallet_core - .decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?; + wallet_core.decode_insert_privacy_preserving_transaction_results( + tx, + &acc_decode_data, + )?; + } + + let path = wallet_core.store_persistent_accounts().await?; + + println!("Stored persistent accounts at {path:#?}"); + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } + NativeTokenTransferProgramSubcommandShielded::ShieldedForeign { + from, + to_npk, + to_ipk, + amount, + } => { + let from: Address = from.parse().unwrap(); - let path = wallet_core.store_persistent_accounts().await?; + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); - println!("Stored persistent accounts at {path:#?}"); + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); - Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) - } - NativeTokenTransferProgramSubcommandShielded::SendNativeTokenTransferShieldedForeignAccount { - from, - to_npk, - to_ipk, - amount, - } => { - let from: Address = from.parse().unwrap(); + let (res, _) = wallet_core + .send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + .await?; - let to_npk_res = hex::decode(to_npk)?; - let mut to_npk = [0; 32]; - to_npk.copy_from_slice(&to_npk_res); - let to_npk = nssa_core::NullifierPublicKey(to_npk); + println!("Results of tx send is {res:#?}"); - let to_ipk_res = hex::decode(to_ipk)?; - let mut to_ipk = [0u8; 33]; - to_ipk.copy_from_slice(&to_ipk_res); - let to_ipk = - nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + let tx_hash = res.tx_hash; - let (res, _) = wallet_core - .send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) - .await?; + let path = wallet_core.store_persistent_accounts().await?; - println!("Results of tx send is {res:#?}"); + println!("Stored persistent accounts at {path:#?}"); - let tx_hash = res.tx_hash; - - let path = wallet_core.store_persistent_accounts().await?; - - println!("Stored persistent accounts at {path:#?}"); - - Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) - } + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } } } } @@ -295,11 +307,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { NativeTokenTransferProgramSubcommand::Shielded(shielded_subcommand) => { shielded_subcommand.handle_subcommand(wallet_core).await } - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferDeshielded { - from, - to, - amount, - } => { + NativeTokenTransferProgramSubcommand::Deshielded { from, to, amount } => { let from: Address = from.parse().unwrap(); let to: Address = to.parse().unwrap(); @@ -329,11 +337,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } - NativeTokenTransferProgramSubcommand::SendNativeTokenTransferPublic { - from, - to, - amount, - } => { + NativeTokenTransferProgramSubcommand::Public { from, to, amount } => { let from: Address = from.parse().unwrap(); let to: Address = to.parse().unwrap(); diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/pinata_program.rs index 6ccfb97..75d3d6a 100644 --- a/wallet/src/cli/pinata_program.rs +++ b/wallet/src/cli/pinata_program.rs @@ -21,7 +21,7 @@ pub enum PinataProgramSubcommand { pub enum PinataProgramSubcommandPublic { // TODO: Testnet only. Refactor to prevent compilation on mainnet. // Claim piñata prize - ClaimPinata { + Claim { ///pinata_addr - valid 32 byte hex string #[arg(long)] pinata_addr: String, @@ -39,7 +39,7 @@ pub enum PinataProgramSubcommandPublic { pub enum PinataProgramSubcommandPrivate { // TODO: Testnet only. Refactor to prevent compilation on mainnet. // Claim piñata prize - ClaimPinataPrivateReceiverOwned { + ClaimPrivateOwned { ///pinata_addr - valid 32 byte hex string #[arg(long)] pinata_addr: String, @@ -58,7 +58,7 @@ impl WalletSubcommand for PinataProgramSubcommandPublic { wallet_core: &mut WalletCore, ) -> Result { match self { - PinataProgramSubcommandPublic::ClaimPinata { + PinataProgramSubcommandPublic::Claim { pinata_addr, winner_addr, solution, @@ -84,7 +84,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { wallet_core: &mut WalletCore, ) -> Result { match self { - PinataProgramSubcommandPrivate::ClaimPinataPrivateReceiverOwned { + PinataProgramSubcommandPrivate::ClaimPrivateOwned { pinata_addr, winner_addr, solution, diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index ae96064..501d096 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -18,7 +18,7 @@ use nssa_core::{Commitment, MembershipProof}; use tokio::io::AsyncWriteExt; use crate::cli::{ - WalletSubcommand, chain::ChainSubcommand, + WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand, native_token_transfer_program::NativeTokenTransferProgramSubcommand, pinata_program::PinataProgramSubcommand, }; @@ -193,6 +193,9 @@ pub enum Command { ///Chain command #[command(subcommand)] Chain(ChainSubcommand), + ///Chain command + #[command(subcommand)] + Account(AccountSubcommand), ///Pinata command #[command(subcommand)] PinataProgram(PinataProgramSubcommand), @@ -234,6 +237,11 @@ pub async fn execute_subcommand(command: Command) -> Result { chain_subcommand.handle_subcommand(&mut wallet_core).await? } + Command::Account(account_subcommand) => { + account_subcommand + .handle_subcommand(&mut wallet_core) + .await? + } Command::PinataProgram(pinata_subcommand) => { pinata_subcommand .handle_subcommand(&mut wallet_core) diff --git a/wallet/src/main.rs b/wallet/src/main.rs index 9cab532..ecc50d2 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use clap::Parser; +use clap::{CommandFactory, Parser}; use tokio::runtime::Builder; use wallet::{Args, execute_continious_run, execute_subcommand}; @@ -22,7 +22,8 @@ fn main() -> Result<()> { } else if args.continious_run { execute_continious_run().await.unwrap(); } else { - println!("NOTHING TO DO"); + let help = Args::command().render_long_help(); + println!("{help}"); } });