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

Transaction signing #345

Merged
merged 21 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d067d24
Initial implementation for tx sign
andynog Oct 6, 2020
7ae66f2
Adding logic to build the tx as part of the chain. Added broadcast tx…
andynog Oct 7, 2020
4a867a3
Merge branch 'master' into andy/tx-sign
andynog Oct 10, 2020
b8133a6
Added keys restore command boileplate for abscissa. Restore key funct…
andynog Oct 13, 2020
7af390f
Implemented changes to support tx signing (#47):
andynog Oct 14, 2020
5b92633
Refactoring keybase structure (#47)
andynog Oct 15, 2020
5fda69a
Initial logic to send message (#47)
andynog Oct 16, 2020
9bbb31f
Merge branch 'master' into andy/tx-sign
andynog Oct 16, 2020
11acb90
Got the logic to sign but MsgConnectionOpenInit test against stargate…
andynog Oct 20, 2020
a61629e
MsgConnectionOpenInit tx working logic! (#47)
andynog Oct 20, 2020
574fdbc
Merge branch 'master' into andy/tx-sign
andynog Oct 21, 2020
c1e610a
Added option to tx raw conn-init to specify and read key file content…
andynog Oct 21, 2020
71b63dd
Logic to parse the key_seed.json file passed as tx raw parameter work…
andynog Oct 21, 2020
3202d00
Merging master into andy/tx-sign branch
andynog Oct 23, 2020
a75b1ec
Added support to specify key file and account sequence for the tax ra…
andynog Oct 23, 2020
ff8e28c
Adding instructions on how to submit a transaction (#345)
andynog Oct 27, 2020
c2d2d1c
Fixing format issues (#345)
andynog Oct 27, 2020
ec64525
Fixing tests (#345)
andynog Oct 28, 2020
6e489a6
Merging master and fixing conflicts (#345)
andynog Oct 28, 2020
ec39ef9
Update relayer/src/tx/client.rs
andynog Oct 29, 2020
d68face
Update relayer/src/tx/connection.rs
andynog Oct 29, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ prost-types = "0.6.1"
bytes = "0.6.0"
dyn-clonable = "0.9.0"
regex = "1"
bech32 = "0.7.2"

[dependencies.tendermint]
version = "0.17.0-rc1"
Expand Down
4 changes: 4 additions & 0 deletions modules/src/ics03_connection/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ pub mod test_util {
"0CDA3F47EF3C4906693B170EF650EB968C5F4B2C".to_string()
}

pub fn get_dummy_bech32_account() -> String {
"cosmos1wxeyh7zgn4tctjzs0vtqpc6p5cxq5t2muzl7ng".to_string()
}

pub fn get_dummy_account_id() -> AccountId {
AccountId::from_str(&get_dummy_account_id_raw()).unwrap()
}
Expand Down
27 changes: 21 additions & 6 deletions modules/src/ics03_connection/msgs/conn_open_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::ics03_connection::connection::{validate_version, Counterparty};
use crate::ics03_connection::error::{Error, Kind};
use crate::ics24_host::identifier::{ClientId, ConnectionId};
use crate::tx_msg::Msg;
use std::str::FromStr;

use bech32::{FromBase32, ToBase32};

/// Message type for the `MsgConnectionOpenInit` message.
pub const TYPE_MSG_CONNECTION_OPEN_INIT: &str = "connection_open_init";
Expand Down Expand Up @@ -84,6 +85,19 @@ impl TryFrom<RawMsgConnectionOpenInit> for MsgConnectionOpenInit {
type Error = anomaly::Error<Kind>;

fn try_from(msg: RawMsgConnectionOpenInit) -> Result<Self, Self::Error> {
let (_hrp, data) = bech32::decode(&msg.signer).map_err(|e| {
Kind::InvalidAddress
.context("Error decoding signer ".to_string() + &msg.signer + ":" + &e.to_string())
})?;
let addr_bytes = Vec::<u8>::from_base32(&data).map_err(|e| {
Kind::InvalidAddress
.context("Error converting from bech32: ".to_string() + &e.to_string())
})?;
let acct = AccountId::try_from(addr_bytes).map_err(|e| {
Kind::InvalidAddress
.context("Error converting to account ID: ".to_string() + &e.to_string())
})?;

Ok(Self {
connection_id: msg
.connection_id
Expand All @@ -98,19 +112,20 @@ impl TryFrom<RawMsgConnectionOpenInit> for MsgConnectionOpenInit {
.ok_or_else(|| Kind::MissingCounterparty)?
.try_into()?,
version: validate_version(msg.version).map_err(|e| Kind::InvalidVersion.context(e))?,
signer: AccountId::from_str(msg.signer.as_str())
.map_err(|e| Kind::InvalidSigner.context(e))?,
signer: acct,
})
}
}

impl From<MsgConnectionOpenInit> for RawMsgConnectionOpenInit {
fn from(ics_msg: MsgConnectionOpenInit) -> Self {
// The msg needs to send the bech32 account as the signer
let addr = bech32::encode("cosmos", ics_msg.signer.to_base32()).unwrap();
RawMsgConnectionOpenInit {
client_id: ics_msg.client_id.as_str().to_string(),
connection_id: ics_msg.connection_id.as_str().to_string(),
counterparty: Some(ics_msg.counterparty.into()),
signer: ics_msg.signer.to_string(),
signer: addr,
version: ics_msg.version,
}
}
Expand All @@ -121,7 +136,7 @@ pub mod test_util {
use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnectionOpenInit;

use crate::ics03_connection::msgs::test_util::{
get_dummy_account_id_raw, get_dummy_counterparty,
get_dummy_bech32_account, get_dummy_counterparty,
};

/// Returns a dummy message, for testing only.
Expand All @@ -132,7 +147,7 @@ pub mod test_util {
connection_id: "srcconnection".to_string(),
counterparty: Some(get_dummy_counterparty()),
version: "1.0.0".to_string(),
signer: get_dummy_account_id_raw(),
signer: get_dummy_bech32_account(),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion modules/src/tx_msg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use tendermint::account::Id as AccountId;

pub trait Msg {
pub trait Msg: Sized {
type ValidationError: std::error::Error;

// TODO -- clarify what is this function supposed to do & its connection to ICS26 routing mod.
Expand Down
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");

andynog marked this conversation as resolved.
Show resolved Hide resolved
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
Loading