diff --git a/Cargo.lock b/Cargo.lock index 7861b507ae..7c23c99f23 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=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=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +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=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" 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 c6699f1ce5..e6d0b634ca 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 = "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/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.rs b/crates/apps_lib/src/cli.rs index 36e0de0d7c..26d8cb7a7f 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -7766,8 +7766,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 6542a55b63..6269773784 100644 --- a/crates/apps_lib/src/cli/client.rs +++ b/crates/apps_lib/src/cli/client.rs @@ -3,6 +3,7 @@ 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::wallet::DatedViewingKey; use namada_sdk::{Namada, NamadaImpl}; use crate::cli; @@ -349,8 +350,16 @@ 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 63cf70fea2..15984847e1 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 @@ -583,12 +588,51 @@ 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)) }) } } +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, + )) + }) + .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)) + }) + .map_err(|_find_err| format!("Unknown viewing key {}", raw)) + }) + } +} + impl ArgFromMutContext for DatedSpendingKey { fn arg_from_mut_ctx( ctx: &mut ChainContext, @@ -598,9 +642,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())) }) } } @@ -617,7 +664,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)) }) } @@ -632,10 +678,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())) }) } } @@ -667,8 +715,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 26b2dd0df5..cc95af5ab0 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -4,11 +4,22 @@ use std::io::Write; use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use color_eyre::owo_colors::OwoColorize; -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, PseudoExtendedKey, +}; use namada_core::masp::MaspTransaction; use namada_sdk::address::{Address, ImplicitAddress}; use namada_sdk::args::TxBecomeValidator; -use namada_sdk::collections::HashSet; +use namada_sdk::collections::HashMap; use namada_sdk::governance::cli::onchain::{ DefaultProposal, PgfFundingProposal, PgfStewardProposal, }; @@ -21,7 +32,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; @@ -36,6 +47,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 = 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 = 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 = 15; + /// Wrapper around `signing::aux_signing_data` that stores the optional /// disposable address to the wallet pub async fn aux_signing_data( @@ -78,7 +105,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 @@ -125,7 +152,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 = @@ -143,7 +172,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 = @@ -826,9 +855,313 @@ 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 + } +} + +// 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, + 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.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 source in sources { + // Only attempt an augmentation if proof authorization is not there + if source.to_spending_key().is_none() { + // First find the derivation path corresponding to this viewing + // key + let viewing_key = + ExtendedViewingKey::from(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 + 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. + source.augment_spend_authorizing_key_unchecked(PrivateKey( + jubjub::Fr::default(), + )); + 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( + 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.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| { + 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(); + 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(), + }); + } + 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() + }); + } + Ok(Box::new(bparams)) + } else { + Ok(Box::new(RngBuildParams::new(OsRng))) + } +} + +// 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()))?; + 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)); + } + Ok(()) +} + pub async fn submit_shielded_transfer( namada: &impl Namada, - args: args::TxShieldedTransfer, + mut args: args::TxShieldedTransfer, ) -> Result<(), error::Error> { display_line!( namada.io(), @@ -839,7 +1172,23 @@ pub async fn submit_shielded_transfer( this command.", ); - let (mut tx, signing_data) = args.clone().build(namada).await?; + let sources = args + .data + .iter_mut() + .map(|x| &mut x.source) + .chain(args.gas_spending_key.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?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; let masp_section = tx .sections @@ -867,7 +1216,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 || args.tx.dump_wrapper_tx { tx::dump_tx(namada.io(), &args.tx, tx)?; @@ -919,7 +1276,7 @@ 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> { display_line!( namada.io(), @@ -930,7 +1287,20 @@ pub async fn submit_unshielding_transfer( this command.", ); - let (mut tx, signing_data) = args.clone().build(namada).await?; + let sources = std::iter::once(&mut args.source) + .chain(args.gas_spending_key.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?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; let masp_section = tx .sections @@ -954,12 +1324,27 @@ 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_key.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?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; let opt_masp_section = tx.sections.iter().find_map(|section| section.masp_tx()); diff --git a/crates/apps_lib/src/config/genesis/transactions.rs b/crates/apps_lib/src/config/genesis/transactions.rs index 2873488b1d..cb3e3fb7fa 100644 --- a/crates/apps_lib/src/config/genesis/transactions.rs +++ b/crates/apps_lib/src/config/genesis/transactions.rs @@ -773,6 +773,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(); @@ -794,7 +795,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/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/benches/native_vps.rs b/crates/benches/native_vps.rs index f2350ca722..474067abcd 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).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).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).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 ea6d915e4d..13cda85ba3 100644 --- a/crates/core/src/masp.rs +++ b/crates/core/src/masp.rs @@ -13,6 +13,7 @@ use masp_primitives::transaction::TransparentAddress; pub use masp_primitives::transaction::{ Transaction as MaspTransaction, TxId as TxIdInner, }; +use masp_primitives::zip32::{ExtendedKey, PseudoExtendedKey}; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; @@ -69,7 +70,7 @@ pub struct MaspTxId( serialize_with = "serialize_txid", deserialize_with = "deserialize_txid" )] - TxIdInner, + pub TxIdInner, ); impl From for MaspTxId { @@ -532,12 +533,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 { @@ -552,13 +554,21 @@ 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, } } + /// 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 { @@ -580,7 +590,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) + } } } } @@ -815,12 +827,10 @@ mod test { let addr = address::testing::established_address_1(); assert_eq!(addr.to_string(), TransferSource::Address(addr).to_string()); - let sk = ExtendedSpendingKey::from( - masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]), - ); + let sk = masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]); assert_eq!( - sk.to_string(), - TransferSource::ExtendedSpendingKey(sk).to_string() + ExtendedSpendingKey::from(sk).to_string(), + TransferSource::ExtendedSpendingKey(sk.into()).to_string() ); } @@ -831,11 +841,10 @@ mod test { .address(); assert_eq!(addr.unwrap(), address::testing::established_address_1()); - let addr = - TransferSource::ExtendedSpendingKey(ExtendedSpendingKey::from( - masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]), - )) - .address(); + let addr = TransferSource::ExtendedSpendingKey( + masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]).into(), + ) + .address(); assert!(addr.is_none()); } @@ -849,11 +858,10 @@ mod test { TAddrData::Addr(address::testing::established_address_1()) ); - let addr = - TransferSource::ExtendedSpendingKey(ExtendedSpendingKey::from( - masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]), - )) - .address(); + let addr = TransferSource::ExtendedSpendingKey( + masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]).into(), + ) + .address(); assert!(addr.is_none()); } @@ -866,10 +874,8 @@ mod test { address::testing::established_address_1() ); - let sk = ExtendedSpendingKey::from( - masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]), - ); - let source = TransferSource::ExtendedSpendingKey(sk); + let sk = masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]); + let source = TransferSource::ExtendedSpendingKey(sk.into()); assert_eq!(source.effective_address(), MASP); } diff --git a/crates/node/src/bench_utils.rs b/crates/node/src/bench_utils.rs index b59c03e353..948d94d9fb 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; @@ -105,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, @@ -1135,7 +1136,6 @@ impl Default for BenchShieldedCtx { .wallet .find_viewing_key(viewing_alias) .unwrap() - .key .to_string(), ); let viewing_key = ExtendedFullViewingKey::from( @@ -1178,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, @@ -1223,6 +1227,7 @@ impl BenchShieldedCtx { vec![masp_transfer_data], None, expiration, + &mut RngBuildParams::new(OsRng), ) .await }) diff --git a/crates/node/src/shell/testing/client.rs b/crates/node/src/shell/testing/client.rs index 21ff520bff..8bf1236b4a 100644 --- a/crates/node/src/shell/testing/client.rs +++ b/crates/node/src/shell/testing/client.rs @@ -47,11 +47,15 @@ pub fn run( NamadaClient::WithoutContext(Box::new((sub_cmd, global))) } }; - rt.block_on(CliApi::handle_client_command( + let result = rt.block_on(CliApi::handle_client_command( Some(node.clone()), cmd, TestingIo, - )) + )); + if let Err(err) = &result { + TestingIo.eprintln(format!("{}", err)); + } + result } Bin::Wallet => { args.insert(0, "wallet"); diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index c4934750ac..eb962b6e0e 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -6,6 +6,8 @@ use std::str::FromStr; use std::time::Duration as StdDuration; use either::Either; +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; @@ -122,7 +124,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; @@ -370,8 +372,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 } } @@ -416,8 +419,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 } } @@ -455,8 +459,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 } } @@ -601,9 +606,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/lib.rs b/crates/sdk/src/lib.rs index 65c3e457e6..52f0eb3398 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -45,8 +45,8 @@ 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; use namada_core::ethereum_events::EthAddress; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; @@ -173,7 +173,7 @@ pub trait Namada: NamadaIo { fn new_shielded_transfer( &self, data: Vec, - gas_spending_key: Option, + gas_spending_key: Option, disposable_signing_key: bool, ) -> args::TxShieldedTransfer { args::TxShieldedTransfer { @@ -204,9 +204,9 @@ pub trait Namada: NamadaIo { /// arguments fn new_unshielding_transfer( &self, - source: ExtendedSpendingKey, + source: PseudoExtendedKey, data: Vec, - gas_spending_key: Option, + gas_spending_key: Option, disposable_signing_key: bool, ) -> args::TxUnshieldingTransfer { args::TxUnshieldingTransfer { @@ -608,7 +608,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, @@ -862,7 +862,7 @@ pub mod testing { use namada_core::address::testing::{ arb_established_address, arb_non_internal_address, }; - use namada_core::collections::HashMap; + use namada_core::collections::{HashMap, HashSet}; use namada_core::eth_bridge_pool::PendingTransfer; use namada_core::hash::testing::arb_hash; use namada_core::key::testing::arb_common_keypair; diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index b0ff3ff119..98d87332ec 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 key of the fee payer pub fee_payer: common::PublicKey, + /// ID of the Transaction needing signing + pub shielded_hash: Option, } impl PartialEq for SigningTxData { @@ -194,11 +196,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, } @@ -207,7 +210,7 @@ pub enum Signable { pub async fn default_sign( _tx: Tx, pubkey: common::PublicKey, - _parts: HashSet, + _parts: Signable, _user: (), ) -> Result { Err(Error::Other(format!( @@ -232,7 +235,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 @@ -288,11 +291,14 @@ where // Then try to sign the raw header using the hardware wallet for pubkey in &signing_data.public_keys { - if !used_pubkeys.contains(pubkey) && *pubkey != signing_data.fee_payer { + if !used_pubkeys.contains(pubkey) + && (*pubkey != signing_data.fee_payer + || args.wrapper_signature.is_some()) + { if let Ok(ntx) = sign( tx.clone(), pubkey.clone(), - HashSet::from([Signable::RawHeader]), + Signable::RawHeader, user_data.clone(), ) .await @@ -321,33 +327,25 @@ where Ok(fee_payer_keypair) => { tx.sign_wrapper(fee_payer_keypair); } - // The case where the 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() @@ -429,6 +427,7 @@ pub async fn aux_signing_data( threshold, account_public_keys_map, fee_payer, + shielded_hash: None, }) } @@ -2419,6 +2418,7 @@ mod test_signing { threshold: 1, account_public_keys_map: Some(Default::default()), fee_payer: public_key_fee.clone(), + shielded_hash: None, }; let Error::Tx(TxSubmitError::MissingSigningKeys(1, 0)) = sign_tx( @@ -2454,6 +2454,7 @@ mod test_signing { threshold: 1, account_public_keys_map: Some(Default::default()), fee_payer: public_key.clone(), + shielded_hash: None, }; sign_tx( &RwLock::new(wallet), diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index ebb021f066..7b9a6ad42b 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::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::{ @@ -2541,6 +2543,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( @@ -2554,7 +2557,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()), @@ -2649,6 +2652,7 @@ pub async fn build_ibc_transfer( masp_transfer_data, masp_fee_data, args.tx.expiration.to_datetime(), + bparams, ) .await?; let shielded_tx_epoch = shielded_parts.as_ref().map(|trans| trans.0.epoch); @@ -2699,6 +2703,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, @@ -3050,8 +3055,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), @@ -3113,6 +3119,7 @@ pub async fn build_shielded_transfer( transfer_data, masp_fee_data, args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielded transfer must have shielded parts"); @@ -3142,6 +3149,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(()) }; @@ -3166,7 +3174,7 @@ async fn get_masp_fee_payment_amount( args: &args::Tx, fee_amount: DenominatedAmount, fee_payer: &common::PublicKey, - gas_spending_key: Option, + gas_spending_key: Option, ) -> Result> { let fee_payer_address = Address::from(fee_payer); let balance_key = balance_key(&args.fee_token, &fee_payer_address); @@ -3194,6 +3202,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 @@ -3205,7 +3214,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(), @@ -3279,6 +3288,7 @@ pub async fn build_shielding_transfer( transfer_data, None, args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3309,6 +3319,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(()) }; @@ -3330,8 +3341,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), @@ -3400,6 +3412,7 @@ pub async fn build_unshielding_transfer( transfer_data, masp_fee_data, args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3429,6 +3442,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(()) }; @@ -3452,6 +3466,7 @@ async fn construct_shielded_parts( data: Vec, fee_data: Option, 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(); @@ -3464,7 +3479,7 @@ async fn construct_shielded_parts( .await; shielded - .gen_shielded_transfer(context, data, fee_data, expiration) + .gen_shielded_transfer(context, data, fee_data, expiration, bparams) .await }; @@ -3845,6 +3860,7 @@ pub async fn gen_ibc_shielding_transfer( // Fees are paid from the transparent balance of the relayer None, args.expiration.to_datetime(), + &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 4daacb3337..d1203fe02d 100644 --- a/crates/shielded_token/src/masp.rs +++ b/crates/shielded_token/src/masp.rs @@ -25,7 +25,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; @@ -79,7 +80,7 @@ pub struct ShieldedTransfer { #[allow(missing_docs)] #[derive(Debug)] pub struct MaspFeeData { - pub source: Option, + pub source: Option, pub target: Address, pub token: Address, pub amount: token::DenominatedAmount, @@ -202,20 +203,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) {} @@ -799,7 +800,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()); @@ -810,7 +811,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 @@ -844,7 +845,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) } } @@ -889,7 +890,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 a8b38dec27..50224423d9 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -17,13 +17,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}; @@ -50,11 +50,11 @@ use rand_core::{OsRng, SeedableRng}; use crate::masp::utils::MaspClient; use crate::masp::{ - cloned_pair, to_viewing_key, ContextSyncStatus, Conversions, MaspAmount, - MaspDataLogEntry, MaspFeeData, MaspSourceTransferData, - MaspTargetTransferData, MaspTransferData, MaspTxReorderedData, NoteIndex, - ShieldedSyncConfig, ShieldedTransfer, ShieldedUtils, SpentNotesTracker, - TransferErr, WalletMap, WitnessMap, NETWORK, + cloned_pair, ContextSyncStatus, Conversions, MaspAmount, MaspDataLogEntry, + 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}; @@ -809,7 +809,7 @@ pub trait ShieldedApi: &mut self, context: &impl NamadaIo, spent_notes: &mut SpentNotesTracker, - sk: namada_core::masp::ExtendedSpendingKey, + sk: PseudoExtendedKey, target: ValueSum<(MaspDigitPos, Address), i128>, target_epoch: MaspEpoch, ) -> Result< @@ -820,7 +820,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 @@ -1019,6 +1019,7 @@ pub trait ShieldedApi: data: Vec, fee_data: Option, expiration: Option, + bparams: &mut impl BuildParams, ) -> Result, TransferErr> { // Determine epoch in which to submit potential shielded transaction let epoch = Self::query_masp_epoch(context.client()) @@ -1088,7 +1089,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 @@ -1208,7 +1209,7 @@ pub trait ShieldedApi: &prover, &FeeRule::non_standard(U64Sum::zero()), &mut rng, - &mut RngBuildParams::new(OsRng), + bparams, ) .map_err(|error| TransferErr::Build { error })?; @@ -1413,7 +1414,7 @@ pub trait ShieldedApi: async fn add_inputs( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source: &TransferSource, token: &Address, amount: &Amount, @@ -1498,8 +1499,8 @@ pub trait ShieldedApi: for (asset_type, value) in change.components() { builder .add_sapling_output( - Some(MaspExtendedSpendingKey::from(sk).expsk.ovk), - MaspExtendedSpendingKey::from(sk).default_address().1, + Some(sk.to_viewing_key().fvk.ovk), + sk.to_viewing_key().default_address().1, *asset_type, *value as u64, MemoBytes::empty(), @@ -1512,12 +1513,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), })?; @@ -1579,7 +1575,7 @@ pub trait ShieldedApi: async fn add_outputs( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source: Option, target: &TransferTarget, token: Address, @@ -1627,9 +1623,7 @@ pub trait ShieldedApi: // If we are sending to a shielded address, we need the outgoing // viewing key in the following computations. let ovk_opt = source.clone().and_then(|source| { - source - .spending_key() - .map(|x| MaspExtendedSpendingKey::from(x).expsk.ovk) + source.spending_key().map(|x| x.to_viewing_key().fvk.ovk) }); // Make transaction output tied to the current token, // denomination, and epoch. 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/e2e/helpers.rs b/crates/tests/src/e2e/helpers.rs index ab3579a1a2..5c3a9d25a1 100644 --- a/crates/tests/src/e2e/helpers.rs +++ b/crates/tests/src/e2e/helpers.rs @@ -18,6 +18,7 @@ use eyre::eyre; use namada_apps_lib::cli::context::ENV_VAR_CHAIN_ID; use namada_apps_lib::config::utils::convert_tm_addr_to_socket_addr; use namada_apps_lib::config::{Config, TendermintMode}; +use namada_core::masp::PaymentAddress; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::address::Address; use namada_sdk::chain::Epoch; @@ -30,10 +31,10 @@ use namada_sdk::wallet::Wallet; use toml::Value; use super::setup::{ - self, ensure_hot_key, run_cosmos_cmd, sleep, NamadaBgCmd, NamadaCmd, Test, - ENV_VAR_DEBUG, ENV_VAR_USE_PREBUILT_BINARIES, + self, run_cosmos_cmd, sleep, NamadaBgCmd, NamadaCmd, Test, ENV_VAR_DEBUG, + ENV_VAR_USE_PREBUILT_BINARIES, }; -use crate::e2e::setup::{Bin, CosmosChainType, Who, APPS_PACKAGE}; +use crate::e2e::setup::{constants, Bin, CosmosChainType, Who, APPS_PACKAGE}; use crate::strings::{LEDGER_STARTED, TX_APPLIED_SUCCESS}; use crate::{run, run_as}; @@ -128,6 +129,34 @@ pub fn find_address(test: &Test, alias: impl AsRef) -> Result
{ Ok(address) } +/// Find the address of an account by its alias from the wallet +pub fn find_payment_address( + test: &Test, + alias: impl AsRef, +) -> Result { + let mut find = run!( + test, + Bin::Wallet, + &["find", "--addr", "--alias", alias.as_ref()], + Some(10) + )?; + find.exp_string("Found payment address:")?; + let (unread, matched) = find.exp_regex("\".*\": .*")?; + let address_str = strip_trailing_newline(&matched) + .trim() + .rsplit_once(' ') + .unwrap() + .1; + let address = PaymentAddress::from_str(address_str).map_err(|e| { + eyre!(format!( + "Address: {} parsed from {}, Error: {}\n\nOutput: {}", + address_str, matched, e, unread + )) + })?; + println!("Found {}", address); + Ok(address) +} + /// Find the balance of specific token for an account. #[allow(dead_code)] pub fn find_balance( @@ -562,9 +591,7 @@ fn make_hermes_chain_config(test: &Test) -> Value { chain.insert("account_prefix".to_owned(), Value::String("".to_owned())); chain.insert( "key_name".to_owned(), - Value::String( - ensure_hot_key(setup::constants::CHRISTEL_KEY).to_owned(), - ), + Value::String(constants::FRANK_KEY.to_owned()), ); chain.insert("store_prefix".to_owned(), Value::String("ibc".to_owned())); let mut table = toml::map::Map::new(); diff --git a/crates/tests/src/e2e/ibc_tests.rs b/crates/tests/src/e2e/ibc_tests.rs index bde2ca2fa3..cee0f7b8fd 100644 --- a/crates/tests/src/e2e/ibc_tests.rs +++ b/crates/tests/src/e2e/ibc_tests.rs @@ -47,7 +47,8 @@ use sha2::{Digest, Sha256}; use crate::e2e::helpers::{ epoch_sleep, epochs_per_year_from_min_duration, find_address, - find_cosmos_address, get_actor_rpc, get_cosmos_gov_address, get_epoch, + find_cosmos_address, find_payment_address, get_actor_rpc, + get_cosmos_gov_address, get_epoch, }; use crate::e2e::ledger_tests::{ start_namada_ledger_node_wait_wasm, write_json_file, @@ -134,6 +135,7 @@ fn ibc_transfers() -> Result<()> { None, None, None, + false, )?; wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; @@ -205,6 +207,7 @@ fn ibc_transfers() -> Result<()> { None, None, None, + false, )?; wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; @@ -231,10 +234,12 @@ fn ibc_transfers() -> Result<()> { &port_id_namada, &channel_id_namada, )?; + let masp_receiver = + find_payment_address(&test, AA_PAYMENT_ADDRESS)?.to_string(); transfer_from_cosmos( &test_gaia, COSMOS_USER, - AA_PAYMENT_ADDRESS, + masp_receiver.clone(), COSMOS_COIN, 100, &port_id_gaia, @@ -244,7 +249,7 @@ fn ibc_transfers() -> Result<()> { )?; wait_for_packet_relay(&port_id_gaia, &channel_id_gaia, &test_gaia)?; // Check the token on Namada - check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 100)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 100)?; check_cosmos_balance(&test_gaia, COSMOS_USER, COSMOS_COIN, 800)?; // Shielded transfer 50 samoleans on Namada @@ -258,8 +263,8 @@ fn ibc_transfers() -> Result<()> { ALBERT_KEY, &[], )?; - check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 50)?; - check_balance(&test, AB_VIEWING_KEY, &ibc_denom_on_namada, 50)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 50)?; + check_shielded_balance(&test, AB_VIEWING_KEY, &ibc_denom_on_namada, 50)?; // Unshielding transfer 10 samoleans from Namada to Gaia transfer( @@ -274,9 +279,10 @@ fn ibc_transfers() -> Result<()> { None, None, None, + true, )?; wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; - check_balance(&test, AB_VIEWING_KEY, &ibc_denom_on_namada, 40)?; + check_shielded_balance(&test, AB_VIEWING_KEY, &ibc_denom_on_namada, 40)?; check_cosmos_balance(&test_gaia, COSMOS_USER, COSMOS_COIN, 810)?; // 4. Shielding transfer the received token back to a shielded account on @@ -289,10 +295,12 @@ fn ibc_transfers() -> Result<()> { &port_id_namada, &channel_id_namada, )?; + let masp_receiver = + find_payment_address(&test, AA_PAYMENT_ADDRESS)?.to_string(); transfer_from_cosmos( &test_gaia, COSMOS_USER, - AA_PAYMENT_ADDRESS, + masp_receiver, get_gaia_denom_hash(&ibc_denom_on_gaia), 1_000_000, &port_id_gaia, @@ -302,7 +310,7 @@ fn ibc_transfers() -> Result<()> { )?; wait_for_packet_relay(&port_id_gaia, &channel_id_gaia, &test)?; // Check the token on Namada - check_balance(&test, AA_VIEWING_KEY, APFEL, 1)?; + check_shielded_balance(&test, AA_VIEWING_KEY, APFEL, 1)?; // 5. Refunding when transfer failure @@ -320,6 +328,7 @@ fn ibc_transfers() -> Result<()> { None, None, None, + false, )?; wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; // The balance should not be changed @@ -342,6 +351,7 @@ fn ibc_transfers() -> Result<()> { Some(Duration::new(10, 0)), None, None, + false, )?; // wait for the timeout sleep(10); @@ -368,10 +378,11 @@ fn ibc_transfers() -> Result<()> { None, None, None, + true, )?; wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; // Check the token has been refunded to the refund target - check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; check_balance(&test, IBC_REFUND_TARGET_ALIAS, &ibc_denom_on_namada, 10)?; // Stop Hermes for timeout test @@ -392,6 +403,7 @@ fn ibc_transfers() -> Result<()> { Some(Duration::new(10, 0)), None, None, + true, )?; // wait for the timeout sleep(10); @@ -402,20 +414,22 @@ fn ibc_transfers() -> Result<()> { wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; // Check the token has been refunded to the refund target - check_balance(&test, AA_VIEWING_KEY, APFEL, 0)?; + check_shielded_balance(&test, AA_VIEWING_KEY, APFEL, 0)?; check_balance(&test, IBC_REFUND_TARGET_ALIAS, APFEL, 1)?; // 6. Malformed shielded actions // Check initial balance - check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; check_cosmos_balance(&test_gaia, COSMOS_USER, COSMOS_COIN, 810)?; // Missing memo + let masp_receiver = + find_payment_address(&test, AA_PAYMENT_ADDRESS)?.to_string(); transfer_from_cosmos( &test_gaia, COSMOS_USER, - AA_PAYMENT_ADDRESS, + masp_receiver, COSMOS_COIN, 100, &port_id_gaia, @@ -426,7 +440,7 @@ fn ibc_transfers() -> Result<()> { )?; wait_for_packet_relay(&port_id_namada, &channel_id_namada, &test)?; // Check the balance didn't change - check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; check_cosmos_balance(&test_gaia, COSMOS_USER, COSMOS_COIN, 810)?; // Wrong memo (different amount) @@ -438,10 +452,12 @@ fn ibc_transfers() -> Result<()> { &port_id_namada, &channel_id_namada, )?; + let masp_receiver = + find_payment_address(&test, AA_PAYMENT_ADDRESS)?.to_string(); transfer_from_cosmos( &test_gaia, COSMOS_USER, - AA_PAYMENT_ADDRESS, + masp_receiver, COSMOS_COIN, 101, &port_id_gaia, @@ -452,7 +468,7 @@ fn ibc_transfers() -> Result<()> { )?; wait_for_packet_relay(&port_id_gaia, &channel_id_gaia, &test_gaia)?; // Check the balances didn't change - check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; check_cosmos_balance(&test_gaia, COSMOS_USER, COSMOS_COIN, 810)?; Ok(()) @@ -528,6 +544,7 @@ fn ibc_nft_transfers() -> Result<()> { None, None, None, + false, )?; clear_packet(&port_id_namada, &channel_id_namada, &test)?; check_balance(&test, &namada_receiver, &ibc_trace_on_namada, 0)?; @@ -542,10 +559,12 @@ fn ibc_nft_transfers() -> Result<()> { &port_id_namada, &channel_id_namada, )?; + let masp_receiver = + find_payment_address(&test, AA_PAYMENT_ADDRESS)?.to_string(); nft_transfer_from_cosmos( &test_cosmwasm, COSMOS_USER, - AA_PAYMENT_ADDRESS, + masp_receiver, NFT_ID, &cw721_contract, &ics721_contract, @@ -554,7 +573,7 @@ fn ibc_nft_transfers() -> Result<()> { None, )?; clear_packet(&port_id_cosmwasm, &channel_id_cosmwasm, &test_cosmwasm)?; - check_balance(&test, AA_VIEWING_KEY, &ibc_trace_on_namada, 1)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_trace_on_namada, 1)?; // Shielded transfer on Namada transfer_on_chain( @@ -567,8 +586,8 @@ fn ibc_nft_transfers() -> Result<()> { ALBERT_KEY, &[], )?; - check_balance(&test, AA_VIEWING_KEY, &ibc_trace_on_namada, 0)?; - check_balance(&test, AB_VIEWING_KEY, &ibc_trace_on_namada, 1)?; + check_shielded_balance(&test, AA_VIEWING_KEY, &ibc_trace_on_namada, 0)?; + check_shielded_balance(&test, AB_VIEWING_KEY, &ibc_trace_on_namada, 1)?; // Unshielding transfer transfer( @@ -583,9 +602,10 @@ fn ibc_nft_transfers() -> Result<()> { None, None, None, + true, )?; clear_packet(&port_id_namada, &channel_id_namada, &test)?; - check_balance(&test, AB_VIEWING_KEY, &ibc_trace_on_namada, 0)?; + check_shielded_balance(&test, AB_VIEWING_KEY, &ibc_trace_on_namada, 0)?; Ok(()) } @@ -853,7 +873,7 @@ fn ibc_token_inflation() -> Result<()> { } // Check the target balance is zero before the inflation - check_balance(&test, AA_VIEWING_KEY, NAM, 0)?; + check_shielded_balance(&test, AA_VIEWING_KEY, NAM, 0)?; // Shielding transfer 1 samoleans from Gaia to Namada let shielding_data_path = gen_ibc_shielding_data( &test, @@ -863,10 +883,12 @@ fn ibc_token_inflation() -> Result<()> { &port_id_namada, &channel_id_namada, )?; + let masp_receiver = + find_payment_address(&test, AA_PAYMENT_ADDRESS)?.to_string(); transfer_from_cosmos( &test_gaia, COSMOS_USER, - AA_PAYMENT_ADDRESS, + masp_receiver, COSMOS_COIN, 1, &port_id_gaia, @@ -1011,6 +1033,7 @@ fn ibc_rate_limit() -> Result<()> { None, None, None, + false, )?; // Transfer 1 NAM from Namada to Gaia again will fail @@ -1029,6 +1052,7 @@ fn ibc_rate_limit() -> Result<()> { Some( "Transfer exceeding the per-epoch throughput limit is not allowed", ), + false, )?; // wait for the next epoch @@ -1051,6 +1075,7 @@ fn ibc_rate_limit() -> Result<()> { None, None, None, + false, )?; // wait for the next epoch @@ -1327,6 +1352,7 @@ fn try_invalid_transfers( None, // the IBC denom can't be parsed when using an invalid port Some(&format!("Invalid IBC denom: {nam_addr}")), + false, )?; // invalid channel @@ -1342,6 +1368,7 @@ fn try_invalid_transfers( None, None, Some("IBC token transfer error: context error: `ICS04 Channel error"), + false, )?; Ok(()) @@ -1396,6 +1423,7 @@ fn transfer( timeout_sec: Option, shielding_data_path: Option, expected_err: Option<&str>, + gen_refund_target: bool, ) -> Result { let rpc = get_actor_rpc(test, Who::Validator(0)); @@ -1443,7 +1471,7 @@ fn transfer( tx_args.push(&memo); } - if sender.as_ref().starts_with("zsk") { + if gen_refund_target { let mut cmd = run!( test, Bin::Wallet, @@ -1906,9 +1934,32 @@ fn check_balance( ) -> Result<()> { let rpc = get_actor_rpc(test, Who::Validator(0)); - if owner.as_ref().starts_with("zvk") { - shielded_sync(test, owner.as_ref())?; - } + let query_args = vec![ + "balance", + "--owner", + owner.as_ref(), + "--token", + token.as_ref(), + "--node", + &rpc, + ]; + let mut client = run!(test, Bin::Client, query_args, Some(40))?; + let expected = + format!("{}: {expected_amount}", token.as_ref().to_lowercase()); + client.exp_string(&expected)?; + client.assert_success(); + Ok(()) +} + +fn check_shielded_balance( + test: &Test, + owner: impl AsRef, + token: impl AsRef, + expected_amount: u64, +) -> Result<()> { + let rpc = get_actor_rpc(test, Who::Validator(0)); + + shielded_sync(test, owner.as_ref())?; let query_args = vec![ "balance", diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index cdccd01470..cd7b0aef4b 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -27,6 +27,7 @@ use namada_apps_lib::config::genesis::templates::TokenBalances; 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::defaults::is_use_device; use namada_apps_lib::wallet::{self, Alias}; use namada_core::chain::ChainId; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; @@ -2508,7 +2509,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, @@ -2518,12 +2519,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, @@ -2535,7 +2536,7 @@ fn masp_txs_and_queries() -> Result<()> { "7", "--gas-payer", CHRISTEL_KEY, - ], + ]), TX_APPLIED_SUCCESS, ), // 4. Assert BTC balance at VK(A) is 13 @@ -2545,7 +2546,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, @@ -2557,7 +2558,7 @@ fn masp_txs_and_queries() -> Result<()> { "5", "--gas-payer", CHRISTEL_KEY, - ], + ]), TX_APPLIED_SUCCESS, ), // 6. Assert BTC balance at VK(B) is 2 @@ -2578,6 +2579,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" diff --git a/crates/tests/src/e2e/setup.rs b/crates/tests/src/e2e/setup.rs index ebd2329aaa..d7c9a663e9 100644 --- a/crates/tests/src/e2e/setup.rs +++ b/crates/tests/src/e2e/setup.rs @@ -89,18 +89,6 @@ pub fn apply_use_device(mut tx_args: Vec<&str>) -> Vec<&str> { tx_args } -/// Replace the given key with a key that is stored unencrypted in the wallet. -/// This is useful for IBC tests where a keypair needs to be added to the Hermes -/// keyring or where IBC messages unsupported by the hardware wallet need to be -/// signed -pub fn ensure_hot_key(key: &str) -> &str { - if is_use_device() { - constants::FRANK_KEY - } else { - key - } -} - /// Default functions for offsetting ports when /// adding multiple validators to a network pub fn default_port_offset(ix: u8) -> u16 { @@ -1555,23 +1543,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/helpers.rs b/crates/tests/src/integration/helpers.rs index 487ffab972..c2447a3a50 100644 --- a/crates/tests/src/integration/helpers.rs +++ b/crates/tests/src/integration/helpers.rs @@ -8,6 +8,9 @@ use namada_node::shell::testing::node::MockNode; use namada_node::shell::testing::utils::{Bin, CapturedOutput}; use namada_sdk::key::common; +use crate::e2e::setup::constants::{FRANK, FRANK_KEY}; +use crate::strings::TX_APPLIED_SUCCESS; + /// Query the wallet to get an address from a given alias. pub fn find_address( node: &MockNode, @@ -78,6 +81,66 @@ pub fn find_keypair( }) } +// Make a temporary account with the given balance and return its key. This +// function is useful because the integration tests can no longer assume that +// the secret keys are accessible. +pub fn make_temp_account( + node: &MockNode, + ledger_address: &str, + key_alias: impl AsRef, + token: impl AsRef, + amount: u64, +) -> eyre::Result<(impl AsRef, common::SecretKey)> { + // a. Generate a new key for an implicit account. + run( + node, + Bin::Wallet, + vec![ + "gen", + "--alias", + key_alias.as_ref(), + "--unsafe-dont-encrypt", + "--raw", + ], + )?; + // b. Reveal the public key associated with an address + let reveal_args = vec![ + "reveal-pk", + "--public-key", + key_alias.as_ref(), + "--gas-payer", + FRANK_KEY, + "--node", + ledger_address, + ]; + let captured = CapturedOutput::of(|| run(node, Bin::Client, reveal_args)); + assert!(captured.result.is_ok()); + assert!(captured.contains(TX_APPLIED_SUCCESS)); + // c. Send some funds to the implicit account. + let amount = amount.to_string(); + let credit_args = vec![ + "transparent-transfer", + "--source", + FRANK, + "--target", + key_alias.as_ref(), + "--token", + token.as_ref(), + "--amount", + &amount, + "--signing-keys", + FRANK_KEY, + "--node", + ledger_address, + ]; + let captured = CapturedOutput::of(|| run(node, Bin::Client, credit_args)); + assert!(captured.result.is_ok()); + assert!(captured.contains(TX_APPLIED_SUCCESS)); + // d. Obtain the key pair associated with the new address + let keypair = find_keypair(node, key_alias.as_ref())?; + Ok((key_alias, keypair)) +} + fn strip_trailing_newline(input: &str) -> &str { input .strip_suffix("\r\n") diff --git a/crates/tests/src/integration/ledger_tests.rs b/crates/tests/src/integration/ledger_tests.rs index d609aaddd7..8dcaf35521 100644 --- a/crates/tests/src/integration/ledger_tests.rs +++ b/crates/tests/src/integration/ledger_tests.rs @@ -9,9 +9,7 @@ use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use color_eyre::eyre::Result; use data_encoding::HEXLOWER; -use namada_apps_lib::wallet::defaults::{ - self, get_unencrypted_keypair, is_use_device, -}; +use namada_apps_lib::wallet::defaults::{self, is_use_device}; use namada_core::chain::Epoch; use namada_core::dec::Dec; use namada_core::hash::Hash; @@ -34,14 +32,14 @@ use namada_test_utils::TestWasms; use test_log::test; use crate::e2e::ledger_tests::prepare_proposal_data; +use crate::e2e::setup::apply_use_device; use crate::e2e::setup::constants::{ ALBERT, ALBERT_KEY, APFEL, BERTHA, BERTHA_KEY, BTC, CHRISTEL, CHRISTEL_KEY, DAEWON, DOT, ESTER, ETH, GOVERNANCE_ADDRESS, KARTOFFEL, NAM, PGF_ADDRESS, SCHNITZEL, }; -use crate::e2e::setup::{apply_use_device, ensure_hot_key}; use crate::integration::helpers::{ - find_address, find_keypair, prepare_steward_commission_update_data, + find_address, make_temp_account, prepare_steward_commission_update_data, }; use crate::integration::setup; use crate::strings::{ @@ -553,10 +551,10 @@ fn pos_rewards() -> Result<()> { }); assert_matches!(captured.result, Ok(_)); let _res = captured - .matches(r"Current annual staking rewards rate: 63.483") + .matches(r"Current annual staking rewards rate: 65.705255877354") .expect("Test failed"); let _res = captured - .matches(r"PoS inflation rate: 0.066") + .matches(r"PoS inflation rate: 0.066593164444") .expect("Test failed"); Ok(()) @@ -987,20 +985,20 @@ fn inflation() -> Result<()> { })?; let pos_inflation = [ - 114400000.785983, - 114400001.632333, - 114400002.53905, - 114400003.506134, - 114400004.533585, + 118400000.813463, + 118400001.689407, + 118400002.627832, + 118400003.628738, + 118400004.692125, ]; let steward_inflation = [ - 1980000.36276, - 1980000.72552, - 1980001.08828, - 1980001.45104, - 1980001.8138, + 1980000.375443, + 1980000.750886, + 1980001.126329, + 1980001.501772, + 1980001.877215, ]; - let pgf_inflation = [0.399038, 0.792006, 1.200066, 1.623217, 2.06146]; + let pgf_inflation = [0.41299, 0.819698, 1.242026, 1.679974, 2.133543]; for epoch in 0..5 { node.next_epoch(); @@ -1285,7 +1283,7 @@ fn pgf_governance_proposal() -> Result<()> { let captured = CapturedOutput::of(|| run(&node, Bin::Client, query_balance_args)); assert_matches!(captured.result, Ok(_)); - assert!(captured.contains("nam: 13.785266")); + assert!(captured.contains("nam: 14.267253")); let query_total_supply_args = vec![ "total-supply", @@ -1298,7 +1296,7 @@ fn pgf_governance_proposal() -> Result<()> { CapturedOutput::of(|| run(&node, Bin::Client, query_total_supply_args)); assert_matches!(captured.result, Ok(_)); assert!(captured.contains( - "token tnam1q9kn74xfzytqkqyycfrhycr8ajam8ny935cge0z5: 114400023.904507" + "token tnam1q9kn74xfzytqkqyycfrhycr8ajam8ny935cge0z5: 118400024.740301" )); let query_native_supply_args = @@ -1307,7 +1305,7 @@ fn pgf_governance_proposal() -> Result<()> { run(&node, Bin::Client, query_native_supply_args) }); assert_matches!(captured.result, Ok(_)); - assert!(captured.contains("nam: 114400010.119241")); + assert!(captured.contains("nam: 118400010.473048")); // 8. Submit proposal funding let albert = defaults::albert_address(); @@ -1697,9 +1695,16 @@ fn change_validator_metadata() -> Result<()> { fn offline_sign() -> Result<()> { // This address doesn't matter for tests. But an argument is required. let validator_one_rpc = "http://127.0.0.1:26567"; + // 1. start the ledger node let (node, _services) = setup::setup()?; + // Initialize accounts we can access the secret keys of + let (bradley_alias, _bradley_key) = + make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?; + let (cooper_alias, _cooper_key) = + make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?; + let output_folder = tempfile::tempdir().unwrap(); // 2. Dump a wrapped transfer tx @@ -1710,7 +1715,7 @@ fn offline_sign() -> Result<()> { apply_use_device(vec![ "transparent-transfer", "--source", - BERTHA, + bradley_alias.as_ref(), "--target", ALBERT, "--token", @@ -1722,7 +1727,7 @@ fn offline_sign() -> Result<()> { "--gas-price", "1", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), "--node", &validator_one_rpc, "--dump-wrapper-tx", @@ -1741,26 +1746,23 @@ fn offline_sign() -> Result<()> { .display() .to_string(); - let bertha_sk = find_keypair(&node, BERTHA_KEY).unwrap().to_string(); - let christel_sk = find_keypair(&node, CHRISTEL_KEY).unwrap().to_string(); - // 3. Sign the transaction offline let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - apply_use_device(vec![ + vec![ "utils", "sign-offline", "--data-path", &offline_tx, "--secret-keys", - &bertha_sk, + &bradley_alias.as_ref(), "--secret-key", - &christel_sk, + &cooper_alias.as_ref(), "--output-folder-path", &output_folder.path().to_str().unwrap(), - ]), + ], ) }); assert!(captured.result.is_ok()); @@ -1823,7 +1825,7 @@ fn offline_sign() -> Result<()> { vec![ "balance", "--owner", - ensure_hot_key(BERTHA), + bradley_alias.as_ref(), "--token", NAM, "--node", @@ -1832,7 +1834,7 @@ fn offline_sign() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1999900")); + assert!(captured.contains("nam: 499900")); let captured = CapturedOutput::of(|| { run( &node, @@ -1840,7 +1842,7 @@ fn offline_sign() -> Result<()> { vec![ "balance", "--owner", - ensure_hot_key(CHRISTEL_KEY), + cooper_alias.as_ref(), "--token", NAM, "--node", @@ -1849,7 +1851,7 @@ fn offline_sign() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1800000")); + assert!(captured.contains("nam: 300000")); Ok(()) } @@ -1864,6 +1866,10 @@ fn enforce_fee_payment() -> Result<()> { // 1. start the ledger node let (node, _services) = setup::setup()?; + // Initialize accounts we can access the secret keys of + let (adam_alias, adam_key) = + make_temp_account(&node, validator_one_rpc, "Adam", NAM, 2_000_000)?; + let tempdir = tempfile::tempdir().unwrap(); let mut txs_bytes = vec![]; @@ -1874,7 +1880,7 @@ fn enforce_fee_payment() -> Result<()> { vec![ "balance", "--owner", - ensure_hot_key(ALBERT_KEY), + adam_alias.as_ref(), "--token", NAM, "--node", @@ -1891,7 +1897,7 @@ fn enforce_fee_payment() -> Result<()> { apply_use_device(vec![ "transparent-transfer", "--source", - ensure_hot_key(ALBERT_KEY), + adam_alias.as_ref(), "--target", BERTHA, "--token", @@ -1927,7 +1933,7 @@ fn enforce_fee_payment() -> Result<()> { apply_use_device(vec![ "transparent-transfer", "--source", - ensure_hot_key(ALBERT_KEY), + adam_alias.as_ref(), "--target", CHRISTEL, "--token", @@ -1935,7 +1941,7 @@ fn enforce_fee_payment() -> Result<()> { "--amount", "50", "--gas-payer", - ensure_hot_key(ALBERT_KEY), + adam_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-tx", @@ -1955,9 +1961,7 @@ fn enforce_fee_payment() -> Result<()> { txs_bytes.push(std::fs::read(&file_path).unwrap()); std::fs::remove_file(&file_path).unwrap(); - let sk = get_unencrypted_keypair( - &ensure_hot_key("albert-key").to_ascii_lowercase(), - ); + let sk = adam_key; let pk = sk.to_public(); let native_token = node @@ -2022,7 +2026,7 @@ fn enforce_fee_payment() -> Result<()> { vec![ "balance", "--owner", - ensure_hot_key(ALBERT_KEY), + adam_alias.as_ref(), "--token", NAM, "--node", @@ -2095,7 +2099,7 @@ fn apply_snapshot() -> Result<()> { for _ in 0..3 { node.next_epoch(); } - let tx_args = vec![ + let tx_args = apply_use_device(vec![ "transparent-transfer", "--source", BERTHA, @@ -2110,7 +2114,7 @@ fn apply_snapshot() -> Result<()> { "--node", &validator_one_rpc, "--force", - ]; + ]); let captured = CapturedOutput::of(|| run(&node, Bin::Client, tx_args)); assert_matches!(captured.result, Ok(_)); diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index c447c0ba24..8169dc55ba 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -4,11 +4,11 @@ use std::str::FromStr; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; use namada_apps_lib::wallet::defaults::{ - albert_keypair, bertha_keypair, christel_keypair, + get_unencrypted_keypair, is_use_device, }; use namada_core::address::Address; use namada_core::dec::Dec; -use namada_core::masp::TokenMap; +use namada_core::masp::{MaspTxId, TokenMap}; use namada_node::shell::testing::client::run; use namada_node::shell::testing::node::NodeResults; use namada_node::shell::testing::utils::{Bin, CapturedOutput}; @@ -19,17 +19,19 @@ use namada_sdk::state::{StorageRead, StorageWrite}; use namada_sdk::time::DateTimeUtc; use namada_sdk::token::storage_key::masp_token_map_key; use namada_sdk::token::{self, Amount, DenominatedAmount}; -use namada_sdk::tx::Tx; +use namada_sdk::tx::{Section, Tx}; use namada_sdk::{tx, DEFAULT_GAS_LIMIT}; use test_log::test; use super::{helpers, 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, C_SPENDING_KEY, ETH, MASP, NAM, + CHRISTEL_KEY, C_SPENDING_KEY, ETH, FRANK_KEY, MASP, NAM, }; +use crate::integration::helpers::make_temp_account; use crate::strings::TX_APPLIED_SUCCESS; /// Enable masp rewards before some token is shielded, @@ -104,7 +106,7 @@ fn init_null_rewards() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", BERTHA, @@ -116,7 +118,7 @@ fn init_null_rewards() -> Result<()> { "1000000", "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -243,7 +245,7 @@ fn init_null_rewards() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -257,7 +259,7 @@ fn init_null_rewards() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -300,7 +302,7 @@ fn init_null_rewards() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -314,7 +316,7 @@ fn init_null_rewards() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -357,7 +359,7 @@ fn init_null_rewards() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -371,7 +373,7 @@ fn init_null_rewards() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -468,7 +470,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", BERTHA, @@ -480,7 +482,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { HALF_TEST_TOKEN_INITIAL_SUPPLY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -550,7 +552,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -564,7 +566,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -637,7 +639,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", BERTHA, @@ -649,7 +651,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { HALF_TEST_TOKEN_INITIAL_SUPPLY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -722,7 +724,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -738,7 +740,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { RPC, "--gas-limit", "65000", - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -781,7 +783,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", BERTHA_KEY, @@ -795,7 +797,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -840,7 +842,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -857,7 +859,7 @@ fn values_spanning_multiple_masp_digits() -> Result<()> { C_SPENDING_KEY, "--gas-limit", "65000", - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -946,7 +948,7 @@ fn enable_rewards_after_shielding() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", BERTHA, @@ -958,7 +960,7 @@ fn enable_rewards_after_shielding() -> Result<()> { "1000000", "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -1109,7 +1111,7 @@ fn enable_rewards_after_shielding() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1123,7 +1125,7 @@ fn enable_rewards_after_shielding() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -1169,7 +1171,7 @@ fn enable_rewards_after_shielding() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", BERTHA, @@ -1181,7 +1183,7 @@ fn enable_rewards_after_shielding() -> Result<()> { "1000000", "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -1271,7 +1273,7 @@ fn enable_rewards_after_shielding() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1285,7 +1287,7 @@ fn enable_rewards_after_shielding() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -1328,7 +1330,7 @@ fn enable_rewards_after_shielding() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1342,7 +1344,7 @@ fn enable_rewards_after_shielding() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -1385,7 +1387,7 @@ fn enable_rewards_after_shielding() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1399,7 +1401,7 @@ fn enable_rewards_after_shielding() -> Result<()> { BERTHA_KEY, "--node", RPC, - ], + ]), ) }); assert!(captured.result.is_ok(), "{:?}", captured.result); @@ -1459,7 +1461,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1469,9 +1471,11 @@ fn masp_incentives() -> Result<()> { BTC, "--amount", "1", + "--signing-keys", + ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -1716,7 +1720,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1726,9 +1730,11 @@ fn masp_incentives() -> Result<()> { ETH, "--amount", "0.001", + "--signing-keys", + ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -1826,7 +1832,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 @@ -1846,7 +1852,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(); @@ -1855,7 +1861,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -1869,7 +1875,7 @@ fn masp_incentives() -> Result<()> { BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -1927,7 +1933,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 @@ -1955,7 +1961,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(); @@ -1965,7 +1971,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1979,7 +1985,7 @@ fn masp_incentives() -> Result<()> { ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2048,7 +2054,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(); @@ -2099,7 +2105,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) @@ -2119,7 +2125,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 @@ -2135,7 +2141,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -2144,14 +2150,14 @@ fn masp_incentives() -> Result<()> { "--token", NAM, "--amount", - "1.451732", + "1.502496", "--gas-limit", "60000", "--signing-keys", BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2170,7 +2176,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -2184,7 +2190,7 @@ fn masp_incentives() -> Result<()> { ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2258,7 +2264,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.003222")); + assert!(captured.contains("nam: 0.003216")); Ok(()) } @@ -2287,7 +2293,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -2299,7 +2305,7 @@ fn spend_unconverted_asset_type() -> Result<()> { "20", "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2311,7 +2317,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -2323,7 +2329,7 @@ fn spend_unconverted_asset_type() -> Result<()> { "0.000001", "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2370,7 +2376,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", B_SPENDING_KEY, @@ -2384,7 +2390,7 @@ fn spend_unconverted_asset_type() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2443,7 +2449,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, @@ -2457,12 +2463,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, @@ -2476,12 +2482,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, @@ -2493,12 +2499,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, @@ -2512,12 +2518,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, @@ -2531,12 +2537,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, @@ -2550,12 +2556,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, @@ -2569,12 +2575,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, @@ -2588,7 +2594,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 @@ -2632,7 +2638,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, @@ -2648,7 +2654,7 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), ]; @@ -2672,7 +2678,9 @@ 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() @@ -2749,32 +2757,9 @@ 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", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; + // Initialize accounts we can access the secret keys of + let (cooper_alias, cooper_key) = + make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?; // 1. Shield tokens _ = node.next_epoch(); @@ -2782,7 +2767,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2794,7 +2779,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2804,7 +2789,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2816,7 +2801,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "200", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2826,7 +2811,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2838,11 +2823,12 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); assert!(captured.contains(TX_APPLIED_SUCCESS)); + // sync shielded context run( &node, @@ -2860,7 +2846,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2877,10 +2863,11 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); + let file_path = tempdir .path() .read_dir() @@ -2896,7 +2883,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2907,16 +2894,17 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--amount", "50", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); + let file_path = tempdir .path() .read_dir() @@ -2932,7 +2920,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", B_SPENDING_KEY, @@ -2943,16 +2931,17 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--amount", "50", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); + let file_path = tempdir .path() .read_dir() @@ -2964,7 +2953,7 @@ 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 = cooper_key; let pk = sk.to_public(); let native_token = node @@ -3010,27 +2999,16 @@ 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", - ], - )?; + // Initialize accounts we can access the secret keys of + let (cooper_alias, cooper_key) = + make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?; // 1. Shield tokens _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3042,7 +3020,7 @@ fn expired_masp_tx() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; // sync shielded context run( @@ -3060,7 +3038,7 @@ fn expired_masp_tx() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -3071,7 +3049,7 @@ fn expired_masp_tx() -> Result<()> { "--amount", "50", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), // We want to create an expired masp tx. Doing so will also set // the expiration field of the header which can // be a problem because this would lead to the @@ -3089,7 +3067,7 @@ fn expired_masp_tx() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3105,7 +3083,7 @@ 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 = cooper_key; let pk = sk.to_public(); let native_token = node @@ -3197,7 +3175,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3207,9 +3185,11 @@ fn cross_epoch_unshield() -> Result<()> { NAM, "--amount", "1000", + "--signing-keys", + ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3234,7 +3214,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -3251,7 +3231,7 @@ fn cross_epoch_unshield() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3272,7 +3252,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "tx", "--owner", ALBERT_KEY, @@ -3280,7 +3260,7 @@ fn cross_epoch_unshield() -> Result<()> { tx_path.to_str().unwrap(), "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3344,7 +3324,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3356,11 +3336,12 @@ fn dynamic_assets() -> Result<()> { "1", "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); assert!(captured.contains(TX_APPLIED_SUCCESS)); + // sync the shielded context run( &node, @@ -3479,7 +3460,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3491,11 +3472,12 @@ fn dynamic_assets() -> Result<()> { "1", "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); assert!(captured.contains(TX_APPLIED_SUCCESS)); + // sync the shielded context run( &node, @@ -3824,7 +3806,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -3838,7 +3820,7 @@ fn dynamic_assets() -> Result<()> { BERTHA_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3875,7 +3857,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -3889,7 +3871,7 @@ fn dynamic_assets() -> Result<()> { BERTHA_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3939,39 +3921,12 @@ 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", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - // Shield some tokens let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3985,11 +3940,12 @@ fn masp_fee_payment() -> Result<()> { CHRISTEL_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); assert!(captured.contains(TX_APPLIED_SUCCESS)); + _ = node.next_masp_epoch(); // sync shielded context run( @@ -4020,7 +3976,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -4039,7 +3995,7 @@ fn masp_fee_payment() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_err()); @@ -4074,7 +4030,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transparent-transfer", "--source", ALBERT_KEY, @@ -4088,7 +4044,7 @@ fn masp_fee_payment() -> Result<()> { CHRISTEL_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4118,7 +4074,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transparent-transfer", "--source", BERTHA_KEY, @@ -4134,7 +4090,7 @@ fn masp_fee_payment() -> Result<()> { validator_one_rpc, // Force to skip check in client "--force", - ], + ]), ) }); assert!(captured.result.is_err()); @@ -4162,7 +4118,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -4179,11 +4135,12 @@ fn masp_fee_payment() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); assert!(captured.contains(TX_APPLIED_SUCCESS)); + // sync shielded context run( &node, @@ -4246,39 +4203,12 @@ 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", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - // Shield some tokens let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -4290,7 +4220,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { "1000000", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4330,7 +4260,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -4347,7 +4277,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_err()); @@ -4394,39 +4324,12 @@ 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", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - // Shield some tokens let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -4442,7 +4345,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { BERTHA_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4498,7 +4401,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -4518,7 +4421,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { A_SPENDING_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4584,51 +4487,12 @@ 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", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_c", - "--value", - AC_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - // Shield some tokens let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -4640,7 +4504,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "10000", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4649,7 +4513,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -4661,7 +4525,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "300000", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4716,7 +4580,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -4735,7 +4599,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4825,39 +4689,12 @@ 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", - ], - )?; - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - // Shield some tokens let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -4869,7 +4706,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { "1", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4878,7 +4715,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -4892,7 +4729,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4901,7 +4738,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -4915,7 +4752,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -4987,7 +4824,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -5008,7 +4845,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -5112,30 +4949,21 @@ 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", - ], - )?; + // Initialize accounts we can access the secret keys of + let (adam_alias, adam_key) = + make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?; + let (bradley_alias, bradley_key) = + make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?; // Generate a tx to shield some tokens let captured = CapturedOutput::of(|| { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", - ALBERT_KEY, + adam_alias.as_ref(), "--target", AA_PAYMENT_ADDRESS, "--token", @@ -5143,16 +4971,17 @@ fn identical_output_descriptions() -> Result<()> { "--amount", "1000", "--gas-payer", - BERTHA_KEY, + bradley_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-wrapper-tx", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); + let file_path = tempdir .path() .read_dir() @@ -5172,10 +5001,11 @@ fn identical_output_descriptions() -> Result<()> { let signing_data = SigningTxData { owner: None, - public_keys: vec![albert_keypair().to_public()], + public_keys: vec![adam_key.to_public()], threshold: 1, account_public_keys_map: None, - fee_payer: albert_keypair().to_public(), + fee_payer: adam_key.to_public(), + shielded_hash: None, }; let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![ @@ -5185,13 +5015,13 @@ fn identical_output_descriptions() -> Result<()> { .unwrap(); batched_tx.sign_raw( - vec![albert_keypair()], + vec![adam_key.clone()], AccountPublicKeysMap::from_iter( - vec![(albert_keypair().to_public())].into_iter(), + vec![(adam_key.to_public())].into_iter(), ), None, ); - batched_tx.sign_wrapper(bertha_keypair()); + batched_tx.sign_wrapper(bradley_key); let wrapper_hash = batched_tx.wrapper_hash(); let inner_cmts = batched_tx.commitments(); @@ -5273,7 +5103,7 @@ fn identical_output_descriptions() -> Result<()> { vec![ "balance", "--owner", - ALBERT_KEY, + adam_alias.as_ref(), "--token", NAM, "--node", @@ -5282,7 +5112,7 @@ fn identical_output_descriptions() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1998000")); + assert!(captured.contains("nam: 498000")); let captured = CapturedOutput::of(|| { run( @@ -5307,7 +5137,7 @@ fn identical_output_descriptions() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -5322,7 +5152,7 @@ fn identical_output_descriptions() -> Result<()> { BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -5368,6 +5198,16 @@ fn identical_output_descriptions() -> Result<()> { Ok(()) } +// Extract the shielded section hash from the transaction +fn get_shielded_hash(tx: &namada_sdk::tx::Tx) -> Option { + for section in &tx.sections { + if let Section::MaspTx(masp) = section { + return Some(MaspTxId::from(masp.txid())); + } + } + None +} + // Test MASP batched txs where one is failing and one is successful and check // that both the protocol and the shielded sync command behave correctly. Since // the batches are not atomic check that the valid transactions get committed @@ -5382,11 +5222,19 @@ fn masp_batch() -> Result<()> { _ = node.next_masp_epoch(); let tempdir = tempfile::tempdir().unwrap(); + // Initialize accounts we can access the secret keys of + let (adam_alias, adam_key) = + make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?; + let (bradley_alias, _bradley_key) = + make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?; + let (cooper_alias, cooper_key) = + make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?; + // Assert reference NAM balances at VK(A), Albert and Bertha for (owner, balance) in [ (AA_VIEWING_KEY, 0), - (ALBERT_KEY, 2_000_000), - (BERTHA_KEY, 2_000_000), + (adam_alias.as_ref(), 500_000), + (bradley_alias.as_ref(), 500_000), ] { let captured = CapturedOutput::of(|| { run( @@ -5410,7 +5258,7 @@ fn masp_batch() -> Result<()> { // Generate txs for the batch to shield some tokens. Use two different // sources let mut batch = vec![]; - for source in [ALBERT_KEY, BERTHA_KEY] { + for source in [adam_alias.as_ref(), bradley_alias.as_ref()] { let captured = CapturedOutput::of(|| { run( &node, @@ -5428,7 +5276,7 @@ fn masp_batch() -> Result<()> { "--gas-limit", "60000", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-wrapper-tx", @@ -5457,10 +5305,11 @@ fn masp_batch() -> Result<()> { let signing_data = SigningTxData { owner: None, - public_keys: vec![albert_keypair().to_public()], + public_keys: vec![adam_key.to_public()], threshold: 1, account_public_keys_map: None, - fee_payer: albert_keypair().to_public(), + fee_payer: adam_key.to_public(), + shielded_hash: None, }; let mut txs = vec![]; @@ -5472,8 +5321,20 @@ fn masp_batch() -> Result<()> { for (tx0, tx1) in [(tx0.clone(), tx1.clone()), (tx1, tx0)] { let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![ - (tx0, signing_data.clone()), - (tx1, signing_data.clone()), + ( + tx0.clone(), + SigningTxData { + shielded_hash: get_shielded_hash(&tx0), + ..signing_data.clone() + }, + ), + ( + tx1.clone(), + SigningTxData { + shielded_hash: get_shielded_hash(&tx1), + ..signing_data.clone() + }, + ), ]) .unwrap(); batched_tx.header.atomic = false; @@ -5481,13 +5342,13 @@ fn masp_batch() -> Result<()> { // Sign the batch with just the signer of one tx to force the failure of // the other one batched_tx.sign_raw( - vec![albert_keypair()], + vec![adam_key.clone()], AccountPublicKeysMap::from_iter( - vec![(albert_keypair().to_public())].into_iter(), + vec![(adam_key.to_public())].into_iter(), ), None, ); - batched_tx.sign_wrapper(christel_keypair()); + batched_tx.sign_wrapper(cooper_key.clone()); wrapper_hashes.push(batched_tx.wrapper_hash()); for cmt in batched_tx.commitments() { @@ -5575,11 +5436,11 @@ fn masp_batch() -> Result<()> { ], )?; - // Assert NAM balances at VK(A), Albert and Bertha + // Assert NAM balances at VK(A), Bob and Bertha for (owner, balance) in [ (AA_VIEWING_KEY, 2_000), - (ALBERT_KEY, 1_998_000), - (BERTHA_KEY, 2_000_000), + (adam_alias.as_ref(), 498_000), + (bradley_alias.as_ref(), 500_000), ] { let captured = CapturedOutput::of(|| { run( @@ -5617,11 +5478,19 @@ fn masp_atomic_batch() -> Result<()> { _ = node.next_masp_epoch(); let tempdir = tempfile::tempdir().unwrap(); + // Initialize accounts we can access the secret keys of + let (adam_alias, adam_key) = + make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?; + let (bradley_alias, _bradley_key) = + make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?; + let (cooper_alias, cooper_key) = + make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?; + // Assert reference NAM balances at VK(A), Albert and Bertha are unchanged for (owner, balance) in [ (AA_VIEWING_KEY, 0), - (ALBERT_KEY, 2_000_000), - (BERTHA_KEY, 2_000_000), + (adam_alias.as_ref(), 500_000), + (bradley_alias.as_ref(), 500_000), ] { let captured = CapturedOutput::of(|| { run( @@ -5645,7 +5514,7 @@ fn masp_atomic_batch() -> Result<()> { // Generate txs for the batch to shield some tokens. Use two different // sources let mut batch = vec![]; - for source in [ALBERT_KEY, BERTHA_KEY] { + for source in [adam_alias.as_ref(), bradley_alias.as_ref()] { let captured = CapturedOutput::of(|| { run( &node, @@ -5663,7 +5532,7 @@ fn masp_atomic_batch() -> Result<()> { "--gas-limit", "60000", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-wrapper-tx", @@ -5691,10 +5560,11 @@ fn masp_atomic_batch() -> Result<()> { let signing_data = SigningTxData { owner: None, - public_keys: vec![albert_keypair().to_public()], + public_keys: vec![adam_key.to_public()], threshold: 1, account_public_keys_map: None, - fee_payer: albert_keypair().to_public(), + fee_payer: adam_key.to_public(), + shielded_hash: None, }; let mut txs = vec![]; @@ -5706,8 +5576,20 @@ fn masp_atomic_batch() -> Result<()> { for (tx0, tx1) in [(tx0.clone(), tx1.clone()), (tx1, tx0)] { let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![ - (tx0, signing_data.clone()), - (tx1, signing_data.clone()), + ( + tx0.clone(), + SigningTxData { + shielded_hash: get_shielded_hash(&tx0), + ..signing_data.clone() + }, + ), + ( + tx1.clone(), + SigningTxData { + shielded_hash: get_shielded_hash(&tx1), + ..signing_data.clone() + }, + ), ]) .unwrap(); batched_tx.header.atomic = true; @@ -5715,13 +5597,13 @@ fn masp_atomic_batch() -> Result<()> { // Sign the batch with just the signer of one tx to force the failure of // the other one batched_tx.sign_raw( - vec![albert_keypair()], + vec![adam_key.clone()], AccountPublicKeysMap::from_iter( - vec![(albert_keypair().to_public())].into_iter(), + vec![(adam_key.to_public())].into_iter(), ), None, ); - batched_tx.sign_wrapper(christel_keypair()); + batched_tx.sign_wrapper(cooper_key.clone()); wrapper_hashes.push(batched_tx.wrapper_hash()); for cmt in batched_tx.commitments() { @@ -5809,8 +5691,8 @@ fn masp_atomic_batch() -> Result<()> { // Assert NAM balances at VK(A), Albert and Bertha are unchanged for (owner, balance) in [ (AA_VIEWING_KEY, 0), - (ALBERT_KEY, 2_000_000), - (BERTHA_KEY, 2_000_000), + (adam_alias.as_ref(), 500_000), + (bradley_alias.as_ref(), 500_000), ] { let captured = CapturedOutput::of(|| { run( @@ -5849,13 +5731,23 @@ fn tricky_masp_txs() -> Result<()> { _ = node.next_masp_epoch(); let tempdir = tempfile::tempdir().unwrap(); + // Initialize accounts we can access the secret keys of + let (adam_alias, _adam_key) = + make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?; + let (arthur_alias, arthur_key) = + make_temp_account(&node, validator_one_rpc, "Arthur", NAM, 500_000)?; + let (bradley_alias, bradley_key) = + make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?; + let (cooper_alias, _cooper_key) = + make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?; + // Assert reference NAM balances at VK(A), Albert, Bertha and Christel for (owner, balance) in [ (AA_VIEWING_KEY, 0), - (ALBERT_KEY, 2_000_000), - (BERTHA_KEY, 2_000_000), - (ALBERT, 1_980_000), - (CHRISTEL, 2_000_000), + (arthur_alias.as_ref(), 500_000), + (bradley_alias.as_ref(), 500_000), + (adam_alias.as_ref(), 500_000), + (cooper_alias.as_ref(), 500_000), ] { let captured = CapturedOutput::of(|| { run( @@ -5884,7 +5776,7 @@ fn tricky_masp_txs() -> Result<()> { vec![ "shield", "--source", - ALBERT, + adam_alias.as_ref(), "--target", AA_PAYMENT_ADDRESS, "--token", @@ -5892,7 +5784,7 @@ fn tricky_masp_txs() -> Result<()> { "--amount", "1000", "--gas-payer", - CHRISTEL_KEY, + cooper_alias.as_ref(), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-tx", @@ -5928,15 +5820,15 @@ fn tricky_masp_txs() -> Result<()> { vec![ "transparent-transfer", "--source", - ALBERT_KEY, + arthur_alias.as_ref(), "--target", - CHRISTEL, + cooper_alias.as_ref(), "--token", NAM, "--amount", "1000", "--gas-payer", - CHRISTEL_KEY, + FRANK_KEY, "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-wrapper-tx", @@ -5963,13 +5855,13 @@ fn tricky_masp_txs() -> Result<()> { tx0.add_masp_tx_section(masp_transaction.clone()); tx0.sign_raw( - vec![albert_keypair()], + vec![arthur_key.clone()], AccountPublicKeysMap::from_iter( - vec![(albert_keypair().to_public())].into_iter(), + vec![(arthur_key.to_public())].into_iter(), ), None, ); - tx0.sign_wrapper(christel_keypair()); + tx0.sign_wrapper(get_unencrypted_keypair("frank-key")); // Generate second tx let captured = CapturedOutput::of(|| { @@ -5979,7 +5871,7 @@ fn tricky_masp_txs() -> Result<()> { vec![ "shield", "--source", - BERTHA_KEY, + bradley_alias.as_ref(), "--target", AA_PAYMENT_ADDRESS, "--token", @@ -5987,7 +5879,7 @@ fn tricky_masp_txs() -> Result<()> { "--amount", "1000", "--gas-payer", - CHRISTEL_KEY, + FRANK_KEY, "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-wrapper-tx", @@ -6014,13 +5906,13 @@ fn tricky_masp_txs() -> Result<()> { tx1.add_masp_tx_section(masp_transaction); tx1.sign_raw( - vec![bertha_keypair()], + vec![bradley_key.clone()], AccountPublicKeysMap::from_iter( - vec![(bertha_keypair().to_public())].into_iter(), + vec![(bradley_key.to_public())].into_iter(), ), None, ); - tx1.sign_wrapper(christel_keypair()); + tx1.sign_wrapper(get_unencrypted_keypair("frank-key")); let txs = vec![tx0.to_bytes(), tx1.to_bytes()]; node.clear_results(); @@ -6043,10 +5935,10 @@ fn tricky_masp_txs() -> Result<()> { // Assert NAM balances at VK(A), Albert, Bertha and Christel for (owner, balance) in [ (AA_VIEWING_KEY, 1_000), - (ALBERT_KEY, 1_999_000), - (BERTHA_KEY, 1_999_000), - (ALBERT, 1_980_000), - (CHRISTEL, 2_001_000), + (arthur_alias.as_ref(), 499_000), + (bradley_alias.as_ref(), 499_000), + (adam_alias.as_ref(), 500_000), + (cooper_alias.as_ref(), 501_000), ] { let captured = CapturedOutput::of(|| { run( diff --git a/crates/token/src/lib.rs b/crates/token/src/lib.rs index 17b5fdc0a2..057e6895a0 100644 --- a/crates/token/src/lib.rs +++ b/crates/token/src/lib.rs @@ -336,6 +336,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, }; @@ -416,7 +417,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 e800caa552..0d83576fec 100644 --- a/crates/tx/src/types.rs +++ b/crates/tx/src/types.rs @@ -259,6 +259,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 5e5e61a7c2..e5ba30b630 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() @@ -585,8 +593,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 +620,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, @@ -921,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 @@ -1137,12 +1168,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) @@ -1169,10 +1202,8 @@ impl Wallet { ) .inspect(|alias| { // Cache the newly added key - self.decrypted_spendkey_cache.insert( - alias.clone(), - DatedKeypair::new(spend_key, birthday), - ); + 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 287bd7822d..7eb42bbbc4 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, @@ -194,6 +203,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 { + 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, @@ -254,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 } @@ -400,14 +422,14 @@ impl Store { self.remove_alias(&alias); let (spendkey_to_store, _raw_spendkey) = - StoredKeypair::new(DatedKeypair::new(spendkey, birthday), password); + 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( + birthday.map(|x| self.birthdays.insert(alias.clone(), x)); + self.view_keys.insert( + alias.clone(), zip32::ExtendedFullViewingKey::from(&spendkey.into()).into(), - birthday, ); - self.view_keys.insert(alias.clone(), viewkey); path.map(|p| self.derivation_paths.insert(alias.clone(), p)); Some(alias) } @@ -418,6 +440,7 @@ impl Store { alias: Alias, viewkey: ExtendedViewingKey, birthday: Option, + path: Option, force: bool, ) -> Option { // abort if the alias is reserved @@ -435,15 +458,16 @@ 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, } } 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) } @@ -585,6 +609,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 @@ -597,12 +622,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, @@ -614,6 +641,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..f349cb64cf 100644 --- a/genesis/hardware/src/pre-genesis/wallet.toml +++ b/genesis/hardware/src/pre-genesis/wallet.toml @@ -1,8 +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" @@ -20,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/established/established-account-tx-frank.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-frank.toml new file mode 100644 index 0000000000..7e3dbfc6f6 --- /dev/null +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-frank.toml @@ -0,0 +1,4 @@ +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qz0vuxgzsz5lu7peghyr4yx5tuexecm27hdgglkpe444v6nmy6d8yuxhn0r"] diff --git a/genesis/localnet/src/pre-genesis/signed-transactions.toml b/genesis/localnet/src/pre-genesis/signed-transactions.toml index ba13452927..2ede877849 100644 --- a/genesis/localnet/src/pre-genesis/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/signed-transactions.toml @@ -1,3 +1,8 @@ +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qz0vuxgzsz5lu7peghyr4yx5tuexecm27hdgglkpe444v6nmy6d8yuxhn0r"] + [[established_account]] vp = "vp_user" threshold = 1 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 fbf083e87b..71cac3309f 100644 --- a/genesis/localnet/src/pre-genesis/wallet.toml +++ b/genesis/localnet/src/pre-genesis/wallet.toml @@ -1,8 +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" @@ -11,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] @@ -20,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] @@ -35,6 +49,8 @@ daewon = "tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn" ester = "tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q" faucet-key = "tnam1qzgcl2znamndmku7ujw6e79dmvd4v7rfd5c89dfz" validator-0 = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +frank = "tnam1qx9ggvnemv8j3a7u92r7mktgfq45q4cj3vch6wmn" +frank-key = "tnam1qq0345c0vr04wyjfkp0lfqkdp5n0t7ua4cswzkhm" [pkhs] 30A4674B47A0F8CFF0BC8746F8AD012438C7BD3A = "bertha-key" @@ -44,5 +60,6 @@ C671BDF31A7552BD2928909151461CD376CBBE18 = "albert-key" 9F36671A3EB250DA0724EFBA35A03DB3E92B3925 = "christel-key" 7716860CF696BE44EA6250858FDB3CDEF9F63049 = "ester" 9D371C6D220F8A4C6D70F0D3F828698881AB2447 = "validator-0-account-key" +1F1AD30F60DF571249B05FF482CD0D26F5FB9DAE = "frank-key" [address_vp_types] diff --git a/genesis/localnet/transactions.toml b/genesis/localnet/transactions.toml index 8447b9e471..7e990f1544 100644 --- a/genesis/localnet/transactions.toml +++ b/genesis/localnet/transactions.toml @@ -53,6 +53,11 @@ tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qpk # 2. +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qz0vuxgzsz5lu7peghyr4yx5tuexecm27hdgglkpe444v6nmy6d8yuxhn0r"] + [[established_account]] vp = "vp_user" threshold = 1 diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index a31ac28ac3..7c850bbae1 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=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=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +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=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +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 f9acde8215..a7ed2f50c7 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=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=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +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=12ed8b060b295c06502a2ff8468e4a941cb7cca4#12ed8b060b295c06502a2ff8468e4a941cb7cca4" +source = "git+https://github.com/anoma/masp?rev=2914e6ff9a922bae8f1cb63a79d796a69af3d8aa#2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" dependencies = [ "bellman", "blake2b_simd",