Skip to content

Commit

Permalink
Expand MASP hardware wallet support to other transaction types.
Browse files Browse the repository at this point in the history
  • Loading branch information
murisi committed Sep 24, 2024
1 parent 2aa2602 commit e08c8b7
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 61 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ libc = "0.2.97"
libloading = "0.7.2"
linkme = "0.3.24"
# branch = "tomas/arbitrary"
masp_primitives = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56" }
masp_proofs = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56", default-features = false, features = ["local-prover"] }
masp_primitives = { git = "https://github.com/anoma/masp", rev = "2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" }
masp_proofs = { git = "https://github.com/anoma/masp", rev = "2914e6ff9a922bae8f1cb63a79d796a69af3d8aa", default-features = false, features = ["local-prover"] }
num256 = "0.3.5"
num_cpus = "1.13.0"
num-derive = "0.4"
Expand Down
137 changes: 96 additions & 41 deletions crates/apps_lib/src/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use masp_primitives::transaction::components::sapling::builder::{
SpendBuildParams, StoredBuildParams,
};
use masp_primitives::transaction::components::sapling::fees::InputView;
use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedKey};
use masp_primitives::zip32::{
ExtendedFullViewingKey, ExtendedKey, PseudoExtendedKey,
};
use namada_sdk::address::{Address, ImplicitAddress};
use namada_sdk::args::TxBecomeValidator;
use namada_sdk::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -43,6 +45,22 @@ use crate::wallet::{
gen_validator_keys, read_and_confirm_encryption_password, WalletTransport,
};

// Maximum number of spend description randomness parameters that can be
// generated on the hardware wallet. It is hard to compute the exact required
// number because a given MASP source could be distributed amongst several
// notes.
const MAX_HW_SPEND: usize = 10;
// Maximum number of convert description randomness parameters that can be
// generated on the hardware wallet. It is hard to compute the exact required
// number because the number of conversions that are used depends on the
// protocol's current state.
const MAX_HW_CONVERT: usize = 10;
// Maximum number of output description randomness parameters that can be
// generated on the hardware wallet. It is hard to compute the exact required
// number because the number of outputs depends on the number of dummy outputs
// introduced.
const MAX_HW_OUTPUT: usize = 10;

