From 5840f9b779b937639834e82b1a873680d62739c4 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Tue, 28 Oct 2025 16:02:30 +0200 Subject: [PATCH] fix: cli full refactor --- common/src/lib.rs | 2 + integration_tests/src/lib.rs | 23 +- integration_tests/src/test_suite_map.rs | 87 +++----- sequencer_core/src/sequencer_store/mod.rs | 4 +- wallet/src/cli/account.rs | 134 +++-------- .../src/cli/native_token_transfer_program.rs | 40 ++-- wallet/src/cli/pinata_program.rs | 56 ++++- wallet/src/cli/token_program.rs | 46 ++-- wallet/src/config.rs | 6 + wallet/src/helperfunctions.rs | 32 ++- wallet/src/lib.rs | 210 +++++++++--------- 11 files changed, 324 insertions(+), 316 deletions(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index c44d03f..7976479 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -8,3 +8,5 @@ pub mod transaction; //TODO: Compile only for tests pub mod test_utils; pub type HashType = [u8; 32]; + +pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7"; diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index f09d808..f718f9d 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -43,11 +43,11 @@ pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &[u8] = include_bytes!("data_changer.bin"); fn make_public_account_input_from_str(addr: &str) -> String { - format!("Public/{addr:?}") + format!("Public/{addr}") } fn make_private_account_input_from_str(addr: &str) -> String { - format!("Private/{addr:?}") + format!("Private/{addr}") } #[allow(clippy::type_complexity)] @@ -92,7 +92,7 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) seq_http_server_handle.stop(true).await; let wallet_home = wallet::helperfunctions::get_home().unwrap(); - let persistent_data_home = wallet_home.join("curr_accounts.json"); + let persistent_data_home = wallet_home.join("storage.json"); //Removing persistent accounts after run to not affect other executions //Not necessary an error, if fails as there is tests for failure scenario @@ -163,3 +163,20 @@ async fn verify_commitment_is_in_state( Ok(Some(_)) ) } + +#[cfg(test)] +mod tests { + use crate::{make_private_account_input_from_str, make_public_account_input_from_str}; + + #[test] + fn correct_addr_from_prefix() { + let addr1 = "cafecafe"; + let addr2 = "deadbeaf"; + + let addr1_pub = make_public_account_input_from_str(addr1); + let addr2_priv = make_private_account_input_from_str(addr2); + + assert_eq!(addr1_pub, "Public/cafecafe".to_string()); + assert_eq!(addr2_priv, "Private/deadbeaf".to_string()); + } +} diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs index ec75a32..0bf8f00 100644 --- a/integration_tests/src/test_suite_map.rs +++ b/integration_tests/src/test_suite_map.rs @@ -1,25 +1,21 @@ use std::{collections::HashMap, path::PathBuf, pin::Pin, time::Duration}; -use common::sequencer_client::SequencerClient; +use common::{PINATA_BASE58, sequencer_client::SequencerClient}; use log::info; use nssa::{Address, ProgramDeploymentTransaction, program::Program}; use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; use wallet::{ Command, SubcommandReturnValue, WalletCore, cli::{ - account::{AccountSubcommand, FetchSubcommand, NewSubcommand}, + account::{AccountSubcommand, NewSubcommand}, native_token_transfer_program::AuthTransferSubcommand, - pinata_program::{ - PinataProgramSubcommand, PinataProgramSubcommandPrivate, PinataProgramSubcommandPublic, - }, + pinata_program::PinataProgramAgnosticSubcommand, token_program::TokenProgramAgnosticSubcommand, }, - config::PersistentAccountData, - helperfunctions::{fetch_config, fetch_persistent_accounts}, + config::{PersistentAccountData, PersistentStorage}, + helperfunctions::{fetch_config, fetch_persistent_storage}, }; -use sequencer_core::sequencer_store::PINATA_BASE58; - use crate::{ ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE, NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, @@ -83,7 +79,10 @@ pub fn prepare_function_map() -> HashMap { wallet::execute_subcommand(command).await.unwrap(); - let persistent_accounts = fetch_persistent_accounts().await.unwrap(); + let PersistentStorage { + accounts: persistent_accounts, + last_synced_block: _, + } = fetch_persistent_storage().await.unwrap(); let mut new_persistent_account_addr = String::new(); @@ -290,7 +289,10 @@ pub fn prepare_function_map() -> HashMap { .await .unwrap(); - let persistent_accounts = fetch_persistent_accounts().await.unwrap(); + let PersistentStorage { + accounts: persistent_accounts, + last_synced_block: _, + } = fetch_persistent_storage().await.unwrap(); let mut new_persistent_accounts_addr = Vec::new(); @@ -668,7 +670,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap() @@ -679,11 +681,7 @@ pub fn prepare_function_map() -> HashMap { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { - tx_hash, - acc_addr: recipient_addr.to_string(), - output_id: 1, - })); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); wallet::execute_subcommand(command).await.unwrap(); @@ -1096,11 +1094,7 @@ pub fn prepare_function_map() -> HashMap { let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { - tx_hash, - acc_addr: to_addr.to_string(), - output_id: 1, - })); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); wallet::execute_subcommand(command).await.unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) .await @@ -1335,13 +1329,10 @@ pub fn prepare_function_map() -> HashMap { let pinata_addr = PINATA_BASE58; let pinata_prize = 150; let solution = 989106; - let command = Command::Pinata(PinataProgramSubcommand::Public( - PinataProgramSubcommandPublic::Claim { - pinata_addr: pinata_addr.to_string(), - winner_addr: ACC_SENDER.to_string(), - solution, - }, - )); + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to_addr: make_public_account_input_from_str(ACC_SENDER), + solution, + }); let wallet_config = fetch_config().await.unwrap(); @@ -1427,14 +1418,18 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_authenticated_transfer_initialize_function() { info!("test initialize account for authenticated transfer"); - let command = Command::AuthenticatedTransferInitializePublicAccount {}; - + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {})); let SubcommandReturnValue::RegisterAccount { addr } = wallet::execute_subcommand(command).await.unwrap() else { panic!("Error creating account"); }; + let command = Command::AuthTransfer(AuthTransferSubcommand::Init { + addr: addr.to_string(), + }); + wallet::execute_subcommand(command).await.unwrap(); + info!("Checking correct execution"); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1463,13 +1458,10 @@ pub fn prepare_function_map() -> HashMap { let pinata_prize = 150; let solution = 989106; - let command = Command::Pinata(PinataProgramSubcommand::Private( - PinataProgramSubcommandPrivate::ClaimPrivateOwned { - pinata_addr: pinata_addr.to_string(), - winner_addr: ACC_SENDER_PRIVATE.to_string(), - solution, - }, - )); + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to_addr: make_private_account_input_from_str(ACC_SENDER_PRIVATE), + solution, + }); let wallet_config = fetch_config().await.unwrap(); @@ -1481,7 +1473,7 @@ pub fn prepare_function_map() -> HashMap { .unwrap() .balance; - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = wallet::execute_subcommand(command).await.unwrap() else { panic!("invalid subcommand return value"); @@ -1497,11 +1489,7 @@ pub fn prepare_function_map() -> HashMap { .unwrap() .balance; - let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { - tx_hash: tx_hash.clone(), - acc_addr: ACC_SENDER_PRIVATE.to_string(), - output_id: 0, - })); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); wallet::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().await.unwrap(); @@ -1538,13 +1526,10 @@ pub fn prepare_function_map() -> HashMap { panic!("invalid subcommand return value"); }; - let command = Command::Pinata(PinataProgramSubcommand::Private( - PinataProgramSubcommandPrivate::ClaimPrivateOwned { - pinata_addr: pinata_addr.to_string(), - winner_addr: winner_addr.to_string(), - solution, - }, - )); + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to_addr: make_private_account_input_from_str(&winner_addr.to_string()), + solution, + }); let wallet_config = fetch_config().await.unwrap(); diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index 186b266..dd99639 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -9,8 +9,6 @@ use crate::config::AccountInitialData; pub mod block_store; -pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7"; - pub struct SequecerChainStore { pub state: nssa::V02State, pub block_store: SequecerBlockStore, @@ -35,6 +33,8 @@ impl SequecerChainStore { #[cfg(feature = "testnet")] let state = { + use common::PINATA_BASE58; + let mut this = nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments); this.add_pinata_program(PINATA_BASE58.parse().unwrap()); diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index b902346..06218fe 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -1,7 +1,6 @@ use anyhow::Result; use base58::ToBase58; use clap::Subcommand; -use common::transaction::NSSATransaction; use nssa::{Address, program::Program}; use serde::Serialize; @@ -9,6 +8,7 @@ use crate::{ SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::{AddressPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix}, + parse_block_range, }; const TOKEN_DEFINITION_TYPE: u8 = 0; @@ -76,9 +76,6 @@ pub enum AccountSubcommand { #[arg(short, long)] addr: String, }, - ///Fetch - #[command(subcommand)] - Fetch(FetchSubcommand), ///New #[command(subcommand)] New(NewSubcommand), @@ -86,28 +83,6 @@ pub enum AccountSubcommand { SyncPrivate {}, } -///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 NewSubcommand { @@ -117,74 +92,6 @@ pub enum NewSubcommand { Private {}, } -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 NewSubcommand { async fn handle_subcommand( self, @@ -196,7 +103,7 @@ impl WalletSubcommand for NewSubcommand { println!("Generated new account with addr {addr}"); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -221,7 +128,7 @@ impl WalletSubcommand for NewSubcommand { hex::encode(key.incoming_viewing_public_key.to_bytes()) ); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -287,6 +194,8 @@ impl WalletSubcommand for AccountSubcommand { AccountSubcommand::Get { raw, addr } => { let (addr, addr_kind) = parse_addr_with_privacy_prefix(&addr)?; + let addr = addr.parse()?; + let account = match addr_kind { AddressPrivacyKind::Public => wallet_core.get_account_public(addr).await?, AddressPrivacyKind::Private => wallet_core @@ -333,14 +242,39 @@ impl WalletSubcommand for AccountSubcommand { Ok(SubcommandReturnValue::Empty) } - AccountSubcommand::Fetch(fetch_subcommand) => { - fetch_subcommand.handle_subcommand(wallet_core).await - } AccountSubcommand::New(new_subcommand) => { new_subcommand.handle_subcommand(wallet_core).await } AccountSubcommand::SyncPrivate {} => { - todo!(); + let last_synced_block = wallet_core.last_synced_block; + let curr_last_block = wallet_core + .sequencer_client + .get_last_block() + .await? + .last_block; + + if !wallet_core + .storage + .user_data + .user_private_accounts + .is_empty() + { + parse_block_range( + last_synced_block, + curr_last_block, + wallet_core.sequencer_client.clone(), + wallet_core, + ) + .await?; + } else { + wallet_core.last_synced_block = curr_last_block; + + let path = wallet_core.store_persistent_data().await?; + + println!("Stored persistent data at {path:#?}"); + } + + Ok(SubcommandReturnValue::SyncedToBlock(curr_last_block)) } } } diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/native_token_transfer_program.rs index 2f97365..19e8368 100644 --- a/wallet/src/cli/native_token_transfer_program.rs +++ b/wallet/src/cli/native_token_transfer_program.rs @@ -60,11 +60,13 @@ impl WalletSubcommand for AuthTransferSubcommand { println!("Transaction data is {transfer_tx:?}"); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); } AddressPrivacyKind::Private => { + let addr = addr.parse()?; + let (res, [secret]) = wallet_core .register_account_under_authenticated_transfers_programs_private(addr) .await?; @@ -85,7 +87,7 @@ impl WalletSubcommand for AuthTransferSubcommand { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); } @@ -120,33 +122,29 @@ impl WalletSubcommand for AuthTransferSubcommand { match (from_privacy, to_privacy) { (AddressPrivacyKind::Public, AddressPrivacyKind::Public) => { - NativeTokenTransferProgramSubcommand::Public { - from: from.to_string(), - to: to.to_string(), - amount, - } + NativeTokenTransferProgramSubcommand::Public { from, to, amount } } (AddressPrivacyKind::Private, AddressPrivacyKind::Private) => { NativeTokenTransferProgramSubcommand::Private( NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { - from: from.to_string(), - to: to.to_string(), + from, + to, amount, }, ) } (AddressPrivacyKind::Private, AddressPrivacyKind::Public) => { NativeTokenTransferProgramSubcommand::Deshielded { - from: from.to_string(), - to: to.to_string(), + from, + to, amount, } } (AddressPrivacyKind::Public, AddressPrivacyKind::Private) => { NativeTokenTransferProgramSubcommand::Shielded( NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { - from: from.to_string(), - to: to.to_string(), + from, + to, amount, }, ) @@ -160,7 +158,7 @@ impl WalletSubcommand for AuthTransferSubcommand { AddressPrivacyKind::Private => { NativeTokenTransferProgramSubcommand::Private( NativeTokenTransferProgramSubcommandPrivate::PrivateForeign { - from: from.to_string(), + from, to_npk, to_ipk, amount, @@ -170,7 +168,7 @@ impl WalletSubcommand for AuthTransferSubcommand { AddressPrivacyKind::Public => { NativeTokenTransferProgramSubcommand::Shielded( NativeTokenTransferProgramSubcommandShielded::ShieldedForeign { - from: from.to_string(), + from, to_npk, to_ipk, amount, @@ -340,7 +338,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -384,7 +382,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -434,7 +432,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -467,7 +465,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let tx_hash = res.tx_hash; - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -513,7 +511,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -533,7 +531,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { println!("Transaction data is {transfer_tx:?}"); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/pinata_program.rs index 75d3d6a..6e79362 100644 --- a/wallet/src/cli/pinata_program.rs +++ b/wallet/src/cli/pinata_program.rs @@ -1,9 +1,59 @@ use anyhow::Result; use clap::Subcommand; -use common::transaction::NSSATransaction; +use common::{PINATA_BASE58, transaction::NSSATransaction}; use log::info; -use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; +use crate::{ + SubcommandReturnValue, WalletCore, + cli::WalletSubcommand, + helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix}, +}; + +///Represents generic CLI subcommand for a wallet working with pinata program +#[derive(Subcommand, Debug, Clone)] +pub enum PinataProgramAgnosticSubcommand { + ///Claim + Claim { + ///to_addr - valid 32 byte base58 string + #[arg(long)] + to_addr: String, + ///solution - solution to pinata challenge + #[arg(long)] + solution: u128, + }, +} + +impl WalletSubcommand for PinataProgramAgnosticSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + let underlying_subcommand = match self { + PinataProgramAgnosticSubcommand::Claim { to_addr, solution } => { + let (to_addr, to_addr_privacy) = parse_addr_with_privacy_prefix(&to_addr)?; + + match to_addr_privacy { + AddressPrivacyKind::Public => { + PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim { + pinata_addr: PINATA_BASE58.to_string(), + winner_addr: to_addr, + solution, + }) + } + AddressPrivacyKind::Private => PinataProgramSubcommand::Private( + PinataProgramSubcommandPrivate::ClaimPrivateOwned { + pinata_addr: PINATA_BASE58.to_string(), + winner_addr: to_addr, + solution, + }, + ), + } + } + }; + + underlying_subcommand.handle_subcommand(wallet_core).await + } +} ///Represents generic CLI subcommand for a wallet working with pinata program #[derive(Subcommand, Debug, Clone)] @@ -131,7 +181,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/token_program.rs b/wallet/src/cli/token_program.rs index dc269e4..99ebecd 100644 --- a/wallet/src/cli/token_program.rs +++ b/wallet/src/cli/token_program.rs @@ -64,8 +64,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AddressPrivacyKind::Public, AddressPrivacyKind::Public) => { TokenProgramSubcommand::Public( TokenProgramSubcommandPublic::CreateNewToken { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), + definition_addr, + supply_addr, name, total_supply, }, @@ -74,18 +74,20 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AddressPrivacyKind::Public, AddressPrivacyKind::Private) => { TokenProgramSubcommand::Private( TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), + definition_addr, + supply_addr, name, total_supply, }, ) } (AddressPrivacyKind::Private, AddressPrivacyKind::Private) => { - todo!(); + //ToDo: maybe implement this one. It is not immediately clear why definition should be private. + anyhow::bail!("Unavailable privacy pairing") } (AddressPrivacyKind::Private, AddressPrivacyKind::Public) => { - todo!(); + //Probably valid. If definition is not public, but supply is it is very suspicious. + anyhow::bail!("Unavailable privacy pairing") } }; @@ -120,8 +122,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AddressPrivacyKind::Public, AddressPrivacyKind::Public) => { TokenProgramSubcommand::Public( TokenProgramSubcommandPublic::TransferToken { - sender_addr: from.to_string(), - recipient_addr: to.to_string(), + sender_addr: from, + recipient_addr: to, balance_to_move: amount, }, ) @@ -129,8 +131,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AddressPrivacyKind::Private, AddressPrivacyKind::Private) => { TokenProgramSubcommand::Private( TokenProgramSubcommandPrivate::TransferTokenPrivateOwned { - sender_addr: from.to_string(), - recipient_addr: to.to_string(), + sender_addr: from, + recipient_addr: to, balance_to_move: amount, }, ) @@ -138,8 +140,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AddressPrivacyKind::Private, AddressPrivacyKind::Public) => { TokenProgramSubcommand::Deshielded( TokenProgramSubcommandDeshielded::TransferTokenDeshielded { - sender_addr: from.to_string(), - recipient_addr: to.to_string(), + sender_addr: from, + recipient_addr: to, balance_to_move: amount, }, ) @@ -147,8 +149,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AddressPrivacyKind::Public, AddressPrivacyKind::Private) => { TokenProgramSubcommand::Shielded( TokenProgramSubcommandShielded::TransferTokenShieldedOwned { - sender_addr: from.to_string(), - recipient_addr: to.to_string(), + sender_addr: from, + recipient_addr: to, balance_to_move: amount, }, ) @@ -161,7 +163,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { match from_privacy { AddressPrivacyKind::Private => TokenProgramSubcommand::Private( TokenProgramSubcommandPrivate::TransferTokenPrivateForeign { - sender_addr: from.to_string(), + sender_addr: from, recipient_npk: to_npk, recipient_ipk: to_ipk, balance_to_move: amount, @@ -169,7 +171,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { ), AddressPrivacyKind::Public => TokenProgramSubcommand::Shielded( TokenProgramSubcommandShielded::TransferTokenShieldedForeign { - sender_addr: from.to_string(), + sender_addr: from, recipient_npk: to_npk, recipient_ipk: to_ipk, balance_to_move: amount, @@ -401,7 +403,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -458,7 +460,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -508,7 +510,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -556,7 +558,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -611,7 +613,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { println!("Transaction data is {:?}", tx.message); } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -665,7 +667,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/config.rs b/wallet/src/config.rs index aa9b5ba..af1f493 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -46,6 +46,12 @@ pub enum PersistentAccountData { Private(PersistentAccountDataPrivate), } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PersistentStorage { + pub accounts: Vec, + pub last_synced_block: u64, +} + impl InitialAccountData { pub fn address(&self) -> nssa::Address { match &self { diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 83eef43..1d35efb 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -12,8 +12,7 @@ use serde::Serialize; use crate::{ HOME_DIR_ENV_VAR, config::{ - PersistentAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic, - WalletConfig, + PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig, }, }; @@ -30,21 +29,24 @@ pub async fn fetch_config() -> Result { Ok(serde_json::from_slice(&config_contents)?) } -/// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json` +/// Fetch data stored at `NSSA_WALLET_HOME_DIR/storage.json` /// /// If file not present, it is considered as empty list of persistent accounts -pub async fn fetch_persistent_accounts() -> Result> { +pub async fn fetch_persistent_storage() -> Result { let home = get_home()?; - let accs_path = home.join("curr_accounts.json"); - let mut persistent_accounts_content = vec![]; + let accs_path = home.join("storage.json"); + let mut storage_content = vec![]; 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)?) + file.read_to_end(&mut storage_content).await?; + Ok(serde_json::from_slice(&storage_content)?) } Err(err) => match err.kind() { - std::io::ErrorKind::NotFound => Ok(vec![]), + std::io::ErrorKind::NotFound => Ok(PersistentStorage { + accounts: vec![], + last_synced_block: 0, + }), _ => { anyhow::bail!("IO error {err:#?}"); } @@ -52,8 +54,11 @@ pub async fn fetch_persistent_accounts() -> Result> { } } -/// Produces a list of accounts for storage -pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec { +/// Produces data for storage +pub fn produce_data_for_storage( + user_data: &NSSAUserData, + last_synced_block: u64, +) -> PersistentStorage { let mut vec_for_storage = vec![]; for (addr, key) in &user_data.pub_account_signing_keys { @@ -77,7 +82,10 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec Vec { diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 897b7d5..8b9ec78 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -20,15 +20,18 @@ use clap::{Parser, Subcommand}; use nssa_core::{Commitment, MembershipProof}; use tokio::io::AsyncWriteExt; -use crate::cli::{ - WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand, - native_token_transfer_program::AuthTransferSubcommand, pinata_program::PinataProgramSubcommand, - token_program::TokenProgramAgnosticSubcommand, +use crate::{ + cli::{ + WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand, + native_token_transfer_program::AuthTransferSubcommand, + pinata_program::PinataProgramAgnosticSubcommand, + token_program::TokenProgramAgnosticSubcommand, + }, + config::PersistentStorage, + helperfunctions::fetch_persistent_storage, }; use crate::{ - helperfunctions::{ - fetch_config, fetch_persistent_accounts, get_home, produce_data_for_storage, - }, + helperfunctions::{fetch_config, get_home, produce_data_for_storage}, poller::TxPoller, }; @@ -48,6 +51,7 @@ pub struct WalletCore { pub storage: WalletChainStore, pub poller: TxPoller, pub sequencer_client: Arc, + pub last_synced_block: u64, } impl WalletCore { @@ -57,7 +61,10 @@ impl WalletCore { let mut storage = WalletChainStore::new(config)?; - let persistent_accounts = fetch_persistent_accounts().await?; + let PersistentStorage { + accounts: persistent_accounts, + last_synced_block, + } = fetch_persistent_storage().await?; for pers_acc_data in persistent_accounts { storage.insert_account_data(pers_acc_data); } @@ -66,23 +73,24 @@ impl WalletCore { storage, poller: tx_poller, sequencer_client: client.clone(), + last_synced_block, }) } - ///Store persistent accounts at home - pub async fn store_persistent_accounts(&self) -> Result { + ///Store persistent data at home + pub async fn store_persistent_data(&self) -> Result { let home = get_home()?; - let accs_path = home.join("curr_accounts.json"); + let storage_path = home.join("storage.json"); - let data = produce_data_for_storage(&self.storage.user_data); - let accs = serde_json::to_vec_pretty(&data)?; + let data = produce_data_for_storage(&self.storage.user_data, self.last_synced_block); + let storage = serde_json::to_vec_pretty(&data)?; - let mut accs_file = tokio::fs::File::create(accs_path.as_path()).await?; - accs_file.write_all(&accs).await?; + let mut storage_file = tokio::fs::File::create(storage_path.as_path()).await?; + storage_file.write_all(&storage).await?; - info!("Stored accounts data at {accs_path:#?}"); + info!("Stored data at {storage_path:#?}"); - Ok(accs_path) + Ok(storage_path) } pub fn create_new_account_public(&mut self) -> Address { @@ -201,11 +209,10 @@ pub enum Command { Account(AccountSubcommand), ///Pinata command #[command(subcommand)] - Pinata(PinataProgramSubcommand), + Pinata(PinataProgramAgnosticSubcommand), ///Token command #[command(subcommand)] Token(TokenProgramAgnosticSubcommand), - AuthenticatedTransferInitializePublicAccount {}, // Check the wallet can connect to the node and builtin local programs // match the remote versions CheckHealth {}, @@ -229,6 +236,7 @@ pub enum SubcommandReturnValue { RegisterAccount { addr: nssa::Address }, Account(nssa::Account), Empty, + SyncedToBlock(u64), } pub async fn execute_subcommand(command: Command) -> Result { @@ -284,25 +292,6 @@ pub async fn execute_subcommand(command: Command) -> Result { - let addr = wallet_core.create_new_account_public(); - - println!("Generated new account with addr {addr}"); - - let path = wallet_core.store_persistent_accounts().await?; - - println!("Stored persistent accounts at {path:#?}"); - - let res = wallet_core - .register_account_under_authenticated_transfers_programs(addr) - .await?; - - println!("Results of tx send is {res:#?}"); - - let _transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; - - SubcommandReturnValue::RegisterAccount { addr } - } Command::Token(token_subcommand) => { token_subcommand.handle_subcommand(&mut wallet_core).await? } @@ -311,6 +300,80 @@ pub async fn execute_subcommand(command: Command) -> Result, + wallet_core: &mut WalletCore, +) -> Result<()> { + for block_id in start..(stop + 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.last_synced_block = block_id; + wallet_core.store_persistent_data().await?; + + println!( + "Block at id {block_id} with timestamp {} parsed", + block.timestamp + ); + } + + Ok(()) +} + pub async fn execute_continious_run() -> Result<()> { let config = fetch_config().await?; let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); @@ -320,70 +383,13 @@ pub async fn execute_continious_run() -> Result<()> { 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().await?; - - println!( - "Block at id {block_id} with timestamp {} parsed", - block.timestamp - ); - } + parse_block_range( + curr_last_block, + latest_block_num, + seq_client.clone(), + &mut wallet_core, + ) + .await?; curr_last_block = latest_block_num + 1;