From 3fe7ee9e6c142f7d5f9726916aaddd15afdf6eda Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Mon, 23 Sep 2024 11:49:55 +0200 Subject: [PATCH 01/11] Implemented MASP signing using the hardware wallet. --- Cargo.lock | 9 +- Cargo.toml | 6 +- crates/apps_lib/Cargo.toml | 1 + crates/apps_lib/src/cli/context.rs | 63 +++- crates/apps_lib/src/cli/wallet.rs | 70 ++++- crates/apps_lib/src/client/tx.rs | 289 +++++++++++++++++- .../src/config/genesis/transactions.rs | 1 + crates/benches/native_vps.rs | 13 +- crates/core/src/masp.rs | 12 +- crates/node/src/bench_utils.rs | 2 + crates/sdk/src/args.rs | 7 +- crates/sdk/src/lib.rs | 7 +- crates/sdk/src/signing.rs | 3 + crates/sdk/src/tx.rs | 22 +- crates/shielded_token/src/masp.rs | 23 +- .../src/masp/shielded_wallet.rs | 50 ++- crates/shielded_token/src/validation.rs | 4 +- crates/tests/src/integration/masp.rs | 1 + crates/token/src/lib.rs | 3 +- crates/tx/src/types.rs | 24 ++ crates/wallet/src/lib.rs | 29 +- crates/wallet/src/store.rs | 17 +- wasm/Cargo.lock | 6 +- wasm_for_tests/Cargo.lock | 6 +- 24 files changed, 578 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffd306a156..7d6ea5452e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4264,7 +4264,7 @@ dependencies = [ [[package]] name = "ledger-namada-rs" version = "0.0.1" -source = "git+https://github.com/Zondax/ledger-namada?tag=v0.0.24#ca9da5e0129f676934809f58acffbd855d280664" +source = "git+https://github.com/Zondax/ledger-namada?rev=f54b76adcc1430db0496e894ad72cd74cfb6eb88#f54b76adcc1430db0496e894ad72cd74cfb6eb88" dependencies = [ "bincode", "byteorder", @@ -4494,7 +4494,7 @@ dependencies = [ [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "arbitrary", "borsh", @@ -4508,7 +4508,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "aes", "arbitrary", @@ -4541,7 +4541,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "bellman", "blake2b_simd", @@ -4769,6 +4769,7 @@ dependencies = [ "futures", "git2", "itertools 0.12.1", + "jubjub 0.10.0 (git+https://github.com/heliaxdev/jubjub.git?rev=a373686962f4e9d0edb3b4716f86ff6bbd9aa86c)", "kdam", "lazy_static", "ledger-lib", diff --git a/Cargo.toml b/Cargo.toml index d62f7c0174..6ef70ad493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,15 +134,15 @@ konst = { version = "0.3.8", default-features = false } lazy_static = "1.4.0" # TODO: upstreamed in https://github.com/ledger-community/rust-ledger/pull/9 ledger-lib = { git = "https://github.com/heliaxdev/rust-ledger", rev = "f96f4559b3237d09218f7583df01acf36034ea79", default-features = false, features = ["transport_tcp"] } -ledger-namada-rs = { git = "https://github.com/Zondax/ledger-namada", tag = "v0.0.24" } +ledger-namada-rs = { git = "https://github.com/Zondax/ledger-namada", rev = "f54b76adcc1430db0496e894ad72cd74cfb6eb88" } ledger-transport = "0.10.0" ledger-transport-hid = "0.10.0" libc = "0.2.97" libloading = "0.7.2" linkme = "0.3.24" # branch = "tomas/arbitrary" -masp_primitives = { git = "https://github.com/anoma/masp", rev = "12ed8b060b295c06502a2ff8468e4a941cb7cca4" } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "12ed8b060b295c06502a2ff8468e4a941cb7cca4", default-features = false, features = ["local-prover"] } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56" } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56", default-features = false, features = ["local-prover"] } num256 = "0.3.5" num_cpus = "1.13.0" num-derive = "0.4" diff --git a/crates/apps_lib/Cargo.toml b/crates/apps_lib/Cargo.toml index c0fd42e600..9faaf7f38c 100644 --- a/crates/apps_lib/Cargo.toml +++ b/crates/apps_lib/Cargo.toml @@ -53,6 +53,7 @@ fd-lock.workspace = true flate2.workspace = true futures.workspace = true itertools.workspace = true +jubjub.workspace = true kdam.workspace = true lazy_static = { workspace = true, optional = true } linkme = { workspace = true, optional = true } diff --git a/crates/apps_lib/src/cli/context.rs b/crates/apps_lib/src/cli/context.rs index 16f4304f98..8cc2da6c78 100644 --- a/crates/apps_lib/src/cli/context.rs +++ b/crates/apps_lib/src/cli/context.rs @@ -6,6 +6,11 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use color_eyre::eyre::Result; +use masp_primitives::zip32::sapling::PseudoExtendedKey; +use masp_primitives::zip32::{ + ExtendedFullViewingKey as MaspExtendedViewingKey, + ExtendedSpendingKey as MaspExtendedSpendingKey, +}; use namada_core::masp::{ BalanceOwner, ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, TransferSource, TransferTarget, @@ -47,7 +52,7 @@ pub type WalletAddrOrNativeToken = FromContext; /// A raw extended spending key (bech32m encoding) or an alias of an extended /// spending key in the wallet -pub type WalletSpendingKey = FromContext; +pub type WalletSpendingKey = FromContext; /// A raw dated extended spending key (bech32m encoding) or an alias of an /// extended spending key in the wallet @@ -588,6 +593,48 @@ impl ArgFromMutContext for ExtendedSpendingKey { } } +impl ArgFromMutContext for PseudoExtendedKey { + fn arg_from_mut_ctx( + ctx: &mut ChainContext, + raw: impl AsRef, + ) -> Result { + let raw = raw.as_ref(); + // Either the string is a raw extended spending key + ExtendedSpendingKey::from_str(raw) + .map(|x| PseudoExtendedKey::from(MaspExtendedSpendingKey::from(x))) + .or_else(|_parse_err| { + ExtendedViewingKey::from_str(raw).map(|x| { + PseudoExtendedKey::from(MaspExtendedViewingKey::from(x)) + }) + }) + .or_else(|_parse_err| { + // Or it is a stored alias of one + ctx.wallet + .find_spending_key(raw, None) + .map(|k| { + PseudoExtendedKey::from(MaspExtendedSpendingKey::from( + k.key, + )) + }) + .map_err(|_find_err| { + format!("Unknown spending key {}", raw) + }) + }) + .or_else(|_parse_err| { + // Or it is a stored alias of one + ctx.wallet + .find_viewing_key(raw) + .copied() + .map(|k| { + PseudoExtendedKey::from(MaspExtendedViewingKey::from( + k.key, + )) + }) + .map_err(|_find_err| format!("Unknown viewing key {}", raw)) + }) + } +} + impl ArgFromMutContext for DatedSpendingKey { fn arg_from_mut_ctx( ctx: &mut ChainContext, @@ -666,8 +713,18 @@ impl ArgFromMutContext for TransferSource { Address::arg_from_ctx(ctx, raw) .map(Self::Address) .or_else(|_| { - ExtendedSpendingKey::arg_from_mut_ctx(ctx, raw) - .map(Self::ExtendedSpendingKey) + ExtendedSpendingKey::arg_from_mut_ctx(ctx, raw).map(|x| { + Self::ExtendedSpendingKey(PseudoExtendedKey::from( + MaspExtendedSpendingKey::from(x), + )) + }) + }) + .or_else(|_| { + ExtendedViewingKey::arg_from_mut_ctx(ctx, raw).map(|x| { + Self::ExtendedSpendingKey(PseudoExtendedKey::from( + MaspExtendedViewingKey::from(x), + )) + }) }) } } diff --git a/crates/apps_lib/src/cli/wallet.rs b/crates/apps_lib/src/cli/wallet.rs index e6a763f234..42f24a6b8d 100644 --- a/crates/apps_lib/src/cli/wallet.rs +++ b/crates/apps_lib/src/cli/wallet.rs @@ -8,7 +8,10 @@ use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use color_eyre::eyre::Result; use itertools::sorted; -use ledger_namada_rs::{BIP44Path, NamadaApp}; +use ledger_namada_rs::{BIP44Path, KeyResponse, NamadaApp, NamadaKeys}; +use ledger_transport_hid::hidapi::HidApi; +use ledger_transport_hid::TransportNativeHID; +use masp_primitives::zip32::ExtendedFullViewingKey; use namada_core::chain::BlockHeight; use namada_core::masp::{ExtendedSpendingKey, MaspValue, PaymentAddress}; use namada_sdk::address::{Address, DecodeError}; @@ -184,7 +187,7 @@ fn payment_addresses_list( } /// Derives a masp spending key from the mnemonic code in the wallet. -fn shielded_key_derive( +async fn shielded_key_derive( ctx: Context, io: &impl Io, args::KeyDerive { @@ -232,9 +235,56 @@ fn shielded_key_derive( }) .0 } else { - display_line!(io, "Not implemented."); - display_line!(io, "No changes are persisted. Exiting."); - cli::safe_exit(1) + let hidapi = HidApi::new().unwrap_or_else(|err| { + edisplay_line!(io, "Failed to create HidApi: {}", err); + cli::safe_exit(1) + }); + let app = NamadaApp::new( + TransportNativeHID::new(&hidapi).unwrap_or_else(|err| { + edisplay_line!(io, "Unable to connect to Ledger: {}", err); + cli::safe_exit(1) + }), + ); + let response = app + .retrieve_keys( + &BIP44Path { + path: derivation_path.to_string(), + }, + NamadaKeys::ViewKey, + true, + ) + .await + .unwrap_or_else(|err| { + edisplay_line!( + io, + "Unable to connect to query address and public key from \ + Ledger: {}", + err + ); + cli::safe_exit(1) + }); + let KeyResponse::ViewKey(response_key) = response else { + edisplay_line!(io, "Unexpected response from Ledger"); + cli::safe_exit(1) + }; + let xfvk = ExtendedFullViewingKey::try_from_slice(&response_key.xfvk) + .expect( + "unable to decode extended full viewing key from the hardware \ + wallet", + ); + + wallet + .insert_viewing_key( + alias, + xfvk.into(), + birthday, + alias_force, + Some(derivation_path), + ) + .unwrap_or_else(|| { + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }) }; wallet .save() @@ -367,7 +417,13 @@ fn shielded_key_address_add( let (alias, typ) = match masp_value { MaspValue::FullViewingKey(viewing_key) => { let alias = wallet - .insert_viewing_key(alias, viewing_key, birthday, alias_force) + .insert_viewing_key( + alias, + viewing_key, + birthday, + alias_force, + None, + ) .unwrap_or_else(|| { edisplay_line!(io, "Viewing key not added"); cli::safe_exit(1); @@ -638,7 +694,7 @@ async fn key_derive( if !args_key_derive.shielded { transparent_key_and_address_derive(ctx, io, args_key_derive).await } else { - shielded_key_derive(ctx, io, args_key_derive) + shielded_key_derive(ctx, io, args_key_derive).await } } diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 1ffae47901..35cbf4a80c 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -3,10 +3,19 @@ use std::io::Write; use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; -use ledger_namada_rs::{BIP44Path, NamadaApp}; +use ledger_namada_rs::{BIP44Path, KeyResponse, NamadaApp, NamadaKeys}; +use masp_primitives::sapling::redjubjub::PrivateKey; +use masp_primitives::sapling::{redjubjub, ProofGenerationKey}; +use masp_primitives::transaction::components::sapling; +use masp_primitives::transaction::components::sapling::builder::{ + BuildParams, ConvertBuildParams, OutputBuildParams, RngBuildParams, + SpendBuildParams, StoredBuildParams, +}; +use masp_primitives::transaction::components::sapling::fees::InputView; +use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedKey}; use namada_sdk::address::{Address, ImplicitAddress}; use namada_sdk::args::TxBecomeValidator; -use namada_sdk::collections::HashSet; +use namada_sdk::collections::{HashMap, HashSet}; use namada_sdk::governance::cli::onchain::{ DefaultProposal, PgfFundingProposal, PgfStewardProposal, }; @@ -19,7 +28,7 @@ use namada_sdk::tx::data::compute_inner_tx_hash; use namada_sdk::tx::{CompressedAuthorization, Section, Signer, Tx}; use namada_sdk::wallet::alias::{validator_address, validator_consensus_key}; use namada_sdk::wallet::{Wallet, WalletIo}; -use namada_sdk::{error, signing, tx, Namada}; +use namada_sdk::{error, signing, tx, ExtendedViewingKey, Namada}; use rand::rngs::OsRng; use tokio::sync::RwLock; @@ -816,15 +825,285 @@ pub async fn submit_transparent_transfer( Ok(()) } +// A mapper that replaces authorization signatures with those in a built-in map +struct MapSaplingSigAuth( + HashMap::AuthSig>, +); + +impl sapling::MapAuth + for MapSaplingSigAuth +{ + fn map_proof( + &self, + p: ::Proof, + _pos: usize, + ) -> ::Proof { + p + } + + fn map_auth_sig( + &self, + s: ::AuthSig, + pos: usize, + ) -> ::AuthSig { + self.0.get(&pos).cloned().unwrap_or(s) + } + + fn map_authorization(&self, a: sapling::Authorized) -> sapling::Authorized { + a + } +} + pub async fn submit_shielded_transfer( namada: &impl Namada, - args: args::TxShieldedTransfer, + mut args: args::TxShieldedTransfer, ) -> Result<(), error::Error> { - let (mut tx, signing_data) = args.clone().build(namada).await?; + // Records the shielded keys that are on the hardware wallet + let mut shielded_hw_keys = HashMap::new(); + // Construct the build parameters that parameterized the Transaction + // authorizations + let mut bparams: Box = if args.tx.use_device { + let transport = WalletTransport::from_arg(args.tx.device_transport); + let app = NamadaApp::new(transport); + // Clear hardware wallet randomness buffers + app.clean_randomness_buffers().await.map_err(|err| { + error::Error::Other(format!( + "Unable to clear randomness buffer. Error: {}", + err, + )) + })?; + let wallet = namada.wallet().await; + // Augment the pseudo spending key with a proof authorization key + for data in &mut args.data { + // Only attempt an augmentation if proof authorization is not there + if data.source.to_spending_key().is_none() { + // First find the derivation path corresponding to this viewing + // key + let viewing_key = + ExtendedViewingKey::from(data.source.to_viewing_key()); + let path = wallet + .find_path_by_viewing_key(&viewing_key) + .map_err(|err| { + error::Error::Other(format!( + "Unable to find derivation path from the wallet \ + for viewing key {}. Error: {}", + viewing_key, err, + )) + })?; + let path = BIP44Path { + path: path.to_string(), + }; + // Then confirm that the viewing key at this path in the + // hardware wallet matches the viewing key in this pseudo + // spending key + let response = app + .retrieve_keys(&path, NamadaKeys::ViewKey, true) + .await + .map_err(|err| { + error::Error::Other(format!( + "Unable to obtain viewing key from the hardware \ + wallet at path {}. Error: {}", + path.path, err, + )) + })?; + let KeyResponse::ViewKey(response_key) = response else { + return Err(error::Error::Other( + "Unexpected response from Ledger".to_string(), + )); + }; + let xfvk = + ExtendedFullViewingKey::try_from_slice(&response_key.xfvk) + .expect( + "unable to decode extended full viewing key from \ + the hardware wallet", + ); + if ExtendedFullViewingKey::from(viewing_key) != xfvk { + return Err(error::Error::Other(format!( + "Unexpected viewing key response from Ledger: {}", + ExtendedViewingKey::from(xfvk), + ))); + } + // Then obtain the proof authorization key at this path in the + // hardware wallet + let response = app + .retrieve_keys(&path, NamadaKeys::ProofGenerationKey, false) + .await + .map_err(|err| { + error::Error::Other(format!( + "Unable to obtain proof generation key from the \ + hardware wallet for viewing key {}. Error: {}", + viewing_key, err, + )) + })?; + let KeyResponse::ProofGenKey(response_key) = response else { + return Err(error::Error::Other( + "Unexpected response from Ledger".to_string(), + )); + }; + let pgk = ProofGenerationKey::try_from_slice( + &[response_key.ak, response_key.nsk].concat(), + ) + .map_err(|err| { + error::Error::Other(format!( + "Unexpected proof generation key in response from the \ + hardware wallet: {}.", + err, + )) + })?; + // Augment the pseudo spending key + data.source.augment_proof_generation_key(pgk).map_err( + |_| { + error::Error::Other( + "Proof generation key in response from the \ + hardware wallet does not correspond to stored \ + viewing key." + .to_string(), + ) + }, + )?; + // Finally, augment an incorrect spend authorization key just to + // make sure that the Transaction is built. + data.source.augment_spend_authorizing_key_unchecked( + PrivateKey(jubjub::Fr::default()), + ); + shielded_hw_keys.insert(path.path, viewing_key); + } + } + // Get randomness to aid in construction of various descriptors + let mut bparams = StoredBuildParams::default(); + // Number of spend descriptions is the number of transfers + let spend_len = args.data.len(); + // Number of convert description is assumed to be double the number of + // transfers. This is because each spend description might first be + // converted to epoch 0 before going to the intended epoch. + let convert_len = args.data.len() * 2; + // Number of output descriptions is assumed to be double the number of + // transfers. This is because there may be change from each output + // that's destined for the sender. + let output_len = args.data.len() * 2; + for _ in 0..spend_len { + let spend_randomness = app + .get_spend_randomness() + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + bparams.spend_params.push(SpendBuildParams { + rcv: jubjub::Fr::from_bytes(&spend_randomness.rcv).unwrap(), + alpha: jubjub::Fr::from_bytes(&spend_randomness.alpha).unwrap(), + ..SpendBuildParams::default() + }); + } + for _ in 0..convert_len { + let convert_randomness = app + .get_convert_randomness() + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + bparams.convert_params.push(ConvertBuildParams { + rcv: jubjub::Fr::from_bytes(&convert_randomness.rcv).unwrap(), + }); + } + for _ in 0..output_len { + let output_randomness = app + .get_output_randomness() + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + bparams.output_params.push(OutputBuildParams { + rcv: jubjub::Fr::from_bytes(&output_randomness.rcv).unwrap(), + rseed: output_randomness.rcm, + ..OutputBuildParams::default() + }); + } + Box::new(bparams) + } else { + Box::new(RngBuildParams::new(OsRng)) + }; + let (mut tx, signing_data) = + args.clone().build(namada, &mut bparams).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { + // Get the MASP section that is the target of our signing + if let Some(shielded_hash) = signing_data.shielded_hash { + let mut masp_tx = tx + .get_masp_section(&shielded_hash) + .expect("Expected to find the indicated MASP Transaction") + .clone(); + + let masp_builder = tx + .get_masp_builder(&shielded_hash) + .expect("Expected to find the indicated MASP Builder"); + + // Reverse the spend metadata to enable looking up construction + // material + let sapling_inputs = masp_builder.builder.sapling_inputs(); + let mut descriptor_map = vec![0; sapling_inputs.len()]; + for i in 0.. { + if let Some(pos) = masp_builder.metadata.spend_index(i) { + descriptor_map[pos] = i; + } else { + break; + }; + } + // Sign the MASP Transaction using each relevant key in the + // hardware wallet + let mut app = None; + for (path, vk) in shielded_hw_keys { + // Initialize the Ledger app interface if it is uninitialized + let app = app.get_or_insert_with(|| { + NamadaApp::new(WalletTransport::from_arg( + args.tx.device_transport, + )) + }); + // Sign the MASP Transaction using the current viewing key + let path = BIP44Path { + path: path.to_string(), + }; + app.sign_masp_spends(&path, &tx.serialize_to_vec()) + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + // Now prepare a new list of authorizations based on hardware + // wallet responses + let mut authorizations = HashMap::new(); + for (tx_pos, builder_pos) in descriptor_map.iter().enumerate() { + // Read the next spend authorization signature from the + // hardware wallet + let response = app + .get_spend_signature() + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + let signature = redjubjub::Signature::try_from_slice( + &[response.rbar, response.sbar].concat(), + ) + .map_err(|err| { + error::Error::Other(format!( + "Unexpected spend authorization key in response \ + from the hardware wallet: {}.", + err, + )) + })?; + if *sapling_inputs[*builder_pos].key() + == ExtendedFullViewingKey::from(vk) + { + // If this descriptor was produced by the current + // viewing key (which comes from the hardware wallet), + // then use the authorization from the hardware wallet + authorizations.insert(tx_pos, signature); + } + } + // Finally, patch the MASP Transaction with the fetched spend + // authorization signature + masp_tx = (*masp_tx).clone().map_authorization::( + (), + MapSaplingSigAuth(authorizations), + ).freeze().map_err(|err| error::Error::Other(format!( + "Unable to apply hardware walleet sourced authorization \ + signatures to the transaction being constructed: {}.", + err, + )))?; + } + tx.remove_masp_section(&shielded_hash); + tx.add_section(Section::MaspTx(masp_tx)); + } sign(namada, &mut tx, &args.tx, signing_data).await?; namada.submit(tx, &args.tx).await?; } diff --git a/crates/apps_lib/src/config/genesis/transactions.rs b/crates/apps_lib/src/config/genesis/transactions.rs index 4ab224db20..3d64a1b010 100644 --- a/crates/apps_lib/src/config/genesis/transactions.rs +++ b/crates/apps_lib/src/config/genesis/transactions.rs @@ -761,6 +761,7 @@ impl Signed { public_keys: pks.clone(), threshold, fee_payer: genesis_fee_payer_pk(), + shielded_hash: None, }; let mut tx = self.data.tx_to_sign(); diff --git a/crates/benches/native_vps.rs b/crates/benches/native_vps.rs index f2350ca722..6e12b24523 100644 --- a/crates/benches/native_vps.rs +++ b/crates/benches/native_vps.rs @@ -10,6 +10,7 @@ use masp_primitives::sapling::Node; use masp_primitives::transaction::sighash::{signature_hash, SignableInput}; use masp_primitives::transaction::txid::TxIdDigester; use masp_primitives::transaction::TransactionData; +use masp_primitives::zip32::ExtendedSpendingKey; use masp_proofs::group::GroupEncoding; use masp_proofs::sapling::BatchValidator; use namada_apps_lib::address::{self, Address, InternalAddress}; @@ -410,7 +411,9 @@ fn prepare_ibc_tx_and_ctx(bench_name: &str) -> (BenchShieldedCtx, BatchedTx) { shielded_ctx.shell.write().commit_block(); shielded_ctx.generate_shielded_action( Amount::native_whole(10), - TransferSource::ExtendedSpendingKey(albert_spending_key.key), + TransferSource::ExtendedSpendingKey( + ExtendedSpendingKey::from(albert_spending_key.key).into(), + ), defaults::bertha_address().to_string(), ) } @@ -602,12 +605,16 @@ fn setup_storage_for_masp_verification( ), "unshielding" => shielded_ctx.generate_masp_tx( amount, - TransferSource::ExtendedSpendingKey(albert_spending_key.key), + TransferSource::ExtendedSpendingKey( + ExtendedSpendingKey::from(albert_spending_key.key).into(), + ), TransferTarget::Address(defaults::albert_address()), ), "shielded" => shielded_ctx.generate_masp_tx( amount, - TransferSource::ExtendedSpendingKey(albert_spending_key.key), + TransferSource::ExtendedSpendingKey( + ExtendedSpendingKey::from(albert_spending_key.key).into(), + ), TransferTarget::PaymentAddress(bertha_payment_addr), ), _ => panic!("Unexpected bench test"), diff --git a/crates/core/src/masp.rs b/crates/core/src/masp.rs index ce9d7967c9..ca00a4b7be 100644 --- a/crates/core/src/masp.rs +++ b/crates/core/src/masp.rs @@ -11,6 +11,7 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::sapling::ViewingKey; use masp_primitives::transaction::TransparentAddress; pub use masp_primitives::transaction::TxId as TxIdInner; +use masp_primitives::zip32::{ExtendedKey, PseudoExtendedKey}; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; @@ -67,7 +68,7 @@ pub struct MaspTxId( serialize_with = "serialize_txid", deserialize_with = "deserialize_txid" )] - TxIdInner, + pub TxIdInner, ); impl From for MaspTxId { @@ -518,12 +519,13 @@ impl<'de> serde::Deserialize<'de> for ExtendedSpendingKey { } /// Represents a source of funds for a transfer +#[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub enum TransferSource { /// A transfer coming from a transparent address Address(Address), /// A transfer coming from a shielded address - ExtendedSpendingKey(ExtendedSpendingKey), + ExtendedSpendingKey(PseudoExtendedKey), } impl TransferSource { @@ -538,7 +540,7 @@ impl TransferSource { } /// Get the contained ExtendedSpendingKey contained, if any - pub fn spending_key(&self) -> Option { + pub fn spending_key(&self) -> Option { match self { Self::ExtendedSpendingKey(x) => Some(*x), _ => None, @@ -566,7 +568,9 @@ impl Display for TransferSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Address(x) => x.fmt(f), - Self::ExtendedSpendingKey(x) => x.fmt(f), + Self::ExtendedSpendingKey(x) => { + ExtendedViewingKey::from(x.to_viewing_key()).fmt(f) + } } } } diff --git a/crates/node/src/bench_utils.rs b/crates/node/src/bench_utils.rs index e8224f4a24..d86dee1ad0 100644 --- a/crates/node/src/bench_utils.rs +++ b/crates/node/src/bench_utils.rs @@ -14,6 +14,7 @@ use std::sync::{Arc, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; +use masp_primitives::transaction::components::sapling::builder::RngBuildParams; use masp_primitives::transaction::Transaction; use masp_primitives::zip32::ExtendedFullViewingKey; use masp_proofs::prover::LocalTxProver; @@ -1223,6 +1224,7 @@ impl BenchShieldedCtx { None, expiration, true, + &mut RngBuildParams::new(OsRng), ) .await }) diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index f7349c40ea..7ecfd966d5 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -5,6 +5,8 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Duration as StdDuration; +use masp_primitives::transaction::components::sapling::builder::BuildParams; +use masp_primitives::zip32::PseudoExtendedKey; use namada_core::address::Address; use namada_core::chain::{BlockHeight, ChainId, Epoch}; use namada_core::collections::HashMap; @@ -121,7 +123,7 @@ impl NamadaTypes for SdkTypes { type MaspIndexerAddress = String; type PaymentAddress = namada_core::masp::PaymentAddress; type PublicKey = namada_core::key::common::PublicKey; - type SpendingKey = namada_core::masp::ExtendedSpendingKey; + type SpendingKey = PseudoExtendedKey; type TendermintAddress = tendermint_rpc::Url; type TransferSource = namada_core::masp::TransferSource; type TransferTarget = namada_core::masp::TransferTarget; @@ -381,8 +383,9 @@ impl TxShieldedTransfer { pub async fn build( &mut self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { - tx::build_shielded_transfer(context, self).await + tx::build_shielded_transfer(context, self, bparams).await } } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index f3f0e2284b..c016902b38 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -45,6 +45,7 @@ use std::path::PathBuf; use std::str::FromStr; use args::{DeviceTransport, InputAmount, SdkTypes}; +use masp_primitives::zip32::PseudoExtendedKey; use namada_core::address::Address; use namada_core::collections::HashSet; use namada_core::dec::Dec; @@ -171,7 +172,7 @@ pub trait Namada: NamadaIo { fn new_shielded_transfer( &self, data: Vec, - gas_spending_keys: Vec, + gas_spending_keys: Vec, disposable_signing_key: bool, ) -> args::TxShieldedTransfer { args::TxShieldedTransfer { @@ -202,9 +203,9 @@ pub trait Namada: NamadaIo { /// arguments fn new_unshielding_transfer( &self, - source: ExtendedSpendingKey, + source: PseudoExtendedKey, data: Vec, - gas_spending_keys: Vec, + gas_spending_keys: Vec, disposable_signing_key: bool, ) -> args::TxUnshieldingTransfer { args::TxUnshieldingTransfer { diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index 8d1c058489..d396e2b444 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -73,6 +73,8 @@ pub struct SigningTxData { pub account_public_keys_map: Option, /// The public keys of the fee payer pub fee_payer: common::PublicKey, + /// ID of the Transaction needing signing + pub shielded_hash: Option, } /// Find the public key for the given address and try to load the keypair @@ -395,6 +397,7 @@ pub async fn aux_signing_data( threshold, account_public_keys_map, fee_payer, + shielded_hash: None, }) } diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 9e74fd3cc5..a5e47be354 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -10,6 +10,9 @@ use borsh::BorshSerialize; use borsh_ext::BorshSerializeExt; use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::builder::Builder; +use masp_primitives::transaction::components::sapling::builder::{ + BuildParams, RngBuildParams, +}; use masp_primitives::transaction::components::sapling::fees::{ ConvertView, InputView as SaplingInputView, OutputView as SaplingOutputView, }; @@ -18,6 +21,7 @@ use masp_primitives::transaction::components::transparent::fees::{ }; use masp_primitives::transaction::components::I128Sum; use masp_primitives::transaction::{builder, Transaction as MaspTransaction}; +use masp_primitives::zip32::PseudoExtendedKey; use namada_account::{InitAccount, UpdateAccount}; use namada_core::address::{Address, IBC, MASP}; use namada_core::arith::checked; @@ -38,9 +42,7 @@ use namada_core::ibc::core::client::types::Height as IbcHeight; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_core::ibc::primitives::Timestamp as IbcTimestamp; use namada_core::key::{self, *}; -use namada_core::masp::{ - AssetData, ExtendedSpendingKey, MaspEpoch, TransferSource, TransferTarget, -}; +use namada_core::masp::{AssetData, MaspEpoch, TransferSource, TransferTarget}; use namada_core::storage; use namada_core::time::DateTimeUtc; use namada_governance::cli::onchain::{ @@ -2629,6 +2631,7 @@ pub async fn build_ibc_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + &mut RngBuildParams::new(OsRng), ) .await?; let shielded_tx_epoch = shielded_parts.as_ref().map(|trans| trans.0.epoch); @@ -3030,8 +3033,9 @@ pub async fn build_transparent_transfer( pub async fn build_shielded_transfer( context: &N, args: &mut args::TxShieldedTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData)> { - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, Some(MASP), @@ -3094,6 +3098,7 @@ pub async fn build_shielded_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielded transfer must have shielded parts"); @@ -3123,6 +3128,7 @@ pub async fn build_shielded_transfer( }); data.shielded_section_hash = Some(section_hash); + signing_data.shielded_hash = Some(section_hash); tracing::debug!("Transfer data {data:?}"); Ok(()) }; @@ -3147,7 +3153,7 @@ async fn get_masp_fee_payment_amount( args: &args::Tx, fee_amount: DenominatedAmount, fee_payer: &common::PublicKey, - gas_spending_keys: Vec, + gas_spending_keys: Vec, ) -> Result> { let fee_payer_address = Address::from(fee_payer); let balance_key = balance_key(&args.fee_token, &fee_payer_address); @@ -3261,6 +3267,7 @@ pub async fn build_shielding_transfer( None, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + &mut RngBuildParams::new(OsRng), ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3383,6 +3390,7 @@ pub async fn build_unshielding_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + &mut RngBuildParams::new(OsRng), ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3436,6 +3444,7 @@ async fn construct_shielded_parts( fee_data: Option, update_ctx: bool, expiration: Option, + bparams: &mut impl BuildParams, ) -> Result)>> { // Precompute asset types to increase chances of success in decoding let token_map = context.wallet().await.get_addresses(); @@ -3449,7 +3458,7 @@ async fn construct_shielded_parts( shielded .gen_shielded_transfer( - context, data, fee_data, expiration, update_ctx, + context, data, fee_data, expiration, update_ctx, bparams, ) .await }; @@ -3816,6 +3825,7 @@ pub async fn gen_ibc_shielding_transfer( None, args.expiration.to_datetime(), true, + &mut RngBuildParams::new(OsRng), ) .await .map_err(|err| TxSubmitError::MaspError(err.to_string()))? diff --git a/crates/shielded_token/src/masp.rs b/crates/shielded_token/src/masp.rs index cc1fc79fbb..c95ae9ac74 100644 --- a/crates/shielded_token/src/masp.rs +++ b/crates/shielded_token/src/masp.rs @@ -26,7 +26,8 @@ use masp_primitives::transaction::components::sapling::builder::SaplingMetadata; use masp_primitives::transaction::components::{I128Sum, ValueSum}; use masp_primitives::transaction::Transaction; use masp_primitives::zip32::{ - ExtendedFullViewingKey, ExtendedSpendingKey as MaspExtendedSpendingKey, + ExtendedFullViewingKey, ExtendedKey, + ExtendedSpendingKey as MaspExtendedSpendingKey, PseudoExtendedKey, }; use masp_proofs::prover::LocalTxProver; use namada_core::address::Address; @@ -81,7 +82,7 @@ pub struct ShieldedTransfer { #[allow(missing_docs)] #[derive(Debug)] pub struct MaspFeeData { - pub sources: Vec, + pub sources: Vec, pub target: Address, pub token: Address, pub amount: token::DenominatedAmount, @@ -131,7 +132,7 @@ pub struct MaspTxReorderedData { /// Data about the unspent amounts for any given shielded source coming from the /// spent notes in their posses that have been added to the builder. Can be used /// to either pay fees or to return a change -pub type Changes = HashMap; +pub type Changes = HashMap; /// Shielded pool data for a token #[allow(missing_docs)] @@ -169,20 +170,20 @@ pub struct WalletMap; impl masp_primitives::transaction::components::sapling::builder::MapBuilder< P1, - MaspExtendedSpendingKey, + PseudoExtendedKey, (), ExtendedFullViewingKey, > for WalletMap { fn map_params(&self, _s: P1) {} - fn map_key(&self, s: MaspExtendedSpendingKey) -> ExtendedFullViewingKey { - (&s).into() + fn map_key(&self, s: PseudoExtendedKey) -> ExtendedFullViewingKey { + s.to_viewing_key() } } impl - MapBuilder + MapBuilder for WalletMap { fn map_notifier(&self, _s: N1) {} @@ -826,7 +827,7 @@ pub mod testing { mut rng in arb_rng().prop_map(TestCsprng), bparams_rng in arb_rng().prop_map(TestCsprng), prover_rng in arb_rng().prop_map(TestCsprng), - ) -> (MaspExtendedSpendingKey, Diversifier, Note, Node) { + ) -> (PseudoExtendedKey, Diversifier, Note, Node) { let mut spending_key_seed = [0; 32]; rng.fill_bytes(&mut spending_key_seed); let spending_key = MaspExtendedSpendingKey::master(spending_key_seed.as_ref()); @@ -837,7 +838,7 @@ pub mod testing { .to_payment_address(div) .expect("a PaymentAddress"); - let mut builder = Builder::::new( + let mut builder = Builder::::new( NETWORK, // NOTE: this is going to add 20 more blocks to the actual // expiration but there's no other exposed function that we could @@ -871,7 +872,7 @@ pub mod testing { assert_eq!(payment_addr, pa); // Make a path to out new note let node = Node::new(shielded_output.cmu.to_repr()); - (spending_key, div, note, node) + (PseudoExtendedKey::from(spending_key), div, note, node) } } @@ -916,7 +917,7 @@ pub mod testing { ).unwrap(), *value, )).collect::>() - ) -> Vec<(MaspExtendedSpendingKey, Diversifier, Note, Node)> { + ) -> Vec<(PseudoExtendedKey, Diversifier, Note, Node)> { spend_description } } diff --git a/crates/shielded_token/src/masp/shielded_wallet.rs b/crates/shielded_token/src/masp/shielded_wallet.rs index 470e197995..d54b5279bf 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -18,13 +18,13 @@ use masp_primitives::sapling::{ Diversifier, Node, Note, Nullifier, ViewingKey, }; use masp_primitives::transaction::builder::Builder; -use masp_primitives::transaction::components::sapling::builder::RngBuildParams; +use masp_primitives::transaction::components::sapling::builder::BuildParams; use masp_primitives::transaction::components::{ I128Sum, TxOut, U64Sum, ValueSum, }; use masp_primitives::transaction::fees::fixed::FeeRule; use masp_primitives::transaction::{builder, Transaction}; -use masp_primitives::zip32::ExtendedSpendingKey as MaspExtendedSpendingKey; +use masp_primitives::zip32::{ExtendedKey, PseudoExtendedKey}; use namada_core::address::Address; use namada_core::arith::checked; use namada_core::borsh::{BorshDeserialize, BorshSerialize}; @@ -51,12 +51,11 @@ use rand_core::{OsRng, SeedableRng}; use crate::masp::utils::MaspClient; use crate::masp::{ - cloned_pair, is_amount_required, to_viewing_key, Changes, - ContextSyncStatus, Conversions, MaspAmount, MaspDataLog, MaspFeeData, - MaspSourceTransferData, MaspTargetTransferData, MaspTransferData, - MaspTxReorderedData, NoteIndex, ShieldedSyncConfig, ShieldedTransfer, - ShieldedUtils, SpentNotesTracker, TransferErr, WalletMap, WitnessMap, - NETWORK, + cloned_pair, is_amount_required, Changes, ContextSyncStatus, Conversions, + MaspAmount, MaspDataLog, MaspFeeData, MaspSourceTransferData, + MaspTargetTransferData, MaspTransferData, MaspTxReorderedData, NoteIndex, + ShieldedSyncConfig, ShieldedTransfer, ShieldedUtils, SpentNotesTracker, + TransferErr, WalletMap, WitnessMap, NETWORK, }; #[cfg(any(test, feature = "testing"))] use crate::masp::{testing, ENV_VAR_MASP_TEST_SEED}; @@ -689,7 +688,7 @@ pub trait ShieldedApi: &mut self, context: &impl NamadaIo, spent_notes: &mut SpentNotesTracker, - sk: namada_core::masp::ExtendedSpendingKey, + sk: PseudoExtendedKey, is_native_token: bool, target: I128Sum, target_epoch: MaspEpoch, @@ -702,7 +701,7 @@ pub trait ShieldedApi: ), eyre::Error, > { - let vk = &to_viewing_key(&sk.into()).vk; + let vk = &sk.to_viewing_key().fvk.vk; // TODO: we should try to use the smallest notes possible to fund the // transaction to allow people to fetch less often // Establish connection with which to do exchange rate queries @@ -907,6 +906,7 @@ pub trait ShieldedApi: fee_data: Option, expiration: Option, update_ctx: bool, + bparams: &mut impl BuildParams, ) -> Result, TransferErr> { // Determine epoch in which to submit potential shielded transaction let epoch = Self::query_masp_epoch(context.client()) @@ -979,7 +979,7 @@ pub trait ShieldedApi: u32::MAX - 20 } }; - let mut builder = Builder::::new( + let mut builder = Builder::::new( NETWORK, // NOTE: this is going to add 20 more blocks to the actual // expiration but there's no other exposed function that we could @@ -1082,7 +1082,7 @@ pub trait ShieldedApi: &prover, &FeeRule::non_standard(U64Sum::zero()), &mut rng, - &mut RngBuildParams::new(OsRng), + bparams, ) .map_err(|error| TransferErr::Build { error, data: None })?; @@ -1187,7 +1187,7 @@ pub trait ShieldedApi: async fn add_inputs( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source: &TransferSource, token: &Address, amount: &DenominatedAmount, @@ -1244,12 +1244,7 @@ pub trait ShieldedApi: // Commit the notes found to our transaction for (diversifier, note, merkle_path) in unspent_notes { builder - .add_sapling_spend( - sk.into(), - diversifier, - note, - merkle_path, - ) + .add_sapling_spend(sk, diversifier, note, merkle_path) .map_err(|e| TransferErr::Build { error: builder::Error::SaplingBuild(e), data: None, @@ -1316,7 +1311,7 @@ pub trait ShieldedApi: async fn add_outputs( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source: TransferSource, target: &TransferTarget, token: Address, @@ -1362,9 +1357,8 @@ pub trait ShieldedApi: let contr = std::cmp::min(u128::from(*rem_amount), val) as u64; // If we are sending to a shielded address, we need the outgoing // viewing key in the following computations. - let ovk_opt = source - .spending_key() - .map(|x| MaspExtendedSpendingKey::from(x).expsk.ovk); + let ovk_opt = + source.spending_key().map(|x| x.to_viewing_key().fvk.ovk); // Make transaction output tied to the current token, // denomination, and epoch. if let Some(pa) = payment_address { @@ -1457,9 +1451,9 @@ pub trait ShieldedApi: async fn add_fees( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source_data: &HashMap, - sources: Vec, + sources: Vec, target: &Address, token: &Address, amount: &DenominatedAmount, @@ -1699,17 +1693,17 @@ pub trait ShieldedApi: #[allow(clippy::result_large_err)] #[allow(async_fn_in_trait)] fn add_changes( - builder: &mut Builder, + builder: &mut Builder, changes: Changes, ) -> Result<(), TransferErr> { for (sp, changes) in changes.into_iter() { for (asset_type, amt) in changes.components() { if let Ordering::Greater = amt.cmp(&0) { - let sk = MaspExtendedSpendingKey::from(sp.to_owned()); + let sk = sp.to_viewing_key(); // Send the change in this asset type back to the sender builder .add_sapling_output( - Some(sk.expsk.ovk), + Some(sk.fvk.ovk), sk.default_address().1, *asset_type, *amt as u64, diff --git a/crates/shielded_token/src/validation.rs b/crates/shielded_token/src/validation.rs index 65a970cc85..39d46748f6 100644 --- a/crates/shielded_token/src/validation.rs +++ b/crates/shielded_token/src/validation.rs @@ -16,6 +16,7 @@ use masp_primitives::transaction::txid::TxIdDigester; use masp_primitives::transaction::{ Authorization, Authorized, Transaction, TransactionData, Unauthorized, }; +use masp_primitives::zip32::ExtendedSpendingKey; use masp_proofs::bellman::groth16::VerifyingKey; use masp_proofs::sapling::BatchValidator; use namada_gas::Gas; @@ -56,7 +57,8 @@ pub struct PartialAuthorized; impl Authorization for PartialAuthorized { type SaplingAuth = ::SaplingAuth; - type TransparentAuth = ::TransparentAuth; + type TransparentAuth = + as Authorization>::TransparentAuth; } /// MASP verifying keys diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 07690d1e0e..108d18dfa3 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -3587,6 +3587,7 @@ fn identical_output_descriptions() -> Result<()> { threshold: 1, account_public_keys_map: None, fee_payer: albert_keypair().to_public(), + shielded_hash: None, }; let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![ diff --git a/crates/token/src/lib.rs b/crates/token/src/lib.rs index dff43909bf..30d19bc3a5 100644 --- a/crates/token/src/lib.rs +++ b/crates/token/src/lib.rs @@ -284,6 +284,7 @@ pub mod testing { }; use masp_primitives::transaction::components::{TxOut, U64Sum}; use masp_primitives::transaction::fees::fixed::FeeRule; + use masp_primitives::zip32::PseudoExtendedKey; use namada_core::address::testing::{ arb_established_address, arb_non_internal_address, }; @@ -364,7 +365,7 @@ pub mod testing { assets in Just(assets), ) -> ( Transfer, - Builder::, + Builder::, HashMap, ) { // Enable assets to be more easily decoded diff --git a/crates/tx/src/types.rs b/crates/tx/src/types.rs index 156f62c211..dc392572f4 100644 --- a/crates/tx/src/types.rs +++ b/crates/tx/src/types.rs @@ -1292,6 +1292,30 @@ impl Tx { None } + /// Remove the transaction section with the given hash + pub fn remove_masp_section(&mut self, hash: &MaspTxId) { + self.sections.retain(|section| { + if let Section::MaspTx(masp) = section { + if MaspTxId::from(masp.txid()) == *hash { + return false; + } + } + true + }); + } + + /// Get the MASP builder section with the given hash + pub fn get_masp_builder(&self, hash: &MaspTxId) -> Option<&MaspBuilder> { + for section in &self.sections { + if let Section::MaspBuilder(builder) = section { + if builder.target == *hash { + return Some(builder); + } + } + } + None + } + /// Set the last transaction memo hash stored in the header pub fn set_memo_sechash(&mut self, hash: namada_core::hash::Hash) { let item = match self.header.batch.pop() { diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs index 2341979f9a..68e6fec27b 100644 --- a/crates/wallet/src/lib.rs +++ b/crates/wallet/src/lib.rs @@ -585,8 +585,21 @@ impl Wallet { (mnemonic, passphrase) }; let seed = Seed::new(&mnemonic, &passphrase); - let spend_key = - derive_hd_spending_key(seed.as_bytes(), derivation_path.clone()); + // Path to obtain the ZIP32 seed + let zip32_seed_path = + DerivationPath::default_for_transparent_scheme(SchemeType::Ed25519); + // Obtain the ZIP32 seed using SLIP10 + let seed = derive_hd_secret_key( + SchemeType::Ed25519, + seed.as_bytes(), + zip32_seed_path, + ) + .try_to_sk::() + .expect("Expected Ed25519 key") + .0 + .to_bytes(); + // Now ZIP32 derive the extended spending key from the new seed + let spend_key = derive_hd_spending_key(&seed, derivation_path.clone()); self.insert_spending_key( alias, @@ -599,6 +612,16 @@ impl Wallet { .map(|alias| (alias, spend_key)) } + /// Find a derivation path by viewing key + pub fn find_path_by_viewing_key( + &self, + vk: &ExtendedViewingKey, + ) -> Result { + self.store + .find_path_by_viewing_key(vk) + .ok_or_else(|| FindKeyError::KeyNotFound(vk.to_string())) + } + /// 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, @@ -1137,12 +1160,14 @@ impl Wallet { view_key: ExtendedViewingKey, birthday: Option, force_alias: bool, + path: Option, ) -> Option { self.store .insert_viewing_key::( alias.into(), view_key, birthday, + path, force_alias, ) .map(Into::into) diff --git a/crates/wallet/src/store.rs b/crates/wallet/src/store.rs index 287bd7822d..8237abbab5 100644 --- a/crates/wallet/src/store.rs +++ b/crates/wallet/src/store.rs @@ -194,6 +194,19 @@ impl Store { self.derivation_paths.get(self.pkhs.get(pkh)?).cloned() } + /// Find a derivation path by viewing key + pub fn find_path_by_viewing_key( + &self, + viewing_key: &ExtendedViewingKey, + ) -> Option { + for (alias, vk) in &self.view_keys { + if *viewing_key == vk.key { + return self.derivation_paths.get(alias).cloned(); + } + } + None + } + /// Find the public key by a public key hash. pub fn find_public_key_by_pkh( &self, @@ -418,6 +431,7 @@ impl Store { alias: Alias, viewkey: ExtendedViewingKey, birthday: Option, + path: Option, force: bool, ) -> Option { // abort if the alias is reserved @@ -435,7 +449,7 @@ impl Store { ConfirmationResponse::Replace => {} ConfirmationResponse::Reselect(new_alias) => { return self.insert_viewing_key::( - new_alias, viewkey, birthday, false, + new_alias, viewkey, birthday, path, false, ); } ConfirmationResponse::Skip => return None, @@ -444,6 +458,7 @@ impl Store { self.remove_alias(&alias); self.view_keys .insert(alias.clone(), DatedKeypair::new(viewkey, birthday)); + path.map(|p| self.derivation_paths.insert(alias.clone(), p)); Some(alias) } diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 70b07f5340..e1cd900a07 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3449,7 +3449,7 @@ dependencies = [ [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "borsh", "chacha20", @@ -3462,7 +3462,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "aes", "bip0039", @@ -3494,7 +3494,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "bellman", "blake2b_simd", diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index b854ddb998..550b010015 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -1863,7 +1863,7 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "borsh", "chacha20", @@ -1876,7 +1876,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "aes", "bip0039", @@ -1907,7 +1907,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" dependencies = [ "bellman", "blake2b_simd", From 2aa2602eeac995c5d8c840542fa0bf061036fdbc Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Mon, 23 Sep 2024 15:00:46 +0200 Subject: [PATCH 02/11] Factored out the logic for MASP hardware wallet signing. --- crates/apps_lib/src/cli.rs | 10 +- crates/apps_lib/src/client/tx.rs | 239 +++++++++++++++++++------------ 2 files changed, 149 insertions(+), 100 deletions(-) diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index a56be799c2..c309248c5e 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -96,9 +96,9 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { let node = SubCmd::parse(matches).map(Self::Node); let client = SubCmd::parse(matches).map(Self::Client); - let relayer = SubCmd::parse(matches).map(Self::Relayer); - let eth_bridge_pool = - SubCmd::parse(matches).map(Self::EthBridgePool); + // let relayer = SubCmd::parse(matches).map(Self::Relayer); + // let eth_bridge_pool = + // SubCmd::parse(matches).map(Self::EthBridgePool); let wallet = SubCmd::parse(matches).map(Self::Wallet); let ledger = SubCmd::parse(matches).map(Self::Ledger); let tx_custom = SubCmd::parse(matches).map(Self::TxCustom); @@ -121,8 +121,8 @@ pub mod cmds { let tx_reveal_pk = SubCmd::parse(matches).map(Self::TxRevealPk); let complete = SubCmd::parse(matches).map(Self::Complete); node.or(client) - .or(relayer) - .or(eth_bridge_pool) + // .or(relayer) + // .or(eth_bridge_pool) .or(wallet) .or(ledger) .or(tx_custom) diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 35cbf4a80c..ecffb7c028 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -854,24 +854,23 @@ impl sapling::MapAuth } } -pub async fn submit_shielded_transfer( +// Identify the viewing keys in the given transaction for which we do not +// possess spending keys in the software wallet, and augment them with a proof +// generation key from the hardware wallet. Returns a mapping from viewing keys +// to corresponding ZIP 32 paths in the hardware wallet. This function errors +// out if any ZIP 32 path that it handles maps to a different viewing key than +// it does on the software client. +async fn augment_masp_hardware_keys( namada: &impl Namada, - mut args: args::TxShieldedTransfer, -) -> Result<(), error::Error> { + args: &mut args::TxShieldedTransfer, +) -> Result, error::Error> { // Records the shielded keys that are on the hardware wallet let mut shielded_hw_keys = HashMap::new(); // Construct the build parameters that parameterized the Transaction // authorizations - let mut bparams: Box = if args.tx.use_device { + if args.tx.use_device { let transport = WalletTransport::from_arg(args.tx.device_transport); let app = NamadaApp::new(transport); - // Clear hardware wallet randomness buffers - app.clean_randomness_buffers().await.map_err(|err| { - error::Error::Other(format!( - "Unable to clear randomness buffer. Error: {}", - err, - )) - })?; let wallet = namada.wallet().await; // Augment the pseudo spending key with a proof authorization key for data in &mut args.data { @@ -969,6 +968,29 @@ pub async fn submit_shielded_transfer( shielded_hw_keys.insert(path.path, viewing_key); } } + Ok(shielded_hw_keys) + } else { + Ok(HashMap::new()) + } +} + +// If the hardware wallet is beig used, use it to generate the random build +// parameters for the spend, convert, and output descriptions. +async fn generate_masp_build_params( + args: &args::TxShieldedTransfer, +) -> Result, error::Error> { + // Construct the build parameters that parameterized the Transaction + // authorizations + if args.tx.use_device { + let transport = WalletTransport::from_arg(args.tx.device_transport); + let app = NamadaApp::new(transport); + // Clear hardware wallet randomness buffers + app.clean_randomness_buffers().await.map_err(|err| { + error::Error::Other(format!( + "Unable to clear randomness buffer. Error: {}", + err, + )) + })?; // Get randomness to aid in construction of various descriptors let mut bparams = StoredBuildParams::default(); // Number of spend descriptions is the number of transfers @@ -1012,98 +1034,125 @@ pub async fn submit_shielded_transfer( ..OutputBuildParams::default() }); } - Box::new(bparams) + Ok(Box::new(bparams)) } else { - Box::new(RngBuildParams::new(OsRng)) - }; - let (mut tx, signing_data) = - args.clone().build(namada, &mut bparams).await?; + Ok(Box::new(RngBuildParams::new(OsRng))) + } +} - if args.tx.dump_tx { - tx::dump_tx(namada.io(), &args.tx, tx); - } else { - // Get the MASP section that is the target of our signing - if let Some(shielded_hash) = signing_data.shielded_hash { - let mut masp_tx = tx - .get_masp_section(&shielded_hash) - .expect("Expected to find the indicated MASP Transaction") - .clone(); - - let masp_builder = tx - .get_masp_builder(&shielded_hash) - .expect("Expected to find the indicated MASP Builder"); - - // Reverse the spend metadata to enable looking up construction - // material - let sapling_inputs = masp_builder.builder.sapling_inputs(); - let mut descriptor_map = vec![0; sapling_inputs.len()]; - for i in 0.. { - if let Some(pos) = masp_builder.metadata.spend_index(i) { - descriptor_map[pos] = i; - } else { - break; - }; - } - // Sign the MASP Transaction using each relevant key in the - // hardware wallet - let mut app = None; - for (path, vk) in shielded_hw_keys { - // Initialize the Ledger app interface if it is uninitialized - let app = app.get_or_insert_with(|| { - NamadaApp::new(WalletTransport::from_arg( - args.tx.device_transport, - )) - }); - // Sign the MASP Transaction using the current viewing key - let path = BIP44Path { - path: path.to_string(), - }; - app.sign_masp_spends(&path, &tx.serialize_to_vec()) +// Sign the given transaction's MASP component using signatures produced by the +// hardware wallet. This function takes the list of spending keys that are +// hosted on the hardware wallet. +async fn masp_sign( + tx: &mut Tx, + args: &args::Tx, + signing_data: &SigningTxData, + shielded_hw_keys: HashMap, +) -> Result<(), error::Error> { + // Get the MASP section that is the target of our signing + if let Some(shielded_hash) = signing_data.shielded_hash { + let mut masp_tx = tx + .get_masp_section(&shielded_hash) + .expect("Expected to find the indicated MASP Transaction") + .clone(); + + let masp_builder = tx + .get_masp_builder(&shielded_hash) + .expect("Expected to find the indicated MASP Builder"); + + // Reverse the spend metadata to enable looking up construction + // material + let sapling_inputs = masp_builder.builder.sapling_inputs(); + let mut descriptor_map = vec![0; sapling_inputs.len()]; + for i in 0.. { + if let Some(pos) = masp_builder.metadata.spend_index(i) { + descriptor_map[pos] = i; + } else { + break; + }; + } + // Sign the MASP Transaction using each relevant key in the + // hardware wallet + let mut app = None; + for (path, vk) in shielded_hw_keys { + // Initialize the Ledger app interface if it is uninitialized + let app = app.get_or_insert_with(|| { + NamadaApp::new(WalletTransport::from_arg(args.device_transport)) + }); + // Sign the MASP Transaction using the current viewing key + let path = BIP44Path { + path: path.to_string(), + }; + app.sign_masp_spends(&path, &tx.serialize_to_vec()) + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + // Now prepare a new list of authorizations based on hardware + // wallet responses + let mut authorizations = HashMap::new(); + for (tx_pos, builder_pos) in descriptor_map.iter().enumerate() { + // Read the next spend authorization signature from the + // hardware wallet + let response = app + .get_spend_signature() .await .map_err(|err| error::Error::Other(err.to_string()))?; - // Now prepare a new list of authorizations based on hardware - // wallet responses - let mut authorizations = HashMap::new(); - for (tx_pos, builder_pos) in descriptor_map.iter().enumerate() { - // Read the next spend authorization signature from the - // hardware wallet - let response = app - .get_spend_signature() - .await - .map_err(|err| error::Error::Other(err.to_string()))?; - let signature = redjubjub::Signature::try_from_slice( - &[response.rbar, response.sbar].concat(), - ) - .map_err(|err| { - error::Error::Other(format!( - "Unexpected spend authorization key in response \ - from the hardware wallet: {}.", - err, - )) - })?; - if *sapling_inputs[*builder_pos].key() - == ExtendedFullViewingKey::from(vk) - { - // If this descriptor was produced by the current - // viewing key (which comes from the hardware wallet), - // then use the authorization from the hardware wallet - authorizations.insert(tx_pos, signature); - } + let signature = redjubjub::Signature::try_from_slice( + &[response.rbar, response.sbar].concat(), + ) + .map_err(|err| { + error::Error::Other(format!( + "Unexpected spend authorization key in response from \ + the hardware wallet: {}.", + err, + )) + })?; + if *sapling_inputs[*builder_pos].key() + == ExtendedFullViewingKey::from(vk) + { + // If this descriptor was produced by the current + // viewing key (which comes from the hardware wallet), + // then use the authorization from the hardware wallet + authorizations.insert(tx_pos, signature); } - // Finally, patch the MASP Transaction with the fetched spend - // authorization signature - masp_tx = (*masp_tx).clone().map_authorization::( + } + // Finally, patch the MASP Transaction with the fetched spend + // authorization signature + masp_tx = (*masp_tx) + .clone() + .map_authorization::( (), MapSaplingSigAuth(authorizations), - ).freeze().map_err(|err| error::Error::Other(format!( - "Unable to apply hardware walleet sourced authorization \ - signatures to the transaction being constructed: {}.", - err, - )))?; - } - tx.remove_masp_section(&shielded_hash); - tx.add_section(Section::MaspTx(masp_tx)); + ) + .freeze() + .map_err(|err| { + error::Error::Other(format!( + "Unable to apply hardware walleet sourced \ + authorization signatures to the transaction being \ + constructed: {}.", + err, + )) + })?; } + tx.remove_masp_section(&shielded_hash); + tx.add_section(Section::MaspTx(masp_tx)); + } + Ok(()) +} + +pub async fn submit_shielded_transfer( + namada: &impl Namada, + mut args: args::TxShieldedTransfer, +) -> Result<(), error::Error> { + let shielded_hw_keys = + augment_masp_hardware_keys(namada, &mut args).await?; + let mut bparams = generate_masp_build_params(&args).await?; + let (mut tx, signing_data) = + args.clone().build(namada, &mut bparams).await?; + + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + } else { + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; sign(namada, &mut tx, &args.tx, signing_data).await?; namada.submit(tx, &args.tx).await?; } From e08c8b79776c7094bdfcbcdf92bec88a62b3f430 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Tue, 24 Sep 2024 14:55:15 +0200 Subject: [PATCH 03/11] Expand MASP hardware wallet support to other transaction types. --- Cargo.lock | 6 +- Cargo.toml | 4 +- crates/apps_lib/src/client/tx.rs | 137 ++++++++++++++++++++++--------- crates/core/src/masp.rs | 8 ++ crates/sdk/src/args.rs | 9 +- crates/sdk/src/tx.rs | 18 ++-- wasm/Cargo.lock | 6 +- wasm_for_tests/Cargo.lock | 6 +- 8 files changed, 133 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d6ea5452e..0a7f260874 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4494,7 +4494,7 @@ dependencies = [ [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "arbitrary", "borsh", @@ -4508,7 +4508,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "aes", "arbitrary", @@ -4541,7 +4541,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 6ef70ad493..e6849d9d75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,8 +141,8 @@ libc = "0.2.97" libloading = "0.7.2" linkme = "0.3.24" # branch = "tomas/arbitrary" -masp_primitives = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56" } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56", default-features = false, features = ["local-prover"] } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "2914e6ff9a922bae8f1cb63a79d796a69af3d8aa", default-features = false, features = ["local-prover"] } num256 = "0.3.5" num_cpus = "1.13.0" num-derive = "0.4" diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index ecffb7c028..2cac601715 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -12,7 +12,9 @@ use masp_primitives::transaction::components::sapling::builder::{ SpendBuildParams, StoredBuildParams, }; use masp_primitives::transaction::components::sapling::fees::InputView; -use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedKey}; +use masp_primitives::zip32::{ + ExtendedFullViewingKey, ExtendedKey, PseudoExtendedKey, +}; use namada_sdk::address::{Address, ImplicitAddress}; use namada_sdk::args::TxBecomeValidator; use namada_sdk::collections::{HashMap, HashSet}; @@ -43,6 +45,22 @@ use crate::wallet::{ gen_validator_keys, read_and_confirm_encryption_password, WalletTransport, }; +// Maximum number of spend description randomness parameters that can be +// generated on the hardware wallet. It is hard to compute the exact required +// number because a given MASP source could be distributed amongst several +// notes. +const MAX_HW_SPEND: usize = 10; +// Maximum number of convert description randomness parameters that can be +// generated on the hardware wallet. It is hard to compute the exact required +// number because the number of conversions that are used depends on the +// protocol's current state. +const MAX_HW_CONVERT: usize = 10; +// Maximum number of output description randomness parameters that can be +// generated on the hardware wallet. It is hard to compute the exact required +// number because the number of outputs depends on the number of dummy outputs +// introduced. +const MAX_HW_OUTPUT: usize = 10; + /// Wrapper around `signing::aux_signing_data` that stores the optional /// disposable address to the wallet pub async fn aux_signing_data( @@ -862,24 +880,25 @@ impl sapling::MapAuth // it does on the software client. async fn augment_masp_hardware_keys( namada: &impl Namada, - args: &mut args::TxShieldedTransfer, + args: &args::Tx, + sources: impl Iterator, ) -> Result, error::Error> { // Records the shielded keys that are on the hardware wallet let mut shielded_hw_keys = HashMap::new(); // Construct the build parameters that parameterized the Transaction // authorizations - if args.tx.use_device { - let transport = WalletTransport::from_arg(args.tx.device_transport); + if args.use_device { + let transport = WalletTransport::from_arg(args.device_transport); let app = NamadaApp::new(transport); let wallet = namada.wallet().await; // Augment the pseudo spending key with a proof authorization key - for data in &mut args.data { + for source in sources { // Only attempt an augmentation if proof authorization is not there - if data.source.to_spending_key().is_none() { + if source.to_spending_key().is_none() { // First find the derivation path corresponding to this viewing // key let viewing_key = - ExtendedViewingKey::from(data.source.to_viewing_key()); + ExtendedViewingKey::from(source.to_viewing_key()); let path = wallet .find_path_by_viewing_key(&viewing_key) .map_err(|err| { @@ -950,21 +969,18 @@ async fn augment_masp_hardware_keys( )) })?; // Augment the pseudo spending key - data.source.augment_proof_generation_key(pgk).map_err( - |_| { - error::Error::Other( - "Proof generation key in response from the \ - hardware wallet does not correspond to stored \ - viewing key." - .to_string(), - ) - }, - )?; + source.augment_proof_generation_key(pgk).map_err(|_| { + error::Error::Other( + "Proof generation key in response from the hardware \ + wallet does not correspond to stored viewing key." + .to_string(), + ) + })?; // Finally, augment an incorrect spend authorization key just to // make sure that the Transaction is built. - data.source.augment_spend_authorizing_key_unchecked( - PrivateKey(jubjub::Fr::default()), - ); + source.augment_spend_authorizing_key_unchecked(PrivateKey( + jubjub::Fr::default(), + )); shielded_hw_keys.insert(path.path, viewing_key); } } @@ -977,12 +993,15 @@ async fn augment_masp_hardware_keys( // If the hardware wallet is beig used, use it to generate the random build // parameters for the spend, convert, and output descriptions. async fn generate_masp_build_params( - args: &args::TxShieldedTransfer, + spend_len: usize, + convert_len: usize, + output_len: usize, + args: &args::Tx, ) -> Result, error::Error> { // Construct the build parameters that parameterized the Transaction // authorizations - if args.tx.use_device { - let transport = WalletTransport::from_arg(args.tx.device_transport); + if args.use_device { + let transport = WalletTransport::from_arg(args.device_transport); let app = NamadaApp::new(transport); // Clear hardware wallet randomness buffers app.clean_randomness_buffers().await.map_err(|err| { @@ -993,16 +1012,6 @@ async fn generate_masp_build_params( })?; // Get randomness to aid in construction of various descriptors let mut bparams = StoredBuildParams::default(); - // Number of spend descriptions is the number of transfers - let spend_len = args.data.len(); - // Number of convert description is assumed to be double the number of - // transfers. This is because each spend description might first be - // converted to epoch 0 before going to the intended epoch. - let convert_len = args.data.len() * 2; - // Number of output descriptions is assumed to be double the number of - // transfers. This is because there may be change from each output - // that's destined for the sender. - let output_len = args.data.len() * 2; for _ in 0..spend_len { let spend_randomness = app .get_spend_randomness() @@ -1011,7 +1020,6 @@ async fn generate_masp_build_params( bparams.spend_params.push(SpendBuildParams { rcv: jubjub::Fr::from_bytes(&spend_randomness.rcv).unwrap(), alpha: jubjub::Fr::from_bytes(&spend_randomness.alpha).unwrap(), - ..SpendBuildParams::default() }); } for _ in 0..convert_len { @@ -1143,9 +1151,20 @@ pub async fn submit_shielded_transfer( namada: &impl Namada, mut args: args::TxShieldedTransfer, ) -> Result<(), error::Error> { + let sources = args + .data + .iter_mut() + .map(|x| &mut x.source) + .chain(args.gas_spending_keys.iter_mut()); let shielded_hw_keys = - augment_masp_hardware_keys(namada, &mut args).await?; - let mut bparams = generate_masp_build_params(&args).await?; + augment_masp_hardware_keys(namada, &args.tx, sources).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; let (mut tx, signing_data) = args.clone().build(namada, &mut bparams).await?; @@ -1165,7 +1184,15 @@ pub async fn submit_shielding_transfer( ) -> Result<(), error::Error> { // Repeat once if the tx fails on a crossover of an epoch for _ in 0..2 { - let (tx, signing_data, tx_epoch) = args.clone().build(namada).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (tx, signing_data, tx_epoch) = + args.clone().build(namada, &mut bparams).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); @@ -1217,13 +1244,26 @@ pub async fn submit_shielding_transfer( pub async fn submit_unshielding_transfer( namada: &impl Namada, - args: args::TxUnshieldingTransfer, + mut args: args::TxUnshieldingTransfer, ) -> Result<(), error::Error> { - let (mut tx, signing_data) = args.clone().build(namada).await?; + let sources = std::iter::once(&mut args.source) + .chain(args.gas_spending_keys.iter_mut()); + let shielded_hw_keys = + augment_masp_hardware_keys(namada, &args.tx, sources).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (mut tx, signing_data) = + args.clone().build(namada, &mut bparams).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; sign(namada, &mut tx, &args.tx, signing_data).await?; namada.submit(tx, &args.tx).await?; } @@ -1232,16 +1272,31 @@ pub async fn submit_unshielding_transfer( pub async fn submit_ibc_transfer( namada: &N, - args: args::TxIbcTransfer, + mut args: args::TxIbcTransfer, ) -> Result<(), error::Error> where ::Error: std::fmt::Display, { - let (tx, signing_data, _) = args.build(namada).await?; + let sources = args + .source + .spending_key_mut() + .into_iter() + .chain(args.gas_spending_keys.iter_mut()); + let shielded_hw_keys = + augment_masp_hardware_keys(namada, &args.tx, sources).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (mut tx, signing_data, _) = args.build(namada, &mut bparams).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; batch_opt_reveal_pk_and_submit( namada, &args.tx, diff --git a/crates/core/src/masp.rs b/crates/core/src/masp.rs index ca00a4b7be..62ef4aa69b 100644 --- a/crates/core/src/masp.rs +++ b/crates/core/src/masp.rs @@ -547,6 +547,14 @@ impl TransferSource { } } + /// Get the contained ExtendedSpendingKey contained, if any + pub fn spending_key_mut(&mut self) -> Option<&mut PseudoExtendedKey> { + match self { + Self::ExtendedSpendingKey(x) => Some(x), + _ => None, + } + } + /// Get the contained Address, if any pub fn address(&self) -> Option
{ match self { diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index 7ecfd966d5..4f8a158b13 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -430,8 +430,9 @@ impl TxShieldingTransfer { pub async fn build( &mut self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, MaspEpoch)> { - tx::build_shielding_transfer(context, self).await + tx::build_shielding_transfer(context, self, bparams).await } } @@ -469,8 +470,9 @@ impl TxUnshieldingTransfer { pub async fn build( &mut self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { - tx::build_unshielding_transfer(context, self).await + tx::build_unshielding_transfer(context, self, bparams).await } } @@ -618,9 +620,10 @@ impl TxIbcTransfer { pub async fn build( &self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option)> { - tx::build_ibc_transfer(context, self).await + tx::build_ibc_transfer(context, self, bparams).await } } diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index a5e47be354..935b35b1c5 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -2522,6 +2522,7 @@ pub async fn build_pgf_stewards_proposal( pub async fn build_ibc_transfer( context: &impl Namada, args: &args::TxIbcTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData, Option)> { if args.ibc_shielding_data.is_some() && args.ibc_memo.is_some() { return Err(Error::Other( @@ -2535,7 +2536,7 @@ pub async fn build_ibc_transfer( get_refund_target(context, &args.source, &args.refund_target).await?; let source = args.source.effective_address(); - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, Some(source.clone()), @@ -2631,7 +2632,7 @@ pub async fn build_ibc_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), - &mut RngBuildParams::new(OsRng), + bparams, ) .await?; let shielded_tx_epoch = shielded_parts.as_ref().map(|trans| trans.0.epoch); @@ -2682,6 +2683,7 @@ pub async fn build_ibc_transfer( let masp_tx_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx.clone()).1; transfer.shielded_section_hash = Some(masp_tx_hash); + signing_data.shielded_hash = Some(masp_tx_hash); tx.add_masp_builder(MaspBuilder { asset_types, metadata: shielded_transfer.metadata, @@ -3181,6 +3183,7 @@ async fn get_masp_fee_payment_amount( pub async fn build_shielding_transfer( context: &N, args: &mut args::TxShieldingTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData, MaspEpoch)> { let source = if args.data.len() == 1 { // If only one transfer take its source as the signer @@ -3192,7 +3195,7 @@ pub async fn build_shielding_transfer( // argument None }; - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, source.clone(), @@ -3267,7 +3270,7 @@ pub async fn build_shielding_transfer( None, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), - &mut RngBuildParams::new(OsRng), + bparams, ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3298,6 +3301,7 @@ pub async fn build_shielding_transfer( }); data.shielded_section_hash = Some(shielded_section_hash); + signing_data.shielded_hash = Some(shielded_section_hash); tracing::debug!("Transfer data {data:?}"); Ok(()) }; @@ -3319,8 +3323,9 @@ pub async fn build_shielding_transfer( pub async fn build_unshielding_transfer( context: &N, args: &mut args::TxUnshieldingTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData)> { - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, Some(MASP), @@ -3390,7 +3395,7 @@ pub async fn build_unshielding_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), - &mut RngBuildParams::new(OsRng), + bparams, ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3420,6 +3425,7 @@ pub async fn build_unshielding_transfer( }); data.shielded_section_hash = Some(shielded_section_hash); + signing_data.shielded_hash = Some(shielded_section_hash); tracing::debug!("Transfer data {data:?}"); Ok(()) }; diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index e1cd900a07..133a547f19 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3449,7 +3449,7 @@ dependencies = [ [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "borsh", "chacha20", @@ -3462,7 +3462,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "aes", "bip0039", @@ -3494,7 +3494,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "bellman", "blake2b_simd", diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index 550b010015..ea503616cd 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -1863,7 +1863,7 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "borsh", "chacha20", @@ -1876,7 +1876,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "aes", "bip0039", @@ -1907,7 +1907,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=a35f73be69b21ee62cd4940f37855161cbed2a56#a35f73be69b21ee62cd4940f37855161cbed2a56" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "bellman", "blake2b_simd", From 3098315e842f54da1404c5aaa2b324c65c60493a Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 26 Sep 2024 16:30:47 +0200 Subject: [PATCH 04/11] Separate the storage of shielded keys from their birthdays. --- crates/apps_lib/src/cli.rs | 3 +- crates/apps_lib/src/cli/client.rs | 5 ++- crates/apps_lib/src/cli/context.rs | 25 ++++++------ crates/benches/native_vps.rs | 6 +-- crates/node/src/bench_utils.rs | 7 +++- crates/wallet/src/lib.rs | 20 ++++++--- crates/wallet/src/store.rs | 43 ++++++++++++-------- genesis/hardware/src/pre-genesis/wallet.toml | 2 + genesis/localnet/src/pre-genesis/wallet.toml | 2 + 9 files changed, 68 insertions(+), 45 deletions(-) diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index c309248c5e..facd39f4db 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -7645,8 +7645,7 @@ pub mod args { find_viewing_key(&mut wallet) } else { find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) - } - .key; + }; Ok(PayAddressGen:: { alias: self.alias, diff --git a/crates/apps_lib/src/cli/client.rs b/crates/apps_lib/src/cli/client.rs index a11f6b785b..19f63744f0 100644 --- a/crates/apps_lib/src/cli/client.rs +++ b/crates/apps_lib/src/cli/client.rs @@ -4,6 +4,7 @@ use color_eyre::eyre::Result; use namada_sdk::io::{display_line, Io, NamadaIo}; use namada_sdk::masp::ShieldedContext; use namada_sdk::{Namada, NamadaImpl}; +use namada_sdk::wallet::DatedViewingKey; use crate::cli; use crate::cli::api::{CliApi, CliClient}; @@ -349,8 +350,8 @@ impl CliApi { chain_ctx .wallet .get_viewing_keys() - .values() - .copied(), + .into_iter() + .map(|(k, v)| DatedViewingKey::new(v, chain_ctx.wallet.find_birthday(k).copied())), ); crate::client::masp::syncing( diff --git a/crates/apps_lib/src/cli/context.rs b/crates/apps_lib/src/cli/context.rs index 8cc2da6c78..fd86a2b80d 100644 --- a/crates/apps_lib/src/cli/context.rs +++ b/crates/apps_lib/src/cli/context.rs @@ -587,7 +587,6 @@ impl ArgFromMutContext for ExtendedSpendingKey { // Or it is a stored alias of one ctx.wallet .find_spending_key(raw, None) - .map(|k| k.key) .map_err(|_find_err| format!("Unknown spending key {}", raw)) }) } @@ -612,9 +611,7 @@ impl ArgFromMutContext for PseudoExtendedKey { ctx.wallet .find_spending_key(raw, None) .map(|k| { - PseudoExtendedKey::from(MaspExtendedSpendingKey::from( - k.key, - )) + PseudoExtendedKey::from(MaspExtendedSpendingKey::from(k)) }) .map_err(|_find_err| { format!("Unknown spending key {}", raw) @@ -626,9 +623,7 @@ impl ArgFromMutContext for PseudoExtendedKey { .find_viewing_key(raw) .copied() .map(|k| { - PseudoExtendedKey::from(MaspExtendedViewingKey::from( - k.key, - )) + PseudoExtendedKey::from(MaspExtendedViewingKey::from(k)) }) .map_err(|_find_err| format!("Unknown viewing key {}", raw)) }) @@ -644,9 +639,12 @@ impl ArgFromMutContext for DatedSpendingKey { // Either the string is a raw extended spending key FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one - ctx.wallet + let sk = ctx.wallet .find_spending_key(raw, None) - .map_err(|_find_err| format!("Unknown spending key {}", raw)) + .map_err(|_find_err| format!("Unknown spending key {}", raw))?; + let birthday = ctx.wallet + .find_birthday(raw); + Ok(DatedSpendingKey::new(sk, birthday.copied())) }) } } @@ -663,7 +661,6 @@ impl ArgFromMutContext for ExtendedViewingKey { ctx.wallet .find_viewing_key(raw) .copied() - .map(|k| k.key) .map_err(|_find_err| format!("Unknown viewing key {}", raw)) }) } @@ -678,10 +675,12 @@ impl ArgFromMutContext for DatedViewingKey { // Either the string is a raw full viewing key FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one - ctx.wallet + let vk = ctx.wallet .find_viewing_key(raw) - .copied() - .map_err(|_find_err| format!("Unknown viewing key {}", raw)) + .map_err(|_find_err| format!("Unknown viewing key {}", raw))?; + let birthday = ctx.wallet + .find_birthday(raw); + Ok(DatedViewingKey::new(*vk, birthday.copied())) }) } } diff --git a/crates/benches/native_vps.rs b/crates/benches/native_vps.rs index 6e12b24523..474067abcd 100644 --- a/crates/benches/native_vps.rs +++ b/crates/benches/native_vps.rs @@ -412,7 +412,7 @@ fn prepare_ibc_tx_and_ctx(bench_name: &str) -> (BenchShieldedCtx, BatchedTx) { shielded_ctx.generate_shielded_action( Amount::native_whole(10), TransferSource::ExtendedSpendingKey( - ExtendedSpendingKey::from(albert_spending_key.key).into(), + ExtendedSpendingKey::from(albert_spending_key).into(), ), defaults::bertha_address().to_string(), ) @@ -606,14 +606,14 @@ fn setup_storage_for_masp_verification( "unshielding" => shielded_ctx.generate_masp_tx( amount, TransferSource::ExtendedSpendingKey( - ExtendedSpendingKey::from(albert_spending_key.key).into(), + ExtendedSpendingKey::from(albert_spending_key).into(), ), TransferTarget::Address(defaults::albert_address()), ), "shielded" => shielded_ctx.generate_masp_tx( amount, TransferSource::ExtendedSpendingKey( - ExtendedSpendingKey::from(albert_spending_key.key).into(), + ExtendedSpendingKey::from(albert_spending_key).into(), ), TransferTarget::PaymentAddress(bertha_payment_addr), ), diff --git a/crates/node/src/bench_utils.rs b/crates/node/src/bench_utils.rs index d86dee1ad0..1490d600c3 100644 --- a/crates/node/src/bench_utils.rs +++ b/crates/node/src/bench_utils.rs @@ -106,7 +106,7 @@ pub use namada_sdk::tx::{ TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL as TX_VOTE_PROPOSAL_WASM, TX_WITHDRAW_WASM, VP_USER_WASM, }; -use namada_sdk::wallet::Wallet; +use namada_sdk::wallet::{DatedSpendingKey, Wallet}; use namada_sdk::{ parameters, proof_of_stake, tendermint, Namada, NamadaImpl, PaymentAddress, TransferSource, TransferTarget, @@ -1136,7 +1136,6 @@ impl Default for BenchShieldedCtx { .wallet .find_viewing_key(viewing_alias) .unwrap() - .key .to_string(), ); let viewing_key = ExtendedFullViewingKey::from( @@ -1179,6 +1178,10 @@ impl BenchShieldedCtx { .wallet .find_spending_key(ALBERT_SPENDING_KEY, None) .unwrap(); + let spending_key = DatedSpendingKey::new( + spending_key, + self.wallet.find_birthday(ALBERT_SPENDING_KEY).copied(), + ); self.shielded = async_runtime .block_on(namada_apps_lib::client::masp::syncing( self.shielded, diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs index 68e6fec27b..338ee84003 100644 --- a/crates/wallet/src/lib.rs +++ b/crates/wallet/src/lib.rs @@ -274,7 +274,7 @@ pub struct Wallet { utils: U, store: Store, decrypted_key_cache: HashMap, - decrypted_spendkey_cache: HashMap, + decrypted_spendkey_cache: HashMap, } impl From> for Store { @@ -436,12 +436,20 @@ impl Wallet { pub fn find_viewing_key( &self, alias: impl AsRef, - ) -> Result<&DatedViewingKey, FindKeyError> { + ) -> Result<&ExtendedViewingKey, FindKeyError> { self.store.find_viewing_key(alias.as_ref()).ok_or_else(|| { FindKeyError::KeyNotFound(alias.as_ref().to_string()) }) } + /// Find the birthday of the given alias + pub fn find_birthday( + &self, + alias: impl AsRef, + ) -> Option<&BlockHeight> { + self.store.find_birthday(alias.as_ref()) + } + /// Find the payment address with the given alias in the wallet and return /// it pub fn find_payment_addr( @@ -501,7 +509,7 @@ impl Wallet { } /// Get all known viewing keys by their alias - pub fn get_viewing_keys(&self) -> HashMap { + pub fn get_viewing_keys(&self) -> HashMap { self.store .get_viewing_keys() .iter() @@ -512,7 +520,7 @@ impl Wallet { /// Get all known viewing keys by their alias pub fn get_spending_keys( &self, - ) -> HashMap> { + ) -> HashMap> { self.store .get_spending_keys() .iter() @@ -944,7 +952,7 @@ impl Wallet { &mut self, alias: impl AsRef, password: Option>, - ) -> Result { + ) -> Result { // Try cache first if let Some(cached_key) = self .decrypted_spendkey_cache @@ -1196,7 +1204,7 @@ impl Wallet { // Cache the newly added key self.decrypted_spendkey_cache.insert( alias.clone(), - DatedKeypair::new(spend_key, birthday), + spend_key, ); }) .map(Into::into) diff --git a/crates/wallet/src/store.rs b/crates/wallet/src/store.rs index 8237abbab5..cb15a6c359 100644 --- a/crates/wallet/src/store.rs +++ b/crates/wallet/src/store.rs @@ -22,7 +22,6 @@ use zeroize::Zeroizing; use super::alias::{self, Alias}; use super::derivation_path::DerivationPath; use super::pre_genesis; -use crate::keys::{DatedKeypair, DatedSpendingKey, DatedViewingKey}; use crate::{StoredKeypair, WalletIo}; /// Actions that can be taken when there is an alias conflict @@ -63,10 +62,12 @@ pub struct ValidatorData { /// A Storage area for keys and addresses #[derive(Serialize, Deserialize, Debug, Default)] pub struct Store { + /// Known birthdays + birthdays: BTreeMap, /// Known viewing keys - view_keys: BTreeMap, + view_keys: BTreeMap, /// Known spending keys - spend_keys: BTreeMap>, + spend_keys: BTreeMap>, /// Payment address book payment_addrs: BiBTreeMap, /// Cryptographic keypairs @@ -135,7 +136,7 @@ impl Store { pub fn find_spending_key( &self, alias: impl AsRef, - ) -> Option<&StoredKeypair> { + ) -> Option<&StoredKeypair> { self.spend_keys.get(&alias.into()) } @@ -143,10 +144,18 @@ impl Store { pub fn find_viewing_key( &self, alias: impl AsRef, - ) -> Option<&DatedViewingKey> { + ) -> Option<&ExtendedViewingKey> { self.view_keys.get(&alias.into()) } + /// Find the birthday of the given alias + pub fn find_birthday( + &self, + alias: impl AsRef, + ) -> Option<&BlockHeight> { + self.birthdays.get(&alias.into()) + } + /// Find the payment address with the given alias and return it pub fn find_payment_addr( &self, @@ -200,7 +209,7 @@ impl Store { viewing_key: &ExtendedViewingKey, ) -> Option { for (alias, vk) in &self.view_keys { - if *viewing_key == vk.key { + if *viewing_key == *vk { return self.derivation_paths.get(alias).cloned(); } } @@ -267,14 +276,14 @@ impl Store { } /// Get all known viewing keys by their alias. - pub fn get_viewing_keys(&self) -> &BTreeMap { + pub fn get_viewing_keys(&self) -> &BTreeMap { &self.view_keys } /// Get all known spending keys by their alias. pub fn get_spending_keys( &self, - ) -> &BTreeMap> { + ) -> &BTreeMap> { &self.spend_keys } @@ -412,15 +421,11 @@ impl Store { } self.remove_alias(&alias); - let (spendkey_to_store, _raw_spendkey) = - StoredKeypair::new(DatedKeypair::new(spendkey, birthday), password); + let (spendkey_to_store, _raw_spendkey) = StoredKeypair::new(spendkey, password); self.spend_keys.insert(alias.clone(), spendkey_to_store); // Simultaneously add the derived viewing key to ease balance viewing - let viewkey = DatedKeypair::new( - zip32::ExtendedFullViewingKey::from(&spendkey.into()).into(), - birthday, - ); - self.view_keys.insert(alias.clone(), viewkey); + birthday.map(|x| self.birthdays.insert(alias.clone(), x)); + self.view_keys.insert(alias.clone(), zip32::ExtendedFullViewingKey::from(&spendkey.into()).into()); path.map(|p| self.derivation_paths.insert(alias.clone(), p)); Some(alias) } @@ -456,8 +461,8 @@ impl Store { } } self.remove_alias(&alias); - self.view_keys - .insert(alias.clone(), DatedKeypair::new(viewkey, birthday)); + birthday.map(|x| self.birthdays.insert(alias.clone(), x)); + self.view_keys.insert(alias.clone(), viewkey); path.map(|p| self.derivation_paths.insert(alias.clone(), p)); Some(alias) } @@ -600,6 +605,7 @@ impl Store { || self.pkhs.values().contains(alias) || self.public_keys.contains_key(alias) || self.derivation_paths.contains_key(alias) + || self.birthdays.contains_key(alias) } /// Completely remove the given alias from all maps in the wallet @@ -612,12 +618,14 @@ impl Store { self.pkhs.retain(|_key, val| val != alias); self.public_keys.remove(alias); self.derivation_paths.remove(alias); + self.birthdays.remove(alias); } /// Extend this store from another store (typically pre-genesis). /// Note that this method ignores `validator_data` if any. pub fn extend(&mut self, store: Store) { let Self { + birthdays, view_keys, spend_keys, payment_addrs, @@ -629,6 +637,7 @@ impl Store { validator_data: _, address_vp_types, } = self; + birthdays.extend(store.birthdays); view_keys.extend(store.view_keys); spend_keys.extend(store.spend_keys); payment_addrs.extend(store.payment_addrs); diff --git a/genesis/hardware/src/pre-genesis/wallet.toml b/genesis/hardware/src/pre-genesis/wallet.toml index 1f36ffe432..7866cb9c2f 100644 --- a/genesis/hardware/src/pre-genesis/wallet.toml +++ b/genesis/hardware/src/pre-genesis/wallet.toml @@ -1,3 +1,5 @@ +[birthdays] + [view_keys] [spend_keys] diff --git a/genesis/localnet/src/pre-genesis/wallet.toml b/genesis/localnet/src/pre-genesis/wallet.toml index fbf083e87b..eb231c1e0e 100644 --- a/genesis/localnet/src/pre-genesis/wallet.toml +++ b/genesis/localnet/src/pre-genesis/wallet.toml @@ -1,3 +1,5 @@ +[birthdays] + [view_keys] [spend_keys] From 0ecf8edbcd801b2c23c4111c326f3ef7a26def9d Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Fri, 27 Sep 2024 16:14:57 +0200 Subject: [PATCH 05/11] Never use the hardware wallet to sign the fee header alone. Always ensure removal of MASP Builder data. --- crates/apps_lib/src/cli/client.rs | 12 +++++- crates/apps_lib/src/cli/context.rs | 16 ++++---- crates/apps_lib/src/client/tx.rs | 10 +++-- .../src/config/genesis/transactions.rs | 2 +- crates/apps_lib/src/config/genesis/utils.rs | 5 +-- crates/sdk/src/lib.rs | 3 +- crates/sdk/src/signing.rs | 37 ++++++++----------- crates/wallet/src/lib.rs | 6 +-- crates/wallet/src/store.rs | 8 +++- 9 files changed, 53 insertions(+), 46 deletions(-) diff --git a/crates/apps_lib/src/cli/client.rs b/crates/apps_lib/src/cli/client.rs index 19f63744f0..38cddc6556 100644 --- a/crates/apps_lib/src/cli/client.rs +++ b/crates/apps_lib/src/cli/client.rs @@ -3,8 +3,8 @@ use std::io::Read; use color_eyre::eyre::Result; use namada_sdk::io::{display_line, Io, NamadaIo}; use namada_sdk::masp::ShieldedContext; -use namada_sdk::{Namada, NamadaImpl}; use namada_sdk::wallet::DatedViewingKey; +use namada_sdk::{Namada, NamadaImpl}; use crate::cli; use crate::cli::api::{CliApi, CliClient}; @@ -351,7 +351,15 @@ impl CliApi { .wallet .get_viewing_keys() .into_iter() - .map(|(k, v)| DatedViewingKey::new(v, chain_ctx.wallet.find_birthday(k).copied())), + .map(|(k, v)| { + DatedViewingKey::new( + v, + chain_ctx + .wallet + .find_birthday(k) + .copied(), + ) + }), ); crate::client::masp::syncing( diff --git a/crates/apps_lib/src/cli/context.rs b/crates/apps_lib/src/cli/context.rs index fd86a2b80d..63a6cb2801 100644 --- a/crates/apps_lib/src/cli/context.rs +++ b/crates/apps_lib/src/cli/context.rs @@ -611,7 +611,9 @@ impl ArgFromMutContext for PseudoExtendedKey { ctx.wallet .find_spending_key(raw, None) .map(|k| { - PseudoExtendedKey::from(MaspExtendedSpendingKey::from(k)) + PseudoExtendedKey::from(MaspExtendedSpendingKey::from( + k, + )) }) .map_err(|_find_err| { format!("Unknown spending key {}", raw) @@ -639,11 +641,11 @@ impl ArgFromMutContext for DatedSpendingKey { // Either the string is a raw extended spending key FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one - let sk = ctx.wallet + let sk = ctx + .wallet .find_spending_key(raw, None) .map_err(|_find_err| format!("Unknown spending key {}", raw))?; - let birthday = ctx.wallet - .find_birthday(raw); + let birthday = ctx.wallet.find_birthday(raw); Ok(DatedSpendingKey::new(sk, birthday.copied())) }) } @@ -675,11 +677,11 @@ impl ArgFromMutContext for DatedViewingKey { // Either the string is a raw full viewing key FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one - let vk = ctx.wallet + let vk = ctx + .wallet .find_viewing_key(raw) .map_err(|_find_err| format!("Unknown viewing key {}", raw))?; - let birthday = ctx.wallet - .find_birthday(raw); + let birthday = ctx.wallet.find_birthday(raw); Ok(DatedViewingKey::new(*vk, birthday.copied())) }) } diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 2cac601715..319703b5e0 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -17,7 +17,7 @@ use masp_primitives::zip32::{ }; use namada_sdk::address::{Address, ImplicitAddress}; use namada_sdk::args::TxBecomeValidator; -use namada_sdk::collections::{HashMap, HashSet}; +use namada_sdk::collections::HashMap; use namada_sdk::governance::cli::onchain::{ DefaultProposal, PgfFundingProposal, PgfStewardProposal, }; @@ -103,7 +103,7 @@ pub async fn aux_signing_data( pub async fn with_hardware_wallet<'a, U, T>( mut tx: Tx, pubkey: common::PublicKey, - parts: HashSet, + parts: signing::Signable, (wallet, app): (&RwLock>, &NamadaApp), ) -> Result where @@ -150,7 +150,9 @@ where .await .map_err(|err| error::Error::Other(err.to_string()))?; // Sign the raw header if that is requested - if parts.contains(&signing::Signable::RawHeader) { + if parts == signing::Signable::RawHeader + || parts == signing::Signable::FeeRawHeader + { let pubkey = common::PublicKey::try_from_slice(&response.pubkey) .expect("unable to parse public key from Ledger"); let signature = @@ -168,7 +170,7 @@ where tx.add_section(Section::Authorization(compressed.expand(&tx))); } // Sign the fee header if that is requested - if parts.contains(&signing::Signable::FeeHeader) { + if parts == signing::Signable::FeeRawHeader { let pubkey = common::PublicKey::try_from_slice(&response.pubkey) .expect("unable to parse public key from Ledger"); let signature = diff --git a/crates/apps_lib/src/config/genesis/transactions.rs b/crates/apps_lib/src/config/genesis/transactions.rs index 3d64a1b010..04421c0fcb 100644 --- a/crates/apps_lib/src/config/genesis/transactions.rs +++ b/crates/apps_lib/src/config/genesis/transactions.rs @@ -783,7 +783,7 @@ impl Signed { async fn software_wallet_sign( tx: Tx, pubkey: common::PublicKey, - _parts: HashSet, + _parts: namada_sdk::signing::Signable, _user: (), ) -> Result { if pubkey == genesis_fee_payer_pk() { diff --git a/crates/apps_lib/src/config/genesis/utils.rs b/crates/apps_lib/src/config/genesis/utils.rs index c5971d9d2c..d5622111dd 100644 --- a/crates/apps_lib/src/config/genesis/utils.rs +++ b/crates/apps_lib/src/config/genesis/utils.rs @@ -2,7 +2,6 @@ use std::path::Path; use eyre::Context; use ledger_namada_rs::NamadaApp; -use namada_sdk::collections::HashSet; use namada_sdk::key::common; use namada_sdk::tx::Tx; use namada_sdk::wallet::Wallet; @@ -52,14 +51,14 @@ pub fn write_toml( pub(super) async fn with_hardware_wallet<'a, T>( tx: Tx, pubkey: common::PublicKey, - parts: HashSet, + parts: signing::Signable, (wallet, app): (&RwLock>, &NamadaApp), ) -> Result where T: ledger_transport::Exchange + Send + Sync, ::Error: std::error::Error, { - if parts.contains(&signing::Signable::FeeHeader) { + if parts == signing::Signable::FeeRawHeader { Ok(tx) } else { crate::client::tx::with_hardware_wallet( diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index c016902b38..bb19d936ad 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -47,7 +47,6 @@ use std::str::FromStr; use args::{DeviceTransport, InputAmount, SdkTypes}; use masp_primitives::zip32::PseudoExtendedKey; use namada_core::address::Address; -use namada_core::collections::HashSet; use namada_core::dec::Dec; use namada_core::ethereum_events::EthAddress; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; @@ -608,7 +607,7 @@ pub trait Namada: NamadaIo { tx: &mut Tx, args: &args::Tx, signing_data: SigningTxData, - with: impl Fn(Tx, common::PublicKey, HashSet, D) -> F + with: impl Fn(Tx, common::PublicKey, signing::Signable, D) -> F + MaybeSend + MaybeSync, user_data: D, diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index d396e2b444..9c5ff2d721 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -171,11 +171,12 @@ pub async fn tx_signers( } } -/// The different parts of a transaction that can be signed +/// The different parts of a transaction that can be signed. Note that it's +/// impossible to sign the fee header without signing the raw header. #[derive(Eq, Hash, PartialEq)] pub enum Signable { - /// Fee header - FeeHeader, + /// Fee and raw header + FeeRawHeader, /// Raw header RawHeader, } @@ -184,7 +185,7 @@ pub enum Signable { pub async fn default_sign( _tx: Tx, pubkey: common::PublicKey, - _parts: HashSet, + _parts: Signable, _user: (), ) -> Result { Err(Error::Other(format!( @@ -209,7 +210,7 @@ pub async fn sign_tx<'a, D, F, U>( args: &args::Tx, tx: &mut Tx, signing_data: SigningTxData, - sign: impl Fn(Tx, common::PublicKey, HashSet, D) -> F, + sign: impl Fn(Tx, common::PublicKey, Signable, D) -> F, user_data: D, ) -> Result<(), Error> where @@ -269,7 +270,7 @@ where if let Ok(ntx) = sign( tx.clone(), pubkey.clone(), - HashSet::from([Signable::RawHeader]), + Signable::RawHeader, user_data.clone(), ) .await @@ -292,30 +293,24 @@ where Ok(fee_payer_keypair) => { tx.sign_wrapper(fee_payer_keypair); } - // The case where tge fee payer also signs the inner transaction - Err(_) - if signing_data.public_keys.contains(&signing_data.fee_payer) => - { - *tx = sign( - tx.clone(), - signing_data.fee_payer.clone(), - HashSet::from([Signable::FeeHeader, Signable::RawHeader]), - user_data, - ) - .await?; - used_pubkeys.insert(signing_data.fee_payer.clone()); - } - // The case where the fee payer does not sign the inner transaction Err(_) => { *tx = sign( tx.clone(), signing_data.fee_payer.clone(), - HashSet::from([Signable::FeeHeader]), + Signable::FeeRawHeader, user_data, ) .await?; + if signing_data.public_keys.contains(&signing_data.fee_payer) { + used_pubkeys.insert(signing_data.fee_payer.clone()); + } } } + // Remove redundant sections now that the signing process is complete. + // Though this call might be redundant in circumstances, it is placed here + // as a safeguard to prevent the transmission of private data to the + // network. + tx.protocol_filter(); // Then make sure that the number of public keys used exceeds the threshold let used_pubkeys_len = used_pubkeys .len() diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs index 338ee84003..301b14b985 100644 --- a/crates/wallet/src/lib.rs +++ b/crates/wallet/src/lib.rs @@ -1202,10 +1202,8 @@ impl Wallet { ) .inspect(|alias| { // Cache the newly added key - self.decrypted_spendkey_cache.insert( - alias.clone(), - spend_key, - ); + self.decrypted_spendkey_cache + .insert(alias.clone(), spend_key); }) .map(Into::into) } diff --git a/crates/wallet/src/store.rs b/crates/wallet/src/store.rs index cb15a6c359..7eb42bbbc4 100644 --- a/crates/wallet/src/store.rs +++ b/crates/wallet/src/store.rs @@ -421,11 +421,15 @@ impl Store { } self.remove_alias(&alias); - let (spendkey_to_store, _raw_spendkey) = StoredKeypair::new(spendkey, password); + let (spendkey_to_store, _raw_spendkey) = + StoredKeypair::new(spendkey, password); self.spend_keys.insert(alias.clone(), spendkey_to_store); // Simultaneously add the derived viewing key to ease balance viewing birthday.map(|x| self.birthdays.insert(alias.clone(), x)); - self.view_keys.insert(alias.clone(), zip32::ExtendedFullViewingKey::from(&spendkey.into()).into()); + self.view_keys.insert( + alias.clone(), + zip32::ExtendedFullViewingKey::from(&spendkey.into()).into(), + ); path.map(|p| self.derivation_paths.insert(alias.clone(), p)); Some(alias) } From a375e6ec499a2da8eb6c11985548529b4688dfdd Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Fri, 27 Sep 2024 20:13:29 +0200 Subject: [PATCH 06/11] Moved the shielded keys used in the integration tests into the localnet wallet. --- crates/apps_lib/src/client/tx.rs | 6 +- crates/tests/src/e2e/setup.rs | 20 +- crates/tests/src/integration/masp.rs | 434 +++++------------- genesis/hardware/src/pre-genesis/wallet.toml | 10 + genesis/localnet/balances.toml | 15 + .../pre-genesis/unsigned-transactions.toml | 6 + genesis/localnet/src/pre-genesis/wallet.toml | 15 + 7 files changed, 171 insertions(+), 335 deletions(-) diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 319703b5e0..b040218675 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -49,17 +49,17 @@ use crate::wallet::{ // generated on the hardware wallet. It is hard to compute the exact required // number because a given MASP source could be distributed amongst several // notes. -const MAX_HW_SPEND: usize = 10; +const MAX_HW_SPEND: usize = 15; // Maximum number of convert description randomness parameters that can be // generated on the hardware wallet. It is hard to compute the exact required // number because the number of conversions that are used depends on the // protocol's current state. -const MAX_HW_CONVERT: usize = 10; +const MAX_HW_CONVERT: usize = 15; // Maximum number of output description randomness parameters that can be // generated on the hardware wallet. It is hard to compute the exact required // number because the number of outputs depends on the number of dummy outputs // introduced. -const MAX_HW_OUTPUT: usize = 10; +const MAX_HW_OUTPUT: usize = 15; /// Wrapper around `signing::aux_signing_data` that stores the optional /// disposable address to the wallet diff --git a/crates/tests/src/e2e/setup.rs b/crates/tests/src/e2e/setup.rs index b9b709a913..86aec5c961 100644 --- a/crates/tests/src/e2e/setup.rs +++ b/crates/tests/src/e2e/setup.rs @@ -1463,23 +1463,23 @@ pub mod constants { pub const FRANK_KEY: &str = "Frank-key"; // Shielded spending and viewing keys and payment addresses - pub const A_SPENDING_KEY: &str = "zsknam1qdrk9kd8qqqqpqy3pxzxu2kexydl7ug22s3808htl604emmz9qlde9cl9mx6euhvh3cpl9w7guustfzjxsyaeqtefhden6q8776t9cr9vkqztj7u0mgs5k9nz945sypev9ppptn5d85as3ccsnu3q6g3acqp2gpsrwe6naqg3stqp43uk9x2cj79gcxuum8a7jayjqlv4ptcfnunqkqzsj6m2r3sn8ft0tyqqpv28nghe4ag68eccaqx7v5f65he95g5uwq2wr4yuqc06jgc7"; - pub const B_SPENDING_KEY: &str = "zsknam1qdml0zguqqqqpqx8elavks722m0cjelgh3r044cfregyw049jze9lwha2cfqdqnekecnttdvygd6s784kch2v3wjs45g5z0n36hpqv5ruy8jjfu5mz2snl8ljyz79h3szmyf43zve79l6hwnlfk94r422tfwr2f62vvgkeqvc4z2dgrvqy033ymq5ylz3gmf6wdzhsdmzm0h9uv9374x755rzgvmcxhxntu6v63acqktv6zk390e9pd6vr0pzqaq6auu59kwpnw0haczfyju8"; + pub const A_SPENDING_KEY: &str = "albert-svk"; + pub const B_SPENDING_KEY: &str = "bertha-svk"; // A payment address derived from A_SPENDING_KEY - pub const AA_PAYMENT_ADDRESS: &str = "znam1ky620tz7z658cralqt693qpvk42wvth468zp38nqvq2apmex5rfut3dfqm2asrsqv0tc7saqje7"; + pub const AA_PAYMENT_ADDRESS: &str = "albert-pa"; // A payment address derived from B_SPENDING_KEY - pub const AB_PAYMENT_ADDRESS: &str = "znam1zxt8e22uz666ce7hxqpc69yfj3tpd9v26ep2epwn34kvyuwjh98hhre9897shcjj4cnqugwlv4q"; + pub const AB_PAYMENT_ADDRESS: &str = "bertha-pa-a"; // A viewing key derived from B_SPENDING_KEY - pub const AB_VIEWING_KEY: &str = "zvknam1qdml0zguqqqqpqx8elavks722m0cjelgh3r044cfregyw049jze9lwha2cfqdqnekem0xdqf9ytuhaxzeunyl7svgvxjv5g73m24k7w0h6q7wtvcltvlzynzhc5grlfgv7037lfh8w3su5krnzzzjh4nsleydtlns4gl0vmnc4z2dgrvqy033ymq5ylz3gmf6wdzhsdmzm0h9uv9374x755rzgvmcxhxntu6v63acqktv6zk390e9pd6vr0pzqaq6auu59kwpnw0hacdsfkws"; + pub const AB_VIEWING_KEY: &str = "bertha-svk"; // A payment address derived from B_VIEWING_KEY - pub const BB_PAYMENT_ADDRESS: &str = "znam1mqt0ja2zccy70du2d6rcr77jscgq3gkekfvhrqe7zkxa8rr3qsjsrd66gxnrykdmdeh5wmglmcm"; + pub const BB_PAYMENT_ADDRESS: &str = "bertha-pa-b"; // A viewing key derived from A_SPENDING_KEY - pub const AA_VIEWING_KEY: &str = "zvknam1qdrk9kd8qqqqpqy3pxzxu2kexydl7ug22s3808htl604emmz9qlde9cl9mx6euhvhnc63hymme53jz3mmwrzfkr9tk82nqacf5vlmj9du3s3rjz0h6usnh47pw0ufw4u6yrfvf95wfa9xj0m8pcrns9yh90s0jkf3cqy2z7c3stqp43uk9x2cj79gcxuum8a7jayjqlv4ptcfnunqkqzsj6m2r3sn8ft0tyqqpv28nghe4ag68eccaqx7v5f65he95g5uwq2wr4yuqc8djdrp"; - pub const C_SPENDING_KEY: &str = "zsknam1qdy5g4udqqqqpqrfdzej0s45m8s6nprder4udwqm3ql8wx34e8f46dv8cwnmcjp40uj3qy5tgetj27jytvxk4vpa3pjsd80y332nj542w39wta8lsrzqzs822ydgmz5g2sd2k29hxc3uh77v5cmcext799fxn6sa9rd3zuggl6flgjz7wz9wwu9kxd4rth4clw6ug4drxln96y96nf8fmvgm5eddm93azuzlkjj0dpw343ukwcfuvkdhd772539cskgggcqsaaf0j7czshjwe"; + pub const AA_VIEWING_KEY: &str = "albert-svk"; + pub const C_SPENDING_KEY: &str = "christel-svk"; // A viewing key derived from C_SPENDING_KEY - pub const AC_VIEWING_KEY: &str = "zvknam1qdy5g4udqqqqpqrfdzej0s45m8s6nprder4udwqm3ql8wx34e8f46dv8cwnmcjp40lr4vutffut7ed5x6egd6etcdh9sxh3j9fe5dshhrn3nq4yfp78gt8ve59y4vnu45xlt93vtrzsxtwlxjjgu2p496lc3ye8m83qplsqfl6flgjz7wz9wwu9kxd4rth4clw6ug4drxln96y96nf8fmvgm5eddm93azuzlkjj0dpw343ukwcfuvkdhd772539cskgggcqsaaf0j7cfyd3jr"; + pub const AC_VIEWING_KEY: &str = "christel-svk"; // A viewing key derived from C_VIEWING_KEY - pub const AC_PAYMENT_ADDRESS: &str = "znam1xv4ml6fp3zqjhw20xj3srd75cq8tyejdst0xweq60c70732ty2chd2v39tllpzf4uf6s66vfm6w"; + pub const AC_PAYMENT_ADDRESS: &str = "christel-pa"; // Native VP aliases pub const GOVERNANCE_ADDRESS: &str = "governance"; diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 108d18dfa3..051d257c08 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -20,6 +20,7 @@ use namada_sdk::DEFAULT_GAS_LIMIT; use test_log::test; use super::setup; +use crate::e2e::setup::apply_use_device; use crate::e2e::setup::constants::{ AA_PAYMENT_ADDRESS, AA_VIEWING_KEY, AB_PAYMENT_ADDRESS, AB_VIEWING_KEY, AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, ALBERT_KEY, A_SPENDING_KEY, @@ -46,7 +47,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -56,9 +57,11 @@ fn masp_incentives() -> Result<()> { BTC, "--amount", "1", + "--signing-keys", + ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -262,7 +265,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -272,9 +275,11 @@ fn masp_incentives() -> Result<()> { ETH, "--amount", "0.001", + "--signing-keys", + ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -372,7 +377,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.725514")); + assert!(captured.contains("nam: 0.750883")); // Assert NAM balance at MASP pool is an accumulation of // rewards from both the shielded BTC and shielded ETH @@ -392,7 +397,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.358764")); + assert!(captured.contains("nam: 1.384131")); // Wait till epoch boundary node.next_masp_epoch(); @@ -400,7 +405,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -414,7 +419,7 @@ fn masp_incentives() -> Result<()> { BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -472,7 +477,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.451732")); + assert!(captured.contains("nam: 1.502496")); node.next_masp_epoch(); // sync the shielded context @@ -501,7 +506,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 3.219616")); + assert!(captured.contains("nam: 3.270374")); // Wait till epoch boundary node.next_masp_epoch(); @@ -510,7 +515,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -524,7 +529,7 @@ fn masp_incentives() -> Result<()> { ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -592,7 +597,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 3.723616")); + assert!(captured.contains("nam: 3.774374")); // Wait till epoch boundary node.next_masp_epoch(); @@ -644,7 +649,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.451732")); + assert!(captured.contains("nam: 1.502496")); // Assert NAM balance at MASP pool is // the accumulation of rewards from the shielded assets (BTC and ETH) @@ -664,7 +669,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 3.723616")); + assert!(captured.contains("nam: 3.774374")); // Wait till epoch boundary to prevent conversion expiry during transaction // construction @@ -676,11 +681,12 @@ fn masp_incentives() -> Result<()> { vec!["shielded-sync", "--node", validator_one_rpc], )?; node.assert_success(); + // Send all NAM rewards from SK(B) to Christel run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -691,12 +697,12 @@ fn masp_incentives() -> Result<()> { "--gas-limit", "300000", "--amount", - "1.451732", + "1.502496", "--signing-keys", BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -709,11 +715,12 @@ fn masp_incentives() -> Result<()> { vec!["shielded-sync", "--node", validator_one_rpc], )?; node.assert_success(); + // Send all NAM rewards from SK(A) to Bertha run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -727,7 +734,7 @@ fn masp_incentives() -> Result<()> { ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -801,7 +808,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.003222")); + assert!(captured.contains("nam: 0.003216")); Ok(()) } @@ -829,7 +836,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -841,7 +848,7 @@ fn spend_unconverted_asset_type() -> Result<()> { "20", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -850,7 +857,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -862,7 +869,7 @@ fn spend_unconverted_asset_type() -> Result<()> { "0.000001", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -907,7 +914,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", B_SPENDING_KEY, @@ -921,7 +928,7 @@ fn spend_unconverted_asset_type() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -979,7 +986,7 @@ fn masp_txs_and_queries() -> Result<()> { let txs_args = vec![ // 0. Attempt to spend 10 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -993,12 +1000,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 1. Attempt to spend 15 BTC at SK(A) to Bertha ( - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1012,12 +1019,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 2. Send 20 BTC from Albert to PA(A) ( - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1029,12 +1036,12 @@ fn masp_txs_and_queries() -> Result<()> { "20", "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 3. Attempt to spend 10 ETH at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1048,12 +1055,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 4. Spend 7 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1067,12 +1074,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 5. Spend 7 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1086,12 +1093,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 6. Attempt to spend 7 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1105,12 +1112,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 7. Spend 6 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1124,7 +1131,7 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 8. Assert BTC balance at VK(A) is 0 @@ -1168,7 +1175,7 @@ fn masp_txs_and_queries() -> Result<()> { ), // 11. Send 20 BTC from SK(B) to Bertha ( - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -1182,7 +1189,7 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), ]; @@ -1288,41 +1295,12 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // 1. Shield tokens _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1334,14 +1312,14 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1353,14 +1331,14 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "200", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1372,7 +1350,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync shielded context @@ -1391,7 +1369,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1408,7 +1386,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -1425,7 +1403,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1442,7 +1420,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -1459,7 +1437,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", B_SPENDING_KEY, @@ -1476,7 +1454,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -1536,28 +1514,12 @@ fn expired_masp_tx() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // 1. Shield tokens _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1569,7 +1531,7 @@ fn expired_masp_tx() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync shielded context @@ -1587,7 +1549,7 @@ fn expired_masp_tx() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1614,7 +1576,7 @@ fn expired_masp_tx() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1720,7 +1682,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1730,9 +1692,11 @@ fn cross_epoch_unshield() -> Result<()> { NAM, "--amount", "1000", + "--signing-keys", + ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1755,7 +1719,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1772,7 +1736,7 @@ fn cross_epoch_unshield() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1791,7 +1755,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "tx", "--owner", ALBERT_KEY, @@ -1799,7 +1763,7 @@ fn cross_epoch_unshield() -> Result<()> { tx_path.to_str().unwrap(), "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1861,7 +1825,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1873,7 +1837,7 @@ fn dynamic_assets() -> Result<()> { "1", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync the shielded context @@ -1995,7 +1959,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -2007,7 +1971,7 @@ fn dynamic_assets() -> Result<()> { "1", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync the shielded context @@ -2357,40 +2321,11 @@ fn masp_fee_payment() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2404,7 +2339,7 @@ fn masp_fee_payment() -> Result<()> { CHRISTEL_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); _ = node.next_masp_epoch(); @@ -2438,7 +2373,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2455,7 +2390,7 @@ fn masp_fee_payment() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_err()); @@ -2574,7 +2509,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2589,7 +2524,7 @@ fn masp_fee_payment() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync shielded context @@ -2655,40 +2590,11 @@ fn masp_fee_payment_gas_limit() -> Result<()> { })?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2700,7 +2606,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { "1000000", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -2739,7 +2645,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -2756,7 +2662,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_err()); @@ -2805,40 +2711,11 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2854,7 +2731,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { BERTHA_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -2909,7 +2786,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -2927,7 +2804,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2994,53 +2871,11 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_c", - "--value", - AC_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3052,13 +2887,13 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "10000", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3070,7 +2905,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "300000", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -3124,7 +2959,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -3143,7 +2978,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3234,40 +3069,11 @@ fn masp_fee_payment_with_different_token() -> Result<()> { })?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3279,13 +3085,13 @@ fn masp_fee_payment_with_different_token() -> Result<()> { "1", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3299,13 +3105,13 @@ fn masp_fee_payment_with_different_token() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3319,7 +3125,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -3390,7 +3196,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -3411,7 +3217,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3516,27 +3322,11 @@ fn identical_output_descriptions() -> Result<()> { _ = node.next_masp_epoch(); let tempdir = tempfile::tempdir().unwrap(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Generate a tx to shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3555,7 +3345,7 @@ fn identical_output_descriptions() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -3701,7 +3491,7 @@ fn identical_output_descriptions() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -3716,7 +3506,7 @@ fn identical_output_descriptions() -> Result<()> { BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); diff --git a/genesis/hardware/src/pre-genesis/wallet.toml b/genesis/hardware/src/pre-genesis/wallet.toml index 7866cb9c2f..f349cb64cf 100644 --- a/genesis/hardware/src/pre-genesis/wallet.toml +++ b/genesis/hardware/src/pre-genesis/wallet.toml @@ -1,10 +1,17 @@ [birthdays] [view_keys] +albert-svk = "zvknam1qw7ysd8dqyqqpqp3rpe8e66t6y7elv4p5xgqh7904ck0ch87nsu8m3ftxnzzydg6l8q9yuzmczdgp9zjh4w8dtpmr880mn0r6wzefz35zdcucwqc8v2gz5cne222tszq20z9etczeeqs498zpjprfvkjl8uzqxcqp5a7665x6fuxdh8p8mn2haeryp8yjk4np6ka4xsdtmfw2hjwq3s7mk07pc6gjllc0u4yjnwu8fklf40rvcvgwqn6t87x97nd8ajqrgdffayv48q54ghg7" +bertha-svk = "zvknam1qw7ysd8dqgqqpqzvpu7a7edpt7rwfwk4y99f0j4ngvules039mt205ljusd0q5jcf6spkwcdgpruphr54exyxwsuxaxkdpgl60wnpa7vzzrsk3w94rqv3ul03efva6w3kvpucwcrmj8hravjhf0xg8nkyrl865hadhty5ye590gr7xempm62s38yw4nxepytnrjn6mnln7f0qgnhpkj5850nwrvqkpfygvvhy4wp9edm4j4haftmclvxeymcktepjul847klrasr6rctw0mw8" +christel-svk = "zvknam1qw7ysd8dqvqqpqxy9fcc0qc4ntgjz5rxnlds3ywqryel8xk9regvpynx0syfmmq8ngcagfgsd5g0rrlh0ux5trlsepwwlnhes24gx77zk3emft6nyxs6jjp7y6j2gph9h3y3utndzywsa365hfcj2em9svkgguu0hvm3ey4qj3y5hwr37vwmclchx8sztfc7547glxs9nhmtys78ysl5rekdqu8z0lw8cafejwj3ajce3vf86su2pa3pffrgqscmvacsehph82stsucn0r9gu" [spend_keys] [payment_addrs] +albert-pa = "znam1zsdp40r8e52vg33fyd4gg4tkcjvvmj554l24m76zn6404wkklpjluexvrmppz7jkr8t4vvwfyan" +bertha-pa-a = "znam13e60xy9n3ze7w385d00a5hzxp0qvwkg89kvqlv3gpv48h5n5w5d2shgqfjl5q0lp8zp9yx2kqvw" +bertha-pa-b = "znam1dz4wxnjwvyz0ymvptwgp2fkr0fsjsgcum28swj6nm9uajwa8rt0aynf0463q6h8cw4sqsd9rg6g" +christel-pa = "znam14gxuky9uh0md3uj7d3s60e2fwcne0nnu9w562lxjspdfvuawfjxuta4vp2nxv6rr9j72jmqpzf5" [secret_keys] faucet-key = "unencrypted:00548aa8393422b88dce5f4be8ee0320638061c3e0649ada1b0dacbec4c0c75bb2" @@ -22,8 +29,11 @@ frank-key = "ED25519_PK_PREFIXtpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav3 [derivation_paths] albert-key = "m/44'/877'/0'/0'/0'" +albert-svk = "m/32'/877'/1'" bertha-key = "m/44'/877'/0'/0'/1'" +bertha-svk = "m/32'/877'/2'" christel-key = "m/44'/877'/0'/0'/2'" +christel-svk = "m/32'/877'/3'" daewon = "m/44'/877'/0'/0'/3'" ester = "m/44'/877'/0'/0'/4'" validator-0-account-key = "m/44'/877'/0'/0'/5'" diff --git a/genesis/localnet/balances.toml b/genesis/localnet/balances.toml index e4db4e5c85..64f5facdbf 100644 --- a/genesis/localnet/balances.toml +++ b/genesis/localnet/balances.toml @@ -33,6 +33,9 @@ tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "200000" tnam1qrdrgx6d3rzl2f5yn6nde6wg20sl6kmtug3ecg7z = "200000" # validator-0-account-key tnam1qzwnw8rdyg8c5nrdwrcd87pgdxygr2eyguzy9c44 = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "2000000" +tnam1qq0345c0vr04wyjfkp0lfqkdp5n0t7ua4cswzkhm = "2000000" [token.BTC] # albert @@ -47,6 +50,8 @@ tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn = "1000000" tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q = "1000000" # validator-0 tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "1000000" [token.ETH] # albert @@ -61,6 +66,8 @@ tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn = "1000000" tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q = "1000000" # validator-0 tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "1000000" [token.DOT] @@ -76,6 +83,8 @@ tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn = "1000000" tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q = "1000000" # validator-0 tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "1000000" [token.Schnitzel] # albert @@ -90,6 +99,8 @@ tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn = "1000000" tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q = "1000000" # validator-0 tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "1000000" [token.Apfel] # albert @@ -104,6 +115,8 @@ tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn = "1000000" tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q = "1000000" # validator-0 tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "1000000" [token.Kartoffel] # albert @@ -118,3 +131,5 @@ tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn = "1000000" tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q = "1000000" # validator-0 tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" +# frank +tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn = "1000000" diff --git a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml index 32dc9fff47..784e8846ba 100644 --- a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml +++ b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml @@ -28,6 +28,12 @@ vp = "vp_user" threshold = 1 public_keys = ["tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79"] +# Frank +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qz0vuxgzsz5lu7peghyr4yx5tuexecm27hdgglkpe444v6nmy6d8yuxhn0r"] + ########################################################################################## # Albert bonds to `validator-0` diff --git a/genesis/localnet/src/pre-genesis/wallet.toml b/genesis/localnet/src/pre-genesis/wallet.toml index eb231c1e0e..71cac3309f 100644 --- a/genesis/localnet/src/pre-genesis/wallet.toml +++ b/genesis/localnet/src/pre-genesis/wallet.toml @@ -1,10 +1,20 @@ [birthdays] [view_keys] +albert-svk = "zvknam1qdrk9kd8qqqqpqy3pxzxu2kexydl7ug22s3808htl604emmz9qlde9cl9mx6euhvhnc63hymme53jz3mmwrzfkr9tk82nqacf5vlmj9du3s3rjz0h6usnh47pw0ufw4u6yrfvf95wfa9xj0m8pcrns9yh90s0jkf3cqy2z7c3stqp43uk9x2cj79gcxuum8a7jayjqlv4ptcfnunqkqzsj6m2r3sn8ft0tyqqpv28nghe4ag68eccaqx7v5f65he95g5uwq2wr4yuqc8djdrp" +bertha-svk = "zvknam1qdml0zguqqqqpqx8elavks722m0cjelgh3r044cfregyw049jze9lwha2cfqdqnekem0xdqf9ytuhaxzeunyl7svgvxjv5g73m24k7w0h6q7wtvcltvlzynzhc5grlfgv7037lfh8w3su5krnzzzjh4nsleydtlns4gl0vmnc4z2dgrvqy033ymq5ylz3gmf6wdzhsdmzm0h9uv9374x755rzgvmcxhxntu6v63acqktv6zk390e9pd6vr0pzqaq6auu59kwpnw0hacdsfkws" +christel-svk = "zvknam1qdy5g4udqqqqpqrfdzej0s45m8s6nprder4udwqm3ql8wx34e8f46dv8cwnmcjp40lr4vutffut7ed5x6egd6etcdh9sxh3j9fe5dshhrn3nq4yfp78gt8ve59y4vnu45xlt93vtrzsxtwlxjjgu2p496lc3ye8m83qplsqfl6flgjz7wz9wwu9kxd4rth4clw6ug4drxln96y96nf8fmvgm5eddm93azuzlkjj0dpw343ukwcfuvkdhd772539cskgggcqsaaf0j7cfyd3jr" [spend_keys] +albert-svk = "unencrypted:zsknam1qdrk9kd8qqqqpqy3pxzxu2kexydl7ug22s3808htl604emmz9qlde9cl9mx6euhvh3cpl9w7guustfzjxsyaeqtefhden6q8776t9cr9vkqztj7u0mgs5k9nz945sypev9ppptn5d85as3ccsnu3q6g3acqp2gpsrwe6naqg3stqp43uk9x2cj79gcxuum8a7jayjqlv4ptcfnunqkqzsj6m2r3sn8ft0tyqqpv28nghe4ag68eccaqx7v5f65he95g5uwq2wr4yuqc06jgc7" +bertha-svk = "unencrypted:zsknam1qdml0zguqqqqpqx8elavks722m0cjelgh3r044cfregyw049jze9lwha2cfqdqnekecnttdvygd6s784kch2v3wjs45g5z0n36hpqv5ruy8jjfu5mz2snl8ljyz79h3szmyf43zve79l6hwnlfk94r422tfwr2f62vvgkeqvc4z2dgrvqy033ymq5ylz3gmf6wdzhsdmzm0h9uv9374x755rzgvmcxhxntu6v63acqktv6zk390e9pd6vr0pzqaq6auu59kwpnw0haczfyju8" +christel-svk = "unencrypted:zsknam1qdy5g4udqqqqpqrfdzej0s45m8s6nprder4udwqm3ql8wx34e8f46dv8cwnmcjp40uj3qy5tgetj27jytvxk4vpa3pjsd80y332nj542w39wta8lsrzqzs822ydgmz5g2sd2k29hxc3uh77v5cmcext799fxn6sa9rd3zuggl6flgjz7wz9wwu9kxd4rth4clw6ug4drxln96y96nf8fmvgm5eddm93azuzlkjj0dpw343ukwcfuvkdhd772539cskgggcqsaaf0j7czshjwe" [payment_addrs] +albert-pa = "znam1ky620tz7z658cralqt693qpvk42wvth468zp38nqvq2apmex5rfut3dfqm2asrsqv0tc7saqje7" +bertha-pa-a = "znam1zxt8e22uz666ce7hxqpc69yfj3tpd9v26ep2epwn34kvyuwjh98hhre9897shcjj4cnqugwlv4q" +bertha-pa-b = "znam1mqt0ja2zccy70du2d6rcr77jscgq3gkekfvhrqe7zkxa8rr3qsjsrd66gxnrykdmdeh5wmglmcm" +christel-pa = "znam1xv4ml6fp3zqjhw20xj3srd75cq8tyejdst0xweq60c70732ty2chd2v39tllpzf4uf6s66vfm6w" [secret_keys] albert-key = "unencrypted:000d5e9d7d66f0e4307edacde6e6578e31d331bcf234352647d00d20955102d3ce" @@ -13,6 +23,7 @@ christel-key = "unencrypted:00a08de8d33b9798328d2e4476fade49f515dc82754451fc6ef7 daewon = "unencrypted:00d19e226c0e7d123d79f5908b5948d4c461b66a5f8aa95600c28b55ab6f5dc772" ester = "unencrypted:01369093e2035d84f72a7e5a17c89b7a938b5d08cc87b2289805e3afcc66ef9a42" faucet-key = "unencrypted:00548aa8393422b88dce5f4be8ee0320638061c3e0649ada1b0dacbec4c0c75bb2" +frank-key = "unencrypted:00a925a940b1a82fbafd8f4c1e4f49ae697b6f4e71540320c7603f8eef2341158c" validator-0-account-key = "unencrypted:0024204e13c51b26ed9b42c05647bc46b3821bb453e53d194962ede57ce5ec66ac" [public_keys] @@ -22,6 +33,7 @@ christel-key = "ED25519_PK_PREFIXtpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkax daewon = "ED25519_PK_PREFIXtpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm" ester = "SECP256K1_PK_PREFIXtpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4" faucet-key = "ED25519_PK_PREFIXtpknam1qzh2d8vk9wvj2j63fa3cvjru9uldpdjctjjxpafl5r8vwwf56pdgyq0vra4" +frank-key = "ED25519_PK_PREFIXtpknam1qz0vuxgzsz5lu7peghyr4yx5tuexecm27hdgglkpe444v6nmy6d8yuxhn0r" validator-0-account-key = "ED25519_PK_PREFIXtpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj" [derivation_paths] @@ -37,6 +49,8 @@ daewon = "tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn" ester = "tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q" faucet-key = "tnam1qzgcl2znamndmku7ujw6e79dmvd4v7rfd5c89dfz" validator-0 = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +frank = "tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn" +frank-key = "tnam1qq0345c0vr04wyjfkp0lfqkdp5n0t7ua4cswzkhm" [pkhs] 30A4674B47A0F8CFF0BC8746F8AD012438C7BD3A = "bertha-key" @@ -46,5 +60,6 @@ C671BDF31A7552BD2928909151461CD376CBBE18 = "albert-key" 9F36671A3EB250DA0724EFBA35A03DB3E92B3925 = "christel-key" 7716860CF696BE44EA6250858FDB3CDEF9F63049 = "ester" 9D371C6D220F8A4C6D70F0D3F828698881AB2447 = "validator-0-account-key" +1F1AD30F60DF571249B05FF482CD0D26F5FB9DAE = "frank-key" [address_vp_types] From 91c5b2a024012916736b7216c15159f7566ca2d3 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 29 Sep 2024 18:33:57 +0200 Subject: [PATCH 07/11] Dont do a dry run if using a device in the MASP integration tests. --- crates/tests/src/integration/masp.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 051d257c08..15995c25f6 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; -use namada_apps_lib::wallet::defaults::{albert_keypair, christel_keypair}; +use namada_apps_lib::wallet::defaults::{albert_keypair, christel_keypair, is_use_device}; use namada_core::dec::Dec; use namada_core::masp::TokenMap; use namada_node::shell::testing::client::run; @@ -1214,7 +1214,10 @@ fn masp_txs_and_queries() -> Result<()> { Bin::Client, vec!["shielded-sync", "--node", validator_one_rpc], )?; - let tx_args = if dry_run { + let tx_args = if dry_run && is_use_device() + { + continue; + } else if dry_run { [tx_args.clone(), vec!["--dry-run"]].concat() } else { tx_args.clone() From 04432f400a3adeeb2c4281634dc476a5d83b5b2d Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Mon, 30 Sep 2024 12:52:19 +0200 Subject: [PATCH 08/11] Always sign MASP Transactions before dumping because randomness parameters cannot be reused. Fixed MASP integration tests depending on access to secret keys. --- crates/apps_lib/src/client/tx.rs | 6 ++--- crates/apps_lib/src/wallet/defaults.rs | 13 +++++++--- crates/tests/src/integration/masp.rs | 34 ++++++++++++++++---------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index b040218675..925e3771c1 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -1169,11 +1169,11 @@ pub async fn submit_shielded_transfer( .await?; let (mut tx, signing_data) = args.clone().build(namada, &mut bparams).await?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { - masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; sign(namada, &mut tx, &args.tx, signing_data).await?; namada.submit(tx, &args.tx).await?; } @@ -1261,11 +1261,11 @@ pub async fn submit_unshielding_transfer( .await?; let (mut tx, signing_data) = args.clone().build(namada, &mut bparams).await?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { - masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; sign(namada, &mut tx, &args.tx, signing_data).await?; namada.submit(tx, &args.tx).await?; } @@ -1294,11 +1294,11 @@ where ) .await?; let (mut tx, signing_data, _) = args.build(namada, &mut bparams).await?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { - masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; batch_opt_reveal_pk_and_submit( namada, &args.tx, diff --git a/crates/apps_lib/src/wallet/defaults.rs b/crates/apps_lib/src/wallet/defaults.rs index 9a97a60ff3..b2dd13b4d0 100644 --- a/crates/apps_lib/src/wallet/defaults.rs +++ b/crates/apps_lib/src/wallet/defaults.rs @@ -4,9 +4,10 @@ pub use dev::{ addresses, albert_address, albert_keypair, bertha_address, bertha_keypair, christel_address, christel_keypair, daewon_address, daewon_keypair, - derive_template_dir, ester_address, ester_keypair, get_unencrypted_keypair, - is_use_device, keys, tokens, validator_account_keypair, validator_address, - validator_keypair, validator_keys, + derive_template_dir, ester_address, ester_keypair, frank_keypair, + get_unencrypted_keypair, is_use_device, keys, tokens, + validator_account_keypair, validator_address, validator_keypair, + validator_keys, }; #[cfg(any(test, feature = "testing", feature = "benches"))] @@ -46,6 +47,7 @@ mod dev { ("albert".into(), albert_keypair()), ("bertha".into(), bertha_keypair()), ("christel".into(), christel_keypair()), + ("frank".into(), frank_keypair()), ("daewon".into(), daewon_keypair()), ("ester".into(), ester_keypair()), ("validator".into(), validator_keypair()), @@ -175,6 +177,11 @@ mod dev { get_unencrypted_keypair("ester") } + /// Get frank's keypair from the pre-genesis wallet. + pub fn frank_keypair() -> common::SecretKey { + get_unencrypted_keypair("frank-key") + } + /// Get the validator consensus keypair from the wallet. pub fn validator_keypair() -> common::SecretKey { VALIDATOR_WALLET.consensus_key.clone() diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 15995c25f6..9c5b89e4a1 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; -use namada_apps_lib::wallet::defaults::{albert_keypair, christel_keypair, is_use_device}; +use namada_apps_lib::wallet::defaults::{ + get_unencrypted_keypair, is_use_device, +}; use namada_core::dec::Dec; use namada_core::masp::TokenMap; use namada_node::shell::testing::client::run; @@ -20,13 +22,13 @@ use namada_sdk::DEFAULT_GAS_LIMIT; use test_log::test; use super::setup; -use crate::e2e::setup::apply_use_device; use crate::e2e::setup::constants::{ AA_PAYMENT_ADDRESS, AA_VIEWING_KEY, AB_PAYMENT_ADDRESS, AB_VIEWING_KEY, AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, ALBERT_KEY, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BERTHA_KEY, BTC, B_SPENDING_KEY, CHRISTEL, CHRISTEL_KEY, ETH, MASP, NAM, }; +use crate::e2e::setup::{apply_use_device, ensure_hot_key}; use crate::strings::TX_APPLIED_SUCCESS; /// In this test we verify that users of the MASP receive the correct rewards @@ -1214,8 +1216,7 @@ fn masp_txs_and_queries() -> Result<()> { Bin::Client, vec!["shielded-sync", "--node", validator_one_rpc], )?; - let tx_args = if dry_run && is_use_device() - { + let tx_args = if dry_run && is_use_device() { continue; } else if dry_run { [tx_args.clone(), vec!["--dry-run"]].concat() @@ -1471,7 +1472,9 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { txs_bytes.push(std::fs::read(&file_path).unwrap()); std::fs::remove_file(&file_path).unwrap(); - let sk = christel_keypair(); + let sk = get_unencrypted_keypair( + &ensure_hot_key(CHRISTEL_KEY).to_ascii_lowercase(), + ); let pk = sk.to_public(); let native_token = node @@ -1594,7 +1597,9 @@ fn expired_masp_tx() -> Result<()> { let tx_bytes = std::fs::read(&file_path).unwrap(); std::fs::remove_file(&file_path).unwrap(); - let sk = christel_keypair(); + let sk = get_unencrypted_keypair( + &ensure_hot_key(CHRISTEL_KEY).to_ascii_lowercase(), + ); let pk = sk.to_public(); let native_token = node @@ -3332,7 +3337,7 @@ fn identical_output_descriptions() -> Result<()> { apply_use_device(vec![ "shield", "--source", - ALBERT_KEY, + ensure_hot_key(ALBERT_KEY), "--target", AA_PAYMENT_ADDRESS, "--token", @@ -3342,7 +3347,7 @@ fn identical_output_descriptions() -> Result<()> { "--gas-limit", "300000", "--gas-payer", - ALBERT_KEY, + ensure_hot_key(ALBERT_KEY), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-tx", @@ -3374,12 +3379,15 @@ fn identical_output_descriptions() -> Result<()> { tx_clone.header.batch.clear(); tx_clone.header.batch.insert(cmt); + let keypair = get_unencrypted_keypair( + &ensure_hot_key(ALBERT_KEY).to_ascii_lowercase(), + ); let signing_data = SigningTxData { owner: None, - public_keys: vec![albert_keypair().to_public()], + public_keys: vec![keypair.to_public()], threshold: 1, account_public_keys_map: None, - fee_payer: albert_keypair().to_public(), + fee_payer: keypair.to_public(), shielded_hash: None, }; @@ -3390,13 +3398,13 @@ fn identical_output_descriptions() -> Result<()> { .unwrap(); batched_tx.sign_raw( - vec![albert_keypair()], + vec![keypair.clone()], AccountPublicKeysMap::from_iter( - vec![(albert_keypair().to_public())].into_iter(), + vec![(keypair.to_public())].into_iter(), ), None, ); - batched_tx.sign_wrapper(albert_keypair()); + batched_tx.sign_wrapper(keypair); let wrapper_hash = batched_tx.wrapper_hash(); let inner_cmts = batched_tx.commitments(); From 69704a018feb67de1dd6f474a1c59fd39c187292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 3 Oct 2024 13:58:07 +0100 Subject: [PATCH 09/11] test/e2e/masp: add support for testing with HW wallet --- crates/tests/src/e2e/ledger_tests.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index dceed2b2ac..87e3cf47c7 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -25,6 +25,7 @@ use namada_apps_lib::config::utils::convert_tm_addr_to_socket_addr; use namada_apps_lib::config::{self, ethereum_bridge}; use namada_apps_lib::tendermint_config::net::Address as TendermintAddress; use namada_apps_lib::wallet; +use namada_apps_lib::wallet::defaults::is_use_device; use namada_core::chain::ChainId; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::address::Address; @@ -2501,7 +2502,7 @@ fn masp_txs_and_queries() -> Result<()> { let txs_args = vec![ // 2. Shield 20 BTC from Albert to PA(A) ( - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -2511,12 +2512,12 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "20", - ], + ]), TX_APPLIED_SUCCESS, ), // 3. Transfer 7 BTC from SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2528,7 +2529,7 @@ fn masp_txs_and_queries() -> Result<()> { "7", "--gas-payer", CHRISTEL_KEY, - ], + ]), TX_APPLIED_SUCCESS, ), // 4. Assert BTC balance at VK(A) is 13 @@ -2538,7 +2539,7 @@ fn masp_txs_and_queries() -> Result<()> { ), // 5. Unshield 5 BTC from SK(B) to Bertha ( - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -2550,7 +2551,7 @@ fn masp_txs_and_queries() -> Result<()> { "5", "--gas-payer", CHRISTEL_KEY, - ], + ]), TX_APPLIED_SUCCESS, ), // 6. Assert BTC balance at VK(B) is 2 @@ -2571,6 +2572,9 @@ fn masp_txs_and_queries() -> Result<()> { )?; sync.assert_success(); for &dry_run in &[true, false] { + if dry_run && is_use_device() { + continue; + } let tx_args = if dry_run && (tx_args[0] == "transfer" || tx_args[0] == "shield" From 96ea169c2b4281aac78491c270f9f854bd565d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 4 Oct 2024 11:29:55 +0100 Subject: [PATCH 10/11] test/e2e/masp_txs_and_queries: add speculos automation --- crates/tests/src/e2e/ledger_tests.rs | 34 +++ crates/tests/src/hw_wallet_automation.rs | 337 +++++++++++++++++++++++ 2 files changed, 371 insertions(+) diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index 87e3cf47c7..ff3c48e1ea 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -2465,6 +2465,36 @@ fn masp_txs_and_queries() -> Result<()> { }, None, )?; + + // If used, keep Speculos alive for duration of the test + let mut speculos: Option = None; + if hw_wallet_automation::uses_automation() { + // Gen automation for Speculos + let automation = + hw_wallet_automation::gen_automation_e2e_masp_tx_and_queries(); + let json = serde_json::to_vec_pretty(&automation).unwrap(); + let path = test.test_dir.path().join("automation.json"); + std::fs::write(&path, json).unwrap(); + + // Start Speculos with the automation + speculos = Some( + Command::new(speculos_path()) + .args([ + &speculos_app_elf(), + "--seed", + hw_wallet_automation::SEED, + "--automation", + &format!("file:{}", path.to_string_lossy()), + "--log-level", + "automation:DEBUG", + "--display", + "headless", + ]) + .spawn() + .unwrap(), + ); + } + // Run all cmds on the first validator let who = Who::Validator(0); set_ethereum_bridge_mode( @@ -2591,6 +2621,10 @@ fn masp_txs_and_queries() -> Result<()> { } } + if let Some(mut process) = speculos { + process.kill().unwrap() + } + Ok(()) } diff --git a/crates/tests/src/hw_wallet_automation.rs b/crates/tests/src/hw_wallet_automation.rs index 1773a107ac..c1e4316982 100644 --- a/crates/tests/src/hw_wallet_automation.rs +++ b/crates/tests/src/hw_wallet_automation.rs @@ -248,6 +248,343 @@ pub fn gen_automation_e2e_pos_bonds() -> Automation { gen_automation(steps) } +// Generate automation file for `e2e::ledger_tests::masp_tx_and_queries` +pub fn gen_automation_e2e_masp_tx_and_queries() -> Automation { + let view_key_steps = || -> Steps { + [ + please_review_steps(), + vec![Step::Expect { + text: Text("Ext Full View Key (1/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Ext Full View Key (2/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Ext Full View Key (3/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Ext Full V...w Key (4/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Ext Full View Key (5/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Ext Full V...w Key (6/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("HD Path"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("APPROVE"), + action: Some(PressAndRelease(Button::Both)), + }], + ] + .concat() + }; + + let shielded_transfer_steps = || -> Steps { + [ + please_review_steps(), + vec![ + Step::Expect { + text: Text("Type"), + action: None, + }, + Step::Expect { + text: Text("Transfer"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("Sender (1/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (2/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (3/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (4/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (5/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (6/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sending Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Sending Amount"), + action: None, + }, + Step::Expect { + text: Text("20.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + // Receiver of the transferred amount + vec![Step::Expect { + text: Text("Destination (1/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Destination (2/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Receiving Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Receiving Amount"), + action: None, + }, + Step::Expect { + text: Text("7.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + // The change of balance that is kept by the sender + vec![Step::Expect { + text: Text("Destination (1/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Destination (2/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Receiving Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Receiving Amount"), + action: None, + }, + Step::Expect { + text: Text("13.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("APPROVE"), + action: Some(PressAndRelease(Button::Both)), + }], + ] + .concat() + }; + + let unshielding_transfer_steps = || -> Steps { + [ + please_review_steps(), + vec![ + Step::Expect { + text: Text("Type"), + action: None, + }, + Step::Expect { + text: Text("Transfer"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("Sender (1/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (2/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (3/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (4/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (5/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sender (6/6)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Sending Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Sending Amount"), + action: None, + }, + Step::Expect { + text: Text("7.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + // Receiver of the unshielded amount + vec![Step::Expect { + text: Text("Destination"), + action: None, + }], + address_steps(), + vec![Step::Expect { + text: Text("Receiving Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Receiving Amount"), + action: None, + }, + Step::Expect { + text: Text("5.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + // The change of balance that is kept by the sender + vec![Step::Expect { + text: Text("Destination (1/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Destination (2/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Receiving Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Receiving Amount"), + action: None, + }, + Step::Expect { + text: Text("2.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("APPROVE"), + action: Some(PressAndRelease(Button::Both)), + }], + ] + .concat() + }; + + let steps: Steps = [ + // _____________________________________________________________________ + // 1. tx - shield + please_review_steps(), + vec![ + Step::Expect { + text: Text("Type"), + action: None, + }, + Step::Expect { + text: Text("Transfer"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("Sender"), + action: None, + }], + address_steps(), + vec![Step::Expect { + text: Text("Sending Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Sending Amount"), + action: None, + }, + Step::Expect { + text: Text("20.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("Destination (1/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Destination (2/2)"), + action: Some(PressAndRelease(Button::Right)), + }], + vec![Step::Expect { + text: Text("Receiving Token"), + action: None, + }], + address_steps(), + vec![ + Step::Expect { + text: Text("Receiving Amount"), + action: None, + }, + Step::Expect { + text: Text("20.0"), + action: Some(PressAndRelease(Button::Right)), + }, + ], + vec![Step::Expect { + text: Text("APPROVE"), + action: Some(PressAndRelease(Button::Both)), + }], + // _____________________________________________________________________ + // 2. tx - shielded transfer + view_key_steps(), + // The same steps are repeated twice, first to generate the signature + // on the device ... + shielded_transfer_steps(), + // ... then to obtain the signature out of the device + shielded_transfer_steps(), + // _____________________________________________________________________ + // 3. tx - unshielding transfer + view_key_steps(), + // The same steps are repeated twice, first to generate the signature + // on the device ... + unshielding_transfer_steps(), + // ... then to obtain the signature out of the device + unshielding_transfer_steps(), + ] + .concat(); + + gen_automation(steps) +} + type Steps = Vec; #[derive(Debug, Clone)] From 054614e444eab904c2e0b73a847837945a7686eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 4 Oct 2024 11:30:19 +0100 Subject: [PATCH 11/11] ci: add masp test with device automation --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f4a3dd74e..80b09c60ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -710,7 +710,7 @@ jobs: chmod +x /usr/local/bin/hermes - name: Run e2e tests with device automation id: e2e - run: cargo +${{ env.NIGHTLY }} test --lib "e2e::ledger_tests::pos_bonds" -- --exact + run: cargo +${{ env.NIGHTLY }} test --lib -- "e2e::ledger_tests::pos_bonds" "e2e::ledger_tests::masp_txs_and_queries" --exact env: NAMADA_SPECULOS_PATH: "/root/.local/pipx/venvs/speculos/bin/speculos" NAMADA_SPECULOS_APP_ELF: "/ledger-namada/app_s2.elf"