Skip to content

Commit

Permalink
Merge commands keys add and keys restore into a single command. (#…
Browse files Browse the repository at this point in the history
…2251)

* Merged commands 'keys add' and 'keys restore' into a single command, 'keys add'. Improved restoring a key with mnemonic by taking a file containing the mnemonic as input instead of taking the mnemonic as command line input

* Updated Hermes guide with new merged 'keys add' command. And updated changelog with improvement

* Fixed e2e setup script to take correct flag when adding keys

* Fixed small errors in Hermes guide and improved comments for changed 'keys add' command.

* Improved Hermes guide section 7.2

* Updated the Hermes guide section 7.2 to match actual features
  • Loading branch information
ljoss17 committed Jun 1, 2022
1 parent adbe235 commit a304c62
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 157 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Merged commands `keys add` and `keys restore` into single command `keys add`. The flag to specify the key name for the CLI command `keys add` has been changed from `-n` to `-k`. Restoring a key now takes a file containing the mnemonic as input instead of directly taking the mnemonic. ([#1075](https://github.com/informalsystems/ibc-rs/issues/1075))
4 changes: 2 additions & 2 deletions ci/e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ echo "Add keys for chains"
echo "-----------------------------------------------------------------------------------------------------------------"
hermes -c "$CONFIG_PATH" keys add "$CHAIN_A" -f user_seed_"$CHAIN_A".json
hermes -c "$CONFIG_PATH" keys add "$CHAIN_B" -f user_seed_"$CHAIN_B".json
hermes -c "$CONFIG_PATH" keys add "$CHAIN_A" -f user2_seed_"$CHAIN_A".json -n user2
hermes -c "$CONFIG_PATH" keys add "$CHAIN_B" -f user2_seed_"$CHAIN_B".json -n user2
hermes -c "$CONFIG_PATH" keys add "$CHAIN_A" -f user2_seed_"$CHAIN_A".json -k user2
hermes -c "$CONFIG_PATH" keys add "$CHAIN_B" -f user2_seed_"$CHAIN_B".json -k user2

echo "================================================================================================================="
echo " END-TO-END TESTS "
Expand Down
106 changes: 80 additions & 26 deletions guide/src/commands/keys/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
> store the private key file. The key file will be stored on the local file system
> in the user __$HOME__ folder under `$HOME/.hermes/keys/`
> __BREAKING__: As of Hermes v0.2.0, the format of the keys stored on disk has changed, and
> keys which had been previously configured must now be re-imported using either the `keys add`
> or the `keys restore` commands.
> __BREAKING__: As of Hermes v1.0.0, the sub-command `keys restore` has been removed.
> Please use the sub-command `keys add` in order to restore a key.
---

Expand All @@ -20,7 +19,7 @@ To see the available sub-commands for the `keys` command run:
hermes help keys
```

Currently there are two sub-commands supported `add` and `list`:
The available sub-commands are the following:

```shell
USAGE:
Expand All @@ -31,10 +30,12 @@ DESCRIPTION:

SUBCOMMANDS:
help Get usage information
add Adds a key to a configured chain
add Adds key to a configured chain or restores a key to a configured chain
using a mnemonic
balance Query balance for a key from a configured chain. If no key is given, the
key is retrieved from the configuration file
delete Delete key(s) from a configured chain
list List keys configured on a chain
restore restore a key to a configured chain using a mnemonic
```

### Key Seed file (Private Key)
Expand Down Expand Up @@ -63,23 +64,55 @@ The command outputs a JSON similar to the one below.

You can save this to a file (e.g. `key_seed.json`) and use it to add to the relayer with `hermes keys add <chain_id> -f key_seed.json`. See the `Adding Keys` section for more details.

### Adding Keys
### Adding and restoring Keys

The command `keys add` has two exclusive flags, `--key-file` and `--mnemonic-file` which are respectively used to add and restore a key.

```shell
hermes keys add [OPTIONS] --key-file <KEY_FILE> --mnemonic-file <MNEMONIC_FILE> <CHAIN_ID>

DESCRIPTION:
Adds key to a configured chain or restores a key to a configured chain using a mnemonic

ARGS:
chain_id identifier of the chain

FLAGS:
-f, --key-file <KEY_FILE>
path to the key file

-m, --mnemonic-file <MNEMONIC_FILE>
path to file containing mnemonic to restore the key from

OPTIONS:
-k, --key-name <KEY_NAME>
name of the key (defaults to the `key_name` defined in the config)

-p, --hd-path <HD_PATH>
derivation path for this key [default: m/44'/118'/0'/0/0]
```
#### Add a private key to a chain from a key file
```shell
hermes keys add <OPTIONS>
hermes keys add [OPTIONS] --key-file <KEY_FILE> <CHAIN_ID>
DESCRIPTION:
Adds a key to a configured chain
Adds key to a configured chain or restores a key to a configured chain using a mnemonic
POSITIONAL ARGUMENTS:
ARGS:
chain_id identifier of the chain
FLAGS:
-f, --file FILE path to the key file
-n, --name NAME name of the key (defaults to the `key_name` defined in the config)
-p, --hd-path HD-PATH derivation path for this key (default: m/44'/118'/0'/0/0)
-f, --key-file <KEY_FILE>
path to the key file
OPTIONS:
-k, --key-name <KEY_NAME>
name of the key (defaults to the `key_name` defined in the config)
-p, --hd-path <HD_PATH>
derivation path for this key [default: m/44'/118'/0'/0/0]
```

To add a private key file to a chain:
Expand All @@ -88,6 +121,18 @@ To add a private key file to a chain:
hermes -c config.toml keys add [CHAIN_ID] -f [PRIVATE_KEY_FILE]
```

The content of the file key should have the same format as the output of the `gaiad keys add` command:

```json
{
"name": "testkey",
"type": "local",
"address": "cosmos1tc3vcuxyyac0dmayf887t95tdg7qpyql48w7gj",
"pubkey": "cosmospub1addwnpepqgg7ng4ycm60pdxfzdfh4hjvkwcr3da59mr8k883vsstx60ruv7kur4525u",
"mnemonic": "[24 words mnemonic]"
}
```

If the command is successful a message similar to the one below will be displayed:

```json
Expand All @@ -96,44 +141,53 @@ Success: Added key testkey ([ADDRESS]) on [CHAIN ID] chain

> **Key name:**
> By default, the key will be named after the `key_name` property specified in the configuration file.
> To use a different key name, specify the `--name` option when invoking `keys add`.
> To use a different key name, specify the `--key-name` option when invoking `keys add`.
>
> ```
> hermes -c config.toml keys add [CHAINID] -f [PRIVATE_KEY_FILE] -n [KEY_NAME]
> hermes -c config.toml keys add [CHAINID] -f [PRIVATE_KEY_FILE] -k [KEY_NAME]
> ```
#### Restore a private key to a chain from a mnemonic

```shell
USAGE:
hermes keys restore <OPTIONS>
hermes keys add [OPTIONS] --mnemonic-file <MNEMONIC_FILE> <CHAIN_ID>

DESCRIPTION:
restore a key to a configured chain using a mnemonic
Adds key to a configured chain or restores a key to a configured chain using a mnemonic

POSITIONAL ARGUMENTS:
ARGS:
chain_id identifier of the chain

FLAGS:
-m, --mnemonic MNEMONIC mnemonic to restore the key from
-n, --name NAME name of the key (defaults to the `key_name` defined in the config)
-p, --hd-path HD-PATH derivation path for this key (default: m/44'/118'/0'/0/0)
-m, --mnemonic-file <MNEMONIC_FILE>
path to file containing mnemonic to restore the key from

OPTIONS:
-k, --key-name <KEY_NAME>
name of the key (defaults to the `key_name` defined in the config)

-p, --hd-path <HD_PATH>
derivation path for this key [default: m/44'/118'/0'/0/0]
```
To restore a key from its mnemonic:
```shell
hermes -c config.toml keys restore [CHAIN_ID] -m "[MNEMONIC]"
hermes -c config.toml keys add [CHAIN_ID] -m "[MNEMONIC_FILE]"
```
or using an explicit [derivation path](https://github.com/satoshilabs/slips/blob/master/slip-0044.md), for example
an Ethereum coin type (used for Evmos, Injective, Umee, Cronos, and
possibly other networks):
```shell
hermes -c config.toml keys restore --mnemonic <MNEMONIC> --hd-path "m/44'/60'/0'/0/0" <CHAIN_ID>
hermes -c config.toml keys add --mnemonic-file <MNEMONIC_FILE> --hd-path "m/44'/60'/0'/0/0" <CHAIN_ID>
```
The mnemonic file needs to have the 24 mnemonic words on the same line, separated by a white space. So the content should have the following format:
```
word1 word2 word3 ... word24
```
If the command is successful a message similar to the one below will be displayed:
Expand All @@ -143,10 +197,10 @@ Success: Restore key testkey ([ADDRESS]) on [CHAIN ID] chain
> **Key name:**
> By default, the key will be named after the `key_name` property specified in the configuration file.
> To use a different key name, specify the `--name` option when invoking `keys restore`.
> To use a different key name, specify the `--key-name` option when invoking `keys add`.
>
> ```
> hermes -c config.toml keys restore [CHAINID] -m "[MNEMONIC]" -n [KEY_NAME]
> hermes -c config.toml keys add [CHAINID] -m "[MNEMONIC_FILE]" -k [KEY_NAME]
> ```
### Delete keys
Expand Down
6 changes: 1 addition & 5 deletions relayer-cli/src/commands/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ mod add;
mod balance;
mod delete;
mod list;
mod restore;

/// `keys` subcommand
#[derive(Command, Debug, Parser, Runnable)]
pub enum KeysCmd {
/// Adds a key to a configured chain
/// Adds key to a configured chain or restores a key to a configured chain using a mnemonic
Add(add::KeysAddCmd),

/// Delete key(s) from a configured chain
Expand All @@ -20,9 +19,6 @@ pub enum KeysCmd {
/// List keys configured on a chain
List(list::KeysListCmd),

/// Restore a key to a configured chain using a mnemonic
Restore(restore::KeyRestoreCmd),

/// Query balance for a key from a configured chain. If no key is given, the key is retrieved from the configuration file.
Balance(balance::KeyBalanceCmd),
}
103 changes: 87 additions & 16 deletions relayer-cli/src/commands/keys/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,49 @@ use ibc_relayer::{
use crate::application::app_config;
use crate::conclude::Output;

/// The data structure that represents the arguments when invoking the `keys add` CLI command.
///
/// The command has one argument and two exclusive flags:
///
/// The command to add a key from a file:
///
/// `keys add [OPTIONS] --key-file <KEY_FILE> <CHAIN_ID>`
///
/// The command to restore a key from a file containing mnemonic:
///
/// `keys add [OPTIONS] --mnemonic-file <MNEMONIC_FILE> <CHAIN_ID>`
///
/// The key-file and mnemonic-file flags can't be given at the same time, this will cause a terminating error.
/// If successful the key will be created or restored, depending on which flag was given.
#[derive(Clone, Command, Debug, Parser)]
pub struct KeysAddCmd {
#[clap(required = true, help = "identifier of the chain")]
chain_id: ChainId,

#[clap(short = 'f', long, required = true, help = "path to the key file")]
file: PathBuf,
#[clap(
short = 'f',
long,
required = true,
help = "path to the key file",
group = "add-restore"
)]
key_file: Option<PathBuf>,

#[clap(
short,
long,
required = true,
help = "path to file containing mnemonic to restore the key from",
group = "add-restore"
)]
mnemonic_file: Option<PathBuf>,

#[clap(
short = 'n',
short,
long,
help = "name of the key (defaults to the `key_name` defined in the config)"
)]
name: Option<String>,
key_name: Option<String>,

#[clap(
short = 'p',
Expand All @@ -47,7 +76,7 @@ impl KeysAddCmd {
.ok_or_else(|| format!("chain '{}' not found in configuration file", self.chain_id))?;

let name = self
.name
.key_name
.clone()
.unwrap_or_else(|| chain_config.key_name.clone());

Expand All @@ -56,7 +85,6 @@ impl KeysAddCmd {

Ok(KeysAddOptions {
config: chain_config.clone(),
file: self.file.clone(),
name,
hd_path,
})
Expand All @@ -67,7 +95,6 @@ impl KeysAddCmd {
pub struct KeysAddOptions {
pub name: String,
pub config: ChainConfig,
pub file: PathBuf,
pub hd_path: HDPath,
}

Expand All @@ -80,15 +107,43 @@ impl Runnable for KeysAddCmd {
Ok(result) => result,
};

let key = add_key(&opts.config, &opts.name, &opts.file, &opts.hd_path);

match key {
Ok(key) => Output::success_msg(format!(
"Added key '{}' ({}) on chain {}",
opts.name, key.account, opts.config.id
))
.exit(),
Err(e) => Output::error(format!("{}", e)).exit(),
// Check if --file or --mnemonic was given as input.
match (self.key_file.clone(), self.mnemonic_file.clone()) {
(Some(key_file), _) => {
let key = add_key(&opts.config, &opts.name, &key_file, &opts.hd_path);
match key {
Ok(key) => Output::success_msg(format!(
"Added key '{}' ({}) on chain {}",
opts.name, key.account, opts.config.id
))
.exit(),
Err(e) => Output::error(format!(
"An error occurred adding the key on chain {} from file {:?}: {}",
self.chain_id, key_file, e
))
.exit(),
}
}
(_, Some(mnemonic_file)) => {
let key = restore_key(&mnemonic_file, &opts.name, &opts.hd_path, &opts.config);

match key {
Ok(key) => Output::success_msg(format!(
"Restored key '{}' ({}) on chain {}",
opts.name, key.account, opts.config.id
))
.exit(),
Err(e) => Output::error(format!(
"An error occurred restoring the key on chain {} from file {:?}: {}",
self.chain_id, mnemonic_file, e
))
.exit(),
}
}
// This case should never trigger.
// The 'required' parameter for the flags will trigger an error if both flags have not been given.
// And the 'group' parameter for the flags will trigger an error if both flags are given.
_ => Output::error(format!("--mnemonic-file and --key-file can't both be None")).exit(),
}
}
}
Expand All @@ -107,3 +162,19 @@ pub fn add_key(
keyring.add_key(key_name, key.clone())?;
Ok(key)
}

pub fn restore_key(
mnemonic: &Path,
key_name: &str,
hdpath: &HDPath,
config: &ChainConfig,
) -> Result<KeyEntry, Box<dyn std::error::Error>> {
let mnemonic_content =
fs::read_to_string(mnemonic).map_err(|_| "error reading the mnemonic file")?;

let mut keyring = KeyRing::new(Store::Test, &config.account_prefix, &config.id)?;
let key_entry = keyring.key_from_mnemonic(&mnemonic_content, hdpath, &config.address_type)?;

keyring.add_key(key_name, key_entry.clone())?;
Ok(key_entry)
}
Loading

0 comments on commit a304c62

Please sign in to comment.