Skip to content

Commit

Permalink
feat: add HD derivation path support
Browse files Browse the repository at this point in the history
  • Loading branch information
karbyshev committed Apr 12, 2023
1 parent 0dcad40 commit 87ed83e
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 54 deletions.
3 changes: 3 additions & 0 deletions apps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ color-eyre = "0.5.10"
config = "0.11.0"
data-encoding = "2.3.2"
derivative = "2.2.0"
derivation-path = "0.2.0"
ed25519-consensus = "1.2.0"
ferveo = {git = "https://github.com/anoma/ferveo", branch = "bat/fix-thiserror"}
ferveo-common = {git = "https://github.com/anoma/ferveo", branch = "bat/fix-thiserror"}
Expand Down Expand Up @@ -120,6 +121,7 @@ serde_bytes = "0.11.5"
serde_json = {version = "1.0.62", features = ["raw_value"]}
sha2 = "0.9.3"
signal-hook = "0.3.9"
slip10_ed25519 = "0.1.3"
# sysinfo with disabled multithread feature
sysinfo = {version = "=0.21.1", default-features = false}
tar = "0.4.37"
Expand All @@ -134,6 +136,7 @@ tendermint-proto = {version = "0.23.6", optional = true}
tendermint-rpc = {version = "0.23.6", features = ["http-client", "websocket-client"], optional = true}
thiserror = "1.0.30"
tiny-bip39 = "1.0.0"
tiny-hderive = "0.3.0"
tokio = {version = "1.8.2", features = ["full"]}
toml = "0.5.8"
tonic = "0.6.1"
Expand Down
16 changes: 14 additions & 2 deletions apps/src/bin/namada-wallet/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,17 @@ fn key_and_address_restore(
scheme,
alias,
unsafe_dont_encrypt,
use_empty_derivation_path,
derivation_path,
}: args::KeyAndAddressRestore,
) {
let mut wallet = ctx.wallet;
let derived_key = wallet.derive_key_from_user_mnemonic_code(
scheme,
alias,
unsafe_dont_encrypt,
use_empty_derivation_path,
derivation_path,
);
match derived_key {
Ok((alias, _key)) => {
Expand All @@ -341,11 +345,19 @@ fn key_and_address_gen(
alias,
unsafe_dont_encrypt,
use_mnemonic,
use_empty_derivation_path,
derivation_path,
}: args::KeyAndAddressGen,
) {
let mut wallet = ctx.wallet;
let generated_key =
wallet.gen_key(scheme, alias, unsafe_dont_encrypt, use_mnemonic);
let generated_key = wallet.gen_key(
scheme,
alias,
unsafe_dont_encrypt,
use_mnemonic,
use_empty_derivation_path,
derivation_path,
);
match generated_key {
Ok((alias, _key)) => {
wallet.save().unwrap_or_else(|err| eprintln!("{}", err));
Expand Down
59 changes: 56 additions & 3 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,10 @@ pub mod args {
arg_default_from_ctx("gas-token", DefaultFn(|| "NAM".into()));
const GENESIS_PATH: Arg<PathBuf> = arg("genesis-path");
const GENESIS_VALIDATOR: ArgOpt<String> = arg("genesis-validator").opt();
const HD_WALLET_DERIVATION_PATH: Arg<String> = arg("hd-path");
const HD_WALLET_DERIVATION_PATH_OPT: ArgOpt<String> =
HD_WALLET_DERIVATION_PATH.opt();
const HD_WALLET_USE_EMPTY_DERIVATION_PATH: ArgFlag = flag("empty-hd-path");
const LEDGER_ADDRESS_ABOUT: &str =
"Address of a ledger node as \"{scheme}://{host}:{port}\". If the \
scheme is not supplied, it is assumed to be TCP.";
Expand Down Expand Up @@ -3047,17 +3051,26 @@ pub mod args {
pub alias: Option<String>,
/// Don't encrypt the keypair
pub unsafe_dont_encrypt: bool,
/// Use empty derivation path
pub use_empty_derivation_path: bool,
/// BIP44 derivation path
pub derivation_path: Option<String>,
}

impl Args for KeyAndAddressRestore {
fn parse(matches: &ArgMatches) -> Self {
let scheme = SCHEME.parse(matches);
let alias = ALIAS_OPT.parse(matches);
let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches);
let use_empty_derivation_path =
HD_WALLET_USE_EMPTY_DERIVATION_PATH.parse(matches);
let derivation_path = HD_WALLET_DERIVATION_PATH_OPT.parse(matches);
Self {
scheme,
alias,
unsafe_dont_encrypt,
use_empty_derivation_path,
derivation_path,
}
}

Expand All @@ -3075,6 +3088,18 @@ pub mod args {
"UNSAFE: Do not encrypt the keypair. Do not use this for keys \
used in a live network.",
))
.arg(
HD_WALLET_USE_EMPTY_DERIVATION_PATH
.def()
.about("Use empty HD key derivation path."),
)
.arg(HD_WALLET_DERIVATION_PATH_OPT.def().about(
"HD key derivation path. If none provided, a scheme default \
path is used:\nThe default path for secp256k1 scheme is \
m/44'/60'/0/0/0.\nThe default path for ed25519 scheme is \
m/44'/877'/0'/0'/0'.\nFor secp256k1 scheme, all path indices \
will be promoted to hardened indexes.",
))
}
}

Expand All @@ -3087,8 +3112,12 @@ pub mod args {
pub alias: Option<String>,
/// Don't encrypt the keypair
pub unsafe_dont_encrypt: bool,
/// Use bip39 mnemonic code
/// Use BIP39 mnemonic code
pub use_mnemonic: bool,
/// Use empty derivation path
pub use_empty_derivation_path: bool,
/// BIP44 derivation path
pub derivation_path: Option<String>,
}

impl Args for KeyAndAddressGen {
Expand All @@ -3097,11 +3126,16 @@ pub mod args {
let alias = ALIAS_OPT.parse(matches);
let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches);
let use_mnemonic = MNEMONIC.parse(matches);
let use_empty_derivation_path =
HD_WALLET_USE_EMPTY_DERIVATION_PATH.parse(matches);
let derivation_path = HD_WALLET_DERIVATION_PATH_OPT.parse(matches);
Self {
scheme,
alias,
unsafe_dont_encrypt,
use_mnemonic,
use_empty_derivation_path,
derivation_path,
}
}

Expand All @@ -3120,9 +3154,28 @@ pub mod args {
used in a live network.",
))
.arg(MNEMONIC.def().about(
"Use mnemonic code for key generation. Only English wordlist \
is supported.",
"Use BIP39 mnemonic code for key generation. Only English \
wordlist is supported.",
))
.arg(
HD_WALLET_USE_EMPTY_DERIVATION_PATH
.def()
.requires(MNEMONIC.name)
.about("Use empty HD key derivation path."),
)
.arg(
HD_WALLET_DERIVATION_PATH_OPT
.def()
.requires(MNEMONIC.name)
.about(
"HD key derivation path. If none provided, a scheme \
default path is used:\nThe default path for \
secp256k1 scheme is m/44'/60'/0/0/0.\nThe default \
path for ed25519 scheme is m/44'/877'/0'/0'/0'.\nFor \
secp256k1 scheme, all path indexes will will be \
promoted to hardened indexes.",
),
)
}
}

