From fd98fdac89041aebe41a9b1f33043791c474ad3e Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sat, 11 Feb 2023 14:48:24 +0200 Subject: [PATCH] Now use WalletUtils as a backup for when alias/password parameters are not supplied. --- apps/src/bin/namada-wallet/cli.rs | 8 ++-- apps/src/lib/cli.rs | 4 +- apps/src/lib/cli/context.rs | 8 ++-- apps/src/lib/client/signing.rs | 3 +- apps/src/lib/node/ledger/shell/mod.rs | 2 +- apps/src/lib/wallet/mod.rs | 2 +- shared/src/ledger/args.rs | 2 + shared/src/ledger/signing.rs | 18 +++++--- shared/src/ledger/tx.rs | 5 ++- shared/src/ledger/wallet/mod.rs | 64 +++++++++++++++++++++------ wasm/checksums.json | 36 +++++++-------- 11 files changed, 102 insertions(+), 50 deletions(-) diff --git a/apps/src/bin/namada-wallet/cli.rs b/apps/src/bin/namada-wallet/cli.rs index 16d2e94f50..a3673b6399 100644 --- a/apps/src/bin/namada-wallet/cli.rs +++ b/apps/src/bin/namada-wallet/cli.rs @@ -84,7 +84,7 @@ fn address_key_find( println!("Viewing key: {}", viewing_key); if unsafe_show_secret { // Check if alias is also a spending key - match wallet.find_spending_key(&alias) { + match wallet.find_spending_key(&alias, None) { Ok(spending_key) => println!("Spending key: {}", spending_key), Err(FindKeyError::KeyNotFound) => {} Err(err) => eprintln!("{}", err), @@ -331,7 +331,7 @@ fn key_find( ) { let mut wallet = ctx.wallet; let found_keypair = match public_key { - Some(pk) => wallet.find_key_by_pk(&pk), + Some(pk) => wallet.find_key_by_pk(&pk, None), None => { let alias = alias.or(value); match alias { @@ -342,7 +342,7 @@ fn key_find( ); cli::safe_exit(1) } - Some(alias) => wallet.find_key(alias.to_lowercase()), + Some(alias) => wallet.find_key(alias.to_lowercase(), None), } } }; @@ -414,7 +414,7 @@ fn key_list( fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { let mut wallet = ctx.wallet; wallet - .find_key(alias.to_lowercase()) + .find_key(alias.to_lowercase(), None) .map(|keypair| { let file_data = keypair .try_to_vec() diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index d360ae1d35..5d91067509 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2813,6 +2813,7 @@ pub mod args { signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), signer: self.signer.map(|x| ctx.get(&x)), tx_code_path: ctx.read_wasm(self.tx_code_path), + password: self.password, } } } @@ -2877,10 +2878,10 @@ pub mod args { let fee_amount = GAS_AMOUNT.parse(matches); let fee_token = GAS_TOKEN.parse(matches); let gas_limit = GAS_LIMIT.parse(matches).into(); - let signing_key = SIGNING_KEY_OPT.parse(matches); let signer = SIGNER.parse(matches); let tx_code_path = PathBuf::from(TX_REVEAL_PK); + let password = None; Self { dry_run, force, @@ -2893,6 +2894,7 @@ pub mod args { signing_key, signer, tx_code_path, + password, } } } diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 6d05d3f513..f9d95bdb04 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -347,7 +347,7 @@ impl ArgFromMutContext for common::SecretKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it can be an alias ctx.wallet - .find_key(raw) + .find_key(raw, None) .map_err(|_find_err| format!("Unknown key {}", raw)) }) } @@ -364,13 +364,13 @@ impl ArgFromMutContext for common::PublicKey { // Or it can be a public key hash in hex string FromStr::from_str(raw) .map(|pkh: PublicKeyHash| { - let key = ctx.wallet.find_key_by_pkh(&pkh).unwrap(); + let key = ctx.wallet.find_key_by_pkh(&pkh, None).unwrap(); key.ref_to() }) // Or it can be an alias that may be found in the wallet .or_else(|_parse_err| { ctx.wallet - .find_key(raw) + .find_key(raw, None) .map(|x| x.ref_to()) .map_err(|x| x.to_string()) }) @@ -388,7 +388,7 @@ 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) + .find_spending_key(raw, None) .map_err(|_find_err| format!("Unknown spending key {}", raw)) }) } diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 422ba500be..33c1e6c69c 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -22,7 +22,8 @@ pub async fn find_keypair< wallet: &mut Wallet, addr: &Address, ) -> Result { - namada::ledger::signing::find_keypair::(client, wallet, addr).await + namada::ledger::signing::find_keypair::(client, wallet, addr, None) + .await } /// Given CLI arguments and some defaults, determine the rightful transaction diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 40fc42eb70..16a1f42c8f 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -653,7 +653,7 @@ where let pk = common::SecretKey::deserialize(&mut pk_bytes.as_slice()) .expect("Validator's public key should be deserializable") .ref_to(); - wallet.find_key_by_pk(&pk).expect( + wallet.find_key_by_pk(&pk, None).expect( "A validator's established keypair should be stored in its \ wallet", ) diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index de92f0c2ca..dbe76b7b35 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -112,7 +112,7 @@ pub fn gen_validator_keys( ) -> Result { let protocol_keypair = protocol_pk.map(|pk| { wallet - .find_key_by_pkh(&PublicKeyHash::from(&pk)) + .find_key_by_pkh(&PublicKeyHash::from(&pk), None) .ok() .or_else(|| { wallet diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 24dfb1fc4c..f4906b42c8 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -380,6 +380,8 @@ pub struct Tx { pub signer: Option, /// Path to the TX WASM code file pub tx_code_path: C::Data, + /// Password to decrypt key + pub password: Option, } /// MASP add key or address arguments diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index e170443696..940cc56598 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -12,7 +12,9 @@ use crate::types::storage::Epoch; use crate::types::transaction::{hash_tx, Fee, WrapperTx}; /// Find the public key for the given address and try to load the keypair -/// for it from the wallet. Errors if the key cannot be found or loaded. +/// for it 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 async fn find_keypair< C: crate::ledger::queries::Client + Sync, U: WalletUtils, @@ -20,6 +22,7 @@ pub async fn find_keypair< client: &C, wallet: &mut Wallet, addr: &Address, + password: Option, ) -> Result { match addr { Address::Established(_) => { @@ -33,7 +36,7 @@ pub async fn find_keypair< addr.encode() )), )?; - wallet.find_key_by_pk(&public_key).map_err(|err| { + wallet.find_key_by_pk(&public_key, password).map_err(|err| { Error::Other(format!( "Unable to load the keypair from the wallet for public \ key {}. Failed with: {}", @@ -42,7 +45,7 @@ pub async fn find_keypair< }) } Address::Implicit(ImplicitAddress(pkh)) => { - wallet.find_key_by_pkh(pkh).map_err(|err| { + wallet.find_key_by_pkh(pkh, password).map_err(|err| { Error::Other(format!( "Unable to load the keypair from the wallet for the \ implicit address {}. Failed with: {}", @@ -98,8 +101,13 @@ pub async fn tx_signer< TxSigningKey::WalletKeypair(signing_key) => Ok(signing_key), TxSigningKey::WalletAddress(signer) => { let signer = signer; - let signing_key = - find_keypair::(client, wallet, &signer).await?; + let signing_key = find_keypair::( + client, + wallet, + &signer, + args.password.clone(), + ) + .await?; // Check if the signer is implicit account that needs to reveal its // PK first if matches!(signer, Address::Implicit(_)) { diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 8bc0cb677d..bd1206fa1a 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -276,9 +276,10 @@ pub async fn submit_reveal_pk_aux< Ok(signing_key.clone()) } else if let Some(signer) = args.signer.as_ref() { let signer = signer; - find_keypair::(client, wallet, signer).await + find_keypair::(client, wallet, signer, args.password.clone()) + .await } else { - find_keypair::(client, wallet, &addr).await + find_keypair::(client, wallet, &addr, args.password.clone()).await }?; let epoch = rpc::query_epoch(client).await; let to_broadcast = if args.dry_run { diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index 921c139dd5..7515c1109a 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -6,6 +6,7 @@ mod store; use std::collections::HashMap; use std::fmt::Display; +use std::marker::PhantomData; use std::str::FromStr; pub use alias::Alias; @@ -43,6 +44,29 @@ pub trait WalletUtils { ) -> store::ConfirmationResponse; } +/// A degenerate implementation of wallet interactivity +pub struct SdkWalletUtils(PhantomData); + +impl WalletUtils for SdkWalletUtils { + type Storage = U; + + fn read_password(_prompt_msg: &str) -> String { + panic!("attempted to prompt for password in non-interactive mode"); + } + + fn read_alias(_prompt_msg: &str) -> String { + panic!("attempted to prompt for alias in non-interactive mode"); + } + + fn show_overwrite_confirmation( + _alias: &Alias, + _alias_for: &str, + ) -> store::ConfirmationResponse { + // Automatically replace aliases in non-interactive mode + store::ConfirmationResponse::Replace + } +} + /// The error that is produced when a given key cannot be obtained #[derive(Error, Debug)] pub enum FindKeyError { @@ -127,12 +151,13 @@ impl Wallet { } /// Find the stored key by an alias, a public key hash or a public key. - /// If the key is encrypted, will prompt for password from stdin. - /// Any keys that are decrypted are stored in and read from a cache to avoid - /// prompting for password multiple times. + /// 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_key( &mut self, alias_pkh_or_pk: impl AsRef, + password: Option, ) -> Result { // Try cache first if let Some(cached_key) = self @@ -150,13 +175,17 @@ impl Wallet { &mut self.decrypted_key_cache, stored_key, alias_pkh_or_pk.into(), + password, ) } - /// Find the spending key with the given alias in the wallet and return it + /// 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) = @@ -173,6 +202,7 @@ impl Wallet { &mut self.decrypted_spendkey_cache, stored_spendkey, alias.into(), + password, ) } @@ -196,12 +226,13 @@ impl Wallet { } /// Find the stored key by a public key. - /// If the key is encrypted, will prompt for password from stdin. - /// Any keys that are decrypted are stored in and read from a cache to avoid - /// prompting for password multiple times. + /// 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(); @@ -222,16 +253,18 @@ impl Wallet { &mut self.decrypted_key_cache, stored_key, alias, + password, ) } /// Find the stored key by a public key hash. - /// If the key is encrypted, will prompt for password from stdin. - /// Any keys that are decrypted are stored in and read from a cache to avoid - /// prompting for password multiple times. + /// 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 @@ -251,25 +284,30 @@ impl Wallet { &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, prompt for password from - /// stdin and if successfully decrypted, store it in a cache. + /// If a given storage key needs to be decrypted and password is not + /// supplied, then interactively prompt for password and if successfully + /// decrypted, store it in a cache. fn decrypt_stored_key< T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone, >( decrypted_key_cache: &mut HashMap, stored_key: &StoredKeypair, alias: Alias, + password: Option, ) -> Result where ::Err: Display, { match stored_key { StoredKeypair::Encrypted(encrypted) => { - let password = U::read_password("Enter decryption password: "); + let password = password.unwrap_or_else(|| { + U::read_password("Enter decryption password: ") + }); let key = encrypted .decrypt(password) .map_err(FindKeyError::KeyDecryptionError)?; diff --git a/wasm/checksums.json b/wasm/checksums.json index e93d1ab334..a99df0c8bc 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.8dd78cddd65198c913edd032d8e2d864d5995c57808f69a4108f307187f16fdc.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.0cd7df497599d34bc9c4f3614768e362e8d96e4169c2ee700060a5c9349c4ee0.wasm", - "tx_ibc.wasm": "tx_ibc.f67ddffe00c5c8273c17de95d25822c99c59427712c7ac114cd77086ef32adcc.wasm", - "tx_init_account.wasm": "tx_init_account.39a30513f8b5f7fa89c597c936ad69143c5e97c0cc4f0b11b49b2140c03c1735.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.313b6facdc6a8d350d7319bad7fc71b78ee14225739e4cd670bd6d07aac1faf4.wasm", - "tx_init_validator.wasm": "tx_init_validator.85ef7aac819377bc0e601feb265908fc0aad56df9b6f02138ad80e51eba68935.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.4863466a5464833bd1203411d4eb549cb87848f7e2c9c7b45f5c73e5122e022d.wasm", - "tx_transfer.wasm": "tx_transfer.ca670e5a21b23dc7d214fa8eff053cb2818067930a31f086621b4120a9a5f947.wasm", - "tx_unbond.wasm": "tx_unbond.f2aee3b86de8d5533d08ab3ede8cc7f333364a8a4221128e75ac6769afa46e8d.wasm", - "tx_update_vp.wasm": "tx_update_vp.077679787c5c2c67884503032295b13fe24a913cc32a03bd03bcb5c60b917ff9.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.d92d32836372561a12bde8d36100b63b135b6a7da0a7878dd4af9744a7e5c023.wasm", - "tx_withdraw.wasm": "tx_withdraw.5fd90d417a90695b6b5ce5f4662b6c6b4d5c24377caa2056868fdded4f7753c0.wasm", - "vp_implicit.wasm": "vp_implicit.f92f68a709ff250272f3c70125f2f8bf15eeccb0f463ec119b5b7c4203cb6541.wasm", - "vp_masp.wasm": "vp_masp.e5b2294b67928573d79fe44409c46e5a504633125461d0b1cd868b7889854f57.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.449e39c73fec113f3d2a5f3a0b7569933a89a0ffbff62ca5891851b036115b62.wasm", - "vp_token.wasm": "vp_token.533f6662665a1a2a59968cdc93f87e87f31dc6268075f83355880a6fdca406be.wasm", - "vp_user.wasm": "vp_user.b09918e5e083ffcfbde4710c75f9903b651ffcbe7beaf8e737250b4de3c9986d.wasm", - "vp_validator.wasm": "vp_validator.939832589c41e0b29c629cbca00de695fb882888d7fd9e84e8c42cb1daab4dde.wasm" + "tx_bond.wasm": "tx_bond.fd0584036bed91278878e939ca75d1d1eea1356b08652d9802a33ae7d40e63a1.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.7d8ebecc8001aa06b19bfb58852044ce5c928ba63ef0681532d329092b2c07d2.wasm", + "tx_ibc.wasm": "tx_ibc.621225d32e24f0a6a62d496d360398e0211b45d0b044e62ad776b667dae2e5b0.wasm", + "tx_init_account.wasm": "tx_init_account.05a2fd217ca9a8fca2a3b145b78fa4677cd6f236c7ef4090fe96803555923c68.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.5f8d4e0956dd908eb9a5a93fa8705ce57a3f0375acb57993e1ed88efdd3be75a.wasm", + "tx_init_validator.wasm": "tx_init_validator.79e1d78674a1ec7fdaab5adee0965222401625892eb0e75b1ce4b1d83f521d8b.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.0db1e35f022e3efbcc974409fcbe0ded8062a38fe015002b87c6da396688c28e.wasm", + "tx_transfer.wasm": "tx_transfer.047e3ab42ddeb3e045db4ca0e3c3896edaf392cf98563ac17e40ecd3d155c612.wasm", + "tx_unbond.wasm": "tx_unbond.e01b0032a40beaf212611a03240be463c855841a5d446425090b2438c71b2dc9.wasm", + "tx_update_vp.wasm": "tx_update_vp.e19d594ea1eced596b96b21ce4a8513be4f8859d807a80b3fbe57d4f67fe93fc.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.fcd7372363098e5905f133e48b713b186234640f575cf25100154c9ce1218507.wasm", + "tx_withdraw.wasm": "tx_withdraw.0fbc63d71a67e5c6d6c3bc1e25e99f1ebcdf4b9335fadf73c555984e05190e39.wasm", + "vp_implicit.wasm": "vp_implicit.a5798a2500270bfd286552f43e52abc3ba3a2844966595b2aa46d20c6b4a32db.wasm", + "vp_masp.wasm": "vp_masp.a055ca419be1fb826c912b1f37dc01a70e75d6b7672924bcfc8fe4eb6730e7db.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.0bbb333a4f6947d52c2c73b6d99abb4d482961e487a0dca72c6278bc1e883485.wasm", + "vp_token.wasm": "vp_token.9cd955a27b2bcc98849185faa35cdd3debe1573cdae08a841a04fddd86ef65c9.wasm", + "vp_user.wasm": "vp_user.27c17d97530284f1690273ef8affe1925153dedbf05d8556bfe9178be18ad866.wasm", + "vp_validator.wasm": "vp_validator.42db54ef509b4310cb984e04fa76ff1861775dfe54e6cb0187a072322d819df7.wasm" } \ No newline at end of file