From 76dab9f6120d5bf70cf20fae69aa220598c20812 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Tue, 5 Mar 2024 23:06:19 +0100 Subject: [PATCH 01/66] Wallet store atomic update; atomic versions of validator data getters --- Cargo.toml | 1 + crates/apps/src/lib/client/tx.rs | 7 +- crates/apps/src/lib/node/ledger/shell/mod.rs | 4 +- crates/apps/src/lib/wallet/mod.rs | 12 +- crates/sdk/src/wallet/mod.rs | 234 +++++++++++++++++-- 5 files changed, 228 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0d980f9109..3a97b137bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -129,6 +129,7 @@ num-rational = "0.4.1" num-traits = "0.2.14" once_cell = "1.8.0" orion = "0.16.0" +partial_application = "0.2.1" paste = "1.0.9" pretty_assertions = "1.4.0" primitive-types = "0.12.1" diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index 5c67d3aef4..2c4a6d805a 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -588,10 +588,13 @@ pub async fn submit_become_validator( // add validator address and keys to the wallet let mut wallet = namada.wallet_mut().await; - wallet.add_validator_data(args.address.clone(), validator_keys); + // wallet.add_validator_data(address.clone(), validator_keys); wallet - .save() + .add_validator_data_atomic(args.address.clone(), validator_keys) .unwrap_or_else(|err| edisplay_line!(namada.io(), "{}", err)); + // wallet.save().unwrap_or_else(|err| { + // edisplay_line!(namada.io(), "{}", err) + // }); let tendermint_home = config.ledger.cometbft_dir(); tendermint_node::write_validator_key( diff --git a/crates/apps/src/lib/node/ledger/shell/mod.rs b/crates/apps/src/lib/node/ledger/shell/mod.rs index 7c880e7693..9fa68504c4 100644 --- a/crates/apps/src/lib/node/ledger/shell/mod.rs +++ b/crates/apps/src/lib/node/ledger/shell/mod.rs @@ -456,7 +456,7 @@ where "Loading wallet from {}", wallet_path.to_string_lossy() ); - let mut wallet = crate::wallet::load(wallet_path) + let wallet = crate::wallet::load(wallet_path) .expect("Validator node must have a wallet"); let validator_local_config_path = wallet_path.join("validator_local_config.toml"); @@ -475,7 +475,7 @@ where }; wallet - .take_validator_data() + .take_validator_data_atomic() .map(|data| ShellMode::Validator { data, broadcast_sender, diff --git a/crates/apps/src/lib/wallet/mod.rs b/crates/apps/src/lib/wallet/mod.rs index b35575e25b..cb293382ec 100644 --- a/crates/apps/src/lib/wallet/mod.rs +++ b/crates/apps/src/lib/wallet/mod.rs @@ -12,7 +12,7 @@ pub use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::fs::FsWalletStorage; use namada_sdk::wallet::store::Store; use namada_sdk::wallet::{ - ConfirmationResponse, FindKeyError, Wallet, WalletIo, + ConfirmationResponse, FindKeyError, Wallet, WalletIo, WalletStorage, }; pub use namada_sdk::wallet::{ValidatorData, ValidatorKeys}; use rand_core::OsRng; @@ -191,7 +191,7 @@ pub fn read_and_confirm_passphrase_tty( /// for signing protocol txs and for the DKG (which will also be stored) /// A protocol keypair may be optionally provided, indicating that /// we should reuse a keypair already in the wallet -pub fn gen_validator_keys( +pub fn gen_validator_keys( wallet: &mut Wallet, eth_bridge_pk: Option, protocol_pk: Option, @@ -221,8 +221,8 @@ fn find_secret_key( extract_key: F, ) -> Result, FindKeyError> where - F: Fn(&ValidatorData) -> common::SecretKey, - U: WalletIo, + F: Fn(ValidatorData) -> common::SecretKey, + U: WalletIo + WalletStorage, { maybe_pk .map(|pk| { @@ -231,7 +231,9 @@ where // TODO: optionally encrypt validator keys .find_key_by_pkh(&pkh, None) .ok() - .or_else(|| wallet.get_validator_data().map(extract_key)) + .or_else(|| { + wallet.take_validator_data_atomic().map(extract_key) + }) .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) }) .transpose() diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index b18025b178..3af5955c95 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -95,6 +95,9 @@ pub enum LoadStoreError { /// Wallet store writing error #[error("Failed to write the wallet store: {0}")] StoreNewWallet(String), + /// Wallet store update error + #[error("Failed to update the wallet store from {0}: {1}")] + UpdateWallet(String, String), } /// Captures the permanent storage parts of the wallet's functioning @@ -104,6 +107,26 @@ pub trait WalletStorage: Sized + Clone { /// Load a wallet from the store file. fn load(&self, wallet: &mut Wallet) -> Result<(), LoadStoreError>; + + // XXX REMOVE + /// Atomically update the wallet store + fn update( + &self, + wallet: &mut Wallet, + transform: impl FnOnce(&mut Wallet), + ) -> Result<(), LoadStoreError>; + + /// Atomically update the wallet store + fn update_store( + &self, + update: impl FnOnce(&mut Store), + ) -> Result<(), LoadStoreError>; + + /// Load wallet from the store file (read only) + fn load_store_read_only(&self) -> Result; + + // fn close(&self, wallet: &Wallet, storage_lock: WalletStorageLock) + // -> Result<(), LoadStoreError>; } #[cfg(feature = "std")] @@ -182,6 +205,130 @@ pub mod fs { Store::decode(store).map_err(LoadStoreError::Decode)?; Ok(()) } + + fn load_store_read_only(&self) -> Result { + let wallet_file = self.store_dir().join(FILE_NAME); + let mut options = fs::OpenOptions::new(); + options.read(true).write(false); + let lock = + RwLock::new(options.open(wallet_file).map_err(|err| { + LoadStoreError::ReadWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?); + let guard = lock.read().map_err(|err| { + LoadStoreError::ReadWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + LoadStoreError::ReadWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + Store::decode(store).map_err(LoadStoreError::Decode) + } + + fn update( + &self, + wallet: &mut Wallet, + transform: impl FnOnce(&mut Wallet), + ) -> Result<(), LoadStoreError> { + let wallet_file = self.store_dir().join(FILE_NAME); + let mut options = fs::OpenOptions::new(); + options.create(true).write(true).truncate(true); + let mut lock = + RwLock::new(options.open(&wallet_file).map_err(|err| { + LoadStoreError::UpdateWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?); + let mut guard = lock.write().map_err(|err| { + LoadStoreError::UpdateWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + LoadStoreError::UpdateWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + wallet.store = + Store::decode(store).map_err(LoadStoreError::Decode)?; + + // Apply wallet transformation + transform(wallet); + + let data = wallet.store.encode(); + // XXX + // Make sure the dir exists + // let wallet_dir = wallet_path.parent().unwrap(); + // fs::create_dir_all(wallet_dir).map_err(|err| { + // LoadStoreError::StoreNewWallet(err.to_string()) + // })?; + // Write the file + guard + .write_all(&data) + .map_err(|err| LoadStoreError::StoreNewWallet(err.to_string())) + } + + fn update_store( + &self, + update: impl FnOnce(&mut Store), + ) -> Result<(), LoadStoreError> { + let wallet_file = self.store_dir().join(FILE_NAME); + let mut options = fs::OpenOptions::new(); + options.create(true).write(true).truncate(true); + let mut lock = + RwLock::new(options.open(&wallet_file).map_err(|err| { + LoadStoreError::UpdateWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?); + let mut guard = lock.write().map_err(|err| { + LoadStoreError::UpdateWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + LoadStoreError::UpdateWallet( + self.store_dir().to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + let mut store = + Store::decode(store).map_err(LoadStoreError::Decode)?; + + // Apply store transformation + update(&mut store); + + let data = store.encode(); + // XXX + // Make sure the dir exists + // let wallet_dir = wallet_path.parent().unwrap(); + // fs::create_dir_all(wallet_dir).map_err(|err| { + // LoadStoreError::StoreNewWallet(err.to_string()) + // })?; + // Write the file + guard + .write_all(&data) + .map_err(|err| LoadStoreError::StoreNewWallet(err.to_string())) + } + // fn close(&self, wallet: &Wallet, storage_lock: + // WalletStorageLock) -> Result<(), LoadStoreError> { + // Ok(()) + // } } /// For a non-interactive filesystem based wallet @@ -262,54 +409,70 @@ impl From> for Store { impl Wallet { /// Create a new wallet from the given backing store and storage location + // pub fn new(utils: U) -> Self { pub fn new(utils: U, store: Store) -> Self { Self { utils, + // store: Store::default(), store, decrypted_key_cache: HashMap::default(), decrypted_spendkey_cache: HashMap::default(), } } - /// Add validator data to the store - pub fn add_validator_data( - &mut self, - address: Address, - keys: ValidatorKeys, - ) { - self.store.add_validator_data(address, keys); - } - - /// Returns a reference to the validator data, if it exists. - pub fn get_validator_data(&self) -> Option<&ValidatorData> { - self.store.get_validator_data() - } - + // pub fn new(utils: U, store_location: Path) -> Self { + // Self { + // utils, + // store: Store::default(), + // store_location, + // decrypted_key_cache: HashMap::default(), + // decrypted_spendkey_cache: HashMap::default(), + // } + // } + + // /// Add validator data to the store + // fn add_validator_data( + // &mut self, + // address: Address, + // keys: ValidatorKeys, + // ) { + // self.store.add_validator_data(address, keys); + // } + + // /// Returns a reference to the validator data, if it exists. + // pub fn get_validator_data(&self) -> Option<&ValidatorData> { + // self.store.get_validator_data() + // } + + // XXX REMOVE? /// Returns a mut reference to the validator data, if it exists. - pub fn get_validator_data_mut(&mut self) -> Option<&mut ValidatorData> { - self.store.get_validator_data_mut() - } + // pub fn get_validator_data_mut(&mut self) -> Option<&mut ValidatorData> { + // self.store.get_validator_data_mut() + // } - /// Take the validator data, if it exists. - pub fn take_validator_data(&mut self) -> Option { - self.store.take_validator_data() - } + // /// Take the validator data, if it exists. + // pub fn take_validator_data(&mut self) -> Option { + // self.store.take_validator_data() + // } /// Returns the validator data, if it exists. pub fn into_validator_data(self) -> Option { self.store.into_validator_data() } + // XXX REMOVE? /// Provide immutable access to the backing store pub fn store(&self) -> &Store { &self.store } + // XXX REMOVE? /// Provide mutable access to the backing store pub fn store_mut(&mut self) -> &mut Store { &mut self.store } + /// XXX HERE /// Extend this wallet from pre-genesis validator wallet. pub fn extend_from_pre_genesis_validator( &mut self, @@ -528,6 +691,35 @@ impl Wallet { pub fn save(&self) -> Result<(), LoadStoreError> { self.utils.save(self) } + + /// Add validator data to the store + pub fn add_validator_data_atomic( + &mut self, + address: Address, + keys: ValidatorKeys, + ) -> Result<(), LoadStoreError> { + self.utils + .clone() + .update_store(|store| store.add_validator_data(address, keys)) + } + + /// XXX does not make sense in the current context -- REMOVE? + /// Returns the validator data, if it exists. + pub fn take_validator_data_atomic(&self) -> Option { + self.utils + .clone() + .load_store_read_only() + .ok() + .and_then(Store::into_validator_data) + } + + pub fn into_validator_data_atomic(self) -> Option { + self.utils + .clone() + .load_store_read_only() + .ok() + .and_then(Store::into_validator_data) + } } impl Wallet { From 7595c6dda84e0400e21ebe3bde92b1fd41360fb0 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 10:47:01 +0100 Subject: [PATCH 02/66] Atomic version of `extend_from_pregenesis` --- crates/apps/src/lib/config/genesis/chain.rs | 12 +++++++----- crates/sdk/src/wallet/mod.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/apps/src/lib/config/genesis/chain.rs b/crates/apps/src/lib/config/genesis/chain.rs index aea50c5889..beb6ce3895 100644 --- a/crates/apps/src/lib/config/genesis/chain.rs +++ b/crates/apps/src/lib/config/genesis/chain.rs @@ -151,11 +151,13 @@ impl Finalized { .find_validator(&tendermint_pk) .map(|tx| Address::Established(tx.tx.data.address.raw.clone())) .expect("Validator alias not found in genesis transactions."); - wallet.extend_from_pre_genesis_validator( - address, - alias, - validator_wallet, - ) + wallet + .extend_from_pre_genesis_validator_atomic( + address, + alias, + validator_wallet, + ) + .expect("Could not apply pre-genesis validator wallet.") } // Add some internal addresses to the wallet diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 3af5955c95..355fa91764 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -720,6 +720,22 @@ impl Wallet { .ok() .and_then(Store::into_validator_data) } + + /// Extend the wallet from pre-genesis validator wallet. + pub fn extend_from_pre_genesis_validator_atomic( + &self, + validator_address: Address, + validator_alias: Alias, + other: pre_genesis::ValidatorWallet, + ) -> Result<(), LoadStoreError> { + self.utils.clone().update_store( + partial!(Store::extend_from_pre_genesis_validator => _, + validator_address, + validator_alias, + other + ), + ) + } } impl Wallet { From d3455497b1d6721b2553245800a7abe47a239738 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 12:54:23 +0100 Subject: [PATCH 03/66] Fix comment --- crates/sdk/src/wallet/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sdk/src/wallet/store.rs b/crates/sdk/src/wallet/store.rs index 82b32a887d..0a7c9ff297 100644 --- a/crates/sdk/src/wallet/store.rs +++ b/crates/sdk/src/wallet/store.rs @@ -660,7 +660,7 @@ impl Store { }); } - /// get an address with the vp type + /// Get addresses with the vp type pub fn get_addresses_with_vp_type( &self, vp_type: AddressVpType, From d7e328787bcd9173e8e636f719ebf604b634ddf7 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 12:57:31 +0100 Subject: [PATCH 04/66] Atomic versions of `get_addresses_with_vp_type`, `add_vp_type_to_address`, `tokens_with_aliases` --- crates/apps/src/lib/client/rpc.rs | 12 +- crates/apps/src/lib/config/genesis/chain.rs | 12 +- crates/sdk/src/wallet/mod.rs | 115 ++++++++++++-------- 3 files changed, 86 insertions(+), 53 deletions(-) diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index 307c12b516..5dfde495cf 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -797,7 +797,11 @@ async fn query_tokens( tokens.insert(alias, token.clone()); } } - None => tokens = wallet.tokens_with_aliases(), + None => { + tokens = wallet + .tokens_with_aliases() + .expect("Failed to read tokens from the wallet store.") + } } if !show_ibc_tokens { @@ -1248,7 +1252,8 @@ pub async fn print_decoded_balance_with_epoch( let tokens = context .wallet() .await - .get_addresses_with_vp_type(AddressVpType::Token); + .get_addresses_with_vp_type_atomic(AddressVpType::Token) + .expect("Failed to read from the wallet storage"); if balance.is_zero() { display_line!(context.io(), "No shielded balance found for given key"); } @@ -2540,7 +2545,8 @@ pub async fn query_conversions( let tokens = context .wallet() .await - .get_addresses_with_vp_type(AddressVpType::Token); + .get_addresses_with_vp_type_atomic(AddressVpType::Token) + .expect("Failed to read from the wallet storage."); let conversions = rpc::query_conversions(context.client()) .await .expect("Conversions should be defined"); diff --git a/crates/apps/src/lib/config/genesis/chain.rs b/crates/apps/src/lib/config/genesis/chain.rs index beb6ce3895..414fcd0392 100644 --- a/crates/apps/src/lib/config/genesis/chain.rs +++ b/crates/apps/src/lib/config/genesis/chain.rs @@ -136,10 +136,12 @@ impl Finalized { config.address.clone(), false, ); - wallet.add_vp_type_to_address( - AddressVpType::Token, - config.address.clone(), - ); + wallet + .add_vp_type_to_address_atomic( + AddressVpType::Token, + config.address.clone(), + ) + .expect("Failed to update the wallet store."); } if let Some(pre_genesis_wallet) = pre_genesis_wallet { wallet.extend(pre_genesis_wallet); @@ -157,7 +159,7 @@ impl Finalized { alias, validator_wallet, ) - .expect("Could not apply pre-genesis validator wallet.") + .expect("Failed to apply pre-genesis validator wallet.") } // Add some internal addresses to the wallet diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 355fa91764..3435f1e35b 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -472,50 +472,39 @@ impl Wallet { &mut self.store } - /// XXX HERE - /// Extend this wallet from pre-genesis validator wallet. - pub fn extend_from_pre_genesis_validator( - &mut self, - validator_address: Address, - validator_alias: Alias, - other: pre_genesis::ValidatorWallet, - ) { - self.store.extend_from_pre_genesis_validator( - validator_address, - validator_alias, - other, - ) - } + // /// Extend this wallet from pre-genesis validator wallet. + // pub fn extend_from_pre_genesis_validator( + // &mut self, + // validator_address: Address, + // validator_alias: Alias, + // other: pre_genesis::ValidatorWallet, + // ) { + // self.store.extend_from_pre_genesis_validator( + // validator_address, + // validator_alias, + // other, + // ) + // } /// Gets all addresses given a vp_type - pub fn get_addresses_with_vp_type( - &self, - vp_type: AddressVpType, - ) -> HashSet
{ - self.store.get_addresses_with_vp_type(vp_type) - } - - /// Add a vp_type to a given address - pub fn add_vp_type_to_address( - &mut self, - vp_type: AddressVpType, - address: Address, - ) { - // defaults to an empty set - self.store.add_vp_type_to_address(vp_type, address) - } + // pub fn get_addresses_with_vp_type( + // &self, + // vp_type: AddressVpType, + // ) -> HashSet
{ + // self.store.get_addresses_with_vp_type(vp_type) + // } - /// Get addresses with tokens VP type keyed and ordered by their aliases. - pub fn tokens_with_aliases(&self) -> BTreeMap { - self.get_addresses_with_vp_type(AddressVpType::Token) - .into_iter() - .map(|addr| { - let alias = self.lookup_alias(&addr); - (alias, addr) - }) - .collect() - } + // /// Add a vp_type to a given address + // pub fn add_vp_type_to_address( + // &mut self, + // vp_type: AddressVpType, + // address: Address, + // ) { + // // defaults to an empty set + // self.store.add_vp_type_to_address(vp_type, address) + // } + /// XXX HERE /// Find the stored address by an alias. pub fn find_address( &self, @@ -728,13 +717,49 @@ impl Wallet { validator_alias: Alias, other: pre_genesis::ValidatorWallet, ) -> Result<(), LoadStoreError> { - self.utils.clone().update_store( - partial!(Store::extend_from_pre_genesis_validator => _, + self.utils.clone().update_store(|store| { + store.extend_from_pre_genesis_validator( validator_address, validator_alias, - other - ), - ) + other, + ) + }) + } + + /// Gets all addresses given a vp_type + pub fn get_addresses_with_vp_type_atomic( + &self, + vp_type: AddressVpType, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .get_addresses_with_vp_type(vp_type)) + } + + /// Add a vp_type to a given address + pub fn add_vp_type_to_address_atomic( + &mut self, + vp_type: AddressVpType, + address: Address, + ) -> Result<(), LoadStoreError> { + self.utils.clone().update_store(|store| { + store.add_vp_type_to_address(vp_type, address) + }) + } + + /// Get addresses with tokens VP type keyed and ordered by their aliases. + pub fn tokens_with_aliases( + &self, + ) -> Result, LoadStoreError> { + Ok(self + .get_addresses_with_vp_type_atomic(AddressVpType::Token)? + .into_iter() + .map(|addr| { + let alias = self.lookup_alias(&addr); + (alias, addr) + }) + .collect()) } } From f25632d5df0bec712f2f4a31e118addbfea6e276 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 15:08:40 +0100 Subject: [PATCH 05/66] Atomic version of `find_address` --- crates/apps/src/lib/cli/context.rs | 7 ++-- crates/apps/src/lib/cli/wallet.rs | 10 ++++-- crates/apps/src/lib/wallet/defaults.rs | 12 ++++--- crates/sdk/src/wallet/mod.rs | 44 ++++++++++++++++++-------- crates/tests/src/e2e/ledger_tests.rs | 9 ++++-- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index bf4d0569e4..97c3947218 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -431,7 +431,8 @@ impl ArgFromContext for Address { .map(|(trace_path, base_denom)| { let base_token = ctx .wallet - .find_address(&base_denom) + .find_address_atomic(&base_denom) + .expect("Failed to read from the wallet storage.") .map(|addr| addr.to_string()) .unwrap_or(base_denom); let ibc_denom = format!("{trace_path}/{base_token}"); @@ -447,8 +448,8 @@ impl ArgFromContext for Address { // Or it can be an alias that may be found in the wallet .or_else(|_| { ctx.wallet - .find_address(raw) - .map(|x| x.into_owned()) + .find_address_atomic(raw) + .expect("Failed to read from the wallet storage.") .ok_or(Skip) }) .map_err(|_| format!("Unknown address {raw}")) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 3a1796f20b..bef266524c 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -920,7 +920,10 @@ fn transparent_address_or_alias_find( ); } else if alias.is_some() { let alias = alias.unwrap().to_lowercase(); - if let Some(address) = wallet.find_address(&alias) { + if let Some(address) = wallet + .find_address_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { display_line!(io, "Found address {}", address.to_pretty_string()); } else { display_line!( @@ -1054,7 +1057,10 @@ fn transparent_key_address_find_by_alias( // Find transparent address if !keys_only { - if let Some(address) = wallet.find_address(&alias) { + if let Some(address) = wallet + .find_address_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { found = true; display_line!(io, &mut w_lock; "Found transparent address:") .unwrap(); diff --git a/crates/apps/src/lib/wallet/defaults.rs b/crates/apps/src/lib/wallet/defaults.rs index 2393b28c6b..ccbe5440f7 100644 --- a/crates/apps/src/lib/wallet/defaults.rs +++ b/crates/apps/src/lib/wallet/defaults.rs @@ -92,7 +92,8 @@ mod dev { /// An established user address for testing & development pub fn albert_address() -> Address { PREGENESIS_WALLET - .find_address("albert") + .find_address_atomic("albert") + .expect("Failed to read from the wallet storage.") .expect("Albert's address should be in the pre-genesis wallet") .into_owned() } @@ -100,7 +101,8 @@ mod dev { /// An established user address for testing & development pub fn bertha_address() -> Address { PREGENESIS_WALLET - .find_address("bertha") + .find_address_atomic("bertha") + .expect("Failed to read from the wallet storage.") .expect("Bertha's address should be in the pre-genesis wallet") .into_owned() } @@ -108,7 +110,8 @@ mod dev { /// An established user address for testing & development pub fn christel_address() -> Address { PREGENESIS_WALLET - .find_address("christel") + .find_address_atomic("christel") + .expect("Failed to read from the wallet storage.") .expect("Christel's address should be in the pre-genesis wallet") .into_owned() } @@ -126,7 +129,8 @@ mod dev { /// An established validator address for testing & development pub fn validator_address() -> Address { PREGENESIS_WALLET - .find_address("validator-0") + .find_address_atomic("validator-0") + .expect("Failed to read from the wallet storage.") .expect( "The zeroth validator's address should be in the pre-genesis \ wallet", diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 3435f1e35b..5a6856584b 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -504,21 +504,21 @@ impl Wallet { // self.store.add_vp_type_to_address(vp_type, address) // } - /// XXX HERE - /// Find the stored address by an alias. - pub fn find_address( - &self, - alias: impl AsRef, - ) -> Option> { - Alias::is_reserved(alias.as_ref()) - .map(std::borrow::Cow::Owned) - .or_else(|| { - self.store - .find_address(alias) - .map(std::borrow::Cow::Borrowed) - }) - } + // /// Find the stored address by an alias. + // pub fn find_address( + // &self, + // alias: impl AsRef, + // ) -> Option> { + // Alias::is_reserved(alias.as_ref()) + // .map(std::borrow::Cow::Owned) + // .or_else(|| { + // self.store + // .find_address(alias) + // .map(std::borrow::Cow::Borrowed) + // }) + // } + /// XXX HERE /// Find an alias by the address if it's in the wallet. pub fn find_alias(&self, address: &Address) -> Option<&Alias> { self.store.find_alias(address) @@ -761,6 +761,22 @@ impl Wallet { }) .collect()) } + + /// Find the stored address by an alias. + pub fn find_address_atomic( + &self, + alias: impl AsRef, + ) -> Result, LoadStoreError> { + Alias::is_reserved(alias.as_ref()) + .map(Ok) + .or_else(|| { + self.utils + .load_store_read_only() + .map(move |store| store.find_address(alias).cloned()) + .transpose() + }) + .transpose() + } } impl Wallet { diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index 454c3a678b..f9af6a79d5 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -668,7 +668,8 @@ fn pos_bonds() -> Result<()> { genesis.transactions.bond = Some({ let wallet = get_pregenesis_wallet(base_dir); let validator_1_address = wallet - .find_address("validator-1") + .find_address_atomic("validator-1") + .expect("Failed to read from the wallet storage.") .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); bonds @@ -1620,7 +1621,8 @@ fn deactivate_and_reactivate_validator() -> Result<()> { genesis.transactions.bond = Some({ let wallet = get_pregenesis_wallet(base_dir); let validator_1_address = wallet - .find_address("validator-1") + .find_address_atomic("validator-1") + .expect("Failed to read from the wallet storage.") .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); bonds @@ -1794,7 +1796,8 @@ fn test_invalid_validator_txs() -> Result<()> { genesis.transactions.bond = Some({ let wallet = get_pregenesis_wallet(base_dir); let validator_1_address = wallet - .find_address("validator-1") + .find_address_atomic("validator-1") + .expect("Failed to read from the wallet storage.") .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); bonds From c0719aa57eb7c1b4c00d86afa9dce1f2d974d749 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 17:20:34 +0100 Subject: [PATCH 06/66] Make store error clonable --- crates/sdk/src/wallet/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 5a6856584b..50a4d8b214 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -84,7 +84,7 @@ pub trait WalletIo: Sized + Clone { } /// Errors of wallet loading and storing -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum LoadStoreError { /// Wallet store decoding error #[error("Failed decoding the wallet store: {0}")] From bcfe595a98d91a97671dab41848392c337936bad Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 17:27:13 +0100 Subject: [PATCH 07/66] Atomic versions of `find_address`, `find_alias` --- crates/apps/src/lib/cli/wallet.rs | 5 ++++- crates/apps/src/lib/client/rpc.rs | 26 ++++++++++++++++++-------- crates/apps/src/lib/client/tx.rs | 4 +++- crates/apps/src/lib/client/utils.rs | 4 +++- crates/apps/src/lib/wallet/defaults.rs | 4 ---- crates/sdk/src/wallet/mod.rs | 24 ++++++++++++++++++++++++ crates/tests/src/e2e/ledger_tests.rs | 9 +++------ 7 files changed, 55 insertions(+), 21 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index bef266524c..338407bf44 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -934,7 +934,10 @@ fn transparent_address_or_alias_find( ); } } else if address.is_some() { - if let Some(alias) = wallet.find_alias(address.as_ref().unwrap()) { + if let Some(alias) = wallet + .find_alias_atomic(address.as_ref().unwrap()) + .expect("Failed to read from the wallet storage.") + { display_line!(io, "Found alias {}", alias); } else { display_line!( diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index 5dfde495cf..2cd34a9e78 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -676,7 +676,9 @@ async fn print_balances( format!( ": {}, owned by {}", context.format_amount(tok, balance).await, - wallet.lookup_alias(owner) + wallet + .lookup_alias_atomic(owner) + .expect("Failed to read from the wallet storage.") ), ), None => continue, @@ -720,11 +722,13 @@ async fn print_balances( context.io(), &mut w; "No balances owned by {}", - wallet.lookup_alias(target) + wallet.lookup_alias_atomic(target).expect("Failed to read from the wallet storage.") ) .unwrap(), (Some(token), None) => { - let token_alias = wallet.lookup_alias(token); + let token_alias = wallet + .lookup_alias_atomic(token) + .expect("Failed to read from the wallet storage."); display_line!(context.io(), &mut w; "No balances for token {}", token_alias).unwrap() } (None, None) => { @@ -758,7 +762,11 @@ async fn lookup_token_alias( Address::Internal(InternalAddress::Erc20(eth_addr)) => { eth_addr.to_canonical() } - _ => context.wallet().await.lookup_alias(token), + _ => context + .wallet() + .await + .lookup_alias_atomic(token) + .expect("Failed to read from the wallet storage."), } } @@ -791,7 +799,8 @@ async fn query_tokens( tokens.insert(eth_addr.to_string(), token.clone()); } else { let alias = wallet - .find_alias(token) + .find_alias_atomic(token) + .expect("Failed to read from the wallet storage.") .map(|alias| alias.to_string()) .unwrap_or(token.to_string()); tokens.insert(alias, token.clone()); @@ -799,8 +808,8 @@ async fn query_tokens( } None => { tokens = wallet - .tokens_with_aliases() - .expect("Failed to read tokens from the wallet store.") + .tokens_with_aliases_atomic() + .expect("Failed to read from the wallet storage.") } } @@ -836,7 +845,8 @@ pub async fn query_ibc_tokens( let wallet = context.wallet().await; let token = args.token.map(|t| { wallet - .find_address(&t) + .find_address_atomic(&t) + .expect("Failed to read from the wallet storage.") .map(|addr| addr.to_string()) .unwrap_or(t) }); diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index 2c4a6d805a..c410ff714f 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -317,7 +317,9 @@ pub async fn submit_change_consensus_key( // Determine the alias for the new key let mut wallet = namada.wallet_mut().await; - let alias = wallet.find_alias(&validator).cloned(); + let alias = wallet + .find_alias_atomic(&validator) + .expect("Failed to read from the wallet storage."); let base_consensus_key_alias = alias .map(|al| validator_consensus_key(&al)) .unwrap_or_else(|| { diff --git a/crates/apps/src/lib/client/utils.rs b/crates/apps/src/lib/client/utils.rs index 24a9591098..d7900ceab4 100644 --- a/crates/apps/src/lib/client/utils.rs +++ b/crates/apps/src/lib/client/utils.rs @@ -687,7 +687,9 @@ pub fn derive_genesis_addresses( let maybe_alias = maybe_pre_genesis_wallet.as_ref().and_then(|wallet| { let implicit_address = (&pk.raw).into(); - wallet.find_alias(&implicit_address) + wallet + .find_alias_atomic(&implicit_address) + .expect("Failed to read from the wallet storage.") }); if let Some(alias) = maybe_alias { diff --git a/crates/apps/src/lib/wallet/defaults.rs b/crates/apps/src/lib/wallet/defaults.rs index ccbe5440f7..29638000fe 100644 --- a/crates/apps/src/lib/wallet/defaults.rs +++ b/crates/apps/src/lib/wallet/defaults.rs @@ -95,7 +95,6 @@ mod dev { .find_address_atomic("albert") .expect("Failed to read from the wallet storage.") .expect("Albert's address should be in the pre-genesis wallet") - .into_owned() } /// An established user address for testing & development @@ -104,7 +103,6 @@ mod dev { .find_address_atomic("bertha") .expect("Failed to read from the wallet storage.") .expect("Bertha's address should be in the pre-genesis wallet") - .into_owned() } /// An established user address for testing & development @@ -113,7 +111,6 @@ mod dev { .find_address_atomic("christel") .expect("Failed to read from the wallet storage.") .expect("Christel's address should be in the pre-genesis wallet") - .into_owned() } /// An implicit user address for testing & development @@ -135,7 +132,6 @@ mod dev { "The zeroth validator's address should be in the pre-genesis \ wallet", ) - .into_owned() } /// Get an unencrypted keypair from the pre-genesis wallet. diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 50a4d8b214..b2e4fc5cfd 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -777,6 +777,30 @@ impl Wallet { }) .transpose() } + + /// Find an alias by the address if it's in the wallet. + pub fn find_alias_atomic( + &self, + address: &Address, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_alias(address) + .cloned()) + } + + /// Try to find an alias for a given address from the wallet. If not found, + /// formats the address into a string. + pub fn lookup_alias_atomic( + &self, + addr: &Address, + ) -> Result { + Ok(match self.find_alias_atomic(addr)? { + Some(alias) => format!("{}", alias), + None => format!("{}", addr), + }) + } } impl Wallet { diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index f9af6a79d5..7ba04b048c 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -672,8 +672,7 @@ fn pos_bonds() -> Result<()> { .expect("Failed to read from the wallet storage.") .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); - bonds - .retain(|bond| bond.data.validator != *validator_1_address); + bonds.retain(|bond| bond.data.validator != validator_1_address); bonds }); genesis @@ -1625,8 +1624,7 @@ fn deactivate_and_reactivate_validator() -> Result<()> { .expect("Failed to read from the wallet storage.") .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); - bonds - .retain(|bond| bond.data.validator != *validator_1_address); + bonds.retain(|bond| bond.data.validator != validator_1_address); bonds }); genesis @@ -1800,8 +1798,7 @@ fn test_invalid_validator_txs() -> Result<()> { .expect("Failed to read from the wallet storage.") .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); - bonds - .retain(|bond| bond.data.validator != *validator_1_address); + bonds.retain(|bond| bond.data.validator != validator_1_address); bonds }); genesis From ba46390e30c0322a1d1692a565d2e3096910fe6c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 17:28:11 +0100 Subject: [PATCH 08/66] Fix atomic version of `tokens_with_aliases` --- crates/sdk/src/wallet/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index b2e4fc5cfd..76ad9a18da 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -749,17 +749,23 @@ impl Wallet { } /// Get addresses with tokens VP type keyed and ordered by their aliases. - pub fn tokens_with_aliases( + pub fn tokens_with_aliases_atomic( &self, ) -> Result, LoadStoreError> { - Ok(self + let res = self .get_addresses_with_vp_type_atomic(AddressVpType::Token)? .into_iter() - .map(|addr| { - let alias = self.lookup_alias(&addr); - (alias, addr) + .map(|addr| match self.lookup_alias_atomic(&addr) { + Ok(alias) => Ok((alias, addr)), + Err(err) => Err(err), }) - .collect()) + .collect::>(); + // TODO rewrite when Iter::try_collect gets stabilized + if let Some(Err(err)) = res.iter().find(|x| x.is_err()) { + Err(err.clone()) + } else { + Ok(res.into_iter().map(Result::unwrap).collect()) + } } /// Find the stored address by an alias. From 047cc5995a328f634ee0aaaa7d44e7948739b944 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 18:06:10 +0100 Subject: [PATCH 09/66] Atomic version of `find_viewing_key` --- crates/apps/src/lib/bench_utils.rs | 3 +- crates/apps/src/lib/cli.rs | 4 +- crates/apps/src/lib/cli/context.rs | 4 +- crates/apps/src/lib/cli/wallet.rs | 5 +- crates/apps/src/lib/client/rpc.rs | 4 +- crates/sdk/src/wallet/mod.rs | 120 +++++++++++++++++------------ 6 files changed, 82 insertions(+), 58 deletions(-) diff --git a/crates/apps/src/lib/bench_utils.rs b/crates/apps/src/lib/bench_utils.rs index 8dbb038312..8fbbf95fbc 100644 --- a/crates/apps/src/lib/bench_utils.rs +++ b/crates/apps/src/lib/bench_utils.rs @@ -977,7 +977,8 @@ impl Default for BenchShieldedCtx { let viewing_key: FromContext = FromContext::new( chain_ctx .wallet - .find_viewing_key(viewing_alias) + .find_viewing_key_atomic(viewing_alias) + .expect("Failed to read from the wallet storage.") .unwrap() .to_string(), ); diff --git a/crates/apps/src/lib/cli.rs b/crates/apps/src/lib/cli.rs index dc03d33d21..1f792beb27 100644 --- a/crates/apps/src/lib/cli.rs +++ b/crates/apps/src/lib/cli.rs @@ -6468,8 +6468,8 @@ pub mod args { use crate::wallet::CliWalletUtils; let find_viewing_key = |w: &mut Wallet| { - w.find_viewing_key(&self.viewing_key.raw) - .copied() + w.find_viewing_key_atomic(&self.viewing_key.raw) + .expect("Failed to read from the wallet storage.") .unwrap_or_else(|_| { eprintln!( "Unknown viewing key {}", diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index 97c3947218..6d2997831c 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -554,8 +554,8 @@ impl ArgFromMutContext for ExtendedViewingKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one ctx.wallet - .find_viewing_key(raw) - .copied() + .find_viewing_key_atomic(raw) + .expect("Failed to read from the wallet storage.") .map_err(|_find_err| format!("Unknown viewing key {}", raw)) }) } diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 338407bf44..848c623c1f 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1100,7 +1100,10 @@ fn shielded_key_address_find_by_alias( _ => "not encrypted", }; // Check if alias is a viewing key - if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { + if let Ok(viewing_key) = wallet + .find_viewing_key_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { found = true; display_line!(io, &mut w_lock; "Found shielded keys:").unwrap(); display_line!(io, diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index 2cd34a9e78..f5102d47cb 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -808,8 +808,8 @@ async fn query_tokens( } None => { tokens = wallet - .tokens_with_aliases_atomic() - .expect("Failed to read from the wallet storage.") + .tokens_with_aliases_atomic() + .expect("Failed to read from the wallet storage.") } } diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 76ad9a18da..53bc58c921 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -518,59 +518,31 @@ impl Wallet { // }) // } - /// XXX HERE - /// Find an alias by the address if it's in the wallet. - pub fn find_alias(&self, address: &Address) -> Option<&Alias> { - self.store.find_alias(address) - } - - /// Try to find an alias for a given address from the wallet. If not found, - /// formats the address into a string. - pub fn lookup_alias(&self, addr: &Address) -> String { - match self.find_alias(addr) { - Some(alias) => format!("{}", alias), - None => format!("{}", addr), - } - } + // /// Find an alias by the address if it's in the wallet. + // pub fn find_alias(&self, address: &Address) -> Option<&Alias> { + // self.store.find_alias(address) + // } - /// Try to find an alias of the base token in the given IBC denomination - /// from the wallet. If not found, formats the IBC denomination into a - /// string. - pub fn lookup_ibc_token_alias(&self, ibc_denom: impl AsRef) -> String { - // Convert only an IBC denom or a Namada address since an NFT trace - // doesn't have the alias - is_ibc_denom(&ibc_denom) - .map(|(trace_path, base_token)| { - let base_token_alias = match Address::decode(&base_token) { - Ok(base_token) => self.lookup_alias(&base_token), - Err(_) => base_token, - }; - if trace_path.is_empty() { - base_token_alias - } else { - format!("{}/{}", trace_path, base_token_alias) - } - }) - .or_else(|| { - // It's not an IBC denom, but could be a raw Namada address - match Address::decode(&ibc_denom) { - Ok(addr) => Some(self.lookup_alias(&addr)), - Err(_) => None, - } - }) - .unwrap_or(ibc_denom.as_ref().to_string()) - } + // /// Try to find an alias for a given address from the wallet. If not + // found, /// formats the address into a string. + // pub fn lookup_alias(&self, addr: &Address) -> String { + // match self.find_alias(addr) { + // Some(alias) => format!("{}", alias), + // None => format!("{}", addr), + // } + // } - /// Find the viewing key with the given alias in the wallet and return it - pub fn find_viewing_key( - &self, - alias: impl AsRef, - ) -> Result<&ExtendedViewingKey, FindKeyError> { - self.store.find_viewing_key(alias.as_ref()).ok_or_else(|| { - FindKeyError::KeyNotFound(alias.as_ref().to_string()) - }) - } + // /// Find the viewing key with the given alias in the wallet and return it + // pub fn find_viewing_key( + // &self, + // alias: impl AsRef, + // ) -> Result<&ExtendedViewingKey, FindKeyError> { + // self.store.find_viewing_key(alias.as_ref()).ok_or_else(|| { + // FindKeyError::KeyNotFound(alias.as_ref().to_string()) + // }) + // } + /// XXX HERE /// Find the payment address with the given alias in the wallet and return /// it pub fn find_payment_addr( @@ -807,6 +779,54 @@ impl Wallet { None => format!("{}", addr), }) } + + /// Try to find an alias of the base token in the given IBC denomination + /// from the wallet. If not found, formats the IBC denomination into a + /// string. + pub fn lookup_ibc_token_alias(&self, ibc_denom: impl AsRef) -> String { + // Convert only an IBC denom or a Namada address since an NFT trace + // doesn't have the alias + is_ibc_denom(&ibc_denom) + .map(|(trace_path, base_token)| { + let base_token_alias = match Address::decode(&base_token) { + Ok(base_token) => self + .lookup_alias_atomic(&base_token) + .expect("Failed to read from the wallet storage."), + Err(_) => base_token, + }; + if trace_path.is_empty() { + base_token_alias + } else { + format!("{}/{}", trace_path, base_token_alias) + } + }) + .or_else(|| { + // It's not an IBC denom, but could be a raw Namada address + match Address::decode(&ibc_denom) { + Ok(addr) => Some( + self.lookup_alias_atomic(&addr) + .expect("Failed to read from the wallet storage."), + ), + Err(_) => None, + } + }) + .unwrap_or(ibc_denom.as_ref().to_string()) + } + + /// Find the viewing key with the given alias in the wallet and return it + pub fn find_viewing_key_atomic( + &self, + alias: impl AsRef, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_viewing_key(alias.as_ref()) + .cloned() + .ok_or_else(|| { + FindKeyError::KeyNotFound(alias.as_ref().to_string()) + })) + } } impl Wallet { From a6e6b186805bfb49807f718b68be849f38b8dee4 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 18:20:12 +0100 Subject: [PATCH 10/66] Atomic version of `find_payment_addr` --- crates/apps/src/lib/cli/context.rs | 4 ++-- crates/apps/src/lib/cli/wallet.rs | 10 ++++++++-- crates/benches/native_vps.rs | 6 ++++-- crates/benches/txs.rs | 6 ++++-- crates/sdk/src/wallet/mod.rs | 30 ++++++++++++++++++++++-------- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index 6d2997831c..0a03b0252c 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -571,8 +571,8 @@ impl ArgFromContext for PaymentAddress { FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one ctx.wallet - .find_payment_addr(raw) - .cloned() + .find_payment_addr_atomic(raw) + .expect("Failed to read from the wallet storage.") .ok_or_else(|| format!("Unknown payment address {}", raw)) }) } diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 848c623c1f..6bb1626961 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -965,7 +965,10 @@ fn payment_address_or_alias_find( ); } else if alias.is_some() { let alias = alias.unwrap().to_lowercase(); - if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + if let Some(payment_addr) = wallet + .find_payment_addr_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { display_line!(io, "Found payment address {}", payment_addr); } else { display_line!( @@ -1141,7 +1144,10 @@ fn shielded_key_address_find_by_alias( // Find payment addresses if !keys_only { - if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + if let Some(payment_addr) = wallet + .find_payment_addr_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { found = true; display_line!(io, &mut w_lock; "Found payment address:").unwrap(); display_line!(io, diff --git a/crates/benches/native_vps.rs b/crates/benches/native_vps.rs index 6092900eab..7e4a0904f8 100644 --- a/crates/benches/native_vps.rs +++ b/crates/benches/native_vps.rs @@ -537,12 +537,14 @@ fn setup_storage_for_masp_verification( .to_owned(); let albert_payment_addr = shielded_ctx .wallet - .find_payment_addr(ALBERT_PAYMENT_ADDRESS) + .find_payment_addr_atomic(ALBERT_PAYMENT_ADDRESS) + .expect("Failed to read from the wallet storage.") .unwrap() .to_owned(); let bertha_payment_addr = shielded_ctx .wallet - .find_payment_addr(BERTHA_PAYMENT_ADDRESS) + .find_payment_addr_atomic(BERTHA_PAYMENT_ADDRESS) + .expect("Failed to read from the wallet storage.") .unwrap() .to_owned(); diff --git a/crates/benches/txs.rs b/crates/benches/txs.rs index ad68d3e1ab..3efb616122 100644 --- a/crates/benches/txs.rs +++ b/crates/benches/txs.rs @@ -71,12 +71,14 @@ fn transfer(c: &mut Criterion) { .to_owned(); let albert_payment_addr = shielded_ctx .wallet - .find_payment_addr(ALBERT_PAYMENT_ADDRESS) + .find_payment_addr_atomic(ALBERT_PAYMENT_ADDRESS) + .expect("Failed to read from the wallet storage.") .unwrap() .to_owned(); let bertha_payment_addr = shielded_ctx .wallet - .find_payment_addr(BERTHA_PAYMENT_ADDRESS) + .find_payment_addr_atomic(BERTHA_PAYMENT_ADDRESS) + .expect("Failed to read from the wallet storage.") .unwrap() .to_owned(); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 53bc58c921..4867171067 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -543,15 +543,16 @@ impl Wallet { // } /// XXX HERE - /// Find the payment address with the given alias in the wallet and return - /// it - pub fn find_payment_addr( - &self, - alias: impl AsRef, - ) -> Option<&PaymentAddress> { - self.store.find_payment_addr(alias.as_ref()) - } + // /// Find the payment address with the given alias in the wallet and + // return /// it + // pub fn find_payment_addr( + // &self, + // alias: impl AsRef, + // ) -> Option<&PaymentAddress> { + // self.store.find_payment_addr(alias.as_ref()) + // } + /// XXX HERE /// Find an alias by the payment address if it's in the wallet. pub fn find_alias_by_payment_addr( &self, @@ -827,6 +828,19 @@ impl Wallet { FindKeyError::KeyNotFound(alias.as_ref().to_string()) })) } + + /// Find the payment address with the given alias in the wallet and return + /// it + pub fn find_payment_addr_atomic( + &self, + alias: impl AsRef, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_payment_addr(alias.as_ref()) + .cloned()) + } } impl Wallet { From d0cd76e5ad7a87020f3473ce771e42aedf215459 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 19:32:25 +0100 Subject: [PATCH 11/66] Atomic version of `find_alias_by_payment_addr` --- crates/apps/src/lib/cli/wallet.rs | 7 +++-- crates/sdk/src/wallet/mod.rs | 50 ++++++++++++++++++------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 6bb1626961..88894f1239 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -980,8 +980,11 @@ fn payment_address_or_alias_find( ); } } else if payment_address.is_some() { - if let Some(alias) = - wallet.find_alias_by_payment_addr(payment_address.as_ref().unwrap()) + if let Some(alias) = wallet + .find_alias_by_payment_addr_atomic( + payment_address.as_ref().unwrap(), + ) + .expect("Failed to read from the wallet storage.") { display_line!(io, "Found alias {}", alias); } else { diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 4867171067..baaf3ac40d 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -552,29 +552,25 @@ impl Wallet { // self.store.find_payment_addr(alias.as_ref()) // } - /// XXX HERE - /// Find an alias by the payment address if it's in the wallet. - pub fn find_alias_by_payment_addr( - &self, - payment_address: &PaymentAddress, - ) -> Option<&Alias> { - self.store.find_alias_by_payment_addr(payment_address) - } + // /// Find an alias by the payment address if it's in the wallet. + // pub fn find_alias_by_payment_addr( + // &self, + // payment_address: &PaymentAddress, + // ) -> Option<&Alias> { + // self.store.find_alias_by_payment_addr(payment_address) + // } - /// Get all known keys by their alias, paired with PKH, if known. - pub fn get_secret_keys( - &self, - ) -> HashMap< - String, - (&StoredKeypair, Option<&PublicKeyHash>), - > { - self.store - .get_secret_keys() - .into_iter() - .map(|(alias, value)| (alias.into(), value)) - .collect() - } + // /// Get all known keys by their alias, paired with PKH, if known. + // pub fn get_secret_keys( + // &self, + // ) -> HashMap< + // String, + // (&StoredKeypair, Option<&PublicKeyHash>), + // > { self.store .get_secret_keys() .into_iter() .map(|(alias, value)| + // > (alias.into(), value)) .collect() + // } + /// XXX HERE /// Get all known public keys by their alias. pub fn get_public_keys(&self) -> HashMap { self.store @@ -841,6 +837,18 @@ impl Wallet { .find_payment_addr(alias.as_ref()) .cloned()) } + + /// Find an alias by the payment address if it's in the wallet. + pub fn find_alias_by_payment_addr_atomic( + &self, + payment_address: &PaymentAddress, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_alias_by_payment_addr(payment_address) + .cloned()) + } } impl Wallet { From cb3253919c7aea8f1cb48d0ee5da3a74daff7df5 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 19:33:04 +0100 Subject: [PATCH 12/66] Make StoredKeypair and EncryptedKeypair clonable --- crates/sdk/src/wallet/keys.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/sdk/src/wallet/keys.rs b/crates/sdk/src/wallet/keys.rs index 7c94df49d6..aa01b317a7 100644 --- a/crates/sdk/src/wallet/keys.rs +++ b/crates/sdk/src/wallet/keys.rs @@ -18,7 +18,7 @@ const ENCRYPTED_KEY_PREFIX: &str = "encrypted:"; const UNENCRYPTED_KEY_PREFIX: &str = "unencrypted:"; /// A keypair stored in a wallet -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum StoredKeypair where ::Err: Display, @@ -114,7 +114,7 @@ pub enum DeserializeStoredKeypairError { } /// An encrypted keypair stored in a wallet -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EncryptedKeypair( Vec, PhantomData, From 31897463c2d4b4b0acc44810d22868891aafeb2f Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 19:34:07 +0100 Subject: [PATCH 13/66] Atomic version of `get_secret_keys` --- crates/apps/src/lib/cli/wallet.rs | 4 +++- crates/apps/src/lib/client/tx.rs | 4 +++- crates/apps/src/lib/wallet/defaults.rs | 12 ++++++++---- crates/sdk/src/wallet/mod.rs | 22 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 88894f1239..5a77b8da73 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1184,7 +1184,9 @@ fn transparent_keys_list( } else { let mut w_lock = io::stdout().lock(); display_line!(io, &mut w_lock; "Known transparent keys:").unwrap(); - let known_secret_keys = wallet.get_secret_keys(); + let known_secret_keys = wallet + .get_secret_keys_atomic() + .expect("Failed to read from the wallet storage."); for (alias, public_key) in known_public_keys { let stored_keypair = known_secret_keys.get(&alias); let encrypted = match stored_keypair { diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index c410ff714f..ea7f094cc1 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -326,7 +326,9 @@ pub async fn submit_change_consensus_key( validator_consensus_key(&validator.to_string().into()) }); let mut consensus_key_alias = base_consensus_key_alias.to_string(); - let all_keys = wallet.get_secret_keys(); + let all_keys = wallet + .get_secret_keys_atomic() + .expect("Failed to read from the wallet storage."); let mut key_counter = 0; while all_keys.contains_key(&consensus_key_alias) { key_counter += 1; diff --git a/crates/apps/src/lib/wallet/defaults.rs b/crates/apps/src/lib/wallet/defaults.rs index 29638000fe..70f7be2c8c 100644 --- a/crates/apps/src/lib/wallet/defaults.rs +++ b/crates/apps/src/lib/wallet/defaults.rs @@ -136,14 +136,18 @@ mod dev { /// Get an unencrypted keypair from the pre-genesis wallet. pub fn get_unencrypted_keypair(name: &str) -> common::SecretKey { - let sk = match PREGENESIS_WALLET.get_secret_keys().get(name).unwrap().0 + match PREGENESIS_WALLET + .get_secret_keys_atomic() + .expect("Failed to read from the wallet storage.") + .get(name) + .unwrap() + .0 { namada_sdk::wallet::StoredKeypair::Encrypted(_) => { panic!("{name}'s keypair should not be encrypted") } - namada_sdk::wallet::StoredKeypair::Raw(sk) => sk, - }; - sk.clone() + namada_sdk::wallet::StoredKeypair::Raw(ref sk) => sk.clone(), + } } /// Get albert's keypair from the pre-genesis wallet. diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index baaf3ac40d..db82caa17f 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -849,6 +849,28 @@ impl Wallet { .find_alias_by_payment_addr(payment_address) .cloned()) } + + /// Get all known keys by their alias, paired with PKH, if known. + #[allow(clippy::type_complexity)] + pub fn get_secret_keys_atomic( + &self, + ) -> Result< + HashMap< + String, + (StoredKeypair, Option), + >, + LoadStoreError, + > { + Ok(self + .utils + .load_store_read_only()? + .get_secret_keys() + .into_iter() + .map(|(alias, (kp, pkh))| { + (alias.into(), (kp.clone(), pkh.cloned())) + }) + .collect()) + } } impl Wallet { From 4b703ddbe0a8c582d1e6de11a838a2b518722467 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 22:24:02 +0100 Subject: [PATCH 14/66] Atomic version of `git_public_keys` --- crates/apps/src/lib/cli/wallet.rs | 4 +++- crates/sdk/src/wallet/mod.rs | 31 ++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 5a77b8da73..801d185e3a 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1172,7 +1172,9 @@ fn transparent_keys_list( unsafe_show_secret: bool, show_hint: bool, ) { - let known_public_keys = wallet.get_public_keys(); + let known_public_keys = wallet + .get_public_keys_atomic() + .expect("Failed to read from the wallet storage."); if known_public_keys.is_empty() { if show_hint { display_line!( diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index db82caa17f..ee51c551ec 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -570,16 +570,16 @@ impl Wallet { // > (alias.into(), value)) .collect() // } - /// XXX HERE - /// Get all known public keys by their alias. - pub fn get_public_keys(&self) -> HashMap { - self.store - .get_public_keys() - .iter() - .map(|(alias, value)| (alias.into(), value.clone())) - .collect() - } + // /// Get all known public keys by their alias. + // pub fn get_public_keys(&self) -> HashMap { + // self.store + // .get_public_keys() + // .iter() + // .map(|(alias, value)| (alias.into(), value.clone())) + // .collect() + // } + /// XXX HERE /// Get all known addresses by their alias, paired with PKH, if known. pub fn get_addresses(&self) -> HashMap { self.store @@ -871,6 +871,19 @@ impl Wallet { }) .collect()) } + + /// Get all known public keys by their alias. + pub fn get_public_keys_atomic( + &self, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .get_public_keys() + .iter() + .map(|(alias, value)| (alias.into(), value.clone())) + .collect()) + } } impl Wallet { From bd0cb462194435ded5fbf382aa88c54ace3c7771 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 23:08:41 +0100 Subject: [PATCH 15/66] Atomic version of `get_addresses` --- crates/apps/src/lib/cli/wallet.rs | 4 +++- crates/apps/src/lib/client/rpc.rs | 10 +++++++++- crates/sdk/src/signing.rs | 7 ++++--- crates/sdk/src/tx.rs | 12 ++++++++++-- crates/sdk/src/wallet/mod.rs | 31 ++++++++++++++++++++++--------- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 801d185e3a..06c96baf59 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1343,7 +1343,9 @@ fn transparent_addresses_list( io: &impl Io, show_hint: bool, ) { - let known_addresses = wallet.get_addresses(); + let known_addresses = wallet + .get_addresses_atomic() + .expect("Failed to read from the wallet storage."); if known_addresses.is_empty() { if show_hint { display_line!( diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index f5102d47cb..b71ac15ed5 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -163,7 +163,15 @@ pub async fn query_transfers( let query_token = args.token; let wallet = context.wallet().await; let query_owner = args.owner.map_or_else( - || Either::Right(wallet.get_addresses().into_values().collect()), + || { + Either::Right( + wallet + .get_addresses_atomic() + .expect("Failed to read from the wallet storage.") + .into_values() + .collect(), + ) + }, Either::Left, ); let mut shielded = context.shielded_mut().await; diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index cd01dca1c5..a750f67bb6 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -59,7 +59,7 @@ use crate::tx::{ VP_USER_WASM, }; pub use crate::wallet::store::AddressVpType; -use crate::wallet::{Wallet, WalletIo}; +use crate::wallet::{Wallet, WalletIo, WalletStorage}; use crate::{args, display_line, rpc, MaybeSend, Namada}; /// A structure holding the signing data to craft a transaction @@ -1070,12 +1070,13 @@ fn proposal_type_to_ledger_vector( /// Converts the given transaction to the form that is displayed on the Ledger /// device pub async fn to_ledger_vector( - wallet: &Wallet, + wallet: &Wallet, tx: &Tx, ) -> Result { // To facilitate lookups of human-readable token names let tokens: HashMap = wallet - .get_addresses() + .get_addresses_atomic() + .expect("Failed to read from the wallet storage.") .into_iter() .map(|(alias, addr)| (addr, alias)) .collect(); diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 2faef0c99a..ec820399a4 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -3032,7 +3032,11 @@ async fn construct_shielded_parts( update_ctx: bool, ) -> Result)>> { // Precompute asset types to increase chances of success in decoding - let token_map = context.wallet().await.get_addresses(); + let token_map = context + .wallet() + .await + .get_addresses_atomic() + .expect("Failed to read from the wallet storage."); let tokens = token_map.values().collect(); let _ = context .shielded_mut() @@ -3330,7 +3334,11 @@ pub async fn gen_ibc_shielded_transfer( validate_amount(context, args.amount, &token, false).await?; // Precompute asset types to increase chances of success in decoding - let token_map = context.wallet().await.get_addresses(); + let token_map = context + .wallet() + .await + .get_addresses_atomic() + .expect("Failed to read from the wallet storage."); let tokens = token_map.values().collect(); let _ = context .shielded_mut() diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index ee51c551ec..af202be6e4 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -579,16 +579,16 @@ impl Wallet { // .collect() // } - /// XXX HERE - /// Get all known addresses by their alias, paired with PKH, if known. - pub fn get_addresses(&self) -> HashMap { - self.store - .get_addresses() - .iter() - .map(|(alias, value)| (alias.into(), value.clone())) - .collect() - } + // /// Get all known addresses by their alias, paired with PKH, if known. + // pub fn get_addresses(&self) -> HashMap { + // self.store + // .get_addresses() + // .iter() + // .map(|(alias, value)| (alias.into(), value.clone())) + // .collect() + // } + /// XXX HERE /// Get all known payment addresses by their alias pub fn get_payment_addrs(&self) -> HashMap { self.store @@ -884,6 +884,19 @@ impl Wallet { .map(|(alias, value)| (alias.into(), value.clone())) .collect()) } + + /// Get all known addresses by their alias, paired with PKH, if known. + pub fn get_addresses_atomic( + &self, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .get_addresses() + .iter() + .map(|(alias, value)| (alias.into(), value.clone())) + .collect()) + } } impl Wallet { From fbb933696e15eee813982f2dc9ca3b9c2db8b8d1 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 6 Mar 2024 23:13:06 +0100 Subject: [PATCH 16/66] Fix message --- crates/apps/src/lib/config/genesis/chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/apps/src/lib/config/genesis/chain.rs b/crates/apps/src/lib/config/genesis/chain.rs index 414fcd0392..948baa1ee0 100644 --- a/crates/apps/src/lib/config/genesis/chain.rs +++ b/crates/apps/src/lib/config/genesis/chain.rs @@ -141,7 +141,7 @@ impl Finalized { AddressVpType::Token, config.address.clone(), ) - .expect("Failed to update the wallet store."); + .expect("Failed to update the wallet storage."); } if let Some(pre_genesis_wallet) = pre_genesis_wallet { wallet.extend(pre_genesis_wallet); From 44b7f049e73aa5cf8985a2417f4bdb3ccfa0f1ee Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 01:33:26 +0100 Subject: [PATCH 17/66] Atomic version of `get_payment_addrs` --- crates/apps/src/lib/cli/wallet.rs | 4 +++- crates/apps/src/lib/client/rpc.rs | 3 ++- crates/sdk/src/wallet/mod.rs | 31 ++++++++++++++++++++++--------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 06c96baf59..40ad73fcc6 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -163,7 +163,9 @@ fn payment_addresses_list( io: &impl Io, show_hint: bool, ) { - let known_addresses = wallet.get_payment_addrs(); + let known_addresses = wallet + .get_payment_addrs_atomic() + .expect("Failed to read from the wallet storage."); if known_addresses.is_empty() { if show_hint { display_line!( diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index b71ac15ed5..583ffa8fb5 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -483,7 +483,8 @@ pub async fn query_pinned_balance( vec![pa] } else { wallet - .get_payment_addrs() + .get_payment_addrs_atomic() + .expect("Failed to read from the wallet storage.") .into_values() .filter(PaymentAddress::is_pinned) .collect() diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index af202be6e4..26c7b63a9d 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -588,16 +588,16 @@ impl Wallet { // .collect() // } - /// XXX HERE - /// Get all known payment addresses by their alias - pub fn get_payment_addrs(&self) -> HashMap { - self.store - .get_payment_addrs() - .iter() - .map(|(alias, value)| (alias.into(), *value)) - .collect() - } + // /// Get all known payment addresses by their alias + // pub fn get_payment_addrs(&self) -> HashMap { + // self.store + // .get_payment_addrs() + // .iter() + // .map(|(alias, value)| (alias.into(), *value)) + // .collect() + // } + /// XXX HERE /// Get all known viewing keys by their alias pub fn get_viewing_keys(&self) -> HashMap { self.store @@ -897,6 +897,19 @@ impl Wallet { .map(|(alias, value)| (alias.into(), value.clone())) .collect()) } + + /// Get all known payment addresses by their alias + pub fn get_payment_addrs_atomic( + &self, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .get_payment_addrs() + .iter() + .map(|(alias, value)| (alias.into(), *value)) + .collect()) + } } impl Wallet { From c1ce9f3377380c4704bd8203a096c009d6683860 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 10:15:42 +0100 Subject: [PATCH 18/66] Atomic version of `get_viewing_keys` --- crates/apps/src/lib/cli/client.rs | 3 ++- crates/apps/src/lib/cli/wallet.rs | 4 +++- crates/apps/src/lib/client/rpc.rs | 14 ++++++++++---- crates/sdk/src/masp.rs | 3 ++- crates/sdk/src/wallet/mod.rs | 32 ++++++++++++++++++++++--------- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/crates/apps/src/lib/cli/client.rs b/crates/apps/src/lib/cli/client.rs index 1f1616887d..44fde4f6a0 100644 --- a/crates/apps/src/lib/cli/client.rs +++ b/crates/apps/src/lib/cli/client.rs @@ -310,7 +310,8 @@ impl CliApi { let chain_ctx = ctx.take_chain_or_exit(); let vks = chain_ctx .wallet - .get_viewing_keys() + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage.") .values() .copied() .map(|vk| ExtendedFullViewingKey::from(vk).fvk.vk) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 40ad73fcc6..5dc383b04c 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -86,8 +86,10 @@ fn shielded_keys_list( unsafe_show_secret: bool, show_hint: bool, ) { - let known_view_keys = wallet.get_viewing_keys(); let known_spend_keys = wallet.get_spending_keys(); + let known_view_keys = wallet + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage."); if known_view_keys.is_empty() { if show_hint { display_line!( diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index 583ffa8fb5..86acf53450 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -190,12 +190,16 @@ pub async fn query_transfers( context.io(), &query_owner, &query_token, - &wallet.get_viewing_keys(), + &wallet + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage."), ) .await .unwrap(); // To facilitate lookups of human-readable token names - let vks = wallet.get_viewing_keys(); + let vks = wallet + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage."); // To enable ExtendedFullViewingKeys to be displayed instead of ViewingKeys let fvk_map: HashMap<_, _> = vks .values() @@ -491,7 +495,8 @@ pub async fn query_pinned_balance( }; // Get the viewing keys with which to try note decryptions let viewing_keys: Vec = wallet - .get_viewing_keys() + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage.") .values() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); @@ -977,7 +982,8 @@ pub async fn query_shielded_balance( None => context .wallet() .await - .get_viewing_keys() + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage.") .values() .copied() .collect(), diff --git a/crates/sdk/src/masp.rs b/crates/sdk/src/masp.rs index 73d47363f2..ec431c192f 100644 --- a/crates/sdk/src/masp.rs +++ b/crates/sdk/src/masp.rs @@ -2351,7 +2351,8 @@ impl ShieldedContext { let vks: Vec<_> = context .wallet() .await - .get_viewing_keys() + .get_viewing_keys_atomic() + .expect("Failed to read from the wallet storage.") .values() .map(|evk| ExtendedFullViewingKey::from(*evk).fvk.vk) .collect(); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 26c7b63a9d..61d0fe3785 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -597,16 +597,16 @@ impl Wallet { // .collect() // } - /// XXX HERE - /// Get all known viewing keys by their alias - pub fn get_viewing_keys(&self) -> HashMap { - self.store - .get_viewing_keys() - .iter() - .map(|(alias, value)| (alias.into(), *value)) - .collect() - } + // /// Get all known viewing keys by their alias + // pub fn get_viewing_keys(&self) -> HashMap { + // self.store + // .get_viewing_keys() + // .iter() + // .map(|(alias, value)| (alias.into(), *value)) + // .collect() + // } + /// XXX HERE /// Get all known viewing keys by their alias pub fn get_spending_keys( &self, @@ -910,6 +910,20 @@ impl Wallet { .map(|(alias, value)| (alias.into(), *value)) .collect()) } + + /// Get all known viewing keys by their alias + pub fn get_viewing_keys_atomic( + &self, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .get_viewing_keys() + .iter() + .map(|(alias, value)| (alias.into(), *value)) + .collect()) + } + } impl Wallet { From ea028a3fcb622321538bf20abcdcc92d9db58fbe Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 10:59:03 +0100 Subject: [PATCH 19/66] Atomic version of `get_spending_keys` --- crates/apps/src/lib/cli/wallet.rs | 4 ++- crates/sdk/src/wallet/mod.rs | 41 +++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 5dc383b04c..4932dbb832 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -86,10 +86,12 @@ fn shielded_keys_list( unsafe_show_secret: bool, show_hint: bool, ) { - let known_spend_keys = wallet.get_spending_keys(); let known_view_keys = wallet .get_viewing_keys_atomic() .expect("Failed to read from the wallet storage."); + let known_spend_keys = wallet + .get_spending_keys_atomic() + .expect("Failed to read from the wallet storage."); if known_view_keys.is_empty() { if show_hint { display_line!( diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 61d0fe3785..4450a121e6 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -542,9 +542,8 @@ impl Wallet { // }) // } - /// XXX HERE // /// Find the payment address with the given alias in the wallet and - // return /// it + // /// return it // pub fn find_payment_addr( // &self, // alias: impl AsRef, @@ -606,18 +605,18 @@ impl Wallet { // .collect() // } - /// XXX HERE - /// Get all known viewing keys by their alias - pub fn get_spending_keys( - &self, - ) -> HashMap> { - self.store - .get_spending_keys() - .iter() - .map(|(alias, value)| (alias.into(), value)) - .collect() - } + // /// Get all known viewing keys by their alias + // pub fn get_spending_keys( + // &self, + // ) -> HashMap> { + // self.store + // .get_spending_keys() + // .iter() + // .map(|(alias, value)| (alias.into(), value)) + // .collect() + // } + /// XXX HERE /// Check if alias is an encrypted secret key pub fn is_encrypted_secret_key( &self, @@ -924,6 +923,22 @@ impl Wallet { .collect()) } + /// Get all known viewing keys by their alias + pub fn get_spending_keys_atomic( + &self, + ) -> Result< + HashMap>, + LoadStoreError, + > { + Ok(self + .utils + .load_store_read_only()? + .get_spending_keys() + .iter() + .map(|(alias, value)| (alias.into(), value.clone())) + .collect()) + } + } impl Wallet { From 9335bae9d6f237ddfd2efdfd5aa91d9802ac7008 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 11:23:17 +0100 Subject: [PATCH 20/66] Atomic version of `is_encrypted_secret_key` --- crates/apps/src/lib/cli/wallet.rs | 5 ++++- crates/sdk/src/wallet/mod.rs | 29 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 4932dbb832..9275d5b41f 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1024,7 +1024,10 @@ fn transparent_key_address_find_by_alias( if let Ok(public_key) = wallet.find_public_key(&alias) { found = true; display_line!(io, &mut w_lock; "Found transparent keys:").unwrap(); - let encrypted = match wallet.is_encrypted_secret_key(&alias) { + let encrypted = match wallet + .is_encrypted_secret_key_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { None => "external", Some(res) if res => "encrypted", _ => "not encrypted", diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 4450a121e6..5a78080ba6 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -617,15 +617,15 @@ impl Wallet { // } /// XXX HERE - /// Check if alias is an encrypted secret key - pub fn is_encrypted_secret_key( - &self, - alias: impl AsRef, - ) -> Option { - self.store - .find_secret_key(alias) - .map(|stored_keypair| stored_keypair.is_encrypted()) - } + // /// Check if alias is an encrypted secret key + // pub fn is_encrypted_secret_key( + // &self, + // alias: impl AsRef, + // ) -> Option { + // self.store + // .find_secret_key(alias) + // .map(|stored_keypair| stored_keypair.is_encrypted()) + // } /// Check if alias is an encrypted spending key pub fn is_encrypted_spending_key( @@ -939,6 +939,17 @@ impl Wallet { .collect()) } + /// Check if alias is an encrypted secret key + pub fn is_encrypted_secret_key_atomic( + &self, + alias: impl AsRef, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_secret_key(alias) + .map(|stored_keypair| stored_keypair.is_encrypted())) + } } impl Wallet { From ba115f240705f6b9a1c883bfcaa157d7c9e5f13b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 11:40:27 +0100 Subject: [PATCH 21/66] Atomic version of `is_encrypted_spending_key` --- crates/apps/src/lib/cli/wallet.rs | 5 ++++- crates/sdk/src/wallet/mod.rs | 34 +++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 9275d5b41f..d0a082e421 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1109,7 +1109,10 @@ fn shielded_key_address_find_by_alias( // Find shielded keys if !addresses_only { - let encrypted = match wallet.is_encrypted_spending_key(&alias) { + let encrypted = match wallet + .is_encrypted_spending_key_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { None => "external", Some(res) if res => "encrypted", _ => "not encrypted", diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 5a78080ba6..8719b3b6e7 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -486,7 +486,7 @@ impl Wallet { // ) // } - /// Gets all addresses given a vp_type + // /// Gets all addresses given a vp_type // pub fn get_addresses_with_vp_type( // &self, // vp_type: AddressVpType, @@ -616,7 +616,6 @@ impl Wallet { // .collect() // } - /// XXX HERE // /// Check if alias is an encrypted secret key // pub fn is_encrypted_secret_key( // &self, @@ -627,17 +626,18 @@ impl Wallet { // .map(|stored_keypair| stored_keypair.is_encrypted()) // } - /// Check if alias is an encrypted spending key - pub fn is_encrypted_spending_key( - &self, - alias: impl AsRef, - ) -> Option { - self.store - .find_spending_key(alias) - .map(|stored_spend_key| stored_spend_key.is_encrypted()) - } + // /// Check if alias is an encrypted spending key + // pub fn is_encrypted_spending_key( + // &self, + // alias: impl AsRef, + // ) -> Option { + // self.store + // .find_spending_key(alias) + // .map(|stored_spend_key| stored_spend_key.is_encrypted()) + // } } +// XXX HERE impl Wallet { /// Load a wallet from the store file. pub fn load(&mut self) -> Result<(), LoadStoreError> { @@ -950,6 +950,18 @@ impl Wallet { .find_secret_key(alias) .map(|stored_keypair| stored_keypair.is_encrypted())) } + + /// Check if alias is an encrypted spending key + pub fn is_encrypted_spending_key_atomic( + &self, + alias: impl AsRef, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_spending_key(alias) + .map(|stored_spend_key| stored_spend_key.is_encrypted())) + } } impl Wallet { From c930e4861497a0ed33b96aa1b725b70ef197872e Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 12:10:08 +0100 Subject: [PATCH 22/66] Clean --- crates/sdk/src/wallet/mod.rs | 183 ----------------------------------- 1 file changed, 183 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 8719b3b6e7..889dd9d5b7 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -430,31 +430,12 @@ impl Wallet { // } // } - // /// Add validator data to the store - // fn add_validator_data( - // &mut self, - // address: Address, - // keys: ValidatorKeys, - // ) { - // self.store.add_validator_data(address, keys); - // } - - // /// Returns a reference to the validator data, if it exists. - // pub fn get_validator_data(&self) -> Option<&ValidatorData> { - // self.store.get_validator_data() - // } - // XXX REMOVE? /// Returns a mut reference to the validator data, if it exists. // pub fn get_validator_data_mut(&mut self) -> Option<&mut ValidatorData> { // self.store.get_validator_data_mut() // } - // /// Take the validator data, if it exists. - // pub fn take_validator_data(&mut self) -> Option { - // self.store.take_validator_data() - // } - /// Returns the validator data, if it exists. pub fn into_validator_data(self) -> Option { self.store.into_validator_data() @@ -471,170 +452,6 @@ impl Wallet { pub fn store_mut(&mut self) -> &mut Store { &mut self.store } - - // /// Extend this wallet from pre-genesis validator wallet. - // pub fn extend_from_pre_genesis_validator( - // &mut self, - // validator_address: Address, - // validator_alias: Alias, - // other: pre_genesis::ValidatorWallet, - // ) { - // self.store.extend_from_pre_genesis_validator( - // validator_address, - // validator_alias, - // other, - // ) - // } - - // /// Gets all addresses given a vp_type - // pub fn get_addresses_with_vp_type( - // &self, - // vp_type: AddressVpType, - // ) -> HashSet
{ - // self.store.get_addresses_with_vp_type(vp_type) - // } - - // /// Add a vp_type to a given address - // pub fn add_vp_type_to_address( - // &mut self, - // vp_type: AddressVpType, - // address: Address, - // ) { - // // defaults to an empty set - // self.store.add_vp_type_to_address(vp_type, address) - // } - - // /// Find the stored address by an alias. - // pub fn find_address( - // &self, - // alias: impl AsRef, - // ) -> Option> { - // Alias::is_reserved(alias.as_ref()) - // .map(std::borrow::Cow::Owned) - // .or_else(|| { - // self.store - // .find_address(alias) - // .map(std::borrow::Cow::Borrowed) - // }) - // } - - // /// Find an alias by the address if it's in the wallet. - // pub fn find_alias(&self, address: &Address) -> Option<&Alias> { - // self.store.find_alias(address) - // } - - // /// Try to find an alias for a given address from the wallet. If not - // found, /// formats the address into a string. - // pub fn lookup_alias(&self, addr: &Address) -> String { - // match self.find_alias(addr) { - // Some(alias) => format!("{}", alias), - // None => format!("{}", addr), - // } - // } - - // /// Find the viewing key with the given alias in the wallet and return it - // pub fn find_viewing_key( - // &self, - // alias: impl AsRef, - // ) -> Result<&ExtendedViewingKey, FindKeyError> { - // self.store.find_viewing_key(alias.as_ref()).ok_or_else(|| { - // FindKeyError::KeyNotFound(alias.as_ref().to_string()) - // }) - // } - - // /// Find the payment address with the given alias in the wallet and - // /// return it - // pub fn find_payment_addr( - // &self, - // alias: impl AsRef, - // ) -> Option<&PaymentAddress> { - // self.store.find_payment_addr(alias.as_ref()) - // } - - // /// Find an alias by the payment address if it's in the wallet. - // pub fn find_alias_by_payment_addr( - // &self, - // payment_address: &PaymentAddress, - // ) -> Option<&Alias> { - // self.store.find_alias_by_payment_addr(payment_address) - // } - - // /// Get all known keys by their alias, paired with PKH, if known. - // pub fn get_secret_keys( - // &self, - // ) -> HashMap< - // String, - // (&StoredKeypair, Option<&PublicKeyHash>), - // > { self.store .get_secret_keys() .into_iter() .map(|(alias, value)| - // > (alias.into(), value)) .collect() - // } - - // /// Get all known public keys by their alias. - // pub fn get_public_keys(&self) -> HashMap { - // self.store - // .get_public_keys() - // .iter() - // .map(|(alias, value)| (alias.into(), value.clone())) - // .collect() - // } - - // /// Get all known addresses by their alias, paired with PKH, if known. - // pub fn get_addresses(&self) -> HashMap { - // self.store - // .get_addresses() - // .iter() - // .map(|(alias, value)| (alias.into(), value.clone())) - // .collect() - // } - - // /// Get all known payment addresses by their alias - // pub fn get_payment_addrs(&self) -> HashMap { - // self.store - // .get_payment_addrs() - // .iter() - // .map(|(alias, value)| (alias.into(), *value)) - // .collect() - // } - - // /// Get all known viewing keys by their alias - // pub fn get_viewing_keys(&self) -> HashMap { - // self.store - // .get_viewing_keys() - // .iter() - // .map(|(alias, value)| (alias.into(), *value)) - // .collect() - // } - - // /// Get all known viewing keys by their alias - // pub fn get_spending_keys( - // &self, - // ) -> HashMap> { - // self.store - // .get_spending_keys() - // .iter() - // .map(|(alias, value)| (alias.into(), value)) - // .collect() - // } - - // /// Check if alias is an encrypted secret key - // pub fn is_encrypted_secret_key( - // &self, - // alias: impl AsRef, - // ) -> Option { - // self.store - // .find_secret_key(alias) - // .map(|stored_keypair| stored_keypair.is_encrypted()) - // } - - // /// Check if alias is an encrypted spending key - // pub fn is_encrypted_spending_key( - // &self, - // alias: impl AsRef, - // ) -> Option { - // self.store - // .find_spending_key(alias) - // .map(|stored_spend_key| stored_spend_key.is_encrypted()) - // } } // XXX HERE From c8316359004bda11021509e7b9dc1e85ff3ba921 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 13:07:02 +0100 Subject: [PATCH 23/66] Atomic version of `find_path_by_pkh` --- crates/apps/src/lib/client/tx.rs | 5 +++-- crates/sdk/src/wallet/mod.rs | 31 ++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index ea7f094cc1..44b40d98f4 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -62,7 +62,7 @@ pub async fn aux_signing_data( Ok(signing_data) } -pub async fn with_hardware_wallet<'a, U: WalletIo + Clone>( +pub async fn with_hardware_wallet<'a, U: WalletIo + WalletStorage + Clone>( mut tx: Tx, pubkey: common::PublicKey, parts: HashSet, @@ -72,7 +72,8 @@ pub async fn with_hardware_wallet<'a, U: WalletIo + Clone>( let path = wallet .read() .await - .find_path_by_pkh(&(&pubkey).into()) + .find_path_by_pkh_atomic(&(&pubkey).into()) + .expect("Failed to read from the wallet storage.") .map_err(|_| { error::Error::Other( "Unable to find derivation path for key".to_string(), diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 889dd9d5b7..f8e1c7622d 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -779,6 +779,19 @@ impl Wallet { .find_spending_key(alias) .map(|stored_spend_key| stored_spend_key.is_encrypted())) } + + /// Find a derivation path by public key hash + pub fn find_path_by_pkh_atomic( + &self, + pkh: &PublicKeyHash, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_path_by_pkh(pkh) + .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))) + } + } impl Wallet { @@ -1126,15 +1139,15 @@ impl Wallet { self.find_key_by_pkh(&pkh, password) } - /// Find a derivation path by public key hash - pub fn find_path_by_pkh( - &self, - pkh: &PublicKeyHash, - ) -> Result { - self.store - .find_path_by_pkh(pkh) - .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) - } + // /// Find a derivation path by public key hash + // pub fn find_path_by_pkh( + // &self, + // pkh: &PublicKeyHash, + // ) -> Result { + // self.store + // .find_path_by_pkh(pkh) + // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) + // } /// Find the public key by a public key hash. /// If the key is encrypted and password not supplied, then password will be From b17041796c391c87027b1d96d20f4542066427e7 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 13:07:53 +0100 Subject: [PATCH 24/66] Atomic version of `find_public_key_by_pkh` --- crates/apps/src/lib/cli/context.rs | 5 +++- crates/apps/src/lib/client/tx.rs | 5 ++-- crates/sdk/src/signing.rs | 3 ++- crates/sdk/src/wallet/mod.rs | 41 ++++++++++++++++++++---------- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index 0a03b0252c..2ab8953e36 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -518,7 +518,10 @@ impl ArgFromContext for common::PublicKey { // Or it can be a public key hash in hex string FromStr::from_str(raw) .map(|pkh: PublicKeyHash| { - ctx.wallet.find_public_key_by_pkh(&pkh).unwrap() + ctx.wallet + .find_public_key_by_pkh_atomic(&pkh) + .expect("Failed to read from the wallet storage.") + .unwrap() }) // Or it can be an alias that may be found in the wallet .or_else(|_parse_err| { diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index 44b40d98f4..046bec594d 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -17,7 +17,7 @@ use namada::tx::{CompressedSignature, Section, Signer, Tx}; use namada_sdk::args::TxBecomeValidator; use namada_sdk::rpc::{InnerTxResult, TxBroadcastData, TxResponse}; use namada_sdk::wallet::alias::validator_consensus_key; -use namada_sdk::wallet::{Wallet, WalletIo}; +use namada_sdk::wallet::{Wallet, WalletIo, WalletStorage}; use namada_sdk::{display_line, edisplay_line, error, signing, tx, Namada}; use rand::rngs::OsRng; use tokio::sync::RwLock; @@ -201,7 +201,8 @@ pub async fn submit_reveal_aux( let public_key = context .wallet_mut() .await - .find_public_key_by_pkh(pkh) + .find_public_key_by_pkh_atomic(pkh) + .expect("Failed to read from the wallet storage") .map_err(|e| error::Error::Other(e.to_string()))?; if tx::is_reveal_pk_needed(context.client(), address, args.force) diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index a750f67bb6..5178cac3e2 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -102,7 +102,8 @@ pub async fn find_pk( Address::Implicit(ImplicitAddress(pkh)) => Ok(context .wallet_mut() .await - .find_public_key_by_pkh(pkh) + .find_public_key_by_pkh_atomic(pkh) + .expect("Failed to read from the wallet storage.") .map_err(|err| { Error::Other(format!( "Unable to load the keypair from the wallet for the \ diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index f8e1c7622d..94af87ac7c 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -792,6 +792,21 @@ impl Wallet { .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))) } + /// Find the public key by a public key hash. + /// If the key is encrypted and password not supplied, then password will be + /// interactively prompted for. Any keys that are decrypted are stored in + /// and read from a cache to avoid prompting for password multiple times. + pub fn find_public_key_by_pkh_atomic( + &self, + pkh: &PublicKeyHash, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_public_key_by_pkh(pkh) + .cloned() + .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))) + } } impl Wallet { @@ -1149,19 +1164,19 @@ impl Wallet { // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) // } - /// Find the public key by a public key hash. - /// If the key is encrypted and password not supplied, then password will be - /// interactively prompted for. Any keys that are decrypted are stored in - /// and read from a cache to avoid prompting for password multiple times. - pub fn find_public_key_by_pkh( - &self, - pkh: &PublicKeyHash, - ) -> Result { - self.store - .find_public_key_by_pkh(pkh) - .cloned() - .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) - } + // /// Find the public key by a public key hash. + // /// If the key is encrypted and password not supplied, then password will + // be /// interactively prompted for. Any keys that are decrypted are + // stored in /// and read from a cache to avoid prompting for password + // multiple times. pub fn find_public_key_by_pkh( + // &self, + // pkh: &PublicKeyHash, + // ) -> Result { + // self.store + // .find_public_key_by_pkh(pkh) + // .cloned() + // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) + // } /// Find the stored key by a public key hash. /// If the key is encrypted and password is not supplied, then password will From 10d4db9d585cd50f20043b38276ff9140da80fc8 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 14:35:42 +0100 Subject: [PATCH 25/66] Atomic `find_key_by_pk`, `find_key_by_pkh` --- crates/apps/src/lib/cli/wallet.rs | 4 ++- crates/apps/src/lib/client/tx.rs | 3 +- crates/apps/src/lib/wallet/mod.rs | 3 +- crates/sdk/src/signing.rs | 7 +++-- crates/sdk/src/wallet/mod.rs | 48 +++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index d0a082e421..2b561513c9 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -880,7 +880,9 @@ fn transparent_key_find( ) { let mut wallet = load_wallet(ctx); let found_keypair = match public_key { - Some(pk) => wallet.find_key_by_pk(&pk, None), + Some(pk) => wallet + .find_key_by_pk_atomic(&pk, None) + .expect("Failed to read from the wallet storage."), None => { let alias = alias.map(|a| a.to_lowercase()).or(public_key_hash); match alias { diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index 046bec594d..f538c92dfb 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -606,7 +606,8 @@ pub async fn submit_become_validator( tendermint_node::write_validator_key( &tendermint_home, &wallet - .find_key_by_pk(&consensus_key, None) + .find_key_by_pk_atomic(&consensus_key, None) + .expect("Failed to read from the wallet storage.") .expect("unable to find consensus key pair in the wallet"), ) .unwrap(); diff --git a/crates/apps/src/lib/wallet/mod.rs b/crates/apps/src/lib/wallet/mod.rs index cb293382ec..c4b1a3bc0f 100644 --- a/crates/apps/src/lib/wallet/mod.rs +++ b/crates/apps/src/lib/wallet/mod.rs @@ -229,7 +229,8 @@ where let pkh = PublicKeyHash::from(&pk); wallet // TODO: optionally encrypt validator keys - .find_key_by_pkh(&pkh, None) + .find_key_by_pkh_atomic(&pkh, None) + .expect("Failed to read from the wallet storage.") .ok() .or_else(|| { wallet.take_validator_data_atomic().map(extract_key) diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index 5178cac3e2..80b90a6965 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -122,13 +122,14 @@ pub async fn find_pk( /// Load the secret key corresponding to the given public key from the wallet. /// If the keypair is encrypted but a password is not supplied, then it is /// interactively prompted. Errors if the key cannot be found or loaded. -pub fn find_key_by_pk( +pub fn find_key_by_pk( wallet: &mut Wallet, args: &args::Tx, public_key: &common::PublicKey, ) -> Result { wallet - .find_key_by_pk(public_key, args.password.clone()) + .find_key_by_pk_atomic(public_key, args.password.clone()) + .expect("Failed to read from the wallet storage.") .map_err(|err| { Error::Other(format!( "Unable to load the keypair from the wallet for public key \ @@ -208,7 +209,7 @@ pub async fn sign_tx<'a, D, F, U>( ) -> Result<(), Error> where D: Clone + MaybeSend, - U: WalletIo, + U: WalletIo + WalletStorage, F: std::future::Future>, { let mut used_pubkeys = HashSet::new(); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 94af87ac7c..4d3ba7cd58 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1369,3 +1369,51 @@ impl Wallet { self.store.remove_alias(&alias.into()) } } + +impl Wallet { + /// Find the stored key by a public key. + /// If the key is encrypted and password not supplied, then password will be + /// interactively prompted for. Any keys that are decrypted are stored in + /// and read from a cache to avoid prompting for password multiple times. + pub fn find_key_by_pk_atomic( + &mut self, + pk: &common::PublicKey, + password: Option>, + ) -> Result, LoadStoreError> { + // Try to look-up alias for the given pk. Otherwise, use the PKH string. + let pkh: PublicKeyHash = pk.into(); + self.find_key_by_pkh_atomic(&pkh, password) + } + + /// Find the stored key by a public key hash. + /// If the key is encrypted and password is not supplied, then password will + /// be interactively prompted for. Any keys that are decrypted are stored in + /// and read from a cache to avoid prompting for password multiple times. + pub fn find_key_by_pkh_atomic( + &mut self, + pkh: &PublicKeyHash, + password: Option>, + ) -> Result, LoadStoreError> { + let store = self.utils.load_store_read_only()?; + // Try to look-up alias for the given pk. Otherwise, use the PKH string. + let alias = store + .find_alias_by_pkh(pkh) + .unwrap_or_else(|| pkh.to_string().into()); + // Try read cache + if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { + return Ok(Ok(cached_key.clone())); + } + // Look-up from store + let res = if let Some(stored_key) = store.find_key_by_pkh(pkh) { + Self::decrypt_stored_key( + &mut self.decrypted_key_cache, + stored_key, + alias, + password, + ) + } else { + Err(FindKeyError::KeyNotFound(pkh.to_string())) + }; + Ok(res) + } +} From 72f0ca02cdb34d125fed48bfc61ed1a5c3cf701a Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 16:38:59 +0100 Subject: [PATCH 26/66] Atomic version of `find_public_key` --- crates/apps/src/lib/cli/context.rs | 5 +- crates/apps/src/lib/cli/wallet.rs | 5 +- crates/apps/src/lib/client/utils.rs | 9 +- crates/sdk/src/wallet/mod.rs | 125 ++++++++++++++++------------ 4 files changed, 83 insertions(+), 61 deletions(-) diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index 2ab8953e36..496da61a18 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -525,7 +525,10 @@ impl ArgFromContext for common::PublicKey { }) // Or it can be an alias that may be found in the wallet .or_else(|_parse_err| { - ctx.wallet.find_public_key(raw).map_err(|x| x.to_string()) + ctx.wallet + .find_public_key_atomic(raw) + .expect("Failed to read from the wallet storage.") + .map_err(|x| x.to_string()) }) }) } diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 2b561513c9..74a42e7390 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1023,7 +1023,10 @@ fn transparent_key_address_find_by_alias( // Find transparent keys if !addresses_only { // Check if alias is a public key - if let Ok(public_key) = wallet.find_public_key(&alias) { + if let Ok(public_key) = wallet + .find_public_key_atomic(&alias) + .expect("Failed to read from the wallet storage.") + { found = true; display_line!(io, &mut w_lock; "Found transparent keys:").unwrap(); let encrypted = match wallet diff --git a/crates/apps/src/lib/client/utils.rs b/crates/apps/src/lib/client/utils.rs index d7900ceab4..5f698d1916 100644 --- a/crates/apps/src/lib/client/utils.rs +++ b/crates/apps/src/lib/client/utils.rs @@ -727,15 +727,16 @@ pub fn init_genesis_established_account( .wallet_aliases .iter() .map(|alias| { - let pk = pre_genesis_wallet.find_public_key(alias).unwrap_or_else( - |err| { + let pk = pre_genesis_wallet + .find_public_key_atomic(alias) + .expect("Failed to read from the wallet storage.") + .unwrap_or_else(|err| { eprintln!( "Failed to look-up `{alias}` in the pre-genesis \ wallet: {err}", ); safe_exit(1) - }, - ); + }); StringEncoded::new(pk) }) .collect(); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 4d3ba7cd58..256b8d1670 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -807,6 +807,21 @@ impl Wallet { .cloned() .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))) } + + /// Find the public key by an alias or a public key hash. + pub fn find_public_key_atomic( + &self, + alias_or_pkh: impl AsRef, + ) -> Result, LoadStoreError> { + Ok(self + .utils + .load_store_read_only()? + .find_public_key(alias_or_pkh.as_ref()) + .cloned() + .ok_or_else(|| { + FindKeyError::KeyNotFound(alias_or_pkh.as_ref().to_string()) + })) + } } impl Wallet { @@ -1097,18 +1112,18 @@ impl Wallet { ) } - /// Find the public key by an alias or a public key hash. - pub fn find_public_key( - &self, - alias_or_pkh: impl AsRef, - ) -> Result { - self.store - .find_public_key(alias_or_pkh.as_ref()) - .cloned() - .ok_or_else(|| { - FindKeyError::KeyNotFound(alias_or_pkh.as_ref().to_string()) - }) - } + // /// Find the public key by an alias or a public key hash. + // pub fn find_public_key( + // &self, + // alias_or_pkh: impl AsRef, + // ) -> Result { + // self.store + // .find_public_key(alias_or_pkh.as_ref()) + // .cloned() + // .ok_or_else(|| { + // FindKeyError::KeyNotFound(alias_or_pkh.as_ref().to_string()) + // }) + // } /// Find the spending key with the given alias in the wallet and return it. /// If the spending key is encrypted but a password is not supplied, then it @@ -1140,19 +1155,19 @@ impl Wallet { ) } - /// Find the stored key by a public key. - /// If the key is encrypted and password not supplied, then password will be - /// interactively prompted for. Any keys that are decrypted are stored in - /// and read from a cache to avoid prompting for password multiple times. - pub fn find_key_by_pk( - &mut self, - pk: &common::PublicKey, - password: Option>, - ) -> Result { - // Try to look-up alias for the given pk. Otherwise, use the PKH string. - let pkh: PublicKeyHash = pk.into(); - self.find_key_by_pkh(&pkh, password) - } + // /// Find the stored key by a public key. + // /// If the key is encrypted and password not supplied, then password will + // be /// interactively prompted for. Any keys that are decrypted are + // stored in /// and read from a cache to avoid prompting for password + // multiple times. pub fn find_key_by_pk( + // &mut self, + // pk: &common::PublicKey, + // password: Option>, + // ) -> Result { + // // Try to look-up alias for the given pk. Otherwise, use the PKH + // string. let pkh: PublicKeyHash = pk.into(); + // self.find_key_by_pkh(&pkh, password) + // } // /// Find a derivation path by public key hash // pub fn find_path_by_pkh( @@ -1178,36 +1193,36 @@ impl Wallet { // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) // } - /// Find the stored key by a public key hash. - /// If the key is encrypted and password is not supplied, then password will - /// be interactively prompted for. Any keys that are decrypted are stored in - /// and read from a cache to avoid prompting for password multiple times. - pub fn find_key_by_pkh( - &mut self, - pkh: &PublicKeyHash, - password: Option>, - ) -> Result { - // Try to look-up alias for the given pk. Otherwise, use the PKH string. - let alias = self - .store - .find_alias_by_pkh(pkh) - .unwrap_or_else(|| pkh.to_string().into()); - // Try read cache - if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { - return Ok(cached_key.clone()); - } - // Look-up from store - let stored_key = self - .store - .find_key_by_pkh(pkh) - .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))?; - Self::decrypt_stored_key( - &mut self.decrypted_key_cache, - stored_key, - alias, - password, - ) - } + // /// Find the stored key by a public key hash. + // /// If the key is encrypted and password is not supplied, then password + // will /// be interactively prompted for. Any keys that are decrypted + // are stored in /// and read from a cache to avoid prompting for + // password multiple times. pub fn find_key_by_pkh( + // &mut self, + // pkh: &PublicKeyHash, + // password: Option>, + // ) -> Result { + // // Try to look-up alias for the given pk. Otherwise, use the PKH + // string. let alias = self + // .store + // .find_alias_by_pkh(pkh) + // .unwrap_or_else(|| pkh.to_string().into()); + // // Try read cache + // if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { + // return Ok(cached_key.clone()); + // } + // // Look-up from store + // let stored_key = self + // .store + // .find_key_by_pkh(pkh) + // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))?; + // Self::decrypt_stored_key( + // &mut self.decrypted_key_cache, + // stored_key, + // alias, + // password, + // ) + // } /// Decrypt stored key, if it's not stored un-encrypted. /// If a given storage key needs to be decrypted and password is not From 5217ce2131d66b613f19bdeed3b2ad687adba93b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 17:26:56 +0100 Subject: [PATCH 27/66] Atomic version of `find_secret_key` --- crates/apps/src/lib/cli/context.rs | 3 +- crates/apps/src/lib/cli/wallet.rs | 16 +++-- crates/sdk/src/wallet/mod.rs | 96 +++++++++++++++++++--------- crates/tests/src/e2e/helpers.rs | 10 ++- crates/tests/src/e2e/ledger_tests.rs | 5 +- 5 files changed, 91 insertions(+), 39 deletions(-) diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index 496da61a18..a610d2d04c 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -501,7 +501,8 @@ impl ArgFromMutContext for common::SecretKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it can be an alias ctx.wallet - .find_secret_key(raw, None) + .find_secret_key_atomic(raw, None) + .expect("Failed to read from the wallet storage.") .map_err(|_find_err| format!("Unknown key {}", raw)) }) } diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 74a42e7390..d6a8b84471 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -894,7 +894,9 @@ fn transparent_key_find( ); cli::safe_exit(1) } - Some(alias) => wallet.find_secret_key(alias, None), + Some(alias) => wallet + .find_secret_key_atomic(alias, None) + .expect("Failed to read from the wallet storage."), } } }; @@ -1056,7 +1058,10 @@ fn transparent_key_address_find_by_alias( if decrypt { // Check if alias is also a secret key. Decrypt and print it if // requested. - match wallet.find_secret_key(&alias, None) { + match wallet + .find_secret_key_atomic(&alias, None) + .expect("Failed to read from the wallet storage.") + { Ok(keypair) => { if unsafe_show_secret { display_line!(io, &mut w_lock; " Secret key: {}", keypair) .unwrap(); @@ -1272,7 +1277,8 @@ fn key_export( let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); let key_to_export = wallet - .find_secret_key(&alias, None) + .find_secret_key_atomic(&alias, None) + .expect("Failed to read from the wallet storage.") .map(|sk| Box::new(sk) as Box) .or(wallet .find_spending_key(&alias, None) @@ -1299,7 +1305,9 @@ fn key_convert( ) { let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); - let sk = wallet.find_secret_key(&alias, None); + let sk = wallet + .find_secret_key_atomic(&alias, None) + .expect("Failed to read from the wallet storage."); let key: serde_json::Value = validator_key_to_json(&sk.unwrap()).unwrap(); let file_name = format!("priv_validator_key_{}.json", alias); let file = File::create(&file_name).unwrap(); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 256b8d1670..b16eb00a8f 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1081,36 +1081,36 @@ impl Wallet { disposable_keypair } - /// Find the stored key by an alias, a public key hash or a public key. - /// If the key is encrypted and password not supplied, then password will be - /// interactively prompted. Any keys that are decrypted are stored in and - /// read from a cache to avoid prompting for password multiple times. - pub fn find_secret_key( - &mut self, - alias_pkh_or_pk: impl AsRef, - password: Option>, - ) -> Result { - // Try cache first - if let Some(cached_key) = self - .decrypted_key_cache - .get(&Alias::from(alias_pkh_or_pk.as_ref())) - { - return Ok(cached_key.clone()); - } - // If not cached, look-up in store - let stored_key = self - .store - .find_secret_key(alias_pkh_or_pk.as_ref()) - .ok_or_else(|| { - FindKeyError::KeyNotFound(alias_pkh_or_pk.as_ref().to_string()) - })?; - Self::decrypt_stored_key::<_>( - &mut self.decrypted_key_cache, - stored_key, - alias_pkh_or_pk.into(), - password, - ) - } + // /// Find the stored key by an alias, a public key hash or a public key. + // /// If the key is encrypted and password not supplied, then password will + // be /// interactively prompted. Any keys that are decrypted are stored + // in and /// read from a cache to avoid prompting for password multiple + // times. pub fn find_secret_key( + // &mut self, + // alias_pkh_or_pk: impl AsRef, + // password: Option>, + // ) -> Result { + // // Try cache first + // if let Some(cached_key) = self + // .decrypted_key_cache + // .get(&alias_pkh_or_pk.as_ref().into()) + // { + // return Ok(cached_key.clone()); + // } + // // If not cached, look-up in store + // let stored_key = self + // .store + // .find_secret_key(alias_pkh_or_pk.as_ref()) + // .ok_or_else(|| { + // FindKeyError::KeyNotFound(alias_pkh_or_pk.as_ref().to_string()) + // })?; + // Self::decrypt_stored_key::<_>( + // &mut self.decrypted_key_cache, + // stored_key, + // alias_pkh_or_pk.into(), + // password, + // ) + // } // /// Find the public key by an alias or a public key hash. // pub fn find_public_key( @@ -1431,4 +1431,40 @@ impl Wallet { }; Ok(res) } + + /// Find the stored key by an alias, a public key hash or a public key. + /// If the key is encrypted and password not supplied, then password will be + /// interactively prompted. Any keys that are decrypted are stored in and + /// read from a cache to avoid prompting for password multiple times. + pub fn find_secret_key_atomic( + &mut self, + alias_pkh_or_pk: impl AsRef, + password: Option>, + ) -> Result, LoadStoreError> { + // Try cache first + if let Some(cached_key) = self + .decrypted_key_cache + .get(&Alias::from(alias_pkh_or_pk.as_ref())) + { + return Ok(Ok(cached_key.clone())); + } + // If not cached, look-up in store + let res = if let Some(stored_key) = self + .utils + .load_store_read_only()? + .find_secret_key(alias_pkh_or_pk.as_ref()) + { + Self::decrypt_stored_key::<_>( + &mut self.decrypted_key_cache, + stored_key, + alias_pkh_or_pk.into(), + password, + ) + } else { + Err(FindKeyError::KeyNotFound( + alias_pkh_or_pk.as_ref().to_string(), + )) + }; + Ok(res) + } } diff --git a/crates/tests/src/e2e/helpers.rs b/crates/tests/src/e2e/helpers.rs index 1118a38ba2..b69f4d665d 100644 --- a/crates/tests/src/e2e/helpers.rs +++ b/crates/tests/src/e2e/helpers.rs @@ -196,7 +196,8 @@ pub fn get_validator_pk(test: &Test, who: Who) -> Option { }; let mut wallet = get_node_wallet(test, who); let sk = wallet - .find_secret_key(format!("validator-{index}-balance-key"), None) + .find_secret_key_atomic(format!("validator-{index}-balance-key"), None) + .expect("Failed to read from the wallet storage.") .ok()?; Some(sk.ref_to()) } @@ -220,8 +221,11 @@ pub fn get_pregenesis_pk>( base_dir_path: P, ) -> Option { let mut wallet = get_pregenesis_wallet(base_dir_path); - let sk = wallet.find_secret_key(alias, None).ok()?; - Some(sk.ref_to()) + let sk = wallet + .find_secret_key_atomic(alias, None) + .expect("Failed to read from the wallet storage.") + .ok()?; + Some(sk.to_public()) } /// Get a pregenesis public key. diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index 7ba04b048c..43f0f5739d 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -2035,7 +2035,10 @@ fn change_consensus_key() -> Result<()> { // Get the new consensus SK let new_key_alias = "validator-0-consensus-key-1"; - let new_sk = wallet.find_secret_key(new_key_alias, None).unwrap(); + let new_sk = wallet + .find_secret_key_atomic(new_key_alias, None) + .expect("Failed to read from the wallet storage.") + .unwrap(); // Write the key to CometBFT dir let cometbft_dir = test.get_cometbft_home(Who::Validator(0)); namada_apps::node::ledger::tendermint_node::write_validator_key( From 07a00b8babf0f9495e91be40fbaa5cd209c2c1ca Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 18:03:32 +0100 Subject: [PATCH 28/66] Atomic version of `find_spending_key` --- crates/apps/src/lib/bench_utils.rs | 3 +- crates/apps/src/lib/cli/context.rs | 3 +- crates/apps/src/lib/cli/wallet.rs | 8 ++- crates/benches/native_vps.rs | 3 +- crates/benches/txs.rs | 3 +- crates/sdk/src/wallet/mod.rs | 90 ++++++++++++++++++++---------- 6 files changed, 75 insertions(+), 35 deletions(-) diff --git a/crates/apps/src/lib/bench_utils.rs b/crates/apps/src/lib/bench_utils.rs index 8fbbf95fbc..b952277073 100644 --- a/crates/apps/src/lib/bench_utils.rs +++ b/crates/apps/src/lib/bench_utils.rs @@ -1021,7 +1021,8 @@ impl BenchShieldedCtx { let async_runtime = tokio::runtime::Runtime::new().unwrap(); let spending_key = self .wallet - .find_spending_key(ALBERT_SPENDING_KEY, None) + .find_spending_key_atomic(ALBERT_SPENDING_KEY, None) + .expect("Failed to read from the wallet storage.") .unwrap(); self.shielded = async_runtime .block_on(crate::client::masp::syncing( diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index a610d2d04c..086c902476 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -545,7 +545,8 @@ impl ArgFromMutContext for ExtendedSpendingKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one ctx.wallet - .find_spending_key(raw, None) + .find_spending_key_atomic(raw, None) + .expect("Failed to read from the wallet storage.") .map_err(|_find_err| format!("Unknown spending key {}", raw)) }) } diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index d6a8b84471..201e64dfc0 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1145,7 +1145,10 @@ fn shielded_key_address_find_by_alias( if decrypt { // Check if alias is also a spending key. Decrypt and print it // if requested. - match wallet.find_spending_key(&alias, None) { + match wallet + .find_spending_key_atomic(&alias, None) + .expect("Failed to read from the wallet storage.") + { Ok(spending_key) => { if unsafe_show_secret { display_line!(io, &mut w_lock; " Spending key: {}", spending_key).unwrap(); @@ -1281,7 +1284,8 @@ fn key_export( .expect("Failed to read from the wallet storage.") .map(|sk| Box::new(sk) as Box) .or(wallet - .find_spending_key(&alias, None) + .find_spending_key_atomic(&alias, None) + .expect("Failed to read from the wallet storage.") .map(|spk| Box::new(spk) as Box)); key_to_export .map(|key| { diff --git a/crates/benches/native_vps.rs b/crates/benches/native_vps.rs index 7e4a0904f8..b3af5f4e82 100644 --- a/crates/benches/native_vps.rs +++ b/crates/benches/native_vps.rs @@ -532,7 +532,8 @@ fn setup_storage_for_masp_verification( let albert_spending_key = shielded_ctx .wallet - .find_spending_key(ALBERT_SPENDING_KEY, None) + .find_spending_key_atomic(ALBERT_SPENDING_KEY, None) + .expect("Failed to read from the wallet storage.") .unwrap() .to_owned(); let albert_payment_addr = shielded_ctx diff --git a/crates/benches/txs.rs b/crates/benches/txs.rs index 3efb616122..ee1e12ec3a 100644 --- a/crates/benches/txs.rs +++ b/crates/benches/txs.rs @@ -66,7 +66,8 @@ fn transfer(c: &mut Criterion) { let albert_spending_key = shielded_ctx .wallet - .find_spending_key(ALBERT_SPENDING_KEY, None) + .find_spending_key_atomic(ALBERT_SPENDING_KEY, None) + .expect("Failed to read from the wallet storage.") .unwrap() .to_owned(); let albert_payment_addr = shielded_ctx diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index b16eb00a8f..9fd9d879b1 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1125,35 +1125,34 @@ impl Wallet { // }) // } - /// Find the spending key with the given alias in the wallet and return it. - /// If the spending key is encrypted but a password is not supplied, then it - /// will be interactively prompted. - pub fn find_spending_key( - &mut self, - alias: impl AsRef, - password: Option>, - ) -> Result { - // Try cache first - if let Some(cached_key) = self - .decrypted_spendkey_cache - .get(&Alias::from(alias.as_ref())) - { - return Ok(*cached_key); - } - // If not cached, look-up in store - let stored_spendkey = self - .store - .find_spending_key(alias.as_ref()) - .ok_or_else(|| { - FindKeyError::KeyNotFound(alias.as_ref().to_string()) - })?; - Self::decrypt_stored_key::<_>( - &mut self.decrypted_spendkey_cache, - stored_spendkey, - alias.into(), - password, - ) - } + // /// Find the spending key with the given alias in the wallet and return + // it. /// If the spending key is encrypted but a password is not + // supplied, then it /// will be interactively prompted. + // pub fn find_spending_key( + // &mut self, + // alias: impl AsRef, + // password: Option>, + // ) -> Result { + // // Try cache first + // if let Some(cached_key) = + // self.decrypted_spendkey_cache.get(&alias.as_ref().into()) + // { + // return Ok(*cached_key); + // } + // // If not cached, look-up in store + // let stored_spendkey = self + // .store + // .find_spending_key(alias.as_ref()) + // .ok_or_else(|| { + // FindKeyError::KeyNotFound(alias.as_ref().to_string()) + // })?; + // Self::decrypt_stored_key::<_>( + // &mut self.decrypted_spendkey_cache, + // stored_spendkey, + // alias.into(), + // password, + // ) + // } // /// Find the stored key by a public key. // /// If the key is encrypted and password not supplied, then password will @@ -1467,4 +1466,37 @@ impl Wallet { }; Ok(res) } + + /// Find the spending key with the given alias in the wallet and return it. + /// If the spending key is encrypted but a password is not supplied, then it + /// will be interactively prompted. + pub fn find_spending_key_atomic( + &mut self, + alias: impl AsRef, + password: Option>, + ) -> Result, LoadStoreError> { + // Try cache first + if let Some(cached_key) = self + .decrypted_spendkey_cache + .get(&Alias::from(alias.as_ref())) + { + return Ok(Ok(*cached_key)); + } + // If not cached, look-up in store + let res = if let Some(stored_spendkey) = self + .utils + .load_store_read_only()? + .find_spending_key(alias.as_ref()) + { + Self::decrypt_stored_key::<_>( + &mut self.decrypted_spendkey_cache, + stored_spendkey, + alias.into(), + password, + ) + } else { + Err(FindKeyError::KeyNotFound(alias.as_ref().to_string())) + }; + Ok(res) + } } From e4294600c958a1091ff90c88819787221c21a3fd Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 18:25:22 +0100 Subject: [PATCH 29/66] Atomic version of `insert_address` --- crates/apps/src/lib/cli/wallet.rs | 3 +- crates/apps/src/lib/config/genesis/chain.rs | 24 ++++++---- crates/sdk/src/tx.rs | 14 ++++-- crates/sdk/src/wallet/mod.rs | 51 ++++++++++++++------- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 201e64dfc0..6f6fc053c6 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1463,7 +1463,8 @@ fn transparent_address_add( let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); if wallet - .insert_address(&alias, address, alias_force) + .insert_address_atomic(&alias, address, alias_force) + .expect("Failed to update the wallet storage.") .is_none() { edisplay_line!(io, "Address not added"); diff --git a/crates/apps/src/lib/config/genesis/chain.rs b/crates/apps/src/lib/config/genesis/chain.rs index 948baa1ee0..5e6549b43c 100644 --- a/crates/apps/src/lib/config/genesis/chain.rs +++ b/crates/apps/src/lib/config/genesis/chain.rs @@ -131,11 +131,13 @@ impl Finalized { ) -> Wallet { let mut wallet = crate::wallet::load_or_new(base_dir); for (alias, config) in &self.tokens.token { - wallet.insert_address( - alias.normalize(), - config.address.clone(), - false, - ); + wallet + .insert_address_atomic( + alias.normalize(), + config.address.clone(), + false, + ) + .expect("Failed to update the wallet storage."); wallet .add_vp_type_to_address_atomic( AddressVpType::Token, @@ -172,11 +174,13 @@ impl Finalized { InternalAddress::Governance, InternalAddress::Pgf, ] { - wallet.insert_address( - int_add.to_string().to_lowercase(), - Address::Internal(int_add.clone()), - false, - ); + wallet + .insert_address_atomic( + int_add.to_string().to_lowercase(), + Address::Internal(int_add.clone()), + false, + ) + .expect("Failed to update the wallet storage."); } wallet diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index ec820399a4..f92513a68d 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -506,11 +506,15 @@ pub async fn save_initialized_accounts( None => N::WalletUtils::read_alias(&encoded).into(), }; let alias = alias.into_owned(); - let added = context.wallet_mut().await.insert_address( - alias.clone(), - address.clone(), - args.wallet_alias_force, - ); + let added = context + .wallet_mut() + .await + .insert_address_atomic( + alias.clone(), + address.clone(), + args.wallet_alias_force, + ) + .expect("Failed to update the wallet storage."); match added { Some(new_alias) if new_alias != encoded => { display_line!( diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 9fd9d879b1..57893fba9b 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1255,21 +1255,21 @@ impl Wallet { } } - /// Add a new address with the given alias. If the alias is already used, - /// will ask whether the existing alias should be replaced, a different - /// alias is desired, or the alias creation should be cancelled. Return - /// the chosen alias if the address has been added, otherwise return - /// nothing. - pub fn insert_address( - &mut self, - alias: impl AsRef, - address: Address, - force_alias: bool, - ) -> Option { - self.store - .insert_address::(alias.into(), address, force_alias) - .map(Into::into) - } + // /// Add a new address with the given alias. If the alias is already used, + // /// will ask whether the existing alias should be replaced, a different + // /// alias is desired, or the alias creation should be cancelled. Return + // /// the chosen alias if the address has been added, otherwise return + // /// nothing. + // pub fn insert_address( + // &mut self, + // alias: impl AsRef, + // address: Address, + // force_alias: bool, + // ) -> Option { + // self.store + // .insert_address::(alias.into(), address, force_alias) + // .map(Into::into) + // } /// Add a new keypair with the given alias. If the alias is already used, /// will ask whether the existing alias should be replaced, a different @@ -1279,7 +1279,7 @@ impl Wallet { pub fn insert_keypair( &mut self, alias: String, - alias_force: bool, + force_alias: bool, sk: common::SecretKey, password: Option>, address: Option
, @@ -1499,4 +1499,23 @@ impl Wallet { }; Ok(res) } + + /// Add a new address with the given alias. If the alias is already used, + /// will ask whether the existing alias should be replaced, a different + /// alias is desired, or the alias creation should be cancelled. Return + /// the chosen alias if the address has been added, otherwise return + /// nothing. + pub fn insert_address_atomic( + &mut self, + alias: impl AsRef, + address: Address, + force_alias: bool, + ) -> Result, LoadStoreError> { + let mut addr_alias: Option = Option::default(); + self.utils.update_store(|store| { + addr_alias = + store.insert_address::(alias.into(), address, force_alias); + })?; + Ok(addr_alias.map(Into::into)) + } } From 2509ac75335faa9855ea25a67e4383848289ce42 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 18:45:23 +0100 Subject: [PATCH 30/66] Atomic version of `insert_keypair` --- crates/apps/src/lib/cli/wallet.rs | 10 +++++++++- crates/apps/src/lib/client/tx.rs | 3 ++- crates/sdk/src/wallet/mod.rs | 33 +++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 6f6fc053c6..877628a325 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -1409,7 +1409,15 @@ fn transparent_secret_key_add( let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let alias = wallet - .insert_keypair(alias, alias_force, sk, encryption_password, None, None) + .insert_keypair_atomic( + alias, + alias_force, + sk, + encryption_password, + None, + None, + ) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Failed to add a keypair."); display_line!(io, "No changes are persisted. Exiting."); diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index f538c92dfb..aef06c78bf 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -556,7 +556,7 @@ pub async fn submit_become_validator( namada .wallet_mut() .await - .insert_keypair( + .insert_keypair_atomic( protocol_key_alias, args.tx.wallet_alias_force, protocol_sk.clone(), @@ -564,6 +564,7 @@ pub async fn submit_become_validator( None, None, ) + .expect("Failed to update the wallet storage.") .ok_or(error::Error::Other(String::from( "Failed to store the keypair.", )))?; diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 57893fba9b..d9cb7b57b5 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1271,6 +1271,7 @@ impl Wallet { // .map(Into::into) // } + // XXX REMOVE /// Add a new keypair with the given alias. If the alias is already used, /// will ask whether the existing alias should be replaced, a different /// alias is desired, or the alias creation should be cancelled. Return @@ -1518,4 +1519,36 @@ impl Wallet { })?; Ok(addr_alias.map(Into::into)) } + + /// Add a new keypair with the given alias. If the alias is already used, + /// will ask whether the existing alias should be replaced, a different + /// alias is desired, or the alias creation should be cancelled. Return + /// the chosen alias if the keypair has been added, otherwise return + /// nothing. + pub fn insert_keypair_atomic( + &mut self, + alias: String, + force_alias: bool, + sk: common::SecretKey, + password: Option>, + address: Option
, + path: Option, + ) -> Result, LoadStoreError> { + let mut keypair_alias: Option = Option::default(); + self.utils.update_store(|store| { + keypair_alias = store.insert_keypair::( + alias.into(), + sk.clone(), + password, + address, + path, + force_alias, + ) + })?; + Ok(keypair_alias.map(|alias| { + // Cache the newly added key + self.decrypted_key_cache.insert(alias.clone(), sk); + alias.into() + })) + } } From 08d4eb44c6fbc264f51a5c74352dcd9d4f71bcb5 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 18:48:57 +0100 Subject: [PATCH 31/66] Clean --- crates/sdk/src/wallet/mod.rs | 158 ----------------------------------- 1 file changed, 158 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index d9cb7b57b5..af668cf274 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1081,148 +1081,6 @@ impl Wallet { disposable_keypair } - // /// Find the stored key by an alias, a public key hash or a public key. - // /// If the key is encrypted and password not supplied, then password will - // be /// interactively prompted. Any keys that are decrypted are stored - // in and /// read from a cache to avoid prompting for password multiple - // times. pub fn find_secret_key( - // &mut self, - // alias_pkh_or_pk: impl AsRef, - // password: Option>, - // ) -> Result { - // // Try cache first - // if let Some(cached_key) = self - // .decrypted_key_cache - // .get(&alias_pkh_or_pk.as_ref().into()) - // { - // return Ok(cached_key.clone()); - // } - // // If not cached, look-up in store - // let stored_key = self - // .store - // .find_secret_key(alias_pkh_or_pk.as_ref()) - // .ok_or_else(|| { - // FindKeyError::KeyNotFound(alias_pkh_or_pk.as_ref().to_string()) - // })?; - // Self::decrypt_stored_key::<_>( - // &mut self.decrypted_key_cache, - // stored_key, - // alias_pkh_or_pk.into(), - // password, - // ) - // } - - // /// Find the public key by an alias or a public key hash. - // pub fn find_public_key( - // &self, - // alias_or_pkh: impl AsRef, - // ) -> Result { - // self.store - // .find_public_key(alias_or_pkh.as_ref()) - // .cloned() - // .ok_or_else(|| { - // FindKeyError::KeyNotFound(alias_or_pkh.as_ref().to_string()) - // }) - // } - - // /// Find the spending key with the given alias in the wallet and return - // it. /// If the spending key is encrypted but a password is not - // supplied, then it /// will be interactively prompted. - // pub fn find_spending_key( - // &mut self, - // alias: impl AsRef, - // password: Option>, - // ) -> Result { - // // Try cache first - // if let Some(cached_key) = - // self.decrypted_spendkey_cache.get(&alias.as_ref().into()) - // { - // return Ok(*cached_key); - // } - // // If not cached, look-up in store - // let stored_spendkey = self - // .store - // .find_spending_key(alias.as_ref()) - // .ok_or_else(|| { - // FindKeyError::KeyNotFound(alias.as_ref().to_string()) - // })?; - // Self::decrypt_stored_key::<_>( - // &mut self.decrypted_spendkey_cache, - // stored_spendkey, - // alias.into(), - // password, - // ) - // } - - // /// Find the stored key by a public key. - // /// If the key is encrypted and password not supplied, then password will - // be /// interactively prompted for. Any keys that are decrypted are - // stored in /// and read from a cache to avoid prompting for password - // multiple times. pub fn find_key_by_pk( - // &mut self, - // pk: &common::PublicKey, - // password: Option>, - // ) -> Result { - // // Try to look-up alias for the given pk. Otherwise, use the PKH - // string. let pkh: PublicKeyHash = pk.into(); - // self.find_key_by_pkh(&pkh, password) - // } - - // /// Find a derivation path by public key hash - // pub fn find_path_by_pkh( - // &self, - // pkh: &PublicKeyHash, - // ) -> Result { - // self.store - // .find_path_by_pkh(pkh) - // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) - // } - - // /// Find the public key by a public key hash. - // /// If the key is encrypted and password not supplied, then password will - // be /// interactively prompted for. Any keys that are decrypted are - // stored in /// and read from a cache to avoid prompting for password - // multiple times. pub fn find_public_key_by_pkh( - // &self, - // pkh: &PublicKeyHash, - // ) -> Result { - // self.store - // .find_public_key_by_pkh(pkh) - // .cloned() - // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) - // } - - // /// Find the stored key by a public key hash. - // /// If the key is encrypted and password is not supplied, then password - // will /// be interactively prompted for. Any keys that are decrypted - // are stored in /// and read from a cache to avoid prompting for - // password multiple times. pub fn find_key_by_pkh( - // &mut self, - // pkh: &PublicKeyHash, - // password: Option>, - // ) -> Result { - // // Try to look-up alias for the given pk. Otherwise, use the PKH - // string. let alias = self - // .store - // .find_alias_by_pkh(pkh) - // .unwrap_or_else(|| pkh.to_string().into()); - // // Try read cache - // if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { - // return Ok(cached_key.clone()); - // } - // // Look-up from store - // let stored_key = self - // .store - // .find_key_by_pkh(pkh) - // .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))?; - // Self::decrypt_stored_key( - // &mut self.decrypted_key_cache, - // stored_key, - // alias, - // password, - // ) - // } - /// Decrypt stored key, if it's not stored un-encrypted. /// If a given storage key needs to be decrypted and password is not /// supplied, then interactively prompt for password and if successfully @@ -1255,22 +1113,6 @@ impl Wallet { } } - // /// Add a new address with the given alias. If the alias is already used, - // /// will ask whether the existing alias should be replaced, a different - // /// alias is desired, or the alias creation should be cancelled. Return - // /// the chosen alias if the address has been added, otherwise return - // /// nothing. - // pub fn insert_address( - // &mut self, - // alias: impl AsRef, - // address: Address, - // force_alias: bool, - // ) -> Option { - // self.store - // .insert_address::(alias.into(), address, force_alias) - // .map(Into::into) - // } - // XXX REMOVE /// Add a new keypair with the given alias. If the alias is already used, /// will ask whether the existing alias should be replaced, a different From 6dc71d4ec45fe5bfe3726ea454eb38e42df72080 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 19:01:04 +0100 Subject: [PATCH 32/66] Atomic version of `insert_public_key` --- crates/apps/src/lib/cli/wallet.rs | 12 +++++++-- crates/sdk/src/wallet/mod.rs | 44 ++++++++++++++++--------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 877628a325..348f298a56 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -525,13 +525,14 @@ async fn transparent_key_and_address_derive( .expect("unable to decode address from hardware wallet"); wallet - .insert_public_key( + .insert_public_key_atomic( alias, pubkey, Some(address), Some(derivation_path), alias_force, ) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { display_line!(io, "No changes are persisted. Exiting."); cli::safe_exit(1) @@ -1444,7 +1445,14 @@ fn transparent_public_key_add( let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); if wallet - .insert_public_key(alias.clone(), pubkey, None, None, alias_force) + .insert_public_key_atomic( + alias.clone(), + pubkey, + None, + None, + alias_force, + ) + .expect("Failed to update the wallet storage.") .is_none() { edisplay_line!(io, "Public key not added"); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index af668cf274..030b1580ef 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1144,27 +1144,6 @@ impl Wallet { }) } - /// Insert a new public key with the given alias. If the alias is already - /// used, then display a prompt for overwrite confirmation. - pub fn insert_public_key( - &mut self, - alias: String, - pubkey: common::PublicKey, - address: Option
, - path: Option, - force_alias: bool, - ) -> Option { - self.store - .insert_public_key::( - alias.into(), - pubkey, - address, - path, - force_alias, - ) - .map(Into::into) - } - /// Insert a viewing key into the wallet under the given alias pub fn insert_viewing_key( &mut self, @@ -1393,4 +1372,27 @@ impl Wallet { alias.into() })) } + + /// Insert a new public key with the given alias. If the alias is already + /// used, then display a prompt for overwrite confirmation. + pub fn insert_public_key_atomic( + &mut self, + alias: String, + pubkey: common::PublicKey, + address: Option
, + path: Option, + force_alias: bool, + ) -> Result, LoadStoreError> { + let mut pk_alias: Option = Option::default(); + self.utils.update_store(|store| { + pk_alias = store.insert_public_key::( + alias.into(), + pubkey, + address, + path, + force_alias, + ) + })?; + Ok(pk_alias.map(Into::into)) + } } From 827d20374cdf62672a5e8fce84a9b76ccc8129f0 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 19:09:50 +0100 Subject: [PATCH 33/66] Atomic version of `insert_viewing_key` --- crates/apps/src/lib/cli/wallet.rs | 3 ++- crates/sdk/src/wallet/mod.rs | 30 ++++++++++++++++++------------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 348f298a56..fbbcc24b26 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -362,7 +362,8 @@ fn shielded_key_address_add( let (alias, typ) = match masp_value { MaspValue::FullViewingKey(viewing_key) => { let alias = wallet - .insert_viewing_key(alias, viewing_key, alias_force) + .insert_viewing_key_atomic(alias, viewing_key, alias_force) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Viewing key not added"); cli::safe_exit(1); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 030b1580ef..305003b6ab 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1144,18 +1144,6 @@ impl Wallet { }) } - /// Insert a viewing key into the wallet under the given alias - pub fn insert_viewing_key( - &mut self, - alias: String, - view_key: ExtendedViewingKey, - force_alias: bool, - ) -> Option { - self.store - .insert_viewing_key::(alias.into(), view_key, force_alias) - .map(Into::into) - } - /// Insert a spending key into the wallet under the given alias pub fn insert_spending_key( &mut self, @@ -1395,4 +1383,22 @@ impl Wallet { })?; Ok(pk_alias.map(Into::into)) } + + /// Insert a viewing key into the wallet under the given alias + pub fn insert_viewing_key_atomic( + &mut self, + alias: String, + view_key: ExtendedViewingKey, + force_alias: bool, + ) -> Result, LoadStoreError> { + let mut vk_alias: Option = Option::default(); + self.utils.update_store(|store| { + vk_alias = store.insert_viewing_key::( + alias.into(), + view_key, + force_alias, + ) + })?; + Ok(vk_alias.map(Into::into)) + } } From fe4423359cc849b5b508db4f0967585c1cf73acb Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 19:10:34 +0100 Subject: [PATCH 34/66] Minor fix --- crates/apps/src/lib/cli/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index fbbcc24b26..fea7b07d8a 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -521,9 +521,9 @@ async fn transparent_key_and_address_derive( }); let pubkey = common::PublicKey::try_from_slice(&response.public_key) - .expect("unable to decode public key from hardware wallet"); + .expect("Unable to decode public key from hardware wallet."); let address = Address::from_str(&response.address_str) - .expect("unable to decode address from hardware wallet"); + .expect("Unable to decode address from hardware wallet."); wallet .insert_public_key_atomic( From 0602ba72d1ab723eb634b7ea51b1f928979eec3e Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 19:20:26 +0100 Subject: [PATCH 35/66] Atomic version of `insert_spending_key` --- crates/apps/src/lib/cli/wallet.rs | 3 ++- crates/sdk/src/wallet/mod.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index fea7b07d8a..11ab763db6 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -374,13 +374,14 @@ fn shielded_key_address_add( let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let alias = wallet - .insert_spending_key( + .insert_spending_key_atomic( alias, alias_force, spending_key, password, None, ) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Spending key not added"); cli::safe_exit(1); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 305003b6ab..cb0b853bfe 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1144,6 +1144,7 @@ impl Wallet { }) } + // XXX REMOVE /// Insert a spending key into the wallet under the given alias pub fn insert_spending_key( &mut self, @@ -1401,4 +1402,33 @@ impl Wallet { })?; Ok(vk_alias.map(Into::into)) } + + /// Insert a spending key into the wallet under the given alias + pub fn insert_spending_key_atomic( + &mut self, + alias: String, + force_alias: bool, + spend_key: ExtendedSpendingKey, + password: Option>, + path: Option, + ) -> Result, LoadStoreError> { + let mut spend_key_alias: Option = Option::default(); + self.utils.update_store(|store| { + spend_key_alias = store.insert_spending_key::( + alias.into(), + spend_key, + password, + path, + force_alias, + ) + })?; + Ok(spend_key_alias + .map(|alias| { + // Cache the newly added key + self.decrypted_spendkey_cache + .insert(alias.clone(), spend_key); + alias + }) + .map(Into::into)) + } } From b16cd22073f4ee98f6ea2b251e3032da3f174aa1 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 19:30:40 +0100 Subject: [PATCH 36/66] Atomic version of `insert_payment_addr` --- crates/apps/src/lib/bench_utils.rs | 3 ++- crates/apps/src/lib/cli/wallet.rs | 6 ++++-- crates/sdk/src/wallet/mod.rs | 30 ++++++++++++++++++------------ 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/apps/src/lib/bench_utils.rs b/crates/apps/src/lib/bench_utils.rs index b952277073..06d1b800d4 100644 --- a/crates/apps/src/lib/bench_utils.rs +++ b/crates/apps/src/lib/bench_utils.rs @@ -992,11 +992,12 @@ impl Default for BenchShieldedCtx { let payment_addr = viewing_key.to_payment_address(div).unwrap(); let _ = chain_ctx .wallet - .insert_payment_addr( + .insert_payment_addr_atomic( alias, PaymentAddress::from(payment_addr).pinned(false), true, ) + .expect("Failed to update the wallet storage.") .unwrap(); } diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 11ab763db6..69e5d5bcde 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -334,7 +334,8 @@ fn payment_address_gen( .expect("a PaymentAddress"); let payment_addr = PaymentAddress::from(masp_payment_addr).pinned(pin); let alias = wallet - .insert_payment_addr(alias, payment_addr, alias_force) + .insert_payment_addr_atomic(alias, payment_addr, alias_force) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Payment address not added"); cli::safe_exit(1); @@ -390,7 +391,8 @@ fn shielded_key_address_add( } MaspValue::PaymentAddress(payment_addr) => { let alias = wallet - .insert_payment_addr(alias, payment_addr, alias_force) + .insert_payment_addr_atomic(alias, payment_addr, alias_force) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Payment address not added"); cli::safe_exit(1); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index cb0b853bfe..d06cea8699 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1171,18 +1171,6 @@ impl Wallet { .map(Into::into) } - /// Insert a payment address into the wallet under the given alias - pub fn insert_payment_addr( - &mut self, - alias: String, - payment_addr: PaymentAddress, - force_alias: bool, - ) -> Option { - self.store - .insert_payment_addr::(alias.into(), payment_addr, force_alias) - .map(Into::into) - } - /// Extend this wallet from another wallet (typically pre-genesis). /// Note that this method ignores `store.validator_data` if any. pub fn extend(&mut self, wallet: Self) { @@ -1431,4 +1419,22 @@ impl Wallet { }) .map(Into::into)) } + + /// Insert a payment address into the wallet under the given alias + pub fn insert_payment_addr_atomic( + &mut self, + alias: String, + payment_addr: PaymentAddress, + force_alias: bool, + ) -> Result, LoadStoreError> { + let mut pay_addr_alias: Option = Option::default(); + self.utils.update_store(|store| { + pay_addr_alias = store.insert_payment_addr::( + alias.into(), + payment_addr, + force_alias, + ) + })?; + Ok(pay_addr_alias.map(Into::into)) + } } From c35b6817f303e9894cefdcb211c2953279d8d301 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 20:04:54 +0100 Subject: [PATCH 37/66] Atomic version of `extend` --- crates/apps/src/lib/config/genesis/chain.rs | 4 +++- crates/sdk/src/wallet/mod.rs | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/apps/src/lib/config/genesis/chain.rs b/crates/apps/src/lib/config/genesis/chain.rs index 5e6549b43c..15c132cbc7 100644 --- a/crates/apps/src/lib/config/genesis/chain.rs +++ b/crates/apps/src/lib/config/genesis/chain.rs @@ -146,7 +146,9 @@ impl Finalized { .expect("Failed to update the wallet storage."); } if let Some(pre_genesis_wallet) = pre_genesis_wallet { - wallet.extend(pre_genesis_wallet); + wallet + .extend_atomic(pre_genesis_wallet) + .expect("Failed to apply pre-genesis wallet."); } if let Some((alias, validator_wallet)) = validator { let tendermint_pk = validator_wallet.tendermint_node_key.ref_to(); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index d06cea8699..cec8af5b0a 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -340,6 +340,10 @@ pub mod fs { impl FsWalletUtils { /// Initialize a wallet at the given directory + // pub fn new(store_dir: PathBuf) -> Wallet { + // Wallet::new(Self { store_dir }) + // } + pub fn new(store_dir: PathBuf) -> Wallet { Wallet::new(Self { store_dir }, Store::default()) } @@ -822,6 +826,16 @@ impl Wallet { FindKeyError::KeyNotFound(alias_or_pkh.as_ref().to_string()) })) } + + /// Extend this wallet from another wallet (typically pre-genesis). + /// Note that this method ignores `store.validator_data` if any. + pub fn extend_atomic( + &mut self, + wallet: Self, + ) -> Result<(), LoadStoreError> { + let other_store = wallet.utils.load_store_read_only()?; + self.utils.update_store(|store| store.extend(other_store)) + } } impl Wallet { @@ -1081,6 +1095,7 @@ impl Wallet { disposable_keypair } + /// XXX OK /// Decrypt stored key, if it's not stored un-encrypted. /// If a given storage key needs to be decrypted and password is not /// supplied, then interactively prompt for password and if successfully @@ -1171,12 +1186,6 @@ impl Wallet { .map(Into::into) } - /// Extend this wallet from another wallet (typically pre-genesis). - /// Note that this method ignores `store.validator_data` if any. - pub fn extend(&mut self, wallet: Self) { - self.store.extend(wallet.store) - } - /// Remove keys and addresses associated with the given alias pub fn remove_all_by_alias(&mut self, alias: String) { self.store.remove_alias(&alias.into()) From 7b319ed49afc082293737ed98c25d9e10b08a1ec Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 20:31:24 +0100 Subject: [PATCH 38/66] Atomic version of `remove_all_by_alias` --- crates/apps/src/lib/cli/wallet.rs | 4 +++- crates/sdk/src/wallet/mod.rs | 14 +++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 69e5d5bcde..fa54c9155c 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -867,7 +867,9 @@ fn key_address_remove( ) { let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); - wallet.remove_all_by_alias(alias.clone()); + wallet + .remove_all_by_alias_atomic(alias.clone()) + .expect("Failed to update the wallet storage."); wallet .save() .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index cec8af5b0a..59afb76748 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -836,6 +836,15 @@ impl Wallet { let other_store = wallet.utils.load_store_read_only()?; self.utils.update_store(|store| store.extend(other_store)) } + + /// Remove keys and addresses associated with the given alias + pub fn remove_all_by_alias_atomic( + &mut self, + alias: String, + ) -> Result<(), LoadStoreError> { + self.utils + .update_store(|store| store.remove_alias(&alias.into())) + } } impl Wallet { @@ -1185,11 +1194,6 @@ impl Wallet { }) .map(Into::into) } - - /// Remove keys and addresses associated with the given alias - pub fn remove_all_by_alias(&mut self, alias: String) { - self.store.remove_alias(&alias.into()) - } } impl Wallet { From c57d445f98f41355a6386dcf628cc2e9b0a857ec Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 22:14:36 +0100 Subject: [PATCH 39/66] Atomic `derive_store_secret_key_from_mnemonic_code` --- crates/apps/src/lib/cli/wallet.rs | 3 +- crates/sdk/src/wallet/mod.rs | 110 ++++++++++++++++++------------ 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index fa54c9155c..eac47ec5a8 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -479,7 +479,7 @@ async fn transparent_key_and_address_derive( let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); wallet - .derive_store_key_from_mnemonic_code( + .derive_store_secret_key_from_mnemonic_code( scheme, Some(alias), alias_force, @@ -488,6 +488,7 @@ async fn transparent_key_and_address_derive( prompt_bip39_passphrase, encryption_password, ) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Failed to derive a keypair."); display_line!(io, "No changes are persisted. Exiting."); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 59afb76748..537dd07faf 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -848,22 +848,16 @@ impl Wallet { } impl Wallet { - /// Restore a spending key from the user mnemonic code (read from stdin) - /// using a given ZIP32 derivation path and insert it into the store with - /// the provided alias, converted to lower case. - /// The key is encrypted with the provided password. If no password - /// provided, will prompt for password from stdin. - /// Stores the key in decrypted key cache and returns the alias of the key - /// and a reference-counting pointer to the key. - pub fn derive_store_spending_key_from_mnemonic_code( - &mut self, - alias: String, - alias_force: bool, + /// XXX OK + /// Derive a keypair from the user mnemonic code (read from stdin) using + /// a given BIP44 derivation path. + pub fn derive_secret_key_from_mnemonic_code( + &self, + scheme: SchemeType, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, - password: Option>, - ) -> Option<(String, ExtendedSpendingKey)> { + ) -> Option { let (mnemonic, passphrase) = if let Some(mnemonic_passphrase) = mnemonic_passphrase { mnemonic_passphrase @@ -877,39 +871,29 @@ impl Wallet { (mnemonic, passphrase) }; let seed = Seed::new(&mnemonic, &passphrase); - let spend_key = - derive_hd_spending_key(seed.as_bytes(), derivation_path.clone()); - - self.insert_spending_key( - alias, - alias_force, - spend_key, - password, - Some(derivation_path), - ) - .map(|alias| (alias, spend_key)) + Some(derive_hd_secret_key( + scheme, + seed.as_bytes(), + derivation_path.clone(), + )) } - /// Restore a keypair from the user mnemonic code (read from stdin) using - /// a given BIP44 derivation path and derive an implicit address from its - /// public part and insert them into the store with the provided alias, - /// converted to lower case. If none provided, the alias will be the public - /// key hash (in lowercase too). + /// Restore a spending key from the user mnemonic code (read from stdin) + /// using a given ZIP32 derivation path and insert it into the store with + /// the provided alias, converted to lower case. /// The key is encrypted with the provided password. If no password /// provided, will prompt for password from stdin. /// Stores the key in decrypted key cache and returns the alias of the key /// and a reference-counting pointer to the key. - #[allow(clippy::too_many_arguments)] - pub fn derive_store_key_from_mnemonic_code( + pub fn derive_store_spending_key_from_mnemonic_code( &mut self, - scheme: SchemeType, - alias: Option, + alias: String, alias_force: bool, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, password: Option>, - ) -> Option<(String, common::SecretKey)> { + ) -> Option<(String, ExtendedSpendingKey)> { let (mnemonic, passphrase) = if let Some(mnemonic_passphrase) = mnemonic_passphrase { mnemonic_passphrase @@ -923,21 +907,17 @@ impl Wallet { (mnemonic, passphrase) }; let seed = Seed::new(&mnemonic, &passphrase); - let sk = derive_hd_secret_key( - scheme, - seed.as_bytes(), - derivation_path.clone(), - ); + let spend_key = + derive_hd_spending_key(seed.as_bytes(), derivation_path.clone()); - self.insert_keypair( - alias.unwrap_or_default(), + self.insert_spending_key( + alias, alias_force, - sk.clone(), + spend_key, password, - None, Some(derivation_path), ) - .map(|alias| (alias, sk)) + .map(|alias| (alias, spend_key)) } /// Generate a spending key similarly to how it's done for keypairs @@ -1450,4 +1430,46 @@ impl Wallet { })?; Ok(pay_addr_alias.map(Into::into)) } + + // XXX OK + /// Derive a keypair from the user mnemonic code (read from stdin) using + /// a given BIP44 derivation path and derive an implicit address from its + /// public part and insert them into the store with the provided alias, + /// converted to lower case. If none provided, the alias will be the public + /// key hash (in lower case too). + /// The key is encrypted with the provided password. If no password + /// provided, will prompt for password from stdin. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and the derived secret key. + #[allow(clippy::too_many_arguments)] + pub fn derive_store_secret_key_from_mnemonic_code( + &mut self, + scheme: SchemeType, + alias: Option, + alias_force: bool, + derivation_path: DerivationPath, + mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, + prompt_bip39_passphrase: bool, + password: Option>, + ) -> Result, LoadStoreError> { + self.derive_secret_key_from_mnemonic_code( + scheme, + derivation_path.clone(), + mnemonic_passphrase, + prompt_bip39_passphrase, + ) + .and_then(|sk| { + self.insert_keypair_atomic( + alias.unwrap_or_default(), + alias_force, + sk.clone(), + password, + None, + Some(derivation_path), + ) + .map(|o| o.map(|alias| (alias, sk))) + .transpose() + }) + .transpose() + } } From 9d519fdc4cb8ae36be908fb610dd38e2aa824c81 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 7 Mar 2024 22:47:17 +0100 Subject: [PATCH 40/66] Atomic `derive_store_spending_key_from_mnemonic_code` --- crates/apps/src/lib/cli/wallet.rs | 1 + crates/sdk/src/wallet/mod.rs | 69 ++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index eac47ec5a8..78c1834adb 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -228,6 +228,7 @@ fn shielded_key_derive( prompt_bip39_passphrase, encryption_password, ) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { edisplay_line!(io, "Failed to derive a key."); display_line!(io, "No changes are persisted. Exiting."); diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 537dd07faf..2596d5cbea 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -874,26 +874,19 @@ impl Wallet { Some(derive_hd_secret_key( scheme, seed.as_bytes(), - derivation_path.clone(), + derivation_path, )) } - /// Restore a spending key from the user mnemonic code (read from stdin) - /// using a given ZIP32 derivation path and insert it into the store with - /// the provided alias, converted to lower case. - /// The key is encrypted with the provided password. If no password - /// provided, will prompt for password from stdin. - /// Stores the key in decrypted key cache and returns the alias of the key - /// and a reference-counting pointer to the key. - pub fn derive_store_spending_key_from_mnemonic_code( - &mut self, - alias: String, - alias_force: bool, + /// XXX OK + /// Derive a spending key from the user mnemonic code (read from stdin) + /// using a given ZIP32 derivation path. + pub fn derive_spending_key_from_mnemonic_code( + &self, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, - password: Option>, - ) -> Option<(String, ExtendedSpendingKey)> { + ) -> Option { let (mnemonic, passphrase) = if let Some(mnemonic_passphrase) = mnemonic_passphrase { mnemonic_passphrase @@ -907,17 +900,7 @@ impl Wallet { (mnemonic, passphrase) }; let seed = Seed::new(&mnemonic, &passphrase); - let spend_key = - derive_hd_spending_key(seed.as_bytes(), derivation_path.clone()); - - self.insert_spending_key( - alias, - alias_force, - spend_key, - password, - Some(derivation_path), - ) - .map(|alias| (alias, spend_key)) + Some(derive_hd_spending_key(seed.as_bytes(), derivation_path)) } /// Generate a spending key similarly to how it's done for keypairs @@ -1472,4 +1455,40 @@ impl Wallet { }) .transpose() } + + // XXX OK + /// Derive a spending key from the user mnemonic code (read from stdin) + /// using a given ZIP32 derivation path and insert it into the store with + /// the provided alias, converted to lower case. + /// The key is encrypted with the provided password. If no password + /// provided, will prompt for password from stdin. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and the derived spending key. + pub fn derive_store_spending_key_from_mnemonic_code( + &mut self, + alias: String, + alias_force: bool, + derivation_path: DerivationPath, + mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, + prompt_bip39_passphrase: bool, + password: Option>, + ) -> Result, LoadStoreError> { + self.derive_spending_key_from_mnemonic_code( + derivation_path.clone(), + mnemonic_passphrase, + prompt_bip39_passphrase, + ) + .and_then(|spend_key| { + self.insert_spending_key_atomic( + alias, + alias_force, + spend_key, + password, + Some(derivation_path), + ) + .map(|o| o.map(|alias| (alias, spend_key))) + .transpose() + }) + .transpose() + } } From a3f6e2c7eb2ea85b71db91a5d43b1156c8ccdb0f Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 00:03:39 +0100 Subject: [PATCH 41/66] Atomic `gen_store_secret_key` --- crates/apps/src/lib/cli/wallet.rs | 16 ++++++++------- crates/apps/src/lib/client/tx.rs | 12 +++++++---- crates/sdk/src/wallet/mod.rs | 34 ++++++++++++++++++++++++++++--- crates/tests/src/e2e/setup.rs | 15 ++++++++------ 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 78c1834adb..78cd95159b 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -576,13 +576,15 @@ fn transparent_key_and_address_gen( let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let alias = if raw { - wallet.gen_store_secret_key( - scheme, - Some(alias), - alias_force, - encryption_password, - &mut OsRng, - ) + wallet + .gen_store_secret_key_atomic( + scheme, + Some(alias), + alias_force, + encryption_password, + &mut OsRng, + ) + .expect("Failed to update the wallet storage.") } else { let derivation_path = decode_transparent_derivation_path(scheme, derivation_path) diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index aef06c78bf..49d83ea8bd 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -355,7 +355,7 @@ pub async fn submit_change_consensus_key( let password = read_and_confirm_encryption_password(args.unsafe_dont_encrypt); wallet - .gen_store_secret_key( + .gen_store_secret_key_atomic( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, Some(consensus_key_alias.clone()), @@ -363,6 +363,7 @@ pub async fn submit_change_consensus_key( password, &mut OsRng, ) + .expect("Failed to update the wallet storage.") .expect("Key generation should not fail.") .1 .ref_to() @@ -448,7 +449,7 @@ pub async fn submit_become_validator( let password = read_and_confirm_encryption_password(args.unsafe_dont_encrypt); wallet - .gen_store_secret_key( + .gen_store_secret_key_atomic( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, Some(consensus_key_alias.clone().into()), @@ -456,6 +457,7 @@ pub async fn submit_become_validator( password, &mut OsRng, ) + .expect("Failed to update the wallet storage.") .expect("Key generation should not fail.") .1 .ref_to() @@ -479,7 +481,7 @@ pub async fn submit_become_validator( let password = read_and_confirm_encryption_password(args.unsafe_dont_encrypt); wallet - .gen_store_secret_key( + .gen_store_secret_key_atomic( // Note that ETH only allows secp256k1 SchemeType::Secp256k1, Some(eth_cold_key_alias.clone()), @@ -487,6 +489,7 @@ pub async fn submit_become_validator( password, &mut OsRng, ) + .expect("Failed to update the wallet storage.") .expect("Key generation should not fail.") .1 .ref_to() @@ -510,7 +513,7 @@ pub async fn submit_become_validator( let password = read_and_confirm_encryption_password(args.unsafe_dont_encrypt); wallet - .gen_store_secret_key( + .gen_store_secret_key_atomic( // Note that ETH only allows secp256k1 SchemeType::Secp256k1, Some(eth_hot_key_alias.clone()), @@ -518,6 +521,7 @@ pub async fn submit_become_validator( password, &mut OsRng, ) + .expect("Failed to update the wallet storage.") .expect("Key generation should not fail.") .1 .ref_to() diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 2596d5cbea..41f78f266c 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -916,15 +916,15 @@ impl Wallet { .map(|alias| (alias, spend_key)) } + // XXX REMOVE /// Generate a new keypair, derive an implicit address from its public key /// and insert them into the store with the provided alias, converted to - /// lower case. If none provided, the alias will be the public key hash (in - /// lowercase too). If the alias already exists, optionally force overwrite + /// lower case. If the alias already exists, optionally force overwrite /// the keypair for the alias. /// If no encryption password is provided, the keypair will be stored raw /// without encryption. /// Stores the key in decrypted key cache and returns the alias of the key - /// and a reference-counting pointer to the key. + /// and the generated keypair. pub fn gen_store_secret_key( &mut self, scheme: SchemeType, @@ -1491,4 +1491,32 @@ impl Wallet { }) .transpose() } + + /// Generate a new keypair, derive an implicit address from its public key + /// and insert them into the store with the provided alias, converted to + /// lower case. If the alias already exists, optionally force overwrite + /// the keypair for the alias. + /// If no encryption password is provided, the keypair will be stored raw + /// without encryption. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and the generated keypair. + pub fn gen_store_secret_key_atomic( + &mut self, + scheme: SchemeType, + alias: Option, + alias_force: bool, + password: Option>, + rng: &mut (impl CryptoRng + RngCore), + ) -> Result, LoadStoreError> { + let sk = gen_secret_key(scheme, rng); + self.insert_keypair_atomic( + alias.unwrap_or_default(), + force_alias, + sk.clone(), + password, + None, + None, + ) + .map(|o| o.map(|alias| (alias, sk))) + } } diff --git a/crates/tests/src/e2e/setup.rs b/crates/tests/src/e2e/setup.rs index 267ea15d1d..def62b85c3 100644 --- a/crates/tests/src/e2e/setup.rs +++ b/crates/tests/src/e2e/setup.rs @@ -163,13 +163,14 @@ where .expect("Could not locate pre-genesis wallet used for e2e tests."); let alias = format!("validator-{val}-balance-key"); let (alias, sk) = wallet - .gen_store_secret_key( + .gen_store_secret_key_atomic( SchemeType::Ed25519, Some(alias), true, None, &mut OsRng, ) + .expect("Failed to update the wallet storage.") .unwrap_or_else(|| { panic!("Could not generate new key for validator-{}", val) }); @@ -188,11 +189,13 @@ where ); address }; - wallet.insert_address( - validator_alias.clone(), - Address::Established(validator_address.clone()), - true, - ); + wallet + .insert_address_atomic( + validator_alias.clone(), + Address::Established(validator_address.clone()), + true, + ) + .expect("Failed to update the wallet storage."); wallet::save(&wallet).unwrap(); // invoke `init-genesis-established-account` to generate a new // established account with the generated balance key From fa487b9ba26ef906fbcdd6dd1c7d5c0ead9494e4 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 00:55:49 +0100 Subject: [PATCH 42/66] Atomic `gen_store_spending_key` --- crates/apps/src/lib/bench_utils.rs | 30 ++++++++++++++++----------- crates/apps/src/lib/cli/wallet.rs | 9 +++++++- crates/sdk/src/wallet/mod.rs | 33 ++++++++++++++++++------------ 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/crates/apps/src/lib/bench_utils.rs b/crates/apps/src/lib/bench_utils.rs index 06d1b800d4..458f68c4a1 100644 --- a/crates/apps/src/lib/bench_utils.rs +++ b/crates/apps/src/lib/bench_utils.rs @@ -953,18 +953,24 @@ impl Default for BenchShieldedCtx { let mut chain_ctx = ctx.take_chain_or_exit(); // Generate spending key for Albert and Bertha - chain_ctx.wallet.gen_store_spending_key( - ALBERT_SPENDING_KEY.to_string(), - None, - true, - &mut OsRng, - ); - chain_ctx.wallet.gen_store_spending_key( - BERTHA_SPENDING_KEY.to_string(), - None, - true, - &mut OsRng, - ); + chain_ctx + .wallet + .gen_store_spending_key_atomic( + ALBERT_SPENDING_KEY.to_string(), + None, + true, + &mut OsRng, + ) + .expect("Failed to update the wallet storage."); + chain_ctx + .wallet + .gen_store_spending_key_atomic( + BERTHA_SPENDING_KEY.to_string(), + None, + true, + &mut OsRng, + ) + .expect("Failed to update the wallet storage."); crate::wallet::save(&chain_ctx.wallet).unwrap(); // Generate payment addresses for both Albert and Bertha diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 78cd95159b..b53d5e5e37 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -269,7 +269,14 @@ fn shielded_key_gen( let alias = alias.to_lowercase(); let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let alias = if raw { - wallet.gen_store_spending_key(alias, password, alias_force, &mut OsRng) + wallet + .gen_store_spending_key_atomic( + alias, + password, + alias_force, + &mut OsRng, + ) + .expect("Failed to update the wallet storage.") } else { let derivation_path = decode_shielded_derivation_path(derivation_path) .unwrap_or_else(|err| { diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 41f78f266c..afe2ae999d 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -903,19 +903,6 @@ impl Wallet { Some(derive_hd_spending_key(seed.as_bytes(), derivation_path)) } - /// Generate a spending key similarly to how it's done for keypairs - pub fn gen_store_spending_key( - &mut self, - alias: String, - password: Option>, - force_alias: bool, - csprng: &mut (impl CryptoRng + RngCore), - ) -> Option<(String, ExtendedSpendingKey)> { - let spend_key = gen_spending_key(csprng); - self.insert_spending_key(alias, force_alias, spend_key, password, None) - .map(|alias| (alias, spend_key)) - } - // XXX REMOVE /// Generate a new keypair, derive an implicit address from its public key /// and insert them into the store with the provided alias, converted to @@ -945,6 +932,7 @@ impl Wallet { .map(|alias| (alias, sk)) } + // XXX OK /// Generate a BIP39 mnemonic code, and derive HD wallet seed from it using /// the given passphrase. If no passphrase is provided, optionally prompt /// for a passphrase. @@ -1519,4 +1507,23 @@ impl Wallet { ) .map(|o| o.map(|alias| (alias, sk))) } + + /// Generate a new spending key similarly to how it's done for keypairs + pub fn gen_store_spending_key_atomic( + &mut self, + alias: String, + password: Option>, + force_alias: bool, + csprng: &mut (impl CryptoRng + RngCore), + ) -> Result, LoadStoreError> { + let spend_key = gen_spending_key(csprng); + self.insert_spending_key_atomic( + alias, + force_alias, + spend_key, + password, + None, + ) + .map(|o| o.map(|alias| (alias, spend_key))) + } } From 695bd390fc44a8a6ac8a28156c062af580780dc2 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 01:15:40 +0100 Subject: [PATCH 43/66] Clean --- crates/sdk/src/wallet/mod.rs | 58 +----------------------------------- 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index afe2ae999d..a087ec3daf 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -108,14 +108,6 @@ pub trait WalletStorage: Sized + Clone { /// Load a wallet from the store file. fn load(&self, wallet: &mut Wallet) -> Result<(), LoadStoreError>; - // XXX REMOVE - /// Atomically update the wallet store - fn update( - &self, - wallet: &mut Wallet, - transform: impl FnOnce(&mut Wallet), - ) -> Result<(), LoadStoreError>; - /// Atomically update the wallet store fn update_store( &self, @@ -233,53 +225,6 @@ pub mod fs { Store::decode(store).map_err(LoadStoreError::Decode) } - fn update( - &self, - wallet: &mut Wallet, - transform: impl FnOnce(&mut Wallet), - ) -> Result<(), LoadStoreError> { - let wallet_file = self.store_dir().join(FILE_NAME); - let mut options = fs::OpenOptions::new(); - options.create(true).write(true).truncate(true); - let mut lock = - RwLock::new(options.open(&wallet_file).map_err(|err| { - LoadStoreError::UpdateWallet( - self.store_dir().to_str().unwrap().parse().unwrap(), - err.to_string(), - ) - })?); - let mut guard = lock.write().map_err(|err| { - LoadStoreError::UpdateWallet( - self.store_dir().to_str().unwrap().parse().unwrap(), - err.to_string(), - ) - })?; - let mut store = Vec::::new(); - (&*guard).read_to_end(&mut store).map_err(|err| { - LoadStoreError::UpdateWallet( - self.store_dir().to_str().unwrap().parse().unwrap(), - err.to_string(), - ) - })?; - wallet.store = - Store::decode(store).map_err(LoadStoreError::Decode)?; - - // Apply wallet transformation - transform(wallet); - - let data = wallet.store.encode(); - // XXX - // Make sure the dir exists - // let wallet_dir = wallet_path.parent().unwrap(); - // fs::create_dir_all(wallet_dir).map_err(|err| { - // LoadStoreError::StoreNewWallet(err.to_string()) - // })?; - // Write the file - guard - .write_all(&data) - .map_err(|err| LoadStoreError::StoreNewWallet(err.to_string())) - } - fn update_store( &self, update: impl FnOnce(&mut Store), @@ -288,7 +233,7 @@ pub mod fs { let mut options = fs::OpenOptions::new(); options.create(true).write(true).truncate(true); let mut lock = - RwLock::new(options.open(&wallet_file).map_err(|err| { + RwLock::new(options.open(wallet_file).map_err(|err| { LoadStoreError::UpdateWallet( self.store_dir().to_str().unwrap().parse().unwrap(), err.to_string(), @@ -417,7 +362,6 @@ impl Wallet { pub fn new(utils: U, store: Store) -> Self { Self { utils, - // store: Store::default(), store, decrypted_key_cache: HashMap::default(), decrypted_spendkey_cache: HashMap::default(), From 44cdf5243ca3c04eba59274342fa7f0bcd330511 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 02:00:40 +0100 Subject: [PATCH 44/66] Remove partial_application crate --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3a97b137bf..0d980f9109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -129,7 +129,6 @@ num-rational = "0.4.1" num-traits = "0.2.14" once_cell = "1.8.0" orion = "0.16.0" -partial_application = "0.2.1" paste = "1.0.9" pretty_assertions = "1.4.0" primitive-types = "0.12.1" From e23c38dd6892f47efc061aa608b830ca89b0f9f7 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 13:25:16 +0100 Subject: [PATCH 45/66] Refactor `derive_hd_seed` --- crates/sdk/src/wallet/mod.rs | 111 +++++++++++++++++------------------ 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index a087ec3daf..dd90566e64 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -792,29 +792,64 @@ impl Wallet { } impl Wallet { + // XXX OK + /// Generate a BIP39 mnemonic code, and derive HD wallet seed from it using + /// the given passphrase. If no passphrase is provided, optionally prompt + /// for a passphrase. + pub fn gen_hd_seed( + passphrase: Option>, + rng: &mut U::Rng, + prompt_bip39_passphrase: bool, + ) -> (Mnemonic, Seed) { + const MNEMONIC_TYPE: MnemonicType = MnemonicType::Words24; + let mnemonic = U::generate_mnemonic_code(MNEMONIC_TYPE, rng); + println!( + "Safely store your {} words mnemonic.", + MNEMONIC_TYPE.word_count() + ); + println!("{}", mnemonic.clone().into_phrase()); + + let passphrase = passphrase.unwrap_or_else(|| { + if prompt_bip39_passphrase { + U::read_mnemonic_passphrase(true) + } else { + Zeroizing::default() + } + }); + let seed = Seed::new(&mnemonic, &passphrase); + (mnemonic, seed) + } + + /// XXX OK + /// Derive HD wallet seed from the BIP39 mnemonic code and passphrase. If no + /// passphrase is provided, optionally prompt for a passphrase. + pub fn derive_hd_seed( + mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, + prompt_bip39_passphrase: bool, + ) -> Option { + let (mnemonic, passphrase) = mnemonic_passphrase.or_else(|| { + let mnemonic = U::read_mnemonic_code()?; + let passphrase = if prompt_bip39_passphrase { + U::read_mnemonic_passphrase(false) + } else { + Zeroizing::default() + }; + Some((mnemonic, passphrase)) + })?; + Some(Seed::new(&mnemonic, &passphrase)) + } + /// XXX OK /// Derive a keypair from the user mnemonic code (read from stdin) using /// a given BIP44 derivation path. pub fn derive_secret_key_from_mnemonic_code( - &self, scheme: SchemeType, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, ) -> Option { - let (mnemonic, passphrase) = - if let Some(mnemonic_passphrase) = mnemonic_passphrase { - mnemonic_passphrase - } else { - let mnemonic = U::read_mnemonic_code()?; - let passphrase = if prompt_bip39_passphrase { - U::read_mnemonic_passphrase(false) - } else { - Zeroizing::default() - }; - (mnemonic, passphrase) - }; - let seed = Seed::new(&mnemonic, &passphrase); + let seed = + Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase)?; Some(derive_hd_secret_key( scheme, seed.as_bytes(), @@ -826,24 +861,12 @@ impl Wallet { /// Derive a spending key from the user mnemonic code (read from stdin) /// using a given ZIP32 derivation path. pub fn derive_spending_key_from_mnemonic_code( - &self, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, ) -> Option { - let (mnemonic, passphrase) = - if let Some(mnemonic_passphrase) = mnemonic_passphrase { - mnemonic_passphrase - } else { - let mnemonic = U::read_mnemonic_code()?; - let passphrase = if prompt_bip39_passphrase { - U::read_mnemonic_passphrase(false) - } else { - Zeroizing::default() - }; - (mnemonic, passphrase) - }; - let seed = Seed::new(&mnemonic, &passphrase); + let seed = + Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase)?; Some(derive_hd_spending_key(seed.as_bytes(), derivation_path)) } @@ -876,34 +899,6 @@ impl Wallet { .map(|alias| (alias, sk)) } - // XXX OK - /// Generate a BIP39 mnemonic code, and derive HD wallet seed from it using - /// the given passphrase. If no passphrase is provided, optionally prompt - /// for a passphrase. - pub fn gen_hd_seed( - passphrase: Option>, - rng: &mut U::Rng, - prompt_bip39_passphrase: bool, - ) -> (Mnemonic, Seed) { - const MNEMONIC_TYPE: MnemonicType = MnemonicType::Words24; - let mnemonic = U::generate_mnemonic_code(MNEMONIC_TYPE, rng); - println!( - "Safely store your {} words mnemonic.", - MNEMONIC_TYPE.word_count() - ); - println!("{}", mnemonic.clone().into_phrase()); - - let passphrase = passphrase.unwrap_or_else(|| { - if prompt_bip39_passphrase { - U::read_mnemonic_passphrase(true) - } else { - Zeroizing::default() - } - }); - let seed = Seed::new(&mnemonic, &passphrase); - (mnemonic, seed) - } - /// Derive a keypair from the given seed and path, derive an implicit /// address from this keypair, and insert them into the store with the /// provided alias, converted to lower case. If none provided, the alias @@ -1367,7 +1362,7 @@ impl Wallet { prompt_bip39_passphrase: bool, password: Option>, ) -> Result, LoadStoreError> { - self.derive_secret_key_from_mnemonic_code( + Self::derive_secret_key_from_mnemonic_code( scheme, derivation_path.clone(), mnemonic_passphrase, @@ -1405,7 +1400,7 @@ impl Wallet { prompt_bip39_passphrase: bool, password: Option>, ) -> Result, LoadStoreError> { - self.derive_spending_key_from_mnemonic_code( + Self::derive_spending_key_from_mnemonic_code( derivation_path.clone(), mnemonic_passphrase, prompt_bip39_passphrase, From 1869be0f0179b10fc0307005db2258f70a69b622 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 16:00:02 +0100 Subject: [PATCH 46/66] Atomic `derive_store_hd_secret_key` --- crates/apps/src/lib/cli/wallet.rs | 18 ++++---- crates/sdk/src/wallet/mod.rs | 71 ++++++++++++++++--------------- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index b53d5e5e37..c3017e12f1 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -612,14 +612,16 @@ fn transparent_key_and_address_gen( &mut OsRng, prompt_bip39_passphrase, ); - wallet.derive_store_hd_secret_key( - scheme, - Some(alias), - alias_force, - seed, - derivation_path, - encryption_password, - ) + wallet + .derive_store_hd_secret_key_atomic( + scheme, + alias, + alias_force, + seed, + derivation_path, + encryption_password, + ) + .expect("Failed to update the wallet storage.") } .map(|x| x.0) .unwrap_or_else(|| { diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index dd90566e64..4a37a0f164 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -899,40 +899,6 @@ impl Wallet { .map(|alias| (alias, sk)) } - /// Derive a keypair from the given seed and path, derive an implicit - /// address from this keypair, and insert them into the store with the - /// provided alias, converted to lower case. If none provided, the alias - /// will be the public key hash (in lowercase too). If the alias already - /// exists, optionally force overwrite the keypair for the alias. - /// If no encryption password is provided, the keypair will be stored raw - /// without encryption. - /// Stores the key in decrypted key cache and returns the alias of the key - /// and the key itself. - pub fn derive_store_hd_secret_key( - &mut self, - scheme: SchemeType, - alias: Option, - alias_force: bool, - seed: Seed, - derivation_path: DerivationPath, - password: Option>, - ) -> Option<(String, common::SecretKey)> { - let sk = derive_hd_secret_key( - scheme, - seed.as_bytes(), - derivation_path.clone(), - ); - self.insert_keypair( - alias.unwrap_or_default(), - alias_force, - sk.clone(), - password, - None, - Some(derivation_path), - ) - .map(|alias| (alias, sk)) - } - /// Derive a masp shielded key from the given seed and path, and insert it /// into the store with the provided alias, converted to lower case. If the /// alias already exists, optionally force overwrite the key for the @@ -1383,6 +1349,41 @@ impl Wallet { .transpose() } + /// Derive a keypair from the given seed and path, derive an implicit + /// address from this keypair, and insert them into the store with the + /// provided alias, converted to lower case. If the alias already + /// exists, optionally force overwrite the keypair for the alias. + /// If no encryption password is provided, the keypair will be stored raw + /// without encryption. + /// Stores the key in decrypted key cache and returns the alias of the + /// derived key and the key itself. + pub fn derive_store_hd_secret_key_atomic( + &mut self, + scheme: SchemeType, + alias: String, + force_alias: bool, + seed: Seed, + derivation_path: DerivationPath, + password: Option>, + ) -> Result, LoadStoreError> { + let sk = derive_hd_secret_key( + scheme, + seed.as_bytes(), + derivation_path.clone(), + ); + let res = self + .insert_keypair_atomic( + alias, + force_alias, + sk.clone(), + password, + None, + Some(derivation_path), + )? + .map(|alias| (alias, sk)); + Ok(res) + } + // XXX OK /// Derive a spending key from the user mnemonic code (read from stdin) /// using a given ZIP32 derivation path and insert it into the store with @@ -1419,6 +1420,7 @@ impl Wallet { .transpose() } + // XXX OK DONT TOUCH /// Generate a new keypair, derive an implicit address from its public key /// and insert them into the store with the provided alias, converted to /// lower case. If the alias already exists, optionally force overwrite @@ -1447,6 +1449,7 @@ impl Wallet { .map(|o| o.map(|alias| (alias, sk))) } + // XXX OK DONT TOUCH /// Generate a new spending key similarly to how it's done for keypairs pub fn gen_store_spending_key_atomic( &mut self, From f66063e28792ab62e04fe0c692b6032c68c82947 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 17:26:38 +0100 Subject: [PATCH 47/66] Refactor `derive_store_secret_key_from_mnemonic_code` --- crates/apps/src/lib/cli/wallet.rs | 2 +- crates/sdk/src/wallet/mod.rs | 50 ++++++++----------------------- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index c3017e12f1..9b64ed9f6c 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -489,7 +489,7 @@ async fn transparent_key_and_address_derive( wallet .derive_store_secret_key_from_mnemonic_code( scheme, - Some(alias), + alias, alias_force, derivation_path, None, diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 4a37a0f164..8ba53bc770 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -839,24 +839,6 @@ impl Wallet { Some(Seed::new(&mnemonic, &passphrase)) } - /// XXX OK - /// Derive a keypair from the user mnemonic code (read from stdin) using - /// a given BIP44 derivation path. - pub fn derive_secret_key_from_mnemonic_code( - scheme: SchemeType, - derivation_path: DerivationPath, - mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, - prompt_bip39_passphrase: bool, - ) -> Option { - let seed = - Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase)?; - Some(derive_hd_secret_key( - scheme, - seed.as_bytes(), - derivation_path, - )) - } - /// XXX OK /// Derive a spending key from the user mnemonic code (read from stdin) /// using a given ZIP32 derivation path. @@ -1321,32 +1303,26 @@ impl Wallet { pub fn derive_store_secret_key_from_mnemonic_code( &mut self, scheme: SchemeType, - alias: Option, + alias: String, alias_force: bool, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, password: Option>, ) -> Result, LoadStoreError> { - Self::derive_secret_key_from_mnemonic_code( - scheme, - derivation_path.clone(), - mnemonic_passphrase, - prompt_bip39_passphrase, - ) - .and_then(|sk| { - self.insert_keypair_atomic( - alias.unwrap_or_default(), - alias_force, - sk.clone(), - password, - None, - Some(derivation_path), - ) - .map(|o| o.map(|alias| (alias, sk))) + Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase) + .and_then(|seed| { + self.derive_store_hd_secret_key_atomic( + scheme, + alias, + alias_force, + seed, + derivation_path, + password, + ) + .transpose() + }) .transpose() - }) - .transpose() } /// Derive a keypair from the given seed and path, derive an implicit From 414ddff3ba8199225984d708855362188d655c38 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 18:09:14 +0100 Subject: [PATCH 48/66] Refactor --- crates/apps/src/lib/cli/wallet.rs | 2 +- crates/sdk/src/wallet/mod.rs | 72 +++++++++++++++---------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index 9b64ed9f6c..c1b6aff008 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -487,7 +487,7 @@ async fn transparent_key_and_address_derive( let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); wallet - .derive_store_secret_key_from_mnemonic_code( + .derive_store_hd_secret_key_from_mnemonic_code( scheme, alias, alias_force, diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 8ba53bc770..26c68cd27b 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -1289,42 +1289,6 @@ impl Wallet { Ok(pay_addr_alias.map(Into::into)) } - // XXX OK - /// Derive a keypair from the user mnemonic code (read from stdin) using - /// a given BIP44 derivation path and derive an implicit address from its - /// public part and insert them into the store with the provided alias, - /// converted to lower case. If none provided, the alias will be the public - /// key hash (in lower case too). - /// The key is encrypted with the provided password. If no password - /// provided, will prompt for password from stdin. - /// Stores the key in decrypted key cache and returns the alias of the key - /// and the derived secret key. - #[allow(clippy::too_many_arguments)] - pub fn derive_store_secret_key_from_mnemonic_code( - &mut self, - scheme: SchemeType, - alias: String, - alias_force: bool, - derivation_path: DerivationPath, - mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, - prompt_bip39_passphrase: bool, - password: Option>, - ) -> Result, LoadStoreError> { - Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase) - .and_then(|seed| { - self.derive_store_hd_secret_key_atomic( - scheme, - alias, - alias_force, - seed, - derivation_path, - password, - ) - .transpose() - }) - .transpose() - } - /// Derive a keypair from the given seed and path, derive an implicit /// address from this keypair, and insert them into the store with the /// provided alias, converted to lower case. If the alias already @@ -1360,6 +1324,42 @@ impl Wallet { Ok(res) } + // XXX OK + /// Derive a keypair from the user mnemonic code (read from stdin) using + /// a given BIP44 derivation path and derive an implicit address from its + /// public part and insert them into the store with the provided alias, + /// converted to lower case. If none provided, the alias will be the public + /// key hash (in lower case too). + /// The key is encrypted with the provided password. If no password + /// provided, will prompt for password from stdin. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and the derived secret key. + #[allow(clippy::too_many_arguments)] + pub fn derive_store_hd_secret_key_from_mnemonic_code( + &mut self, + scheme: SchemeType, + alias: String, + force_alias: bool, + derivation_path: DerivationPath, + mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, + prompt_bip39_passphrase: bool, + password: Option>, + ) -> Result, LoadStoreError> { + Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase) + .and_then(|seed| { + self.derive_store_hd_secret_key_atomic( + scheme, + alias, + force_alias, + seed, + derivation_path, + password, + ) + .transpose() + }) + .transpose() + } + // XXX OK /// Derive a spending key from the user mnemonic code (read from stdin) /// using a given ZIP32 derivation path and insert it into the store with From 9cdc7ef8296fc210e50b2f3f5860de6246191027 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 18:32:28 +0100 Subject: [PATCH 49/66] Atomic `derive_store_hd_spendind_key` --- crates/apps/src/lib/cli/wallet.rs | 18 +++++----- crates/sdk/src/wallet/mod.rs | 60 ++++++++++++++++--------------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index c1b6aff008..ae2fad5a9b 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -220,7 +220,7 @@ fn shielded_key_derive( let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); wallet - .derive_store_spending_key_from_mnemonic_code( + .derive_store_hd_spending_key_from_mnemonic_code( alias, alias_force, derivation_path, @@ -296,13 +296,15 @@ fn shielded_key_gen( &mut OsRng, prompt_bip39_passphrase, ); - wallet.derive_store_hd_spendind_key( - alias, - alias_force, - seed, - derivation_path, - password, - ) + wallet + .derive_store_hd_spendind_key_atomic( + alias, + alias_force, + seed, + derivation_path, + password, + ) + .expect("Failed to update the wallet storage.") } .map(|x| x.0) .unwrap_or_else(|| { diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 26c68cd27b..702d4ba0dc 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -881,34 +881,6 @@ impl Wallet { .map(|alias| (alias, sk)) } - /// Derive a masp shielded key from the given seed and path, and insert it - /// into the store with the provided alias, converted to lower case. If the - /// alias already exists, optionally force overwrite the key for the - /// alias. - /// If no encryption password is provided, the key will be stored raw - /// without encryption. - /// Stores the key in decrypted key cache and returns the alias of the key - /// and the key itself. - pub fn derive_store_hd_spendind_key( - &mut self, - alias: String, - force_alias: bool, - seed: Seed, - derivation_path: DerivationPath, - password: Option>, - ) -> Option<(String, ExtendedSpendingKey)> { - let spend_key = - derive_hd_spending_key(seed.as_bytes(), derivation_path.clone()); - self.insert_spending_key( - alias, - force_alias, - spend_key, - password, - Some(derivation_path), - ) - .map(|alias| (alias, spend_key)) - } - /// Generate a disposable signing key for fee payment and store it under the /// precomputed alias in the wallet. This is simply a wrapper around /// `gen_key` to manage the alias @@ -1324,6 +1296,36 @@ impl Wallet { Ok(res) } + /// Derive a masp shielded key from the given seed and path, and insert it + /// into the store with the provided alias, converted to lower case. If the + /// alias already exists, optionally force overwrite the key for the + /// alias. + /// If no encryption password is provided, the key will be stored raw + /// without encryption. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and the key itself. + pub fn derive_store_hd_spendind_key_atomic( + &mut self, + alias: String, + force_alias: bool, + seed: Seed, + derivation_path: DerivationPath, + password: Option>, + ) -> Result, LoadStoreError> { + let spend_key = + derive_hd_spending_key(seed.as_bytes(), derivation_path.clone()); + let res = self + .insert_spending_key_atomic( + alias, + force_alias, + spend_key, + password, + Some(derivation_path), + )? + .map(|alias| (alias, spend_key)); + Ok(res) + } + // XXX OK /// Derive a keypair from the user mnemonic code (read from stdin) using /// a given BIP44 derivation path and derive an implicit address from its @@ -1368,7 +1370,7 @@ impl Wallet { /// provided, will prompt for password from stdin. /// Stores the key in decrypted key cache and returns the alias of the key /// and the derived spending key. - pub fn derive_store_spending_key_from_mnemonic_code( + pub fn derive_store_hd_spending_key_from_mnemonic_code( &mut self, alias: String, alias_force: bool, From 70ded5f4f3305ce9a37f6f79dde89433c874b4f7 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 18:40:30 +0100 Subject: [PATCH 50/66] Refactor `derive_store_hd_spending_key_from_mnemonic_code` --- crates/sdk/src/wallet/mod.rs | 44 +++++++++++------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 702d4ba0dc..1a4055ed47 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -839,19 +839,6 @@ impl Wallet { Some(Seed::new(&mnemonic, &passphrase)) } - /// XXX OK - /// Derive a spending key from the user mnemonic code (read from stdin) - /// using a given ZIP32 derivation path. - pub fn derive_spending_key_from_mnemonic_code( - derivation_path: DerivationPath, - mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, - prompt_bip39_passphrase: bool, - ) -> Option { - let seed = - Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase)?; - Some(derive_hd_spending_key(seed.as_bytes(), derivation_path)) - } - // XXX REMOVE /// Generate a new keypair, derive an implicit address from its public key /// and insert them into the store with the provided alias, converted to @@ -1373,29 +1360,24 @@ impl Wallet { pub fn derive_store_hd_spending_key_from_mnemonic_code( &mut self, alias: String, - alias_force: bool, + force_alias: bool, derivation_path: DerivationPath, mnemonic_passphrase: Option<(Mnemonic, Zeroizing)>, prompt_bip39_passphrase: bool, password: Option>, ) -> Result, LoadStoreError> { - Self::derive_spending_key_from_mnemonic_code( - derivation_path.clone(), - mnemonic_passphrase, - prompt_bip39_passphrase, - ) - .and_then(|spend_key| { - self.insert_spending_key_atomic( - alias, - alias_force, - spend_key, - password, - Some(derivation_path), - ) - .map(|o| o.map(|alias| (alias, spend_key))) + Self::derive_hd_seed(mnemonic_passphrase, prompt_bip39_passphrase) + .and_then(|seed| { + self.derive_store_hd_spendind_key_atomic( + alias, + force_alias, + seed, + derivation_path, + password, + ) + .transpose() + }) .transpose() - }) - .transpose() } // XXX OK DONT TOUCH @@ -1411,7 +1393,7 @@ impl Wallet { &mut self, scheme: SchemeType, alias: Option, - alias_force: bool, + force_alias: bool, password: Option>, rng: &mut (impl CryptoRng + RngCore), ) -> Result, LoadStoreError> { From 440ab11f91a13aa0a4663c1b466ce1ab48062287 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 18:59:05 +0100 Subject: [PATCH 51/66] Atomic `gen_disposable_signing_key` --- crates/sdk/src/signing.rs | 6 ++-- crates/sdk/src/wallet/mod.rs | 66 ++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index 80b90a6965..92aee45107 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -345,7 +345,8 @@ pub async fn aux_signing_data( context .wallet_mut() .await - .gen_disposable_signing_key(&mut OsRng) + .gen_disposable_signing_key_atomic(&mut OsRng) + .expect("Failed to update the wallet store.") .to_public() } else { match &args.wrapper_fee_payer { @@ -385,7 +386,8 @@ pub async fn init_validator_signing_data( context .wallet_mut() .await - .gen_disposable_signing_key(&mut OsRng) + .gen_disposable_signing_key_atomic(&mut OsRng) + .expect("Failed to update the wallet store.") .to_public() } else { match &args.wrapper_fee_payer { diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 1a4055ed47..55a684c797 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -868,39 +868,6 @@ impl Wallet { .map(|alias| (alias, sk)) } - /// Generate a disposable signing key for fee payment and store it under the - /// precomputed alias in the wallet. This is simply a wrapper around - /// `gen_key` to manage the alias - pub fn gen_disposable_signing_key( - &mut self, - rng: &mut (impl CryptoRng + RngCore), - ) -> common::SecretKey { - // Create the alias - let mut ctr = 1; - let mut alias = format!("disposable_{ctr}"); - - while self.store().contains_alias(&Alias::from(&alias)) { - ctr += 1; - alias = format!("disposable_{ctr}"); - } - // Generate a disposable keypair to sign the wrapper if requested - // TODO: once the wrapper transaction has been applied, this key can be - // deleted from wallet (the transaction being accepted is not enough - // cause we could end up doing a rollback) - let (alias, disposable_keypair) = self - .gen_store_secret_key( - SchemeType::Ed25519, - Some(alias), - false, - None, - rng, - ) - .expect("Failed to initialize disposable keypair"); - - println!("Created disposable keypair with alias {alias}"); - disposable_keypair - } - /// XXX OK /// Decrypt stored key, if it's not stored un-encrypted. /// If a given storage key needs to be decrypted and password is not @@ -1428,4 +1395,37 @@ impl Wallet { ) .map(|o| o.map(|alias| (alias, spend_key))) } + + /// Generate a disposable signing key for fee payment and store it under the + /// precomputed alias in the wallet. This is simply a wrapper around + /// `gen_key` to manage the alias + pub fn gen_disposable_signing_key_atomic( + &mut self, + rng: &mut (impl CryptoRng + RngCore), + ) -> Result { + // Create the alias + let mut ctr = 1; + let mut alias = format!("disposable_{ctr}"); + + while self.store().contains_alias(&Alias::from(&alias)) { + ctr += 1; + alias = format!("disposable_{ctr}"); + } + // Generate a disposable keypair to sign the wrapper if requested + // TODO: once the wrapper transaction has been applied, this key can be + // deleted from wallet (the transaction being accepted is not enough + // cause we could end up doing a rollback) + let (alias, disposable_keypair) = self + .gen_store_secret_key_atomic( + SchemeType::Ed25519, + Some(alias), + false, + None, + rng, + )? + .expect("Failed to initialize disposable keypair"); + + println!("Created disposable keypair with alias {alias}"); + Ok(disposable_keypair) + } } From 1ee25e577e227565cf90cf98b9a5cfce41489c30 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Mar 2024 19:00:11 +0100 Subject: [PATCH 52/66] Clean --- crates/sdk/src/wallet/mod.rs | 87 ------------------------------------ 1 file changed, 87 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 55a684c797..4897e55c3d 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -839,35 +839,6 @@ impl Wallet { Some(Seed::new(&mnemonic, &passphrase)) } - // XXX REMOVE - /// Generate a new keypair, derive an implicit address from its public key - /// and insert them into the store with the provided alias, converted to - /// lower case. If the alias already exists, optionally force overwrite - /// the keypair for the alias. - /// If no encryption password is provided, the keypair will be stored raw - /// without encryption. - /// Stores the key in decrypted key cache and returns the alias of the key - /// and the generated keypair. - pub fn gen_store_secret_key( - &mut self, - scheme: SchemeType, - alias: Option, - alias_force: bool, - password: Option>, - rng: &mut (impl CryptoRng + RngCore), - ) -> Option<(String, common::SecretKey)> { - let sk = gen_secret_key(scheme, rng); - self.insert_keypair( - alias.unwrap_or_default(), - alias_force, - sk.clone(), - password, - None, - None, - ) - .map(|alias| (alias, sk)) - } - /// XXX OK /// Decrypt stored key, if it's not stored un-encrypted. /// If a given storage key needs to be decrypted and password is not @@ -900,64 +871,6 @@ impl Wallet { StoredKeypair::Raw(raw) => Ok(raw.clone()), } } - - // XXX REMOVE - /// Add a new keypair with the given alias. If the alias is already used, - /// will ask whether the existing alias should be replaced, a different - /// alias is desired, or the alias creation should be cancelled. Return - /// the chosen alias if the keypair has been added, otherwise return - /// nothing. - pub fn insert_keypair( - &mut self, - alias: String, - force_alias: bool, - sk: common::SecretKey, - password: Option>, - address: Option
, - path: Option, - ) -> Option { - self.store - .insert_keypair::( - alias.into(), - sk.clone(), - password, - address, - path, - alias_force, - ) - .map(|alias| { - // Cache the newly added key - self.decrypted_key_cache.insert(alias.clone(), sk); - alias.into() - }) - } - - // XXX REMOVE - /// Insert a spending key into the wallet under the given alias - pub fn insert_spending_key( - &mut self, - alias: String, - force_alias: bool, - spend_key: ExtendedSpendingKey, - password: Option>, - path: Option, - ) -> Option { - self.store - .insert_spending_key::( - alias.into(), - spend_key, - password, - path, - force_alias, - ) - .map(|alias| { - // Cache the newly added key - self.decrypted_spendkey_cache - .insert(alias.clone(), spend_key); - alias - }) - .map(Into::into) - } } impl Wallet { From 56b94bebc3622d1f59a34b652fbbad2d01b45699 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 15 Mar 2024 12:23:12 +0100 Subject: [PATCH 53/66] Remove redundant mut modifiers --- crates/apps/src/lib/cli.rs | 22 +++---- crates/apps/src/lib/cli/client.rs | 102 ++++++++++++++--------------- crates/apps/src/lib/cli/relayer.rs | 2 +- crates/sdk/src/tx.rs | 5 +- 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/crates/apps/src/lib/cli.rs b/crates/apps/src/lib/cli.rs index 1f792beb27..34fee8cc88 100644 --- a/crates/apps/src/lib/cli.rs +++ b/crates/apps/src/lib/cli.rs @@ -4295,7 +4295,7 @@ pub mod args { impl CliToSdk> for TxInitAccount { fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { let tx = self.tx.to_sdk(ctx); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); TxInitAccount:: { tx, vp_code_path: self.vp_code_path, @@ -4350,7 +4350,7 @@ pub mod args { impl CliToSdk> for TxBecomeValidator { fn to_sdk(self, ctx: &mut Context) -> TxBecomeValidator { let tx = self.tx.to_sdk(ctx); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); TxBecomeValidator:: { tx, scheme: self.scheme, @@ -4470,7 +4470,7 @@ pub mod args { impl CliToSdk> for TxInitValidator { fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { let tx = self.tx.to_sdk(ctx); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); TxInitValidator:: { tx, scheme: self.scheme, @@ -4619,7 +4619,7 @@ pub mod args { impl CliToSdk> for TxUpdateAccount { fn to_sdk(self, ctx: &mut Context) -> TxUpdateAccount { let tx = self.tx.to_sdk(ctx); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); TxUpdateAccount:: { tx, vp_code_path: self.vp_code_path, @@ -5021,7 +5021,7 @@ pub mod args { impl CliToSdk> for RevealPk { fn to_sdk(self, ctx: &mut Context) -> RevealPk { let tx = self.tx.to_sdk(ctx); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); RevealPk:: { tx, public_key: chain_ctx.get(&self.public_key), @@ -5588,7 +5588,7 @@ pub mod args { impl CliToSdk> for ConsensusKeyChange { fn to_sdk(self, ctx: &mut Context) -> ConsensusKeyChange { let tx = self.tx.to_sdk(ctx); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); ConsensusKeyChange:: { tx, validator: chain_ctx.get(&self.validator), @@ -6412,7 +6412,7 @@ pub mod args { impl CliToSdk> for Query { fn to_sdk(self, ctx: &mut Context) -> Query { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); Query:: { ledger_address: chain_ctx.get(&self.ledger_address), } @@ -6467,7 +6467,7 @@ pub mod args { use crate::wallet::CliWalletUtils; - let find_viewing_key = |w: &mut Wallet| { + let find_viewing_key = |w: &Wallet| { w.find_viewing_key_atomic(&self.viewing_key.raw) .expect("Failed to read from the wallet storage.") .unwrap_or_else(|_| { @@ -6481,10 +6481,10 @@ pub mod args { let viewing_key = if ctx.global_args.is_pre_genesis { let wallet_path = ctx.global_args.base_dir.join(PRE_GENESIS_DIR); - let mut wallet = crate::wallet::load_or_new(&wallet_path); - find_viewing_key(&mut wallet) + let wallet = crate::wallet::load_or_new(&wallet_path); + find_viewing_key(&wallet) } else { - find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) + find_viewing_key(&ctx.borrow_chain_or_exit().wallet) }; PayAddressGen:: { alias: self.alias, diff --git a/crates/apps/src/lib/cli/client.rs b/crates/apps/src/lib/cli/client.rs index 44fde4f6a0..b79f2f4a46 100644 --- a/crates/apps/src/lib/cli/client.rs +++ b/crates/apps/src/lib/cli/client.rs @@ -28,7 +28,7 @@ impl CliApi { // Ledger cmds Sub::TxCustom(TxCustom(args)) => { // TODO: too much boilerplate to setup client - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -54,7 +54,7 @@ impl CliApi { } } Sub::TxTransfer(TxTransfer(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -66,7 +66,7 @@ impl CliApi { tx::submit_transfer(&namada, args).await?; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -78,7 +78,7 @@ impl CliApi { tx::submit_ibc_transfer(&namada, args).await?; } Sub::TxUpdateAccount(TxUpdateAccount(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -90,7 +90,7 @@ impl CliApi { tx::submit_update_account(&namada, args).await?; } Sub::TxInitAccount(TxInitAccount(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -116,7 +116,7 @@ impl CliApi { } } Sub::TxBecomeValidator(TxBecomeValidator(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -141,7 +141,7 @@ impl CliApi { .await?; } Sub::TxInitValidator(TxInitValidator(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -166,7 +166,7 @@ impl CliApi { .await?; } Sub::TxInitProposal(TxInitProposal(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -178,7 +178,7 @@ impl CliApi { tx::submit_init_proposal(&namada, args).await?; } Sub::TxVoteProposal(TxVoteProposal(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -190,7 +190,7 @@ impl CliApi { tx::submit_vote_proposal(&namada, args).await?; } Sub::TxRevealPk(TxRevealPk(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -202,7 +202,7 @@ impl CliApi { tx::submit_reveal_pk(&namada, args).await?; } Sub::Bond(Bond(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -214,7 +214,7 @@ impl CliApi { tx::submit_bond(&namada, args).await?; } Sub::Unbond(Unbond(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -226,7 +226,7 @@ impl CliApi { tx::submit_unbond(&namada, args).await?; } Sub::Withdraw(Withdraw(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -238,7 +238,7 @@ impl CliApi { tx::submit_withdraw(&namada, args).await?; } Sub::ClaimRewards(ClaimRewards(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -250,7 +250,7 @@ impl CliApi { tx::submit_claim_rewards(&namada, args).await?; } Sub::Redelegate(Redelegate(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -264,7 +264,7 @@ impl CliApi { Sub::TxCommissionRateChange(TxCommissionRateChange( args, )) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -277,7 +277,7 @@ impl CliApi { .await?; } Sub::TxChangeConsensusKey(TxChangeConsensusKey(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -289,7 +289,7 @@ impl CliApi { tx::submit_change_consensus_key(&namada, args).await?; } Sub::TxMetadataChange(TxMetadataChange(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -339,7 +339,7 @@ impl CliApi { #[cfg(feature = "namada-eth-bridge")] Sub::AddToEthBridgePool(args) => { let args = args.0; - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -358,7 +358,7 @@ impl CliApi { ); } Sub::TxUnjailValidator(TxUnjailValidator(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -370,7 +370,7 @@ impl CliApi { tx::submit_unjail_validator(&namada, args).await?; } Sub::TxDeactivateValidator(TxDeactivateValidator(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -382,7 +382,7 @@ impl CliApi { tx::submit_deactivate_validator(&namada, args).await?; } Sub::TxReactivateValidator(TxReactivateValidator(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -396,7 +396,7 @@ impl CliApi { Sub::TxUpdateStewardCommission( TxUpdateStewardCommission(args), ) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -409,7 +409,7 @@ impl CliApi { .await?; } Sub::TxResignSteward(TxResignSteward(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -422,7 +422,7 @@ impl CliApi { } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.ledger_address); let client = client.unwrap_or_else(|| { @@ -433,7 +433,7 @@ impl CliApi { rpc::query_and_print_epoch(&namada).await; } Sub::QueryNextEpochInfo(QueryNextEpochInfo(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.ledger_address); let client = client.unwrap_or_else(|| { @@ -444,7 +444,7 @@ impl CliApi { rpc::query_and_print_next_epoch_info(&namada).await; } Sub::QueryStatus(QueryStatus(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.ledger_address); let client = client.unwrap_or_else(|| { @@ -454,7 +454,7 @@ impl CliApi { rpc::query_and_print_status(&namada).await; } Sub::QueryValidatorState(QueryValidatorState(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -467,7 +467,7 @@ impl CliApi { .await; } Sub::QueryTransfers(QueryTransfers(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -479,7 +479,7 @@ impl CliApi { rpc::query_transfers(&namada, args).await; } Sub::QueryConversions(QueryConversions(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -491,7 +491,7 @@ impl CliApi { rpc::query_conversions(&namada, args).await; } Sub::QueryMaspRewardTokens(QueryMaspRewardTokens(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.ledger_address); let client = client.unwrap_or_else(|| { @@ -502,7 +502,7 @@ impl CliApi { rpc::query_masp_reward_tokens(&namada).await; } Sub::QueryBlock(QueryBlock(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.ledger_address); let client = client.unwrap_or_else(|| { @@ -513,7 +513,7 @@ impl CliApi { rpc::query_block(&namada).await; } Sub::QueryBalance(QueryBalance(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -537,7 +537,7 @@ impl CliApi { rpc::query_ibc_tokens(&namada, args).await; } Sub::QueryBonds(QueryBonds(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -551,7 +551,7 @@ impl CliApi { .expect("expected successful query of bonds"); } Sub::QueryBondedStake(QueryBondedStake(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -563,7 +563,7 @@ impl CliApi { rpc::query_bonded_stake(&namada, args).await; } Sub::QueryCommissionRate(QueryCommissionRate(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -576,7 +576,7 @@ impl CliApi { .await; } Sub::QueryMetaData(QueryMetaData(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -588,7 +588,7 @@ impl CliApi { rpc::query_and_print_metadata(&namada, args).await; } Sub::QuerySlashes(QuerySlashes(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -600,7 +600,7 @@ impl CliApi { rpc::query_slashes(&namada, args).await; } Sub::QueryRewards(QueryRewards(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -612,7 +612,7 @@ impl CliApi { rpc::query_and_print_rewards(&namada, args).await; } Sub::QueryDelegations(QueryDelegations(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -624,7 +624,7 @@ impl CliApi { rpc::query_delegations(&namada, args).await; } Sub::QueryFindValidator(QueryFindValidator(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -636,7 +636,7 @@ impl CliApi { rpc::query_find_validator(&namada, args).await; } Sub::QueryResult(QueryResult(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -648,7 +648,7 @@ impl CliApi { rpc::query_result(&namada, args).await; } Sub::QueryRawBytes(QueryRawBytes(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -660,7 +660,7 @@ impl CliApi { rpc::query_raw_bytes(&namada, args).await; } Sub::QueryProposal(QueryProposal(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -672,7 +672,7 @@ impl CliApi { rpc::query_proposal(&namada, args).await; } Sub::QueryProposalResult(QueryProposalResult(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -684,7 +684,7 @@ impl CliApi { rpc::query_proposal_result(&namada, args).await; } Sub::QueryProposalVotes(QueryProposalVotes(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -698,7 +698,7 @@ impl CliApi { Sub::QueryProtocolParameters(QueryProtocolParameters( args, )) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -710,7 +710,7 @@ impl CliApi { rpc::query_protocol_parameters(&namada, args).await; } Sub::QueryPgf(QueryPgf(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -722,7 +722,7 @@ impl CliApi { rpc::query_pgf(&namada, args).await; } Sub::QueryAccount(QueryAccount(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { @@ -734,7 +734,7 @@ impl CliApi { rpc::query_account(&namada, args).await; } Sub::SignTx(SignTx(args)) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); let client = client.unwrap_or_else(|| { @@ -782,7 +782,7 @@ impl CliApi { Utils::EpochSleep(EpochSleep(args)) => { let mut ctx = cli::Context::new::(global_args) .expect("expected to construct a context"); - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.ledger_address); let client = C::from_tendermint_address(&ledger_address); client.wait_until_node_is_synced(&io).await?; diff --git a/crates/apps/src/lib/cli/relayer.rs b/crates/apps/src/lib/cli/relayer.rs index 6e4391190a..32c77be839 100644 --- a/crates/apps/src/lib/cli/relayer.rs +++ b/crates/apps/src/lib/cli/relayer.rs @@ -42,7 +42,7 @@ impl CliApi { EthBridgePoolWithCtx::RecommendBatch(RecommendBatch( args, )) => { - let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let chain_ctx = ctx.borrow_chain_or_exit(); let ledger_address = chain_ctx.get(&args.query.ledger_address); let client = client.unwrap_or_else(|| { diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index f92513a68d..af4e3e3823 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -489,7 +489,7 @@ pub async fn save_initialized_accounts( ); // Store newly initialized account addresses in the wallet for (ix, address) in initialized_accounts.iter().enumerate() { - let encoded = address.encode(); + let encoded = address.encode(); let alias: Cow = match &args.initialized_account_alias { Some(initialized_account_alias) => { if len == 1 { @@ -3563,7 +3563,8 @@ async fn get_refund_target( let alias = format!("ibc-refund-target-{}", rng.next_u64()); let mut wallet = context.wallet_mut().await; wallet - .insert_payment_addr(alias, payment_addr, false) + .insert_payment_addr_atomic(alias, payment_addr, false) + .expect("Failed to update the wallet storage.") .ok_or_else(|| { Error::Other( "Adding a new payment address failed".to_string(), From b35d95585c3797a16b4ab74b46f8636c380de2db Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Mon, 22 Apr 2024 17:45:18 +0200 Subject: [PATCH 54/66] Atomic `lookup_ibc_token_alias` --- crates/apps/src/lib/client/rpc.rs | 29 ++++++++++++++++++++--------- crates/sdk/src/wallet/mod.rs | 21 ++++++++++----------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/crates/apps/src/lib/client/rpc.rs b/crates/apps/src/lib/client/rpc.rs index 86acf53450..1f8ebfa8e8 100644 --- a/crates/apps/src/lib/client/rpc.rs +++ b/crates/apps/src/lib/client/rpc.rs @@ -767,9 +767,11 @@ async fn lookup_token_alias( ) .await { - Ok(ibc_trace) => { - context.wallet().await.lookup_ibc_token_alias(ibc_trace) - } + Ok(ibc_trace) => context + .wallet() + .await + .lookup_ibc_token_alias_atomic(ibc_trace) + .expect("Failed to read from the wallet storage"), Err(_) => token.to_string(), } } @@ -802,8 +804,11 @@ async fn query_tokens( { let ibc_denom = rpc::query_ibc_denom(context, token.to_string(), owner).await; - let alias = - context.wallet().await.lookup_ibc_token_alias(ibc_denom); + let alias = context + .wallet() + .await + .lookup_ibc_token_alias_atomic(ibc_denom) + .expect("Failed to read from the wallet storage"); tokens.insert(alias, token.clone()); // we don't need to check other IBC prefixes return tokens; @@ -840,8 +845,11 @@ async fn query_tokens( { Ok(ibc_tokens) => { for (trace, addr) in ibc_tokens { - let ibc_trace_alias = - context.wallet().await.lookup_ibc_token_alias(trace); + let ibc_trace_alias = context + .wallet() + .await + .lookup_ibc_token_alias_atomic(trace) + .expect("Failed to read from the wallet storage"); tokens.insert(ibc_trace_alias, addr); } } @@ -868,8 +876,11 @@ pub async fn query_ibc_tokens( match rpc::query_ibc_tokens(context, token, owner.as_ref()).await { Ok(ibc_tokens) => { for (trace, addr) in ibc_tokens { - let alias = - context.wallet().await.lookup_ibc_token_alias(trace); + let alias = context + .wallet() + .await + .lookup_ibc_token_alias_atomic(trace) + .expect("Failed to read from the wallet storage"); display_line!(context.io(), "{}: {}", alias, addr); } } diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 4897e55c3d..721e57b003 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -544,34 +544,33 @@ impl Wallet { /// Try to find an alias of the base token in the given IBC denomination /// from the wallet. If not found, formats the IBC denomination into a /// string. - pub fn lookup_ibc_token_alias(&self, ibc_denom: impl AsRef) -> String { + pub fn lookup_ibc_token_alias_atomic( + &self, + ibc_denom: impl AsRef, + ) -> Result { // Convert only an IBC denom or a Namada address since an NFT trace // doesn't have the alias is_ibc_denom(&ibc_denom) .map(|(trace_path, base_token)| { let base_token_alias = match Address::decode(&base_token) { - Ok(base_token) => self - .lookup_alias_atomic(&base_token) - .expect("Failed to read from the wallet storage."), + Ok(base_token) => self.lookup_alias_atomic(&base_token)?, Err(_) => base_token, }; - if trace_path.is_empty() { + let alias = if trace_path.is_empty() { base_token_alias } else { format!("{}/{}", trace_path, base_token_alias) - } + }; + Ok(alias) }) .or_else(|| { // It's not an IBC denom, but could be a raw Namada address match Address::decode(&ibc_denom) { - Ok(addr) => Some( - self.lookup_alias_atomic(&addr) - .expect("Failed to read from the wallet storage."), - ), + Ok(addr) => Some(self.lookup_alias_atomic(&addr)), Err(_) => None, } }) - .unwrap_or(ibc_denom.as_ref().to_string()) + .unwrap_or(Ok(ibc_denom.as_ref().to_string())) } /// Find the viewing key with the given alias in the wallet and return it From 115ba7110ecb0974813587dee1f29633ef863c23 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Mon, 22 Apr 2024 17:45:46 +0200 Subject: [PATCH 55/66] Formatting --- crates/sdk/src/tx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index af4e3e3823..27fb276777 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -489,7 +489,7 @@ pub async fn save_initialized_accounts( ); // Store newly initialized account addresses in the wallet for (ix, address) in initialized_accounts.iter().enumerate() { - let encoded = address.encode(); + let encoded = address.encode(); let alias: Cow = match &args.initialized_account_alias { Some(initialized_account_alias) => { if len == 1 { From c799c7b3f3b1b00a5d4779009ee0d7ea6443c561 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 12:38:38 +0200 Subject: [PATCH 56/66] Make Store clonable --- crates/sdk/src/wallet/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sdk/src/wallet/store.rs b/crates/sdk/src/wallet/store.rs index 0a7c9ff297..052a2c457d 100644 --- a/crates/sdk/src/wallet/store.rs +++ b/crates/sdk/src/wallet/store.rs @@ -57,7 +57,7 @@ pub struct ValidatorData { } /// A Storage area for keys and addresses -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct Store { /// Known viewing keys view_keys: BTreeMap, From 978197d791428ac0ea3438e73991a65bb2f04300 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 12:40:13 +0200 Subject: [PATCH 57/66] Add wallet in-memory store --- crates/sdk/src/wallet/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 721e57b003..8a24a0053f 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -346,6 +346,7 @@ pub struct Wallet { /// Location where this shielded context is saved utils: U, store: Store, + store_in_mem: Option, decrypted_key_cache: HashMap, decrypted_spendkey_cache: HashMap, } @@ -363,6 +364,8 @@ impl Wallet { Self { utils, store, + // XXX comment + store_in_mem: Option::default(), decrypted_key_cache: HashMap::default(), decrypted_spendkey_cache: HashMap::default(), } From e806aa0c2f15e57f04a8f53465ea851a09aef42f Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 12:41:42 +0200 Subject: [PATCH 58/66] Add in-memory store loader --- crates/sdk/src/wallet/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 8a24a0053f..8fe6b66dde 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -108,6 +108,12 @@ pub trait WalletStorage: Sized + Clone { /// Load a wallet from the store file. fn load(&self, wallet: &mut Wallet) -> Result<(), LoadStoreError>; + /// Load store into memory + fn load_in_mem( + &self, + wallet: &mut Wallet, + ) -> Result<(), LoadStoreError>; + /// Atomically update the wallet store fn update_store( &self, @@ -198,6 +204,14 @@ pub mod fs { Ok(()) } + fn load_in_mem( + &self, + wallet: &mut Wallet, + ) -> Result<(), LoadStoreError> { + wallet.store_in_mem = Some(self.load_store_read_only()?); + Ok(()) + } + fn load_store_read_only(&self) -> Result { let wallet_file = self.store_dir().join(FILE_NAME); let mut options = fs::OpenOptions::new(); From 06ad2404d526ad86c60cf1e16c0bf7a58a6bdbbd Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 13:01:26 +0200 Subject: [PATCH 59/66] Add dry run mode setters --- crates/apps/src/lib/cli/context.rs | 13 +++++++++++++ crates/sdk/src/wallet/mod.rs | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/crates/apps/src/lib/cli/context.rs b/crates/apps/src/lib/cli/context.rs index 086c902476..ff48f73848 100644 --- a/crates/apps/src/lib/cli/context.rs +++ b/crates/apps/src/lib/cli/context.rs @@ -206,6 +206,19 @@ impl Context { .unwrap_or_else(|| safe_exit_on_missing_chain_context()) } + /// Switch the wallet into dry run mode + pub fn set_dry_run(&mut self) -> () { + self.chain + .as_mut() + .unwrap_or_else(|| safe_exit_on_missing_chain_context()) + .wallet + .set_dry_run() + .unwrap_or_else(|err| { + eprintln!("Failed to load wallet store: {}", err); + utils::safe_exit(1) + }) + } + /// Make an implementation of Namada from this object and parameters. pub fn to_sdk(self, client: C, io: IO) -> impl Namada where diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 8fe6b66dde..1708ed5c90 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -431,6 +431,10 @@ impl Wallet { self.utils.save(self) } + pub fn set_dry_run(&mut self) -> Result<(), LoadStoreError> { + self.utils.clone().load_in_mem(self) + } + /// Add validator data to the store pub fn add_validator_data_atomic( &mut self, From b16a38a360a5a86a8a06e048914b31eba04520a8 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 13:03:08 +0200 Subject: [PATCH 60/66] Add wallet store util functions --- crates/sdk/src/wallet/mod.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 1708ed5c90..5ce82fecb4 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -435,6 +435,41 @@ impl Wallet { self.utils.clone().load_in_mem(self) } + fn into_store(self) -> Result { + if let Some(store) = self.store_in_mem { + // return in-memory wallet store + Ok(store) + } else { + // read wallet storage + self.utils.load_store_read_only() + } + } + + fn get_store(&self) -> Result { + if let Some(ref store) = self.store_in_mem { + // return in-memory wallet store + Ok(store.clone()) + } else { + // read wallet storage + self.utils.load_store_read_only() + } + } + + fn update_store( + &mut self, + update: impl FnOnce(&mut Store), + ) -> Result<(), LoadStoreError> { + if let Some(store) = &mut self.store_in_mem { + // update in-memory wallet store (e.g., for dry-run tx + // executions) + update(store); + Ok(()) + } else { + // update wallet storage + self.utils.update_store(update) + } + } + /// Add validator data to the store pub fn add_validator_data_atomic( &mut self, From 694206a3974b0775d94d3c656266dbd97e2fef34 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 13:56:35 +0200 Subject: [PATCH 61/66] Remove `take_validator_data` --- crates/apps/src/lib/node/ledger/shell/mod.rs | 2 +- crates/apps/src/lib/wallet/mod.rs | 4 +--- crates/sdk/src/wallet/mod.rs | 16 ++++------------ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/crates/apps/src/lib/node/ledger/shell/mod.rs b/crates/apps/src/lib/node/ledger/shell/mod.rs index 9fa68504c4..eecf5a5976 100644 --- a/crates/apps/src/lib/node/ledger/shell/mod.rs +++ b/crates/apps/src/lib/node/ledger/shell/mod.rs @@ -475,7 +475,7 @@ where }; wallet - .take_validator_data_atomic() + .into_validator_data_atomic() .map(|data| ShellMode::Validator { data, broadcast_sender, diff --git a/crates/apps/src/lib/wallet/mod.rs b/crates/apps/src/lib/wallet/mod.rs index c4b1a3bc0f..96316dcf8b 100644 --- a/crates/apps/src/lib/wallet/mod.rs +++ b/crates/apps/src/lib/wallet/mod.rs @@ -232,9 +232,7 @@ where .find_key_by_pkh_atomic(&pkh, None) .expect("Failed to read from the wallet storage.") .ok() - .or_else(|| { - wallet.take_validator_data_atomic().map(extract_key) - }) + .or_else(|| wallet.get_validator_data_atomic().map(extract_key)) .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string())) }) .transpose() diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 5ce82fecb4..f18e6b243c 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -481,22 +481,14 @@ impl Wallet { .update_store(|store| store.add_validator_data(address, keys)) } - /// XXX does not make sense in the current context -- REMOVE? /// Returns the validator data, if it exists. - pub fn take_validator_data_atomic(&self) -> Option { - self.utils - .clone() - .load_store_read_only() - .ok() - .and_then(Store::into_validator_data) + pub fn get_validator_data_atomic(&self) -> Option { + self.get_store().ok().and_then(Store::into_validator_data) } + /// Returns the validator data, if it exists. pub fn into_validator_data_atomic(self) -> Option { - self.utils - .clone() - .load_store_read_only() - .ok() - .and_then(Store::into_validator_data) + self.into_store().ok().and_then(Store::into_validator_data) } /// Extend the wallet from pre-genesis validator wallet. From fe91624d3666657fd04c752c1fcc39b33a407230 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 13:58:34 +0200 Subject: [PATCH 62/66] Use new dry-run wallet mode --- crates/apps/src/lib/cli/client.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/crates/apps/src/lib/cli/client.rs b/crates/apps/src/lib/cli/client.rs index b79f2f4a46..dbe5bdbe9e 100644 --- a/crates/apps/src/lib/cli/client.rs +++ b/crates/apps/src/lib/cli/client.rs @@ -36,17 +36,14 @@ impl CliApi { }); client.wait_until_node_is_synced(&io).await?; let args = args.to_sdk(&mut ctx); - let namada = ctx.to_sdk(client, io); let dry_run = args.tx.dry_run || args.tx.dry_run_wrapper; + if dry_run { + ctx.set_dry_run() + } + let namada = ctx.to_sdk(client, io); tx::submit_custom(&namada, args).await?; - if !dry_run { - namada - .wallet() - .await - .save() - .unwrap_or_else(|err| eprintln!("{}", err)); - } else { + if dry_run { namada.io().println( "Transaction dry run. No addresses have been \ saved.", @@ -98,17 +95,14 @@ impl CliApi { }); client.wait_until_node_is_synced(&io).await?; let args = args.to_sdk(&mut ctx); - let namada = ctx.to_sdk(client, io); let dry_run = args.tx.dry_run || args.tx.dry_run_wrapper; + if dry_run { + ctx.set_dry_run(); + } + let namada = ctx.to_sdk(client, io); tx::submit_init_account(&namada, args).await?; - if !dry_run { - namada - .wallet() - .await - .save() - .unwrap_or_else(|err| eprintln!("{}", err)); - } else { + if dry_run { namada.io().println( "Transaction dry run. No addresses have been \ saved.", From 5a110d3229d4fe835abb492446c255b5ed2bd470 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 25 Apr 2024 14:01:29 +0200 Subject: [PATCH 63/66] Use proper update store function --- crates/sdk/src/wallet/mod.rs | 87 ++++++++++++------------------------ 1 file changed, 28 insertions(+), 59 deletions(-) diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index f18e6b243c..4fbebbc89c 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -476,9 +476,7 @@ impl Wallet { address: Address, keys: ValidatorKeys, ) -> Result<(), LoadStoreError> { - self.utils - .clone() - .update_store(|store| store.add_validator_data(address, keys)) + self.update_store(|store| store.add_validator_data(address, keys)) } /// Returns the validator data, if it exists. @@ -493,12 +491,12 @@ impl Wallet { /// Extend the wallet from pre-genesis validator wallet. pub fn extend_from_pre_genesis_validator_atomic( - &self, + &mut self, validator_address: Address, validator_alias: Alias, other: pre_genesis::ValidatorWallet, ) -> Result<(), LoadStoreError> { - self.utils.clone().update_store(|store| { + self.update_store(|store| { store.extend_from_pre_genesis_validator( validator_address, validator_alias, @@ -512,10 +510,7 @@ impl Wallet { &self, vp_type: AddressVpType, ) -> Result, LoadStoreError> { - Ok(self - .utils - .load_store_read_only()? - .get_addresses_with_vp_type(vp_type)) + Ok(self.get_store()?.get_addresses_with_vp_type(vp_type)) } /// Add a vp_type to a given address @@ -524,7 +519,7 @@ impl Wallet { vp_type: AddressVpType, address: Address, ) -> Result<(), LoadStoreError> { - self.utils.clone().update_store(|store| { + self.update_store(|store| { store.add_vp_type_to_address(vp_type, address) }) } @@ -557,8 +552,7 @@ impl Wallet { Alias::is_reserved(alias.as_ref()) .map(Ok) .or_else(|| { - self.utils - .load_store_read_only() + self.get_store() .map(move |store| store.find_address(alias).cloned()) .transpose() }) @@ -570,11 +564,7 @@ impl Wallet { &self, address: &Address, ) -> Result, LoadStoreError> { - Ok(self - .utils - .load_store_read_only()? - .find_alias(address) - .cloned()) + Ok(self.get_store()?.find_alias(address).cloned()) } /// Try to find an alias for a given address from the wallet. If not found, @@ -627,8 +617,7 @@ impl Wallet { alias: impl AsRef, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .find_viewing_key(alias.as_ref()) .cloned() .ok_or_else(|| { @@ -642,11 +631,7 @@ impl Wallet { &self, alias: impl AsRef, ) -> Result, LoadStoreError> { - Ok(self - .utils - .load_store_read_only()? - .find_payment_addr(alias.as_ref()) - .cloned()) + Ok(self.get_store()?.find_payment_addr(alias.as_ref()).cloned()) } /// Find an alias by the payment address if it's in the wallet. @@ -655,8 +640,7 @@ impl Wallet { payment_address: &PaymentAddress, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .find_alias_by_payment_addr(payment_address) .cloned()) } @@ -673,8 +657,7 @@ impl Wallet { LoadStoreError, > { Ok(self - .utils - .load_store_read_only()? + .get_store()? .get_secret_keys() .into_iter() .map(|(alias, (kp, pkh))| { @@ -688,8 +671,7 @@ impl Wallet { &self, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .get_public_keys() .iter() .map(|(alias, value)| (alias.into(), value.clone())) @@ -701,8 +683,7 @@ impl Wallet { &self, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .get_addresses() .iter() .map(|(alias, value)| (alias.into(), value.clone())) @@ -714,8 +695,7 @@ impl Wallet { &self, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .get_payment_addrs() .iter() .map(|(alias, value)| (alias.into(), *value)) @@ -727,8 +707,7 @@ impl Wallet { &self, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .get_viewing_keys() .iter() .map(|(alias, value)| (alias.into(), *value)) @@ -743,8 +722,7 @@ impl Wallet { LoadStoreError, > { Ok(self - .utils - .load_store_read_only()? + .get_store()? .get_spending_keys() .iter() .map(|(alias, value)| (alias.into(), value.clone())) @@ -757,8 +735,7 @@ impl Wallet { alias: impl AsRef, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .find_secret_key(alias) .map(|stored_keypair| stored_keypair.is_encrypted())) } @@ -781,8 +758,7 @@ impl Wallet { pkh: &PublicKeyHash, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .find_path_by_pkh(pkh) .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))) } @@ -796,8 +772,7 @@ impl Wallet { pkh: &PublicKeyHash, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .find_public_key_by_pkh(pkh) .cloned() .ok_or_else(|| FindKeyError::KeyNotFound(pkh.to_string()))) @@ -809,8 +784,7 @@ impl Wallet { alias_or_pkh: impl AsRef, ) -> Result, LoadStoreError> { Ok(self - .utils - .load_store_read_only()? + .get_store()? .find_public_key(alias_or_pkh.as_ref()) .cloned() .ok_or_else(|| { @@ -824,8 +798,8 @@ impl Wallet { &mut self, wallet: Self, ) -> Result<(), LoadStoreError> { - let other_store = wallet.utils.load_store_read_only()?; - self.utils.update_store(|store| store.extend(other_store)) + let other_store = wallet.into_store()?; + self.update_store(|store| store.extend(other_store)) } /// Remove keys and addresses associated with the given alias @@ -833,8 +807,7 @@ impl Wallet { &mut self, alias: String, ) -> Result<(), LoadStoreError> { - self.utils - .update_store(|store| store.remove_alias(&alias.into())) + self.update_store(|store| store.remove_alias(&alias.into())) } } @@ -944,7 +917,7 @@ impl Wallet { pkh: &PublicKeyHash, password: Option>, ) -> Result, LoadStoreError> { - let store = self.utils.load_store_read_only()?; + let store = self.get_store()?; // Try to look-up alias for the given pk. Otherwise, use the PKH string. let alias = store .find_alias_by_pkh(pkh) @@ -984,10 +957,8 @@ impl Wallet { return Ok(Ok(cached_key.clone())); } // If not cached, look-up in store - let res = if let Some(stored_key) = self - .utils - .load_store_read_only()? - .find_secret_key(alias_pkh_or_pk.as_ref()) + let res = if let Some(stored_key) = + self.get_store()?.find_secret_key(alias_pkh_or_pk.as_ref()) { Self::decrypt_stored_key::<_>( &mut self.decrypted_key_cache, @@ -1019,10 +990,8 @@ impl Wallet { return Ok(Ok(*cached_key)); } // If not cached, look-up in store - let res = if let Some(stored_spendkey) = self - .utils - .load_store_read_only()? - .find_spending_key(alias.as_ref()) + let res = if let Some(stored_spendkey) = + self.get_store()?.find_spending_key(alias.as_ref()) { Self::decrypt_stored_key::<_>( &mut self.decrypted_spendkey_cache, From 1d70c18dbfd7a3d72ff04708e4fd695bd09f6dd9 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 26 Apr 2024 17:39:56 +0200 Subject: [PATCH 64/66] Enable atomic wallet for change consensus key --- crates/apps/src/lib/cli/client.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/apps/src/lib/cli/client.rs b/crates/apps/src/lib/cli/client.rs index dbe5bdbe9e..64c9d53c4f 100644 --- a/crates/apps/src/lib/cli/client.rs +++ b/crates/apps/src/lib/cli/client.rs @@ -279,6 +279,11 @@ impl CliApi { }); client.wait_until_node_is_synced(&io).await?; let args = args.to_sdk(&mut ctx); + let dry_run = + args.tx.dry_run || args.tx.dry_run_wrapper; + if dry_run { + ctx.set_dry_run() + } let namada = ctx.to_sdk(client, io); tx::submit_change_consensus_key(&namada, args).await?; } From b5cb9530ef0b42d539d59e0c5769b28450c3cb07 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 26 Apr 2024 17:40:55 +0200 Subject: [PATCH 65/66] Adapt change-consensus-key for atomic wallet --- crates/apps/src/lib/client/tx.rs | 142 +++++++++++++++++-------------- 1 file changed, 80 insertions(+), 62 deletions(-) diff --git a/crates/apps/src/lib/client/tx.rs b/crates/apps/src/lib/client/tx.rs index 49d83ea8bd..9ba6637801 100644 --- a/crates/apps/src/lib/client/tx.rs +++ b/crates/apps/src/lib/client/tx.rs @@ -315,66 +315,74 @@ pub async fn submit_change_consensus_key( args: args::ConsensusKeyChange, ) -> Result<(), error::Error> { let validator = args.validator; - let consensus_key = args.consensus_key; + let user_consensus_key = args.consensus_key; - // Determine the alias for the new key let mut wallet = namada.wallet_mut().await; - let alias = wallet - .find_alias_atomic(&validator) - .expect("Failed to read from the wallet storage."); - let base_consensus_key_alias = alias - .map(|al| validator_consensus_key(&al)) - .unwrap_or_else(|| { - validator_consensus_key(&validator.to_string().into()) - }); - let mut consensus_key_alias = base_consensus_key_alias.to_string(); - let all_keys = wallet - .get_secret_keys_atomic() - .expect("Failed to read from the wallet storage."); - let mut key_counter = 0; - while all_keys.contains_key(&consensus_key_alias) { - key_counter += 1; - consensus_key_alias = - format!("{base_consensus_key_alias}-{key_counter}"); - } // Check the given key or generate a new one - let new_key = consensus_key - .map(|key| match key { - common::PublicKey::Ed25519(_) => key, - common::PublicKey::Secp256k1(_) => { - edisplay_line!( - namada.io(), - "Consensus key can only be ed25519" + let (consensus_key_alias, consensus_key, is_consensus_key_generated) = + user_consensus_key + .map(|key| match key { + common::PublicKey::Ed25519(_) => { + // no alias, the user key will not be saved + (String::default(), key, false) + } + common::PublicKey::Secp256k1(_) => { + edisplay_line!( + namada.io(), + "Consensus key can only be ed25519" + ); + safe_exit(1) + } + }) + .unwrap_or_else(|| { + display_line!(namada.io(), "Generating new consensus key..."); + + // Determine the alias for the new key + let validator_alias = wallet + .find_alias_atomic(&validator) + .expect("Failed to read from the wallet storage."); + let base_consensus_key_alias = validator_alias + .map(|a| validator_consensus_key(&a)) + .unwrap_or_else(|| { + validator_consensus_key(&validator.to_string().into()) + }); + + let mut consensus_key_alias = + base_consensus_key_alias.to_string(); + let all_keys = wallet + .get_secret_keys_atomic() + .expect("Failed to read from the wallet storage."); + let mut key_counter = 0; + while all_keys.contains_key(&consensus_key_alias) { + key_counter += 1; + consensus_key_alias = + format!("{base_consensus_key_alias}-{key_counter}"); + } + let password = read_and_confirm_encryption_password( + args.unsafe_dont_encrypt, ); - safe_exit(1) - } - }) - .unwrap_or_else(|| { - display_line!(namada.io(), "Generating new consensus key..."); - let password = - read_and_confirm_encryption_password(args.unsafe_dont_encrypt); - wallet - .gen_store_secret_key_atomic( - // Note that TM only allows ed25519 for consensus key - SchemeType::Ed25519, - Some(consensus_key_alias.clone()), - args.tx.wallet_alias_force, - password, - &mut OsRng, - ) - .expect("Failed to update the wallet storage.") - .expect("Key generation should not fail.") - .1 - .ref_to() - }); + + let (alias, sk) = wallet + .gen_store_secret_key_atomic( + // Note that TM only allows ed25519 for consensus key + SchemeType::Ed25519, + Some(consensus_key_alias.clone()), + args.tx.wallet_alias_force, + password, + &mut OsRng, + ) + .expect("Failed to update the wallet storage.") + .expect("Key generation should not fail."); + (alias, sk.ref_to(), true) + }); // To avoid wallet deadlocks in following operations drop(wallet); let args = namada::sdk::args::ConsensusKeyChange { validator: validator.clone(), - consensus_key: Some(new_key.clone()), + consensus_key: Some(consensus_key.clone()), ..args }; @@ -384,29 +392,39 @@ pub async fn submit_change_consensus_key( tx::dump_tx(namada.io(), &args.tx, tx); } else { sign(namada, &mut tx, &args.tx, signing_data).await?; - let resp = namada.submit(tx, &args.tx).await?; - - if !args.tx.dry_run { - if resp.is_applied_and_valid().is_some() { - namada.wallet_mut().await.save().unwrap_or_else(|err| { - edisplay_line!(namada.io(), "{}", err) - }); + let resp = namada.submit(tx, &args.tx).await?; + if resp.is_applied_and_valid().is_some() { + if !args.tx.dry_run { + if is_consensus_key_generated { + display_line!( + namada.io(), + "New consensus key stored with alias \ + \"{consensus_key_alias}\".", + ); + } display_line!( namada.io(), - "New consensus key stored with alias \ - \"{consensus_key_alias}\". It will become active \ + "The new consensus key will become active \ {EPOCH_SWITCH_BLOCKS_DELAY} blocks before pipeline \ offset from the current epoch, at which point you'll \ need to give the new key to CometBFT in order to be able \ to sign with it in consensus.", ); + } else { + display_line!( + namada.io(), + "Transaction dry run. No new consensus key has been saved." + ); } } else { - display_line!( - namada.io(), - "Transaction dry run. No new consensus key has been saved." - ); + // revert the wallet changes: remove the generated key + if is_consensus_key_generated { + let mut wallet = namada.wallet_mut().await; + wallet + .remove_all_by_alias_atomic(consensus_key_alias) + .expect("Failed to update the wallet storage."); + } } } Ok(()) From ecf3cf4bd42398fa5db529927c1fae132379d173 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 26 Apr 2024 17:57:48 +0200 Subject: [PATCH 66/66] Remove old "save" calls --- crates/apps/src/lib/cli/wallet.rs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/crates/apps/src/lib/cli/wallet.rs b/crates/apps/src/lib/cli/wallet.rs index ae2fad5a9b..ce46f90ae1 100644 --- a/crates/apps/src/lib/cli/wallet.rs +++ b/crates/apps/src/lib/cli/wallet.rs @@ -240,9 +240,6 @@ fn shielded_key_derive( display_line!(io, "No changes are persisted. Exiting."); cli::safe_exit(1) }; - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a key and an address with alias: \"{}\"", @@ -313,9 +310,6 @@ fn shielded_key_gen( cli::safe_exit(1); }); - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a spending key with alias: \"{}\"", @@ -350,7 +344,6 @@ fn payment_address_gen( edisplay_line!(io, "Payment address not added"); cli::safe_exit(1); }); - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); display_line!( io, "Successfully generated payment address {} with alias {}", @@ -410,7 +403,6 @@ fn shielded_key_address_add( (alias, "payment address") } }; - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); display_line!( io, "Successfully added a {} with the following alias to wallet: {}", @@ -553,9 +545,6 @@ async fn transparent_key_and_address_derive( cli::safe_exit(1) }) }; - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a key and an address with alias: \"{}\"", @@ -631,9 +620,6 @@ fn transparent_key_and_address_gen( println!("No changes are persisted. Exiting."); cli::safe_exit(0); }); - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a key and an address with alias: \"{}\"", @@ -885,9 +871,6 @@ fn key_address_remove( wallet .remove_all_by_alias_atomic(alias.clone()) .expect("Failed to update the wallet storage."); - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!(io, "Successfully removed alias: \"{}\"", alias); } @@ -1445,9 +1428,6 @@ fn transparent_secret_key_add( display_line!(io, "No changes are persisted. Exiting."); cli::safe_exit(1); }); - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a key and an address with alias: \"{}\"", @@ -1479,9 +1459,6 @@ fn transparent_public_key_add( edisplay_line!(io, "Public key not added"); cli::safe_exit(1); } - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a public key with alias: \"{}\"", @@ -1507,9 +1484,6 @@ fn transparent_address_add( edisplay_line!(io, "Address not added"); cli::safe_exit(1); } - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added an address with alias: \"{}\"",