diff --git a/Cargo.lock b/Cargo.lock index ffd306a156..0a7f260874 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 d62f7c0174..e6849d9d75 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 a56be799c2..facd39f4db 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -96,9 +96,9 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { let node = SubCmd::parse(matches).map(Self::Node); let client = SubCmd::parse(matches).map(Self::Client); - let relayer = SubCmd::parse(matches).map(Self::Relayer); - let eth_bridge_pool = - SubCmd::parse(matches).map(Self::EthBridgePool); + // let relayer = SubCmd::parse(matches).map(Self::Relayer); + // let eth_bridge_pool = + // SubCmd::parse(matches).map(Self::EthBridgePool); let wallet = SubCmd::parse(matches).map(Self::Wallet); let ledger = SubCmd::parse(matches).map(Self::Ledger); let tx_custom = SubCmd::parse(matches).map(Self::TxCustom); @@ -121,8 +121,8 @@ pub mod cmds { let tx_reveal_pk = SubCmd::parse(matches).map(Self::TxRevealPk); let complete = SubCmd::parse(matches).map(Self::Complete); node.or(client) - .or(relayer) - .or(eth_bridge_pool) + // .or(relayer) + // .or(eth_bridge_pool) .or(wallet) .or(ledger) .or(tx_custom) @@ -7645,8 +7645,7 @@ pub mod args { find_viewing_key(&mut wallet) } else { find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) - } - .key; + }; Ok(PayAddressGen:: { alias: self.alias, diff --git a/crates/apps_lib/src/cli/client.rs b/crates/apps_lib/src/cli/client.rs index a11f6b785b..38cddc6556 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 16f4304f98..63a6cb2801 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 @@ -582,12 +587,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, @@ -597,9 +641,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())) }) } } @@ -616,7 +663,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)) }) } @@ -631,10 +677,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())) }) } } @@ -666,8 +714,18 @@ impl ArgFromMutContext for TransferSource { Address::arg_from_ctx(ctx, raw) .map(Self::Address) .or_else(|_| { - ExtendedSpendingKey::arg_from_mut_ctx(ctx, raw) - .map(Self::ExtendedSpendingKey) + ExtendedSpendingKey::arg_from_mut_ctx(ctx, raw).map(|x| { + Self::ExtendedSpendingKey(PseudoExtendedKey::from( + MaspExtendedSpendingKey::from(x), + )) + }) + }) + .or_else(|_| { + ExtendedViewingKey::arg_from_mut_ctx(ctx, raw).map(|x| { + Self::ExtendedSpendingKey(PseudoExtendedKey::from( + MaspExtendedViewingKey::from(x), + )) + }) }) } } diff --git a/crates/apps_lib/src/cli/wallet.rs b/crates/apps_lib/src/cli/wallet.rs index e6a763f234..42f24a6b8d 100644 --- a/crates/apps_lib/src/cli/wallet.rs +++ b/crates/apps_lib/src/cli/wallet.rs @@ -8,7 +8,10 @@ use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use color_eyre::eyre::Result; use itertools::sorted; -use ledger_namada_rs::{BIP44Path, NamadaApp}; +use ledger_namada_rs::{BIP44Path, KeyResponse, NamadaApp, NamadaKeys}; +use ledger_transport_hid::hidapi::HidApi; +use ledger_transport_hid::TransportNativeHID; +use masp_primitives::zip32::ExtendedFullViewingKey; use namada_core::chain::BlockHeight; use namada_core::masp::{ExtendedSpendingKey, MaspValue, PaymentAddress}; use namada_sdk::address::{Address, DecodeError}; @@ -184,7 +187,7 @@ fn payment_addresses_list( } /// Derives a masp spending key from the mnemonic code in the wallet. -fn shielded_key_derive( +async fn shielded_key_derive( ctx: Context, io: &impl Io, args::KeyDerive { @@ -232,9 +235,56 @@ fn shielded_key_derive( }) .0 } else { - display_line!(io, "Not implemented."); - display_line!(io, "No changes are persisted. Exiting."); - cli::safe_exit(1) + let hidapi = HidApi::new().unwrap_or_else(|err| { + edisplay_line!(io, "Failed to create HidApi: {}", err); + cli::safe_exit(1) + }); + let app = NamadaApp::new( + TransportNativeHID::new(&hidapi).unwrap_or_else(|err| { + edisplay_line!(io, "Unable to connect to Ledger: {}", err); + cli::safe_exit(1) + }), + ); + let response = app + .retrieve_keys( + &BIP44Path { + path: derivation_path.to_string(), + }, + NamadaKeys::ViewKey, + true, + ) + .await + .unwrap_or_else(|err| { + edisplay_line!( + io, + "Unable to connect to query address and public key from \ + Ledger: {}", + err + ); + cli::safe_exit(1) + }); + let KeyResponse::ViewKey(response_key) = response else { + edisplay_line!(io, "Unexpected response from Ledger"); + cli::safe_exit(1) + }; + let xfvk = ExtendedFullViewingKey::try_from_slice(&response_key.xfvk) + .expect( + "unable to decode extended full viewing key from the hardware \ + wallet", + ); + + wallet + .insert_viewing_key( + alias, + xfvk.into(), + birthday, + alias_force, + Some(derivation_path), + ) + .unwrap_or_else(|| { + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }) }; wallet .save() @@ -367,7 +417,13 @@ fn shielded_key_address_add( let (alias, typ) = match masp_value { MaspValue::FullViewingKey(viewing_key) => { let alias = wallet - .insert_viewing_key(alias, viewing_key, birthday, alias_force) + .insert_viewing_key( + alias, + viewing_key, + birthday, + alias_force, + None, + ) .unwrap_or_else(|| { edisplay_line!(io, "Viewing key not added"); cli::safe_exit(1); @@ -638,7 +694,7 @@ async fn key_derive( if !args_key_derive.shielded { transparent_key_and_address_derive(ctx, io, args_key_derive).await } else { - shielded_key_derive(ctx, io, args_key_derive) + shielded_key_derive(ctx, io, args_key_derive).await } } diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 1ffae47901..925e3771c1 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -3,10 +3,21 @@ use std::io::Write; use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; -use ledger_namada_rs::{BIP44Path, NamadaApp}; +use ledger_namada_rs::{BIP44Path, KeyResponse, NamadaApp, NamadaKeys}; +use masp_primitives::sapling::redjubjub::PrivateKey; +use masp_primitives::sapling::{redjubjub, ProofGenerationKey}; +use masp_primitives::transaction::components::sapling; +use masp_primitives::transaction::components::sapling::builder::{ + BuildParams, ConvertBuildParams, OutputBuildParams, RngBuildParams, + SpendBuildParams, StoredBuildParams, +}; +use masp_primitives::transaction::components::sapling::fees::InputView; +use masp_primitives::zip32::{ + ExtendedFullViewingKey, ExtendedKey, PseudoExtendedKey, +}; 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, }; @@ -19,7 +30,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; @@ -34,6 +45,22 @@ use crate::wallet::{ gen_validator_keys, read_and_confirm_encryption_password, WalletTransport, }; +// Maximum number of spend description randomness parameters that can be +// generated on the hardware wallet. It is hard to compute the exact required +// number because a given MASP source could be distributed amongst several +// notes. +const MAX_HW_SPEND: usize = 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( @@ -76,7 +103,7 @@ pub async fn aux_signing_data( pub async fn with_hardware_wallet<'a, U, T>( mut tx: Tx, pubkey: common::PublicKey, - parts: HashSet, + parts: signing::Signable, (wallet, app): (&RwLock>, &NamadaApp), ) -> Result where @@ -123,7 +150,9 @@ where .await .map_err(|err| error::Error::Other(err.to_string()))?; // Sign the raw header if that is requested - if parts.contains(&signing::Signable::RawHeader) { + if parts == signing::Signable::RawHeader + || parts == signing::Signable::FeeRawHeader + { let pubkey = common::PublicKey::try_from_slice(&response.pubkey) .expect("unable to parse public key from Ledger"); let signature = @@ -141,7 +170,7 @@ where tx.add_section(Section::Authorization(compressed.expand(&tx))); } // Sign the fee header if that is requested - if parts.contains(&signing::Signable::FeeHeader) { + if parts == signing::Signable::FeeRawHeader { let pubkey = common::PublicKey::try_from_slice(&response.pubkey) .expect("unable to parse public key from Ledger"); let signature = @@ -816,11 +845,331 @@ 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> { - 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_keys.iter_mut()); + let shielded_hw_keys = + augment_masp_hardware_keys(namada, &args.tx, sources).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (mut tx, signing_data) = + args.clone().build(namada, &mut bparams).await?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); @@ -837,7 +1186,15 @@ pub async fn submit_shielding_transfer( ) -> Result<(), error::Error> { // Repeat once if the tx fails on a crossover of an epoch for _ in 0..2 { - let (tx, signing_data, tx_epoch) = args.clone().build(namada).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (tx, signing_data, tx_epoch) = + args.clone().build(namada, &mut bparams).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); @@ -889,9 +1246,22 @@ pub async fn submit_shielding_transfer( pub async fn submit_unshielding_transfer( namada: &impl Namada, - args: args::TxUnshieldingTransfer, + mut args: args::TxUnshieldingTransfer, ) -> Result<(), error::Error> { - let (mut tx, signing_data) = args.clone().build(namada).await?; + let sources = std::iter::once(&mut args.source) + .chain(args.gas_spending_keys.iter_mut()); + let shielded_hw_keys = + augment_masp_hardware_keys(namada, &args.tx, sources).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (mut tx, signing_data) = + args.clone().build(namada, &mut bparams).await?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); @@ -904,12 +1274,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_keys.iter_mut()); + let shielded_hw_keys = + augment_masp_hardware_keys(namada, &args.tx, sources).await?; + let mut bparams = generate_masp_build_params( + MAX_HW_SPEND, + MAX_HW_CONVERT, + MAX_HW_OUTPUT, + &args.tx, + ) + .await?; + let (mut tx, signing_data, _) = args.build(namada, &mut bparams).await?; + masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); diff --git a/crates/apps_lib/src/config/genesis/transactions.rs b/crates/apps_lib/src/config/genesis/transactions.rs index 4ab224db20..04421c0fcb 100644 --- a/crates/apps_lib/src/config/genesis/transactions.rs +++ b/crates/apps_lib/src/config/genesis/transactions.rs @@ -761,6 +761,7 @@ impl Signed { public_keys: pks.clone(), threshold, fee_payer: genesis_fee_payer_pk(), + shielded_hash: None, }; let mut tx = self.data.tx_to_sign(); @@ -782,7 +783,7 @@ impl Signed { async fn software_wallet_sign( tx: Tx, pubkey: common::PublicKey, - _parts: HashSet, + _parts: namada_sdk::signing::Signable, _user: (), ) -> Result { if pubkey == genesis_fee_payer_pk() { diff --git a/crates/apps_lib/src/config/genesis/utils.rs b/crates/apps_lib/src/config/genesis/utils.rs index c5971d9d2c..d5622111dd 100644 --- a/crates/apps_lib/src/config/genesis/utils.rs +++ b/crates/apps_lib/src/config/genesis/utils.rs @@ -2,7 +2,6 @@ use std::path::Path; use eyre::Context; use ledger_namada_rs::NamadaApp; -use namada_sdk::collections::HashSet; use namada_sdk::key::common; use namada_sdk::tx::Tx; use namada_sdk::wallet::Wallet; @@ -52,14 +51,14 @@ pub fn write_toml( pub(super) async fn with_hardware_wallet<'a, T>( tx: Tx, pubkey: common::PublicKey, - parts: HashSet, + parts: signing::Signable, (wallet, app): (&RwLock>, &NamadaApp), ) -> Result where T: ledger_transport::Exchange + Send + Sync, ::Error: std::error::Error, { - if parts.contains(&signing::Signable::FeeHeader) { + if parts == signing::Signable::FeeRawHeader { Ok(tx) } else { crate::client::tx::with_hardware_wallet( diff --git a/crates/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 ce9d7967c9..62ef4aa69b 100644 --- a/crates/core/src/masp.rs +++ b/crates/core/src/masp.rs @@ -11,6 +11,7 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::sapling::ViewingKey; use masp_primitives::transaction::TransparentAddress; pub use masp_primitives::transaction::TxId as TxIdInner; +use masp_primitives::zip32::{ExtendedKey, PseudoExtendedKey}; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; @@ -67,7 +68,7 @@ pub struct MaspTxId( serialize_with = "serialize_txid", deserialize_with = "deserialize_txid" )] - TxIdInner, + pub TxIdInner, ); impl From for MaspTxId { @@ -518,12 +519,13 @@ impl<'de> serde::Deserialize<'de> for ExtendedSpendingKey { } /// Represents a source of funds for a transfer +#[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub enum TransferSource { /// A transfer coming from a transparent address Address(Address), /// A transfer coming from a shielded address - ExtendedSpendingKey(ExtendedSpendingKey), + ExtendedSpendingKey(PseudoExtendedKey), } impl TransferSource { @@ -538,13 +540,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 { @@ -566,7 +576,9 @@ impl Display for TransferSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Address(x) => x.fmt(f), - Self::ExtendedSpendingKey(x) => x.fmt(f), + Self::ExtendedSpendingKey(x) => { + ExtendedViewingKey::from(x.to_viewing_key()).fmt(f) + } } } } diff --git a/crates/node/src/bench_utils.rs b/crates/node/src/bench_utils.rs index e8224f4a24..1490d600c3 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 { None, expiration, true, + &mut RngBuildParams::new(OsRng), ) .await }) diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index f7349c40ea..4f8a158b13 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -5,6 +5,8 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Duration as StdDuration; +use masp_primitives::transaction::components::sapling::builder::BuildParams; +use masp_primitives::zip32::PseudoExtendedKey; use namada_core::address::Address; use namada_core::chain::{BlockHeight, ChainId, Epoch}; use namada_core::collections::HashMap; @@ -121,7 +123,7 @@ impl NamadaTypes for SdkTypes { type MaspIndexerAddress = String; type PaymentAddress = namada_core::masp::PaymentAddress; type PublicKey = namada_core::key::common::PublicKey; - type SpendingKey = namada_core::masp::ExtendedSpendingKey; + type SpendingKey = PseudoExtendedKey; type TendermintAddress = tendermint_rpc::Url; type TransferSource = namada_core::masp::TransferSource; type TransferTarget = namada_core::masp::TransferTarget; @@ -381,8 +383,9 @@ impl TxShieldedTransfer { pub async fn build( &mut self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { - tx::build_shielded_transfer(context, self).await + tx::build_shielded_transfer(context, self, bparams).await } } @@ -427,8 +430,9 @@ impl TxShieldingTransfer { pub async fn build( &mut self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, MaspEpoch)> { - tx::build_shielding_transfer(context, self).await + tx::build_shielding_transfer(context, self, bparams).await } } @@ -466,8 +470,9 @@ impl TxUnshieldingTransfer { pub async fn build( &mut self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { - tx::build_unshielding_transfer(context, self).await + tx::build_unshielding_transfer(context, self, bparams).await } } @@ -615,9 +620,10 @@ impl TxIbcTransfer { pub async fn build( &self, context: &impl Namada, + bparams: &mut impl BuildParams, ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option)> { - tx::build_ibc_transfer(context, self).await + tx::build_ibc_transfer(context, self, bparams).await } } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index f3f0e2284b..bb19d936ad 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}; @@ -171,7 +171,7 @@ pub trait Namada: NamadaIo { fn new_shielded_transfer( &self, data: Vec, - gas_spending_keys: Vec, + gas_spending_keys: Vec, disposable_signing_key: bool, ) -> args::TxShieldedTransfer { args::TxShieldedTransfer { @@ -202,9 +202,9 @@ pub trait Namada: NamadaIo { /// arguments fn new_unshielding_transfer( &self, - source: ExtendedSpendingKey, + source: PseudoExtendedKey, data: Vec, - gas_spending_keys: Vec, + gas_spending_keys: Vec, disposable_signing_key: bool, ) -> args::TxUnshieldingTransfer { args::TxUnshieldingTransfer { @@ -607,7 +607,7 @@ pub trait Namada: NamadaIo { tx: &mut Tx, args: &args::Tx, signing_data: SigningTxData, - with: impl Fn(Tx, common::PublicKey, HashSet, D) -> F + with: impl Fn(Tx, common::PublicKey, signing::Signable, D) -> F + MaybeSend + MaybeSync, user_data: D, diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index 8d1c058489..9c5ff2d721 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -73,6 +73,8 @@ pub struct SigningTxData { pub account_public_keys_map: Option, /// The public keys of the fee payer pub fee_payer: common::PublicKey, + /// ID of the Transaction needing signing + pub shielded_hash: Option, } /// Find the public key for the given address and try to load the keypair @@ -169,11 +171,12 @@ pub async fn tx_signers( } } -/// The different parts of a transaction that can be signed +/// The different parts of a transaction that can be signed. Note that it's +/// impossible to sign the fee header without signing the raw header. #[derive(Eq, Hash, PartialEq)] pub enum Signable { - /// Fee header - FeeHeader, + /// Fee and raw header + FeeRawHeader, /// Raw header RawHeader, } @@ -182,7 +185,7 @@ pub enum Signable { pub async fn default_sign( _tx: Tx, pubkey: common::PublicKey, - _parts: HashSet, + _parts: Signable, _user: (), ) -> Result { Err(Error::Other(format!( @@ -207,7 +210,7 @@ pub async fn sign_tx<'a, D, F, U>( args: &args::Tx, tx: &mut Tx, signing_data: SigningTxData, - sign: impl Fn(Tx, common::PublicKey, HashSet, D) -> F, + sign: impl Fn(Tx, common::PublicKey, Signable, D) -> F, user_data: D, ) -> Result<(), Error> where @@ -267,7 +270,7 @@ where if let Ok(ntx) = sign( tx.clone(), pubkey.clone(), - HashSet::from([Signable::RawHeader]), + Signable::RawHeader, user_data.clone(), ) .await @@ -290,30 +293,24 @@ where Ok(fee_payer_keypair) => { tx.sign_wrapper(fee_payer_keypair); } - // The case where tge fee payer also signs the inner transaction - Err(_) - if signing_data.public_keys.contains(&signing_data.fee_payer) => - { - *tx = sign( - tx.clone(), - signing_data.fee_payer.clone(), - HashSet::from([Signable::FeeHeader, Signable::RawHeader]), - user_data, - ) - .await?; - used_pubkeys.insert(signing_data.fee_payer.clone()); - } - // The case where the fee payer does not sign the inner transaction Err(_) => { *tx = sign( tx.clone(), signing_data.fee_payer.clone(), - HashSet::from([Signable::FeeHeader]), + Signable::FeeRawHeader, user_data, ) .await?; + if signing_data.public_keys.contains(&signing_data.fee_payer) { + used_pubkeys.insert(signing_data.fee_payer.clone()); + } } } + // Remove redundant sections now that the signing process is complete. + // Though this call might be redundant in circumstances, it is placed here + // as a safeguard to prevent the transmission of private data to the + // network. + tx.protocol_filter(); // Then make sure that the number of public keys used exceeds the threshold let used_pubkeys_len = used_pubkeys .len() @@ -395,6 +392,7 @@ pub async fn aux_signing_data( threshold, account_public_keys_map, fee_payer, + shielded_hash: None, }) } diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 9e74fd3cc5..935b35b1c5 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -10,6 +10,9 @@ use borsh::BorshSerialize; use borsh_ext::BorshSerializeExt; use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::builder::Builder; +use masp_primitives::transaction::components::sapling::builder::{ + BuildParams, RngBuildParams, +}; use masp_primitives::transaction::components::sapling::fees::{ ConvertView, InputView as SaplingInputView, OutputView as SaplingOutputView, }; @@ -18,6 +21,7 @@ use masp_primitives::transaction::components::transparent::fees::{ }; use masp_primitives::transaction::components::I128Sum; use masp_primitives::transaction::{builder, Transaction as MaspTransaction}; +use masp_primitives::zip32::PseudoExtendedKey; use namada_account::{InitAccount, UpdateAccount}; use namada_core::address::{Address, IBC, MASP}; use namada_core::arith::checked; @@ -38,9 +42,7 @@ use namada_core::ibc::core::client::types::Height as IbcHeight; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_core::ibc::primitives::Timestamp as IbcTimestamp; use namada_core::key::{self, *}; -use namada_core::masp::{ - AssetData, ExtendedSpendingKey, MaspEpoch, TransferSource, TransferTarget, -}; +use namada_core::masp::{AssetData, MaspEpoch, TransferSource, TransferTarget}; use namada_core::storage; use namada_core::time::DateTimeUtc; use namada_governance::cli::onchain::{ @@ -2520,6 +2522,7 @@ pub async fn build_pgf_stewards_proposal( pub async fn build_ibc_transfer( context: &impl Namada, args: &args::TxIbcTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData, Option)> { if args.ibc_shielding_data.is_some() && args.ibc_memo.is_some() { return Err(Error::Other( @@ -2533,7 +2536,7 @@ pub async fn build_ibc_transfer( get_refund_target(context, &args.source, &args.refund_target).await?; let source = args.source.effective_address(); - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, Some(source.clone()), @@ -2629,6 +2632,7 @@ pub async fn build_ibc_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + bparams, ) .await?; let shielded_tx_epoch = shielded_parts.as_ref().map(|trans| trans.0.epoch); @@ -2679,6 +2683,7 @@ pub async fn build_ibc_transfer( let masp_tx_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx.clone()).1; transfer.shielded_section_hash = Some(masp_tx_hash); + signing_data.shielded_hash = Some(masp_tx_hash); tx.add_masp_builder(MaspBuilder { asset_types, metadata: shielded_transfer.metadata, @@ -3030,8 +3035,9 @@ pub async fn build_transparent_transfer( pub async fn build_shielded_transfer( context: &N, args: &mut args::TxShieldedTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData)> { - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, Some(MASP), @@ -3094,6 +3100,7 @@ pub async fn build_shielded_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielded transfer must have shielded parts"); @@ -3123,6 +3130,7 @@ pub async fn build_shielded_transfer( }); data.shielded_section_hash = Some(section_hash); + signing_data.shielded_hash = Some(section_hash); tracing::debug!("Transfer data {data:?}"); Ok(()) }; @@ -3147,7 +3155,7 @@ async fn get_masp_fee_payment_amount( args: &args::Tx, fee_amount: DenominatedAmount, fee_payer: &common::PublicKey, - gas_spending_keys: Vec, + gas_spending_keys: Vec, ) -> Result> { let fee_payer_address = Address::from(fee_payer); let balance_key = balance_key(&args.fee_token, &fee_payer_address); @@ -3175,6 +3183,7 @@ async fn get_masp_fee_payment_amount( pub async fn build_shielding_transfer( context: &N, args: &mut args::TxShieldingTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData, MaspEpoch)> { let source = if args.data.len() == 1 { // If only one transfer take its source as the signer @@ -3186,7 +3195,7 @@ pub async fn build_shielding_transfer( // argument None }; - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, source.clone(), @@ -3261,6 +3270,7 @@ pub async fn build_shielding_transfer( None, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3291,6 +3301,7 @@ pub async fn build_shielding_transfer( }); data.shielded_section_hash = Some(shielded_section_hash); + signing_data.shielded_hash = Some(shielded_section_hash); tracing::debug!("Transfer data {data:?}"); Ok(()) }; @@ -3312,8 +3323,9 @@ pub async fn build_shielding_transfer( pub async fn build_unshielding_transfer( context: &N, args: &mut args::TxUnshieldingTransfer, + bparams: &mut impl BuildParams, ) -> Result<(Tx, SigningTxData)> { - let signing_data = signing::aux_signing_data( + let mut signing_data = signing::aux_signing_data( context, &args.tx, Some(MASP), @@ -3383,6 +3395,7 @@ pub async fn build_unshielding_transfer( masp_fee_data, !(args.tx.dry_run || args.tx.dry_run_wrapper), args.tx.expiration.to_datetime(), + bparams, ) .await? .expect("Shielding transfer must have shielded parts"); @@ -3412,6 +3425,7 @@ pub async fn build_unshielding_transfer( }); data.shielded_section_hash = Some(shielded_section_hash); + signing_data.shielded_hash = Some(shielded_section_hash); tracing::debug!("Transfer data {data:?}"); Ok(()) }; @@ -3436,6 +3450,7 @@ async fn construct_shielded_parts( fee_data: Option, update_ctx: bool, expiration: Option, + bparams: &mut impl BuildParams, ) -> Result)>> { // Precompute asset types to increase chances of success in decoding let token_map = context.wallet().await.get_addresses(); @@ -3449,7 +3464,7 @@ async fn construct_shielded_parts( shielded .gen_shielded_transfer( - context, data, fee_data, expiration, update_ctx, + context, data, fee_data, expiration, update_ctx, bparams, ) .await }; @@ -3816,6 +3831,7 @@ pub async fn gen_ibc_shielding_transfer( None, args.expiration.to_datetime(), true, + &mut RngBuildParams::new(OsRng), ) .await .map_err(|err| TxSubmitError::MaspError(err.to_string()))? diff --git a/crates/shielded_token/src/masp.rs b/crates/shielded_token/src/masp.rs index cc1fc79fbb..c95ae9ac74 100644 --- a/crates/shielded_token/src/masp.rs +++ b/crates/shielded_token/src/masp.rs @@ -26,7 +26,8 @@ use masp_primitives::transaction::components::sapling::builder::SaplingMetadata; use masp_primitives::transaction::components::{I128Sum, ValueSum}; use masp_primitives::transaction::Transaction; use masp_primitives::zip32::{ - ExtendedFullViewingKey, ExtendedSpendingKey as MaspExtendedSpendingKey, + ExtendedFullViewingKey, ExtendedKey, + ExtendedSpendingKey as MaspExtendedSpendingKey, PseudoExtendedKey, }; use masp_proofs::prover::LocalTxProver; use namada_core::address::Address; @@ -81,7 +82,7 @@ pub struct ShieldedTransfer { #[allow(missing_docs)] #[derive(Debug)] pub struct MaspFeeData { - pub sources: Vec, + pub sources: Vec, pub target: Address, pub token: Address, pub amount: token::DenominatedAmount, @@ -131,7 +132,7 @@ pub struct MaspTxReorderedData { /// Data about the unspent amounts for any given shielded source coming from the /// spent notes in their posses that have been added to the builder. Can be used /// to either pay fees or to return a change -pub type Changes = HashMap; +pub type Changes = HashMap; /// Shielded pool data for a token #[allow(missing_docs)] @@ -169,20 +170,20 @@ pub struct WalletMap; impl masp_primitives::transaction::components::sapling::builder::MapBuilder< P1, - MaspExtendedSpendingKey, + PseudoExtendedKey, (), ExtendedFullViewingKey, > for WalletMap { fn map_params(&self, _s: P1) {} - fn map_key(&self, s: MaspExtendedSpendingKey) -> ExtendedFullViewingKey { - (&s).into() + fn map_key(&self, s: PseudoExtendedKey) -> ExtendedFullViewingKey { + s.to_viewing_key() } } impl - MapBuilder + MapBuilder for WalletMap { fn map_notifier(&self, _s: N1) {} @@ -826,7 +827,7 @@ pub mod testing { mut rng in arb_rng().prop_map(TestCsprng), bparams_rng in arb_rng().prop_map(TestCsprng), prover_rng in arb_rng().prop_map(TestCsprng), - ) -> (MaspExtendedSpendingKey, Diversifier, Note, Node) { + ) -> (PseudoExtendedKey, Diversifier, Note, Node) { let mut spending_key_seed = [0; 32]; rng.fill_bytes(&mut spending_key_seed); let spending_key = MaspExtendedSpendingKey::master(spending_key_seed.as_ref()); @@ -837,7 +838,7 @@ pub mod testing { .to_payment_address(div) .expect("a PaymentAddress"); - let mut builder = Builder::::new( + let mut builder = Builder::::new( NETWORK, // NOTE: this is going to add 20 more blocks to the actual // expiration but there's no other exposed function that we could @@ -871,7 +872,7 @@ pub mod testing { assert_eq!(payment_addr, pa); // Make a path to out new note let node = Node::new(shielded_output.cmu.to_repr()); - (spending_key, div, note, node) + (PseudoExtendedKey::from(spending_key), div, note, node) } } @@ -916,7 +917,7 @@ pub mod testing { ).unwrap(), *value, )).collect::>() - ) -> Vec<(MaspExtendedSpendingKey, Diversifier, Note, Node)> { + ) -> Vec<(PseudoExtendedKey, Diversifier, Note, Node)> { spend_description } } diff --git a/crates/shielded_token/src/masp/shielded_wallet.rs b/crates/shielded_token/src/masp/shielded_wallet.rs index 470e197995..d54b5279bf 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -18,13 +18,13 @@ use masp_primitives::sapling::{ Diversifier, Node, Note, Nullifier, ViewingKey, }; use masp_primitives::transaction::builder::Builder; -use masp_primitives::transaction::components::sapling::builder::RngBuildParams; +use masp_primitives::transaction::components::sapling::builder::BuildParams; use masp_primitives::transaction::components::{ I128Sum, TxOut, U64Sum, ValueSum, }; use masp_primitives::transaction::fees::fixed::FeeRule; use masp_primitives::transaction::{builder, Transaction}; -use masp_primitives::zip32::ExtendedSpendingKey as MaspExtendedSpendingKey; +use masp_primitives::zip32::{ExtendedKey, PseudoExtendedKey}; use namada_core::address::Address; use namada_core::arith::checked; use namada_core::borsh::{BorshDeserialize, BorshSerialize}; @@ -51,12 +51,11 @@ use rand_core::{OsRng, SeedableRng}; use crate::masp::utils::MaspClient; use crate::masp::{ - cloned_pair, is_amount_required, to_viewing_key, Changes, - ContextSyncStatus, Conversions, MaspAmount, MaspDataLog, MaspFeeData, - MaspSourceTransferData, MaspTargetTransferData, MaspTransferData, - MaspTxReorderedData, NoteIndex, ShieldedSyncConfig, ShieldedTransfer, - ShieldedUtils, SpentNotesTracker, TransferErr, WalletMap, WitnessMap, - NETWORK, + cloned_pair, is_amount_required, Changes, ContextSyncStatus, Conversions, + MaspAmount, MaspDataLog, MaspFeeData, MaspSourceTransferData, + MaspTargetTransferData, MaspTransferData, MaspTxReorderedData, NoteIndex, + ShieldedSyncConfig, ShieldedTransfer, ShieldedUtils, SpentNotesTracker, + TransferErr, WalletMap, WitnessMap, NETWORK, }; #[cfg(any(test, feature = "testing"))] use crate::masp::{testing, ENV_VAR_MASP_TEST_SEED}; @@ -689,7 +688,7 @@ pub trait ShieldedApi: &mut self, context: &impl NamadaIo, spent_notes: &mut SpentNotesTracker, - sk: namada_core::masp::ExtendedSpendingKey, + sk: PseudoExtendedKey, is_native_token: bool, target: I128Sum, target_epoch: MaspEpoch, @@ -702,7 +701,7 @@ pub trait ShieldedApi: ), eyre::Error, > { - let vk = &to_viewing_key(&sk.into()).vk; + let vk = &sk.to_viewing_key().fvk.vk; // TODO: we should try to use the smallest notes possible to fund the // transaction to allow people to fetch less often // Establish connection with which to do exchange rate queries @@ -907,6 +906,7 @@ pub trait ShieldedApi: fee_data: Option, expiration: Option, update_ctx: bool, + bparams: &mut impl BuildParams, ) -> Result, TransferErr> { // Determine epoch in which to submit potential shielded transaction let epoch = Self::query_masp_epoch(context.client()) @@ -979,7 +979,7 @@ pub trait ShieldedApi: u32::MAX - 20 } }; - let mut builder = Builder::::new( + let mut builder = Builder::::new( NETWORK, // NOTE: this is going to add 20 more blocks to the actual // expiration but there's no other exposed function that we could @@ -1082,7 +1082,7 @@ pub trait ShieldedApi: &prover, &FeeRule::non_standard(U64Sum::zero()), &mut rng, - &mut RngBuildParams::new(OsRng), + bparams, ) .map_err(|error| TransferErr::Build { error, data: None })?; @@ -1187,7 +1187,7 @@ pub trait ShieldedApi: async fn add_inputs( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source: &TransferSource, token: &Address, amount: &DenominatedAmount, @@ -1244,12 +1244,7 @@ pub trait ShieldedApi: // Commit the notes found to our transaction for (diversifier, note, merkle_path) in unspent_notes { builder - .add_sapling_spend( - sk.into(), - diversifier, - note, - merkle_path, - ) + .add_sapling_spend(sk, diversifier, note, merkle_path) .map_err(|e| TransferErr::Build { error: builder::Error::SaplingBuild(e), data: None, @@ -1316,7 +1311,7 @@ pub trait ShieldedApi: async fn add_outputs( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source: TransferSource, target: &TransferTarget, token: Address, @@ -1362,9 +1357,8 @@ pub trait ShieldedApi: let contr = std::cmp::min(u128::from(*rem_amount), val) as u64; // If we are sending to a shielded address, we need the outgoing // viewing key in the following computations. - let ovk_opt = source - .spending_key() - .map(|x| MaspExtendedSpendingKey::from(x).expsk.ovk); + let ovk_opt = + source.spending_key().map(|x| x.to_viewing_key().fvk.ovk); // Make transaction output tied to the current token, // denomination, and epoch. if let Some(pa) = payment_address { @@ -1457,9 +1451,9 @@ pub trait ShieldedApi: async fn add_fees( &mut self, context: &impl NamadaIo, - builder: &mut Builder, + builder: &mut Builder, source_data: &HashMap, - sources: Vec, + sources: Vec, target: &Address, token: &Address, amount: &DenominatedAmount, @@ -1699,17 +1693,17 @@ pub trait ShieldedApi: #[allow(clippy::result_large_err)] #[allow(async_fn_in_trait)] fn add_changes( - builder: &mut Builder, + builder: &mut Builder, changes: Changes, ) -> Result<(), TransferErr> { for (sp, changes) in changes.into_iter() { for (asset_type, amt) in changes.components() { if let Ordering::Greater = amt.cmp(&0) { - let sk = MaspExtendedSpendingKey::from(sp.to_owned()); + let sk = sp.to_viewing_key(); // Send the change in this asset type back to the sender builder .add_sapling_output( - Some(sk.expsk.ovk), + Some(sk.fvk.ovk), sk.default_address().1, *asset_type, *amt as u64, diff --git a/crates/shielded_token/src/validation.rs b/crates/shielded_token/src/validation.rs index 65a970cc85..39d46748f6 100644 --- a/crates/shielded_token/src/validation.rs +++ b/crates/shielded_token/src/validation.rs @@ -16,6 +16,7 @@ use masp_primitives::transaction::txid::TxIdDigester; use masp_primitives::transaction::{ Authorization, Authorized, Transaction, TransactionData, Unauthorized, }; +use masp_primitives::zip32::ExtendedSpendingKey; use masp_proofs::bellman::groth16::VerifyingKey; use masp_proofs::sapling::BatchValidator; use namada_gas::Gas; @@ -56,7 +57,8 @@ pub struct PartialAuthorized; impl Authorization for PartialAuthorized { type SaplingAuth = ::SaplingAuth; - type TransparentAuth = ::TransparentAuth; + type TransparentAuth = + as Authorization>::TransparentAuth; } /// MASP verifying keys diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index dceed2b2ac..87e3cf47c7 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -25,6 +25,7 @@ use namada_apps_lib::config::utils::convert_tm_addr_to_socket_addr; use namada_apps_lib::config::{self, ethereum_bridge}; use namada_apps_lib::tendermint_config::net::Address as TendermintAddress; use namada_apps_lib::wallet; +use namada_apps_lib::wallet::defaults::is_use_device; use namada_core::chain::ChainId; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::address::Address; @@ -2501,7 +2502,7 @@ fn masp_txs_and_queries() -> Result<()> { let txs_args = vec![ // 2. Shield 20 BTC from Albert to PA(A) ( - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -2511,12 +2512,12 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "20", - ], + ]), TX_APPLIED_SUCCESS, ), // 3. Transfer 7 BTC from SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2528,7 +2529,7 @@ fn masp_txs_and_queries() -> Result<()> { "7", "--gas-payer", CHRISTEL_KEY, - ], + ]), TX_APPLIED_SUCCESS, ), // 4. Assert BTC balance at VK(A) is 13 @@ -2538,7 +2539,7 @@ fn masp_txs_and_queries() -> Result<()> { ), // 5. Unshield 5 BTC from SK(B) to Bertha ( - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -2550,7 +2551,7 @@ fn masp_txs_and_queries() -> Result<()> { "5", "--gas-payer", CHRISTEL_KEY, - ], + ]), TX_APPLIED_SUCCESS, ), // 6. Assert BTC balance at VK(B) is 2 @@ -2571,6 +2572,9 @@ fn masp_txs_and_queries() -> Result<()> { )?; sync.assert_success(); for &dry_run in &[true, false] { + if dry_run && is_use_device() { + continue; + } let tx_args = if dry_run && (tx_args[0] == "transfer" || tx_args[0] == "shield" diff --git a/crates/tests/src/e2e/setup.rs b/crates/tests/src/e2e/setup.rs index b9b709a913..86aec5c961 100644 --- a/crates/tests/src/e2e/setup.rs +++ b/crates/tests/src/e2e/setup.rs @@ -1463,23 +1463,23 @@ pub mod constants { pub const FRANK_KEY: &str = "Frank-key"; // Shielded spending and viewing keys and payment addresses - pub const A_SPENDING_KEY: &str = "zsknam1qdrk9kd8qqqqpqy3pxzxu2kexydl7ug22s3808htl604emmz9qlde9cl9mx6euhvh3cpl9w7guustfzjxsyaeqtefhden6q8776t9cr9vkqztj7u0mgs5k9nz945sypev9ppptn5d85as3ccsnu3q6g3acqp2gpsrwe6naqg3stqp43uk9x2cj79gcxuum8a7jayjqlv4ptcfnunqkqzsj6m2r3sn8ft0tyqqpv28nghe4ag68eccaqx7v5f65he95g5uwq2wr4yuqc06jgc7"; - pub const B_SPENDING_KEY: &str = "zsknam1qdml0zguqqqqpqx8elavks722m0cjelgh3r044cfregyw049jze9lwha2cfqdqnekecnttdvygd6s784kch2v3wjs45g5z0n36hpqv5ruy8jjfu5mz2snl8ljyz79h3szmyf43zve79l6hwnlfk94r422tfwr2f62vvgkeqvc4z2dgrvqy033ymq5ylz3gmf6wdzhsdmzm0h9uv9374x755rzgvmcxhxntu6v63acqktv6zk390e9pd6vr0pzqaq6auu59kwpnw0haczfyju8"; + pub const A_SPENDING_KEY: &str = "albert-svk"; + pub const B_SPENDING_KEY: &str = "bertha-svk"; // A payment address derived from A_SPENDING_KEY - pub const AA_PAYMENT_ADDRESS: &str = "znam1ky620tz7z658cralqt693qpvk42wvth468zp38nqvq2apmex5rfut3dfqm2asrsqv0tc7saqje7"; + pub const AA_PAYMENT_ADDRESS: &str = "albert-pa"; // A payment address derived from B_SPENDING_KEY - pub const AB_PAYMENT_ADDRESS: &str = "znam1zxt8e22uz666ce7hxqpc69yfj3tpd9v26ep2epwn34kvyuwjh98hhre9897shcjj4cnqugwlv4q"; + pub const AB_PAYMENT_ADDRESS: &str = "bertha-pa-a"; // A viewing key derived from B_SPENDING_KEY - pub const AB_VIEWING_KEY: &str = "zvknam1qdml0zguqqqqpqx8elavks722m0cjelgh3r044cfregyw049jze9lwha2cfqdqnekem0xdqf9ytuhaxzeunyl7svgvxjv5g73m24k7w0h6q7wtvcltvlzynzhc5grlfgv7037lfh8w3su5krnzzzjh4nsleydtlns4gl0vmnc4z2dgrvqy033ymq5ylz3gmf6wdzhsdmzm0h9uv9374x755rzgvmcxhxntu6v63acqktv6zk390e9pd6vr0pzqaq6auu59kwpnw0hacdsfkws"; + pub const AB_VIEWING_KEY: &str = "bertha-svk"; // A payment address derived from B_VIEWING_KEY - pub const BB_PAYMENT_ADDRESS: &str = "znam1mqt0ja2zccy70du2d6rcr77jscgq3gkekfvhrqe7zkxa8rr3qsjsrd66gxnrykdmdeh5wmglmcm"; + pub const BB_PAYMENT_ADDRESS: &str = "bertha-pa-b"; // A viewing key derived from A_SPENDING_KEY - pub const AA_VIEWING_KEY: &str = "zvknam1qdrk9kd8qqqqpqy3pxzxu2kexydl7ug22s3808htl604emmz9qlde9cl9mx6euhvhnc63hymme53jz3mmwrzfkr9tk82nqacf5vlmj9du3s3rjz0h6usnh47pw0ufw4u6yrfvf95wfa9xj0m8pcrns9yh90s0jkf3cqy2z7c3stqp43uk9x2cj79gcxuum8a7jayjqlv4ptcfnunqkqzsj6m2r3sn8ft0tyqqpv28nghe4ag68eccaqx7v5f65he95g5uwq2wr4yuqc8djdrp"; - pub const C_SPENDING_KEY: &str = "zsknam1qdy5g4udqqqqpqrfdzej0s45m8s6nprder4udwqm3ql8wx34e8f46dv8cwnmcjp40uj3qy5tgetj27jytvxk4vpa3pjsd80y332nj542w39wta8lsrzqzs822ydgmz5g2sd2k29hxc3uh77v5cmcext799fxn6sa9rd3zuggl6flgjz7wz9wwu9kxd4rth4clw6ug4drxln96y96nf8fmvgm5eddm93azuzlkjj0dpw343ukwcfuvkdhd772539cskgggcqsaaf0j7czshjwe"; + pub const AA_VIEWING_KEY: &str = "albert-svk"; + pub const C_SPENDING_KEY: &str = "christel-svk"; // A viewing key derived from C_SPENDING_KEY - pub const AC_VIEWING_KEY: &str = "zvknam1qdy5g4udqqqqpqrfdzej0s45m8s6nprder4udwqm3ql8wx34e8f46dv8cwnmcjp40lr4vutffut7ed5x6egd6etcdh9sxh3j9fe5dshhrn3nq4yfp78gt8ve59y4vnu45xlt93vtrzsxtwlxjjgu2p496lc3ye8m83qplsqfl6flgjz7wz9wwu9kxd4rth4clw6ug4drxln96y96nf8fmvgm5eddm93azuzlkjj0dpw343ukwcfuvkdhd772539cskgggcqsaaf0j7cfyd3jr"; + pub const AC_VIEWING_KEY: &str = "christel-svk"; // A viewing key derived from C_VIEWING_KEY - pub const AC_PAYMENT_ADDRESS: &str = "znam1xv4ml6fp3zqjhw20xj3srd75cq8tyejdst0xweq60c70732ty2chd2v39tllpzf4uf6s66vfm6w"; + pub const AC_PAYMENT_ADDRESS: &str = "christel-pa"; // Native VP aliases pub const GOVERNANCE_ADDRESS: &str = "governance"; diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 07690d1e0e..9c5b89e4a1 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; -use namada_apps_lib::wallet::defaults::{albert_keypair, christel_keypair}; +use namada_apps_lib::wallet::defaults::{ + get_unencrypted_keypair, is_use_device, +}; use namada_core::dec::Dec; use namada_core::masp::TokenMap; use namada_node::shell::testing::client::run; @@ -26,6 +28,7 @@ use crate::e2e::setup::constants::{ BB_PAYMENT_ADDRESS, BERTHA, BERTHA_KEY, BTC, B_SPENDING_KEY, CHRISTEL, CHRISTEL_KEY, ETH, MASP, NAM, }; +use crate::e2e::setup::{apply_use_device, ensure_hot_key}; use crate::strings::TX_APPLIED_SUCCESS; /// In this test we verify that users of the MASP receive the correct rewards @@ -46,7 +49,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -56,9 +59,11 @@ fn masp_incentives() -> Result<()> { BTC, "--amount", "1", + "--signing-keys", + ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -262,7 +267,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -272,9 +277,11 @@ fn masp_incentives() -> Result<()> { ETH, "--amount", "0.001", + "--signing-keys", + ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -372,7 +379,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.725514")); + assert!(captured.contains("nam: 0.750883")); // Assert NAM balance at MASP pool is an accumulation of // rewards from both the shielded BTC and shielded ETH @@ -392,7 +399,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.358764")); + assert!(captured.contains("nam: 1.384131")); // Wait till epoch boundary node.next_masp_epoch(); @@ -400,7 +407,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -414,7 +421,7 @@ fn masp_incentives() -> Result<()> { BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -472,7 +479,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.451732")); + assert!(captured.contains("nam: 1.502496")); node.next_masp_epoch(); // sync the shielded context @@ -501,7 +508,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 3.219616")); + assert!(captured.contains("nam: 3.270374")); // Wait till epoch boundary node.next_masp_epoch(); @@ -510,7 +517,7 @@ fn masp_incentives() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -524,7 +531,7 @@ fn masp_incentives() -> Result<()> { ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -592,7 +599,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 3.723616")); + assert!(captured.contains("nam: 3.774374")); // Wait till epoch boundary node.next_masp_epoch(); @@ -644,7 +651,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.451732")); + assert!(captured.contains("nam: 1.502496")); // Assert NAM balance at MASP pool is // the accumulation of rewards from the shielded assets (BTC and ETH) @@ -664,7 +671,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 3.723616")); + assert!(captured.contains("nam: 3.774374")); // Wait till epoch boundary to prevent conversion expiry during transaction // construction @@ -676,11 +683,12 @@ fn masp_incentives() -> Result<()> { vec!["shielded-sync", "--node", validator_one_rpc], )?; node.assert_success(); + // Send all NAM rewards from SK(B) to Christel run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -691,12 +699,12 @@ fn masp_incentives() -> Result<()> { "--gas-limit", "300000", "--amount", - "1.451732", + "1.502496", "--signing-keys", BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -709,11 +717,12 @@ fn masp_incentives() -> Result<()> { vec!["shielded-sync", "--node", validator_one_rpc], )?; node.assert_success(); + // Send all NAM rewards from SK(A) to Bertha run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -727,7 +736,7 @@ fn masp_incentives() -> Result<()> { ALBERT_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -801,7 +810,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.003222")); + assert!(captured.contains("nam: 0.003216")); Ok(()) } @@ -829,7 +838,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -841,7 +850,7 @@ fn spend_unconverted_asset_type() -> Result<()> { "20", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -850,7 +859,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -862,7 +871,7 @@ fn spend_unconverted_asset_type() -> Result<()> { "0.000001", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -907,7 +916,7 @@ fn spend_unconverted_asset_type() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", B_SPENDING_KEY, @@ -921,7 +930,7 @@ fn spend_unconverted_asset_type() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -979,7 +988,7 @@ fn masp_txs_and_queries() -> Result<()> { let txs_args = vec![ // 0. Attempt to spend 10 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -993,12 +1002,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 1. Attempt to spend 15 BTC at SK(A) to Bertha ( - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1012,12 +1021,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 2. Send 20 BTC from Albert to PA(A) ( - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1029,12 +1038,12 @@ fn masp_txs_and_queries() -> Result<()> { "20", "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 3. Attempt to spend 10 ETH at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1048,12 +1057,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 4. Spend 7 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1067,12 +1076,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 5. Spend 7 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1086,12 +1095,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 6. Attempt to spend 7 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1105,12 +1114,12 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Err(""), ), // 7. Spend 6 BTC at SK(A) to PA(B) ( - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1124,7 +1133,7 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), // 8. Assert BTC balance at VK(A) is 0 @@ -1168,7 +1177,7 @@ fn masp_txs_and_queries() -> Result<()> { ), // 11. Send 20 BTC from SK(B) to Bertha ( - vec![ + apply_use_device(vec![ "unshield", "--source", B_SPENDING_KEY, @@ -1182,7 +1191,7 @@ fn masp_txs_and_queries() -> Result<()> { CHRISTEL_KEY, "--node", validator_one_rpc, - ], + ]), Response::Ok(TX_APPLIED_SUCCESS), ), ]; @@ -1207,7 +1216,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() @@ -1288,41 +1299,12 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // 1. Shield tokens _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1334,14 +1316,14 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1353,14 +1335,14 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "200", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1372,7 +1354,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync shielded context @@ -1391,7 +1373,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1408,7 +1390,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -1425,7 +1407,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1442,7 +1424,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -1459,7 +1441,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", B_SPENDING_KEY, @@ -1476,7 +1458,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -1490,7 +1472,9 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { txs_bytes.push(std::fs::read(&file_path).unwrap()); std::fs::remove_file(&file_path).unwrap(); - let sk = christel_keypair(); + let sk = get_unencrypted_keypair( + &ensure_hot_key(CHRISTEL_KEY).to_ascii_lowercase(), + ); let pk = sk.to_public(); let native_token = node @@ -1536,28 +1520,12 @@ fn expired_masp_tx() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // 1. Shield tokens _ = node.next_epoch(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -1569,7 +1537,7 @@ fn expired_masp_tx() -> Result<()> { "100", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync shielded context @@ -1587,7 +1555,7 @@ fn expired_masp_tx() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -1614,7 +1582,7 @@ fn expired_masp_tx() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1629,7 +1597,9 @@ fn expired_masp_tx() -> Result<()> { let tx_bytes = std::fs::read(&file_path).unwrap(); std::fs::remove_file(&file_path).unwrap(); - let sk = christel_keypair(); + let sk = get_unencrypted_keypair( + &ensure_hot_key(CHRISTEL_KEY).to_ascii_lowercase(), + ); let pk = sk.to_public(); let native_token = node @@ -1720,7 +1690,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1730,9 +1700,11 @@ fn cross_epoch_unshield() -> Result<()> { NAM, "--amount", "1000", + "--signing-keys", + ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1755,7 +1727,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -1772,7 +1744,7 @@ fn cross_epoch_unshield() -> Result<()> { "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1791,7 +1763,7 @@ fn cross_epoch_unshield() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "tx", "--owner", ALBERT_KEY, @@ -1799,7 +1771,7 @@ fn cross_epoch_unshield() -> Result<()> { tx_path.to_str().unwrap(), "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -1861,7 +1833,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -1873,7 +1845,7 @@ fn dynamic_assets() -> Result<()> { "1", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync the shielded context @@ -1995,7 +1967,7 @@ fn dynamic_assets() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -2007,7 +1979,7 @@ fn dynamic_assets() -> Result<()> { "1", "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync the shielded context @@ -2357,40 +2329,11 @@ fn masp_fee_payment() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2404,7 +2347,7 @@ fn masp_fee_payment() -> Result<()> { CHRISTEL_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); _ = node.next_masp_epoch(); @@ -2438,7 +2381,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2455,7 +2398,7 @@ fn masp_fee_payment() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_err()); @@ -2574,7 +2517,7 @@ fn masp_fee_payment() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -2589,7 +2532,7 @@ fn masp_fee_payment() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); // sync shielded context @@ -2655,40 +2598,11 @@ fn masp_fee_payment_gas_limit() -> Result<()> { })?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2700,7 +2614,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { "1000000", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -2739,7 +2653,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -2756,7 +2670,7 @@ fn masp_fee_payment_gas_limit() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_err()); @@ -2805,40 +2719,11 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -2854,7 +2739,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { BERTHA_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -2909,7 +2794,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -2927,7 +2812,7 @@ fn masp_fee_payment_with_non_disposable() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -2994,53 +2879,11 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { let (mut node, _services) = setup::setup()?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_c", - "--value", - AC_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3052,13 +2895,13 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "10000", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3070,7 +2913,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "300000", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -3124,7 +2967,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -3143,7 +2986,7 @@ fn masp_fee_payment_with_custom_spending_key() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3234,40 +3077,11 @@ fn masp_fee_payment_with_different_token() -> Result<()> { })?; _ = node.next_masp_epoch(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_b", - "--value", - AB_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT_KEY, @@ -3279,13 +3093,13 @@ fn masp_fee_payment_with_different_token() -> Result<()> { "1", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3299,13 +3113,13 @@ fn masp_fee_payment_with_different_token() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", ALBERT, @@ -3319,7 +3133,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { ALBERT_KEY, "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); @@ -3390,7 +3204,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "transfer", "--source", A_SPENDING_KEY, @@ -3411,7 +3225,7 @@ fn masp_fee_payment_with_different_token() -> Result<()> { "--disposable-gas-payer", "--ledger-address", validator_one_rpc, - ], + ]), ) }); assert!(captured.result.is_ok()); @@ -3516,30 +3330,14 @@ fn identical_output_descriptions() -> Result<()> { _ = node.next_masp_epoch(); let tempdir = tempfile::tempdir().unwrap(); - // Add the relevant viewing keys to the wallet otherwise the shielded - // context won't precache the masp data - run( - &node, - Bin::Wallet, - vec![ - "add", - "--alias", - "alias_a", - "--value", - AA_VIEWING_KEY, - "--unsafe-dont-encrypt", - ], - )?; - node.assert_success(); - // Generate a tx to shield some tokens run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "shield", "--source", - ALBERT_KEY, + ensure_hot_key(ALBERT_KEY), "--target", AA_PAYMENT_ADDRESS, "--token", @@ -3549,13 +3347,13 @@ fn identical_output_descriptions() -> Result<()> { "--gas-limit", "300000", "--gas-payer", - ALBERT_KEY, + ensure_hot_key(ALBERT_KEY), "--output-folder-path", tempdir.path().to_str().unwrap(), "--dump-tx", "--ledger-address", validator_one_rpc, - ], + ]), )?; node.assert_success(); let file_path = tempdir @@ -3581,12 +3379,16 @@ fn identical_output_descriptions() -> Result<()> { tx_clone.header.batch.clear(); tx_clone.header.batch.insert(cmt); + let keypair = get_unencrypted_keypair( + &ensure_hot_key(ALBERT_KEY).to_ascii_lowercase(), + ); let signing_data = SigningTxData { owner: None, - public_keys: vec![albert_keypair().to_public()], + public_keys: vec![keypair.to_public()], threshold: 1, account_public_keys_map: None, - fee_payer: albert_keypair().to_public(), + fee_payer: keypair.to_public(), + shielded_hash: None, }; let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![ @@ -3596,13 +3398,13 @@ fn identical_output_descriptions() -> Result<()> { .unwrap(); batched_tx.sign_raw( - vec![albert_keypair()], + vec![keypair.clone()], AccountPublicKeysMap::from_iter( - vec![(albert_keypair().to_public())].into_iter(), + vec![(keypair.to_public())].into_iter(), ), None, ); - batched_tx.sign_wrapper(albert_keypair()); + batched_tx.sign_wrapper(keypair); let wrapper_hash = batched_tx.wrapper_hash(); let inner_cmts = batched_tx.commitments(); @@ -3700,7 +3502,7 @@ fn identical_output_descriptions() -> Result<()> { run( &node, Bin::Client, - vec![ + apply_use_device(vec![ "unshield", "--source", A_SPENDING_KEY, @@ -3715,7 +3517,7 @@ fn identical_output_descriptions() -> Result<()> { BERTHA_KEY, "--node", validator_one_rpc, - ], + ]), )?; node.assert_success(); diff --git a/crates/token/src/lib.rs b/crates/token/src/lib.rs index dff43909bf..30d19bc3a5 100644 --- a/crates/token/src/lib.rs +++ b/crates/token/src/lib.rs @@ -284,6 +284,7 @@ pub mod testing { }; use masp_primitives::transaction::components::{TxOut, U64Sum}; use masp_primitives::transaction::fees::fixed::FeeRule; + use masp_primitives::zip32::PseudoExtendedKey; use namada_core::address::testing::{ arb_established_address, arb_non_internal_address, }; @@ -364,7 +365,7 @@ pub mod testing { assets in Just(assets), ) -> ( Transfer, - Builder::, + Builder::, HashMap, ) { // Enable assets to be more easily decoded diff --git a/crates/tx/src/types.rs b/crates/tx/src/types.rs index 156f62c211..dc392572f4 100644 --- a/crates/tx/src/types.rs +++ b/crates/tx/src/types.rs @@ -1292,6 +1292,30 @@ impl Tx { None } + /// Remove the transaction section with the given hash + pub fn remove_masp_section(&mut self, hash: &MaspTxId) { + self.sections.retain(|section| { + if let Section::MaspTx(masp) = section { + if MaspTxId::from(masp.txid()) == *hash { + return false; + } + } + true + }); + } + + /// Get the MASP builder section with the given hash + pub fn get_masp_builder(&self, hash: &MaspTxId) -> Option<&MaspBuilder> { + for section in &self.sections { + if let Section::MaspBuilder(builder) = section { + if builder.target == *hash { + return Some(builder); + } + } + } + None + } + /// Set the last transaction memo hash stored in the header pub fn set_memo_sechash(&mut self, hash: namada_core::hash::Hash) { let item = match self.header.batch.pop() { diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs index 2341979f9a..301b14b985 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/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/wasm/Cargo.lock b/wasm/Cargo.lock index 70b07f5340..133a547f19 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3449,7 +3449,7 @@ dependencies = [ [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=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 b854ddb998..ea503616cd 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -1863,7 +1863,7 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "masp_note_encryption" version = "1.0.0" -source = "git+https://github.com/anoma/masp?rev=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",