diff --git a/ci_scripts/test-ubuntu.sh b/ci_scripts/test-ubuntu.sh index 5b7afcf..6a1710b 100644 --- a/ci_scripts/test-ubuntu.sh +++ b/ci_scripts/test-ubuntu.sh @@ -7,4 +7,5 @@ source env.sh RISC0_DEV_MODE=1 cargo test --release cd integration_tests export NSSA_WALLET_HOME_DIR=$(pwd)/configs/debug/wallet/ +export RUST_LOG=info cargo run $(pwd)/configs/debug all diff --git a/common/src/lib.rs b/common/src/lib.rs index 01ae261..3b2ddff 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -66,6 +66,8 @@ pub enum ExecutionFailureKind { ProveError(anyhow::Error), #[error("Failed to decode data from VM: {0:?}")] DecodeError(String), + #[error("Failed to get account data from sequencer")] + SequencerError, #[error("Inputs amounts does not match outputs")] AmountMismatchError, #[error("Accounts key not found")] diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index b6bf8a6..9b7b970 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -84,7 +84,6 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) pub async fn test_success() { let command = Command::SendNativeTokenTransfer { from: ACC_SENDER.to_string(), - nonce: 0, to: ACC_RECEIVER.to_string(), amount: 100, }; @@ -144,7 +143,6 @@ pub async fn test_success_move_to_another_account() { let command = Command::SendNativeTokenTransfer { from: ACC_SENDER.to_string(), - nonce: 0, to: new_persistent_account_addr.clone(), amount: 100, }; @@ -176,7 +174,6 @@ pub async fn test_success_move_to_another_account() { pub async fn test_failure() { let command = Command::SendNativeTokenTransfer { from: ACC_SENDER.to_string(), - nonce: 0, to: ACC_RECEIVER.to_string(), amount: 1000000, }; @@ -214,7 +211,6 @@ pub async fn test_failure() { pub async fn test_success_two_transactions() { let command = Command::SendNativeTokenTransfer { from: ACC_SENDER.to_string(), - nonce: 0, to: ACC_RECEIVER.to_string(), amount: 100, }; @@ -248,7 +244,6 @@ pub async fn test_success_two_transactions() { let command = Command::SendNativeTokenTransfer { from: ACC_SENDER.to_string(), - nonce: 1, to: ACC_RECEIVER.to_string(), amount: 100, }; diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index 5702b38..2c38375 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use constants_types::{CipherText, Nonce}; use elliptic_curve::point::AffineCoordinates; @@ -18,6 +20,8 @@ pub mod secret_holders; pub struct KeyChain { top_secret_key_holder: TopSecretKeyHolder, pub utxo_secret_key_holder: UTXOSecretKeyHolder, + ///Map for all users accounts + pub pub_account_signing_keys: HashMap, pub nullifer_public_key: PublicKey, pub viewing_public_key: PublicKey, } @@ -39,9 +43,47 @@ impl KeyChain { utxo_secret_key_holder, nullifer_public_key, viewing_public_key, + pub_account_signing_keys: HashMap::new(), } } + pub fn new_os_random_with_accounts(accounts: HashMap) -> Self { + //Currently dropping SeedHolder at the end of initialization. + //Now entirely sure if we need it in the future. + let seed_holder = SeedHolder::new_os_random(); + let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); + + let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); + + let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); + let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); + + Self { + top_secret_key_holder, + utxo_secret_key_holder, + nullifer_public_key, + viewing_public_key, + pub_account_signing_keys: accounts, + } + } + + pub fn generate_new_private_key(&mut self) -> nssa::Address { + let private_key = nssa::PrivateKey::new_os_random(); + let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key)); + + self.pub_account_signing_keys.insert(address, private_key); + + address + } + + /// Returns the signing key for public transaction signatures + pub fn get_pub_account_signing_key( + &self, + address: &nssa::Address, + ) -> Option<&nssa::PrivateKey> { + self.pub_account_signing_keys.get(address) + } + pub fn calculate_shared_secret_receiver( &self, ephemeral_public_key_sender: AffinePoint, diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index facc5a2..67cb52c 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -4,135 +4,52 @@ use anyhow::Result; use k256::AffinePoint; use serde::{Deserialize, Serialize}; -use crate::key_management::{ - constants_types::{CipherText, Nonce}, - ephemeral_key_holder::EphemeralKeyHolder, - KeyChain, -}; +use crate::key_management::KeyChain; pub type PublicKey = AffinePoint; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct NSSAUserData { pub key_holder: KeyChain, - ///Map for all users accounts - pub accounts: HashMap, -} - -///A strucure, which represents all the visible(public) information -/// -/// known to each node about account `address` -/// -/// Main usage is to encode data for other account -#[derive(Serialize, Clone)] -pub struct NSSAUserDataPublicMask { - pub nullifier_public_key: AffinePoint, - pub viewing_public_key: AffinePoint, -} - -impl NSSAUserDataPublicMask { - pub fn encrypt_data( - ephemeral_key_holder: &EphemeralKeyHolder, - viewing_public_key_receiver: AffinePoint, - data: &[u8], - ) -> (CipherText, Nonce) { - //Using of parent NSSAUserData fuction - NSSAUserData::encrypt_data(ephemeral_key_holder, viewing_public_key_receiver, data) - } - - //ToDo: Part of a private keys update - // pub fn make_tag(&self) -> Tag { - // self.address.value()[0] - // } } impl NSSAUserData { pub fn new() -> Self { let key_holder = KeyChain::new_os_random(); - Self { - key_holder, - accounts: HashMap::new(), + Self { key_holder } + } + + fn valid_key_transaction_pairing_check( + accounts_keys_map: &HashMap, + ) -> bool { + let mut check_res = true; + for (addr, key) in accounts_keys_map { + if &nssa::Address::from(&nssa::PublicKey::new_from_private_key(key)) != addr { + check_res = false; + } } + check_res } pub fn new_with_accounts( - accounts_data: HashMap, - ) -> Self { - let key_holder = KeyChain::new_os_random(); - - Self { - key_holder, - accounts: accounts_data, + accounts_keys: HashMap, + ) -> Result { + if !Self::valid_key_transaction_pairing_check(&accounts_keys) { + anyhow::bail!("Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"); } + + let key_holder = KeyChain::new_os_random_with_accounts(accounts_keys); + + Ok(Self { key_holder }) } pub fn generate_new_account(&mut self) -> nssa::Address { - let private_key = nssa::PrivateKey::new_os_random(); - let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key)); - let account = nssa_core::account::Account::default(); - - self.accounts.insert(address, (private_key, account)); - - address - } - - pub fn get_account_balance(&self, address: &nssa::Address) -> u128 { - self.accounts - .get(address) - .map(|(_, acc)| acc.balance) - .unwrap_or(0) - } - - pub fn get_account(&self, address: &nssa::Address) -> Option<&nssa_core::account::Account> { - self.accounts.get(address).map(|(_, acc)| acc) + self.key_holder.generate_new_private_key() } pub fn get_account_signing_key(&self, address: &nssa::Address) -> Option<&nssa::PrivateKey> { - self.accounts.get(address).map(|(key, _)| key) - } - - pub fn encrypt_data( - ephemeral_key_holder: &EphemeralKeyHolder, - viewing_public_key_receiver: AffinePoint, - data: &[u8], - ) -> (CipherText, Nonce) { - ephemeral_key_holder.encrypt_data(viewing_public_key_receiver, data) - } - - pub fn decrypt_data( - &self, - ephemeral_public_key_sender: AffinePoint, - ciphertext: CipherText, - nonce: Nonce, - ) -> Result, aes_gcm::Error> { - self.key_holder - .decrypt_data(ephemeral_public_key_sender, ciphertext, nonce) - } - - pub fn update_account_balance(&mut self, address: nssa::Address, new_balance: u128) { - self.accounts - .entry(address) - .and_modify(|(_, acc)| acc.balance = new_balance); - } - - pub fn increment_account_nonce(&mut self, address: nssa::Address) { - self.accounts - .entry(address) - .and_modify(|(_, acc)| acc.nonce += 1); - } - - //ToDo: Part of a private keys update - // pub fn make_tag(&self) -> Tag { - // self.address.value()[0] - // } - - ///Produce account public mask - pub fn make_account_public_mask(&self) -> NSSAUserDataPublicMask { - NSSAUserDataPublicMask { - nullifier_public_key: self.key_holder.nullifer_public_key, - viewing_public_key: self.key_holder.viewing_public_key, - } + self.key_holder.get_pub_account_signing_key(address) } } @@ -150,29 +67,6 @@ mod tests { fn test_new_account() { let mut user_data = NSSAUserData::new(); - let addr = user_data.generate_new_account(); - - assert_eq!(user_data.get_account_balance(&addr), 0); + let _addr = user_data.generate_new_account(); } - - #[test] - fn test_update_balance() { - let mut user_data = NSSAUserData::new(); - - let address = user_data.generate_new_account(); - - user_data.update_account_balance(address, 500); - - assert_eq!(user_data.get_account_balance(&address), 500); - } - - //ToDo: Part of a private keys update - // #[test] - // fn accounts_accounts_mask_tag_consistency() { - // let account = NSSAUserData::new(); - - // let account_mask = account.make_account_public_mask(); - - // assert_eq!(account.make_tag(), account_mask.make_tag()); - // } } diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 2eb0ce2..eb89462 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = { version = "3.0.3" } +risc0-zkvm = "3.0.3" serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index eb5eb30..18285e9 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -648,9 +648,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -2785,12 +2785,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", @@ -2800,15 +2799,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" dependencies = [ "num-conv", "time-core", diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index abe5af6..667fc30 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -48,7 +48,6 @@ mod tests { #[test] fn test_produce_key() { - let key = PrivateKey::new_os_random(); - println!("{:?}", key.0); + let _key = PrivateKey::new_os_random(); } } diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index a6b59ce..6f4201c 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -4,7 +4,7 @@ use anyhow::Result; use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree; use key_protocol::key_protocol_core::NSSAUserData; -use crate::config::{InitialAccountData, WalletConfig}; +use crate::config::{PersistentAccountData, WalletConfig}; pub struct WalletChainStore { pub user_data: NSSAUserData, @@ -14,32 +14,27 @@ pub struct WalletChainStore { impl WalletChainStore { pub fn new(config: WalletConfig) -> Result { - let accounts_data: HashMap = - config - .initial_accounts - .clone() - .into_iter() - .map(|init_acc_data| { - ( - init_acc_data.address, - (init_acc_data.pub_sign_key, init_acc_data.account), - ) - }) - .collect(); + let accounts_keys: HashMap = config + .initial_accounts + .clone() + .into_iter() + .map(|init_acc_data| (init_acc_data.address, init_acc_data.pub_sign_key)) + .collect(); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); Ok(Self { - user_data: NSSAUserData::new_with_accounts(accounts_data), + user_data: NSSAUserData::new_with_accounts(accounts_keys)?, utxo_commitments_store, wallet_config: config, }) } - pub(crate) fn insert_account_data(&mut self, acc_data: InitialAccountData) { + pub(crate) fn insert_account_data(&mut self, acc_data: PersistentAccountData) { self.user_data - .accounts - .insert(acc_data.address, (acc_data.pub_sign_key, acc_data.account)); + .key_holder + .pub_account_signing_keys + .insert(acc_data.address, acc_data.pub_sign_key); } } @@ -101,7 +96,6 @@ mod tests { let store = WalletChainStore::new(config.clone()).unwrap(); - assert_eq!(store.user_data.accounts.len(), 2); assert_eq!( store.utxo_commitments_store.get_root().unwrap_or([0; 32]), [0; 32] diff --git a/wallet/src/config.rs b/wallet/src/config.rs index e6737f9..b6355ce 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -8,6 +8,12 @@ pub struct InitialAccountData { pub pub_sign_key: nssa::PrivateKey, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PersistentAccountData { + pub address: nssa::Address, + pub pub_sign_key: nssa::PrivateKey, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GasConfig { /// Gas spent per deploying one byte of data diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index db6b10f..0ad9aed 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -5,7 +5,7 @@ use key_protocol::key_protocol_core::NSSAUserData; use nssa::Address; use crate::{ - config::{InitialAccountData, WalletConfig}, + config::{PersistentAccountData, WalletConfig}, HOME_DIR_ENV_VAR, }; @@ -31,7 +31,7 @@ pub fn produce_account_addr_from_hex(hex_str: String) -> Result
{ ///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 fn fetch_persistent_accounts() -> Result> { let home = get_home()?; let accs_path = home.join("curr_accounts.json"); @@ -50,13 +50,12 @@ pub fn fetch_persistent_accounts() -> Result> { } ///Produces a list of accounts for storage -pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec { +pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec { let mut vec_for_storage = vec![]; - for (addr, (key, account)) in &user_data.accounts { - vec_for_storage.push(InitialAccountData { + for (addr, key) in &user_data.key_holder.pub_account_signing_keys { + vec_for_storage.push(PersistentAccountData { address: *addr, - account: account.clone(), pub_sign_key: key.clone(), }); } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index eac42b5..7ce7955 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::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc}; use base64::Engine; use common::{ @@ -13,6 +13,7 @@ use log::info; use nssa::Address; use clap::{Parser, Subcommand}; +use nssa_core::account::Account; use crate::{ helperfunctions::{ @@ -36,7 +37,7 @@ pub struct WalletCore { } impl WalletCore { - pub async fn start_from_config_update_chain(config: WalletConfig) -> Result { + pub fn start_from_config_update_chain(config: WalletConfig) -> Result { let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); let tx_poller = TxPoller { polling_delay_millis: config.seq_poll_timeout_millis, @@ -48,12 +49,9 @@ impl WalletCore { let mut storage = WalletChainStore::new(config)?; - //Updating user data with stored accounts - //We do not store/update any key data connected to private executions - //ToDo: Add this into persistent data let persistent_accounts = fetch_persistent_accounts()?; - for acc in persistent_accounts { - storage.insert_account_data(acc); + for pers_acc_data in persistent_accounts { + storage.insert_account_data(pers_acc_data); } Ok(Self { @@ -83,48 +81,77 @@ impl WalletCore { self.storage.user_data.generate_new_account() } + pub fn search_for_initial_account(&self, acc_addr: Address) -> Option { + for initial_acc in &self.storage.wallet_config.initial_accounts { + if initial_acc.address == acc_addr { + return Some(initial_acc.account.clone()); + } + } + None + } + pub async fn send_public_native_token_transfer( &self, from: Address, - nonce: u128, to: Address, balance_to_move: u128, ) -> Result { - let account = self.storage.user_data.get_account(&from); + if let Ok(balance) = self.get_account_balance(from).await { + if balance >= balance_to_move { + if let Ok(nonces) = self.get_accounts_nonces(vec![from]).await { + let addresses = vec![from, to]; + let program_id = nssa::program::Program::authenticated_transfer_program().id(); + let message = nssa::public_transaction::Message::try_new( + program_id, + addresses, + nonces, + balance_to_move, + ) + .unwrap(); - if let Some(account) = account { - if account.balance >= balance_to_move { - let addresses = vec![from, to]; - let nonces = vec![nonce]; - let program_id = nssa::program::Program::authenticated_transfer_program().id(); - let message = nssa::public_transaction::Message::try_new( - program_id, - addresses, - nonces, - balance_to_move, - ) - .unwrap(); + let signing_key = self.storage.user_data.get_account_signing_key(&from); - let signing_key = self.storage.user_data.get_account_signing_key(&from); + if let Some(signing_key) = signing_key { + let witness_set = nssa::public_transaction::WitnessSet::for_message( + &message, + &[signing_key], + ); - if let Some(signing_key) = signing_key { - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let tx = nssa::PublicTransaction::new(message, witness_set); - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.sequencer_client.send_tx(tx).await?) + Ok(self.sequencer_client.send_tx(tx).await?) + } else { + Err(ExecutionFailureKind::KeyNotFoundError) + } } else { - Err(ExecutionFailureKind::KeyNotFoundError) + Err(ExecutionFailureKind::SequencerError) } } else { Err(ExecutionFailureKind::InsufficientFundsError) } } else { - Err(ExecutionFailureKind::AmountMismatchError) + Err(ExecutionFailureKind::SequencerError) } } + ///Get account balance + pub async fn get_account_balance(&self, acc: Address) -> Result { + Ok(self + .sequencer_client + .get_account_balance(acc.to_string()) + .await? + .balance) + } + + ///Get accounts nonces + pub async fn get_accounts_nonces(&self, accs: Vec
) -> Result> { + Ok(self + .sequencer_client + .get_accounts_nonces(accs.into_iter().map(|acc| acc.to_string()).collect()) + .await? + .nonces) + } + ///Poll transactions pub async fn poll_public_native_token_transfer( &self, @@ -137,27 +164,6 @@ impl WalletCore { Ok(pub_tx) } - - ///Execute native token transfer at wallet accounts - pub fn execute_native_token_transfer( - &mut self, - from: Address, - to: Address, - balance_to_move: u128, - ) { - self.storage.user_data.increment_account_nonce(from); - self.storage.user_data.increment_account_nonce(to); - - let from_bal = self.storage.user_data.get_account_balance(&from); - let to_bal = self.storage.user_data.get_account_balance(&to); - - self.storage - .user_data - .update_account_balance(from, from_bal - balance_to_move); - self.storage - .user_data - .update_account_balance(to, to_bal + balance_to_move); - } } ///Represents CLI command for a wallet @@ -169,9 +175,6 @@ pub enum Command { ///from - valid 32 byte hex string #[arg(long)] from: String, - ///nonce - u128 integer - #[arg(long)] - nonce: u128, ///to - valid 32 byte hex string #[arg(long)] to: String, @@ -186,6 +189,16 @@ pub enum Command { #[arg(short, long)] tx_hash: String, }, + ///Get account `addr` balance + GetAccountBalance { + #[arg(short, long)] + addr: String, + }, + ///Get account `addr` nonce + GetAccountNonce { + #[arg(short, long)] + addr: String, + }, } ///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config @@ -199,20 +212,15 @@ pub struct Args { 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).await?; + let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?; match command { - Command::SendNativeTokenTransfer { - from, - nonce, - to, - amount, - } => { + Command::SendNativeTokenTransfer { from, to, amount } => { let from = produce_account_addr_from_hex(from)?; let to = produce_account_addr_from_hex(to)?; let res = wallet_core - .send_public_native_token_transfer(from, nonce, to, amount) + .send_public_native_token_transfer(from, to, amount) .await?; info!("Results of tx send is {res:#?}"); @@ -222,8 +230,6 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { .await?; info!("Transaction data is {transfer_tx:?}"); - - wallet_core.execute_native_token_transfer(from, to, amount); } Command::RegisterAccount {} => { let addr = wallet_core.create_new_account(); @@ -241,6 +247,18 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { info!("Transaction object {tx_obj:#?}"); } + Command::GetAccountBalance { addr } => { + let addr = Address::from_str(&addr)?; + + let balance = wallet_core.get_account_balance(addr).await?; + info!("Accounts {addr:#?} balance is {balance}"); + } + Command::GetAccountNonce { addr } => { + let addr = Address::from_str(&addr)?; + + let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; + info!("Accounts {addr:#?} nonce is {nonce}"); + } } wallet_core.store_persistent_accounts()?;