Skip to content

Commit e08c8b7

Browse files
committed
Expand MASP hardware wallet support to other transaction types.
1 parent 2aa2602 commit e08c8b7

File tree

8 files changed

+133
-61
lines changed

8 files changed

+133
-61
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ libc = "0.2.97"
141141
libloading = "0.7.2"
142142
linkme = "0.3.24"
143143
# branch = "tomas/arbitrary"
144-
masp_primitives = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56" }
145-
masp_proofs = { git = "https://github.com/anoma/masp", rev = "a35f73be69b21ee62cd4940f37855161cbed2a56", default-features = false, features = ["local-prover"] }
144+
masp_primitives = { git = "https://github.com/anoma/masp", rev = "2914e6ff9a922bae8f1cb63a79d796a69af3d8aa" }
145+
masp_proofs = { git = "https://github.com/anoma/masp", rev = "2914e6ff9a922bae8f1cb63a79d796a69af3d8aa", default-features = false, features = ["local-prover"] }
146146
num256 = "0.3.5"
147147
num_cpus = "1.13.0"
148148
num-derive = "0.4"

crates/apps_lib/src/client/tx.rs

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use masp_primitives::transaction::components::sapling::builder::{
1212
SpendBuildParams, StoredBuildParams,
1313
};
1414
use masp_primitives::transaction::components::sapling::fees::InputView;
15-
use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedKey};
15+
use masp_primitives::zip32::{
16+
ExtendedFullViewingKey, ExtendedKey, PseudoExtendedKey,
17+
};
1618
use namada_sdk::address::{Address, ImplicitAddress};
1719
use namada_sdk::args::TxBecomeValidator;
1820
use namada_sdk::collections::{HashMap, HashSet};
@@ -43,6 +45,22 @@ use crate::wallet::{
4345
gen_validator_keys, read_and_confirm_encryption_password, WalletTransport,
4446
};
4547

