Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keys delete command #1174

Merged
merged 16 commits into from
Aug 4, 2021
3 changes: 3 additions & 0 deletions .changelog/unreleased/features/1065-keys-delete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Added `keys delete` CLI command ([#1065])

[#1065]: https://github.com/informalsystems/ibc-rs/issues/1065
33 changes: 33 additions & 0 deletions guide/src/commands/keys/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,39 @@ Success: Restore key testkey ([ADDRESS]) on [CHAIN ID] chain
> hermes -c config.toml keys restore [CHAINID] -m "[MNEMONIC]" -n [KEY_NAME]
> ```

### Delete keys

In order to delete the private keys added to chains use the `keys delete` command

```shell
USAGE:
hermes keys delete <OPTIONS>

DESCRIPTION:
Delete key(s) from a configured chain

POSITIONAL ARGUMENTS:
chain_id identifier of the chain

FLAGS:
-n, --name NAME name of the key
-a, --all delete all keys
```

#### Delete private keys that was previously added to a chain

To delete a single private key by name:

```shell
hermes -c config.toml keys delete [CHAIN_ID] -n [KEY_NAME]
```

Alternatively, to delete all private keys added to a chain:

```shell
hermes -c config.toml keys delete [CHAIN_ID] -a
```

### List keys

In order to list the private keys added to chains use the `keys list` command
Expand Down
5 changes: 5 additions & 0 deletions relayer-cli/src/commands/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use abscissa_core::{Command, Help, Options, Runnable};

mod add;
mod delete;
mod list;
mod restore;

Expand All @@ -16,6 +17,10 @@ pub enum KeysCmd {
#[options(help = "Adds a key to a configured chain")]
Add(add::KeysAddCmd),

/// The `keys delete` subcommand
#[options(help = "Delete key(s) from a configured chain")]
Delete(delete::KeysDeleteCmd),

/// The `keys list` subcommand
#[options(help = "List keys configured on a chain")]
List(list::KeysListCmd),
Expand Down
107 changes: 107 additions & 0 deletions relayer-cli/src/commands/keys/delete.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use abscissa_core::{Command, Options, Runnable};

use ibc::ics24_host::identifier::ChainId;
use ibc_relayer::{
config::{ChainConfig, Config},
keyring::{KeyRing, Store},
};

use crate::application::app_config;
use crate::conclude::Output;

#[derive(Clone, Command, Debug, Options)]
pub struct KeysDeleteCmd {
#[options(free, required, help = "identifier of the chain")]
chain_id: ChainId,

#[options(short = "n", help = "name of the key")]
name: Option<String>,

#[options(short = "a", help = "delete all keys")]
all: bool,
}

impl KeysDeleteCmd {
fn options(
&self,
config: &Config,
) -> Result<KeysDeleteOptions<'_>, Box<dyn std::error::Error>> {
let chain_config = config
.find_chain(&self.chain_id)
.ok_or_else(|| format!("chain '{}' not found in configuration file", self.chain_id))?;

let id = match (self.all, &self.name) {
(true, Some(_)) => {
return Err("cannot set both -n/--name and -a/--all".to_owned().into());
}
(false, None) => {
return Err("must provide either -n/--name or -a/--all"
.to_owned()
.into());
}
(true, None) => KeysDeleteId::All,
(false, Some(ref name)) => KeysDeleteId::Named(name),
};

Ok(KeysDeleteOptions {
config: chain_config.clone(),
id,
})
}
}

#[derive(Clone, Debug)]
struct KeysDeleteOptions<'a> {
id: KeysDeleteId<'a>,
config: ChainConfig,
}

#[derive(Clone, Debug)]
enum KeysDeleteId<'a> {
All,
Named(&'a str),
}

impl Runnable for KeysDeleteCmd {
fn run(&self) {
let config = app_config();

let opts = match self.options(&config) {
Err(err) => return Output::error(err).exit(),
Ok(result) => result,
};

match opts.id {
KeysDeleteId::All => match delete_all_keys(&opts.config) {
Ok(_) => {
Output::success_msg(format!("Removed all keys on chain {}", opts.config.id))
.exit()
}
Err(e) => Output::error(format!("{}", e)).exit(),
},
KeysDeleteId::Named(name) => match delete_key(&opts.config, name) {
Ok(_) => Output::success_msg(format!(
"Removed key ({}) on chain {}",
name, opts.config.id
))
.exit(),
Err(e) => Output::error(format!("{}", e)).exit(),
},
};
}
}

pub fn delete_key(config: &ChainConfig, key_name: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut keyring = KeyRing::new(Store::Test, &config.account_prefix, &config.id)?;
keyring.remove_key(key_name)?;
Ok(())
}

pub fn delete_all_keys(config: &ChainConfig) -> Result<(), Box<dyn std::error::Error>> {
let mut keyring = KeyRing::new(Store::Test, &config.account_prefix, &config.id)?;
let keys = keyring.keys()?;
for key in keys {
keyring.remove_key(&key.0)?;
}
Ok(())
}
26 changes: 26 additions & 0 deletions relayer/src/keyring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl KeyEntry {
pub trait KeyStore {
fn get_key(&self, key_name: &str) -> Result<KeyEntry, Error>;
fn add_key(&mut self, key_name: &str, key_entry: KeyEntry) -> Result<(), Error>;
fn remove_key(&mut self, key_name: &str) -> Result<(), Error>;
fn keys(&self) -> Result<Vec<(String, KeyEntry)>, Error>;
}

Expand Down Expand Up @@ -136,6 +137,14 @@ impl KeyStore for Memory {
}
}

fn remove_key(&mut self, key_name: &str) -> Result<(), Error> {
self.keys
.remove(key_name)
.ok_or_else(Error::key_not_found)?;

Ok(())
}

fn keys(&self) -> Result<Vec<(String, KeyEntry)>, Error> {
Ok(self
.keys
Expand Down Expand Up @@ -198,6 +207,16 @@ impl KeyStore for Test {
Ok(())
}

fn remove_key(&mut self, key_name: &str) -> Result<(), Error> {
let mut filename = self.store.join(key_name);
filename.set_extension(KEYSTORE_FILE_EXTENSION);

fs::remove_file(filename.clone())
.map_err(|e| Error::remove_io_fail(filename.display().to_string(), e))?;

Ok(())
}

fn keys(&self) -> Result<Vec<(String, KeyEntry)>, Error> {
let dir = fs::read_dir(&self.store).map_err(|e| {
Error::key_file_io(
Expand Down Expand Up @@ -271,6 +290,13 @@ impl KeyRing {
}
}

pub fn remove_key(&mut self, key_name: &str) -> Result<(), Error> {
match self {
KeyRing::Memory(m) => m.remove_key(key_name),
KeyRing::Test(d) => d.remove_key(key_name),
}
}

pub fn keys(&self) -> Result<Vec<(String, KeyEntry)>, Error> {
match self {
KeyRing::Memory(m) => m.keys(),
Expand Down
11 changes: 9 additions & 2 deletions relayer/src/keyring/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,15 @@ define_error! {
HomeLocationUnavailable
|_| { "home location is unavailable" },

KeyStore
|_| { "key store error" },
RemoveIoFail
{
file_path: String,
}
[ TraceError<IoError> ]
|e| {
format!("I/O error while removing key file at location '{}'",
e.file_path)
},

InvalidHdPath
{
Expand Down