Skip to content

Commit

Permalink
Merge pull request #207 from isvforall/master
Browse files Browse the repository at this point in the history
Add --address-count options. Generate multiple addresses for the same wallet
  • Loading branch information
howardwu authored Jul 29, 2020
2 parents 47f0bc4 + 4cf9415 commit 217c139
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 36 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ FLAGS:
OPTIONS:
-c, --count <count> Generates a specified number of wallets
-d, --derivation <"path"> Generates an HD wallet for a specified derivation path (in quotes) [possible values: ethereum, keepkey, ledger-legacy, ledger-live, trezor, "<custom path>"]
-i, --index <index> Generates an HD wallet with a specified index
-k, --indices <num_indices> Generates an HD wallet with a specified number of indices
-l, --language <language> Generates an HD wallet with a specified language [possible values: chinese_simplified, chinese_traditional, english, french, italian, japanese, korean, spanish]
-p, --password <password> Generates an HD wallet with a specified password
-w, --word-count <word count> Generates an HD wallet with a specified word count [possible values: 12, 15, 18, 21, 24]
Expand Down Expand Up @@ -371,7 +373,7 @@ wagyu [CRYPTOCURRENCY] import-hd [FLAGS] [OPTIONS]

To import a Bitcoin HD wallet, run:
```
wagyu bitcoin hd [FLAGS] [OPTIONS]
wagyu bitcoin import-hd [FLAGS] [OPTIONS]
```

This command can be run with the following parameters:
Expand All @@ -396,7 +398,7 @@ OPTIONS:

To import an Ethereum HD wallet, run:
```
wagyu ethereum hd [FLAGS] [OPTIONS]
wagyu ethereum import-hd [FLAGS] [OPTIONS]
```