48+
// Maximum number of spend description randomness parameters that can be
49+
// generated on the hardware wallet. It is hard to compute the exact required
50+
// number because a given MASP source could be distributed amongst several
51+
// notes.
52+
const MAX_HW_SPEND: usize = 10;
53+
// Maximum number of convert description randomness parameters that can be
54+
// generated on the hardware wallet. It is hard to compute the exact required
55+
// number because the number of conversions that are used depends on the
56+
// protocol's current state.
57+
const MAX_HW_CONVERT: usize = 10;
58+
// Maximum number of output description randomness parameters that can be
59+
// generated on the hardware wallet. It is hard to compute the exact required
60+
// number because the number of outputs depends on the number of dummy outputs
61+
// introduced.
62+
const MAX_HW_OUTPUT: usize = 10;
63+
4664
/// Wrapper around `signing::aux_signing_data` that stores the optional
4765
/// disposable address to the wallet
4866
pub async fn aux_signing_data(
@@ -862,24 +880,25 @@ impl sapling::MapAuth<sapling::Authorized, sapling::Authorized>
862880
// it does on the software client.
863881
async fn augment_masp_hardware_keys(
864882
namada: &impl Namada,
865-
args: &mut args::TxShieldedTransfer,
883+
args: &args::Tx,
884+
sources: impl Iterator<Item = &mut PseudoExtendedKey>,
866885
) -> Result<HashMap<String, ExtendedViewingKey>, error::Error> {
867886
// Records the shielded keys that are on the hardware wallet
868887
let mut shielded_hw_keys = HashMap::new();
869888
// Construct the build parameters that parameterized the Transaction
870889
// authorizations
871-
if args.tx.use_device {
872-
let transport = WalletTransport::from_arg(args.tx.device_transport);
890+
if args.use_device {
891+
let transport = WalletTransport::from_arg(args.device_transport);
873892
let app = NamadaApp::new(transport);
874893
let wallet = namada.wallet().await;
875894
// Augment the pseudo spending key with a proof authorization key
876-
for data in &mut args.data {
895+
for source in sources {
877896
// Only attempt an augmentation if proof authorization is not there
878-
if data.source.to_spending_key().is_none() {
897+
if source.to_spending_key().is_none() {
879898
// First find the derivation path corresponding to this viewing
880899
// key
881900
let viewing_key =
882-
ExtendedViewingKey::from(data.source.to_viewing_key());
901+
ExtendedViewingKey::from(source.to_viewing_key());
883902
let path = wallet
884903
.find_path_by_viewing_key(&viewing_key)
885904
.map_err(|err| {
@@ -950,21 +969,18 @@ async fn augment_masp_hardware_keys(
950969
))
951970
})?;
952971
// Augment the pseudo spending key
953-
data.source.augment_proof_generation_key(pgk).map_err(
954-
|_| {
955-
error::Error::Other(
956-
"Proof generation key in response from the \
957-
hardware wallet does not correspond to stored \
958-
viewing key."
959-
.to_string(),
960-
)
961-
},
962-
)?;
972+
source.augment_proof_generation_key(pgk).map_err(|_| {
973+
error::Error::Other(
974+
"Proof generation key in response from the hardware \
975+
wallet does not correspond to stored viewing key."
976+
.to_string(),
977+
)
978+
})?;
963979
// Finally, augment an incorrect spend authorization key just to
964980
// make sure that the Transaction is built.
965-
data.source.augment_spend_authorizing_key_unchecked(
966-
PrivateKey(jubjub::Fr::default()),
967-
);
981+
source.augment_spend_authorizing_key_unchecked(PrivateKey(
982+
jubjub::Fr::default(),
983+
));
968984
shielded_hw_keys.insert(path.path, viewing_key);
969985
}
970986
}
@@ -977,12 +993,15 @@ async fn augment_masp_hardware_keys(
977993
// If the hardware wallet is beig used, use it to generate the random build
978994
// parameters for the spend, convert, and output descriptions.
979995
async fn generate_masp_build_params(
980-
args: &args::TxShieldedTransfer,
996+
spend_len: usize,
997+
convert_len: usize,
998+
output_len: usize,
999+
args: &args::Tx,
9811000
) -> Result<Box<dyn BuildParams>, error::Error> {
9821001
// Construct the build parameters that parameterized the Transaction
9831002
// authorizations
984-
if args.tx.use_device {
985-
let transport = WalletTransport::from_arg(args.tx.device_transport);
1003+
if args.use_device {
1004+
let transport = WalletTransport::from_arg(args.device_transport);
9861005
let app = NamadaApp::new(transport);
9871006
// Clear hardware wallet randomness buffers
9881007
app.clean_randomness_buffers().await.map_err(|err| {
@@ -993,16 +1012,6 @@ async fn generate_masp_build_params(
9931012
})?;
9941013
// Get randomness to aid in construction of various descriptors
9951014
let mut bparams = StoredBuildParams::default();
996-
// Number of spend descriptions is the number of transfers
997-
let spend_len = args.data.len();
998-
// Number of convert description is assumed to be double the number of
999-
// transfers. This is because each spend description might first be
1000-
// converted to epoch 0 before going to the intended epoch.
1001-
let convert_len = args.data.len() * 2;
1002-
// Number of output descriptions is assumed to be double the number of
1003-
// transfers. This is because there may be change from each output
1004-
// that's destined for the sender.
1005-
let output_len = args.data.len() * 2;
10061015
for _ in 0..spend_len {
10071016
let spend_randomness = app
10081017
.get_spend_randomness()
@@ -1011,7 +1020,6 @@ async fn generate_masp_build_params(
10111020
bparams.spend_params.push(SpendBuildParams {
10121021
rcv: jubjub::Fr::from_bytes(&spend_randomness.rcv).unwrap(),
10131022
alpha: jubjub::Fr::from_bytes(&spend_randomness.alpha).unwrap(),
1014-
..SpendBuildParams::default()
10151023
});
10161024
}
10171025
for _ in 0..convert_len {
@@ -1143,9 +1151,20 @@ pub async fn submit_shielded_transfer(
11431151
namada: &impl Namada,
11441152
mut args: args::TxShieldedTransfer,
11451153
) -> Result<(), error::Error> {
1154+
let sources = args
1155+
.data
1156+
.iter_mut()
1157+
.map(|x| &mut x.source)
1158+
.chain(args.gas_spending_keys.iter_mut());
11461159
let shielded_hw_keys =
1147-
augment_masp_hardware_keys(namada, &mut args).await?;
1148-
let mut bparams = generate_masp_build_params(&args).await?;
1160+
augment_masp_hardware_keys(namada, &args.tx, sources).await?;
1161+
let mut bparams = generate_masp_build_params(
1162+
MAX_HW_SPEND,
1163+
MAX_HW_CONVERT,
1164+
MAX_HW_OUTPUT,
1165+
&args.tx,
1166+
)
1167+
.await?;
11491168
let (mut tx, signing_data) =
11501169
args.clone().build(namada, &mut bparams).await?;
11511170

@@ -1165,7 +1184,15 @@ pub async fn submit_shielding_transfer(
11651184
) -> Result<(), error::Error> {
11661185
// Repeat once if the tx fails on a crossover of an epoch
11671186
for _ in 0..2 {
1168-
let (tx, signing_data, tx_epoch) = args.clone().build(namada).await?;
1187+
let mut bparams = generate_masp_build_params(
1188+
MAX_HW_SPEND,
1189+
MAX_HW_CONVERT,
1190+
MAX_HW_OUTPUT,
1191+
&args.tx,
1192+
)
1193+
.await?;
1194+
let (tx, signing_data, tx_epoch) =
1195+
args.clone().build(namada, &mut bparams).await?;
11691196

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

12181245
pub async fn submit_unshielding_transfer(
12191246
namada: &impl Namada,
1220-
args: args::TxUnshieldingTransfer,
1247+
mut args: args::TxUnshieldingTransfer,
12211248
) -> Result<(), error::Error> {
1222-
let (mut tx, signing_data) = args.clone().build(namada).await?;
1249+
let sources = std::iter::once(&mut args.source)
1250+
.chain(args.gas_spending_keys.iter_mut());
1251+
let shielded_hw_keys =
1252+
augment_masp_hardware_keys(namada, &args.tx, sources).await?;
1253+
let mut bparams = generate_masp_build_params(
1254+
MAX_HW_SPEND,
1255+
MAX_HW_CONVERT,
1256+
MAX_HW_OUTPUT,
1257+
&args.tx,
1258+
)
1259+
.await?;
1260+
let (mut tx, signing_data) =
1261+
args.clone().build(namada, &mut bparams).await?;
12231262

12241263
if args.tx.dump_tx {
12251264
tx::dump_tx(namada.io(), &args.tx, tx);
12261265
} else {
1266+
masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?;
12271267
sign(namada, &mut tx, &args.tx, signing_data).await?;
12281268
namada.submit(tx, &args.tx).await?;
12291269
}
@@ -1232,16 +1272,31 @@ pub async fn submit_unshielding_transfer(
12321272

12331273
pub async fn submit_ibc_transfer<N: Namada>(
12341274
namada: &N,
1235-
args: args::TxIbcTransfer,
1275+
mut args: args::TxIbcTransfer,
12361276
) -> Result<(), error::Error>
12371277
where
12381278
<N::Client as namada_sdk::io::Client>::Error: std::fmt::Display,
12391279
{
1240-
let (tx, signing_data, _) = args.build(namada).await?;
1280+
let sources = args
1281+
.source
1282+
.spending_key_mut()
1283+
.into_iter()
1284+
.chain(args.gas_spending_keys.iter_mut());
1285+
let shielded_hw_keys =
1286+
augment_masp_hardware_keys(namada, &args.tx, sources).await?;
1287+
let mut bparams = generate_masp_build_params(
1288+
MAX_HW_SPEND,
1289+
MAX_HW_CONVERT,
1290+
MAX_HW_OUTPUT,
1291+
&args.tx,
1292+
)
1293+
.await?;
1294+
let (mut tx, signing_data, _) = args.build(namada, &mut bparams).await?;
12411295

12421296
if args.tx.dump_tx {
12431297
tx::dump_tx(namada.io(), &args.tx, tx);
12441298
} else {
1299+
masp_sign(&mut tx, &args.tx, &signing_data, shielded_hw_keys).await?;
12451300
batch_opt_reveal_pk_and_submit(
12461301
namada,
12471302
&args.tx,

crates/core/src/masp.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,14 @@ impl TransferSource {
547547
}
548548
}
549549

550+
/// Get the contained ExtendedSpendingKey contained, if any
551+
pub fn spending_key_mut(&mut self) -> Option<&mut PseudoExtendedKey> {
552+
match self {
553+
Self::ExtendedSpendingKey(x) => Some(x),
554+
_ => None,
555+
}
556+
}
557+
550558
/// Get the contained Address, if any
551559
pub fn address(&self) -> Option<Address> {
552560
match self {

crates/sdk/src/args.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,9 @@ impl TxShieldingTransfer {
430430
pub async fn build(
431431
&mut self,
432432
context: &impl Namada,
433+
bparams: &mut impl BuildParams,
433434
) -> crate::error::Result<(namada_tx::Tx, SigningTxData, MaspEpoch)> {
434-
tx::build_shielding_transfer(context, self).await
435+
tx::build_shielding_transfer(context, self, bparams).await
435436
}
436437
}
437438

@@ -469,8 +470,9 @@ impl TxUnshieldingTransfer {
469470
pub async fn build(
470471
&mut self,
471472
context: &impl Namada,
473+
bparams: &mut impl BuildParams,
472474
) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> {
473-
tx::build_unshielding_transfer(context, self).await
475+
tx::build_unshielding_transfer(context, self, bparams).await
474476
}
475477
}
476478

@@ -618,9 +620,10 @@ impl TxIbcTransfer {
618620
pub async fn build(
619621
&self,
620622
context: &impl Namada,
623+
bparams: &mut impl BuildParams,
621624
) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option<MaspEpoch>)>
622625
{
623-
tx::build_ibc_transfer(context, self).await
626+
tx::build_ibc_transfer(context, self, bparams).await
624627
}
625628
}
626629

0 commit comments

Comments
 (0)