Skip to content

Commit

Permalink
Transaction signing (informalsystems#345)
Browse files Browse the repository at this point in the history
* Initial implementation for tx sign

* Adding logic to build the tx as part of the chain. Added broadcast tx to chain. informalsystems#47

* Added keys restore command boileplate for abscissa. Restore key functionality not implemented yet informalsystems#47

* Implemented changes to support tx signing (informalsystems#47):

* Implemented very basic keybase to store keys (memory store)

* Logic to restore key (private/public) from mnemonic

* Added keystore to the chain

* Implemented working 'keys restore' command on the relayer

* Refactoring keybase structure (informalsystems#47)

* Initial logic to send message (informalsystems#47)

* Got the logic to sign but MsgConnectionOpenInit test against stargate-4 not working (informalsystems#47)

* MsgConnectionOpenInit tx working logic! (informalsystems#47)

* Added option to tx raw conn-init to specify and read key file content (informalsystems#47)

* Logic to parse the key_seed.json file passed as tx raw parameter working (informalsystems#47)

* Added support to specify key file and account sequence for the tax raw conn-init command (informalsystems#47)

* Adding instructions on how to submit a transaction (informalsystems#345)

* Fixing format issues (informalsystems#345)

* Fixing tests (informalsystems#345)

* Update relayer/src/tx/client.rs

I had this in place so I could test. OK to change it back

Co-authored-by: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update relayer/src/tx/connection.rs

OK, didn't know where to get it from, thanks.

Co-authored-by: Anca Zamfir <ancazamfir@users.noreply.github.com>

Co-authored-by: Anca Zamfir <ancazamfir@users.noreply.github.com>
  • Loading branch information
andynog and ancazamfir committed Oct 30, 2020
1 parent 9160313 commit 36d4096
Show file tree
Hide file tree
Showing 20 changed files with 721 additions and 177 deletions.
1 change: 1 addition & 0 deletions relayer-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ serde_derive = "1.0.116"
sled = "0.34.4"
prost = "0.6.1"
prost-types = { version = "0.6.1" }
hex = "0.4"

[dependencies.tendermint]
version = "0.17.0-rc1"
Expand Down
88 changes: 81 additions & 7 deletions relayer-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,88 @@
# relayer-cli
# Relayer (Rust)

`relayer-cli` is an application.
This is the repository for the IBC Relayer built in Rust.

For updates please check the [releases on the ibc-rs repository](https://github.com/informalsystems/ibc-rs/releases)

## Getting Started

This application is authored using [Abscissa], a Rust application framework.
In order to run the Relayer please ensure you have [Rust installed on your machine](https://www.rust-lang.org/tools/install)

### Submitting an IBC transaction

The `tx raw conn-init` command works now. Signing the message is working and the gaia chain (stargate-4) accepts the transaction.

The command accepts two parameters that allows you to send a transaction:

* **signer-key** (-k) -> specify a key file (name and location) that will be used by the signer. This key seed file has a mnemonic (seed phrase) that can be used to retrieve the private key (BIP-39) used to sign the transaction.
* **account_sequence** (-s) -> this is the account sequence value, basically every time a tx is committed by the account this number increases.

#### Steps to testing the transaction:

* Start two chains using the `dev-env` script from the [ovrclk/relayer](https://github.com/ovrclk/relayer) (make sure to checkout stargate-4 version)
* After you run the script, the Go relayer will create a `data` folder for the chains. Open the key seed file `./data/ibc1/key_seed.json` for chain `ibc-1` and look for the account value


{
"name":"user",
"type":"local",
"address":"cosmos1tqzwwr5hrnq2ceg5fg52m720m50xpfy08at7l9",
"pubkey":"cosmospub1addwnpepq08wntxejcla5hd93stgudw02htdpa9vu5a2ds8xkvmgrkrrpwlj6sdhkz6",
"mnemonic":"[MNEMONIC WORDS"}
}


* In order to find the account sequence run the command below:

For the address value after `gaiad query account` use the `address` from the step above.

`$ gaiad query account cosmos1tqzwwr5hrnq2ceg5fg52m720m50xpfy08at7l9 --home ./data/ibc1 --chain-id ibc1 --node tcp://localhost:26557`

This will return a JSON with a sequence number at the end. Make a note of that, you will need this number as an argument to the transaction command.


'@type': /cosmos.auth.v1beta1.BaseAccount
account_number: "0"
address: cosmos1tqzwwr5hrnq2ceg5fg52m720m50xpfy08at7l9
pub_key:
'@type': /cosmos.crypto.secp256k1.PubKey
key: A87prNmWP9pdpYwWjjXPVdbQ9KzlOqbA5rM2gdhjC78t
sequence: "12"


* Run the transaction command. This will try to initialize an `ibczeroconn2` connection on chain `ibc1`

`$ cargo run --bin relayer -- -c ./relayer-cli/tests/fixtures/two_chains.toml tx raw conn-init ibc0 ibc1 ibczeroclient ibconeclient ibczeroconn2 ibconeconn -s 12 -k key_seed.json`

If you get an empty response it means the tx worked

`conn init, result: []`

* Check if the connection was created on `ibc-1` using the Golang relayer

`$ rly query connections ibc1 | jq .`

If you see an entry in the JSON file that points to the `ibczeroconn2` connection with state `STATE_INIT` it confirms that the transaction worked:


{
"id": "ibczeroconn21",
"client_id": "ibczeroclient",
"versions": [
"\n\u00011\u0012\rORDER_ORDERED\u0012\u000fORDER_UNORDERED"
],
"state": "STATE_INIT",
"counterparty": {
"client_id": "ibconeclient",
"connection_id": "ibconeconn",
"prefix": {
"key_prefix": "aWJj"
}
}
},



For more information, see:

[Documentation]

[Abscissa]: https://github.com/iqlusioninc/abscissa
[Documentation]: https://docs.rs/abscissa_core/
9 changes: 7 additions & 2 deletions relayer-cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//! application's configuration file.

mod config;
mod keys;
mod light;
mod listen;
mod query;
Expand All @@ -15,8 +16,8 @@ mod v0;
mod version;

use self::{
config::ConfigCmd, light::LightCmd, listen::ListenCmd, query::QueryCmd, start::StartCmd,
tx::TxCmd, v0::V0Cmd, version::VersionCmd,
config::ConfigCmd, keys::KeysCmd, light::LightCmd, listen::ListenCmd, query::QueryCmd,
start::StartCmd, tx::TxCmd, v0::V0Cmd, version::VersionCmd,
};

use crate::config::Config;
Expand Down Expand Up @@ -64,6 +65,10 @@ pub enum CliCmd {
/// The `light` subcommand
#[options(help = "basic functionality for managing the lite clients")]
Light(LightCmd),

/// The `keys` subcommand
#[options(help = "manage keys in the relayer for each chain")]
Keys(KeysCmd),
}

/// This trait allows you to define how application configuration is loaded.
Expand Down
16 changes: 16 additions & 0 deletions relayer-cli/src/commands/keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! `keys` subcommand
use abscissa_core::{Command, Help, Options, Runnable};

mod restore;

/// `keys` subcommand
#[derive(Command, Debug, Options, Runnable)]
pub enum KeysCmd {
/// The `help` subcommand
#[options(help = "get usage information")]
Help(Help<Self>),

/// The `keys restore` subcommand
#[options(help = "keys restore")]
Restore(restore::KeyRestoreCmd),
}
74 changes: 74 additions & 0 deletions relayer-cli/src/commands/keys/restore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::application::app_config;
use abscissa_core::{Command, Options, Runnable};
use relayer::config::Config;

use crate::error::{Error, Kind};
use crate::prelude::*;
use relayer::keys::restore::{restore_key, KeysRestoreOptions};

#[derive(Clone, Command, Debug, Options)]
pub struct KeyRestoreCmd {
#[options(free, help = "identifier of the chain")]
chain_id: Option<String>,

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

#[options(free, help = "mnemonic to add key")]
mnemonic: Option<String>,
}

impl KeyRestoreCmd {
fn validate_options(&self, config: &Config) -> Result<KeysRestoreOptions, String> {
let chain_id = self
.chain_id
.clone()
.ok_or_else(|| "missing chain identifier".to_string())?;

let chain_config = config
.chains
.iter()
.find(|c| c.id == chain_id.parse().unwrap())
.ok_or_else(|| {
"Invalid chain identifier. Cannot retrieve the chain configuration".to_string()
})?;

let key_name = self
.name
.clone()
.ok_or_else(|| "missing key name".to_string())?;

let mnemonic_words = self
.mnemonic
.clone()
.ok_or_else(|| "missing mnemonic".to_string())?;

Ok(KeysRestoreOptions {
name: key_name,
mnemonic: mnemonic_words,
chain_config: chain_config.clone(),
})
}
}

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

let opts = match self.validate_options(&config) {
Err(err) => {
status_err!("invalid options: {}", err);
return;
}
Ok(result) => result,
};

let res: Result<Vec<u8>, Error> =
restore_key(opts).map_err(|e| Kind::Keys.context(e).into());

match res {
Ok(r) => status_info!("key restore result: ", "{:?}", hex::encode(r)),
Err(e) => status_info!("key restore failed: ", "{}", e),
}
}
}
23 changes: 22 additions & 1 deletion relayer-cli/src/commands/tx/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use relayer::tx::client::{create_client, CreateClientOptions};
use crate::application::app_config;
use crate::error::{Error, Kind};
use crate::prelude::*;
use std::fs;
use std::path::Path;

#[derive(Clone, Command, Debug, Options)]
pub struct TxCreateClientCmd {
Expand All @@ -19,10 +21,27 @@ pub struct TxCreateClientCmd {
help = "identifier of the client to be created on destination chain"
)]
dest_client_id: Option<String>,

#[options(free, help = "key file for the signer")]
signer_key: Option<String>,
}

impl TxCreateClientCmd {
fn validate_options(&self, config: &Config) -> Result<CreateClientOptions, String> {
// Get content of key seed file
let key_filename = self
.signer_key
.clone()
.ok_or_else(|| "missing signer key file".to_string())?;

let key_file = Path::new(&key_filename).exists();
if !key_file {
return Err("cannot find key file specified".to_string());
}

let key_file_contents = fs::read_to_string(key_filename)
.expect("Something went wrong reading the key seed file");

let dest_chain_id = self
.dest_chain_id
.clone()
Expand Down Expand Up @@ -56,6 +75,7 @@ impl TxCreateClientCmd {
dest_client_id,
dest_chain_config: dest_chain_config.clone(),
src_chain_config: src_chain_config.clone(),
signer_key: key_file_contents,
})
}
}
Expand All @@ -73,7 +93,8 @@ impl Runnable for TxCreateClientCmd {
};
status_info!("Message", "{:?}", opts);

let res: Result<(), Error> = create_client(opts).map_err(|e| Kind::Tx.context(e).into());
let res: Result<Vec<u8>, Error> =
create_client(opts).map_err(|e| Kind::Tx.context(e).into());

match res {
Ok(receipt) => status_info!("client created, result: ", "{:?}", receipt),
Expand Down
40 changes: 38 additions & 2 deletions relayer-cli/src/commands/tx/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use crate::error::{Error, Kind};
use abscissa_core::{Command, Options, Runnable};
use relayer::config::Config;
use relayer::tx::connection::{conn_init, ConnectionOpenInitOptions};
use std::fs;
use std::path::Path;

#[derive(Clone, Command, Debug, Options)]
pub struct TxRawConnInitCmd {
Expand All @@ -22,12 +24,44 @@ pub struct TxRawConnInitCmd {
#[options(free, help = "identifier of the source connection")]
src_connection_id: Option<String>,

#[options(help = "identifier of the destination connection", short = "d")]
#[options(free, help = "identifier of the destination connection")]
dest_connection_id: Option<String>,

#[options(help = "account sequence of the signer", short = "s")]
account_sequence: Option<String>,

#[options(help = "key file for the signer", short = "k")]
signer_key: Option<String>,
}

impl TxRawConnInitCmd {
fn validate_options(&self, config: &Config) -> Result<ConnectionOpenInitOptions, String> {
// Get the account sequence
let parsed = self
.account_sequence
.clone()
.ok_or_else(|| "missing account sequence".to_string())?
.parse::<u64>();

let acct_seq = match parsed {
Ok(v) => v,
Err(e) => return Err("invalid account sequence number".to_string()),
};

// Get content of key seed file
let key_filename = self
.signer_key
.clone()
.ok_or_else(|| "missing signer key file".to_string())?;

let key_file = Path::new(&key_filename).exists();
if !key_file {
return Err("cannot find key file specified".to_string());
}

let key_file_contents = fs::read_to_string(key_filename)
.expect("Something went wrong reading the key seed file");

let src_chain_id = self
.src_chain_id
.clone()
Expand Down Expand Up @@ -87,6 +121,8 @@ impl TxRawConnInitCmd {
dest_connection_id,
src_chain_config: src_chain_config.clone(),
dest_chain_config: dest_chain_config.clone(),
signer_key: key_file_contents,
account_sequence: acct_seq,
};

Ok(opts)
Expand All @@ -106,7 +142,7 @@ impl Runnable for TxRawConnInitCmd {
};
status_info!("Message", "{:?}", opts);

let res: Result<(), Error> = conn_init(opts).map_err(|e| Kind::Tx.context(e).into());
let res: Result<Vec<u8>, Error> = conn_init(opts).map_err(|e| Kind::Tx.context(e).into());

match res {
Ok(receipt) => status_info!("conn init, result: ", "{:?}", receipt),
Expand Down
4 changes: 4 additions & 0 deletions relayer-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ pub enum Kind {
/// Error during transaction submission
#[error("tx error")]
Tx,

/// Error during transaction submission
#[error("keys error")]
Keys,
}

impl Kind {
Expand Down
9 changes: 8 additions & 1 deletion relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ authors = [

[dependencies]
ibc = { path = "../modules" }
ibc-proto = { version = "0.4.0", path = "../proto" }
tendermint-proto = "0.1.0"

anomaly = "0.2.0"
Expand All @@ -28,6 +29,13 @@ prost-types = { version = "0.6.1" }
futures = "0.3.5"
retry = "1.1.0"
crossbeam-channel = "0.5.0"
k256 = { version = "0.5.6", features = ["ecdsa-core", "ecdsa", "sha256"]}
hex = "0.4"
bitcoin = { version= "0.25"}
bitcoin-wallet = "1.1.0"
hdpath = { version = "0.2.0", features = ["with-bitcoin"] }
rust-crypto = "0.2.36"
bech32 = "0.7.2"

[dependencies.tendermint]
version = "0.17.0-rc1"
Expand All @@ -40,7 +48,6 @@ features = ["http-client", "websocket-client"]
version = "0.17.0-rc1"

# Needed for tx sign when ready in tendermint upgrade
#k256 = { version = "0.4", features = ["ecdsa-core", "ecdsa", "sha256"]}
#tendermint-rpc = "0.15"
#rust-crypto = "0.2.36"
#hex = "0.4"
Expand Down
Loading

0 comments on commit 36d4096

Please sign in to comment.