From 66c04159eda2a787863164b3cdf76d0304b85101 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 17 Nov 2023 15:29:46 +0100 Subject: [PATCH 01/46] Unify cli for secret / spending key generation --- apps/src/lib/cli.rs | 90 ++++++++++++++++++-------------------- apps/src/lib/cli/wallet.rs | 40 +++++++++++------ sdk/src/args.rs | 6 ++- 3 files changed, 73 insertions(+), 63 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index c04f5a531b..da7ea8af9a 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -485,6 +485,8 @@ pub mod cmds { Address(WalletAddress), /// MASP key, address management commands Masp(WalletMasp), + /// TODO + NewGen(WalletNewGen), } impl Cmd for NamadaWallet { @@ -492,13 +494,15 @@ pub mod cmds { app.subcommand(WalletKey::def()) .subcommand(WalletAddress::def()) .subcommand(WalletMasp::def()) + .subcommand(WalletNewGen::def()) } fn parse(matches: &ArgMatches) -> Option { let key = SubCmd::parse(matches).map(Self::Key); let address = SubCmd::parse(matches).map(Self::Address); let masp = SubCmd::parse(matches).map(Self::Masp); - key.or(address).or(masp) + let gen_new = SubCmd::parse(matches).map(Self::NewGen); + key.or(address).or(masp).or(gen_new) } } @@ -525,7 +529,6 @@ pub mod cmds { #[allow(clippy::large_enum_variant)] pub enum WalletKey { Derive(KeyDerive), - Gen(KeyGen), Find(KeyFind), List(KeyList), Export(Export), @@ -536,12 +539,11 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { - let generate = SubCmd::parse(matches).map(Self::Gen); let restore = SubCmd::parse(matches).map(Self::Derive); let lookup = SubCmd::parse(matches).map(Self::Find); let list = SubCmd::parse(matches).map(Self::List); let export = SubCmd::parse(matches).map(Self::Export); - generate.or(restore).or(lookup).or(list).or(export) + restore.or(lookup).or(list).or(export) }) } @@ -554,7 +556,6 @@ pub mod cmds { .subcommand_required(true) .arg_required_else_help(true) .subcommand(KeyDerive::def()) - .subcommand(KeyGen::def()) .subcommand(KeyFind::def()) .subcommand(KeyList::def()) .subcommand(Export::def()) @@ -587,17 +588,19 @@ pub mod cmds { } } - /// Generate a new keypair and an implicit address derived from it + /// In the transparent setting, generate a new keypair and an implicit + /// address derived from it. In the shielded setting, generate a new + /// spending key. #[derive(Clone, Debug)] - pub struct KeyGen(pub args::KeyAndAddressGen); + pub struct WalletNewGen(pub args::KeyGen); - impl SubCmd for KeyGen { + impl SubCmd for WalletNewGen { const CMD: &'static str = "gen"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| Self(args::KeyAndAddressGen::parse(matches))) + .map(|matches| Self(args::KeyGen::parse(matches))) } fn def() -> App { @@ -605,9 +608,9 @@ pub mod cmds { .about( "Generates a keypair with a given alias and derives the \ implicit address from its public key. The address will \ - be stored with the same alias.", + be stored with the same alias. TODO shielded", ) - .add_args::() + .add_args::() } } @@ -672,7 +675,6 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum WalletMasp { GenPayAddr(MaspGenPayAddr), - GenSpendKey(MaspGenSpendKey), AddAddrKey(MaspAddAddrKey), ListPayAddrs, ListKeys(MaspListKeys), @@ -685,13 +687,12 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { let genpa = SubCmd::parse(matches).map(Self::GenPayAddr); - let gensk = SubCmd::parse(matches).map(Self::GenSpendKey); let addak = SubCmd::parse(matches).map(Self::AddAddrKey); let listpa = ::parse(matches) .map(|_| Self::ListPayAddrs); let listsk = SubCmd::parse(matches).map(Self::ListKeys); let findak = SubCmd::parse(matches).map(Self::FindAddrKey); - gensk.or(genpa).or(addak).or(listpa).or(listsk).or(findak) + genpa.or(addak).or(listpa).or(listsk).or(findak) }) } @@ -704,7 +705,6 @@ pub mod cmds { ) .subcommand_required(true) .arg_required_else_help(true) - .subcommand(MaspGenSpendKey::def()) .subcommand(MaspGenPayAddr::def()) .subcommand(MaspAddAddrKey::def()) .subcommand(MaspListPayAddrs::def()) @@ -834,7 +834,6 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum WalletAddress { - Gen(AddressGen), Derive(AddressDerive), Find(AddressOrAliasFind), List, @@ -846,13 +845,12 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { - let gen = SubCmd::parse(matches).map(Self::Gen); let restore = SubCmd::parse(matches).map(Self::Derive); let find = SubCmd::parse(matches).map(Self::Find); let list = ::parse(matches).map(|_| Self::List); let add = SubCmd::parse(matches).map(Self::Add); - gen.or(restore).or(find).or(list).or(add) + restore.or(find).or(list).or(add) }) } @@ -864,7 +862,6 @@ pub mod cmds { ) .subcommand_required(true) .arg_required_else_help(true) - .subcommand(AddressGen::def()) .subcommand(AddressDerive::def()) .subcommand(AddressOrAliasFind::def()) .subcommand(AddressList::def()) @@ -872,30 +869,6 @@ pub mod cmds { } } - /// Generate a new keypair and an implicit address derived from it - #[derive(Clone, Debug)] - pub struct AddressGen(pub args::KeyAndAddressGen); - - impl SubCmd for AddressGen { - const CMD: &'static str = "gen"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - AddressGen(args::KeyAndAddressGen::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Generates a keypair with a given alias and derives the \ - implicit address from its public key. The address will \ - be stored with the same alias.", - ) - .add_args::() - } - } - /// Restore a keypair and an implicit address from the mnemonic code #[derive(Clone, Debug)] pub struct AddressDerive(pub args::KeyAndAddressDerive); @@ -3137,6 +3110,7 @@ pub mod args { pub const RAW_ADDRESS: Arg
= arg("address"); pub const RAW_ADDRESS_ESTABLISHED: Arg = arg("address"); pub const RAW_ADDRESS_OPT: ArgOpt
= RAW_ADDRESS.opt(); + pub const RAW_KEY_GEN: ArgFlag = flag("raw"); pub const RAW_PUBLIC_KEY: Arg = arg("public-key"); pub const RAW_PUBLIC_KEY_OPT: ArgOpt = arg_opt("public-key"); @@ -3148,6 +3122,7 @@ pub mod args { pub const SELF_BOND_AMOUNT: Arg = arg("self-bond-amount"); pub const SENDER: Arg = arg("sender"); + pub const SHIELDED: ArgFlag = flag("shielded"); pub const SIGNER: ArgOpt = arg_opt("signer"); pub const SIGNING_KEYS: ArgMulti = arg_multi("signing-keys"); @@ -6324,15 +6299,19 @@ pub mod args { } } - impl Args for KeyAndAddressGen { + impl Args for KeyGen { fn parse(matches: &ArgMatches) -> Self { let scheme = SCHEME.parse(matches); + let shielded = SHIELDED.parse(matches); + let raw = RAW_KEY_GEN.parse(matches); let alias = ALIAS_OPT.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let derivation_path = HD_WALLET_DERIVATION_PATH.parse(matches); Self { scheme, + shielded, + raw, alias, alias_force, unsafe_dont_encrypt, @@ -6341,11 +6320,26 @@ pub mod args { } fn def(app: App) -> App { - app.arg(SCHEME.def().help( - "The type of key that should be generated. Argument must be \ - either ed25519 or secp256k1. If none provided, the default \ - key scheme is ed25519.", + app.arg(SCHEME.def().conflicts_with(SHIELDED.name).help( + "For the transparent pool, the type of key that should be \ + generated. Argument must be either ed25519 or secp256k1. If \ + none provided, the default key scheme is ed25519.\nNot \ + applicable for the shielded pool.", )) + .arg( + SHIELDED + .def() + .help("Generate a spending key for the shielded pool."), + ) + .arg( + RAW_KEY_GEN + .def() + .conflicts_with(HD_WALLET_DERIVATION_PATH.name) + .help( + "Generate a random non-HD secret / spending key. No \ + mnemonic code is generated.", + ), + ) .arg(ALIAS_OPT.def().help( "The key and address alias. If none provided, the alias will \ be the public key hash.", diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 088e204d57..fdc94fb7ce 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -43,9 +43,6 @@ impl CliApi { cmds::WalletKey::Derive(cmds::KeyDerive(args)) => { key_and_address_derive(ctx, io, args).await } - cmds::WalletKey::Gen(cmds::KeyGen(args)) => { - key_and_address_gen(ctx, io, args) - } cmds::WalletKey::Find(cmds::KeyFind(args)) => { key_find(ctx, io, args) } @@ -57,9 +54,6 @@ impl CliApi { } }, cmds::NamadaWallet::Address(sub) => match sub { - cmds::WalletAddress::Gen(cmds::AddressGen(args)) => { - key_and_address_gen(ctx, io, args) - } cmds::WalletAddress::Derive(cmds::AddressDerive(args)) => { key_and_address_derive(ctx, io, args).await } @@ -72,9 +66,6 @@ impl CliApi { } }, cmds::NamadaWallet::Masp(sub) => match sub { - cmds::WalletMasp::GenSpendKey(cmds::MaspGenSpendKey(args)) => { - spending_key_gen(ctx, io, args) - } cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { let args = args.to_sdk(&mut ctx); payment_address_gen(ctx, io, args) @@ -92,6 +83,9 @@ impl CliApi { address_key_find(ctx, io, args) } }, + cmds::NamadaWallet::NewGen(cmds::WalletNewGen(args)) => { + key_gen(ctx, io, args) + } } Ok(()) } @@ -236,18 +230,26 @@ fn payment_addresses_list(ctx: Context, io: &impl Io) { fn spending_key_gen( ctx: Context, io: &impl Io, - args::MaspSpendKeyGen { + args::KeyGen { alias, alias_force, unsafe_dont_encrypt, - }: args::MaspSpendKeyGen, + .. + }: args::KeyGen, ) { + let alias = alias.unwrap_or_else(|| { + display_line!(io, "Missing alias."); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }); let mut wallet = load_wallet(ctx); let alias = alias.to_lowercase(); let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let (alias, _key) = wallet.gen_store_spending_key(alias, password, alias_force, &mut OsRng); - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + wallet + .save() + .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, "Successfully added a spending key with alias: \"{}\"", @@ -463,18 +465,28 @@ async fn key_and_address_derive( ); } +/// TODO +fn key_gen(ctx: Context, io: &impl Io, args_key_gen: args::KeyGen) { + if !args_key_gen.shielded { + key_and_address_gen(ctx, io, args_key_gen) + } else { + spending_key_gen(ctx, io, args_key_gen) + } +} + /// Generate a new keypair and derive implicit address from it and store them in /// the wallet. fn key_and_address_gen( ctx: Context, io: &impl Io, - args::KeyAndAddressGen { + args::KeyGen { scheme, alias, alias_force, unsafe_dont_encrypt, derivation_path, - }: args::KeyAndAddressGen, + .. + }: args::KeyGen, ) { let mut wallet = load_wallet(ctx); let encryption_password = diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 6599b0e9fb..b8bdc9492f 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2079,9 +2079,13 @@ pub struct MaspPayAddrGen { /// Wallet generate key and implicit address arguments #[derive(Clone, Debug)] -pub struct KeyAndAddressGen { +pub struct KeyGen { /// Scheme type pub scheme: SchemeType, + /// Whether to generate a spending key for the shielded pool + pub shielded: bool, + /// Whether to generate a raw non-hd key + pub raw: bool, /// Key alias pub alias: Option, /// Whether to force overwrite the alias, if provided From a956a0cfe3b4f4208e922e5857f715029f1095c9 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 17 Nov 2023 17:49:02 +0100 Subject: [PATCH 02/46] Unify cli for secret / spending key derivation --- apps/src/lib/cli.rs | 97 ++++++++++++++++++++------------------ apps/src/lib/cli/wallet.rs | 27 +++++++---- sdk/src/args.rs | 19 ++++++++ 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index da7ea8af9a..f4eedb6ce8 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -487,6 +487,8 @@ pub mod cmds { Masp(WalletMasp), /// TODO NewGen(WalletNewGen), + /// TODO + NewDerive(WalletNewDerive), } impl Cmd for NamadaWallet { @@ -495,6 +497,7 @@ pub mod cmds { .subcommand(WalletAddress::def()) .subcommand(WalletMasp::def()) .subcommand(WalletNewGen::def()) + .subcommand(WalletNewDerive::def()) } fn parse(matches: &ArgMatches) -> Option { @@ -502,7 +505,8 @@ pub mod cmds { let address = SubCmd::parse(matches).map(Self::Address); let masp = SubCmd::parse(matches).map(Self::Masp); let gen_new = SubCmd::parse(matches).map(Self::NewGen); - key.or(address).or(masp).or(gen_new) + let derive_new = SubCmd::parse(matches).map(Self::NewDerive); + key.or(address).or(masp).or(gen_new).or(derive_new) } } @@ -528,7 +532,6 @@ pub mod cmds { #[derive(Clone, Debug)] #[allow(clippy::large_enum_variant)] pub enum WalletKey { - Derive(KeyDerive), Find(KeyFind), List(KeyList), Export(Export), @@ -539,11 +542,10 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { - let restore = SubCmd::parse(matches).map(Self::Derive); let lookup = SubCmd::parse(matches).map(Self::Find); let list = SubCmd::parse(matches).map(Self::List); let export = SubCmd::parse(matches).map(Self::Export); - restore.or(lookup).or(list).or(export) + lookup.or(list).or(export) }) } @@ -555,7 +557,6 @@ pub mod cmds { ) .subcommand_required(true) .arg_required_else_help(true) - .subcommand(KeyDerive::def()) .subcommand(KeyFind::def()) .subcommand(KeyList::def()) .subcommand(Export::def()) @@ -564,7 +565,7 @@ pub mod cmds { /// Restore a keypair and implicit address from the mnemonic code #[derive(Clone, Debug)] - pub struct KeyDerive(pub args::KeyAndAddressDerive); + pub struct KeyDerive(pub args::KeyDerive); impl SubCmd for KeyDerive { const CMD: &'static str = "derive"; @@ -572,7 +573,7 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| Self(args::KeyAndAddressDerive::parse(matches))) + .map(|matches| Self(args::KeyDerive::parse(matches))) } fn def() -> App { @@ -584,7 +585,7 @@ pub mod cmds { the given alias. A hardware wallet can be used, in which \ case a private key is not derivable.", ) - .add_args::() + .add_args::() } } @@ -614,6 +615,34 @@ pub mod cmds { } } + /// In the transparent setting, derive a keypair and implicit address from + /// the mnemonic code. + /// In the shielded setting, derive a spending key from the mnemonic code. + #[derive(Clone, Debug)] + pub struct WalletNewDerive(pub args::KeyDerive); + + impl SubCmd for WalletNewDerive { + const CMD: &'static str = "derive"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::KeyDerive::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Derives a keypair from the given mnemonic code and HD \ + derivation path and derives the implicit address from \ + its public key. Stores the keypair and the address with \ + the given alias. A hardware wallet can be used, in which \ + case a private key is not derivable. TODO shielded", + ) + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct KeyFind(pub args::KeyFind); @@ -834,7 +863,6 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum WalletAddress { - Derive(AddressDerive), Find(AddressOrAliasFind), List, Add(AddressAdd), @@ -845,12 +873,11 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { - let restore = SubCmd::parse(matches).map(Self::Derive); let find = SubCmd::parse(matches).map(Self::Find); let list = ::parse(matches).map(|_| Self::List); let add = SubCmd::parse(matches).map(Self::Add); - restore.or(find).or(list).or(add) + find.or(list).or(add) }) } @@ -862,39 +889,12 @@ pub mod cmds { ) .subcommand_required(true) .arg_required_else_help(true) - .subcommand(AddressDerive::def()) .subcommand(AddressOrAliasFind::def()) .subcommand(AddressList::def()) .subcommand(AddressAdd::def()) } } - /// Restore a keypair and an implicit address from the mnemonic code - #[derive(Clone, Debug)] - pub struct AddressDerive(pub args::KeyAndAddressDerive); - - impl SubCmd for AddressDerive { - const CMD: &'static str = "derive"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - AddressDerive(args::KeyAndAddressDerive::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Derives a keypair from the given mnemonic code and HD \ - derivation path and derives the implicit address from \ - its public key. Stores the keypair and the address with \ - the given alias. A hardware wallet can be used, in which \ - case a private key is not derivable.", - ) - .add_args::() - } - } - /// Find an address by its alias #[derive(Clone, Debug)] pub struct AddressOrAliasFind(pub args::AddressOrAliasFind); @@ -6247,9 +6247,10 @@ pub mod args { } } - impl Args for KeyAndAddressDerive { + impl Args for KeyDerive { fn parse(matches: &ArgMatches) -> Self { let scheme = SCHEME.parse(matches); + let shielded = SHIELDED.parse(matches); let alias = ALIAS_OPT.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); @@ -6257,6 +6258,7 @@ pub mod args { let derivation_path = HD_WALLET_DERIVATION_PATH.parse(matches); Self { scheme, + shielded, alias, alias_force, unsafe_dont_encrypt, @@ -6266,11 +6268,17 @@ pub mod args { } fn def(app: App) -> App { - app.arg(SCHEME.def().help( - "The type of key that should be added. Argument must be \ - either ed25519 or secp256k1. If none provided, the default \ - key scheme is ed25519.", + app.arg(SCHEME.def().conflicts_with(SHIELDED.name).help( + "For the transparent pool, the type of key that should be \ + derived. Argument must be either ed25519 or secp256k1. If \ + none provided, the default key scheme is ed25519.\nNot \ + applicable for the shielded pool.", )) + .arg( + SHIELDED + .def() + .help("Derive a spending key for the shielded pool."), + ) .arg(ALIAS_OPT.def().help( "The key and address alias. If none provided, the alias will \ be the public key hash.", @@ -6357,8 +6365,7 @@ pub mod args { scheme default path:\n- m/44'/60'/0'/0/0 for secp256k1 \ scheme\n- m/44'/877'/0'/0'/0' for ed25519 scheme.\nFor \ ed25519, all path indices will be promoted to hardened \ - indexes. If none specified, mnemonic code and derivation \ - path are not used.", + indexes. TODO", )) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index fdc94fb7ce..14b2265510 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -40,9 +40,6 @@ impl CliApi { ) -> Result<()> { match cmd { cmds::NamadaWallet::Key(sub) => match sub { - cmds::WalletKey::Derive(cmds::KeyDerive(args)) => { - key_and_address_derive(ctx, io, args).await - } cmds::WalletKey::Find(cmds::KeyFind(args)) => { key_find(ctx, io, args) } @@ -54,9 +51,6 @@ impl CliApi { } }, cmds::NamadaWallet::Address(sub) => match sub { - cmds::WalletAddress::Derive(cmds::AddressDerive(args)) => { - key_and_address_derive(ctx, io, args).await - } cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { address_or_alias_find(ctx, io, args) } @@ -86,6 +80,9 @@ impl CliApi { cmds::NamadaWallet::NewGen(cmds::WalletNewGen(args)) => { key_gen(ctx, io, args) } + cmds::NamadaWallet::NewDerive(cmds::WalletNewDerive(args)) => { + key_derive(ctx, io, args).await + } } Ok(()) } @@ -374,14 +371,15 @@ pub fn decode_derivation_path( async fn key_and_address_derive( ctx: Context, io: &impl Io, - args::KeyAndAddressDerive { + args::KeyDerive { scheme, alias, alias_force, unsafe_dont_encrypt, derivation_path, use_device, - }: args::KeyAndAddressDerive, + .. + }: args::KeyDerive, ) { let mut wallet = load_wallet(ctx); let derivation_path = decode_derivation_path(scheme, derivation_path) @@ -531,6 +529,19 @@ fn key_and_address_gen( ); } +/// TODO +async fn key_derive( + ctx: Context, + io: &impl Io, + args_key_derive: args::KeyDerive, +) { + if !args_key_derive.shielded { + key_and_address_derive(ctx, io, args_key_derive).await + } else { + todo!() + } +} + /// Find a keypair in the wallet store. fn key_find( ctx: Context, diff --git a/sdk/src/args.rs b/sdk/src/args.rs index b8bdc9492f..ab5bffdab9 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2096,6 +2096,25 @@ pub struct KeyGen { pub derivation_path: String, } +/// Wallet restore key and implicit address arguments +#[derive(Clone, Debug)] +pub struct KeyDerive { + /// Scheme type + pub scheme: SchemeType, + /// Whether to generate a spending key for the shielded pool + pub shielded: bool, + /// Key alias + pub alias: Option, + /// Whether to force overwrite the alias, if provided + pub alias_force: bool, + /// Don't encrypt the keypair + pub unsafe_dont_encrypt: bool, + /// BIP44 derivation path + pub derivation_path: String, + /// Use device to generate key and address + pub use_device: bool, +} + /// Wallet restore key and implicit address arguments #[derive(Clone, Debug)] pub struct KeyAndAddressDerive { From 16b80ded8fa82bb08d96ebc973708ef4e74a6830 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Mon, 20 Nov 2023 16:06:08 +0100 Subject: [PATCH 03/46] Unify cli for transparent / shielded key listing --- apps/src/lib/cli.rs | 58 ++++++++++++++++++++++++++++---------- apps/src/lib/cli/wallet.rs | 29 +++++++++++-------- sdk/src/args.rs | 4 ++- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index f4eedb6ce8..bebcff3703 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -489,6 +489,8 @@ pub mod cmds { NewGen(WalletNewGen), /// TODO NewDerive(WalletNewDerive), + /// TODO + NewKeyList(WalletNewKeyList), } impl Cmd for NamadaWallet { @@ -498,6 +500,7 @@ pub mod cmds { .subcommand(WalletMasp::def()) .subcommand(WalletNewGen::def()) .subcommand(WalletNewDerive::def()) + .subcommand(WalletNewKeyList::def()) } fn parse(matches: &ArgMatches) -> Option { @@ -506,7 +509,12 @@ pub mod cmds { let masp = SubCmd::parse(matches).map(Self::Masp); let gen_new = SubCmd::parse(matches).map(Self::NewGen); let derive_new = SubCmd::parse(matches).map(Self::NewDerive); - key.or(address).or(masp).or(gen_new).or(derive_new) + let key_list_new = SubCmd::parse(matches).map(Self::NewKeyList); + key.or(address) + .or(masp) + .or(gen_new) + .or(derive_new) + .or(key_list_new) } } @@ -533,7 +541,6 @@ pub mod cmds { #[allow(clippy::large_enum_variant)] pub enum WalletKey { Find(KeyFind), - List(KeyList), Export(Export), } @@ -543,9 +550,8 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { let lookup = SubCmd::parse(matches).map(Self::Find); - let list = SubCmd::parse(matches).map(Self::List); let export = SubCmd::parse(matches).map(Self::Export); - lookup.or(list).or(export) + lookup.or(export) }) } @@ -558,7 +564,6 @@ pub mod cmds { .subcommand_required(true) .arg_required_else_help(true) .subcommand(KeyFind::def()) - .subcommand(KeyList::def()) .subcommand(Export::def()) } } @@ -662,6 +667,25 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct WalletNewKeyList(pub args::KeyList); + + impl SubCmd for WalletNewKeyList { + const CMD: &'static str = "list"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| (Self(args::KeyList::parse(matches)))) + } + + fn def() -> App { + App::new(Self::CMD) + .about("List all known keys.") + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct KeyList(pub args::KeyList); @@ -706,7 +730,6 @@ pub mod cmds { GenPayAddr(MaspGenPayAddr), AddAddrKey(MaspAddAddrKey), ListPayAddrs, - ListKeys(MaspListKeys), FindAddrKey(MaspFindAddrKey), } @@ -719,9 +742,8 @@ pub mod cmds { let addak = SubCmd::parse(matches).map(Self::AddAddrKey); let listpa = ::parse(matches) .map(|_| Self::ListPayAddrs); - let listsk = SubCmd::parse(matches).map(Self::ListKeys); let findak = SubCmd::parse(matches).map(Self::FindAddrKey); - genpa.or(addak).or(listpa).or(listsk).or(findak) + genpa.or(addak).or(listpa).or(findak) }) } @@ -737,7 +759,6 @@ pub mod cmds { .subcommand(MaspGenPayAddr::def()) .subcommand(MaspAddAddrKey::def()) .subcommand(MaspListPayAddrs::def()) - .subcommand(MaspListKeys::def()) .subcommand(MaspFindAddrKey::def()) } } @@ -6453,21 +6474,28 @@ pub mod args { impl Args for KeyList { fn parse(matches: &ArgMatches) -> Self { + let shielded = SHIELDED.parse(matches); let decrypt = DECRYPT.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { + shielded, decrypt, unsafe_show_secret, } } fn def(app: App) -> App { - app.arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg( - UNSAFE_SHOW_SECRET - .def() - .help("UNSAFE: Print the secret keys."), - ) + app.arg( + SHIELDED + .def() + .help("List spending keys for the shielded pool."), + ) + .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) + .arg( + UNSAFE_SHOW_SECRET + .def() + .help("UNSAFE: Print the secret keys."), + ) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 14b2265510..22e4d38c31 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -43,9 +43,6 @@ impl CliApi { cmds::WalletKey::Find(cmds::KeyFind(args)) => { key_find(ctx, io, args) } - cmds::WalletKey::List(cmds::KeyList(args)) => { - key_list(ctx, io, args) - } cmds::WalletKey::Export(cmds::Export(args)) => { key_export(ctx, io, args) } @@ -70,9 +67,6 @@ impl CliApi { cmds::WalletMasp::ListPayAddrs => { payment_addresses_list(ctx, io) } - cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { - spending_keys_list(ctx, io, args) - } cmds::WalletMasp::FindAddrKey(cmds::MaspFindAddrKey(args)) => { address_key_find(ctx, io, args) } @@ -83,6 +77,9 @@ impl CliApi { cmds::NamadaWallet::NewDerive(cmds::WalletNewDerive(args)) => { key_derive(ctx, io, args).await } + cmds::NamadaWallet::NewKeyList(cmds::WalletNewKeyList(args)) => { + key_list(ctx, io, args) + } } Ok(()) } @@ -131,10 +128,11 @@ fn address_key_find( fn spending_keys_list( ctx: Context, io: &impl Io, - args::MaspKeysList { + args::KeyList { decrypt, unsafe_show_secret, - }: args::MaspKeysList, + .. + }: args::KeyList, ) { let wallet = load_wallet(ctx); let known_view_keys = wallet.get_viewing_keys(); @@ -542,6 +540,15 @@ async fn key_derive( } } +/// TODO +fn key_list(ctx: Context, io: &impl Io, args_key_list: args::KeyList) { + if !args_key_list.shielded { + transparent_keys_list(ctx, io, args_key_list) + } else { + spending_keys_list(ctx, io, args_key_list) + } +} + /// Find a keypair in the wallet store. fn key_find( ctx: Context, @@ -589,12 +596,13 @@ fn key_find( } /// List all known keys. -fn key_list( +fn transparent_keys_list( ctx: Context, io: &impl Io, args::KeyList { decrypt, unsafe_show_secret, + .. }: args::KeyList, ) { let wallet = load_wallet(ctx); @@ -602,8 +610,7 @@ fn key_list( if known_public_keys.is_empty() { display_line!( io, - "No known keys. Try `key gen --alias my-key` to generate a new \ - key.", + "No known keys. Try `gen --alias my-key` to generate a new key.", ); } else { let stdout = io::stdout(); diff --git a/sdk/src/args.rs b/sdk/src/args.rs index ab5bffdab9..3aa21d3e96 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2166,7 +2166,9 @@ pub struct MaspKeysList { /// Wallet list keys arguments #[derive(Clone, Debug)] pub struct KeyList { - /// Don't decrypt keypairs + /// Whether to list spending keys of the shielded pool + pub shielded: bool, + /// Don't decrypt keys pub decrypt: bool, /// Show secret keys to user pub unsafe_show_secret: bool, From b345e35c5aecd0f7dc6b53f4f87a269fa2a4da1b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Tue, 21 Nov 2023 15:45:09 +0100 Subject: [PATCH 04/46] Unify cli for transparent / shielded key / address listing / searching --- apps/src/lib/cli.rs | 366 +++++++++++++++---------------------- apps/src/lib/cli/wallet.rs | 174 +++++++++++------- sdk/src/args.rs | 67 +++---- 3 files changed, 274 insertions(+), 333 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index bebcff3703..b8580e8ba1 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -491,6 +491,12 @@ pub mod cmds { NewDerive(WalletNewDerive), /// TODO NewKeyList(WalletNewKeyList), + /// TODO + NewKeyFind(WalletNewKeyFind), + /// TODO + NewAddrList(WalletNewAddressList), + /// TODO + NewAddrFind(WalletNewAddressFind), } impl Cmd for NamadaWallet { @@ -501,6 +507,9 @@ pub mod cmds { .subcommand(WalletNewGen::def()) .subcommand(WalletNewDerive::def()) .subcommand(WalletNewKeyList::def()) + .subcommand(WalletNewKeyFind::def()) + .subcommand(WalletNewAddressList::def()) + .subcommand(WalletNewAddressFind::def()) } fn parse(matches: &ArgMatches) -> Option { @@ -510,11 +519,17 @@ pub mod cmds { let gen_new = SubCmd::parse(matches).map(Self::NewGen); let derive_new = SubCmd::parse(matches).map(Self::NewDerive); let key_list_new = SubCmd::parse(matches).map(Self::NewKeyList); + let key_find_new = SubCmd::parse(matches).map(Self::NewKeyFind); + let addr_list_new = SubCmd::parse(matches).map(Self::NewAddrList); + let addr_find_new = SubCmd::parse(matches).map(Self::NewAddrFind); key.or(address) .or(masp) .or(gen_new) .or(derive_new) .or(key_list_new) + .or(key_find_new) + .or(addr_list_new) + .or(addr_find_new) } } @@ -540,7 +555,6 @@ pub mod cmds { #[derive(Clone, Debug)] #[allow(clippy::large_enum_variant)] pub enum WalletKey { - Find(KeyFind), Export(Export), } @@ -549,9 +563,8 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { - let lookup = SubCmd::parse(matches).map(Self::Find); let export = SubCmd::parse(matches).map(Self::Export); - lookup.or(export) + export }) } @@ -563,37 +576,10 @@ pub mod cmds { ) .subcommand_required(true) .arg_required_else_help(true) - .subcommand(KeyFind::def()) .subcommand(Export::def()) } } - /// Restore a keypair and implicit address from the mnemonic code - #[derive(Clone, Debug)] - pub struct KeyDerive(pub args::KeyDerive); - - impl SubCmd for KeyDerive { - const CMD: &'static str = "derive"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::KeyDerive::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Derives a keypair from the given mnemonic code and HD \ - derivation path and derives the implicit address from \ - its public key. Stores the keypair and the address with \ - the given alias. A hardware wallet can be used, in which \ - case a private key is not derivable.", - ) - .add_args::() - } - } - /// In the transparent setting, generate a new keypair and an implicit /// address derived from it. In the shielded setting, generate a new /// spending key. @@ -611,7 +597,8 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about( + .about("Generates a new transparent / shielded secret key.") + .long_about( "Generates a keypair with a given alias and derives the \ implicit address from its public key. The address will \ be stored with the same alias. TODO shielded", @@ -638,6 +625,9 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about( + "Derive transparent / shielded key from the mnemonic code.", + ) + .long_about( "Derives a keypair from the given mnemonic code and HD \ derivation path and derives the implicit address from \ its public key. Stores the keypair and the address with \ @@ -648,60 +638,97 @@ pub mod cmds { } } + /// TODO List all known shielded keys #[derive(Clone, Debug)] - pub struct KeyFind(pub args::KeyFind); + pub struct WalletNewKeyList(pub args::KeyList); - impl SubCmd for KeyFind { - const CMD: &'static str = "find"; + impl SubCmd for WalletNewKeyList { + const CMD: &'static str = "list-keys"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| (Self(args::KeyFind::parse(matches)))) + .map(|matches| (Self(args::KeyList::parse(matches)))) } fn def() -> App { App::new(Self::CMD) - .about("Searches for a keypair from a public key or an alias.") - .add_args::() + .about( + "TODO List all known keys. Lists all shielded keys in the \ + wallet.", + ) + .add_args::() } } + /// TODO Find a keypair in the wallet store + /// TODO Find the given shielded address or key #[derive(Clone, Debug)] - pub struct WalletNewKeyList(pub args::KeyList); + pub struct WalletNewKeyFind(pub args::KeyFind); - impl SubCmd for WalletNewKeyList { - const CMD: &'static str = "list"; + impl SubCmd for WalletNewKeyFind { + const CMD: &'static str = "find-key"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| (Self(args::KeyList::parse(matches)))) + .map(|matches| (Self(args::KeyFind::parse(matches)))) } fn def() -> App { App::new(Self::CMD) - .about("List all known keys.") + .about( + "TODO Searches for a keypair from a public key or an \ + alias. Find the given shielded address or key in the \ + wallet.", + ) .add_args::() } } + /// List known addresses + /// TODO List all known payment addresses #[derive(Clone, Debug)] - pub struct KeyList(pub args::KeyList); + pub struct WalletNewAddressList(pub args::AddressList); - impl SubCmd for KeyList { - const CMD: &'static str = "list"; + impl SubCmd for WalletNewAddressList { + const CMD: &'static str = "list-addr"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| (Self(args::KeyList::parse(matches)))) + .map(|matches| Self(args::AddressList::parse(matches))) } fn def() -> App { App::new(Self::CMD) - .about("List all known keys.") - .add_args::() + .about( + "TODO List all known addresses. Lists all payment \ + addresses in the wallet", + ) + .add_args::() + } + } + + /// Find an address by its alias + #[derive(Clone, Debug)] + pub struct WalletNewAddressFind(pub args::AddressFind); + + impl SubCmd for WalletNewAddressFind { + const CMD: &'static str = "find-addr"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::AddressFind::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Find an address by its alias or an alias by its address.", + ) + .add_args::() } } @@ -729,8 +756,6 @@ pub mod cmds { pub enum WalletMasp { GenPayAddr(MaspGenPayAddr), AddAddrKey(MaspAddAddrKey), - ListPayAddrs, - FindAddrKey(MaspFindAddrKey), } impl SubCmd for WalletMasp { @@ -740,10 +765,7 @@ pub mod cmds { matches.subcommand_matches(Self::CMD).and_then(|matches| { let genpa = SubCmd::parse(matches).map(Self::GenPayAddr); let addak = SubCmd::parse(matches).map(Self::AddAddrKey); - let listpa = ::parse(matches) - .map(|_| Self::ListPayAddrs); - let findak = SubCmd::parse(matches).map(Self::FindAddrKey); - genpa.or(addak).or(listpa).or(findak) + genpa.or(addak) }) } @@ -758,65 +780,6 @@ pub mod cmds { .arg_required_else_help(true) .subcommand(MaspGenPayAddr::def()) .subcommand(MaspAddAddrKey::def()) - .subcommand(MaspListPayAddrs::def()) - .subcommand(MaspFindAddrKey::def()) - } - } - - /// Find the given shielded address or key - #[derive(Clone, Debug)] - pub struct MaspFindAddrKey(pub args::AddrKeyFind); - - impl SubCmd for MaspFindAddrKey { - const CMD: &'static str = "find"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::AddrKeyFind::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Find the given shielded address or key in the wallet") - .add_args::() - } - } - - /// List all known shielded keys - #[derive(Clone, Debug)] - pub struct MaspListKeys(pub args::MaspKeysList); - - impl SubCmd for MaspListKeys { - const CMD: &'static str = "list-keys"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::MaspKeysList::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Lists all shielded keys in the wallet") - .add_args::() - } - } - - /// List all known payment addresses - #[derive(Clone, Debug)] - pub struct MaspListPayAddrs; - - impl SubCmd for MaspListPayAddrs { - const CMD: &'static str = "list-addrs"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|_| Self) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Lists all payment addresses in the wallet") } } @@ -884,8 +847,6 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum WalletAddress { - Find(AddressOrAliasFind), - List, Add(AddressAdd), } @@ -894,11 +855,8 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { - let find = SubCmd::parse(matches).map(Self::Find); - let list = - ::parse(matches).map(|_| Self::List); let add = SubCmd::parse(matches).map(Self::Add); - find.or(list).or(add) + add }) } @@ -910,34 +868,10 @@ pub mod cmds { ) .subcommand_required(true) .arg_required_else_help(true) - .subcommand(AddressOrAliasFind::def()) - .subcommand(AddressList::def()) .subcommand(AddressAdd::def()) } } - /// Find an address by its alias - #[derive(Clone, Debug)] - pub struct AddressOrAliasFind(pub args::AddressOrAliasFind); - - impl SubCmd for AddressOrAliasFind { - const CMD: &'static str = "find"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - AddressOrAliasFind(args::AddressOrAliasFind::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Find an address by its alias or an alias by its address.", - ) - .add_args::() - } - } - /// List known addresses #[derive(Clone, Debug)] pub struct AddressList; @@ -6391,95 +6325,46 @@ pub mod args { } } - impl Args for KeyFind { + impl Args for KeyList { fn parse(matches: &ArgMatches) -> Self { - let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); - let alias = ALIAS_OPT.parse(matches); - let value = VALUE.parse(matches); + let shielded = SHIELDED.parse(matches); + let decrypt = DECRYPT.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); - Self { - public_key, - alias, - value, + shielded, + decrypt, unsafe_show_secret, } } fn def(app: App) -> App { app.arg( - RAW_PUBLIC_KEY_OPT - .def() - .help("A public key associated with the keypair.") - .conflicts_with_all([ALIAS_OPT.name, VALUE.name]), - ) - .arg( - ALIAS_OPT - .def() - .help("An alias associated with the keypair.") - .conflicts_with(VALUE.name), - ) - .arg( - VALUE + SHIELDED .def() - .help("A public key or alias associated with the keypair."), + .help("List spending keys for the shielded pool."), ) + .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) .arg( UNSAFE_SHOW_SECRET .def() - .help("UNSAFE: Print the secret key."), + .help("UNSAFE: Print the secret / spending keys."), ) } } - impl Args for AddrKeyFind { - fn parse(matches: &ArgMatches) -> Self { - let alias = ALIAS.parse(matches); - let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); - Self { - alias, - unsafe_show_secret, - } - } - - fn def(app: App) -> App { - app.arg(ALIAS.def().help("The alias that is to be found.")) - .arg( - UNSAFE_SHOW_SECRET - .def() - .help("UNSAFE: Print the spending key values."), - ) - } - } - - impl Args for MaspKeysList { - fn parse(matches: &ArgMatches) -> Self { - let decrypt = DECRYPT.parse(matches); - let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); - Self { - decrypt, - unsafe_show_secret, - } - } - - fn def(app: App) -> App { - app.arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg( - UNSAFE_SHOW_SECRET - .def() - .help("UNSAFE: Print the spending key values."), - ) - } - } - - impl Args for KeyList { + impl Args for KeyFind { fn parse(matches: &ArgMatches) -> Self { let shielded = SHIELDED.parse(matches); - let decrypt = DECRYPT.parse(matches); + let alias = ALIAS_OPT.parse(matches); + let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); + let value = VALUE.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); + Self { shielded, - decrypt, + alias, + public_key, + value, unsafe_show_secret, } } @@ -6488,31 +6373,59 @@ pub mod args { app.arg( SHIELDED .def() - .help("List spending keys for the shielded pool."), + .help("Find spending key for the shielded pool.") + .conflicts_with_all([VALUE.name, RAW_PUBLIC_KEY_OPT.name]), ) - .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) .arg( - UNSAFE_SHOW_SECRET + ALIAS_OPT + .def() + .help( + "TODO An alias associated with the keypair. The alias \ + that is to be found.", + ) + .conflicts_with(VALUE.name), + ) + .arg( + RAW_PUBLIC_KEY_OPT + .def() + .help("A public key associated with the keypair."), + ) + .arg( + VALUE .def() - .help("UNSAFE: Print the secret keys."), + .help("A public key or alias associated with the keypair."), + ) + .group( + ArgGroup::new("key_find_flags") + .args([ALIAS_OPT.name, RAW_PUBLIC_KEY_OPT.name, VALUE.name]) + .required(true), ) + .arg(PRE_GENESIS.def().help( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) + .arg(UNSAFE_SHOW_SECRET.def().help( + "TODO UNSAFE: Print the secret key.Print the spending key \ + values.", + )) } } - impl Args for KeyExport { + impl Args for AddressList { fn parse(matches: &ArgMatches) -> Self { - let alias = ALIAS.parse(matches); - Self { alias } + let shielded = SHIELDED.parse(matches); + Self { shielded } } fn def(app: App) -> App { - app.arg( - ALIAS.def().help("The alias of the key you wish to export."), - ) + app.arg(PRE_GENESIS.def().help( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) } } - impl Args for AddressOrAliasFind { + impl Args for AddressFind { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS_OPT.parse(matches); let address = RAW_ADDRESS_OPT.parse(matches); @@ -6567,6 +6480,19 @@ pub mod args { } } + impl Args for KeyExport { + fn parse(matches: &ArgMatches) -> Self { + let alias = ALIAS.parse(matches); + Self { alias } + } + + fn def(app: App) -> App { + app.arg( + ALIAS.def().help("The alias of the key you wish to export."), + ) + } + } + #[derive(Clone, Debug)] pub struct JoinNetwork { pub chain_id: ChainId, diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 22e4d38c31..ffd08a50c0 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -40,18 +40,11 @@ impl CliApi { ) -> Result<()> { match cmd { cmds::NamadaWallet::Key(sub) => match sub { - cmds::WalletKey::Find(cmds::KeyFind(args)) => { - key_find(ctx, io, args) - } cmds::WalletKey::Export(cmds::Export(args)) => { key_export(ctx, io, args) } }, cmds::NamadaWallet::Address(sub) => match sub { - cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { - address_or_alias_find(ctx, io, args) - } - cmds::WalletAddress::List => address_list(ctx, io), cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { address_add(ctx, io, args) } @@ -64,12 +57,6 @@ impl CliApi { cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { address_key_add(ctx, io, args) } - cmds::WalletMasp::ListPayAddrs => { - payment_addresses_list(ctx, io) - } - cmds::WalletMasp::FindAddrKey(cmds::MaspFindAddrKey(args)) => { - address_key_find(ctx, io, args) - } }, cmds::NamadaWallet::NewGen(cmds::WalletNewGen(args)) => { key_gen(ctx, io, args) @@ -80,47 +67,17 @@ impl CliApi { cmds::NamadaWallet::NewKeyList(cmds::WalletNewKeyList(args)) => { key_list(ctx, io, args) } - } - Ok(()) - } -} - -/// Find shielded address or key -fn address_key_find( - ctx: Context, - io: &impl Io, - args::AddrKeyFind { - alias, - unsafe_show_secret, - }: args::AddrKeyFind, -) { - let mut wallet = load_wallet(ctx); - let alias = alias.to_lowercase(); - if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { - // Check if alias is a viewing key - display_line!(io, "Viewing key: {}", viewing_key); - if unsafe_show_secret { - // Check if alias is also a spending key - match wallet.find_spending_key(&alias, None) { - Ok(spending_key) => { - display_line!(io, "Spending key: {}", spending_key) - } - Err(FindKeyError::KeyNotFound(_)) => {} - Err(err) => edisplay_line!(io, "{}", err), + cmds::NamadaWallet::NewKeyFind(cmds::WalletNewKeyFind(args)) => { + key_find(ctx, io, args) } + cmds::NamadaWallet::NewAddrList(cmds::WalletNewAddressList( + args, + )) => address_list(ctx, io, args), + cmds::NamadaWallet::NewAddrFind(cmds::WalletNewAddressFind( + args, + )) => address_or_alias_find(ctx, io, args), } - } else if let Some(payment_addr) = wallet.find_payment_addr(&alias) { - // Failing that, check if alias is a payment address - display_line!(io, "Payment address: {}", payment_addr); - } else { - // Otherwise alias cannot be referring to any shielded value - display_line!( - io, - "No shielded address or key with alias {} found. Use the commands \ - `masp list-addrs` and `masp list-keys` to see all the known \ - addresses and keys.", - alias.to_lowercase() - ); + Ok(()) } } @@ -140,8 +97,8 @@ fn spending_keys_list( if known_view_keys.is_empty() { display_line!( io, - "No known keys. Try `masp add --alias my-addr --value ...` to add \ - a new key to the wallet.", + "TODO No known keys. Try `masp add --alias my-addr --value ...` \ + to add a new key to the wallet.", ); } else { let stdout = io::stdout(); @@ -202,7 +159,11 @@ fn spending_keys_list( } /// List payment addresses. -fn payment_addresses_list(ctx: Context, io: &impl Io) { +fn payment_addresses_list( + ctx: Context, + io: &impl Io, + // args::AddressList { .. }: args::AddressList, +) { let wallet = load_wallet(ctx); let known_addresses = wallet.get_payment_addrs(); if known_addresses.is_empty() { @@ -366,7 +327,7 @@ pub fn decode_derivation_path( /// Derives a keypair and an implicit address from the mnemonic code in the /// wallet. -async fn key_and_address_derive( +async fn transparent_key_and_address_derive( ctx: Context, io: &impl Io, args::KeyDerive { @@ -461,18 +422,9 @@ async fn key_and_address_derive( ); } -/// TODO -fn key_gen(ctx: Context, io: &impl Io, args_key_gen: args::KeyGen) { - if !args_key_gen.shielded { - key_and_address_gen(ctx, io, args_key_gen) - } else { - spending_key_gen(ctx, io, args_key_gen) - } -} - /// Generate a new keypair and derive implicit address from it and store them in /// the wallet. -fn key_and_address_gen( +fn transparent_key_and_address_gen( ctx: Context, io: &impl Io, args::KeyGen { @@ -527,6 +479,15 @@ fn key_and_address_gen( ); } +/// TODO +fn key_gen(ctx: Context, io: &impl Io, args_key_gen: args::KeyGen) { + if !args_key_gen.shielded { + transparent_key_and_address_gen(ctx, io, args_key_gen) + } else { + spending_key_gen(ctx, io, args_key_gen) + } +} + /// TODO async fn key_derive( ctx: Context, @@ -534,7 +495,7 @@ async fn key_derive( args_key_derive: args::KeyDerive, ) { if !args_key_derive.shielded { - key_and_address_derive(ctx, io, args_key_derive).await + transparent_key_and_address_derive(ctx, io, args_key_derive).await } else { todo!() } @@ -549,8 +510,26 @@ fn key_list(ctx: Context, io: &impl Io, args_key_list: args::KeyList) { } } +/// TODO +fn key_find(ctx: Context, io: &impl Io, args_key_find: args::KeyFind) { + if !args_key_find.shielded { + transparent_key_address_find(ctx, io, args_key_find) + } else { + shielded_key_address_find(ctx, io, args_key_find) + } +} + +/// TODO +fn address_list(ctx: Context, io: &impl Io, args_key_list: args::AddressList) { + if !args_key_list.shielded { + transparent_addresses_list(ctx, io, args_key_list) + } else { + payment_addresses_list(ctx, io) + } +} + /// Find a keypair in the wallet store. -fn key_find( +fn transparent_key_address_find( ctx: Context, io: &impl Io, args::KeyFind { @@ -558,6 +537,7 @@ fn key_find( alias, value, unsafe_show_secret, + .. }: args::KeyFind, ) { let mut wallet = load_wallet(ctx); @@ -595,6 +575,53 @@ fn key_find( } } +/// Find shielded address or key +/// TODO this works for both keys and addresses +/// TODO split to enable finding alias by payment key +fn shielded_key_address_find( + ctx: Context, + io: &impl Io, + args::KeyFind { + alias, + unsafe_show_secret, + .. + }: args::KeyFind, +) { + let mut wallet = load_wallet(ctx); + let alias = alias.unwrap_or_else(|| { + display_line!(io, "Missing alias."); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }); + let alias = alias.to_lowercase(); + if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { + // Check if alias is a viewing key + display_line!(io, "Viewing key: {}", viewing_key); + if unsafe_show_secret { + // Check if alias is also a spending key + match wallet.find_spending_key(&alias, None) { + Ok(spending_key) => { + display_line!(io, "Spending key: {}", spending_key) + } + Err(FindKeyError::KeyNotFound(_)) => {} + Err(err) => edisplay_line!(io, "{}", err), + } + } + } else if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + // Failing that, check if alias is a payment address + display_line!(io, "Payment address: {}", payment_addr); + } else { + // Otherwise alias cannot be referring to any shielded value + display_line!( + io, + "TODO No shielded address or key with alias {} found. Use the \ + commands `masp list-addrs` and `masp list-keys` to see all the \ + known addresses and keys.", + alias.to_lowercase() + ); + } +} + /// List all known keys. fn transparent_keys_list( ctx: Context, @@ -686,8 +713,13 @@ fn key_export( }) } -/// List all known addresses. -fn address_list(ctx: Context, io: &impl Io) { +/// List all known transparent addresses. +fn transparent_addresses_list( + ctx: Context, + io: &impl Io, + _args: args::AddressList, + // args::AddressList { is_pre_genesis, .. }: args::AddressList, +) { let wallet = load_wallet(ctx); let known_addresses = wallet.get_addresses(); if known_addresses.is_empty() { @@ -714,7 +746,7 @@ fn address_list(ctx: Context, io: &impl Io) { fn address_or_alias_find( ctx: Context, io: &impl Io, - args::AddressOrAliasFind { alias, address }: args::AddressOrAliasFind, + args::AddressFind { alias, address }: args::AddressFind, ) { let wallet = load_wallet(ctx); if address.is_some() && alias.is_some() { diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 3aa21d3e96..50aa46d6c6 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2115,53 +2115,29 @@ pub struct KeyDerive { pub use_device: bool, } -/// Wallet restore key and implicit address arguments -#[derive(Clone, Debug)] -pub struct KeyAndAddressDerive { - /// Scheme type - pub scheme: SchemeType, - /// Key alias - pub alias: Option, - /// Whether to force overwrite the alias, if provided - pub alias_force: bool, - /// Don't encrypt the keypair - pub unsafe_dont_encrypt: bool, - /// BIP44 derivation path - pub derivation_path: String, - /// Use device to generate key and address - pub use_device: bool, -} - -/// Wallet key lookup arguments +/// Wallet find shielded address or key arguments +/// TODO Wallet key lookup arguments #[derive(Clone, Debug)] pub struct KeyFind { - /// Public key to lookup keypair with - pub public_key: Option, + /// Whether to find shielded address by alias + pub shielded: bool, /// Key alias to lookup keypair with pub alias: Option, + /// Public key to lookup keypair with + pub public_key: Option, /// Public key hash to lookup keypair with pub value: Option, /// Show secret keys to user pub unsafe_show_secret: bool, } -/// Wallet find shielded address or key arguments -#[derive(Clone, Debug)] -pub struct AddrKeyFind { - /// Address/key alias - pub alias: String, - /// Show secret keys to user - pub unsafe_show_secret: bool, -} - -/// Wallet list shielded keys arguments -#[derive(Clone, Debug)] -pub struct MaspKeysList { - /// Don't decrypt spending keys - pub decrypt: bool, - /// Show secret keys to user - pub unsafe_show_secret: bool, -} +/// Wallet list shielded payment addresses arguments +// #[derive(Clone, Debug)] +// pub struct MaspListPayAddrs { +// /// List shielded payment address pre-genesis instead +// /// of a current chain +// pub is_pre_genesis: bool, +// } /// Wallet list keys arguments #[derive(Clone, Debug)] @@ -2174,22 +2150,29 @@ pub struct KeyList { pub unsafe_show_secret: bool, } -/// Wallet key export arguments +/// List transparent wallet addresses / shielded payment addresses #[derive(Clone, Debug)] -pub struct KeyExport { - /// Key alias - pub alias: String, +pub struct AddressList { + /// Whether to list payment addresses of the shielded pool + pub shielded: bool, } /// Wallet address lookup arguments #[derive(Clone, Debug)] -pub struct AddressOrAliasFind { +pub struct AddressFind { /// Alias to find pub alias: Option, /// Address to find pub address: Option
, } +/// Wallet key export arguments +#[derive(Clone, Debug)] +pub struct KeyExport { + /// Key alias + pub alias: String, +} + /// Wallet address add arguments #[derive(Clone, Debug)] pub struct AddressAdd { From 1947aa9a38d1c67c2ef41de3c3af526a304a52d4 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Tue, 21 Nov 2023 17:34:50 +0100 Subject: [PATCH 05/46] Refactor key export --- apps/src/lib/cli.rs | 46 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b8580e8ba1..06dfd32b50 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -479,8 +479,6 @@ pub mod cmds { #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] pub enum NamadaWallet { - /// Key management commands - Key(WalletKey), /// Address management commands Address(WalletAddress), /// MASP key, address management commands @@ -497,12 +495,13 @@ pub mod cmds { NewAddrList(WalletNewAddressList), /// TODO NewAddrFind(WalletNewAddressFind), + /// TODO + NewKeyExport(WalletNewExportKey), } impl Cmd for NamadaWallet { fn add_sub(app: App) -> App { - app.subcommand(WalletKey::def()) - .subcommand(WalletAddress::def()) + app.subcommand(WalletAddress::def()) .subcommand(WalletMasp::def()) .subcommand(WalletNewGen::def()) .subcommand(WalletNewDerive::def()) @@ -510,10 +509,10 @@ pub mod cmds { .subcommand(WalletNewKeyFind::def()) .subcommand(WalletNewAddressList::def()) .subcommand(WalletNewAddressFind::def()) + .subcommand(WalletNewExportKey::def()) } fn parse(matches: &ArgMatches) -> Option { - let key = SubCmd::parse(matches).map(Self::Key); let address = SubCmd::parse(matches).map(Self::Address); let masp = SubCmd::parse(matches).map(Self::Masp); let gen_new = SubCmd::parse(matches).map(Self::NewGen); @@ -522,7 +521,8 @@ pub mod cmds { let key_find_new = SubCmd::parse(matches).map(Self::NewKeyFind); let addr_list_new = SubCmd::parse(matches).map(Self::NewAddrList); let addr_find_new = SubCmd::parse(matches).map(Self::NewAddrFind); - key.or(address) + let export_new = SubCmd::parse(matches).map(Self::NewKeyExport); + address .or(masp) .or(gen_new) .or(derive_new) @@ -530,6 +530,7 @@ pub mod cmds { .or(key_find_new) .or(addr_list_new) .or(addr_find_new) + .or(export_new) } } @@ -552,34 +553,6 @@ pub mod cmds { } } - #[derive(Clone, Debug)] - #[allow(clippy::large_enum_variant)] - pub enum WalletKey { - Export(Export), - } - - impl SubCmd for WalletKey { - const CMD: &'static str = "key"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).and_then(|matches| { - let export = SubCmd::parse(matches).map(Self::Export); - export - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Keypair management, including methods to generate and \ - look-up keys.", - ) - .subcommand_required(true) - .arg_required_else_help(true) - .subcommand(Export::def()) - } - } - /// In the transparent setting, generate a new keypair and an implicit /// address derived from it. In the shielded setting, generate a new /// spending key. @@ -732,10 +705,11 @@ pub mod cmds { } } + /// Export key #[derive(Clone, Debug)] - pub struct Export(pub args::KeyExport); + pub struct WalletNewExportKey(pub args::KeyExport); - impl SubCmd for Export { + impl SubCmd for WalletNewExportKey { const CMD: &'static str = "export"; fn parse(matches: &ArgMatches) -> Option { From 9efb0784d5c44a64295e71ce2e24372d8950266e Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 00:52:41 +0100 Subject: [PATCH 06/46] Unify cli for key export / add --- apps/src/lib/cli.rs | 190 ++++++++++++------------------------- apps/src/lib/cli/wallet.rs | 55 +++++++---- sdk/src/args.rs | 12 ++- 3 files changed, 105 insertions(+), 152 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06dfd32b50..5970a4fdfd 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -479,30 +479,29 @@ pub mod cmds { #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] pub enum NamadaWallet { - /// Address management commands - Address(WalletAddress), /// MASP key, address management commands Masp(WalletMasp), - /// TODO + /// Key generation NewGen(WalletNewGen), - /// TODO + /// Key derivation NewDerive(WalletNewDerive), - /// TODO + /// Key list NewKeyList(WalletNewKeyList), - /// TODO + /// Key search NewKeyFind(WalletNewKeyFind), - /// TODO + /// Address list NewAddrList(WalletNewAddressList), - /// TODO + /// Address search NewAddrFind(WalletNewAddressFind), - /// TODO + /// Key export NewKeyExport(WalletNewExportKey), + /// Key import + NewKeyAddrAdd(WalletNewKeyAddressAdd), } impl Cmd for NamadaWallet { fn add_sub(app: App) -> App { - app.subcommand(WalletAddress::def()) - .subcommand(WalletMasp::def()) + app.subcommand(WalletMasp::def()) .subcommand(WalletNewGen::def()) .subcommand(WalletNewDerive::def()) .subcommand(WalletNewKeyList::def()) @@ -510,10 +509,10 @@ pub mod cmds { .subcommand(WalletNewAddressList::def()) .subcommand(WalletNewAddressFind::def()) .subcommand(WalletNewExportKey::def()) + .subcommand(WalletNewKeyAddressAdd::def()) } fn parse(matches: &ArgMatches) -> Option { - let address = SubCmd::parse(matches).map(Self::Address); let masp = SubCmd::parse(matches).map(Self::Masp); let gen_new = SubCmd::parse(matches).map(Self::NewGen); let derive_new = SubCmd::parse(matches).map(Self::NewDerive); @@ -522,15 +521,15 @@ pub mod cmds { let addr_list_new = SubCmd::parse(matches).map(Self::NewAddrList); let addr_find_new = SubCmd::parse(matches).map(Self::NewAddrFind); let export_new = SubCmd::parse(matches).map(Self::NewKeyExport); - address - .or(masp) - .or(gen_new) + let key_addr_add = SubCmd::parse(matches).map(Self::NewKeyAddrAdd); + masp.or(gen_new) .or(derive_new) .or(key_list_new) .or(key_find_new) .or(addr_list_new) .or(addr_find_new) .or(export_new) + .or(key_addr_add) } } @@ -725,11 +724,33 @@ pub mod cmds { } } + /// Add public / payment address to the wallet + #[derive(Clone, Debug)] + pub struct WalletNewKeyAddressAdd(pub args::KeyAddressAdd); + + impl SubCmd for WalletNewKeyAddressAdd { + const CMD: &'static str = "add"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| (Self(args::KeyAddressAdd::parse(matches)))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Adds the given transparent / payment address or key to \ + the wallet.", + ) + .add_args::() + } + } + #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] pub enum WalletMasp { GenPayAddr(MaspGenPayAddr), - AddAddrKey(MaspAddAddrKey), } impl SubCmd for WalletMasp { @@ -738,8 +759,7 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { let genpa = SubCmd::parse(matches).map(Self::GenPayAddr); - let addak = SubCmd::parse(matches).map(Self::AddAddrKey); - genpa.or(addak) + genpa }) } @@ -753,27 +773,6 @@ pub mod cmds { .subcommand_required(true) .arg_required_else_help(true) .subcommand(MaspGenPayAddr::def()) - .subcommand(MaspAddAddrKey::def()) - } - } - - /// Add a key or an address - #[derive(Clone, Debug)] - pub struct MaspAddAddrKey(pub args::MaspAddrKeyAdd); - - impl SubCmd for MaspAddAddrKey { - const CMD: &'static str = "add"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - MaspAddAddrKey(args::MaspAddrKeyAdd::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Adds the given payment address or key to the wallet") - .add_args::() } } @@ -819,33 +818,6 @@ pub mod cmds { } } - #[derive(Clone, Debug)] - pub enum WalletAddress { - Add(AddressAdd), - } - - impl SubCmd for WalletAddress { - const CMD: &'static str = "address"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).and_then(|matches| { - let add = SubCmd::parse(matches).map(Self::Add); - add - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Address management, including methods to generate and \ - look-up addresses.", - ) - .subcommand_required(true) - .arg_required_else_help(true) - .subcommand(AddressAdd::def()) - } - } - /// List known addresses #[derive(Clone, Debug)] pub struct AddressList; @@ -862,26 +834,6 @@ pub mod cmds { } } - /// Generate a new keypair and an implicit address derived from it - #[derive(Clone, Debug)] - pub struct AddressAdd(pub args::AddressAdd); - - impl SubCmd for AddressAdd { - const CMD: &'static str = "add"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| AddressAdd(args::AddressAdd::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Store an alias for an address in the wallet.") - .add_args::() - } - } - #[derive(Clone, Debug)] pub enum Ledger { Run(LedgerRun), @@ -3000,7 +2952,7 @@ pub mod args { pub const LEDGER_ADDRESS: Arg = arg("node"); pub const LOCALHOST: ArgFlag = flag("localhost"); - pub const MASP_VALUE: Arg = arg("value"); + pub const MASP_VALUE_OPT: ArgOpt = arg_opt("shielded-value"); pub const MAX_COMMISSION_RATE_CHANGE: Arg = arg("max-commission-rate-change"); pub const MAX_ETH_GAS: ArgOpt = arg_opt("max_eth-gas"); @@ -6048,41 +6000,6 @@ pub mod args { } } - impl Args for MaspAddrKeyAdd { - fn parse(matches: &ArgMatches) -> Self { - let alias = ALIAS.parse(matches); - let alias_force = ALIAS_FORCE.parse(matches); - let value = MASP_VALUE.parse(matches); - let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); - Self { - alias, - alias_force, - value, - unsafe_dont_encrypt, - } - } - - fn def(app: App) -> App { - app.arg( - ALIAS - .def() - .help("An alias to be associated with the new entry."), - ) - .arg(ALIAS_FORCE.def().help( - "Override the alias without confirmation if it already exists.", - )) - .arg( - MASP_VALUE - .def() - .help("A spending key, viewing key, or payment address."), - ) - .arg(UNSAFE_DONT_ENCRYPT.def().help( - "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ - used in a live network.", - )) - } - } - impl Args for MaspSpendKeyGen { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); @@ -6370,7 +6287,7 @@ pub mod args { .help("A public key or alias associated with the keypair."), ) .group( - ArgGroup::new("key_find_flags") + ArgGroup::new("key_find_args") .args([ALIAS_OPT.name, RAW_PUBLIC_KEY_OPT.name, VALUE.name]) .required(true), ) @@ -6425,15 +6342,19 @@ pub mod args { } } - impl Args for AddressAdd { + impl Args for KeyAddressAdd { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); - let address = RAW_ADDRESS.parse(matches); + let address = RAW_ADDRESS_OPT.parse(matches); + let value = MASP_VALUE_OPT.parse(matches); + let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { alias, alias_force, address, + value, + unsafe_dont_encrypt, } } @@ -6441,16 +6362,29 @@ pub mod args { app.arg( ALIAS .def() - .help("An alias to be associated with the address."), + .help("An alias to be associated with the new entry."), ) .arg(ALIAS_FORCE.def().help( "Override the alias without confirmation if it already exists.", )) .arg( - RAW_ADDRESS + RAW_ADDRESS_OPT .def() - .help("The bech32m encoded address string."), + .help("The bech32m encoded transparent address string."), ) + .arg(MASP_VALUE_OPT.def().help( + "A spending key, viewing key, or payment address of the \ + shielded pool.", + )) + .group( + ArgGroup::new("key_address_add_args") + .args([RAW_ADDRESS_OPT.name, MASP_VALUE_OPT.name]) + .required(true), + ) + .arg(UNSAFE_DONT_ENCRYPT.def().help( + "UNSAFE: Do not encrypt the added keys. Do not use this for \ + keys used in a live network.", + )) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index ffd08a50c0..e25707e454 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -39,24 +39,11 @@ impl CliApi { io: &impl Io, ) -> Result<()> { match cmd { - cmds::NamadaWallet::Key(sub) => match sub { - cmds::WalletKey::Export(cmds::Export(args)) => { - key_export(ctx, io, args) - } - }, - cmds::NamadaWallet::Address(sub) => match sub { - cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { - address_add(ctx, io, args) - } - }, cmds::NamadaWallet::Masp(sub) => match sub { cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { let args = args.to_sdk(&mut ctx); payment_address_gen(ctx, io, args) } - cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { - address_key_add(ctx, io, args) - } }, cmds::NamadaWallet::NewGen(cmds::WalletNewGen(args)) => { key_gen(ctx, io, args) @@ -76,6 +63,12 @@ impl CliApi { cmds::NamadaWallet::NewAddrFind(cmds::WalletNewAddressFind( args, )) => address_or_alias_find(ctx, io, args), + cmds::NamadaWallet::NewKeyExport(cmds::WalletNewExportKey( + args, + )) => key_export(ctx, io, args), + cmds::NamadaWallet::NewKeyAddrAdd( + cmds::WalletNewKeyAddressAdd(args), + ) => key_address_add(ctx, io, args), } Ok(()) } @@ -251,16 +244,18 @@ fn payment_address_gen( } /// Add a viewing key, spending key, or payment address to wallet. -fn address_key_add( +fn shielded_key_address_add( ctx: Context, io: &impl Io, - args::MaspAddrKeyAdd { + args::KeyAddressAdd { alias, alias_force, value, unsafe_dont_encrypt, - }: args::MaspAddrKeyAdd, + .. + }: args::KeyAddressAdd, ) { + let value = value.unwrap(); // this should not fail let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); let (alias, typ) = match value { @@ -528,6 +523,24 @@ fn address_list(ctx: Context, io: &impl Io, args_key_list: args::AddressList) { } } +/// TODO +fn key_address_add( + ctx: Context, + io: &impl Io, + args_key_addr_add: args::KeyAddressAdd, +) { + if args_key_addr_add.address.is_some() { + transparent_address_add(ctx, io, args_key_addr_add) + } else if args_key_addr_add.value.is_some() { + shielded_key_address_add(ctx, io, args_key_addr_add) + } else { + unreachable!( + "This should not happen as the group of address and value is \ + required." + ) + } +} + /// Find a keypair in the wallet store. fn transparent_key_address_find( ctx: Context, @@ -690,7 +703,7 @@ fn transparent_keys_list( } } -/// Export a keypair to a file. +/// Export a transparent keypair to a file. fn key_export( ctx: Context, io: &impl Io, @@ -780,15 +793,17 @@ fn address_or_alias_find( } /// Add an address to the wallet. -fn address_add( +fn transparent_address_add( ctx: Context, io: &impl Io, - args::AddressAdd { + args::KeyAddressAdd { alias, alias_force, address, - }: args::AddressAdd, + .. + }: args::KeyAddressAdd, ) { + let address = address.unwrap(); // this should not fail let mut wallet = load_wallet(ctx); if wallet .insert_address(alias.to_lowercase(), address, alias_force) diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 50aa46d6c6..e2e595b166 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2173,15 +2173,19 @@ pub struct KeyExport { pub alias: String, } -/// Wallet address add arguments +/// Wallet key / address add arguments #[derive(Clone, Debug)] -pub struct AddressAdd { +pub struct KeyAddressAdd { /// Address alias pub alias: String, /// Whether to force overwrite the alias pub alias_force: bool, - /// Address to add - pub address: Address, + /// Transparent address to add + pub address: Option
, + /// Any shielded value + pub value: Option, + /// Don't encrypt the keys + pub unsafe_dont_encrypt: bool, } /// Bridge pool batch recommendation. From 34be4924b0743d725f09dc868d12cb73b3c5c5fe Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 10:18:28 +0100 Subject: [PATCH 07/46] Refactor payment key gen --- apps/src/lib/cli.rs | 132 ++++++------------------------------- apps/src/lib/cli/wallet.rs | 16 ++--- sdk/src/args.rs | 55 +++++----------- 3 files changed, 44 insertions(+), 159 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 5970a4fdfd..420a6c91e9 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -479,8 +479,6 @@ pub mod cmds { #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] pub enum NamadaWallet { - /// MASP key, address management commands - Masp(WalletMasp), /// Key generation NewGen(WalletNewGen), /// Key derivation @@ -497,12 +495,13 @@ pub mod cmds { NewKeyExport(WalletNewExportKey), /// Key import NewKeyAddrAdd(WalletNewKeyAddressAdd), + /// Payment address generation + NewPayAddrGen(WalletNewPaymentAddressGen), } impl Cmd for NamadaWallet { fn add_sub(app: App) -> App { - app.subcommand(WalletMasp::def()) - .subcommand(WalletNewGen::def()) + app.subcommand(WalletNewGen::def()) .subcommand(WalletNewDerive::def()) .subcommand(WalletNewKeyList::def()) .subcommand(WalletNewKeyFind::def()) @@ -510,10 +509,10 @@ pub mod cmds { .subcommand(WalletNewAddressFind::def()) .subcommand(WalletNewExportKey::def()) .subcommand(WalletNewKeyAddressAdd::def()) + .subcommand(WalletNewPaymentAddressGen::def()) } fn parse(matches: &ArgMatches) -> Option { - let masp = SubCmd::parse(matches).map(Self::Masp); let gen_new = SubCmd::parse(matches).map(Self::NewGen); let derive_new = SubCmd::parse(matches).map(Self::NewDerive); let key_list_new = SubCmd::parse(matches).map(Self::NewKeyList); @@ -522,7 +521,8 @@ pub mod cmds { let addr_find_new = SubCmd::parse(matches).map(Self::NewAddrFind); let export_new = SubCmd::parse(matches).map(Self::NewKeyExport); let key_addr_add = SubCmd::parse(matches).map(Self::NewKeyAddrAdd); - masp.or(gen_new) + let pay_addr_gen = SubCmd::parse(matches).map(Self::NewPayAddrGen); + gen_new .or(derive_new) .or(key_list_new) .or(key_find_new) @@ -530,6 +530,7 @@ pub mod cmds { .or(addr_find_new) .or(export_new) .or(key_addr_add) + .or(pay_addr_gen) } } @@ -747,66 +748,19 @@ pub mod cmds { } } - #[allow(clippy::large_enum_variant)] - #[derive(Clone, Debug)] - pub enum WalletMasp { - GenPayAddr(MaspGenPayAddr), - } - - impl SubCmd for WalletMasp { - const CMD: &'static str = "masp"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).and_then(|matches| { - let genpa = SubCmd::parse(matches).map(Self::GenPayAddr); - genpa - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Multi-asset shielded pool address and keypair management \ - including methods to generate and look-up addresses and \ - keys.", - ) - .subcommand_required(true) - .arg_required_else_help(true) - .subcommand(MaspGenPayAddr::def()) - } - } - - /// Generate a spending key - #[derive(Clone, Debug)] - pub struct MaspGenSpendKey(pub args::MaspSpendKeyGen); - - impl SubCmd for MaspGenSpendKey { - const CMD: &'static str = "gen-key"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - MaspGenSpendKey(args::MaspSpendKeyGen::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Generates a random spending key") - .add_args::() - } - } - /// Generate a payment address from a viewing key or payment address #[derive(Clone, Debug)] - pub struct MaspGenPayAddr(pub args::MaspPayAddrGen); + pub struct WalletNewPaymentAddressGen( + pub args::PayAddressGen, + ); - impl SubCmd for MaspGenPayAddr { - const CMD: &'static str = "gen-addr"; + impl SubCmd for WalletNewPaymentAddressGen { + const CMD: &'static str = "gen-payment-addr"; fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - MaspGenPayAddr(args::MaspPayAddrGen::parse(matches)) - }) + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::PayAddressGen::parse(matches))) } fn def() -> App { @@ -814,23 +768,7 @@ pub mod cmds { .about( "Generates a payment address from the given spending key", ) - .add_args::>() - } - } - - /// List known addresses - #[derive(Clone, Debug)] - pub struct AddressList; - - impl SubCmd for AddressList { - const CMD: &'static str = "list"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|_| Self) - } - - fn def() -> App { - App::new(Self::CMD).about("List all known addresses.") + .add_args::>() } } @@ -6000,36 +5938,8 @@ pub mod args { } } - impl Args for MaspSpendKeyGen { - fn parse(matches: &ArgMatches) -> Self { - let alias = ALIAS.parse(matches); - let alias_force = ALIAS_FORCE.parse(matches); - let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); - Self { - alias, - alias_force, - unsafe_dont_encrypt, - } - } - - fn def(app: App) -> App { - app.arg( - ALIAS - .def() - .help("An alias to be associated with the spending key."), - ) - .arg(ALIAS_FORCE.def().help( - "Override the alias without confirmation if it already exists.", - )) - .arg(UNSAFE_DONT_ENCRYPT.def().help( - "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ - used in a live network.", - )) - } - } - - impl CliToSdk> for MaspPayAddrGen { - fn to_sdk(self, ctx: &mut Context) -> MaspPayAddrGen { + impl CliToSdk> for PayAddressGen { + fn to_sdk(self, ctx: &mut Context) -> PayAddressGen { use namada_sdk::wallet::Wallet; use crate::wallet::CliWalletUtils; @@ -6053,7 +5963,7 @@ pub mod args { } else { find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) }; - MaspPayAddrGen:: { + PayAddressGen:: { alias: self.alias, alias_force: self.alias_force, viewing_key, @@ -6062,7 +5972,7 @@ pub mod args { } } - impl Args for MaspPayAddrGen { + impl Args for PayAddressGen { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); @@ -6335,7 +6245,7 @@ pub mod args { .help("The bech32m encoded address string."), ) .group( - ArgGroup::new("find_flags") + ArgGroup::new("addr_find_args") .args([ALIAS_OPT.name, RAW_ADDRESS_OPT.name]) .required(true), ) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index e25707e454..311b067280 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -39,12 +39,6 @@ impl CliApi { io: &impl Io, ) -> Result<()> { match cmd { - cmds::NamadaWallet::Masp(sub) => match sub { - cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { - let args = args.to_sdk(&mut ctx); - payment_address_gen(ctx, io, args) - } - }, cmds::NamadaWallet::NewGen(cmds::WalletNewGen(args)) => { key_gen(ctx, io, args) } @@ -69,6 +63,12 @@ impl CliApi { cmds::NamadaWallet::NewKeyAddrAdd( cmds::WalletNewKeyAddressAdd(args), ) => key_address_add(ctx, io, args), + cmds::NamadaWallet::NewPayAddrGen( + cmds::WalletNewPaymentAddressGen(args), + ) => { + let args = args.to_sdk(&mut ctx); + payment_address_gen(ctx, io, args) + } } Ok(()) } @@ -210,13 +210,13 @@ fn spending_key_gen( fn payment_address_gen( ctx: Context, io: &impl Io, - args::MaspPayAddrGen { + args::PayAddressGen { alias, alias_force, viewing_key, pin, .. - }: args::MaspPayAddrGen, + }: args::PayAddressGen, ) { let mut wallet = load_wallet(ctx); let alias = alias.to_lowercase(); diff --git a/sdk/src/args.rs b/sdk/src/args.rs index e2e595b166..5be034817d 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2040,43 +2040,6 @@ impl TxBuilder for Tx { } } -/// MASP add key or address arguments -#[derive(Clone, Debug)] -pub struct MaspAddrKeyAdd { - /// Key alias - pub alias: String, - /// Whether to force overwrite the alias - pub alias_force: bool, - /// Any MASP value - pub value: MaspValue, - /// Don't encrypt the keypair - pub unsafe_dont_encrypt: bool, -} - -/// MASP generate spending key arguments -#[derive(Clone, Debug)] -pub struct MaspSpendKeyGen { - /// Key alias - pub alias: String, - /// Whether to force overwrite the alias - pub alias_force: bool, - /// Don't encrypt the keypair - pub unsafe_dont_encrypt: bool, -} - -/// MASP generate payment address arguments -#[derive(Clone, Debug)] -pub struct MaspPayAddrGen { - /// Key alias - pub alias: String, - /// Whether to force overwrite the alias - pub alias_force: bool, - /// Viewing key - pub viewing_key: C::ViewingKey, - /// Pin - pub pin: bool, -} - /// Wallet generate key and implicit address arguments #[derive(Clone, Debug)] pub struct KeyGen { @@ -2115,8 +2078,7 @@ pub struct KeyDerive { pub use_device: bool, } -/// Wallet find shielded address or key arguments -/// TODO Wallet key lookup arguments +/// Wallet key lookup arguments #[derive(Clone, Debug)] pub struct KeyFind { /// Whether to find shielded address by alias @@ -2150,7 +2112,7 @@ pub struct KeyList { pub unsafe_show_secret: bool, } -/// List transparent wallet addresses / shielded payment addresses +/// List addresses arguments #[derive(Clone, Debug)] pub struct AddressList { /// Whether to list payment addresses of the shielded pool @@ -2188,6 +2150,19 @@ pub struct KeyAddressAdd { pub unsafe_dont_encrypt: bool, } +/// Generate payment address arguments +#[derive(Clone, Debug)] +pub struct PayAddressGen { + /// Key alias + pub alias: String, + /// Whether to force overwrite the alias + pub alias_force: bool, + /// Viewing key + pub viewing_key: C::ViewingKey, + /// Pin + pub pin: bool, +} + /// Bridge pool batch recommendation. #[derive(Clone, Debug)] pub struct RecommendBatch { From 1260d981b81b9745fb21714151d352e31ef90bf9 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 11:40:57 +0100 Subject: [PATCH 08/46] Restore raw key generation functionality --- apps/src/lib/cli/wallet.rs | 49 ++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 311b067280..fa66a9a22c 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -424,6 +424,7 @@ fn transparent_key_and_address_gen( io: &impl Io, args::KeyGen { scheme, + raw, alias, alias_force, unsafe_dont_encrypt, @@ -434,23 +435,30 @@ fn transparent_key_and_address_gen( let mut wallet = load_wallet(ctx); let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); - let derivation_path = decode_derivation_path(scheme, derivation_path) + let alias = if raw { + wallet.gen_store_secret_key( + scheme, + alias, + alias_force, + encryption_password, + &mut OsRng, + ) + } else { + let derivation_path = decode_derivation_path(scheme, derivation_path) + .unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + cli::safe_exit(1) + }); + let (_mnemonic, seed) = Wallet::::gen_hd_seed( + None, + &mut OsRng, + unsafe_dont_encrypt, + ) .unwrap_or_else(|err| { edisplay_line!(io, "{}", err); cli::safe_exit(1) }); - let mut rng = OsRng; - let (_mnemonic, seed) = Wallet::::gen_hd_seed( - None, - &mut rng, - unsafe_dont_encrypt, - ) - .unwrap_or_else(|err| { - edisplay_line!(io, "{}", err); - cli::safe_exit(1) - }); - let alias = wallet - .derive_store_hd_secret_key( + wallet.derive_store_hd_secret_key( scheme, alias, alias_force, @@ -458,12 +466,13 @@ fn transparent_key_and_address_gen( derivation_path, encryption_password, ) - .map(|x| x.0) - .unwrap_or_else(|err| { - eprintln!("{}", err); - println!("No changes are persisted. Exiting."); - cli::safe_exit(0); - }); + } + .map(|x| x.0) + .unwrap_or_else(|err| { + eprintln!("{}", err); + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); wallet .save() .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); @@ -474,7 +483,7 @@ fn transparent_key_and_address_gen( ); } -/// TODO +/// Key generation fn key_gen(ctx: Context, io: &impl Io, args_key_gen: args::KeyGen) { if !args_key_gen.shielded { transparent_key_and_address_gen(ctx, io, args_key_gen) From 827fbc03fe1abb3632861a4c33c626748ce02c86 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 12:20:36 +0100 Subject: [PATCH 09/46] Rename structures --- apps/src/lib/cli.rs | 94 +++++++++++++++++++------------------- apps/src/lib/cli/wallet.rs | 36 +++++++-------- 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 420a6c91e9..dacf50d439 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -480,48 +480,48 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum NamadaWallet { /// Key generation - NewGen(WalletNewGen), + KeyGen(WalletGen), /// Key derivation - NewDerive(WalletNewDerive), + KeyDerive(WalletDerive), /// Key list - NewKeyList(WalletNewKeyList), + KeyList(WalletListKeys), /// Key search - NewKeyFind(WalletNewKeyFind), + KeyFind(WalletFindKeys), /// Address list - NewAddrList(WalletNewAddressList), + AddrList(WalletListAddresses), /// Address search - NewAddrFind(WalletNewAddressFind), + AddrFind(WalletFindAddresses), /// Key export - NewKeyExport(WalletNewExportKey), + KeyExport(WalletExportKey), /// Key import - NewKeyAddrAdd(WalletNewKeyAddressAdd), + KeyAddrAdd(WalletAddKeyAddress), /// Payment address generation - NewPayAddrGen(WalletNewPaymentAddressGen), + PayAddrGen(WalletGenPaymentAddress), } impl Cmd for NamadaWallet { fn add_sub(app: App) -> App { - app.subcommand(WalletNewGen::def()) - .subcommand(WalletNewDerive::def()) - .subcommand(WalletNewKeyList::def()) - .subcommand(WalletNewKeyFind::def()) - .subcommand(WalletNewAddressList::def()) - .subcommand(WalletNewAddressFind::def()) - .subcommand(WalletNewExportKey::def()) - .subcommand(WalletNewKeyAddressAdd::def()) - .subcommand(WalletNewPaymentAddressGen::def()) + app.subcommand(WalletGen::def()) + .subcommand(WalletDerive::def()) + .subcommand(WalletListKeys::def()) + .subcommand(WalletFindKeys::def()) + .subcommand(WalletListAddresses::def()) + .subcommand(WalletFindAddresses::def()) + .subcommand(WalletExportKey::def()) + .subcommand(WalletAddKeyAddress::def()) + .subcommand(WalletGenPaymentAddress::def()) } fn parse(matches: &ArgMatches) -> Option { - let gen_new = SubCmd::parse(matches).map(Self::NewGen); - let derive_new = SubCmd::parse(matches).map(Self::NewDerive); - let key_list_new = SubCmd::parse(matches).map(Self::NewKeyList); - let key_find_new = SubCmd::parse(matches).map(Self::NewKeyFind); - let addr_list_new = SubCmd::parse(matches).map(Self::NewAddrList); - let addr_find_new = SubCmd::parse(matches).map(Self::NewAddrFind); - let export_new = SubCmd::parse(matches).map(Self::NewKeyExport); - let key_addr_add = SubCmd::parse(matches).map(Self::NewKeyAddrAdd); - let pay_addr_gen = SubCmd::parse(matches).map(Self::NewPayAddrGen); + let gen_new = SubCmd::parse(matches).map(Self::KeyGen); + let derive_new = SubCmd::parse(matches).map(Self::KeyDerive); + let key_list_new = SubCmd::parse(matches).map(Self::KeyList); + let key_find_new = SubCmd::parse(matches).map(Self::KeyFind); + let addr_list_new = SubCmd::parse(matches).map(Self::AddrList); + let addr_find_new = SubCmd::parse(matches).map(Self::AddrFind); + let export_new = SubCmd::parse(matches).map(Self::KeyExport); + let key_addr_add = SubCmd::parse(matches).map(Self::KeyAddrAdd); + let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); gen_new .or(derive_new) .or(key_list_new) @@ -557,9 +557,9 @@ pub mod cmds { /// address derived from it. In the shielded setting, generate a new /// spending key. #[derive(Clone, Debug)] - pub struct WalletNewGen(pub args::KeyGen); + pub struct WalletGen(pub args::KeyGen); - impl SubCmd for WalletNewGen { + impl SubCmd for WalletGen { const CMD: &'static str = "gen"; fn parse(matches: &ArgMatches) -> Option { @@ -584,9 +584,9 @@ pub mod cmds { /// the mnemonic code. /// In the shielded setting, derive a spending key from the mnemonic code. #[derive(Clone, Debug)] - pub struct WalletNewDerive(pub args::KeyDerive); + pub struct WalletDerive(pub args::KeyDerive); - impl SubCmd for WalletNewDerive { + impl SubCmd for WalletDerive { const CMD: &'static str = "derive"; fn parse(matches: &ArgMatches) -> Option { @@ -613,9 +613,9 @@ pub mod cmds { /// TODO List all known shielded keys #[derive(Clone, Debug)] - pub struct WalletNewKeyList(pub args::KeyList); + pub struct WalletListKeys(pub args::KeyList); - impl SubCmd for WalletNewKeyList { + impl SubCmd for WalletListKeys { const CMD: &'static str = "list-keys"; fn parse(matches: &ArgMatches) -> Option { @@ -637,10 +637,10 @@ pub mod cmds { /// TODO Find a keypair in the wallet store /// TODO Find the given shielded address or key #[derive(Clone, Debug)] - pub struct WalletNewKeyFind(pub args::KeyFind); + pub struct WalletFindKeys(pub args::KeyFind); - impl SubCmd for WalletNewKeyFind { - const CMD: &'static str = "find-key"; + impl SubCmd for WalletFindKeys { + const CMD: &'static str = "find-keys"; fn parse(matches: &ArgMatches) -> Option { matches @@ -662,9 +662,9 @@ pub mod cmds { /// List known addresses /// TODO List all known payment addresses #[derive(Clone, Debug)] - pub struct WalletNewAddressList(pub args::AddressList); + pub struct WalletListAddresses(pub args::AddressList); - impl SubCmd for WalletNewAddressList { + impl SubCmd for WalletListAddresses { const CMD: &'static str = "list-addr"; fn parse(matches: &ArgMatches) -> Option { @@ -685,9 +685,9 @@ pub mod cmds { /// Find an address by its alias #[derive(Clone, Debug)] - pub struct WalletNewAddressFind(pub args::AddressFind); + pub struct WalletFindAddresses(pub args::AddressFind); - impl SubCmd for WalletNewAddressFind { + impl SubCmd for WalletFindAddresses { const CMD: &'static str = "find-addr"; fn parse(matches: &ArgMatches) -> Option { @@ -707,9 +707,9 @@ pub mod cmds { /// Export key #[derive(Clone, Debug)] - pub struct WalletNewExportKey(pub args::KeyExport); + pub struct WalletExportKey(pub args::KeyExport); - impl SubCmd for WalletNewExportKey { + impl SubCmd for WalletExportKey { const CMD: &'static str = "export"; fn parse(matches: &ArgMatches) -> Option { @@ -727,9 +727,9 @@ pub mod cmds { /// Add public / payment address to the wallet #[derive(Clone, Debug)] - pub struct WalletNewKeyAddressAdd(pub args::KeyAddressAdd); + pub struct WalletAddKeyAddress(pub args::KeyAddressAdd); - impl SubCmd for WalletNewKeyAddressAdd { + impl SubCmd for WalletAddKeyAddress { const CMD: &'static str = "add"; fn parse(matches: &ArgMatches) -> Option { @@ -750,11 +750,9 @@ pub mod cmds { /// Generate a payment address from a viewing key or payment address #[derive(Clone, Debug)] - pub struct WalletNewPaymentAddressGen( - pub args::PayAddressGen, - ); + pub struct WalletGenPaymentAddress(pub args::PayAddressGen); - impl SubCmd for WalletNewPaymentAddressGen { + impl SubCmd for WalletGenPaymentAddress { const CMD: &'static str = "gen-payment-addr"; fn parse(matches: &ArgMatches) -> Option { diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index fa66a9a22c..6dd98d47dc 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -39,33 +39,33 @@ impl CliApi { io: &impl Io, ) -> Result<()> { match cmd { - cmds::NamadaWallet::NewGen(cmds::WalletNewGen(args)) => { + cmds::NamadaWallet::KeyGen(cmds::WalletGen(args)) => { key_gen(ctx, io, args) } - cmds::NamadaWallet::NewDerive(cmds::WalletNewDerive(args)) => { + cmds::NamadaWallet::KeyDerive(cmds::WalletDerive(args)) => { key_derive(ctx, io, args).await } - cmds::NamadaWallet::NewKeyList(cmds::WalletNewKeyList(args)) => { + cmds::NamadaWallet::KeyList(cmds::WalletListKeys(args)) => { key_list(ctx, io, args) } - cmds::NamadaWallet::NewKeyFind(cmds::WalletNewKeyFind(args)) => { + cmds::NamadaWallet::KeyFind(cmds::WalletFindKeys(args)) => { key_find(ctx, io, args) } - cmds::NamadaWallet::NewAddrList(cmds::WalletNewAddressList( - args, - )) => address_list(ctx, io, args), - cmds::NamadaWallet::NewAddrFind(cmds::WalletNewAddressFind( - args, - )) => address_or_alias_find(ctx, io, args), - cmds::NamadaWallet::NewKeyExport(cmds::WalletNewExportKey( + cmds::NamadaWallet::AddrList(cmds::WalletListAddresses(args)) => { + address_list(ctx, io, args) + } + cmds::NamadaWallet::AddrFind(cmds::WalletFindAddresses(args)) => { + address_or_alias_find(ctx, io, args) + } + cmds::NamadaWallet::KeyExport(cmds::WalletExportKey(args)) => { + key_export(ctx, io, args) + } + cmds::NamadaWallet::KeyAddrAdd(cmds::WalletAddKeyAddress(args)) => { + key_address_add(ctx, io, args) + } + cmds::NamadaWallet::PayAddrGen(cmds::WalletGenPaymentAddress( args, - )) => key_export(ctx, io, args), - cmds::NamadaWallet::NewKeyAddrAdd( - cmds::WalletNewKeyAddressAdd(args), - ) => key_address_add(ctx, io, args), - cmds::NamadaWallet::NewPayAddrGen( - cmds::WalletNewPaymentAddressGen(args), - ) => { + )) => { let args = args.to_sdk(&mut ctx); payment_address_gen(ctx, io, args) } From db564107b20531018bbff5c57c53ee2bb8b87407 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 12:24:50 +0100 Subject: [PATCH 10/46] Refactoring --- apps/src/lib/cli.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index dacf50d439..09a28899f9 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -513,22 +513,21 @@ pub mod cmds { } fn parse(matches: &ArgMatches) -> Option { - let gen_new = SubCmd::parse(matches).map(Self::KeyGen); - let derive_new = SubCmd::parse(matches).map(Self::KeyDerive); - let key_list_new = SubCmd::parse(matches).map(Self::KeyList); - let key_find_new = SubCmd::parse(matches).map(Self::KeyFind); - let addr_list_new = SubCmd::parse(matches).map(Self::AddrList); - let addr_find_new = SubCmd::parse(matches).map(Self::AddrFind); - let export_new = SubCmd::parse(matches).map(Self::KeyExport); + let gen = SubCmd::parse(matches).map(Self::KeyGen); + let derive = SubCmd::parse(matches).map(Self::KeyDerive); + let key_list = SubCmd::parse(matches).map(Self::KeyList); + let key_find = SubCmd::parse(matches).map(Self::KeyFind); + let addr_list = SubCmd::parse(matches).map(Self::AddrList); + let addr_find = SubCmd::parse(matches).map(Self::AddrFind); + let export = SubCmd::parse(matches).map(Self::KeyExport); let key_addr_add = SubCmd::parse(matches).map(Self::KeyAddrAdd); let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); - gen_new - .or(derive_new) - .or(key_list_new) - .or(key_find_new) - .or(addr_list_new) - .or(addr_find_new) - .or(export_new) + gen.or(derive) + .or(key_list) + .or(key_find) + .or(addr_list) + .or(addr_find) + .or(export) .or(key_addr_add) .or(pay_addr_gen) } From fdcd859cae3fecfa2b1b2572cc53bc4e5116ff5c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 12:54:16 +0100 Subject: [PATCH 11/46] Make `--alias` obligatory for gen / derive --- apps/src/lib/cli.rs | 16 +++++++++------- apps/src/lib/cli/wallet.rs | 14 ++++---------- sdk/src/args.rs | 4 ++-- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 09a28899f9..59ac8375c3 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -571,9 +571,11 @@ pub mod cmds { App::new(Self::CMD) .about("Generates a new transparent / shielded secret key.") .long_about( - "Generates a keypair with a given alias and derives the \ - implicit address from its public key. The address will \ - be stored with the same alias. TODO shielded", + "In the transparent setting, generates a keypair with a \ + given alias and derives the implicit address from its \ + public key. The address will be stored with the same \ + alias. In the shielded setting, generates a new spending \ + key.", ) .add_args::() } @@ -6004,7 +6006,7 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let scheme = SCHEME.parse(matches); let shielded = SHIELDED.parse(matches); - let alias = ALIAS_OPT.parse(matches); + let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let use_device = USE_DEVICE.parse(matches); @@ -6032,7 +6034,7 @@ pub mod args { .def() .help("Derive a spending key for the shielded pool."), ) - .arg(ALIAS_OPT.def().help( + .arg(ALIAS.def().help( "The key and address alias. If none provided, the alias will \ be the public key hash.", )) @@ -6065,7 +6067,7 @@ pub mod args { let scheme = SCHEME.parse(matches); let shielded = SHIELDED.parse(matches); let raw = RAW_KEY_GEN.parse(matches); - let alias = ALIAS_OPT.parse(matches); + let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let derivation_path = HD_WALLET_DERIVATION_PATH.parse(matches); @@ -6101,7 +6103,7 @@ pub mod args { mnemonic code is generated.", ), ) - .arg(ALIAS_OPT.def().help( + .arg(ALIAS.def().help( "The key and address alias. If none provided, the alias will \ be the public key hash.", )) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 6dd98d47dc..edc9d0192a 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -186,11 +186,6 @@ fn spending_key_gen( .. }: args::KeyGen, ) { - let alias = alias.unwrap_or_else(|| { - display_line!(io, "Missing alias."); - display_line!(io, "No changes are persisted. Exiting."); - cli::safe_exit(1) - }); let mut wallet = load_wallet(ctx); let alias = alias.to_lowercase(); let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); @@ -347,7 +342,7 @@ async fn transparent_key_and_address_derive( wallet .derive_key_from_mnemonic_code( scheme, - alias, + Some(alias), alias_force, derivation_path, None, @@ -390,13 +385,12 @@ async fn transparent_key_and_address_derive( let pubkey = common::PublicKey::try_from_slice(&response.public_key) .expect("unable to decode public key from hardware wallet"); - let pkh = PublicKeyHash::from(&pubkey); let address = Address::from_str(&response.address_str) .expect("unable to decode address from hardware wallet"); wallet .insert_public_key( - alias.unwrap_or_else(|| pkh.to_string()), + alias, pubkey, Some(address), Some(derivation_path), @@ -438,7 +432,7 @@ fn transparent_key_and_address_gen( let alias = if raw { wallet.gen_store_secret_key( scheme, - alias, + Some(alias), alias_force, encryption_password, &mut OsRng, @@ -460,7 +454,7 @@ fn transparent_key_and_address_gen( }); wallet.derive_store_hd_secret_key( scheme, - alias, + Some(alias), alias_force, seed, derivation_path, diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 5be034817d..39a18e07c4 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2050,7 +2050,7 @@ pub struct KeyGen { /// Whether to generate a raw non-hd key pub raw: bool, /// Key alias - pub alias: Option, + pub alias: String, /// Whether to force overwrite the alias, if provided pub alias_force: bool, /// Don't encrypt the keypair @@ -2067,7 +2067,7 @@ pub struct KeyDerive { /// Whether to generate a spending key for the shielded pool pub shielded: bool, /// Key alias - pub alias: Option, + pub alias: String, /// Whether to force overwrite the alias, if provided pub alias_force: bool, /// Don't encrypt the keypair From 5a9f794e7d565f8e1d0ff4a28cc3e34a92d2f7e6 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 13:28:04 +0100 Subject: [PATCH 12/46] Fix: normalize alias strings --- apps/src/lib/cli/wallet.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index edc9d0192a..199044e896 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -336,6 +336,7 @@ async fn transparent_key_and_address_derive( edisplay_line!(io, "{}", err); cli::safe_exit(1) }); + let alias = alias.to_lowercase(); let alias = if !use_device { let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); @@ -426,6 +427,7 @@ fn transparent_key_and_address_gen( .. }: args::KeyGen, ) { + let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); @@ -604,6 +606,7 @@ fn shielded_key_address_find( }: args::KeyFind, ) { let mut wallet = load_wallet(ctx); + // TODO let alias = alias.unwrap_or_else(|| { display_line!(io, "Missing alias."); display_line!(io, "No changes are persisted. Exiting."); @@ -712,12 +715,13 @@ fn key_export( io: &impl Io, args::KeyExport { alias }: args::KeyExport, ) { + let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); wallet - .find_secret_key(alias.to_lowercase(), None) + .find_secret_key(&alias, None) .map(|keypair| { let file_data = keypair.serialize_to_vec(); - let file_name = format!("key_{}", alias.to_lowercase()); + let file_name = format!("key_{}", alias); let mut file = File::create(&file_name).unwrap(); file.write_all(file_data.as_ref()).unwrap(); @@ -771,14 +775,15 @@ fn address_or_alias_find( message." ); } else if alias.is_some() { - if let Some(address) = wallet.find_address(alias.as_ref().unwrap()) { + let alias = alias.unwrap().to_lowercase(); + if let Some(address) = wallet.find_address(&alias) { display_line!(io, "Found address {}", address.to_pretty_string()); } else { display_line!( io, "No address with alias {} found. Use the command `address \ list` to see all the known addresses.", - alias.unwrap().to_lowercase() + alias ); } } else if address.is_some() { @@ -806,10 +811,11 @@ fn transparent_address_add( .. }: args::KeyAddressAdd, ) { + let alias = alias.to_lowercase(); let address = address.unwrap(); // this should not fail let mut wallet = load_wallet(ctx); if wallet - .insert_address(alias.to_lowercase(), address, alias_force) + .insert_address(&alias, address, alias_force) .is_none() { edisplay_line!(io, "Address not added"); @@ -821,7 +827,7 @@ fn transparent_address_add( display_line!( io, "Successfully added a key and an address with alias: \"{}\"", - alias.to_lowercase() + alias ); } From f385aef86c95d0d6204cf126076507ab0b4f436c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 13:31:37 +0100 Subject: [PATCH 13/46] Add todo --- apps/src/lib/cli/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 199044e896..ce8e3a26e0 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -162,8 +162,8 @@ fn payment_addresses_list( if known_addresses.is_empty() { display_line!( io, - "No known payment addresses. Try `masp gen-addr --alias my-addr` \ - to generate a new payment address.", + "TODO No known payment addresses. Try `masp gen-addr --alias \ + my-addr` to generate a new payment address.", ); } else { let stdout = io::stdout(); From 34dd9756359a8e612383d1a0b5a289f402bdbe49 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 13:32:00 +0100 Subject: [PATCH 14/46] Fix output message --- apps/src/lib/cli/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index ce8e3a26e0..cb29ac48c5 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -745,8 +745,8 @@ fn transparent_addresses_list( if known_addresses.is_empty() { display_line!( io, - "No known addresses. Try `address gen --alias my-addr` to \ - generate a new implicit address.", + "No known addresses. Try `gen --alias my-addr` to generate a new \ + implicit address.", ); } else { let stdout = io::stdout(); From 6f69e38762b4f431fddcb4b6154faddf83730bde Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 13:59:28 +0100 Subject: [PATCH 15/46] Update help messages for `gen` and `derive` --- apps/src/lib/cli.rs | 37 ++++++++++++++++++------------------- apps/src/lib/cli/wallet.rs | 2 +- sdk/src/args.rs | 2 +- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 59ac8375c3..9cb49f0539 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -574,8 +574,10 @@ pub mod cmds { "In the transparent setting, generates a keypair with a \ given alias and derives the implicit address from its \ public key. The address will be stored with the same \ - alias. In the shielded setting, generates a new spending \ - key.", + alias.\nIn the shielded setting, generates a new \ + spending key with a given alias.\nIn both settings, by \ + default, an HD-key with a default derivation path is \ + generated, with a random mnemonic code.", ) .add_args::() } @@ -599,14 +601,17 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about( - "Derive transparent / shielded key from the mnemonic code.", + "Derive transparent / shielded key from the mnemonic code \ + or a seed stored on the hardware wallet device.", ) .long_about( - "Derives a keypair from the given mnemonic code and HD \ - derivation path and derives the implicit address from \ - its public key. Stores the keypair and the address with \ - the given alias. A hardware wallet can be used, in which \ - case a private key is not derivable. TODO shielded", + "In the transparent setting, derives a keypair from the \ + given mnemonic code and HD derivation path and derives \ + the implicit address from its public key. Stores the \ + keypair and the address with the given alias.\nIn the \ + shielded setting, derives a spending key.\nA hardware \ + wallet can be used, in which case the private key is not \ + derivable.", ) .add_args::() } @@ -6034,10 +6039,7 @@ pub mod args { .def() .help("Derive a spending key for the shielded pool."), ) - .arg(ALIAS.def().help( - "The key and address alias. If none provided, the alias will \ - be the public key hash.", - )) + .arg(ALIAS.def().help("The key and address alias.")) .arg( ALIAS_FORCE .def() @@ -6103,10 +6105,7 @@ pub mod args { mnemonic code is generated.", ), ) - .arg(ALIAS.def().help( - "The key and address alias. If none provided, the alias will \ - be the public key hash.", - )) + .arg(ALIAS.def().help("The key and address alias.")) .arg(ALIAS_FORCE.def().help( "Override the alias without confirmation if it already exists.", )) @@ -6115,12 +6114,12 @@ pub mod args { used in a live network.", )) .arg(HD_WALLET_DERIVATION_PATH.def().help( - "Generate a new key and wallet using BIP39 mnemonic code and \ - HD derivation path. Use keyword `default` to refer to a \ + "HD key derivation path. Use keyword `default` to refer to a \ scheme default path:\n- m/44'/60'/0'/0/0 for secp256k1 \ scheme\n- m/44'/877'/0'/0'/0' for ed25519 scheme.\nFor \ ed25519, all path indices will be promoted to hardened \ - indexes. TODO", + indexes. If none is specified, the scheme default path is \ + used.", )) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index cb29ac48c5..84f5c6b1e9 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -488,7 +488,7 @@ fn key_gen(ctx: Context, io: &impl Io, args_key_gen: args::KeyGen) { } } -/// TODO +/// HD key derivation from mnemonic code async fn key_derive( ctx: Context, io: &impl Io, diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 39a18e07c4..bde96938c4 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2055,7 +2055,7 @@ pub struct KeyGen { pub alias_force: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, - /// BIP44 derivation path + /// BIP44 / ZIP32 derivation path pub derivation_path: String, } From 8faa98be841b288992c56ae54328a0067ec15aaf Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 15:41:43 +0100 Subject: [PATCH 16/46] Implement export for MASP spending keys --- apps/src/lib/cli.rs | 10 +++++++--- apps/src/lib/cli/wallet.rs | 38 ++++++++++++++++++++++---------------- sdk/src/args.rs | 2 ++ 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 9cb49f0539..e8bc411318 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -726,7 +726,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Exports a keypair to a file.") + .about("Exports a keypair / spending key to a file.") .add_args::() } } @@ -6298,14 +6298,18 @@ pub mod args { impl Args for KeyExport { fn parse(matches: &ArgMatches) -> Self { + let shielded = SHIELDED.parse(matches); let alias = ALIAS.parse(matches); - Self { alias } + Self { shielded, alias } } fn def(app: App) -> App { app.arg( - ALIAS.def().help("The alias of the key you wish to export."), + SHIELDED + .def() + .help("Whether to export the shielded spending key."), ) + .arg(ALIAS.def().help("The alias of the key you wish to export.")) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 84f5c6b1e9..1c8f1ddd15 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -709,28 +709,34 @@ fn transparent_keys_list( } } -/// Export a transparent keypair to a file. +/// Export a transparent keypair / MASP spending key to a file. fn key_export( ctx: Context, io: &impl Io, - args::KeyExport { alias }: args::KeyExport, + args::KeyExport { shielded, alias }: args::KeyExport, ) { let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); - wallet - .find_secret_key(&alias, None) - .map(|keypair| { - let file_data = keypair.serialize_to_vec(); - let file_name = format!("key_{}", alias); - let mut file = File::create(&file_name).unwrap(); - - file.write_all(file_data.as_ref()).unwrap(); - display_line!(io, "Exported to file {}", file_name); - }) - .unwrap_or_else(|err| { - edisplay_line!(io, "{}", err); - cli::safe_exit(1) - }) + if !shielded { + wallet + .find_secret_key(&alias, None) + .map(|sk| Box::new(sk) as Box) + } else { + wallet + .find_spending_key(&alias, None) + .map(|spk| Box::new(spk) as Box) + } + .map(|key| { + let file_data = key.serialize_to_vec(); + let file_name = format!("key_{}", alias); + let mut file = File::create(&file_name).unwrap(); + file.write_all(file_data.as_ref()).unwrap(); + display_line!(io, "Exported to file {}", file_name); + }) + .unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + cli::safe_exit(1) + }) } /// List all known transparent addresses. diff --git a/sdk/src/args.rs b/sdk/src/args.rs index bde96938c4..0b57fe9c0e 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2131,6 +2131,8 @@ pub struct AddressFind { /// Wallet key export arguments #[derive(Clone, Debug)] pub struct KeyExport { + /// Whether to export a MASP spending key + pub shielded: bool, /// Key alias pub alias: String, } From 4919903f987b7f1a553cd27aa56231a1cfccec9b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 22 Nov 2023 15:53:19 +0100 Subject: [PATCH 17/46] Improve some messages, comments --- apps/src/lib/cli.rs | 5 ++++- apps/src/lib/cli/wallet.rs | 2 +- sdk/src/args.rs | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index e8bc411318..229d5a48ff 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -726,7 +726,10 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Exports a keypair / spending key to a file.") + .about( + "Exports a transparent keypair / shielded spending key to \ + a file.", + ) .add_args::() } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 1c8f1ddd15..c3ff6287b2 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -357,7 +357,7 @@ async fn transparent_key_and_address_derive( .0 } else { let hidapi = HidApi::new().unwrap_or_else(|err| { - edisplay_line!(io, "Failed to create Hidapi: {}", err); + edisplay_line!(io, "Failed to create HidApi: {}", err); cli::safe_exit(1) }); let app = NamadaApp::new( diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 0b57fe9c0e..33d90ff519 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2064,7 +2064,7 @@ pub struct KeyGen { pub struct KeyDerive { /// Scheme type pub scheme: SchemeType, - /// Whether to generate a spending key for the shielded pool + /// Whether to generate a MASP spending key pub shielded: bool, /// Key alias pub alias: String, @@ -2081,7 +2081,7 @@ pub struct KeyDerive { /// Wallet key lookup arguments #[derive(Clone, Debug)] pub struct KeyFind { - /// Whether to find shielded address by alias + /// Whether to find a MASP address by alias pub shielded: bool, /// Key alias to lookup keypair with pub alias: Option, @@ -2104,7 +2104,7 @@ pub struct KeyFind { /// Wallet list keys arguments #[derive(Clone, Debug)] pub struct KeyList { - /// Whether to list spending keys of the shielded pool + /// Whether to list MASP spending keys pub shielded: bool, /// Don't decrypt keys pub decrypt: bool, @@ -2115,7 +2115,7 @@ pub struct KeyList { /// List addresses arguments #[derive(Clone, Debug)] pub struct AddressList { - /// Whether to list payment addresses of the shielded pool + /// Whether to list MASP payment addresses pub shielded: bool, } From a5b5382507b1bba4d08d960e3e854808ca6f872e Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 23 Nov 2023 16:39:14 +0100 Subject: [PATCH 18/46] Implement raw key add; simplify cli for add command --- apps/src/lib/cli.rs | 47 +++++++--------- apps/src/lib/cli/wallet.rs | 111 ++++++++++++++++++++++++++----------- sdk/src/args.rs | 7 +-- 3 files changed, 103 insertions(+), 62 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 229d5a48ff..eeefac52b7 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -749,10 +749,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about( - "Adds the given transparent / payment address or key to \ - the wallet.", - ) + .about("Adds the given key or address to the wallet.") .add_args::() } } @@ -2986,7 +2983,8 @@ pub mod args { arg_opt("eth-cold-key"); pub const VALIDATOR_ETH_HOT_KEY: ArgOpt = arg_opt("eth-hot-key"); - pub const VALUE: ArgOpt = arg_opt("value"); + pub const VALUE: Arg = arg("value"); + pub const VALUE_OPT: ArgOpt = VALUE.opt(); pub const VIEWING_KEY: Arg = arg("key"); pub const VP: ArgOpt = arg_opt("vp"); pub const WALLET_ALIAS_FORCE: ArgFlag = flag("wallet-alias-force"); @@ -6159,7 +6157,7 @@ pub mod args { let shielded = SHIELDED.parse(matches); let alias = ALIAS_OPT.parse(matches); let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); - let value = VALUE.parse(matches); + let value = VALUE_OPT.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { @@ -6176,7 +6174,10 @@ pub mod args { SHIELDED .def() .help("Find spending key for the shielded pool.") - .conflicts_with_all([VALUE.name, RAW_PUBLIC_KEY_OPT.name]), + .conflicts_with_all([ + VALUE_OPT.name, + RAW_PUBLIC_KEY_OPT.name, + ]), ) .arg( ALIAS_OPT @@ -6185,7 +6186,7 @@ pub mod args { "TODO An alias associated with the keypair. The alias \ that is to be found.", ) - .conflicts_with(VALUE.name), + .conflicts_with(VALUE_OPT.name), ) .arg( RAW_PUBLIC_KEY_OPT @@ -6193,13 +6194,17 @@ pub mod args { .help("A public key associated with the keypair."), ) .arg( - VALUE + VALUE_OPT .def() .help("A public key or alias associated with the keypair."), ) .group( ArgGroup::new("key_find_args") - .args([ALIAS_OPT.name, RAW_PUBLIC_KEY_OPT.name, VALUE.name]) + .args([ + ALIAS_OPT.name, + RAW_PUBLIC_KEY_OPT.name, + VALUE_OPT.name, + ]) .required(true), ) .arg(PRE_GENESIS.def().help( @@ -6257,13 +6262,11 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); - let address = RAW_ADDRESS_OPT.parse(matches); - let value = MASP_VALUE_OPT.parse(matches); + let value = VALUE.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { alias, alias_force, - address, value, unsafe_dont_encrypt, } @@ -6278,20 +6281,12 @@ pub mod args { .arg(ALIAS_FORCE.def().help( "Override the alias without confirmation if it already exists.", )) - .arg( - RAW_ADDRESS_OPT - .def() - .help("The bech32m encoded transparent address string."), - ) - .arg(MASP_VALUE_OPT.def().help( - "A spending key, viewing key, or payment address of the \ - shielded pool.", + .arg(VALUE.def().help( + "Any value of the following:\n- transparent pool secret key \ + string\n- the bech32m encoded transparent address string\n- \ + shielded pool spending key\n- shielded pool viewing key\n- \ + shielded pool payment address ", )) - .group( - ArgGroup::new("key_address_add_args") - .args([RAW_ADDRESS_OPT.name, MASP_VALUE_OPT.name]) - .required(true), - ) .arg(UNSAFE_DONT_ENCRYPT.def().help( "UNSAFE: Do not encrypt the added keys. Do not use this for \ keys used in a live network.", diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index c3ff6287b2..126af2d4ca 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -12,7 +12,7 @@ use ledger_namada_rs::{BIP44Path, NamadaApp}; use ledger_transport_hid::hidapi::HidApi; use ledger_transport_hid::TransportNativeHID; use masp_primitives::zip32::ExtendedFullViewingKey; -use namada::types::address::Address; +use namada::types::address::{Address, DecodeError}; use namada::types::io::Io; use namada::types::key::*; use namada::types::masp::{MaspValue, PaymentAddress}; @@ -242,18 +242,14 @@ fn payment_address_gen( fn shielded_key_address_add( ctx: Context, io: &impl Io, - args::KeyAddressAdd { - alias, - alias_force, - value, - unsafe_dont_encrypt, - .. - }: args::KeyAddressAdd, + alias: String, + alias_force: bool, + masp_value: MaspValue, + unsafe_dont_encrypt: bool, ) { - let value = value.unwrap(); // this should not fail let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); - let (alias, typ) = match value { + let (alias, typ) = match masp_value { MaspValue::FullViewingKey(viewing_key) => { let alias = wallet .insert_viewing_key(alias, viewing_key, alias_force) @@ -501,7 +497,7 @@ async fn key_derive( } } -/// TODO +/// List keys fn key_list(ctx: Context, io: &impl Io, args_key_list: args::KeyList) { if !args_key_list.shielded { transparent_keys_list(ctx, io, args_key_list) @@ -510,7 +506,7 @@ fn key_list(ctx: Context, io: &impl Io, args_key_list: args::KeyList) { } } -/// TODO +/// Find key or address fn key_find(ctx: Context, io: &impl Io, args_key_find: args::KeyFind) { if !args_key_find.shielded { transparent_key_address_find(ctx, io, args_key_find) @@ -519,7 +515,7 @@ fn key_find(ctx: Context, io: &impl Io, args_key_find: args::KeyFind) { } } -/// TODO +/// List addresses fn address_list(ctx: Context, io: &impl Io, args_key_list: args::AddressList) { if !args_key_list.shielded { transparent_addresses_list(ctx, io, args_key_list) @@ -528,21 +524,78 @@ fn address_list(ctx: Context, io: &impl Io, args_key_list: args::AddressList) { } } -/// TODO +/// Value for add command +#[allow(clippy::large_enum_variant)] +#[derive(Debug)] +pub enum KeyAddrAddValue { + /// Transparent secret key + TranspSecretKey(common::SecretKey), + /// Transparent address + TranspAddress(Address), + /// Masp value + MASPValue(MaspValue), +} + +impl FromStr for KeyAddrAddValue { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + // Try to decode this value first as a secret key, then as an address, + // then as a MASP value + common::SecretKey::from_str(s) + .map(Self::TranspSecretKey) + .or_else(|_| Address::from_str(s).map(Self::TranspAddress)) + .or_else(|_| MaspValue::from_str(s).map(Self::MASPValue)) + } +} + +/// Add key or address fn key_address_add( ctx: Context, io: &impl Io, - args_key_addr_add: args::KeyAddressAdd, + args::KeyAddressAdd { + alias, + alias_force, + value, + unsafe_dont_encrypt, + .. + }: args::KeyAddressAdd, ) { - if args_key_addr_add.address.is_some() { - transparent_address_add(ctx, io, args_key_addr_add) - } else if args_key_addr_add.value.is_some() { - shielded_key_address_add(ctx, io, args_key_addr_add) - } else { - unreachable!( - "This should not happen as the group of address and value is \ - required." - ) + match KeyAddrAddValue::from_str(&value).unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }) { + KeyAddrAddValue::TranspSecretKey(sk) => { + let mut wallet = load_wallet(ctx); + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + wallet + .insert_keypair( + alias, + alias_force, + sk, + encryption_password, + None, + None, + ) + .unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1); + }); + } + KeyAddrAddValue::TranspAddress(address) => { + transparent_address_add(ctx, io, alias, alias_force, address) + } + KeyAddrAddValue::MASPValue(masp_value) => shielded_key_address_add( + ctx, + io, + alias, + alias_force, + masp_value, + unsafe_dont_encrypt, + ), } } @@ -810,15 +863,11 @@ fn address_or_alias_find( fn transparent_address_add( ctx: Context, io: &impl Io, - args::KeyAddressAdd { - alias, - alias_force, - address, - .. - }: args::KeyAddressAdd, + alias: String, + alias_force: bool, + address: Address, ) { let alias = alias.to_lowercase(); - let address = address.unwrap(); // this should not fail let mut wallet = load_wallet(ctx); if wallet .insert_address(&alias, address, alias_force) diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 33d90ff519..32d3c1eb50 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -13,7 +13,6 @@ use namada_core::types::dec::Dec; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::keccak::KeccakHash; use namada_core::types::key::{common, SchemeType}; -use namada_core::types::masp::MaspValue; use namada_core::types::storage::Epoch; use namada_core::types::time::DateTimeUtc; use namada_core::types::transaction::GasLimit; @@ -2144,10 +2143,8 @@ pub struct KeyAddressAdd { pub alias: String, /// Whether to force overwrite the alias pub alias_force: bool, - /// Transparent address to add - pub address: Option
, - /// Any shielded value - pub value: Option, + /// Any supported value + pub value: String, /// Don't encrypt the keys pub unsafe_dont_encrypt: bool, } From 999a42564d104d6d49d3f7047da60f6dcfd5c003 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 24 Nov 2023 15:00:18 +0100 Subject: [PATCH 19/46] Import key from file --- apps/src/lib/cli.rs | 60 +++++++++++++++++++++++++++++ apps/src/lib/cli/wallet.rs | 77 ++++++++++++++++++++++++++++++-------- sdk/src/args.rs | 13 +++++++ 3 files changed, 135 insertions(+), 15 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index eeefac52b7..14db8433af 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -494,6 +494,8 @@ pub mod cmds { /// Key export KeyExport(WalletExportKey), /// Key import + KeyImport(WalletImportKey), + /// Key / address add KeyAddrAdd(WalletAddKeyAddress), /// Payment address generation PayAddrGen(WalletGenPaymentAddress), @@ -508,6 +510,7 @@ pub mod cmds { .subcommand(WalletListAddresses::def()) .subcommand(WalletFindAddresses::def()) .subcommand(WalletExportKey::def()) + .subcommand(WalletImportKey::def()) .subcommand(WalletAddKeyAddress::def()) .subcommand(WalletGenPaymentAddress::def()) } @@ -520,6 +523,7 @@ pub mod cmds { let addr_list = SubCmd::parse(matches).map(Self::AddrList); let addr_find = SubCmd::parse(matches).map(Self::AddrFind); let export = SubCmd::parse(matches).map(Self::KeyExport); + let import = SubCmd::parse(matches).map(Self::KeyImport); let key_addr_add = SubCmd::parse(matches).map(Self::KeyAddrAdd); let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); gen.or(derive) @@ -528,6 +532,7 @@ pub mod cmds { .or(addr_list) .or(addr_find) .or(export) + .or(import) .or(key_addr_add) .or(pay_addr_gen) } @@ -734,6 +739,29 @@ pub mod cmds { } } + /// Import key from a file + #[derive(Clone, Debug)] + pub struct WalletImportKey(pub args::KeyImport); + + impl SubCmd for WalletImportKey { + const CMD: &'static str = "import"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| (Self(args::KeyImport::parse(matches)))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Imports a transparent keypair / shielded spending key \ + from a file.", + ) + .add_args::() + } + } + /// Add public / payment address to the wallet #[derive(Clone, Debug)] pub struct WalletAddKeyAddress(pub args::KeyAddressAdd); @@ -2856,6 +2884,7 @@ pub mod args { pub const FEE_AMOUNT_OPT: ArgOpt = arg_opt("gas-price"); pub const FEE_PAYER_OPT: ArgOpt = arg_opt("gas-payer"); + pub const FILE_PATH: Arg = arg("file"); pub const FORCE: ArgFlag = flag("force"); pub const GAS_LIMIT: ArgDefault = arg_default("gas-limit", DefaultFn(|| GasLimit::from(25_000))); @@ -6311,6 +6340,37 @@ pub mod args { } } + impl Args for KeyImport { + fn parse(matches: &ArgMatches) -> Self { + let file_path = FILE_PATH.parse(matches); + let alias = ALIAS.parse(matches); + let alias_force = ALIAS_FORCE.parse(matches); + let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); + Self { + alias, + alias_force, + file_path, + unsafe_dont_encrypt, + } + } + + fn def(app: App) -> App { + app.arg(FILE_PATH.def().help( + "Path to the file containing the key you wish to import.", + )) + .arg(ALIAS.def().help("The alias assigned to the.")) + .arg( + ALIAS_FORCE + .def() + .help("An alias to be associated with the imported entry."), + ) + .arg(UNSAFE_DONT_ENCRYPT.def().help( + "UNSAFE: Do not encrypt the added keys. Do not use this for \ + keys used in a live network.", + )) + } + } + #[derive(Clone, Debug)] pub struct JoinNetwork { pub chain_id: ChainId, diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 126af2d4ca..d37c363854 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -60,6 +60,9 @@ impl CliApi { cmds::NamadaWallet::KeyExport(cmds::WalletExportKey(args)) => { key_export(ctx, io, args) } + cmds::NamadaWallet::KeyImport(cmds::WalletImportKey(args)) => { + key_import(ctx, io, args) + } cmds::NamadaWallet::KeyAddrAdd(cmds::WalletAddKeyAddress(args)) => { key_address_add(ctx, io, args) } @@ -549,28 +552,20 @@ impl FromStr for KeyAddrAddValue { } } -/// Add key or address -fn key_address_add( +fn add_key_or_address( ctx: Context, io: &impl Io, - args::KeyAddressAdd { - alias, - alias_force, - value, - unsafe_dont_encrypt, - .. - }: args::KeyAddressAdd, + alias: String, + alias_force: bool, + value: KeyAddrAddValue, + unsafe_dont_encrypt: bool, ) { - match KeyAddrAddValue::from_str(&value).unwrap_or_else(|err| { - edisplay_line!(io, "{}", err); - display_line!(io, "No changes are persisted. Exiting."); - cli::safe_exit(1) - }) { + match value { KeyAddrAddValue::TranspSecretKey(sk) => { let mut wallet = load_wallet(ctx); let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); - wallet + let alias = wallet .insert_keypair( alias, alias_force, @@ -584,6 +579,14 @@ fn key_address_add( display_line!(io, "No changes are persisted. Exiting."); cli::safe_exit(1); }); + wallet + .save() + .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); + display_line!( + io, + "Successfully added a key and an address with alias: \"{}\"", + alias + ); } KeyAddrAddValue::TranspAddress(address) => { transparent_address_add(ctx, io, alias, alias_force, address) @@ -599,6 +602,26 @@ fn key_address_add( } } +/// Add key or address +fn key_address_add( + ctx: Context, + io: &impl Io, + args::KeyAddressAdd { + alias, + alias_force, + value, + unsafe_dont_encrypt, + .. + }: args::KeyAddressAdd, +) { + let value = KeyAddrAddValue::from_str(&value).unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }); + add_key_or_address(ctx, io, alias, alias_force, value, unsafe_dont_encrypt) +} + /// Find a keypair in the wallet store. fn transparent_key_address_find( ctx: Context, @@ -792,6 +815,30 @@ fn key_export( }) } +/// Import a transparent keypair / MASP spending key from a file. +fn key_import( + ctx: Context, + io: &impl Io, + args::KeyImport { + file_path, + alias, + alias_force, + unsafe_dont_encrypt, + }: args::KeyImport, +) { + let file_data = std::fs::read_to_string(file_path).unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }); + let value = KeyAddrAddValue::from_str(&file_data).unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1) + }); + add_key_or_address(ctx, io, alias, alias_force, value, unsafe_dont_encrypt) +} + /// List all known transparent addresses. fn transparent_addresses_list( ctx: Context, diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 32d3c1eb50..df32d00a26 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2136,6 +2136,19 @@ pub struct KeyExport { pub alias: String, } +/// Wallet key import arguments +#[derive(Clone, Debug)] +pub struct KeyImport { + /// File name + pub file_path: String, + /// Key alias + pub alias: String, + /// Whether to force overwrite the alias + pub alias_force: bool, + /// Don't encrypt the key + pub unsafe_dont_encrypt: bool, +} + /// Wallet key / address add arguments #[derive(Clone, Debug)] pub struct KeyAddressAdd { From 421aa7ec6739847b8d0e863f15cceb84e4537605 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 24 Nov 2023 15:03:20 +0100 Subject: [PATCH 20/46] Fix comments and help messages --- apps/src/lib/cli.rs | 29 ++++++++++++++--------------- apps/src/lib/cli/wallet.rs | 14 +++++++------- sdk/src/args.rs | 6 +++--- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 14db8433af..a0dc03c63e 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -622,7 +622,7 @@ pub mod cmds { } } - /// TODO List all known shielded keys + /// List all known keys #[derive(Clone, Debug)] pub struct WalletListKeys(pub args::KeyList); @@ -637,10 +637,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about( - "TODO List all known keys. Lists all shielded keys in the \ - wallet.", - ) + .about("List all known secret / shielded keys in the wallet.") .add_args::() } } @@ -661,9 +658,11 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about( - "TODO Searches for a keypair from a public key or an \ - alias. Find the given shielded address or key in the \ + .about("Key search") + .long_about( + "In the transparent setting, searches for a keypair from \ + a public key or an alias.\nIn the shielded setting, \ + searches for the given payment address or key in the \ wallet.", ) .add_args::() @@ -671,7 +670,6 @@ pub mod cmds { } /// List known addresses - /// TODO List all known payment addresses #[derive(Clone, Debug)] pub struct WalletListAddresses(pub args::AddressList); @@ -687,7 +685,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about( - "TODO List all known addresses. Lists all payment \ + "List all known transparent addresses / shielded payment \ addresses in the wallet", ) .add_args::() @@ -716,7 +714,7 @@ pub mod cmds { } } - /// Export key + /// Export key to a file #[derive(Clone, Debug)] pub struct WalletExportKey(pub args::KeyExport); @@ -6240,10 +6238,11 @@ pub mod args { "Use pre-genesis wallet, instead of for the current chain, if \ any.", )) - .arg(UNSAFE_SHOW_SECRET.def().help( - "TODO UNSAFE: Print the secret key.Print the spending key \ - values.", - )) + .arg( + UNSAFE_SHOW_SECRET + .def() + .help("UNSAFE: Print the secret / spending key."), + ) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index d37c363854..cb187a420b 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -93,8 +93,8 @@ fn spending_keys_list( if known_view_keys.is_empty() { display_line!( io, - "TODO No known keys. Try `masp add --alias my-addr --value ...` \ - to add a new key to the wallet.", + "No known keys. Try `add --alias my-addr --value ...` to add a \ + new key to the wallet, or `gen --shielded` to generate a new key.", ); } else { let stdout = io::stdout(); @@ -165,7 +165,7 @@ fn payment_addresses_list( if known_addresses.is_empty() { display_line!( io, - "TODO No known payment addresses. Try `masp gen-addr --alias \ + "No known payment addresses. Try `masp gen-payment-addr --alias \ my-addr` to generate a new payment address.", ); } else { @@ -527,7 +527,7 @@ fn address_list(ctx: Context, io: &impl Io, args_key_list: args::AddressList) { } } -/// Value for add command +/// Value for wallet `add` command #[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum KeyAddrAddValue { @@ -709,9 +709,9 @@ fn shielded_key_address_find( // Otherwise alias cannot be referring to any shielded value display_line!( io, - "TODO No shielded address or key with alias {} found. Use the \ - commands `masp list-addrs` and `masp list-keys` to see all the \ - known addresses and keys.", + "No shielded address or key with alias {} found. Use the commands \ + `list-addr --shielded` and `list-keys --shielded` to see all the \ + known shielded addresses and keys.", alias.to_lowercase() ); } diff --git a/sdk/src/args.rs b/sdk/src/args.rs index df32d00a26..603f1b42b1 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2050,7 +2050,7 @@ pub struct KeyGen { pub raw: bool, /// Key alias pub alias: String, - /// Whether to force overwrite the alias, if provided + /// Whether to force overwrite the alias pub alias_force: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, @@ -2067,7 +2067,7 @@ pub struct KeyDerive { pub shielded: bool, /// Key alias pub alias: String, - /// Whether to force overwrite the alias, if provided + /// Whether to force overwrite the alias pub alias_force: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, @@ -2158,7 +2158,7 @@ pub struct KeyAddressAdd { pub alias_force: bool, /// Any supported value pub value: String, - /// Don't encrypt the keys + /// Don't encrypt the key pub unsafe_dont_encrypt: bool, } From f236eef839d65b81e001ea2173e6dd7bf17b9da0 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 24 Nov 2023 18:58:18 +0100 Subject: [PATCH 21/46] Merge key and address list --- apps/src/lib/cli.rs | 93 +++++++++++++------------------------- apps/src/lib/cli/wallet.rs | 90 +++++++++++++++++++----------------- sdk/src/args.rs | 23 +++------- 3 files changed, 86 insertions(+), 120 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index a0dc03c63e..613aaa3559 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -483,12 +483,10 @@ pub mod cmds { KeyGen(WalletGen), /// Key derivation KeyDerive(WalletDerive), - /// Key list - KeyList(WalletListKeys), + /// Key / address list + KeyAddrList(WalletListKeysAddresses), /// Key search KeyFind(WalletFindKeys), - /// Address list - AddrList(WalletListAddresses), /// Address search AddrFind(WalletFindAddresses), /// Key export @@ -505,9 +503,8 @@ pub mod cmds { fn add_sub(app: App) -> App { app.subcommand(WalletGen::def()) .subcommand(WalletDerive::def()) - .subcommand(WalletListKeys::def()) + .subcommand(WalletListKeysAddresses::def()) .subcommand(WalletFindKeys::def()) - .subcommand(WalletListAddresses::def()) .subcommand(WalletFindAddresses::def()) .subcommand(WalletExportKey::def()) .subcommand(WalletImportKey::def()) @@ -518,18 +515,16 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { let gen = SubCmd::parse(matches).map(Self::KeyGen); let derive = SubCmd::parse(matches).map(Self::KeyDerive); - let key_list = SubCmd::parse(matches).map(Self::KeyList); + let key_addr_list = SubCmd::parse(matches).map(Self::KeyAddrList); let key_find = SubCmd::parse(matches).map(Self::KeyFind); - let addr_list = SubCmd::parse(matches).map(Self::AddrList); let addr_find = SubCmd::parse(matches).map(Self::AddrFind); let export = SubCmd::parse(matches).map(Self::KeyExport); let import = SubCmd::parse(matches).map(Self::KeyImport); let key_addr_add = SubCmd::parse(matches).map(Self::KeyAddrAdd); let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); gen.or(derive) - .or(key_list) + .or(key_addr_list) .or(key_find) - .or(addr_list) .or(addr_find) .or(export) .or(import) @@ -622,23 +617,28 @@ pub mod cmds { } } - /// List all known keys + /// List known keys and addresses #[derive(Clone, Debug)] - pub struct WalletListKeys(pub args::KeyList); + pub struct WalletListKeysAddresses(pub args::KeyAddressList); - impl SubCmd for WalletListKeys { - const CMD: &'static str = "list-keys"; + impl SubCmd for WalletListKeysAddresses { + const CMD: &'static str = "list"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| (Self(args::KeyList::parse(matches)))) + .map(|matches| (Self(args::KeyAddressList::parse(matches)))) } fn def() -> App { App::new(Self::CMD) - .about("List all known secret / shielded keys in the wallet.") - .add_args::() + .about("List known keys and addresses in the wallet.") + .long_about( + "In the transparent setting, list known keypairs and \ + addresses.\nIn the shielded setting, list known spending \ + keys and payment addresses.", + ) + .add_args::() } } @@ -665,30 +665,7 @@ pub mod cmds { searches for the given payment address or key in the \ wallet.", ) - .add_args::() - } - } - - /// List known addresses - #[derive(Clone, Debug)] - pub struct WalletListAddresses(pub args::AddressList); - - impl SubCmd for WalletListAddresses { - const CMD: &'static str = "list-addr"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::AddressList::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "List all known transparent addresses / shielded payment \ - addresses in the wallet", - ) - .add_args::() + .add_args::() } } @@ -2918,8 +2895,9 @@ pub mod args { let raw = "127.0.0.1:26657"; TendermintAddress::from_str(raw).unwrap() })); - pub const LEDGER_ADDRESS: Arg = arg("node"); + pub const LIST_ADDRESSES_ONLY: ArgFlag = flag("addr"); + pub const LIST_KEYS_ONLY: ArgFlag = flag("keys"); pub const LOCALHOST: ArgFlag = flag("localhost"); pub const MASP_VALUE_OPT: ArgOpt = arg_opt("shielded-value"); pub const MAX_COMMISSION_RATE_CHANGE: Arg = @@ -6152,25 +6130,30 @@ pub mod args { } } - impl Args for KeyList { + impl Args for KeyAddressList { fn parse(matches: &ArgMatches) -> Self { let shielded = SHIELDED.parse(matches); let decrypt = DECRYPT.parse(matches); + let keys_only = LIST_KEYS_ONLY.parse(matches); + let addresses_only = LIST_ADDRESSES_ONLY.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { shielded, decrypt, + keys_only, + addresses_only, unsafe_show_secret, } } fn def(app: App) -> App { - app.arg( - SHIELDED - .def() - .help("List spending keys for the shielded pool."), - ) + app.arg(SHIELDED.def().help( + "List spending keys and payment addresses for the shielded \ + pool.", + )) .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) + .arg(LIST_KEYS_ONLY.def().help("List only keys.")) + .arg(LIST_ADDRESSES_ONLY.def().help("List only addresses.")) .arg( UNSAFE_SHOW_SECRET .def() @@ -6246,20 +6229,6 @@ pub mod args { } } - impl Args for AddressList { - fn parse(matches: &ArgMatches) -> Self { - let shielded = SHIELDED.parse(matches); - Self { shielded } - } - - fn def(app: App) -> App { - app.arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) - } - } - impl Args for AddressFind { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS_OPT.parse(matches); diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index cb187a420b..0bc90782c7 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -45,15 +45,12 @@ impl CliApi { cmds::NamadaWallet::KeyDerive(cmds::WalletDerive(args)) => { key_derive(ctx, io, args).await } - cmds::NamadaWallet::KeyList(cmds::WalletListKeys(args)) => { - key_list(ctx, io, args) - } + cmds::NamadaWallet::KeyAddrList(cmds::WalletListKeysAddresses( + args, + )) => key_address_list(ctx, io, args), cmds::NamadaWallet::KeyFind(cmds::WalletFindKeys(args)) => { key_find(ctx, io, args) } - cmds::NamadaWallet::AddrList(cmds::WalletListAddresses(args)) => { - address_list(ctx, io, args) - } cmds::NamadaWallet::AddrFind(cmds::WalletFindAddresses(args)) => { address_or_alias_find(ctx, io, args) } @@ -81,11 +78,8 @@ impl CliApi { fn spending_keys_list( ctx: Context, io: &impl Io, - args::KeyList { - decrypt, - unsafe_show_secret, - .. - }: args::KeyList, + decrypt: bool, + unsafe_show_secret: bool, ) { let wallet = load_wallet(ctx); let known_view_keys = wallet.get_viewing_keys(); @@ -155,17 +149,13 @@ fn spending_keys_list( } /// List payment addresses. -fn payment_addresses_list( - ctx: Context, - io: &impl Io, - // args::AddressList { .. }: args::AddressList, -) { +fn payment_addresses_list(ctx: Context, io: &impl Io) { let wallet = load_wallet(ctx); let known_addresses = wallet.get_payment_addrs(); if known_addresses.is_empty() { display_line!( io, - "No known payment addresses. Try `masp gen-payment-addr --alias \ + "No known payment addresses. Try `gen-payment-addr --alias \ my-addr` to generate a new payment address.", ); } else { @@ -501,11 +491,46 @@ async fn key_derive( } /// List keys -fn key_list(ctx: Context, io: &impl Io, args_key_list: args::KeyList) { - if !args_key_list.shielded { - transparent_keys_list(ctx, io, args_key_list) +fn key_list( + ctx: Context, + io: &impl Io, + args::KeyAddressList { + shielded, + decrypt, + unsafe_show_secret, + .. + }: args::KeyAddressList, +) { + if !shielded { + transparent_keys_list(ctx, io, decrypt, unsafe_show_secret) + } else { + spending_keys_list(ctx, io, decrypt, unsafe_show_secret) + } +} + +/// List addresses +fn address_list( + ctx: Context, + io: &impl Io, + args::KeyAddressList { shielded, .. }: args::KeyAddressList, +) { + if !shielded { + transparent_addresses_list(ctx, io) } else { - spending_keys_list(ctx, io, args_key_list) + payment_addresses_list(ctx, io) + } +} + +/// List keys and addresses +fn key_address_list( + ctx: Context, + io: &impl Io, + args_key_address_list: args::KeyAddressList, +) { + if !args_key_address_list.addresses_only { + key_list(ctx, io, args_key_address_list) + } else if !args_key_address_list.keys_only { + address_list(ctx, io, args_key_address_list) } } @@ -518,15 +543,6 @@ fn key_find(ctx: Context, io: &impl Io, args_key_find: args::KeyFind) { } } -/// List addresses -fn address_list(ctx: Context, io: &impl Io, args_key_list: args::AddressList) { - if !args_key_list.shielded { - transparent_addresses_list(ctx, io, args_key_list) - } else { - payment_addresses_list(ctx, io) - } -} - /// Value for wallet `add` command #[allow(clippy::large_enum_variant)] #[derive(Debug)] @@ -721,11 +737,8 @@ fn shielded_key_address_find( fn transparent_keys_list( ctx: Context, io: &impl Io, - args::KeyList { - decrypt, - unsafe_show_secret, - .. - }: args::KeyList, + decrypt: bool, + unsafe_show_secret: bool, ) { let wallet = load_wallet(ctx); let known_public_keys = wallet.get_public_keys(); @@ -840,12 +853,7 @@ fn key_import( } /// List all known transparent addresses. -fn transparent_addresses_list( - ctx: Context, - io: &impl Io, - _args: args::AddressList, - // args::AddressList { is_pre_genesis, .. }: args::AddressList, -) { +fn transparent_addresses_list(ctx: Context, io: &impl Io) { let wallet = load_wallet(ctx); let known_addresses = wallet.get_addresses(); if known_addresses.is_empty() { diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 603f1b42b1..e21cba4e7b 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2092,32 +2092,21 @@ pub struct KeyFind { pub unsafe_show_secret: bool, } -/// Wallet list shielded payment addresses arguments -// #[derive(Clone, Debug)] -// pub struct MaspListPayAddrs { -// /// List shielded payment address pre-genesis instead -// /// of a current chain -// pub is_pre_genesis: bool, -// } - -/// Wallet list keys arguments +/// Wallet list arguments #[derive(Clone, Debug)] -pub struct KeyList { +pub struct KeyAddressList { /// Whether to list MASP spending keys pub shielded: bool, /// Don't decrypt keys pub decrypt: bool, + /// List keys only + pub keys_only: bool, + /// List addresses only + pub addresses_only: bool, /// Show secret keys to user pub unsafe_show_secret: bool, } -/// List addresses arguments -#[derive(Clone, Debug)] -pub struct AddressList { - /// Whether to list MASP payment addresses - pub shielded: bool, -} - /// Wallet address lookup arguments #[derive(Clone, Debug)] pub struct AddressFind { From bf5c3ac4fc1e24afd61985d6f1912d7781417150 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 24 Nov 2023 19:04:06 +0100 Subject: [PATCH 22/46] Fix typo --- apps/src/lib/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 613aaa3559..c5842ff8e1 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1842,7 +1842,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Change commission raate.") + .about("Change commission rate.") .add_args::>() } } From 14c450613f477b274645c965220b60ab90b75ae8 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Mon, 27 Nov 2023 16:39:06 +0100 Subject: [PATCH 23/46] Improve help messages --- apps/src/lib/cli.rs | 8 ++++---- apps/src/lib/cli/wallet.rs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index c5842ff8e1..ecb58d113c 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -636,7 +636,7 @@ pub mod cmds { .long_about( "In the transparent setting, list known keypairs and \ addresses.\nIn the shielded setting, list known spending \ - keys and payment addresses.", + / viewing keys and payment addresses.", ) .add_args::() } @@ -773,7 +773,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about( - "Generates a payment address from the given spending key", + "Generates a payment address from the given spending key.", ) .add_args::>() } @@ -6148,8 +6148,8 @@ pub mod args { fn def(app: App) -> App { app.arg(SHIELDED.def().help( - "List spending keys and payment addresses for the shielded \ - pool.", + "List viewing / spending keys and payment addresses for the \ + shielded pool.", )) .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) .arg(LIST_KEYS_ONLY.def().help("List only keys.")) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 0bc90782c7..873248bf41 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -88,7 +88,8 @@ fn spending_keys_list( display_line!( io, "No known keys. Try `add --alias my-addr --value ...` to add a \ - new key to the wallet, or `gen --shielded` to generate a new key.", + new key to the wallet, or `gen --shielded --alias my-key` to \ + generate a new key.", ); } else { let stdout = io::stdout(); From 93f429477b9894b8fcb31aa7c05c6fc3aa413e83 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 8 Dec 2023 16:31:20 +0100 Subject: [PATCH 24/46] Store known payment addresses in bimap --- sdk/src/wallet/store.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/sdk/src/wallet/store.rs b/sdk/src/wallet/store.rs index 2ed2f4d915..8ba66df798 100644 --- a/sdk/src/wallet/store.rs +++ b/sdk/src/wallet/store.rs @@ -63,8 +63,8 @@ pub struct Store { view_keys: BTreeMap, /// Known spending keys spend_keys: BTreeMap>, - /// Known payment addresses - payment_addrs: BTreeMap, + /// Payment address book + payment_addrs: BiBTreeMap, /// Cryptographic keypairs secret_keys: BTreeMap>, /// Known public keys @@ -148,7 +148,15 @@ impl Store { &self, alias: impl AsRef, ) -> Option<&PaymentAddress> { - self.payment_addrs.get(&alias.into()) + self.payment_addrs.get_by_left(&alias.into()) + } + + /// Find an alias by the address if it's in the wallet. + pub fn find_alias_by_payment_addr( + &self, + payment_address: &PaymentAddress, + ) -> Option<&Alias> { + self.payment_addrs.get_by_right(payment_address) } /// Find the stored key by a public key. @@ -237,7 +245,7 @@ impl Store { } /// Get all known payment addresses by their alias. - pub fn get_payment_addrs(&self) -> &BTreeMap { + pub fn get_payment_addrs(&self) -> &BiBTreeMap { &self.payment_addrs } @@ -557,7 +565,7 @@ impl Store { /// Check if any map of the wallet contains the given alias pub fn contains_alias(&self, alias: &Alias) -> bool { - self.payment_addrs.contains_key(alias) + self.payment_addrs.contains_left(alias) || self.view_keys.contains_key(alias) || self.spend_keys.contains_key(alias) || self.secret_keys.contains_key(alias) @@ -569,7 +577,7 @@ impl Store { /// Completely remove the given alias from all maps in the wallet fn remove_alias(&mut self, alias: &Alias) { - self.payment_addrs.remove(alias); + self.payment_addrs.remove_by_left(alias); self.view_keys.remove(alias); self.spend_keys.remove(alias); self.secret_keys.remove(alias); From 6fe0c5480b16e9b53f05e9ffff6215c6fc8d44af Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sat, 9 Dec 2023 20:17:15 +0100 Subject: [PATCH 25/46] Reverse find alias for payment address --- sdk/src/wallet/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index c5fe54aea9..f17e4ba90f 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -417,6 +417,14 @@ impl Wallet { self.store.find_payment_addr(alias.as_ref()) } + /// Find an alias by the payment address if it's in the wallet. + pub fn find_alias_by_payment_addr( + &self, + payment_address: &PaymentAddress, + ) -> Option<&Alias> { + self.store.find_alias_by_payment_addr(payment_address) + } + /// Get all known keys by their alias, paired with PKH, if known. pub fn get_secret_keys( &self, From 21ee93167f4f07b16b6695d96c3366dff2ba0eaf Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sat, 9 Dec 2023 21:01:15 +0100 Subject: [PATCH 26/46] Merge find-key and find-addr --- apps/src/lib/cli.rs | 180 ++++++++++------------ apps/src/lib/cli/wallet.rs | 300 +++++++++++++++++++++++++++---------- sdk/src/args.rs | 35 +++-- 3 files changed, 320 insertions(+), 195 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index ecb58d113c..78d42605fa 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -485,10 +485,8 @@ pub mod cmds { KeyDerive(WalletDerive), /// Key / address list KeyAddrList(WalletListKeysAddresses), - /// Key search - KeyFind(WalletFindKeys), - /// Address search - AddrFind(WalletFindAddresses), + /// Key / address search + KeyAddrFind(WalletFindKeysAddresses), /// Key export KeyExport(WalletExportKey), /// Key import @@ -504,8 +502,7 @@ pub mod cmds { app.subcommand(WalletGen::def()) .subcommand(WalletDerive::def()) .subcommand(WalletListKeysAddresses::def()) - .subcommand(WalletFindKeys::def()) - .subcommand(WalletFindAddresses::def()) + .subcommand(WalletFindKeysAddresses::def()) .subcommand(WalletExportKey::def()) .subcommand(WalletImportKey::def()) .subcommand(WalletAddKeyAddress::def()) @@ -516,16 +513,14 @@ pub mod cmds { let gen = SubCmd::parse(matches).map(Self::KeyGen); let derive = SubCmd::parse(matches).map(Self::KeyDerive); let key_addr_list = SubCmd::parse(matches).map(Self::KeyAddrList); - let key_find = SubCmd::parse(matches).map(Self::KeyFind); - let addr_find = SubCmd::parse(matches).map(Self::AddrFind); + let key_addr_find = SubCmd::parse(matches).map(Self::KeyAddrFind); let export = SubCmd::parse(matches).map(Self::KeyExport); let import = SubCmd::parse(matches).map(Self::KeyImport); let key_addr_add = SubCmd::parse(matches).map(Self::KeyAddrAdd); let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); gen.or(derive) .or(key_addr_list) - .or(key_find) - .or(addr_find) + .or(key_addr_find) .or(export) .or(import) .or(key_addr_add) @@ -642,52 +637,31 @@ pub mod cmds { } } - /// TODO Find a keypair in the wallet store - /// TODO Find the given shielded address or key + /// Find known keys and addresses #[derive(Clone, Debug)] - pub struct WalletFindKeys(pub args::KeyFind); + pub struct WalletFindKeysAddresses(pub args::KeyAddressFind); - impl SubCmd for WalletFindKeys { - const CMD: &'static str = "find-keys"; + impl SubCmd for WalletFindKeysAddresses { + const CMD: &'static str = "find"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| (Self(args::KeyFind::parse(matches)))) + .map(|matches| Self(args::KeyAddressFind::parse(matches))) } fn def() -> App { App::new(Self::CMD) - .about("Key search") + .about("Find known keys and addresses in the wallet.") .long_about( - "In the transparent setting, searches for a keypair from \ - a public key or an alias.\nIn the shielded setting, \ - searches for the given payment address or key in the \ - wallet.", + "In the transparent setting, searches for a keypair / \ + address by a given alias, public key, or a public key \ + hash. Looks up an alias of the given address.\nIn the \ + shielded setting, searches for a spending / viewing key \ + and payment address by a given alias. Looks up an alias \ + of the given payment address.", ) - .add_args::() - } - } - - /// Find an address by its alias - #[derive(Clone, Debug)] - pub struct WalletFindAddresses(pub args::AddressFind); - - impl SubCmd for WalletFindAddresses { - const CMD: &'static str = "find-addr"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::AddressFind::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Find an address by its alias or an alias by its address.", - ) - .add_args::() + .add_args::() } } @@ -2746,7 +2720,7 @@ pub mod args { use namada::types::ethereum_events::EthAddress; use namada::types::keccak::KeccakHash; use namada::types::key::*; - use namada::types::masp::MaspValue; + use namada::types::masp::PaymentAddress; use namada::types::storage::{self, BlockHeight, Epoch}; use namada::types::time::DateTimeUtc; use namada::types::token; @@ -2896,10 +2870,9 @@ pub mod args { TendermintAddress::from_str(raw).unwrap() })); pub const LEDGER_ADDRESS: Arg = arg("node"); - pub const LIST_ADDRESSES_ONLY: ArgFlag = flag("addr"); - pub const LIST_KEYS_ONLY: ArgFlag = flag("keys"); + pub const LIST_FIND_ADDRESSES_ONLY: ArgFlag = flag("addr"); + pub const LIST_FIND_KEYS_ONLY: ArgFlag = flag("keys"); pub const LOCALHOST: ArgFlag = flag("localhost"); - pub const MASP_VALUE_OPT: ArgOpt = arg_opt("shielded-value"); pub const MAX_COMMISSION_RATE_CHANGE: Arg = arg("max-commission-rate-change"); pub const MAX_ETH_GAS: ArgOpt = arg_opt("max_eth-gas"); @@ -2939,9 +2912,15 @@ pub mod args { pub const RAW_ADDRESS_ESTABLISHED: Arg = arg("address"); pub const RAW_ADDRESS_OPT: ArgOpt
= RAW_ADDRESS.opt(); pub const RAW_KEY_GEN: ArgFlag = flag("raw"); + pub const RAW_PAYMENT_ADDRESS: Arg = arg("payment-address"); + pub const RAW_PAYMENT_ADDRESS_OPT: ArgOpt = + RAW_PAYMENT_ADDRESS.opt(); pub const RAW_PUBLIC_KEY: Arg = arg("public-key"); pub const RAW_PUBLIC_KEY_OPT: ArgOpt = - arg_opt("public-key"); + RAW_PUBLIC_KEY.opt(); + pub const RAW_PUBLIC_KEY_HASH: Arg = arg("public-key-hash"); + pub const RAW_PUBLIC_KEY_HASH_OPT: ArgOpt = + RAW_PUBLIC_KEY_HASH.opt(); pub const RECEIVER: Arg = arg("receiver"); pub const RELAYER: Arg
= arg("relayer"); pub const SAFE_MODE: ArgFlag = flag("safe-mode"); @@ -2989,7 +2968,6 @@ pub mod args { pub const VALIDATOR_ETH_HOT_KEY: ArgOpt = arg_opt("eth-hot-key"); pub const VALUE: Arg = arg("value"); - pub const VALUE_OPT: ArgOpt = VALUE.opt(); pub const VIEWING_KEY: Arg = arg("key"); pub const VP: ArgOpt = arg_opt("vp"); pub const WALLET_ALIAS_FORCE: ArgFlag = flag("wallet-alias-force"); @@ -6134,8 +6112,8 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let shielded = SHIELDED.parse(matches); let decrypt = DECRYPT.parse(matches); - let keys_only = LIST_KEYS_ONLY.parse(matches); - let addresses_only = LIST_ADDRESSES_ONLY.parse(matches); + let keys_only = LIST_FIND_KEYS_ONLY.parse(matches); + let addresses_only = LIST_FIND_ADDRESSES_ONLY.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { shielded, @@ -6152,8 +6130,12 @@ pub mod args { shielded pool.", )) .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg(LIST_KEYS_ONLY.def().help("List only keys.")) - .arg(LIST_ADDRESSES_ONLY.def().help("List only addresses.")) + .arg(LIST_FIND_KEYS_ONLY.def().help("List keys only.")) + .arg(LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only.")) + .group(ArgGroup::new("only_group").args([ + LIST_FIND_KEYS_ONLY.name, + LIST_FIND_ADDRESSES_ONLY.name, + ])) .arg( UNSAFE_SHOW_SECRET .def() @@ -6162,61 +6144,83 @@ pub mod args { } } - impl Args for KeyFind { + impl Args for KeyAddressFind { fn parse(matches: &ArgMatches) -> Self { let shielded = SHIELDED.parse(matches); let alias = ALIAS_OPT.parse(matches); + let address = RAW_ADDRESS_OPT.parse(matches); let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); - let value = VALUE_OPT.parse(matches); + let public_key_hash = RAW_PUBLIC_KEY_HASH_OPT.parse(matches); + let payment_address = RAW_PAYMENT_ADDRESS_OPT.parse(matches); + let keys_only = LIST_FIND_KEYS_ONLY.parse(matches); + let addresses_only = LIST_FIND_ADDRESSES_ONLY.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); - Self { shielded, alias, + address, public_key, - value, + public_key_hash, + payment_address, + keys_only, + addresses_only, unsafe_show_secret, } } fn def(app: App) -> App { app.arg( - SHIELDED - .def() - .help("Find spending key for the shielded pool.") - .conflicts_with_all([ - VALUE_OPT.name, - RAW_PUBLIC_KEY_OPT.name, - ]), + SHIELDED.def().help( + "Find keys and payment addresses for the shielded pool.", + ), ) .arg( ALIAS_OPT + .def() + .help("An alias associated with the keys / addresses."), + ) + .arg( + RAW_ADDRESS_OPT .def() .help( - "TODO An alias associated with the keypair. The alias \ - that is to be found.", + "The bech32m encoded string of a transparent address.", ) - .conflicts_with(VALUE_OPT.name), + .conflicts_with(SHIELDED.name), ) .arg( - RAW_PUBLIC_KEY_OPT - .def() - .help("A public key associated with the keypair."), + RAW_PUBLIC_KEY_OPT.def().help( + "A public key associated with the transparent keypair.", + ), ) + .arg(RAW_PUBLIC_KEY_HASH_OPT.def().help( + "A public key hash associated with the transparent keypair.", + )) .arg( - VALUE_OPT + RAW_PAYMENT_ADDRESS_OPT .def() - .help("A public key or alias associated with the keypair."), + .help( + "The bech32m encoded string of a shielded payment \ + address.", + ) + .conflicts_with(SHIELDED.name), ) .group( - ArgGroup::new("key_find_args") + ArgGroup::new("addr_find_args") .args([ ALIAS_OPT.name, + RAW_ADDRESS_OPT.name, RAW_PUBLIC_KEY_OPT.name, - VALUE_OPT.name, + RAW_PUBLIC_KEY_HASH_OPT.name, + RAW_PAYMENT_ADDRESS_OPT.name, ]) .required(true), ) + .arg(LIST_FIND_KEYS_ONLY.def().help("Find keys only.")) + .arg(LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only.")) + .group(ArgGroup::new("only_group").args([ + LIST_FIND_KEYS_ONLY.name, + LIST_FIND_ADDRESSES_ONLY.name, + ])) .arg(PRE_GENESIS.def().help( "Use pre-genesis wallet, instead of for the current chain, if \ any.", @@ -6229,32 +6233,6 @@ pub mod args { } } - impl Args for AddressFind { - fn parse(matches: &ArgMatches) -> Self { - let alias = ALIAS_OPT.parse(matches); - let address = RAW_ADDRESS_OPT.parse(matches); - Self { alias, address } - } - - fn def(app: App) -> App { - app.arg( - ALIAS_OPT - .def() - .help("An alias associated with the address."), - ) - .arg( - RAW_ADDRESS_OPT - .def() - .help("The bech32m encoded address string."), - ) - .group( - ArgGroup::new("addr_find_args") - .args([ALIAS_OPT.name, RAW_ADDRESS_OPT.name]) - .required(true), - ) - } - } - impl Args for KeyAddressAdd { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 873248bf41..7b98749867 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -48,12 +48,9 @@ impl CliApi { cmds::NamadaWallet::KeyAddrList(cmds::WalletListKeysAddresses( args, )) => key_address_list(ctx, io, args), - cmds::NamadaWallet::KeyFind(cmds::WalletFindKeys(args)) => { - key_find(ctx, io, args) - } - cmds::NamadaWallet::AddrFind(cmds::WalletFindAddresses(args)) => { - address_or_alias_find(ctx, io, args) - } + cmds::NamadaWallet::KeyAddrFind(cmds::WalletFindKeysAddresses( + args, + )) => key_address_find(ctx, io, args), cmds::NamadaWallet::KeyExport(cmds::WalletExportKey(args)) => { key_export(ctx, io, args) } @@ -535,12 +532,59 @@ fn key_address_list( } } -/// Find key or address -fn key_find(ctx: Context, io: &impl Io, args_key_find: args::KeyFind) { - if !args_key_find.shielded { - transparent_key_address_find(ctx, io, args_key_find) - } else { - shielded_key_address_find(ctx, io, args_key_find) +/// Find keys and addresses +fn key_address_find( + ctx: Context, + io: &impl Io, + args::KeyAddressFind { + shielded, + alias, + address, + public_key, + public_key_hash, + payment_address, + keys_only, + addresses_only, + unsafe_show_secret, + }: args::KeyAddressFind, +) { + if let Some(alias) = alias { + // Search keys and addresses by alias + if !shielded { + transparent_key_address_find_by_alias( + ctx, + io, + alias, + keys_only, + addresses_only, + unsafe_show_secret, + ) + } else { + shielded_key_address_find_by_alias( + ctx, + io, + alias, + keys_only, + addresses_only, + unsafe_show_secret, + ) + } + } else if address.is_some() { + // Search alias by address + transparent_address_or_alias_find(ctx, io, None, address) + } else if public_key.is_some() || public_key_hash.is_some() { + // Search transparent keypair by public key or public key hash + transparent_key_find( + ctx, + io, + None, + public_key, + public_key_hash, + unsafe_show_secret, + ) + } else if payment_address.is_some() { + // Search alias by MASP payment address + payment_address_or_alias_find(ctx, io, None, payment_address) } } @@ -640,22 +684,19 @@ fn key_address_add( } /// Find a keypair in the wallet store. -fn transparent_key_address_find( +fn transparent_key_find( ctx: Context, io: &impl Io, - args::KeyFind { - public_key, - alias, - value, - unsafe_show_secret, - .. - }: args::KeyFind, + alias: Option, + public_key: Option, + public_key_hash: Option, + unsafe_show_secret: bool, ) { let mut wallet = load_wallet(ctx); let found_keypair = match public_key { Some(pk) => wallet.find_key_by_pk(&pk, None), None => { - let alias = alias.or(value); + let alias = alias.or(public_key_hash); match alias { None => { edisplay_line!( @@ -686,27 +727,140 @@ fn transparent_key_address_find( } } -/// Find shielded address or key -/// TODO this works for both keys and addresses -/// TODO split to enable finding alias by payment key -fn shielded_key_address_find( +/// Find address (alias) by its alias (address). +fn transparent_address_or_alias_find( ctx: Context, io: &impl Io, - args::KeyFind { - alias, - unsafe_show_secret, - .. - }: args::KeyFind, + alias: Option, + address: Option
, +) { + let wallet = load_wallet(ctx); + if address.is_some() && alias.is_some() { + panic!( + "This should not be happening: clap should emit its own error \ + message." + ); + } else if alias.is_some() { + let alias = alias.unwrap().to_lowercase(); + if let Some(address) = wallet.find_address(&alias) { + display_line!(io, "Found address {}", address.to_pretty_string()); + } else { + display_line!( + io, + "No address with alias {} found. Use the command `list \ + --addr` to see all the known transparent addresses.", + alias + ); + } + } else if address.is_some() { + if let Some(alias) = wallet.find_alias(address.as_ref().unwrap()) { + display_line!(io, "Found alias {}", alias); + } else { + display_line!( + io, + "No address with alias {} found. Use the command `list \ + --addr` to see all the known transparent addresses.", + address.unwrap() + ); + } + } +} + +/// Find payment address (alias) by its alias (payment address). +fn payment_address_or_alias_find( + ctx: Context, + io: &impl Io, + alias: Option, + payment_address: Option, +) { + let wallet = load_wallet(ctx); + if payment_address.is_some() && alias.is_some() { + panic!( + "This should not be happening: clap should emit its own error \ + message." + ); + } else if alias.is_some() { + let alias = alias.unwrap().to_lowercase(); + if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + display_line!(io, "Found payment address {}", payment_addr); + } else { + display_line!( + io, + "No payment address with alias {} found. Use the command \ + `list --shielded --addr` to see all the known payment \ + addresses.", + alias + ); + } + } else if payment_address.is_some() { + if let Some(alias) = + wallet.find_alias_by_payment_addr(payment_address.as_ref().unwrap()) + { + display_line!(io, "Found alias {}", alias); + } else { + display_line!( + io, + "No address with alias {} found. Use the command `list \ + --shielded --addr` to see all the known payment addresses.", + payment_address.unwrap() + ); + } + } +} + +/// Find transparent addresses and keys by alias +fn transparent_key_address_find_by_alias( + ctx: Context, + io: &impl Io, + alias: String, + keys_only: bool, + addresses_only: bool, + unsafe_show_secret: bool, ) { let mut wallet = load_wallet(ctx); - // TODO - let alias = alias.unwrap_or_else(|| { - display_line!(io, "Missing alias."); - display_line!(io, "No changes are persisted. Exiting."); - cli::safe_exit(1) - }); let alias = alias.to_lowercase(); - if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { + if let Some(keypair) = (!addresses_only) + .then_some(()) + .and_then(|_| wallet.find_secret_key(&alias, None).ok()) + { + let pkh: PublicKeyHash = (&keypair.ref_to()).into(); + display_line!(io, "Public key hash: {}", pkh); + display_line!(io, "Public key: {}", keypair.ref_to()); + if unsafe_show_secret { + display_line!(io, "Secret key: {}", keypair); + } + } else if let Some(address) = (!keys_only) + .then_some(()) + .and_then(|_| wallet.find_address(&alias)) + { + display_line!(io, "Found address {}", address.to_pretty_string()); + } else if !addresses_only && !keys_only { + // Otherwise alias cannot be referring to any shielded value + display_line!( + io, + "No transparent address or key with alias {} found. Use the \ + command `list` to see all the known transparent addresses and \ + keys.", + alias + ); + } +} + +/// Find shielded payment address and keys by alias +fn shielded_key_address_find_by_alias( + ctx: Context, + io: &impl Io, + alias: String, + keys_only: bool, + addresses_only: bool, + unsafe_show_secret: bool, +) { + let mut wallet = load_wallet(ctx); + let alias = alias.to_lowercase(); + if let Some(viewing_key) = (!addresses_only) + .then_some(()) + .and_then(|_| wallet.find_viewing_key(&alias).ok()) + { // Check if alias is a viewing key display_line!(io, "Viewing key: {}", viewing_key); if unsafe_show_secret { @@ -719,17 +873,20 @@ fn shielded_key_address_find( Err(err) => edisplay_line!(io, "{}", err), } } - } else if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + } else if let Some(payment_addr) = (!keys_only) + .then_some(()) + .and_then(|_| wallet.find_payment_addr(&alias)) + { // Failing that, check if alias is a payment address display_line!(io, "Payment address: {}", payment_addr); - } else { + } else if !addresses_only && !keys_only { // Otherwise alias cannot be referring to any shielded value display_line!( io, - "No shielded address or key with alias {} found. Use the commands \ - `list-addr --shielded` and `list-keys --shielded` to see all the \ - known shielded addresses and keys.", - alias.to_lowercase() + "No shielded payment address or key with alias {} found. Use the \ + command `list --shielded` to see all the known shielded \ + addresses and keys.", + alias ); } } @@ -877,42 +1034,33 @@ fn transparent_addresses_list(ctx: Context, io: &impl Io) { } } -/// Find address (alias) by its alias (address). -fn address_or_alias_find( +/// Add a transparent secret key to the wallet. +fn transparent_secret_key_add( ctx: Context, io: &impl Io, - args::AddressFind { alias, address }: args::AddressFind, + alias: String, + alias_force: bool, + sk: common::SecretKey, + unsafe_dont_encrypt: bool, ) { - let wallet = load_wallet(ctx); - if address.is_some() && alias.is_some() { - panic!( - "This should not be happening: clap should emit its own error \ - message." - ); - } else if alias.is_some() { - let alias = alias.unwrap().to_lowercase(); - if let Some(address) = wallet.find_address(&alias) { - display_line!(io, "Found address {}", address.to_pretty_string()); - } else { - display_line!( - io, - "No address with alias {} found. Use the command `address \ - list` to see all the known addresses.", - alias - ); - } - } else if address.is_some() { - if let Some(alias) = wallet.find_alias(address.as_ref().unwrap()) { - display_line!(io, "Found alias {}", alias); - } else { - display_line!( - io, - "No alias with address {} found. Use the command `address \ - list` to see all the known addresses.", - address.unwrap() - ); - } - } + let mut wallet = load_wallet(ctx); + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let alias = wallet + .insert_keypair(alias, alias_force, sk, encryption_password, None, None) + .unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + display_line!(io, "No changes are persisted. Exiting."); + cli::safe_exit(1); + }); + wallet + .save() + .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); + display_line!( + io, + "Successfully added a key and an address with alias: \"{}\"", + alias + ); } /// Add an address to the wallet. diff --git a/sdk/src/args.rs b/sdk/src/args.rs index e21cba4e7b..c5c070baa4 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -13,6 +13,7 @@ use namada_core::types::dec::Dec; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::keccak::KeccakHash; use namada_core::types::key::{common, SchemeType}; +use namada_core::types::masp::PaymentAddress; use namada_core::types::storage::Epoch; use namada_core::types::time::DateTimeUtc; use namada_core::types::transaction::GasLimit; @@ -2077,21 +2078,6 @@ pub struct KeyDerive { pub use_device: bool, } -/// Wallet key lookup arguments -#[derive(Clone, Debug)] -pub struct KeyFind { - /// Whether to find a MASP address by alias - pub shielded: bool, - /// Key alias to lookup keypair with - pub alias: Option, - /// Public key to lookup keypair with - pub public_key: Option, - /// Public key hash to lookup keypair with - pub value: Option, - /// Show secret keys to user - pub unsafe_show_secret: bool, -} - /// Wallet list arguments #[derive(Clone, Debug)] pub struct KeyAddressList { @@ -2107,15 +2093,28 @@ pub struct KeyAddressList { pub unsafe_show_secret: bool, } -/// Wallet address lookup arguments +/// Wallet key / address lookup arguments #[derive(Clone, Debug)] -pub struct AddressFind { +pub struct KeyAddressFind { + /// Whether to find MASP keys / addresses + pub shielded: bool, /// Alias to find pub alias: Option, /// Address to find pub address: Option
, + /// Public key to lookup keypair with + pub public_key: Option, + /// Public key hash to lookup keypair with + pub public_key_hash: Option, + /// Payment address to find + pub payment_address: Option, + /// Find keys only + pub keys_only: bool, + /// Find addresses only + pub addresses_only: bool, + /// Show secret keys to user + pub unsafe_show_secret: bool, } - /// Wallet key export arguments #[derive(Clone, Debug)] pub struct KeyExport { From 5e581ff2058514de8896ba1272e6fb4af5c610a1 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sat, 9 Dec 2023 21:01:51 +0100 Subject: [PATCH 27/46] Improve message --- apps/src/lib/cli/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 7b98749867..ddedaf607e 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -154,7 +154,7 @@ fn payment_addresses_list(ctx: Context, io: &impl Io) { display_line!( io, "No known payment addresses. Try `gen-payment-addr --alias \ - my-addr` to generate a new payment address.", + my-payment-addr` to generate a new payment address.", ); } else { let stdout = io::stdout(); From 8750ded6c33ee76fba8aa53fb3b0a7b47cf6b773 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sat, 9 Dec 2023 21:02:42 +0100 Subject: [PATCH 28/46] Refactor parsing of `add` command value --- apps/src/lib/cli/wallet.rs | 71 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index ddedaf607e..706f94dfed 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -588,27 +588,43 @@ fn key_address_find( } } -/// Value for wallet `add` command -#[allow(clippy::large_enum_variant)] #[derive(Debug)] -pub enum KeyAddrAddValue { +pub enum TransparentValue { /// Transparent secret key TranspSecretKey(common::SecretKey), /// Transparent address TranspAddress(Address), - /// Masp value - MASPValue(MaspValue), } -impl FromStr for KeyAddrAddValue { +impl FromStr for TransparentValue { type Err = DecodeError; fn from_str(s: &str) -> Result { - // Try to decode this value first as a secret key, then as an address, - // then as a MASP value + // Try to decode this value first as a secret key, then as an address common::SecretKey::from_str(s) .map(Self::TranspSecretKey) .or_else(|_| Address::from_str(s).map(Self::TranspAddress)) + } +} + +/// Value for wallet `add` command +#[allow(clippy::large_enum_variant)] +#[derive(Debug)] +pub enum KeyAddrAddValue { + /// Transparent value + TranspValue(TransparentValue), + /// Masp value + MASPValue(MaspValue), +} + +impl FromStr for KeyAddrAddValue { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + // Try to decode this value first as a transparent value, then as a MASP + // value + TransparentValue::from_str(s) + .map(Self::TranspValue) .or_else(|_| MaspValue::from_str(s).map(Self::MASPValue)) } } @@ -622,36 +638,19 @@ fn add_key_or_address( unsafe_dont_encrypt: bool, ) { match value { - KeyAddrAddValue::TranspSecretKey(sk) => { - let mut wallet = load_wallet(ctx); - let encryption_password = - read_and_confirm_encryption_password(unsafe_dont_encrypt); - let alias = wallet - .insert_keypair( - alias, - alias_force, - sk, - encryption_password, - None, - None, - ) - .unwrap_or_else(|err| { - edisplay_line!(io, "{}", err); - display_line!(io, "No changes are persisted. Exiting."); - cli::safe_exit(1); - }); - wallet - .save() - .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); - display_line!( + KeyAddrAddValue::TranspValue(TransparentValue::TranspSecretKey(sk)) => { + transparent_secret_key_add( + ctx, io, - "Successfully added a key and an address with alias: \"{}\"", - alias - ); - } - KeyAddrAddValue::TranspAddress(address) => { - transparent_address_add(ctx, io, alias, alias_force, address) + alias, + alias_force, + sk, + unsafe_dont_encrypt, + ) } + KeyAddrAddValue::TranspValue(TransparentValue::TranspAddress( + address, + )) => transparent_address_add(ctx, io, alias, alias_force, address), KeyAddrAddValue::MASPValue(masp_value) => shielded_key_address_add( ctx, io, From efc24e27498a18ed15f16cfe920200483367cded Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sat, 9 Dec 2023 21:03:36 +0100 Subject: [PATCH 29/46] Improve comment --- apps/src/lib/cli/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 706f94dfed..b0bd6ba8bb 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -1062,7 +1062,7 @@ fn transparent_secret_key_add( ); } -/// Add an address to the wallet. +/// Add a transparent address to the wallet. fn transparent_address_add( ctx: Context, io: &impl Io, From 31554aa3452d94a90bbd7540493c30f470e2bb38 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sun, 10 Dec 2023 17:49:20 +0100 Subject: [PATCH 30/46] Fix help message --- apps/src/lib/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 78d42605fa..187b497b93 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6216,7 +6216,7 @@ pub mod args { .required(true), ) .arg(LIST_FIND_KEYS_ONLY.def().help("Find keys only.")) - .arg(LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only.")) + .arg(LIST_FIND_ADDRESSES_ONLY.def().help("Find addresses only.")) .group(ArgGroup::new("only_group").args([ LIST_FIND_KEYS_ONLY.name, LIST_FIND_ADDRESSES_ONLY.name, From e44fff3f061e5d9acb12646fbe5936c8c05e2d17 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sun, 10 Dec 2023 17:27:03 +0100 Subject: [PATCH 31/46] Adapt e2e tests for new wallet cli --- tests/src/e2e/helpers.rs | 4 +-- tests/src/e2e/ledger_tests.rs | 2 +- tests/src/e2e/wallet_tests.rs | 51 +++++++++++------------------------ 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 9fe5194da6..730f4f1a55 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -107,7 +107,7 @@ pub fn find_address(test: &Test, alias: impl AsRef) -> Result
{ let mut find = run!( test, Bin::Wallet, - &["address", "find", "--alias", alias.as_ref()], + &["find", "--addr", "--alias", alias.as_ref()], Some(10) )?; let (unread, matched) = find.exp_regex("Found address .*")?; @@ -245,8 +245,8 @@ pub fn find_keypair( test, Bin::Wallet, &[ - "key", "find", + "--keys", "--alias", alias.as_ref(), "--unsafe-show-secret" diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index e11317d4fe..06bb2667c9 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2975,7 +2975,7 @@ fn implicit_account_reveal_pk() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["key", "gen", "--alias", &key_alias, "--unsafe-dont-encrypt"], + &["gen", "--alias", &key_alias, "--unsafe-dont-encrypt"], Some(20), )?; cmd.assert_success(); diff --git a/tests/src/e2e/wallet_tests.rs b/tests/src/e2e/wallet_tests.rs index 167d67202e..a79da138b8 100644 --- a/tests/src/e2e/wallet_tests.rs +++ b/tests/src/e2e/wallet_tests.rs @@ -28,12 +28,8 @@ fn wallet_encrypted_key_cmds() -> Result<()> { let password = "VeRySeCuR3"; // 1. key gen - let mut cmd = run!( - test, - Bin::Wallet, - &["key", "gen", "--alias", key_alias], - Some(20), - )?; + let mut cmd = + run!(test, Bin::Wallet, &["gen", "--alias", key_alias], Some(20),)?; cmd.exp_string("Enter your encryption password:")?; cmd.send_line(password)?; @@ -50,7 +46,7 @@ fn wallet_encrypted_key_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["key", "find", "--alias", key_alias], + &["find", "--keys", "--alias", key_alias], Some(20), )?; @@ -60,7 +56,7 @@ fn wallet_encrypted_key_cmds() -> Result<()> { cmd.exp_string("Public key:")?; // 3. key list - let mut cmd = run!(test, Bin::Wallet, &["key", "list"], Some(20))?; + let mut cmd = run!(test, Bin::Wallet, &["list", "--keys"], Some(20))?; cmd.exp_string(&format!( "Alias \"{}\" (encrypted):", key_alias.to_lowercase() @@ -82,12 +78,8 @@ fn wallet_encrypted_key_cmds_env_var() -> Result<()> { env::set_var("NAMADA_WALLET_PASSWORD", password); // 1. key gen - let mut cmd = run!( - test, - Bin::Wallet, - &["key", "gen", "--alias", key_alias], - Some(20), - )?; + let mut cmd = + run!(test, Bin::Wallet, &["gen", "--alias", key_alias], Some(20),)?; cmd.exp_string("Enter BIP39 passphrase (empty for none): ")?; cmd.send_line("")?; @@ -101,7 +93,7 @@ fn wallet_encrypted_key_cmds_env_var() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["key", "find", "--alias", key_alias], + &["find", "--keys", "--alias", key_alias], Some(20), )?; @@ -109,7 +101,7 @@ fn wallet_encrypted_key_cmds_env_var() -> Result<()> { cmd.exp_string("Public key:")?; // 3. key list - let mut cmd = run!(test, Bin::Wallet, &["key", "list"], Some(20))?; + let mut cmd = run!(test, Bin::Wallet, &["list", "--keys"], Some(20))?; cmd.exp_string(&format!("Alias \"{}\" (encrypted):", key_alias))?; Ok(()) @@ -128,7 +120,7 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["key", "gen", "--alias", key_alias, "--unsafe-dont-encrypt"], + &["gen", "--alias", key_alias, "--unsafe-dont-encrypt"], Some(20), )?; cmd.exp_string(&format!( @@ -140,7 +132,7 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["key", "find", "--alias", key_alias], + &["find", "--keys", "--alias", key_alias], Some(20), )?; @@ -148,7 +140,7 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { cmd.exp_string("Public key:")?; // 3. key list - let mut cmd = run!(test, Bin::Wallet, &["key", "list"], Some(20))?; + let mut cmd = run!(test, Bin::Wallet, &["list", "--keys"], Some(20))?; cmd.exp_string(&format!("Alias \"{}\" (not encrypted):", key_alias))?; Ok(()) @@ -170,13 +162,7 @@ fn wallet_address_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &[ - "address", - "gen", - "--alias", - gen_address_alias, - "--unsafe-dont-encrypt", - ], + &["gen", "--alias", gen_address_alias, "--unsafe-dont-encrypt"], Some(20), )?; cmd.exp_string(&format!( @@ -188,14 +174,7 @@ fn wallet_address_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &[ - "address", - "add", - "--address", - add_address, - "--alias", - add_address_alias, - ], + &["add", "--value", add_address, "--alias", add_address_alias], Some(20), )?; cmd.exp_string(&format!( @@ -207,13 +186,13 @@ fn wallet_address_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["address", "find", "--alias", gen_address_alias], + &["find", "--addr", "--alias", gen_address_alias], Some(20), )?; cmd.exp_string("Found address")?; // 4. address list - let mut cmd = run!(test, Bin::Wallet, &["address", "list"], Some(20))?; + let mut cmd = run!(test, Bin::Wallet, &["list", "--addr"], Some(20))?; cmd.exp_string(&format!("\"{}\":", gen_address_alias))?; cmd.exp_string(&format!("\"{}\":", add_address_alias))?; From 3cc0c0a8248b5cf94cefea01e9fb7fabc592d097 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Tue, 12 Dec 2023 16:54:07 +0100 Subject: [PATCH 32/46] Fix key / address listing --- apps/src/lib/cli.rs | 51 +++++++++----- apps/src/lib/cli/wallet.rs | 140 +++++++++++++++++++++---------------- sdk/src/args.rs | 10 +-- 3 files changed, 119 insertions(+), 82 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 187b497b93..4d4b2e69a6 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2948,6 +2948,7 @@ pub mod args { pub const TOKEN: Arg = arg("token"); pub const TRANSFER_SOURCE: Arg = arg("source"); pub const TRANSFER_TARGET: Arg = arg("target"); + pub const TRANSPARENT: ArgFlag = flag("transparent"); pub const TX_HASH: Arg = arg("tx-hash"); pub const THRESHOLD: ArgOpt = arg_opt("threshold"); pub const UNSAFE_DONT_ENCRYPT: ArgFlag = flag("unsafe-dont-encrypt"); @@ -6110,14 +6111,16 @@ pub mod args { impl Args for KeyAddressList { fn parse(matches: &ArgMatches) -> Self { - let shielded = SHIELDED.parse(matches); let decrypt = DECRYPT.parse(matches); + let transparent_only = TRANSPARENT.parse(matches); + let shielded_only = SHIELDED.parse(matches); let keys_only = LIST_FIND_KEYS_ONLY.parse(matches); let addresses_only = LIST_FIND_ADDRESSES_ONLY.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { - shielded, decrypt, + transparent_only, + shielded_only, keys_only, addresses_only, unsafe_show_secret, @@ -6125,22 +6128,34 @@ pub mod args { } fn def(app: App) -> App { - app.arg(SHIELDED.def().help( - "List viewing / spending keys and payment addresses for the \ - shielded pool.", - )) - .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg(LIST_FIND_KEYS_ONLY.def().help("List keys only.")) - .arg(LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only.")) - .group(ArgGroup::new("only_group").args([ - LIST_FIND_KEYS_ONLY.name, - LIST_FIND_ADDRESSES_ONLY.name, - ])) - .arg( - UNSAFE_SHOW_SECRET - .def() - .help("UNSAFE: Print the secret / spending keys."), - ) + app.arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) + .arg( + TRANSPARENT + .def() + .help("List transparent keys / addresses only."), + ) + .arg( + SHIELDED.def().help( + "List keys / addresses of the shielded pool only.", + ), + ) + .group( + ArgGroup::new("only_group_1") + .args([TRANSPARENT.name, SHIELDED.name]), + ) + .arg(LIST_FIND_KEYS_ONLY.def().help("List keys only.")) + .arg( + LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only."), + ) + .group(ArgGroup::new("only_group_2").args([ + LIST_FIND_KEYS_ONLY.name, + LIST_FIND_ADDRESSES_ONLY.name, + ])) + .arg( + UNSAFE_SHOW_SECRET + .def() + .help("UNSAFE: Print the secret / spending keys."), + ) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index b0bd6ba8bb..fbab002af6 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -73,21 +73,23 @@ impl CliApi { /// List spending keys. fn spending_keys_list( - ctx: Context, + wallet: &Wallet, io: &impl Io, decrypt: bool, unsafe_show_secret: bool, + show_hint: bool, ) { - let wallet = load_wallet(ctx); let known_view_keys = wallet.get_viewing_keys(); let known_spend_keys = wallet.get_spending_keys(); if known_view_keys.is_empty() { - display_line!( - io, - "No known keys. Try `add --alias my-addr --value ...` to add a \ - new key to the wallet, or `gen --shielded --alias my-key` to \ - generate a new key.", - ); + if show_hint { + display_line!( + io, + "No known keys. Try `add --alias my-addr --value ...` to add \ + a new key to the wallet, or `gen --shielded --alias my-key` \ + to generate a new key.", + ); + } } else { let stdout = io::stdout(); let mut w = stdout.lock(); @@ -147,15 +149,20 @@ fn spending_keys_list( } /// List payment addresses. -fn payment_addresses_list(ctx: Context, io: &impl Io) { - let wallet = load_wallet(ctx); +fn payment_addresses_list( + wallet: &Wallet, + io: &impl Io, + show_hint: bool, +) { let known_addresses = wallet.get_payment_addrs(); if known_addresses.is_empty() { - display_line!( - io, - "No known payment addresses. Try `gen-payment-addr --alias \ - my-payment-addr` to generate a new payment address.", - ); + if show_hint { + display_line!( + io, + "No known payment addresses. Try `gen-payment-addr --alias \ + my-payment-addr` to generate a new payment address.", + ); + } } else { let stdout = io::stdout(); let mut w = stdout.lock(); @@ -488,47 +495,52 @@ async fn key_derive( } } -/// List keys -fn key_list( +/// List keys and addresses +fn key_address_list( ctx: Context, io: &impl Io, args::KeyAddressList { - shielded, decrypt, + transparent_only, + shielded_only, + keys_only, + addresses_only, unsafe_show_secret, - .. }: args::KeyAddressList, ) { - if !shielded { - transparent_keys_list(ctx, io, decrypt, unsafe_show_secret) - } else { - spending_keys_list(ctx, io, decrypt, unsafe_show_secret) - } -} - -/// List addresses -fn address_list( - ctx: Context, - io: &impl Io, - args::KeyAddressList { shielded, .. }: args::KeyAddressList, -) { - if !shielded { - transparent_addresses_list(ctx, io) - } else { - payment_addresses_list(ctx, io) + let wallet = load_wallet(ctx); + if !shielded_only { + if !addresses_only { + transparent_keys_list( + &wallet, + io, + decrypt, + unsafe_show_secret, + transparent_only && keys_only, + ) + } + if !keys_only { + transparent_addresses_list( + &wallet, + io, + transparent_only && addresses_only, + ) + } } -} -/// List keys and addresses -fn key_address_list( - ctx: Context, - io: &impl Io, - args_key_address_list: args::KeyAddressList, -) { - if !args_key_address_list.addresses_only { - key_list(ctx, io, args_key_address_list) - } else if !args_key_address_list.keys_only { - address_list(ctx, io, args_key_address_list) + if !transparent_only { + if !addresses_only { + spending_keys_list( + &wallet, + io, + decrypt, + unsafe_show_secret, + shielded_only && keys_only, + ) + } + if !keys_only { + payment_addresses_list(&wallet, io, shielded_only && addresses_only) + } } } @@ -892,18 +904,21 @@ fn shielded_key_address_find_by_alias( /// List all known keys. fn transparent_keys_list( - ctx: Context, + wallet: &Wallet, io: &impl Io, decrypt: bool, unsafe_show_secret: bool, + show_hint: bool, ) { - let wallet = load_wallet(ctx); let known_public_keys = wallet.get_public_keys(); if known_public_keys.is_empty() { - display_line!( - io, - "No known keys. Try `gen --alias my-key` to generate a new key.", - ); + if show_hint { + display_line!( + io, + "No known keys. Try `gen --alias my-key` to generate a new \ + key.", + ); + } } else { let stdout = io::stdout(); let mut w = stdout.lock(); @@ -1010,15 +1025,20 @@ fn key_import( } /// List all known transparent addresses. -fn transparent_addresses_list(ctx: Context, io: &impl Io) { - let wallet = load_wallet(ctx); +fn transparent_addresses_list( + wallet: &Wallet, + io: &impl Io, + show_hint: bool, +) { let known_addresses = wallet.get_addresses(); if known_addresses.is_empty() { - display_line!( - io, - "No known addresses. Try `gen --alias my-addr` to generate a new \ - implicit address.", - ); + if show_hint { + display_line!( + io, + "No known addresses. Try `gen --alias my-addr` to generate a \ + new implicit address.", + ); + } } else { let stdout = io::stdout(); let mut w = stdout.lock(); diff --git a/sdk/src/args.rs b/sdk/src/args.rs index c5c070baa4..ee18335dfd 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2079,12 +2079,14 @@ pub struct KeyDerive { } /// Wallet list arguments -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct KeyAddressList { - /// Whether to list MASP spending keys - pub shielded: bool, - /// Don't decrypt keys + /// Whether to decrypt secret / spending keys pub decrypt: bool, + /// Whether to list transparent secret keys only + pub transparent_only: bool, + /// Whether to list MASP spending keys only + pub shielded_only: bool, /// List keys only pub keys_only: bool, /// List addresses only From 616987fe73a04f92d63b5f1a9ffba614c5e75453 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 13 Dec 2023 22:15:53 +0100 Subject: [PATCH 33/46] Fix `find` command; refactoring --- apps/src/lib/cli.rs | 89 ++++++------- apps/src/lib/cli/wallet.rs | 256 ++++++++++++++++++++++--------------- sdk/src/args.rs | 8 +- sdk/src/wallet/mod.rs | 25 +++- 4 files changed, 214 insertions(+), 164 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 4d4b2e69a6..3718685087 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6111,57 +6111,54 @@ pub mod args { impl Args for KeyAddressList { fn parse(matches: &ArgMatches) -> Self { - let decrypt = DECRYPT.parse(matches); let transparent_only = TRANSPARENT.parse(matches); let shielded_only = SHIELDED.parse(matches); let keys_only = LIST_FIND_KEYS_ONLY.parse(matches); let addresses_only = LIST_FIND_ADDRESSES_ONLY.parse(matches); + let decrypt = DECRYPT.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { - decrypt, transparent_only, shielded_only, keys_only, addresses_only, + decrypt, unsafe_show_secret, } } fn def(app: App) -> App { - app.arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg( - TRANSPARENT - .def() - .help("List transparent keys / addresses only."), - ) - .arg( - SHIELDED.def().help( - "List keys / addresses of the shielded pool only.", - ), - ) - .group( - ArgGroup::new("only_group_1") - .args([TRANSPARENT.name, SHIELDED.name]), - ) - .arg(LIST_FIND_KEYS_ONLY.def().help("List keys only.")) - .arg( - LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only."), - ) - .group(ArgGroup::new("only_group_2").args([ - LIST_FIND_KEYS_ONLY.name, - LIST_FIND_ADDRESSES_ONLY.name, - ])) - .arg( - UNSAFE_SHOW_SECRET - .def() - .help("UNSAFE: Print the secret / spending keys."), - ) + app.arg( + TRANSPARENT + .def() + .help("List transparent keys / addresses only."), + ) + .arg( + SHIELDED + .def() + .help("List keys / addresses of the shielded pool only."), + ) + .group( + ArgGroup::new("only_group_1") + .args([TRANSPARENT.name, SHIELDED.name]), + ) + .arg(LIST_FIND_KEYS_ONLY.def().help("List keys only.")) + .arg(LIST_FIND_ADDRESSES_ONLY.def().help("List addresses only.")) + .group(ArgGroup::new("only_group_2").args([ + LIST_FIND_KEYS_ONLY.name, + LIST_FIND_ADDRESSES_ONLY.name, + ])) + .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) + .arg( + UNSAFE_SHOW_SECRET + .def() + .help("UNSAFE: Print the secret / spending keys."), + ) } } impl Args for KeyAddressFind { fn parse(matches: &ArgMatches) -> Self { - let shielded = SHIELDED.parse(matches); let alias = ALIAS_OPT.parse(matches); let address = RAW_ADDRESS_OPT.parse(matches); let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); @@ -6169,9 +6166,9 @@ pub mod args { let payment_address = RAW_PAYMENT_ADDRESS_OPT.parse(matches); let keys_only = LIST_FIND_KEYS_ONLY.parse(matches); let addresses_only = LIST_FIND_ADDRESSES_ONLY.parse(matches); + let decrypt = DECRYPT.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { - shielded, alias, address, public_key, @@ -6179,28 +6176,21 @@ pub mod args { payment_address, keys_only, addresses_only, + decrypt, unsafe_show_secret, } } fn def(app: App) -> App { app.arg( - SHIELDED.def().help( - "Find keys and payment addresses for the shielded pool.", - ), - ) - .arg( ALIAS_OPT .def() .help("An alias associated with the keys / addresses."), ) .arg( - RAW_ADDRESS_OPT - .def() - .help( - "The bech32m encoded string of a transparent address.", - ) - .conflicts_with(SHIELDED.name), + RAW_ADDRESS_OPT.def().help( + "The bech32m encoded string of a transparent address.", + ), ) .arg( RAW_PUBLIC_KEY_OPT.def().help( @@ -6210,15 +6200,9 @@ pub mod args { .arg(RAW_PUBLIC_KEY_HASH_OPT.def().help( "A public key hash associated with the transparent keypair.", )) - .arg( - RAW_PAYMENT_ADDRESS_OPT - .def() - .help( - "The bech32m encoded string of a shielded payment \ - address.", - ) - .conflicts_with(SHIELDED.name), - ) + .arg(RAW_PAYMENT_ADDRESS_OPT.def().help( + "The bech32m encoded string of a shielded payment address.", + )) .group( ArgGroup::new("addr_find_args") .args([ @@ -6240,6 +6224,7 @@ pub mod args { "Use pre-genesis wallet, instead of for the current chain, if \ any.", )) + .arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) .arg( UNSAFE_SHOW_SECRET .def() diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index fbab002af6..bfb9ec9eda 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -91,27 +91,26 @@ fn spending_keys_list( ); } } else { - let stdout = io::stdout(); - let mut w = stdout.lock(); - display_line!(io, &mut w; "Known keys:").unwrap(); + let mut w_lock = io::stdout().lock(); + display_line!(io, &mut w_lock; "Known shielded keys:").unwrap(); for (alias, key) in known_view_keys { - display!(io, &mut w; " Alias \"{}\"", alias).unwrap(); + display!(io, &mut w_lock; " Alias \"{}\"", alias).unwrap(); let spending_key_opt = known_spend_keys.get(&alias); // If this alias is associated with a spending key, indicate whether // or not the spending key is encrypted // TODO: consider turning if let into match if let Some(spending_key) = spending_key_opt { if spending_key.is_encrypted() { - display_line!(io, &mut w; " (encrypted):") + display_line!(io, &mut w_lock; " (encrypted):") } else { - display_line!(io, &mut w; " (not encrypted):") + display_line!(io, &mut w_lock; " (not encrypted):") } .unwrap(); } else { - display_line!(io, &mut w; ":").unwrap(); + display_line!(io, &mut w_lock; ":").unwrap(); } // Always print the corresponding viewing key - display_line!(io, &mut w; " Viewing Key: {}", key).unwrap(); + display_line!(io, &mut w_lock; " Viewing Key: {}", key).unwrap(); // A subset of viewing keys will have corresponding spending keys. // Print those too if they are available and requested. if unsafe_show_secret { @@ -121,7 +120,7 @@ fn spending_keys_list( // decrypted Ok(spending_key) => { display_line!(io, - &mut w; + &mut w_lock; " Spending key: {}", spending_key, ) .unwrap(); @@ -135,7 +134,7 @@ fn spending_keys_list( // been provided Err(err) => { display_line!(io, - &mut w; + &mut w_lock; " Couldn't decrypt the spending key: {}", err, ) @@ -164,11 +163,11 @@ fn payment_addresses_list( ); } } else { - let stdout = io::stdout(); - let mut w = stdout.lock(); - display_line!(io, &mut w; "Known payment addresses:").unwrap(); + let mut w_lock = io::stdout().lock(); + display_line!(io, &mut w_lock; "Known payment addresses:").unwrap(); for (alias, address) in sorted(known_addresses) { - display_line!(io, &mut w; " \"{}\": {}", alias, address).unwrap(); + display_line!(io, &mut w_lock; " \"{}\": {}", alias, address) + .unwrap(); } } } @@ -549,7 +548,6 @@ fn key_address_find( ctx: Context, io: &impl Io, args::KeyAddressFind { - shielded, alias, address, public_key, @@ -557,29 +555,33 @@ fn key_address_find( payment_address, keys_only, addresses_only, + decrypt, unsafe_show_secret, }: args::KeyAddressFind, ) { if let Some(alias) = alias { // Search keys and addresses by alias - if !shielded { - transparent_key_address_find_by_alias( - ctx, - io, - alias, - keys_only, - addresses_only, - unsafe_show_secret, - ) - } else { - shielded_key_address_find_by_alias( - ctx, - io, - alias, - keys_only, - addresses_only, - unsafe_show_secret, - ) + let mut wallet = load_wallet(ctx); + let found_transparent = transparent_key_address_find_by_alias( + &mut wallet, + io, + alias.clone(), + keys_only, + addresses_only, + decrypt, + unsafe_show_secret, + ); + let found_shielded = shielded_key_address_find_by_alias( + &mut wallet, + io, + alias.clone(), + keys_only, + addresses_only, + decrypt, + unsafe_show_secret, + ); + if !found_transparent && !found_shielded { + display_line!(io, "Alias \"{}\" not found.", alias); } } else if address.is_some() { // Search alias by address @@ -707,7 +709,7 @@ fn transparent_key_find( let found_keypair = match public_key { Some(pk) => wallet.find_key_by_pk(&pk, None), None => { - let alias = alias.or(public_key_hash); + let alias = alias.map(|a| a.to_lowercase()).or(public_key_hash); match alias { None => { edisplay_line!( @@ -717,9 +719,7 @@ fn transparent_key_find( ); cli::safe_exit(1) } - Some(alias) => { - wallet.find_secret_key(alias.to_lowercase(), None) - } + Some(alias) => wallet.find_secret_key(alias, None), } } }; @@ -821,85 +821,133 @@ fn payment_address_or_alias_find( /// Find transparent addresses and keys by alias fn transparent_key_address_find_by_alias( - ctx: Context, + wallet: &mut Wallet, io: &impl Io, alias: String, keys_only: bool, addresses_only: bool, + decrypt: bool, unsafe_show_secret: bool, -) { - let mut wallet = load_wallet(ctx); +) -> bool { let alias = alias.to_lowercase(); - if let Some(keypair) = (!addresses_only) - .then_some(()) - .and_then(|_| wallet.find_secret_key(&alias, None).ok()) - { - let pkh: PublicKeyHash = (&keypair.ref_to()).into(); - display_line!(io, "Public key hash: {}", pkh); - display_line!(io, "Public key: {}", keypair.ref_to()); - if unsafe_show_secret { - display_line!(io, "Secret key: {}", keypair); + let mut w_lock = io::stdout().lock(); + let mut found = false; + + // Find transparent keys + if !addresses_only { + // Check if alias is a public key + if let Ok(public_key) = wallet.find_public_key(&alias) { + found = true; + display_line!(io, &mut w_lock; "Found transparent keys:").unwrap(); + let encrypted = match wallet.is_encrypted_secret_key(&alias) { + None => "external", + Some(res) if res => "encrypted", + _ => "not encrypted", + }; + display_line!(io, + &mut w_lock; + " Alias \"{}\" ({}):", alias, encrypted, + ) + .unwrap(); + let pkh = PublicKeyHash::from(&public_key); + display_line!(io, &mut w_lock; " Public key hash: {}", pkh) + .unwrap(); + display_line!( + io, + &mut w_lock; + " Public key: {}", + public_key + ) + .unwrap(); + if decrypt { + // Check if alias is also a secret key + if let Ok(keypair) = wallet.find_secret_key(&alias, None) { + if unsafe_show_secret { + display_line!(io, &mut w_lock; " Secret key: {}", keypair) + .unwrap(); + } + } + } } - } else if let Some(address) = (!keys_only) - .then_some(()) - .and_then(|_| wallet.find_address(&alias)) - { - display_line!(io, "Found address {}", address.to_pretty_string()); - } else if !addresses_only && !keys_only { - // Otherwise alias cannot be referring to any shielded value - display_line!( - io, - "No transparent address or key with alias {} found. Use the \ - command `list` to see all the known transparent addresses and \ - keys.", - alias - ); } + + // Find transparent address + if !keys_only { + if let Some(address) = wallet.find_address(&alias) { + found = true; + display_line!(io, &mut w_lock; "Found transparent address:") + .unwrap(); + display_line!(io, + &mut w_lock; + " \"{}\": {}", alias, address.to_pretty_string(), + ) + .unwrap(); + } + } + + found } /// Find shielded payment address and keys by alias fn shielded_key_address_find_by_alias( - ctx: Context, + wallet: &mut Wallet, io: &impl Io, alias: String, keys_only: bool, addresses_only: bool, + decrypt: bool, unsafe_show_secret: bool, -) { - let mut wallet = load_wallet(ctx); +) -> bool { let alias = alias.to_lowercase(); - if let Some(viewing_key) = (!addresses_only) - .then_some(()) - .and_then(|_| wallet.find_viewing_key(&alias).ok()) - { + let mut w_lock = io::stdout().lock(); + let mut found = false; + + // Find shielded keys + if !addresses_only { + let encrypted = match wallet.is_encrypted_spending_key(&alias) { + None => "external", + Some(res) if res => "encrypted", + _ => "not encrypted", + }; // Check if alias is a viewing key - display_line!(io, "Viewing key: {}", viewing_key); - if unsafe_show_secret { - // Check if alias is also a spending key - match wallet.find_spending_key(&alias, None) { - Ok(spending_key) => { - display_line!(io, "Spending key: {}", spending_key) + if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { + found = true; + display_line!(io, &mut w_lock; "Found shielded keys:").unwrap(); + display_line!(io, + &mut w_lock; + " Alias \"{}\" ({}):", alias, encrypted, + ) + .unwrap(); + display_line!(io, &mut w_lock; " Viewing key: {}", viewing_key) + .unwrap(); + if decrypt { + // Check if alias is also a spending key + match wallet.find_spending_key(&alias, None) { + Ok(spending_key) => { + if unsafe_show_secret { + display_line!(io, &mut w_lock; " Spending key: {}", spending_key).unwrap(); + } + } + Err(FindKeyError::KeyNotFound(_)) => {} + Err(err) => edisplay_line!(io, "{}", err), } - Err(FindKeyError::KeyNotFound(_)) => {} - Err(err) => edisplay_line!(io, "{}", err), } } - } else if let Some(payment_addr) = (!keys_only) - .then_some(()) - .and_then(|_| wallet.find_payment_addr(&alias)) - { - // Failing that, check if alias is a payment address - display_line!(io, "Payment address: {}", payment_addr); - } else if !addresses_only && !keys_only { - // Otherwise alias cannot be referring to any shielded value - display_line!( - io, - "No shielded payment address or key with alias {} found. Use the \ - command `list --shielded` to see all the known shielded \ - addresses and keys.", - alias - ); } + + // Find payment addresses + if !keys_only { + if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + found = true; + display_line!(io, &mut w_lock; "Found payment address:").unwrap(); + display_line!(io, + &mut w_lock; + " \"{}\": {}", alias, payment_addr.to_string(), + ) + .unwrap(); + } + } + found } /// List all known keys. @@ -920,9 +968,8 @@ fn transparent_keys_list( ); } } else { - let stdout = io::stdout(); - let mut w = stdout.lock(); - display_line!(io, &mut w; "Known keys:").unwrap(); + let mut w_lock = io::stdout().lock(); + display_line!(io, &mut w_lock; "Known transparent keys:").unwrap(); let known_secret_keys = wallet.get_secret_keys(); for (alias, public_key) in known_public_keys { let stored_keypair = known_secret_keys.get(&alias); @@ -936,19 +983,19 @@ fn transparent_keys_list( Some(_) => "not encrypted", }; display_line!(io, - &mut w; + &mut w_lock; " Alias \"{}\" ({}):", alias, encrypted, ) .unwrap(); - display_line!(io, &mut w; " Public key hash: {}", PublicKeyHash::from(&public_key)) + display_line!(io, &mut w_lock; " Public key hash: {}", PublicKeyHash::from(&public_key)) .unwrap(); - display_line!(io, &mut w; " Public key: {}", public_key) + display_line!(io, &mut w_lock; " Public key: {}", public_key) .unwrap(); if let Some((stored_keypair, _pkh)) = stored_keypair { match stored_keypair.get::(decrypt, None) { Ok(keypair) if unsafe_show_secret => { display_line!(io, - &mut w; + &mut w_lock; " Secret key: {}", keypair, ) .unwrap(); @@ -959,7 +1006,7 @@ fn transparent_keys_list( } Err(err) => { display_line!(io, - &mut w; + &mut w_lock; " Couldn't decrypt the keypair: {}", err, ) .unwrap(); @@ -1040,12 +1087,11 @@ fn transparent_addresses_list( ); } } else { - let stdout = io::stdout(); - let mut w = stdout.lock(); - display_line!(io, &mut w; "Known addresses:").unwrap(); + let mut w_lock = io::stdout().lock(); + display_line!(io, &mut w_lock; "Known transparent addresses:").unwrap(); for (alias, address) in sorted(known_addresses) { display_line!(io, - &mut w; + &mut w_lock; " \"{}\": {}", alias, address.to_pretty_string(), ) .unwrap(); diff --git a/sdk/src/args.rs b/sdk/src/args.rs index ee18335dfd..dde5953025 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2081,8 +2081,6 @@ pub struct KeyDerive { /// Wallet list arguments #[derive(Clone, Copy, Debug)] pub struct KeyAddressList { - /// Whether to decrypt secret / spending keys - pub decrypt: bool, /// Whether to list transparent secret keys only pub transparent_only: bool, /// Whether to list MASP spending keys only @@ -2091,6 +2089,8 @@ pub struct KeyAddressList { pub keys_only: bool, /// List addresses only pub addresses_only: bool, + /// Whether to decrypt secret / spending keys + pub decrypt: bool, /// Show secret keys to user pub unsafe_show_secret: bool, } @@ -2098,8 +2098,6 @@ pub struct KeyAddressList { /// Wallet key / address lookup arguments #[derive(Clone, Debug)] pub struct KeyAddressFind { - /// Whether to find MASP keys / addresses - pub shielded: bool, /// Alias to find pub alias: Option, /// Address to find @@ -2114,6 +2112,8 @@ pub struct KeyAddressFind { pub keys_only: bool, /// Find addresses only pub addresses_only: bool, + /// Whether to decrypt secret / spending keys + pub decrypt: bool, /// Show secret keys to user pub unsafe_show_secret: bool, } diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index f17e4ba90f..d59d042452 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -485,6 +485,26 @@ impl Wallet { .map(|(alias, value)| (alias.into(), value)) .collect() } + + /// Check if alias is an encrypted secret key + pub fn is_encrypted_secret_key( + &self, + alias: impl AsRef, + ) -> Option { + self.store + .find_secret_key(alias) + .map(|stored_keypair| stored_keypair.is_encrypted()) + } + + /// Check if alias is an encrypted spending key + pub fn is_encrypted_spending_key( + &self, + alias: impl AsRef, + ) -> Option { + self.store + .find_spending_key(alias) + .map(|stored_spend_key| stored_spend_key.is_encrypted()) + } } impl Wallet { @@ -567,9 +587,8 @@ impl Wallet { /// the keypair for the alias. /// If no encryption password is provided, the keypair will be stored raw /// without encryption. - /// Stores the key in decrypted key cache and - /// returns the alias of the key and a reference-counting pointer to the - /// key. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and a reference-counting pointer to the key. pub fn gen_store_secret_key( &mut self, scheme: SchemeType, From bc36fa660c083a5cd4180dded8b7f67af5321173 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 13 Dec 2023 22:41:32 +0100 Subject: [PATCH 34/46] Refactoring --- apps/src/lib/cli/wallet.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index bfb9ec9eda..f7cc7af7a5 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -71,8 +71,8 @@ impl CliApi { } } -/// List spending keys. -fn spending_keys_list( +/// List shielded keys. +fn shielded_keys_list( wallet: &Wallet, io: &impl Io, decrypt: bool, @@ -94,21 +94,15 @@ fn spending_keys_list( let mut w_lock = io::stdout().lock(); display_line!(io, &mut w_lock; "Known shielded keys:").unwrap(); for (alias, key) in known_view_keys { - display!(io, &mut w_lock; " Alias \"{}\"", alias).unwrap(); let spending_key_opt = known_spend_keys.get(&alias); // If this alias is associated with a spending key, indicate whether // or not the spending key is encrypted - // TODO: consider turning if let into match - if let Some(spending_key) = spending_key_opt { - if spending_key.is_encrypted() { - display_line!(io, &mut w_lock; " (encrypted):") - } else { - display_line!(io, &mut w_lock; " (not encrypted):") - } - .unwrap(); - } else { - display_line!(io, &mut w_lock; ":").unwrap(); - } + let encrypted_status = match spending_key_opt { + None => "", + Some(spend_key) if spend_key.is_encrypted() => "(encrypted)", + _ => "(not encrypted)", + }; + display!(io, &mut w_lock; " Alias \"{}\" {}", alias, encrypted_status).unwrap(); // Always print the corresponding viewing key display_line!(io, &mut w_lock; " Viewing Key: {}", key).unwrap(); // A subset of viewing keys will have corresponding spending keys. @@ -529,7 +523,7 @@ fn key_address_list( if !transparent_only { if !addresses_only { - spending_keys_list( + shielded_keys_list( &wallet, io, decrypt, @@ -850,6 +844,7 @@ fn transparent_key_address_find_by_alias( ) .unwrap(); let pkh = PublicKeyHash::from(&public_key); + // Always print the public key and hash display_line!(io, &mut w_lock; " Public key hash: {}", pkh) .unwrap(); display_line!( @@ -860,7 +855,8 @@ fn transparent_key_address_find_by_alias( ) .unwrap(); if decrypt { - // Check if alias is also a secret key + // Check if alias is also a secret key. Decrypt and print it if + // requested. if let Ok(keypair) = wallet.find_secret_key(&alias, None) { if unsafe_show_secret { display_line!(io, &mut w_lock; " Secret key: {}", keypair) @@ -918,10 +914,12 @@ fn shielded_key_address_find_by_alias( " Alias \"{}\" ({}):", alias, encrypted, ) .unwrap(); + // Always print the viewing key display_line!(io, &mut w_lock; " Viewing key: {}", viewing_key) .unwrap(); if decrypt { - // Check if alias is also a spending key + // Check if alias is also a spending key. Decrypt and print it + // if requested. match wallet.find_spending_key(&alias, None) { Ok(spending_key) => { if unsafe_show_secret { @@ -987,10 +985,13 @@ fn transparent_keys_list( " Alias \"{}\" ({}):", alias, encrypted, ) .unwrap(); + // Always print the corresponding public key and hash display_line!(io, &mut w_lock; " Public key hash: {}", PublicKeyHash::from(&public_key)) .unwrap(); display_line!(io, &mut w_lock; " Public key: {}", public_key) .unwrap(); + // A subset of public keys will have corresponding secret keys. + // Print those too if they are available and requested. if let Some((stored_keypair, _pkh)) = stored_keypair { match stored_keypair.get::(decrypt, None) { Ok(keypair) if unsafe_show_secret => { From 5a3a5d67726313bdf11388472b8f8df4715ca1d8 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 13 Dec 2023 23:13:55 +0100 Subject: [PATCH 35/46] Allow to add public keys --- apps/src/lib/cli.rs | 8 +++---- apps/src/lib/cli/wallet.rs | 44 ++++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 3718685087..55e2cf09bd 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6257,10 +6257,10 @@ pub mod args { "Override the alias without confirmation if it already exists.", )) .arg(VALUE.def().help( - "Any value of the following:\n- transparent pool secret key \ - string\n- the bech32m encoded transparent address string\n- \ - shielded pool spending key\n- shielded pool viewing key\n- \ - shielded pool payment address ", + "Any value of the following:\n- transparent pool secret \ + key\n- transparent pool public key\n- transparent pool \ + address\n- shielded pool spending key\n- shielded pool \ + viewing key\n- shielded pool payment address ", )) .arg(UNSAFE_DONT_ENCRYPT.def().help( "UNSAFE: Do not encrypt the added keys. Do not use this for \ diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index f7cc7af7a5..a52f8128c3 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -600,6 +600,8 @@ fn key_address_find( pub enum TransparentValue { /// Transparent secret key TranspSecretKey(common::SecretKey), + /// Transparent public key + TranspPublicKey(common::PublicKey), /// Transparent address TranspAddress(Address), } @@ -608,9 +610,13 @@ impl FromStr for TransparentValue { type Err = DecodeError; fn from_str(s: &str) -> Result { - // Try to decode this value first as a secret key, then as an address + // Try to decode this value first as a secret key, then as a public key, + // then as an address common::SecretKey::from_str(s) .map(Self::TranspSecretKey) + .or_else(|_| { + common::PublicKey::from_str(s).map(Self::TranspPublicKey) + }) .or_else(|_| Address::from_str(s).map(Self::TranspAddress)) } } @@ -656,6 +662,9 @@ fn add_key_or_address( unsafe_dont_encrypt, ) } + KeyAddrAddValue::TranspValue(TransparentValue::TranspPublicKey( + pubkey, + )) => transparent_public_key_add(ctx, io, alias, alias_force, pubkey), KeyAddrAddValue::TranspValue(TransparentValue::TranspAddress( address, )) => transparent_address_add(ctx, io, alias, alias_force, address), @@ -1129,6 +1138,33 @@ fn transparent_secret_key_add( ); } +/// Add a public key to the wallet. +fn transparent_public_key_add( + ctx: Context, + io: &impl Io, + alias: String, + alias_force: bool, + pubkey: common::PublicKey, +) { + let alias = alias.to_lowercase(); + let mut wallet = load_wallet(ctx); + if wallet + .insert_public_key(alias.clone(), pubkey, None, None, alias_force) + .is_none() + { + edisplay_line!(io, "Public key not added"); + cli::safe_exit(1); + } + wallet + .save() + .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); + display_line!( + io, + "Successfully added public key with alias: \"{}\"", + alias + ); +} + /// Add a transparent address to the wallet. fn transparent_address_add( ctx: Context, @@ -1149,11 +1185,7 @@ fn transparent_address_add( wallet .save() .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); - display_line!( - io, - "Successfully added a key and an address with alias: \"{}\"", - alias - ); + display_line!(io, "Successfully added address with alias: \"{}\"", alias); } /// Load wallet for chain when `ctx.chain.is_some()` or pre-genesis wallet when From 032ce0b3154977358546775e700b7055969de34e Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 12:02:27 +0100 Subject: [PATCH 36/46] Expose store remove_alias functionality --- sdk/src/wallet/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/wallet/store.rs b/sdk/src/wallet/store.rs index 8ba66df798..f8a370b347 100644 --- a/sdk/src/wallet/store.rs +++ b/sdk/src/wallet/store.rs @@ -576,7 +576,7 @@ impl Store { } /// Completely remove the given alias from all maps in the wallet - fn remove_alias(&mut self, alias: &Alias) { + pub fn remove_alias(&mut self, alias: &Alias) { self.payment_addrs.remove_by_left(alias); self.view_keys.remove(alias); self.spend_keys.remove(alias); From ddc7370064708cf8f6a175634a62a7d485184314 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 12:04:26 +0100 Subject: [PATCH 37/46] Implement `remove` command --- apps/src/lib/cli.rs | 53 ++++++++++++++++++++++++++++++++++---- apps/src/lib/cli/wallet.rs | 18 +++++++++++++ sdk/src/args.rs | 9 +++++++ sdk/src/wallet/mod.rs | 5 ++++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 55e2cf09bd..08fcbc4341 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -483,6 +483,8 @@ pub mod cmds { KeyGen(WalletGen), /// Key derivation KeyDerive(WalletDerive), + /// Payment address generation + PayAddrGen(WalletGenPaymentAddress), /// Key / address list KeyAddrList(WalletListKeysAddresses), /// Key / address search @@ -493,38 +495,42 @@ pub mod cmds { KeyImport(WalletImportKey), /// Key / address add KeyAddrAdd(WalletAddKeyAddress), - /// Payment address generation - PayAddrGen(WalletGenPaymentAddress), + /// Key / address remove + KeyAddrRemove(WalletRemoveKeyAddress), } impl Cmd for NamadaWallet { fn add_sub(app: App) -> App { app.subcommand(WalletGen::def()) .subcommand(WalletDerive::def()) + .subcommand(WalletGenPaymentAddress::def()) .subcommand(WalletListKeysAddresses::def()) .subcommand(WalletFindKeysAddresses::def()) .subcommand(WalletExportKey::def()) .subcommand(WalletImportKey::def()) .subcommand(WalletAddKeyAddress::def()) - .subcommand(WalletGenPaymentAddress::def()) + .subcommand(WalletRemoveKeyAddress::def()) } fn parse(matches: &ArgMatches) -> Option { let gen = SubCmd::parse(matches).map(Self::KeyGen); let derive = SubCmd::parse(matches).map(Self::KeyDerive); + let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); let key_addr_list = SubCmd::parse(matches).map(Self::KeyAddrList); let key_addr_find = SubCmd::parse(matches).map(Self::KeyAddrFind); let export = SubCmd::parse(matches).map(Self::KeyExport); let import = SubCmd::parse(matches).map(Self::KeyImport); let key_addr_add = SubCmd::parse(matches).map(Self::KeyAddrAdd); - let pay_addr_gen = SubCmd::parse(matches).map(Self::PayAddrGen); + let key_addr_remove = + SubCmd::parse(matches).map(Self::KeyAddrRemove); gen.or(derive) + .or(pay_addr_gen) .or(key_addr_list) .or(key_addr_find) .or(export) .or(import) .or(key_addr_add) - .or(pay_addr_gen) + .or(key_addr_remove) } } @@ -731,6 +737,29 @@ pub mod cmds { } } + /// Remove key / address + #[derive(Clone, Debug)] + pub struct WalletRemoveKeyAddress(pub args::KeyAddressRemove); + + impl SubCmd for WalletRemoveKeyAddress { + const CMD: &'static str = "remove"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::KeyAddressRemove::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Remove the given alias and all associated keys / \ + addresses from the wallet.", + ) + .add_args::() + } + } + /// Generate a payment address from a viewing key or payment address #[derive(Clone, Debug)] pub struct WalletGenPaymentAddress(pub args::PayAddressGen); @@ -2808,6 +2837,7 @@ pub mod args { pub const DESTINATION_VALIDATOR: Arg = arg("destination-validator"); pub const DISCORD_OPT: ArgOpt = arg_opt("discord-handle"); + pub const DO_IT: ArgFlag = flag("do-it"); pub const DONT_ARCHIVE: ArgFlag = flag("dont-archive"); pub const DONT_PREFETCH_WASM: ArgFlag = flag("dont-prefetch-wasm"); pub const DRY_RUN_TX: ArgFlag = flag("dry-run"); @@ -6269,6 +6299,19 @@ pub mod args { } } + impl Args for KeyAddressRemove { + fn parse(matches: &ArgMatches) -> Self { + let alias = ALIAS.parse(matches); + let do_it = DO_IT.parse(matches); + Self { alias, do_it } + } + + fn def(app: App) -> App { + app.arg(ALIAS.def().help("An alias to be removed.")) + .arg(DO_IT.def().help("Confirm alias removal.").required(true)) + } + } + impl Args for KeyExport { fn parse(matches: &ArgMatches) -> Self { let shielded = SHIELDED.parse(matches); diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index a52f8128c3..c3571cdfb9 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -60,6 +60,9 @@ impl CliApi { cmds::NamadaWallet::KeyAddrAdd(cmds::WalletAddKeyAddress(args)) => { key_address_add(ctx, io, args) } + cmds::NamadaWallet::KeyAddrRemove( + cmds::WalletRemoveKeyAddress(args), + ) => key_address_remove(ctx, io, args), cmds::NamadaWallet::PayAddrGen(cmds::WalletGenPaymentAddress( args, )) => { @@ -699,6 +702,21 @@ fn key_address_add( add_key_or_address(ctx, io, alias, alias_force, value, unsafe_dont_encrypt) } +/// Remove keys and addresses +fn key_address_remove( + ctx: Context, + io: &impl Io, + args::KeyAddressRemove { alias, .. }: args::KeyAddressRemove, +) { + let alias = alias.to_lowercase(); + let mut wallet = load_wallet(ctx); + wallet.remove_all_by_alias(alias.clone()); + wallet + .save() + .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); + display_line!(io, "Successfully removed alias: \"{}\"", alias); +} + /// Find a keypair in the wallet store. fn transparent_key_find( ctx: Context, diff --git a/sdk/src/args.rs b/sdk/src/args.rs index dde5953025..f313a50664 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2152,6 +2152,15 @@ pub struct KeyAddressAdd { pub unsafe_dont_encrypt: bool, } +/// Wallet key / address remove arguments +#[derive(Clone, Debug)] +pub struct KeyAddressRemove { + /// Address alias + pub alias: String, + /// Confirmation to remove the alias + pub do_it: bool, +} + /// Generate payment address arguments #[derive(Clone, Debug)] pub struct PayAddressGen { diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index d59d042452..c69300052f 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -989,4 +989,9 @@ impl Wallet { pub fn extend(&mut self, wallet: Self) { self.store.extend(wallet.store) } + + /// Remove keys and addresses associated with the given alias + pub fn remove_all_by_alias(&mut self, alias: String) { + self.store.remove_alias(&alias.into()) + } } From 687b95e447989720561380580d50b79ca153c24e Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 15:01:07 +0100 Subject: [PATCH 38/46] Fix key import --- apps/src/lib/cli/wallet.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index c3571cdfb9..11c72ecc82 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -15,7 +15,7 @@ use masp_primitives::zip32::ExtendedFullViewingKey; use namada::types::address::{Address, DecodeError}; use namada::types::io::Io; use namada::types::key::*; -use namada::types::masp::{MaspValue, PaymentAddress}; +use namada::types::masp::{ExtendedSpendingKey, MaspValue, PaymentAddress}; use namada_sdk::masp::find_valid_diversifier; use namada_sdk::wallet::{ DecryptionError, DerivationPath, DerivationPathError, FindKeyError, Wallet, @@ -1086,17 +1086,37 @@ fn key_import( unsafe_dont_encrypt, }: args::KeyImport, ) { - let file_data = std::fs::read_to_string(file_path).unwrap_or_else(|err| { + let file_data = std::fs::read(file_path).unwrap_or_else(|err| { edisplay_line!(io, "{}", err); display_line!(io, "No changes are persisted. Exiting."); cli::safe_exit(1) }); - let value = KeyAddrAddValue::from_str(&file_data).unwrap_or_else(|err| { - edisplay_line!(io, "{}", err); + if let Ok(sk) = common::SecretKey::try_from_slice(&file_data) { + transparent_secret_key_add( + ctx, + io, + alias, + alias_force, + sk, + unsafe_dont_encrypt, + ); + } else if let Ok(spend_key) = + ExtendedSpendingKey::try_from_slice(&file_data) + { + let masp_value = MaspValue::ExtendedSpendingKey(spend_key); + shielded_key_address_add( + ctx, + io, + alias, + alias_force, + masp_value, + unsafe_dont_encrypt, + ); + } else { + display_line!(io, "Could not parse the data."); display_line!(io, "No changes are persisted. Exiting."); cli::safe_exit(1) - }); - add_key_or_address(ctx, io, alias, alias_force, value, unsafe_dont_encrypt) + } } /// List all known transparent addresses. From 2f2b4b03ac38073de5dd0ad83f0e592d0cd535c0 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 15:01:29 +0100 Subject: [PATCH 39/46] Minor --- apps/src/lib/cli/wallet.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 11c72ecc82..fdf9e94ccb 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -972,6 +972,7 @@ fn shielded_key_address_find_by_alias( .unwrap(); } } + found } From 1d2b9356fc5fcbe3f03466937ab025c80002499c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 15:13:30 +0100 Subject: [PATCH 40/46] Fix export / import --- apps/src/lib/cli.rs | 12 ++++-------- apps/src/lib/cli/wallet.rs | 38 ++++++++++++++++++-------------------- sdk/src/args.rs | 2 -- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 08fcbc4341..a940d78135 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6314,18 +6314,14 @@ pub mod args { impl Args for KeyExport { fn parse(matches: &ArgMatches) -> Self { - let shielded = SHIELDED.parse(matches); let alias = ALIAS.parse(matches); - Self { shielded, alias } + Self { alias } } fn def(app: App) -> App { app.arg( - SHIELDED - .def() - .help("Whether to export the shielded spending key."), + ALIAS.def().help("The alias of the key you wish to export."), ) - .arg(ALIAS.def().help("The alias of the key you wish to export.")) } } @@ -6354,8 +6350,8 @@ pub mod args { .help("An alias to be associated with the imported entry."), ) .arg(UNSAFE_DONT_ENCRYPT.def().help( - "UNSAFE: Do not encrypt the added keys. Do not use this for \ - keys used in a live network.", + "UNSAFE: Do not encrypt the imported keys. Do not use this \ + for keys used in a live network.", )) } } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index fdf9e94ccb..fdb2554428 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -1050,30 +1050,28 @@ fn transparent_keys_list( fn key_export( ctx: Context, io: &impl Io, - args::KeyExport { shielded, alias }: args::KeyExport, + args::KeyExport { alias }: args::KeyExport, ) { let alias = alias.to_lowercase(); let mut wallet = load_wallet(ctx); - if !shielded { - wallet - .find_secret_key(&alias, None) - .map(|sk| Box::new(sk) as Box) - } else { - wallet + let key_to_export = wallet + .find_secret_key(&alias, None) + .map(|sk| Box::new(sk) as Box) + .or(wallet .find_spending_key(&alias, None) - .map(|spk| Box::new(spk) as Box) - } - .map(|key| { - let file_data = key.serialize_to_vec(); - let file_name = format!("key_{}", alias); - let mut file = File::create(&file_name).unwrap(); - file.write_all(file_data.as_ref()).unwrap(); - display_line!(io, "Exported to file {}", file_name); - }) - .unwrap_or_else(|err| { - edisplay_line!(io, "{}", err); - cli::safe_exit(1) - }) + .map(|spk| Box::new(spk) as Box)); + key_to_export + .map(|key| { + let file_data = key.serialize_to_vec(); + let file_name = format!("key_{}", alias); + let mut file = File::create(&file_name).unwrap(); + file.write_all(file_data.as_ref()).unwrap(); + display_line!(io, "Exported to file {}", file_name); + }) + .unwrap_or_else(|err| { + edisplay_line!(io, "{}", err); + cli::safe_exit(1) + }) } /// Import a transparent keypair / MASP spending key from a file. diff --git a/sdk/src/args.rs b/sdk/src/args.rs index f313a50664..379727242b 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2120,8 +2120,6 @@ pub struct KeyAddressFind { /// Wallet key export arguments #[derive(Clone, Debug)] pub struct KeyExport { - /// Whether to export a MASP spending key - pub shielded: bool, /// Key alias pub alias: String, } From df0b1963be268cae233a5eda023ef47df950e54f Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 17:20:32 +0100 Subject: [PATCH 41/46] Improve output if keypair could not be decypted --- apps/src/lib/cli/wallet.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index fdb2554428..9bb37e1968 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -884,11 +884,15 @@ fn transparent_key_address_find_by_alias( if decrypt { // Check if alias is also a secret key. Decrypt and print it if // requested. - if let Ok(keypair) = wallet.find_secret_key(&alias, None) { - if unsafe_show_secret { - display_line!(io, &mut w_lock; " Secret key: {}", keypair) + match wallet.find_secret_key(&alias, None) { + Ok(keypair) => { + if unsafe_show_secret { + display_line!(io, &mut w_lock; " Secret key: {}", keypair) .unwrap(); + } } + Err(FindKeyError::KeyNotFound(_)) => {} + Err(err) => edisplay_line!(io, "{}", err), } } } From 7d9e33cbdf055f82e112f38a1be9a3017847e83b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 17:21:51 +0100 Subject: [PATCH 42/46] Improve decryption status line --- apps/src/lib/cli/wallet.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 9bb37e1968..4b0ebe568b 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -101,11 +101,11 @@ fn shielded_keys_list( // If this alias is associated with a spending key, indicate whether // or not the spending key is encrypted let encrypted_status = match spending_key_opt { - None => "", - Some(spend_key) if spend_key.is_encrypted() => "(encrypted)", - _ => "(not encrypted)", + None => "external", + Some(spend_key) if spend_key.is_encrypted() => "encrypted", + _ => "not encrypted", }; - display!(io, &mut w_lock; " Alias \"{}\" {}", alias, encrypted_status).unwrap(); + display!(io, &mut w_lock; " Alias \"{}\" ({})", alias, encrypted_status).unwrap(); // Always print the corresponding viewing key display_line!(io, &mut w_lock; " Viewing Key: {}", key).unwrap(); // A subset of viewing keys will have corresponding spending keys. From 651a7e34066121ec10d7c608042c45a2e883497b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 18:05:19 +0100 Subject: [PATCH 43/46] Adapt e2e tests to new wallet cli --- tests/src/e2e/helpers.rs | 4 ++- tests/src/e2e/wallet_tests.rs | 66 +++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 730f4f1a55..57446555fc 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -110,7 +110,8 @@ pub fn find_address(test: &Test, alias: impl AsRef) -> Result
{ &["find", "--addr", "--alias", alias.as_ref()], Some(10) )?; - let (unread, matched) = find.exp_regex("Found address .*")?; + find.exp_string("Found transparent address:")?; + let (unread, matched) = find.exp_regex("\".*\": .*")?; let address_str = strip_trailing_newline(&matched) .trim() .rsplit_once(' ') @@ -249,6 +250,7 @@ pub fn find_keypair( "--keys", "--alias", alias.as_ref(), + "--decrypt", "--unsafe-show-secret" ], Some(10) diff --git a/tests/src/e2e/wallet_tests.rs b/tests/src/e2e/wallet_tests.rs index a79da138b8..370bdac670 100644 --- a/tests/src/e2e/wallet_tests.rs +++ b/tests/src/e2e/wallet_tests.rs @@ -46,14 +46,19 @@ fn wallet_encrypted_key_cmds() -> Result<()> { let mut cmd = run!( test, Bin::Wallet, - &["find", "--keys", "--alias", key_alias], + &["find", "--keys", "--alias", key_alias, "--decrypt"], Some(20), )?; + cmd.exp_string("Found transparent keys:")?; + cmd.exp_string(&format!( + " Alias \"{}\" (encrypted):", + key_alias.to_lowercase() + ))?; + cmd.exp_string(" Public key hash:")?; + cmd.exp_string(" Public key:")?; cmd.exp_string("Enter your decryption password:")?; cmd.send_line(password)?; - cmd.exp_string("Public key hash:")?; - cmd.exp_string("Public key:")?; // 3. key list let mut cmd = run!(test, Bin::Wallet, &["list", "--keys"], Some(20))?; @@ -72,7 +77,7 @@ fn wallet_encrypted_key_cmds() -> Result<()> { #[test] fn wallet_encrypted_key_cmds_env_var() -> Result<()> { let test = setup::single_node_net()?; - let key_alias = "test_key_1"; + let key_alias = "Test_Key_1"; let password = "VeRySeCuR3"; env::set_var("NAMADA_WALLET_PASSWORD", password); @@ -86,23 +91,31 @@ fn wallet_encrypted_key_cmds_env_var() -> Result<()> { cmd.exp_string(&format!( "Successfully added a key and an address with alias: \"{}\"", - key_alias + key_alias.to_lowercase() ))?; // 2. key find let mut cmd = run!( test, Bin::Wallet, - &["find", "--keys", "--alias", key_alias], + &["find", "--keys", "--alias", key_alias, "--decrypt"], Some(20), )?; - cmd.exp_string("Public key hash:")?; - cmd.exp_string("Public key:")?; + cmd.exp_string("Found transparent keys:")?; + cmd.exp_string(&format!( + " Alias \"{}\" (encrypted):", + key_alias.to_lowercase() + ))?; + cmd.exp_string(" Public key hash:")?; + cmd.exp_string(" Public key:")?; // 3. key list let mut cmd = run!(test, Bin::Wallet, &["list", "--keys"], Some(20))?; - cmd.exp_string(&format!("Alias \"{}\" (encrypted):", key_alias))?; + cmd.exp_string(&format!( + " Alias \"{}\" (encrypted):", + key_alias.to_lowercase() + ))?; Ok(()) } @@ -114,7 +127,7 @@ fn wallet_encrypted_key_cmds_env_var() -> Result<()> { #[test] fn wallet_unencrypted_key_cmds() -> Result<()> { let test = setup::single_node_net()?; - let key_alias = "test_key_1"; + let key_alias = "Test_Key_1"; // 1. key gen let mut cmd = run!( @@ -125,7 +138,7 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { )?; cmd.exp_string(&format!( "Successfully added a key and an address with alias: \"{}\"", - key_alias + key_alias.to_lowercase() ))?; // 2. key find @@ -136,12 +149,20 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { Some(20), )?; - cmd.exp_string("Public key hash:")?; - cmd.exp_string("Public key:")?; + cmd.exp_string("Found transparent keys:")?; + cmd.exp_string(&format!( + " Alias \"{}\" (not encrypted):", + key_alias.to_lowercase() + ))?; + cmd.exp_string(" Public key hash:")?; + cmd.exp_string(" Public key:")?; // 3. key list let mut cmd = run!(test, Bin::Wallet, &["list", "--keys"], Some(20))?; - cmd.exp_string(&format!("Alias \"{}\" (not encrypted):", key_alias))?; + cmd.exp_string(&format!( + " Alias \"{}\" (not encrypted):", + key_alias.to_lowercase() + ))?; Ok(()) } @@ -154,8 +175,8 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { #[test] fn wallet_address_cmds() -> Result<()> { let test = setup::single_node_net()?; - let gen_address_alias = "test_address_1"; - let add_address_alias = "test_address_2"; + let gen_address_alias = "Test_Address_1"; + let add_address_alias = "Test_Address_2"; let add_address = "tnam1q82t25z5f9gmnv5sztyr8ht9tqhrw4u875qjhy56"; // 1. address gen @@ -167,7 +188,7 @@ fn wallet_address_cmds() -> Result<()> { )?; cmd.exp_string(&format!( "Successfully added a key and an address with alias: \"{}\"", - gen_address_alias + gen_address_alias.to_lowercase() ))?; // 2. address add @@ -178,8 +199,8 @@ fn wallet_address_cmds() -> Result<()> { Some(20), )?; cmd.exp_string(&format!( - "Successfully added a key and an address with alias: \"{}\"", - add_address_alias + "Successfully added an address with alias: \"{}\"", + add_address_alias.to_lowercase() ))?; // 3. address find @@ -189,13 +210,14 @@ fn wallet_address_cmds() -> Result<()> { &["find", "--addr", "--alias", gen_address_alias], Some(20), )?; - cmd.exp_string("Found address")?; + cmd.exp_string("Found transparent address:")?; // 4. address list let mut cmd = run!(test, Bin::Wallet, &["list", "--addr"], Some(20))?; - cmd.exp_string(&format!("\"{}\":", gen_address_alias))?; - cmd.exp_string(&format!("\"{}\":", add_address_alias))?; + cmd.exp_string("Known transparent addresses:")?; + cmd.exp_string(&format!("\"{}\":", gen_address_alias.to_lowercase()))?; + cmd.exp_string(&format!("\"{}\":", add_address_alias.to_lowercase()))?; Ok(()) } From 859defbbf88c315e219a576b5ce22f7d1344dd2a Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 14 Dec 2023 23:39:03 +0100 Subject: [PATCH 44/46] Fix messages --- apps/src/lib/cli/wallet.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 4b0ebe568b..21c5526dbe 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -105,7 +105,7 @@ fn shielded_keys_list( Some(spend_key) if spend_key.is_encrypted() => "encrypted", _ => "not encrypted", }; - display!(io, &mut w_lock; " Alias \"{}\" ({})", alias, encrypted_status).unwrap(); + display_line!(io, &mut w_lock; " Alias \"{}\" ({}):", alias, encrypted_status).unwrap(); // Always print the corresponding viewing key display_line!(io, &mut w_lock; " Viewing Key: {}", key).unwrap(); // A subset of viewing keys will have corresponding spending keys. @@ -1201,7 +1201,7 @@ fn transparent_public_key_add( .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); display_line!( io, - "Successfully added public key with alias: \"{}\"", + "Successfully added a public key with alias: \"{}\"", alias ); } @@ -1226,7 +1226,11 @@ fn transparent_address_add( wallet .save() .unwrap_or_else(|err| edisplay_line!(io, "{}", err)); - display_line!(io, "Successfully added address with alias: \"{}\"", alias); + display_line!( + io, + "Successfully added an address with alias: \"{}\"", + alias + ); } /// Load wallet for chain when `ctx.chain.is_some()` or pre-genesis wallet when From 26ca60c8507e0d00eee8a443db385334b4c6afb1 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 15 Dec 2023 01:39:02 +0100 Subject: [PATCH 45/46] Remove unused import --- apps/src/lib/cli/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 21c5526dbe..c6de0d6c5c 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -20,7 +20,7 @@ use namada_sdk::masp::find_valid_diversifier; use namada_sdk::wallet::{ DecryptionError, DerivationPath, DerivationPathError, FindKeyError, Wallet, }; -use namada_sdk::{display, display_line, edisplay_line}; +use namada_sdk::{display_line, edisplay_line}; use rand_core::OsRng; use crate::cli; From 600529acab7f7c0124fe52e0d0648d192b892fec Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 15 Dec 2023 17:40:29 +0100 Subject: [PATCH 46/46] Add changelog --- .../features/2260-wallet-cli-revamping-main-rebased.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/unreleased/features/2260-wallet-cli-revamping-main-rebased.md diff --git a/.changelog/unreleased/features/2260-wallet-cli-revamping-main-rebased.md b/.changelog/unreleased/features/2260-wallet-cli-revamping-main-rebased.md new file mode 100644 index 0000000000..a174f62c37 --- /dev/null +++ b/.changelog/unreleased/features/2260-wallet-cli-revamping-main-rebased.md @@ -0,0 +1,7 @@ +- The wallet CLI structure has been significantly reworked and simplified. + Alias argument is now obligatory for key generation / derivation + commands. Feature of raw (non-HD) key generation has been restored, + which was removed in the previous release. Key export / import + functionality for both transparent and shielded key kinds has been + implemented. Additionally, several other improvements have been made. + ([\#2260](https://github.com/anoma/namada/pull/2260)) \ No newline at end of file