feat(wallet): leader choosing algorithm

This commit is contained in:
Pravdyvy 2026-07-09 15:58:29 +03:00
parent 40fe9ff209
commit d832559cc3
21 changed files with 434 additions and 159 deletions

View File

@ -314,7 +314,7 @@ impl AccountSubcommand {
raw: bool,
keys: bool,
account_id: CliAccountMention,
wallet_core: &WalletCore,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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() => {

View File

@ -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<SubcommandReturnValue> {
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:#?}");
}

View File

@ -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?;

View File

@ -125,7 +125,7 @@ impl AmmProgramAgnosticSubcommand {
user_holding_lp: CliAccountMention,
balance_a: u128,
balance_b: u128,
wallet_core: &WalletCore,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
let a_id = user_holding_a.resolve(wallet_core.storage())?;
let b_id = user_holding_b.resolve(wallet_core.storage())?;

View File

@ -425,7 +425,7 @@ impl NativeTokenTransferProgramSubcommandShielded {
to_vpk: String,
to_identifier: Option<u128>,
amount: u128,
wallet_core: &WalletCore,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
let (to_npk, to_vpk) = crate::cli::decode_npk_vpk(&to_npk, &to_vpk)?;
@ -497,7 +497,7 @@ impl NativeTokenTransferProgramSubcommand {
from: Option<AccountIdentity>,
to: Option<AccountIdentity>,
amount: u128,
wallet_core: &WalletCore,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
let tx_hash = NativeTokenTransfer(wallet_core)
.send_public_transfer(

View File

@ -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<u128> {
async fn find_solution(wallet: &mut WalletCore, pinata_account_id: AccountId) -> Result<u128> {
let account = wallet.get_account_public(pinata_account_id).await?;
let data: [u8; 33] = account
.data

View File

@ -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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
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<SubcommandReturnValue> {
let definition = definition_account_id.resolve(wallet_core.storage())?;
let supply = supply_account_id.resolve(wallet_core.storage())?;

View File

@ -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<Url, Metrics>,
metric_updates: Vec<MetricsUpdate>,
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<u64> {
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<Option<Block>> {
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<Option<LeeTransaction>> {
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<Account> {
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<BTreeMap<String, ProgramId>> {
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<LeeTransaction> {
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<u64> {
let call_f = async |client: &SequencerClient| client.get_last_block_id().await;
pub async fn send_program_deployment_transaction(
&mut self,
bytecode: Vec<u8>,
) -> Result<HashType> {
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<u64> {
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)

View File

@ -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();

View File

@ -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<Url, SequencerClient>,
_metrics: &HashMap<Url, Metrics>,
) -> (Url, SequencerClient) {
todo!()
client_list: &HashMap<Url, SequencerClient>,
metrics: &HashMap<Url, Metrics>,
) -> 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<Url, Metrics>,
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<u64>,
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<Url, Metrics>,
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<Url, Metrics>,
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::<f32>() / 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::<f32>() / 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);
}
}

View File

@ -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,

View File

@ -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<HashType, ExecutionFailureKind> {
@ -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,

View File

@ -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],

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<HashType, ExecutionFailureKind> {
let instruction_data = Program::serialize_instruction(AuthTransferInstruction::Initialize)?;

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<HashType, ExecutionFailureKind> {
@ -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> {