diff --git a/lez/wallet/src/cli/account.rs b/lez/wallet/src/cli/account.rs index b49cfb25..42e0d688 100644 --- a/lez/wallet/src/cli/account.rs +++ b/lez/wallet/src/cli/account.rs @@ -314,7 +314,7 @@ impl AccountSubcommand { raw: bool, keys: bool, account_id: CliAccountMention, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let resolved = account_id.resolve(wallet_core.storage())?; wallet_core @@ -384,38 +384,64 @@ impl AccountSubcommand { Ok(SubcommandReturnValue::Empty) } - async fn handle_list(long: bool, wallet_core: &WalletCore) -> Result { - let key_chain = &wallet_core.storage.key_chain(); - let storage = wallet_core.storage(); + fn format_with_label( + wallet_core: &WalletCore, + id: AccountIdWithPrivacy, + chain_index: Option<&ChainIndex>, + ) -> String { + let id_str = chain_index.map_or_else(|| id.to_string(), |cci| format!("{cci} {id}")); - let format_with_label = |id: AccountIdWithPrivacy, chain_index: Option<&ChainIndex>| { - let id_str = chain_index.map_or_else(|| id.to_string(), |cci| format!("{cci} {id}")); - - let labels = storage.labels_for_account(id).format(", ").to_string(); - if labels.is_empty() { - id_str - } else { - format!("{id_str} [{labels}]") - } - }; - - if !long { - let accounts = key_chain - .account_ids() - .map(|(id, idx)| format_with_label(id, idx)) - .format("\n"); - println!("{accounts}"); - - return Ok(SubcommandReturnValue::Empty); + let labels = wallet_core + .storage() + .labels_for_account(id) + .format(", ") + .to_string(); + if labels.is_empty() { + id_str + } else { + format!("{id_str} [{labels}]") } + } + + async fn handle_list( + long: bool, + wallet_core: &mut WalletCore, + ) -> Result { + let (public_account_ids, private_account_ids) = { + let key_chain = &wallet_core.storage.key_chain(); + + if !long { + let accounts = key_chain + .account_ids() + .map(|(id, idx)| Self::format_with_label(wallet_core, id, idx)) + .format("\n"); + println!("{accounts}"); + + return Ok(SubcommandReturnValue::Empty); + } + + let public_account_ids: Vec<_> = key_chain + .public_account_ids() + .map(|(id, chain_index)| (id, chain_index.cloned())) + .collect(); + let private_account_ids: Vec<_> = key_chain + .private_account_ids() + .map(|(id, chain_index)| (id, chain_index.cloned())) + .collect(); + + (public_account_ids, private_account_ids) + }; // Detailed listing with --long flag - // Public key tree accounts - for (id, chain_index) in key_chain.public_account_ids() { + for (id, chain_index) in public_account_ids { println!( "{}", - format_with_label(AccountIdWithPrivacy::Public(id), chain_index) + Self::format_with_label( + wallet_core, + AccountIdWithPrivacy::Public(id), + chain_index.as_ref() + ) ); match wallet_core.get_account_public(id).await { Ok(account) if account != Account::default() => { @@ -429,10 +455,14 @@ impl AccountSubcommand { } // Private key tree accounts - for (id, chain_index) in key_chain.private_account_ids() { + for (id, chain_index) in private_account_ids { println!( "{}", - format_with_label(AccountIdWithPrivacy::Private(id), chain_index) + Self::format_with_label( + wallet_core, + AccountIdWithPrivacy::Private(id), + chain_index.as_ref() + ) ); match wallet_core.get_account_private(id) { Some(account) if account != Account::default() => { diff --git a/lez/wallet/src/cli/chain.rs b/lez/wallet/src/cli/chain.rs index 38ec3d7a..b14a5aa3 100644 --- a/lez/wallet/src/cli/chain.rs +++ b/lez/wallet/src/cli/chain.rs @@ -1,7 +1,6 @@ use anyhow::Result; use clap::Subcommand; use common::HashType; -use sequencer_service_rpc::RpcClient as _; use crate::{ WalletCore, @@ -33,23 +32,17 @@ impl WalletSubcommand for ChainSubcommand { ) -> Result { match self { Self::CurrentBlockId => { - let latest_block_id = wallet_core - .optimal_sequencer_client() - .get_last_block_id() - .await?; + let latest_block_id = wallet_core.get_last_block_id().await?; println!("Last block id is {latest_block_id}"); } Self::Block { id } => { - let block = wallet_core.optimal_sequencer_client().get_block(id).await?; + let block = wallet_core.get_block(id).await?; println!("Last block id is {block:#?}"); } Self::Transaction { hash } => { - let tx = wallet_core - .optimal_sequencer_client() - .get_transaction(hash) - .await?; + let tx = wallet_core.get_transaction(hash).await?; println!("Transaction is {tx:#?}"); } diff --git a/lez/wallet/src/cli/mod.rs b/lez/wallet/src/cli/mod.rs index 4b460714..7dc64e34 100644 --- a/lez/wallet/src/cli/mod.rs +++ b/lez/wallet/src/cli/mod.rs @@ -3,10 +3,9 @@ use std::{io::Write as _, path::PathBuf, str::FromStr}; use anyhow::{Context as _, Result}; use bip39::Mnemonic; use clap::{Parser, Subcommand}; -use common::{HashType, transaction::LeeTransaction}; +use common::HashType; use derive_more::Display; use futures::TryFutureExt as _; -use lee::ProgramDeploymentTransaction; use sequencer_service_rpc::RpcClient as _; pub use crate::helperfunctions::{read_mnemonic, read_pin}; @@ -219,7 +218,6 @@ pub async fn execute_subcommand( } Command::CheckHealth => { let remote_program_ids = wallet_core - .optimal_sequencer_client() .get_program_ids() .await .expect("Error fetching program ids"); @@ -284,11 +282,8 @@ pub async fn execute_subcommand( "Failed to read program binary at {}", binary_filepath.display() ))?; - let message = lee::program_deployment_transaction::Message::new(bytecode); - let transaction = ProgramDeploymentTransaction::new(message); let _response = wallet_core - .optimal_sequencer_client() - .send_transaction(LeeTransaction::ProgramDeployment(transaction)) + .send_program_deployment_transaction(bytecode) .await .context("Transaction submission error")?; @@ -384,15 +379,13 @@ pub async fn execute_keys_restoration(wallet_core: &mut WalletCore, depth: u32) wallet_core.sync_to_latest_block().await?; - let optimal_sequencer_client = wallet_core.optimal_sequencer_client_owned(); + let leader_client = wallet_core.leader_owned(); wallet_core .storage .key_chain_mut() .cleanup_trees_remove_uninit_layered(depth, |account_id| { - optimal_sequencer_client - .get_account(account_id) - .map_err(Into::into) + leader_client.get_account(account_id).map_err(Into::into) }) .await?; diff --git a/lez/wallet/src/cli/programs/amm.rs b/lez/wallet/src/cli/programs/amm.rs index 0f544c42..b95746ce 100644 --- a/lez/wallet/src/cli/programs/amm.rs +++ b/lez/wallet/src/cli/programs/amm.rs @@ -125,7 +125,7 @@ impl AmmProgramAgnosticSubcommand { user_holding_lp: CliAccountMention, balance_a: u128, balance_b: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let a_id = user_holding_a.resolve(wallet_core.storage())?; let b_id = user_holding_b.resolve(wallet_core.storage())?; @@ -162,7 +162,7 @@ impl AmmProgramAgnosticSubcommand { amount_in: u128, min_amount_out: u128, token_definition: AccountId, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let a_id = user_holding_a.resolve(wallet_core.storage())?; let b_id = user_holding_b.resolve(wallet_core.storage())?; @@ -194,7 +194,7 @@ impl AmmProgramAgnosticSubcommand { exact_amount_out: u128, max_amount_in: u128, token_definition: AccountId, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let a_id = user_holding_a.resolve(wallet_core.storage())?; let b_id = user_holding_b.resolve(wallet_core.storage())?; @@ -227,7 +227,7 @@ impl AmmProgramAgnosticSubcommand { min_amount_lp: u128, max_amount_a: u128, max_amount_b: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let a_id = user_holding_a.resolve(wallet_core.storage())?; let b_id = user_holding_b.resolve(wallet_core.storage())?; @@ -266,7 +266,7 @@ impl AmmProgramAgnosticSubcommand { balance_lp: u128, min_amount_a: u128, min_amount_b: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let a_id = user_holding_a.resolve(wallet_core.storage())?; let b_id = user_holding_b.resolve(wallet_core.storage())?; diff --git a/lez/wallet/src/cli/programs/native_token_transfer.rs b/lez/wallet/src/cli/programs/native_token_transfer.rs index ccc2f14c..f972612b 100644 --- a/lez/wallet/src/cli/programs/native_token_transfer.rs +++ b/lez/wallet/src/cli/programs/native_token_transfer.rs @@ -425,7 +425,7 @@ impl NativeTokenTransferProgramSubcommandShielded { to_vpk: String, to_identifier: Option, amount: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let (to_npk, to_vpk) = crate::cli::decode_npk_vpk(&to_npk, &to_vpk)?; @@ -497,7 +497,7 @@ impl NativeTokenTransferProgramSubcommand { from: Option, to: Option, amount: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let tx_hash = NativeTokenTransfer(wallet_core) .send_public_transfer( diff --git a/lez/wallet/src/cli/programs/pinata.rs b/lez/wallet/src/cli/programs/pinata.rs index f0f49c5e..dd26f7e6 100644 --- a/lez/wallet/src/cli/programs/pinata.rs +++ b/lez/wallet/src/cli/programs/pinata.rs @@ -167,7 +167,7 @@ impl WalletSubcommand for PinataProgramSubcommand { } async fn ensure_public_recipient_initialized( - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, winner_account_id: AccountId, ) -> Result<()> { let account = wallet_core @@ -187,7 +187,7 @@ async fn ensure_public_recipient_initialized( } fn ensure_private_owned_recipient_initialized( - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, winner_account_id: AccountId, ) -> Result<()> { let Some(account) = wallet_core.get_account_private(winner_account_id) else { @@ -210,7 +210,7 @@ fn ensure_private_owned_recipient_initialized( Ok(()) } -async fn find_solution(wallet: &WalletCore, pinata_account_id: AccountId) -> Result { +async fn find_solution(wallet: &mut WalletCore, pinata_account_id: AccountId) -> Result { let account = wallet.get_account_public(pinata_account_id).await?; let data: [u8; 33] = account .data diff --git a/lez/wallet/src/cli/programs/token.rs b/lez/wallet/src/cli/programs/token.rs index 6486f58c..4686909d 100644 --- a/lez/wallet/src/cli/programs/token.rs +++ b/lez/wallet/src/cli/programs/token.rs @@ -817,7 +817,7 @@ impl TokenProgramSubcommandPublic { sender_account_id: CliAccountMention, recipient_account_id: CliAccountMention, balance_to_move: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let sender = sender_account_id.resolve(wallet_core.storage())?; let recipient = recipient_account_id.resolve(wallet_core.storage())?; @@ -844,7 +844,7 @@ impl TokenProgramSubcommandPublic { definition_account_id: AccountId, holder_account_id: CliAccountMention, amount: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let holder = holder_account_id.resolve(wallet_core.storage())?; let AccountIdWithPrivacy::Public(holder_id) = holder else { @@ -868,7 +868,7 @@ impl TokenProgramSubcommandPublic { definition_account_id: CliAccountMention, holder_account_id: CliAccountMention, amount: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let definition = definition_account_id.resolve(wallet_core.storage())?; let holder = holder_account_id.resolve(wallet_core.storage())?; @@ -1535,7 +1535,7 @@ impl CreateNewTokenProgramSubcommand { supply_account_id: CliAccountMention, name: String, total_supply: u128, - wallet_core: &WalletCore, + wallet_core: &mut WalletCore, ) -> Result { let definition = definition_account_id.resolve(wallet_core.storage())?; let supply = supply_account_id.resolve(wallet_core.storage())?; diff --git a/lez/wallet/src/lib.rs b/lez/wallet/src/lib.rs index 2741827f..6ef9bc78 100644 --- a/lez/wallet/src/lib.rs +++ b/lez/wallet/src/lib.rs @@ -7,16 +7,19 @@ reason = "Most of the shadows come from args parsing which is ok" )] -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::{BTreeMap, HashMap}, + path::PathBuf, +}; pub use account_manager::AccountIdentity; use anyhow::{Context as _, Result}; use bip39::Mnemonic; -use common::{HashType, transaction::LeeTransaction}; +use common::{HashType, block::Block, transaction::LeeTransaction}; use config::WalletConfig; use key_protocol::key_management::key_tree::chain_index::ChainIndex; use lee::{ - Account, AccountId, PrivacyPreservingTransaction, ProgramId, + Account, AccountId, PrivacyPreservingTransaction, ProgramDeploymentTransaction, ProgramId, privacy_preserving_transaction::{ circuit::ProgramWithDependencies, message::EncryptedAccountData, }, @@ -102,7 +105,7 @@ pub struct WalletCore { metrics: HashMap, metric_updates: Vec, - pub multi_sequencer_client: MultiSequencerClient, + multi_sequencer_client: MultiSequencerClient, } impl WalletCore { @@ -201,7 +204,11 @@ impl WalletCore { } pub fn optimal_poller(&self) -> TxPoller { - TxPoller::new(self.config(), self.multi_sequencer_client.leader_clone()) + TxPoller::new(self.config(), self.leader_owned()) + } + + pub fn leader_owned(&self) -> SequencerClient { + self.multi_sequencer_client.leader.clone() } /// Get storage. @@ -501,6 +508,36 @@ impl WalletCore { } } + pub async fn get_last_block_id(&mut self) -> Result { + let call_f = async |client: &SequencerClient| client.get_last_block_id().await; + + let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await; + + self.metric_updates.push(metrics_update); + + Ok(call_res?) + } + + pub async fn get_block(&mut self, block_id: u64) -> Result> { + let call_f = async |client: &SequencerClient| client.get_block(block_id).await; + + let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await; + + self.metric_updates.push(metrics_update); + + Ok(call_res?) + } + + pub async fn get_transaction(&mut self, hash: HashType) -> Result> { + let call_f = async |client: &SequencerClient| client.get_transaction(hash).await; + + let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await; + + self.metric_updates.push(metrics_update); + + Ok(call_res?) + } + /// Get public account. pub async fn get_account_public(&mut self, account_id: AccountId) -> Result { let call_f = async |client: &SequencerClient| client.get_account(account_id).await; @@ -544,6 +581,16 @@ impl WalletCore { Some(Commitment::new(&account_id, account)) } + pub async fn get_program_ids(&mut self) -> Result> { + let call_f = async |client: &SequencerClient| client.get_program_ids().await; + + let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await; + + self.metric_updates.push(metrics_update); + + Ok(call_res?) + } + /// Poll transactions. pub async fn poll_native_token_transfer(&self, hash: HashType) -> Result { self.optimal_poller().poll_tx(hash).await @@ -783,14 +830,28 @@ impl WalletCore { Ok(call_res?) } - pub async fn sync_to_latest_block(&mut self) -> Result { - let call_f = async |client: &SequencerClient| client.get_last_block_id().await; + pub async fn send_program_deployment_transaction( + &mut self, + bytecode: Vec, + ) -> Result { + let message = lee::program_deployment_transaction::Message::new(bytecode); + let transaction = ProgramDeploymentTransaction::new(message); + + let call_f = async |client: &SequencerClient| { + client + .send_transaction(LeeTransaction::ProgramDeployment(transaction)) + .await + }; let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await; self.metric_updates.push(metrics_update); - let latest_block_id = call_res?; + Ok(call_res?) + } + + pub async fn sync_to_latest_block(&mut self) -> Result { + let latest_block_id = self.get_last_block_id().await?; println!("Latest block is {latest_block_id}"); self.sync_to_block(latest_block_id).await?; Ok(latest_block_id) diff --git a/lez/wallet/src/main.rs b/lez/wallet/src/main.rs index d3c27427..8a66a642 100644 --- a/lez/wallet/src/main.rs +++ b/lez/wallet/src/main.rs @@ -33,7 +33,7 @@ async fn main() -> Result<()> { if let Some(command) = command { let mut wallet = if storage_path.exists() { - WalletCore::new_update_chain(config_path, storage_path, metrics_path, None)? + WalletCore::new_update_chain(config_path, storage_path, metrics_path, None).await? } else { // TODO: Maybe move to `WalletCore::from_env()` or similar? @@ -46,7 +46,8 @@ async fn main() -> Result<()> { metrics_path, None, &password, - )?; + ) + .await?; println!(); println!("IMPORTANT: Write down your recovery phrase and store it securely."); @@ -63,7 +64,7 @@ async fn main() -> Result<()> { Ok(()) } else if continuous_run { let mut wallet = - WalletCore::new_update_chain(config_path, storage_path, metrics_path, None)?; + WalletCore::new_update_chain(config_path, storage_path, metrics_path, None).await?; execute_continuous_run(&mut wallet).await } else { let help = Args::command().render_long_help(); diff --git a/lez/wallet/src/multi_client.rs b/lez/wallet/src/multi_client.rs index a79be6d0..534f7fb1 100644 --- a/lez/wallet/src/multi_client.rs +++ b/lez/wallet/src/multi_client.rs @@ -87,7 +87,8 @@ impl MultiSequencerClient { client_list.insert(sequencer_addr.clone(), sequencer_client); } - let (leader_url, leader) = choose_leader(&client_list, metrics); + let (leader_url, leader) = + choose_leader(&client_list, metrics).ok_or(anyhow::anyhow!("Failed to find leader"))?; Ok(Self { client_list, @@ -172,10 +173,97 @@ pub async fn callibration(client: SequencerClient) -> Metrics { } pub fn choose_leader( - _client_list: &HashMap, - _metrics: &HashMap, -) -> (Url, SequencerClient) { - todo!() + client_list: &HashMap, + metrics: &HashMap, +) -> Option<(Url, SequencerClient)> { + let mut client_vec = vec![]; + + // Sort out all unmetered clients + for addr in client_list.keys() { + if metrics.contains_key(addr) { + client_vec.push(addr); + } + } + + if client_vec.is_empty() { + return None; + } + + // Considering the nature of our requests, the latest_block_id is dominant characteristic + let max_block_id_addr = client_vec.iter().fold(client_vec[0], |acc, x| { + let old_latest_block_id = metrics.get(acc).unwrap().latest_block_id; + let new_latest_block_id = metrics.get(*x).unwrap().latest_block_id; + if new_latest_block_id > old_latest_block_id { + *x + } else { + acc + } + }); + + let max_block_id = metrics.get(max_block_id_addr).unwrap().latest_block_id; + + // Sort out all latest clients + client_vec = client_vec + .iter() + .filter_map(|x| { + let latest_block_id = metrics.get(*x).unwrap().latest_block_id; + + if latest_block_id == max_block_id { + Some(*x) + } else { + None + } + }) + .collect(); + + // Conservative assumption: least amount of errors is the best + let min_err_addr = client_vec.iter().fold(client_vec[0], |acc, x| { + let old_err_count = metrics.get(acc).unwrap().errors; + let new_err_count = metrics.get(*x).unwrap().errors; + if new_err_count < old_err_count { + *x + } else { + acc + } + }); + + let min_err_count = metrics.get(min_err_addr).unwrap().errors; + + // Sort out all latest clients + client_vec = client_vec + .iter() + .filter_map(|x| { + let err_count = metrics.get(*x).unwrap().errors; + + if err_count == min_err_count { + Some(*x) + } else { + None + } + }) + .collect(); + + // Choose clients with least latency and variance + let min_lat_var_addr = client_vec.iter().fold(client_vec[0], |acc, x| { + let old = metrics.get(acc).unwrap(); + let (old_lat, old_var) = (old.latency_avg, old.latency_var); + let new = metrics.get(*x).unwrap(); + let (new_lat, new_var) = (new.latency_avg, new.latency_var); + + // Client is better if it is strongly linearly better + if ((new_lat - new_var) > (old_lat - old_var)) + && ((new_lat + new_var) > (old_lat + old_var)) + { + *x + } else { + acc + } + }); + + Some(( + min_lat_var_addr.clone(), + client_list.get(min_lat_var_addr).unwrap().clone(), + )) } pub fn save_metrics_at_path( @@ -191,16 +279,14 @@ pub fn save_metrics_at_path( Ok(()) } -pub fn save_metrics_at_path_with_updates( - mut metrics: HashMap, - leader_url: &Url, - metric_updates: &[MetricsUpdate], - path: &Path, -) -> Result<(), anyhow::Error> { - let leader_metric = metrics - .get_mut(leader_url) - .ok_or(anyhow::anyhow!("Leader URL is in present in metrics"))?; +struct CumulativeUpdates { + pub failure_count: u64, + pub latest_block_id: Option, + pub cumulative_latency: f32, + pub additional_sample_size: usize, +} +fn cumulative_updates(metric_updates: &[MetricsUpdate]) -> CumulativeUpdates { let (failure_count, latest_block_id, cumulative_latency) = metric_updates.iter().fold((0u64, None, 0f32), |acc, x| { let MetricsUpdate { @@ -210,39 +296,150 @@ pub fn save_metrics_at_path_with_updates( } = x; ( if *is_failed { acc.0 + 1 } else { acc.0 }, - acc.1.map(|inner| { - if let Some(new_latest_block_id) = new_latest_block_id { - if inner < *new_latest_block_id { - *new_latest_block_id - } else { - inner - } - } else { - inner - } - }), - acc.2 + latency, + match (acc.1, new_latest_block_id) { + (None, None) => None, + (None, Some(val)) | (Some(val), None) => Some(val), + (Some(val_old), Some(val_new)) => Some(std::cmp::max(val_old, val_new)), + }, + if !*is_failed { acc.2 + latency } else { acc.2 }, ) }); + CumulativeUpdates { + failure_count, + latest_block_id: latest_block_id.copied(), + cumulative_latency, + additional_sample_size: metric_updates.len() - (failure_count as usize), + } +} + +fn cumulative_avg( + latency_avg_old: f32, + cumulative_latency: f32, + orig_size_f: f32, + mod_size_f: f32, +) -> f32 { + (latency_avg_old * orig_size_f + cumulative_latency) / mod_size_f +} + +fn cumulative_var( + latency_avg_old: f32, + latency_avg_new: f32, + latency_var: f32, + orig_size_f: f32, + mod_size_f: f32, +) -> f32 { + (latency_avg_old - latency_avg_new) + * ((3f32 * latency_avg_old + latency_avg_new) * orig_size_f / mod_size_f) + + (latency_var * latency_var) * orig_size_f / mod_size_f +} + +pub fn update_metrics( + metrics: &mut HashMap, + leader_url: &Url, + metric_updates: &[MetricsUpdate], +) -> Result<(), anyhow::Error> { + let leader_metric = metrics + .get_mut(leader_url) + .ok_or(anyhow::anyhow!("Leader URL is in present in metrics"))?; + + let CumulativeUpdates { + failure_count, + latest_block_id, + cumulative_latency, + additional_sample_size, + } = cumulative_updates(metric_updates); + leader_metric.errors += failure_count; if let Some(latest_block_id) = latest_block_id { leader_metric.latest_block_id = latest_block_id; } let orig_size_f = leader_metric.sample_size as f32; - let mod_size_f = (leader_metric.sample_size + metric_updates.len()) as f32; + let mod_size_f = (leader_metric.sample_size + additional_sample_size) as f32; let latency_avg_old = leader_metric.latency_avg; let latency_avg_new = - (leader_metric.latency_avg * orig_size_f + cumulative_latency) / mod_size_f; + cumulative_avg(latency_avg_old, cumulative_latency, orig_size_f, mod_size_f); - let latency_disp_new = (latency_avg_old - latency_avg_new) - * ((3f32 * latency_avg_old + latency_avg_new) * orig_size_f / mod_size_f) - + (leader_metric.latency_var * leader_metric.latency_var) * orig_size_f / mod_size_f; + let latency_disp_new = cumulative_var( + latency_avg_old, + latency_avg_new, + leader_metric.latency_var, + orig_size_f, + mod_size_f, + ); leader_metric.latency_avg = latency_avg_new; leader_metric.latency_var = latency_disp_new.sqrt(); + Ok(()) +} + +pub fn save_metrics_at_path_with_updates( + mut metrics: HashMap, + leader_url: &Url, + metric_updates: &[MetricsUpdate], + path: &Path, +) -> Result<(), anyhow::Error> { + update_metrics(&mut metrics, leader_url, metric_updates)?; + save_metrics_at_path(&metrics, path) } + +#[cfg(test)] +mod tests { + use crate::multi_client::{MetricsUpdate, cumulative_avg, cumulative_updates}; + + #[test] + fn cumulative_updates_test() { + let metrics_updates_vec = vec![ + MetricsUpdate { + latency: 100_f32, + new_latest_block_id: Some(15), + is_failed: false, + }, + MetricsUpdate { + latency: 115_f32, + new_latest_block_id: Some(16), + is_failed: false, + }, + MetricsUpdate { + latency: 50_f32, + new_latest_block_id: None, + is_failed: true, + }, + ]; + + let cumulative_updates = cumulative_updates(&metrics_updates_vec); + + let epsilon = 0.01_f32; + + assert_eq!(cumulative_updates.additional_sample_size, 2); + assert_eq!(cumulative_updates.failure_count, 1); + assert_eq!(cumulative_updates.latest_block_id, Some(16)); + assert!((cumulative_updates.cumulative_latency - 215_f32).abs() < epsilon); + } + + #[test] + fn cumulative_avg_test() { + let mut sample = vec![100_f32; 20]; + + let old_sample_size_f = sample.len() as f32; + let old_avg = sample.iter().sum::() / old_sample_size_f; + + let new_samples = vec![101_f32, 110_f32, 112_f32, 97_f32, 78_f32]; + let mod_sample_size_f = new_samples.len() as f32 + old_sample_size_f; + let cumulative = new_samples.iter().sum(); + + sample.extend_from_slice(&new_samples); + let new_sample_size_f = sample.len() as f32; + let new_avg_1 = sample.iter().sum::() / new_sample_size_f; + + let new_avg_2 = cumulative_avg(old_avg, cumulative, old_sample_size_f, mod_sample_size_f); + + let epsilon = 0.01_f32; + + assert!((new_avg_1 - new_avg_2).abs() < epsilon); + } +} diff --git a/lez/wallet/src/program_facades/amm.rs b/lez/wallet/src/program_facades/amm.rs index bc5c5d62..68802c3f 100644 --- a/lez/wallet/src/program_facades/amm.rs +++ b/lez/wallet/src/program_facades/amm.rs @@ -4,11 +4,11 @@ use lee::{AccountId, program::Program}; use token_core::TokenHolding; use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; -pub struct Amm<'wallet>(pub &'wallet WalletCore); +pub struct Amm<'wallet>(pub &'wallet mut WalletCore); impl Amm<'_> { pub async fn send_new_definition( - &self, + &mut self, user_holding_a: AccountIdentity, user_holding_b: AccountIdentity, user_holding_lp: AccountIdentity, @@ -72,7 +72,7 @@ impl Amm<'_> { } pub async fn send_swap_exact_input( - &self, + &mut self, user_holding_a: AccountIdentity, user_holding_b: AccountIdentity, swap_amount_in: u128, @@ -153,7 +153,7 @@ impl Amm<'_> { } pub async fn send_swap_exact_output( - &self, + &mut self, user_holding_a: AccountIdentity, user_holding_b: AccountIdentity, exact_amount_out: u128, @@ -234,7 +234,7 @@ impl Amm<'_> { } pub async fn send_add_liquidity( - &self, + &mut self, user_holding_a: AccountIdentity, user_holding_b: AccountIdentity, user_holding_lp: AccountIdentity, @@ -299,7 +299,7 @@ impl Amm<'_> { } pub async fn send_remove_liquidity( - &self, + &mut self, user_holding_a: AccountId, user_holding_b: AccountId, user_holding_lp: AccountIdentity, diff --git a/lez/wallet/src/program_facades/ata.rs b/lez/wallet/src/program_facades/ata.rs index 6c9ec568..373469ba 100644 --- a/lez/wallet/src/program_facades/ata.rs +++ b/lez/wallet/src/program_facades/ata.rs @@ -9,11 +9,11 @@ use lee_core::SharedSecretKey; use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; -pub struct Ata<'wallet>(pub &'wallet WalletCore); +pub struct Ata<'wallet>(pub &'wallet mut WalletCore); impl Ata<'_> { pub async fn send_create( - &self, + &mut self, owner: AccountIdentity, definition_id: AccountId, ) -> Result { @@ -44,7 +44,7 @@ impl Ata<'_> { } pub async fn send_transfer( - &self, + &mut self, owner: AccountIdentity, definition_id: AccountId, recipient_id: AccountId, @@ -80,7 +80,7 @@ impl Ata<'_> { } pub async fn send_burn( - &self, + &mut self, owner: AccountIdentity, definition_id: AccountId, amount: u128, @@ -115,7 +115,7 @@ impl Ata<'_> { } pub async fn send_create_private_owner( - &self, + &mut self, owner_id: AccountId, definition_id: AccountId, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { @@ -147,7 +147,7 @@ impl Ata<'_> { } pub async fn send_transfer_private_owner( - &self, + &mut self, owner_id: AccountId, definition_id: AccountId, recipient_id: AccountId, @@ -184,7 +184,7 @@ impl Ata<'_> { } pub async fn send_burn_private_owner( - &self, + &mut self, owner_id: AccountId, definition_id: AccountId, amount: u128, diff --git a/lez/wallet/src/program_facades/bridge.rs b/lez/wallet/src/program_facades/bridge.rs index c9aa585e..4b8ad6f6 100644 --- a/lez/wallet/src/program_facades/bridge.rs +++ b/lez/wallet/src/program_facades/bridge.rs @@ -3,11 +3,11 @@ use lee::{AccountId, program::Program}; use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; -pub struct Bridge<'wallet>(pub &'wallet WalletCore); +pub struct Bridge<'wallet>(pub &'wallet mut WalletCore); impl Bridge<'_> { pub async fn send_withdraw( - &self, + &mut self, sender_account_id: AccountId, amount: u64, bedrock_account_pk: [u8; 32], diff --git a/lez/wallet/src/program_facades/native_token_transfer/deshielded.rs b/lez/wallet/src/program_facades/native_token_transfer/deshielded.rs index f060e0fa..8354c5fc 100644 --- a/lez/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/lez/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -6,7 +6,7 @@ use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_deshielded_transfer( - &self, + &mut self, from: AccountId, to: AccountId, balance_to_move: u128, diff --git a/lez/wallet/src/program_facades/native_token_transfer/mod.rs b/lez/wallet/src/program_facades/native_token_transfer/mod.rs index c4e673fa..c54d344a 100644 --- a/lez/wallet/src/program_facades/native_token_transfer/mod.rs +++ b/lez/wallet/src/program_facades/native_token_transfer/mod.rs @@ -12,7 +12,7 @@ pub mod shielded; clippy::multiple_inherent_impl, reason = "impl blocks split across multiple files for organization" )] -pub struct NativeTokenTransfer<'wallet>(pub &'wallet WalletCore); +pub struct NativeTokenTransfer<'wallet>(pub &'wallet mut WalletCore); fn auth_transfer_preparation( balance_to_move: u128, diff --git a/lez/wallet/src/program_facades/native_token_transfer/private.rs b/lez/wallet/src/program_facades/native_token_transfer/private.rs index 712d6774..a650b78f 100644 --- a/lez/wallet/src/program_facades/native_token_transfer/private.rs +++ b/lez/wallet/src/program_facades/native_token_transfer/private.rs @@ -9,7 +9,7 @@ use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn register_account_private( - &self, + &mut self, from: AccountId, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = authenticated_transfer_core::Instruction::Initialize; @@ -34,7 +34,7 @@ impl NativeTokenTransfer<'_> { } pub async fn send_private_transfer_to_outer_account( - &self, + &mut self, from: AccountId, to_npk: NullifierPublicKey, to_vpk: ViewingPublicKey, @@ -69,7 +69,7 @@ impl NativeTokenTransfer<'_> { } pub async fn send_private_transfer_to_owned_account( - &self, + &mut self, from: AccountId, to: AccountId, balance_to_move: u128, diff --git a/lez/wallet/src/program_facades/native_token_transfer/public.rs b/lez/wallet/src/program_facades/native_token_transfer/public.rs index ee01d9c8..81f62f8b 100644 --- a/lez/wallet/src/program_facades/native_token_transfer/public.rs +++ b/lez/wallet/src/program_facades/native_token_transfer/public.rs @@ -10,7 +10,7 @@ use crate::{ impl NativeTokenTransfer<'_> { pub async fn send_public_transfer( - &self, + &mut self, from: AccountIdentity, to: AccountIdentity, balance_to_move: u128, @@ -28,7 +28,7 @@ impl NativeTokenTransfer<'_> { } pub async fn register_account( - &self, + &mut self, account: AccountIdentity, ) -> Result { let instruction_data = Program::serialize_instruction(AuthTransferInstruction::Initialize)?; diff --git a/lez/wallet/src/program_facades/native_token_transfer/shielded.rs b/lez/wallet/src/program_facades/native_token_transfer/shielded.rs index 002b1176..18c998c5 100644 --- a/lez/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/lez/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -7,7 +7,7 @@ use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer( - &self, + &mut self, from: AccountIdentity, to: AccountId, balance_to_move: u128, @@ -36,7 +36,7 @@ impl NativeTokenTransfer<'_> { } pub async fn send_shielded_transfer_to_outer_account( - &self, + &mut self, from: AccountIdentity, to_npk: NullifierPublicKey, to_vpk: ViewingPublicKey, diff --git a/lez/wallet/src/program_facades/pinata.rs b/lez/wallet/src/program_facades/pinata.rs index 3a1ff898..d7d79844 100644 --- a/lez/wallet/src/program_facades/pinata.rs +++ b/lez/wallet/src/program_facades/pinata.rs @@ -4,11 +4,11 @@ use lee_core::{MembershipProof, SharedSecretKey}; use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; -pub struct Pinata<'wallet>(pub &'wallet WalletCore); +pub struct Pinata<'wallet>(pub &'wallet mut WalletCore); impl Pinata<'_> { pub async fn claim( - &self, + &mut self, pinata_account_id: AccountId, winner_account_id: AccountId, solution: u128, @@ -35,7 +35,7 @@ impl Pinata<'_> { /// The `winner_proof` parameter is accepted for API completeness; the wallet currently fetches /// the membership proof automatically from the chain. pub async fn claim_private_owned_account_already_initialized( - &self, + &mut self, pinata_account_id: AccountId, winner_account_id: AccountId, solution: u128, @@ -46,7 +46,7 @@ impl Pinata<'_> { } pub async fn claim_private_owned_account( - &self, + &mut self, pinata_account_id: AccountId, winner_account_id: AccountId, solution: u128, diff --git a/lez/wallet/src/program_facades/token.rs b/lez/wallet/src/program_facades/token.rs index d634976f..c9418ee4 100644 --- a/lez/wallet/src/program_facades/token.rs +++ b/lez/wallet/src/program_facades/token.rs @@ -5,11 +5,11 @@ use token_core::Instruction; use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; -pub struct Token<'wallet>(pub &'wallet WalletCore); +pub struct Token<'wallet>(pub &'wallet mut WalletCore); impl Token<'_> { pub async fn send_new_definition( - &self, + &mut self, definition: AccountIdentity, supply: AccountIdentity, name: String, @@ -29,7 +29,7 @@ impl Token<'_> { } pub async fn send_new_definition_private_owned_supply( - &self, + &mut self, definition_account_id: AccountId, supply_account_id: AccountId, name: String, @@ -61,7 +61,7 @@ impl Token<'_> { } pub async fn send_new_definition_private_owned_definiton( - &self, + &mut self, definition_account_id: AccountId, supply_account_id: AccountId, name: String, @@ -93,7 +93,7 @@ impl Token<'_> { } pub async fn send_new_definition_private_owned_definiton_and_supply( - &self, + &mut self, definition_account_id: AccountId, supply_account_id: AccountId, name: String, @@ -126,7 +126,7 @@ impl Token<'_> { } pub async fn send_transfer_transaction( - &self, + &mut self, sender: AccountIdentity, recipient: AccountIdentity, amount: u128, @@ -147,7 +147,7 @@ impl Token<'_> { } pub async fn send_transfer_transaction_private_owned_account( - &self, + &mut self, sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, @@ -181,7 +181,7 @@ impl Token<'_> { } pub async fn send_transfer_transaction_private_foreign_account( - &self, + &mut self, sender_account_id: AccountId, recipient_npk: NullifierPublicKey, recipient_vpk: ViewingPublicKey, @@ -219,7 +219,7 @@ impl Token<'_> { } pub async fn send_transfer_transaction_deshielded( - &self, + &mut self, sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, @@ -252,7 +252,7 @@ impl Token<'_> { } pub async fn send_transfer_transaction_shielded_owned_account( - &self, + &mut self, sender: AccountIdentity, recipient_account_id: AccountId, amount: u128, @@ -284,7 +284,7 @@ impl Token<'_> { } pub async fn send_transfer_transaction_shielded_foreign_account( - &self, + &mut self, sender: AccountIdentity, recipient_npk: NullifierPublicKey, recipient_vpk: ViewingPublicKey, @@ -320,7 +320,7 @@ impl Token<'_> { } pub async fn send_burn_transaction( - &self, + &mut self, definition_account_id: AccountId, holder: AccountIdentity, amount: u128, @@ -341,7 +341,7 @@ impl Token<'_> { } pub async fn send_burn_transaction_private_owned_account( - &self, + &mut self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, @@ -375,7 +375,7 @@ impl Token<'_> { } pub async fn send_burn_transaction_deshielded_owned_account( - &self, + &mut self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, @@ -408,7 +408,7 @@ impl Token<'_> { } pub async fn send_burn_transaction_shielded( - &self, + &mut self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, @@ -441,7 +441,7 @@ impl Token<'_> { } pub async fn send_mint_transaction( - &self, + &mut self, definition: AccountIdentity, holder: AccountIdentity, amount: u128, @@ -462,7 +462,7 @@ impl Token<'_> { } pub async fn send_mint_transaction_private_owned_account( - &self, + &mut self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, @@ -496,7 +496,7 @@ impl Token<'_> { } pub async fn send_mint_transaction_private_foreign_account( - &self, + &mut self, definition_account_id: AccountId, holder_npk: NullifierPublicKey, holder_vpk: ViewingPublicKey, @@ -534,7 +534,7 @@ impl Token<'_> { } pub async fn send_mint_transaction_deshielded( - &self, + &mut self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, @@ -567,7 +567,7 @@ impl Token<'_> { } pub async fn send_mint_transaction_shielded_owned_account( - &self, + &mut self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, @@ -600,7 +600,7 @@ impl Token<'_> { } pub async fn send_mint_transaction_shielded_foreign_account( - &self, + &mut self, definition_account_id: AccountId, holder_npk: NullifierPublicKey, holder_vpk: ViewingPublicKey, diff --git a/lez/wallet/src/program_facades/vault.rs b/lez/wallet/src/program_facades/vault.rs index dcb5c5e0..f080c2a0 100644 --- a/lez/wallet/src/program_facades/vault.rs +++ b/lez/wallet/src/program_facades/vault.rs @@ -8,11 +8,11 @@ use lee_core::SharedSecretKey; use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; -pub struct Vault<'wallet>(pub &'wallet WalletCore); +pub struct Vault<'wallet>(pub &'wallet mut WalletCore); impl Vault<'_> { pub async fn send_transfer( - &self, + &mut self, sender_id: AccountId, recipient_id: AccountId, amount: u128, @@ -41,7 +41,7 @@ impl Vault<'_> { } pub async fn send_transfer_private_sender( - &self, + &mut self, sender_id: AccountId, recipient_id: AccountId, amount: u128, @@ -75,7 +75,7 @@ impl Vault<'_> { } pub async fn send_claim( - &self, + &mut self, owner_id: AccountId, amount: u128, ) -> Result { @@ -99,7 +99,7 @@ impl Vault<'_> { } pub async fn send_claim_private_owner( - &self, + &mut self, owner_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> {