Skip to content

Commit 3fe7ee9

Browse files
committed
Implemented MASP signing using the hardware wallet.
1 parent 8d11769 commit 3fe7ee9

File tree

24 files changed

+578
-90
lines changed

24 files changed

+578
-90
lines changed

Cargo.lock

+5-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@ konst = { version = "0.3.8", default-features = false }
134134
lazy_static = "1.4.0"
135135
# TODO: upstreamed in https://github.com/ledger-community/rust-ledger/pull/9
136136
ledger-lib = { git = "https://github.com/heliaxdev/rust-ledger", rev = "f96f4559b3237d09218f7583df01acf36034ea79", default-features = false, features = ["transport_tcp"] }
137-
ledger-namada-rs = { git = "https://github.com/Zondax/ledger-namada", tag = "v0.0.24" }
137+
ledger-namada-rs = { git = "https://github.com/Zondax/ledger-namada", rev = "f54b76adcc1430db0496e894ad72cd74cfb6eb88" }
138138
ledger-transport = "0.10.0"
139139
ledger-transport-hid = "0.10.0"
140140
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 = "12ed8b060b295c06502a2ff8468e4a941cb7cca4" }
145-
masp_proofs = { git = "https://github.com/anoma/masp", rev = "12ed8b060b295c06502a2ff8468e4a941cb7cca4", default-features = false, features = ["local-prover"] }
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"] }
146146
num256 = "0.3.5"
147147
num_cpus = "1.13.0"
148148
num-derive = "0.4"

crates/apps_lib/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fd-lock.workspace = true
5353
flate2.workspace = true
5454
futures.workspace = true
5555
itertools.workspace = true
56+
jubjub.workspace = true
5657
kdam.workspace = true
5758
lazy_static = { workspace = true, optional = true }
5859
linkme = { workspace = true, optional = true }

crates/apps_lib/src/cli/context.rs

+60-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use std::path::{Path, PathBuf};
66
use std::str::FromStr;
77

88
use color_eyre::eyre::Result;
9+
use masp_primitives::zip32::sapling::PseudoExtendedKey;
10+
use masp_primitives::zip32::{
11+
ExtendedFullViewingKey as MaspExtendedViewingKey,
12+
ExtendedSpendingKey as MaspExtendedSpendingKey,
13+
};
914
use namada_core::masp::{
1015
BalanceOwner, ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress,
1116
TransferSource, TransferTarget,
@@ -47,7 +52,7 @@ pub type WalletAddrOrNativeToken = FromContext<AddrOrNativeToken>;
4752

4853
/// A raw extended spending key (bech32m encoding) or an alias of an extended
4954
/// spending key in the wallet
50-
pub type WalletSpendingKey = FromContext<ExtendedSpendingKey>;
55+
pub type WalletSpendingKey = FromContext<PseudoExtendedKey>;
5156

5257
/// A raw dated extended spending key (bech32m encoding) or an alias of an
5358
/// extended spending key in the wallet
@@ -588,6 +593,48 @@ impl ArgFromMutContext for ExtendedSpendingKey {
588593
}
589594
}
590595