This command can be run with the following parameters:
Expand All @@ -410,7 +412,8 @@ OPTIONS:
-d, --derivation <"path"> Imports an HD wallet for a specified derivation path (in quotes) [possible values: ethereum, keepkey, ledger-legacy, ledger-live, trezor, "<custom path>"]
--extended-private <extended private> Imports a partial HD wallet for a specified extended private key
--extended-public <extended public> Imports a partial HD wallet for a specified extended public key
-i, --index <index> Imports an HD wallet for a specified index
-i, --index <index> Imports an HD wallet with a specified index
-k, --indices <num_indices> Imports an HD wallet with a specified number of indices
-m, --mnemonic <"mnemonic"> Imports an HD wallet for a specified mnemonic (in quotes)
-p, --password <password> Imports an HD wallet with a specified password
```
Expand All @@ -419,7 +422,7 @@ OPTIONS:

To import a Zcash HD wallet, run:
```
wagyu zcash hd [FLAGS] [OPTIONS]
wagyu zcash import-hd [FLAGS] [OPTIONS]
```

This command can be run with the following parameters:
Expand Down
100 changes: 73 additions & 27 deletions wagyu/cli/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ pub struct EthereumOptions {
extended_private_key: Option<String>,
extended_public_key: Option<String>,
index: u32,
indices: u32,
language: String,
mnemonic: Option<String>,
password: Option<String>,
Expand Down Expand Up @@ -345,6 +346,7 @@ impl Default for EthereumOptions {
extended_private_key: None,
extended_public_key: None,
index: 0,
indices: 1,
language: "english".into(),
mnemonic: None,
password: None,
Expand Down Expand Up @@ -374,6 +376,7 @@ impl EthereumOptions {
"extended public" => self.extended_public(arguments.value_of(option)),
"json" => self.json(arguments.is_present(option)),
"index" => self.index(clap::value_t!(arguments.value_of(*option), u32).ok()),
"indices" => self.indices(clap::value_t!(arguments.value_of(*option), u32).ok()),
"language" => self.language(arguments.value_of(option)),
"mnemonic" => self.mnemonic(arguments.value_of(option)),
"network" => self.network(arguments.value_of(option)),
Expand Down Expand Up @@ -451,6 +454,14 @@ impl EthereumOptions {
}
}

/// Sets `indices` to the specified number of indices, overriding its previous state.
/// If the specified argument is `None`, then no change occurs.
fn indices(&mut self, argument: Option<u32>) {
if let Some(indices) = argument {
self.indices = indices;
}
}

/// Sets `json` to the specified boolean value, overriding its previous state.
fn json(&mut self, argument: bool) {
self.json = argument;
Expand Down Expand Up @@ -541,21 +552,35 @@ impl EthereumOptions {
"trezor" => Some(format!("m/44'/60'/0'/{}", self.index)),
"custom" => self.path.clone(),
_ => match default {
true => Some(format!("m/44'/60'/0'/{}", self.index)),
true => Some(format!("m/44'/60'/0'/0/{}", self.index)),
false => None,
},
}
}

/// Returns the derivation paths with the specified account, chain, derivation, indices, and path.
/// If `default` is enabled, then return the default path if no derivation was provided.
fn to_derivation_paths(&self, default: bool) -> Vec<Option<String>> {
let start = self.index;
let end = start + self.indices;
let mut options = self.clone();
(start..end).map(|index| {
// Sets the index to the specified index
options.index(Some(index));
// Generates the derivation path for the specified information
options.to_derivation_path(default)
}).collect()
}
}

pub struct EthereumCLI;

impl CLI for EthereumCLI {
type Options = EthereumOptions;

const NAME: NameType = "ethereum";
const ABOUT: AboutType = "Generates a Ethereum wallet (include -h for more options)";
const FLAGS: &'static [FlagType] = &[flag::JSON];
const NAME: NameType = "ethereum";
const OPTIONS: &'static [OptionType] = &[option::COUNT];
const SUBCOMMANDS: &'static [SubCommandType] = &[
subcommand::HD_ETHEREUM,
Expand All @@ -574,7 +599,7 @@ impl CLI for EthereumCLI {
("hd", Some(arguments)) => {
options.subcommand = Some("hd".into());
options.parse(arguments, &["count", "json"]);
options.parse(arguments, &["derivation", "language", "password", "word count"]);
options.parse(arguments, &["derivation", "index", "indices", "language", "password", "word count"]);
}
("import", Some(arguments)) => {
options.subcommand = Some("import".into());
Expand All @@ -593,6 +618,7 @@ impl CLI for EthereumCLI {
"extended private",
"extended public",
"index",
"indices",
"mnemonic",
"password",
],
Expand All @@ -614,18 +640,27 @@ impl CLI for EthereumCLI {
fn output<N: EthereumNetwork, W: EthereumWordlist>(options: EthereumOptions) -> Result<(), CLIError> {
let wallets = match options.subcommand.as_ref().map(String::as_str) {
Some("hd") => {
let path = options.to_derivation_path(true).unwrap();
let password = options.password.as_ref().map(String::as_str);
(0..options.count)
.flat_map(|_| {
match EthereumWallet::new_hd::<N, W, _>(
// Sample a new HD wallet
let wallet = EthereumWallet::new_hd::<N, W, _>(
&mut StdRng::from_entropy(),
options.word_count,
options.password.as_ref().map(String::as_str),
&path,
) {
Ok(wallet) => vec![wallet],
_ => vec![],
}
password,
&options.to_derivation_path(true).unwrap(),
)
.unwrap();
let mnemonic = &wallet.mnemonic.unwrap();

// Generate the HD wallet, from `index` to a number of specified `indices`
options.to_derivation_paths(true).iter().flat_map(|path| {
match EthereumWallet::from_mnemonic::<N, W>(mnemonic, password, path.as_ref().unwrap()) {
Ok(wallet) => vec![wallet],
_ => vec![],
}
})
.collect::<Vec<EthereumWallet>>()
})
.collect()
}
Expand All @@ -645,31 +680,42 @@ impl CLI for EthereumCLI {
fn process_mnemonic<EN: EthereumNetwork, EW: EthereumWordlist>(
mnemonic: &String,
options: &EthereumOptions,
) -> Result<EthereumWallet, CLIError> {
EthereumWallet::from_mnemonic::<EN, EW>(
&mnemonic,
options.password.as_ref().map(String::as_str),
&options.to_derivation_path(true).unwrap(),
)
) -> Result<Vec<EthereumWallet>, CLIError> {
// Generate the mnemonic wallets, from `index` to a number of specified `indices`
let mut wallets = vec![];
let password = options.password.as_ref().map(String::as_str);
for path in options.to_derivation_paths(true) {
wallets.push(EthereumWallet::from_mnemonic::<EN, EW>(mnemonic, password, path.as_ref().unwrap())?);
}
Ok(wallets)
}
vec![process_mnemonic::<N, ChineseSimplified>(&mnemonic, &options)

process_mnemonic::<N, ChineseSimplified>(&mnemonic, &options)
.or(process_mnemonic::<N, ChineseTraditional>(&mnemonic, &options))
.or(process_mnemonic::<N, English>(&mnemonic, &options))
.or(process_mnemonic::<N, French>(&mnemonic, &options))
.or(process_mnemonic::<N, Italian>(&mnemonic, &options))
.or(process_mnemonic::<N, Japanese>(&mnemonic, &options))
.or(process_mnemonic::<N, Korean>(&mnemonic, &options))
.or(process_mnemonic::<N, Spanish>(&mnemonic, &options))?]
.or(process_mnemonic::<N, Spanish>(&mnemonic, &options))?
} else if let Some(extended_private_key) = options.extended_private_key.clone() {
vec![EthereumWallet::from_extended_private_key::<N>(
&extended_private_key,
&options.to_derivation_path(false),
)?]
// Generate the extended private keys, from `index` to a number of specified `indices`
options.to_derivation_paths(true).iter().flat_map(|path| {
match EthereumWallet::from_extended_private_key::<N>(&extended_private_key, path) {
Ok(wallet) => vec![wallet],
_ => vec![],
}
})
.collect::<Vec<EthereumWallet>>()
} else if let Some(extended_public_key) = options.extended_public_key.clone() {
vec![EthereumWallet::from_extended_public_key::<N>(
&extended_public_key,
&options.to_derivation_path(false),
)?]
// Generate the extended public keys, from `index` to a number of specified `indices`
options.to_derivation_paths(true).iter().flat_map(|path| {
match EthereumWallet::from_extended_public_key::<N>(&extended_public_key, path) {
Ok(wallet) => vec![wallet],
_ => vec![],
}
})
.collect::<Vec<EthereumWallet>>()
} else {
vec![]
}
Expand Down
22 changes: 20 additions & 2 deletions wagyu/cli/parameters/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ pub const DIVERSIFIER_HD_ZCASH: OptionType = (
&[],
&[],
);
pub const INDEX_HD: OptionType = (
"[index] -i --index=[index] 'Generates an HD wallet with a specified index'",
&[],
&[],
&[],
);
pub const INDICES_HD: OptionType = (
"[indices] -k --indices=[num_indices] 'Generates an HD wallet with a specified number of indices'",
&[],
&[],
&[],
);
pub const LANGUAGE_HD: OptionType = (
"[language] -l --language=[language] 'Generates an HD wallet with a specified language'",
&[],
Expand Down Expand Up @@ -302,8 +314,14 @@ pub const NETWORK_IMPORT_HD_BITCOIN: OptionType = (
&["mainnet", "testnet"],
&[],
);
pub const INDEX: OptionType = (
"[index] -i --index=[index] 'Imports an HD wallet for a specified index'",
pub const INDEX_IMPORT_HD: OptionType = (
"[index] -i --index=[index] 'Imports an HD wallet with a specified index'",
&[],
&[],
&[],
);
pub const INDICES_IMPORT_HD: OptionType = (
"[indices] -k --indices=[num_indices] 'Imports an HD wallet with a specified number of indices'",
&[],
&[],
&[],
Expand Down
9 changes: 6 additions & 3 deletions wagyu/cli/parameters/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub const HD_ETHEREUM: SubCommandType = (
&[
option::COUNT,
option::DERIVATION_ETHEREUM,
option::INDEX_HD,
option::INDICES_HD,
option::LANGUAGE_HD,
option::PASSWORD_HD,
option::WORD_COUNT,
Expand Down Expand Up @@ -136,7 +138,7 @@ pub const IMPORT_HD_BITCOIN: SubCommandType = (
option::EXTENDED_PUBLIC,
option::EXTENDED_PRIVATE,
option::NETWORK_IMPORT_HD_BITCOIN,
option::INDEX,
option::INDEX_IMPORT_HD,
option::MNEMONIC,
option::PASSWORD_IMPORT_HD,
],
Expand All @@ -155,7 +157,8 @@ pub const IMPORT_HD_ETHEREUM: SubCommandType = (
option::DERIVATION_IMPORT_ETHEREUM,
option::EXTENDED_PUBLIC,
option::EXTENDED_PRIVATE,
option::INDEX,
option::INDEX_IMPORT_HD,
option::INDICES_IMPORT_HD,
option::MNEMONIC,
option::PASSWORD_IMPORT_HD,
],
Expand All @@ -176,7 +179,7 @@ pub const IMPORT_HD_ZCASH: SubCommandType = (
option::DIVERSIFIER_IMPORT_HD_ZCASH,
option::EXTENDED_PUBLIC,
option::EXTENDED_PRIVATE,
option::INDEX,
option::INDEX_IMPORT_HD,
],
&[
AppSettings::ColoredHelp,
Expand Down

0 comments on commit 217c139

Please sign in to comment.