Expand Down
4 changes: 4 additions & 0 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ pub async fn submit_init_validator(
Some(validator_key_alias.clone()),
unsafe_dont_encrypt,
false,
true,
None,
)
.expect("Key generation should not fail.")
.1
Expand All @@ -270,6 +272,8 @@ pub async fn submit_init_validator(
Some(consensus_key_alias.clone()),
unsafe_dont_encrypt,
false,
true,
None,
)
.expect("Key generation should not fail.")
.1
Expand Down
14 changes: 12 additions & 2 deletions apps/src/lib/client/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ pub fn init_network(
config.address = Some(address.to_string());

// Generate the consensus, account and reward keys, unless they're
// pre-defined. Do not use mnemonic codes.
// pre-defined. Do not use mnemonic code. Do not use derivation path.
let mut wallet = Wallet::load_or_new(&chain_dir);

let consensus_pk = try_parse_public_key(
Expand All @@ -495,6 +495,8 @@ pub fn init_network(
Some(alias),
unsafe_dont_encrypt,
false,
true,
None,
)
.expect("Key generation should not fail.");

Expand All @@ -517,6 +519,8 @@ pub fn init_network(
Some(alias),
unsafe_dont_encrypt,
false,
true,
None,
)
.expect("Key generation should not fail.");
keypair.ref_to()
Expand All @@ -535,6 +539,8 @@ pub fn init_network(
Some(alias),
unsafe_dont_encrypt,
false,
true,
None,
)
.expect("Key generation should not fail.");
keypair.ref_to()
Expand Down Expand Up @@ -585,7 +591,7 @@ pub fn init_network(
});

// Create a wallet for all accounts other than validators. Do not use
// mnemonic code.
// mnemonic code. Do not use derivation path.
let mut wallet =
Wallet::load_or_new(&accounts_dir.join(NET_OTHER_ACCOUNTS_DIR));
if let Some(established) = &mut config.established {
Expand Down Expand Up @@ -623,6 +629,8 @@ pub fn init_network(
Some(name.clone()),
unsafe_dont_encrypt,
false,
true,
None,
)
.expect("Key generation should not fail.");
let public_key =
Expand Down Expand Up @@ -876,6 +884,8 @@ fn init_established_account(
Some(format!("{}-key", name.as_ref())),
unsafe_dont_encrypt,
false, // do not use mnemonic code
true,
None,
)
.expect("Key generation should not fail.");
let public_key =
Expand Down
4 changes: 2 additions & 2 deletions apps/src/lib/wallet/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use orion::{aead, kdf};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use super::read_password;
use super::read_encryption_password;

const ENCRYPTED_KEY_PREFIX: &str = "encrypted:";
const UNENCRYPTED_KEY_PREFIX: &str = "unencrypted:";
Expand Down Expand Up @@ -174,7 +174,7 @@ where
StoredKeypair::Encrypted(encrypted_keypair) => {
if decrypt {
let password = password.unwrap_or_else(|| {
read_password("Enter decryption password: ")
read_encryption_password("Enter decryption password: ")
});
let key = encrypted_keypair.decrypt(password)?;
Ok(key)
Expand Down
Loading

0 comments on commit 87ed83e

Please sign in to comment.