/// Wrapper around `signing::aux_signing_data` that stores the optional
/// disposable address to the wallet
pub async fn aux_signing_data(
Expand Down Expand Up @@ -862,24 +880,25 @@ impl sapling::MapAuth<sapling::Authorized, sapling::Authorized>
// it does on the software client.
async fn augment_masp_hardware_keys(
namada: &impl Namada,
args: &mut args::TxShieldedTransfer,
args: &args::Tx,
sources: impl Iterator<Item = &mut PseudoExtendedKey>,
) -> Result<HashMap<String, ExtendedViewingKey>, error::Error> {
// Records the shielded keys that are on the hardware wallet
let mut shielded_hw_keys = HashMap::new();
// Construct the build parameters that parameterized the Transaction
// authorizations
if args.tx.use_device {
let transport = WalletTransport::from_arg(args.tx.device_transport);
if args.use_device {
let transport = WalletTransport::from_arg(args.device_transport);
let app = NamadaApp::new(transport);
let wallet = namada.wallet().await;
// Augment the pseudo spending key with a proof authorization key
for data in &mut args.data {
for source in sources {
// Only attempt an augmentation if proof authorization is not there
if data.source.to_spending_key().is_none() {
if source.to_spending_key().is_none() {
// First find the derivation path corresponding to this viewing
// key
let viewing_key =
ExtendedViewingKey::from(data.source.to_viewing_key());
ExtendedViewingKey::from(source.to_viewing_key());
let path = wallet
.find_path_by_viewing_key(&viewing_key)
.map_err(|err| {
Expand Down Expand Up @@ -950,21 +969,18 @@ async fn augment_masp_hardware_keys(
))
})?;
// Augment the pseudo spending key
data.source.augment_proof_generation_key(pgk).map_err(
|_| {
error::Error::Other(
"Proof generation key in response from the \
hardware wallet does not correspond to stored \
viewing key."
.to_string(),
)
},
)?;
source.augment_proof_generation_key(pgk).map_err(|_| {
error::Error::Other(
"Proof generation key in response from the hardware \
wallet does not correspond to stored viewing key."
.to_string(),
)
})?;
// Finally, augment an incorrect spend authorization key just to
// make sure that the Transaction is built.
data.source.augment_spend_authorizing_key_unchecked(
PrivateKey(jubjub::Fr::default()),
);
source.augment_spend_authorizing_key_unchecked(PrivateKey(
jubjub::Fr::default(),
));
shielded_hw_keys.insert(path.path, viewing_key);
}
}
Expand All @@ -977,12 +993,15 @@ async fn augment_masp_hardware_keys(
// If the hardware wallet is beig used, use it to generate the random build
// parameters for the spend, convert, and output descriptions.
async fn generate_masp_build_params(
args: &args::TxShieldedTransfer,
spend_len: usize,
convert_len: usize,
output_len: usize,
args: &args::Tx,
) -> Result<Box<dyn BuildParams>, error::Error> {
// Construct the build parameters that parameterized the Transaction
// authorizations
if args.tx.use_device {
let transport = WalletTransport::from_arg(args.tx.device_transport);
if args.use_device {
let transport = WalletTransport::from_arg(args.device_transport);
let app = NamadaApp::new(transport);
// Clear hardware wallet randomness buffers
app.clean_randomness_buffers().await.map_err(|err| {
Expand All @@ -993,16 +1012,6 @@ async fn generate_masp_build_params(
})?;
// Get randomness to aid in construction of various descriptors
let mut bparams = StoredBuildParams::default();
// Number of spend descriptions is the number of transfers
let spend_len = args.data.len();
// Number of convert description is assumed to be double the number of
// transfers. This is because each spend description might first be
// converted to epoch 0 before going to the intended epoch.
let convert_len = args.data.len() * 2;
// Number of output descriptions is assumed to be double the number of
// transfers. This is because there may be change from each output
// that's destined for the sender.
let output_len = args.data.len() * 2;
for _ in 0..spend_len {
let spend_randomness = app
.get_spend_randomness()
Expand All @@ -1011,7 +1020,6 @@ async fn generate_masp_build_params(
bparams.spend_params.push(SpendBuildParams {
rcv: jubjub::Fr::from_bytes(&spend_randomness.rcv).unwrap(),
alpha: jubjub::Fr::from_bytes(&spend_randomness.alpha).unwrap(),
..SpendBuildParams::default()
});
}
for _ in 0..convert_len {
Expand Down Expand Up @@ -1143,9 +1151,20 @@ pub async fn submit_shielded_transfer(
namada: &impl Namada,
mut args: args::TxShieldedTransfer,
) -> Result<(), error::Error> {
let sources = args
.data
.iter_mut()
.map(|x| &mut x.source)
.chain(args.gas_spending_keys.iter_mut());
let shielded_hw_keys =
augment_masp_hardware_keys(namada, &mut args).await?;
let mut bparams = generate_masp_build_params(&args).await?;
augment_masp_hardware_keys(namada, &args.tx, sources).await?;
let mut bparams = generate_masp_build_params(
MAX_HW_SPEND,
MAX_HW_CONVERT,
MAX_HW_OUTPUT,
&args.tx,
)
.await?;
let (mut tx, signing_data) =
args.clone().build(namada, &mut bparams).await?;

Expand All @@ -1165,7 +1184,15 @@ pub async fn submit_shielding_transfer(
) -> Result<(), error::Error> {
// Repeat once if the tx fails on a crossover of an epoch
for _ in 0..2 {
let (tx, signing_data, tx_epoch) = args.clone().build(namada).await?;
let mut bparams = generate_masp_build_params(
MAX_HW_SPEND,
MAX_HW_CONVERT,
MAX_HW_OUTPUT,
&args.tx,
)
.await?;
let (tx, signing_data, tx_epoch) =
args.clone().build(namada, &mut bparams).await?;

if args.tx.dump_tx {
tx::dump_tx(namada.io(), &args.tx, tx);
Expand Down Expand Up @@ -1217,13 +1244,26 @@ pub async fn submit_shielding_transfer(

pub async fn submit_unshielding_transfer(
namada: &impl Namada,
args: args::TxUnshieldingTransfer,
mut args: args::TxUnshieldingTransfer,
) -> Result<(), error::Error> {
let (mut tx, signing_data) = args.clone().build(namada).await?;
let sources = std::iter::once(&mut args.source)
.chain(args.gas_spending_keys.iter_mut());
let shielded_hw_keys =
augment_masp_hardware_keys(namada, &args.tx, sources).await?;
let mut bparams = generate_masp_build_params(
MAX_HW_SPEND,
MAX_HW_CONVERT,
MAX_HW_OUTPUT,
&args.tx,
)
.await?;
let (mut tx, signing_data) =
args.clone().build(namada, &mut bparams).await?;

if args.tx.dump_tx {
tx::dump_tx(namada.io(), &args.tx, tx);
} else {
masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?;
sign(namada, &mut tx, &args.tx, signing_data).await?;
namada.submit(tx, &args.tx).await?;
}
Expand All @@ -1232,16 +1272,31 @@ pub async fn submit_unshielding_transfer(

pub async fn submit_ibc_transfer<N: Namada>(
namada: &N,
args: args::TxIbcTransfer,
mut args: args::TxIbcTransfer,
) -> Result<(), error::Error>
where
<N::Client as namada_sdk::io::Client>::Error: std::fmt::Display,
{
let (tx, signing_data, _) = args.build(namada).await?;
let sources = args
.source
.spending_key_mut()
.into_iter()
.chain(args.gas_spending_keys.iter_mut());
let shielded_hw_keys =
augment_masp_hardware_keys(namada, &args.tx, sources).await?;
let mut bparams = generate_masp_build_params(
MAX_HW_SPEND,
MAX_HW_CONVERT,
MAX_HW_OUTPUT,
&args.tx,
)
.await?;
let (mut tx, signing_data, _) = args.build(namada, &mut bparams).await?;

if args.tx.dump_tx {
tx::dump_tx(namada.io(), &args.tx, tx);
} else {
masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?;
batch_opt_reveal_pk_and_submit(
namada,
&args.tx,
Expand Down
8 changes: 8 additions & 0 deletions crates/core/src/masp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,14 @@ impl TransferSource {
}
}

/// Get the contained ExtendedSpendingKey contained, if any
pub fn spending_key_mut(&mut self) -> Option<&mut PseudoExtendedKey> {
match self {
Self::ExtendedSpendingKey(x) => Some(x),
_ => None,
}
}

/// Get the contained Address, if any
pub fn address(&self) -> Option<Address> {
match self {
Expand Down
9 changes: 6 additions & 3 deletions crates/sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,9 @@ impl TxShieldingTransfer {
pub async fn build(
&mut self,
context: &impl Namada,
bparams: &mut impl BuildParams,
) -> crate::error::Result<(namada_tx::Tx, SigningTxData, MaspEpoch)> {
tx::build_shielding_transfer(context, self).await
tx::build_shielding_transfer(context, self, bparams).await
}
}

Expand Down Expand Up @@ -469,8 +470,9 @@ impl TxUnshieldingTransfer {
pub async fn build(
&mut self,
context: &impl Namada,
bparams: &mut impl BuildParams,
) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> {
tx::build_unshielding_transfer(context, self).await
tx::build_unshielding_transfer(context, self, bparams).await
}
}

Expand Down Expand Up @@ -618,9 +620,10 @@ impl TxIbcTransfer {
pub async fn build(
&self,
context: &impl Namada,
bparams: &mut impl BuildParams,
) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option<MaspEpoch>)>
{
tx::build_ibc_transfer(context, self).await
tx::build_ibc_transfer(context, self, bparams).await
}
}

Expand Down
Loading

0 comments on commit e08c8b7

Please sign in to comment.