596+
impl ArgFromMutContext for PseudoExtendedKey {
597+
fn arg_from_mut_ctx(
598+
ctx: &mut ChainContext,
599+
raw: impl AsRef<str>,
600+
) -> Result<Self, String> {
601+
let raw = raw.as_ref();
602+
// Either the string is a raw extended spending key
603+
ExtendedSpendingKey::from_str(raw)
604+
.map(|x| PseudoExtendedKey::from(MaspExtendedSpendingKey::from(x)))
605+
.or_else(|_parse_err| {
606+
ExtendedViewingKey::from_str(raw).map(|x| {
607+
PseudoExtendedKey::from(MaspExtendedViewingKey::from(x))
608+
})
609+
})
610+
.or_else(|_parse_err| {
611+
// Or it is a stored alias of one
612+
ctx.wallet
613+
.find_spending_key(raw, None)
614+
.map(|k| {
615+
PseudoExtendedKey::from(MaspExtendedSpendingKey::from(
616+
k.key,
617+
))
618+
})
619+
.map_err(|_find_err| {
620+
format!("Unknown spending key {}", raw)
621+
})
622+
})
623+
.or_else(|_parse_err| {
624+
// Or it is a stored alias of one
625+
ctx.wallet
626+
.find_viewing_key(raw)
627+
.copied()
628+
.map(|k| {
629+
PseudoExtendedKey::from(MaspExtendedViewingKey::from(
630+
k.key,
631+
))
632+
})
633+
.map_err(|_find_err| format!("Unknown viewing key {}", raw))
634+
})
635+
}
636+
}
637+
591638
impl ArgFromMutContext for DatedSpendingKey {
592639
fn arg_from_mut_ctx(
593640
ctx: &mut ChainContext,
@@ -666,8 +713,18 @@ impl ArgFromMutContext for TransferSource {
666713
Address::arg_from_ctx(ctx, raw)
667714
.map(Self::Address)
668715
.or_else(|_| {
669-
ExtendedSpendingKey::arg_from_mut_ctx(ctx, raw)
670-
.map(Self::ExtendedSpendingKey)
716+
ExtendedSpendingKey::arg_from_mut_ctx(ctx, raw).map(|x| {
717+
Self::ExtendedSpendingKey(PseudoExtendedKey::from(
718+
MaspExtendedSpendingKey::from(x),
719+
))
720+
})
721+
})
722+
.or_else(|_| {
723+
ExtendedViewingKey::arg_from_mut_ctx(ctx, raw).map(|x| {
724+
Self::ExtendedSpendingKey(PseudoExtendedKey::from(
725+
MaspExtendedViewingKey::from(x),
726+
))
727+
})
671728
})
672729
}
673730
}

crates/apps_lib/src/cli/wallet.rs

+63-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use borsh::BorshDeserialize;
88
use borsh_ext::BorshSerializeExt;
99
use color_eyre::eyre::Result;
1010
use itertools::sorted;
11-
use ledger_namada_rs::{BIP44Path, NamadaApp};
11+
use ledger_namada_rs::{BIP44Path, KeyResponse, NamadaApp, NamadaKeys};
12+
use ledger_transport_hid::hidapi::HidApi;
13+
use ledger_transport_hid::TransportNativeHID;
14+
use masp_primitives::zip32::ExtendedFullViewingKey;
1215
use namada_core::chain::BlockHeight;
1316
use namada_core::masp::{ExtendedSpendingKey, MaspValue, PaymentAddress};
1417
use namada_sdk::address::{Address, DecodeError};
@@ -184,7 +187,7 @@ fn payment_addresses_list(
184187
}
185188

186189
/// Derives a masp spending key from the mnemonic code in the wallet.
187-
fn shielded_key_derive(
190+
async fn shielded_key_derive(
188191
ctx: Context,
189192
io: &impl Io,
190193
args::KeyDerive {
@@ -232,9 +235,56 @@ fn shielded_key_derive(
232235
})
233236
.0
234237
} else {
235-
display_line!(io, "Not implemented.");
236-
display_line!(io, "No changes are persisted. Exiting.");
237-
cli::safe_exit(1)
238+
let hidapi = HidApi::new().unwrap_or_else(|err| {
239+
edisplay_line!(io, "Failed to create HidApi: {}", err);
240+
cli::safe_exit(1)
241+
});
242+
let app = NamadaApp::new(
243+
TransportNativeHID::new(&hidapi).unwrap_or_else(|err| {
244+
edisplay_line!(io, "Unable to connect to Ledger: {}", err);
245+
cli::safe_exit(1)
246+
}),
247+
);
248+
let response = app
249+
.retrieve_keys(
250+
&BIP44Path {
251+
path: derivation_path.to_string(),
252+
},
253+
NamadaKeys::ViewKey,
254+
true,
255+
)
256+
.await
257+
.unwrap_or_else(|err| {
258+
edisplay_line!(
259+
io,
260+
"Unable to connect to query address and public key from \
261+
Ledger: {}",
262+
err
263+
);
264+
cli::safe_exit(1)
265+
});
266+
let KeyResponse::ViewKey(response_key) = response else {
267+
edisplay_line!(io, "Unexpected response from Ledger");
268+
cli::safe_exit(1)
269+
};
270+
let xfvk = ExtendedFullViewingKey::try_from_slice(&response_key.xfvk)
271+
.expect(
272+
"unable to decode extended full viewing key from the hardware \
273+
wallet",
274+
);
275+
276+
wallet
277+
.insert_viewing_key(
278+
alias,
279+
xfvk.into(),
280+
birthday,
281+
alias_force,
282+
Some(derivation_path),
283+
)
284+
.unwrap_or_else(|| {
285+
display_line!(io, "No changes are persisted. Exiting.");
286+
cli::safe_exit(1)
287+
})
238288
};
239289
wallet
240290
.save()
@@ -367,7 +417,13 @@ fn shielded_key_address_add(
367417
let (alias, typ) = match masp_value {
368418
MaspValue::FullViewingKey(viewing_key) => {
369419
let alias = wallet
370-
.insert_viewing_key(alias, viewing_key, birthday, alias_force)
420+
.insert_viewing_key(
421+
alias,
422+
viewing_key,
423+
birthday,
424+
alias_force,
425+
None,
426+
)
371427
.unwrap_or_else(|| {
372428
edisplay_line!(io, "Viewing key not added");
373429
cli::safe_exit(1);
@@ -638,7 +694,7 @@ async fn key_derive(
638694
if !args_key_derive.shielded {
639695
transparent_key_and_address_derive(ctx, io, args_key_derive).await
640696
} else {
641-
shielded_key_derive(ctx, io, args_key_derive)
697+
shielded_key_derive(ctx, io, args_key_derive).await
642698
}
643699
}
644700

0 commit comments

Comments
 (0)