From b9440937cb35d62c5a2e833b1c34f3f28efc063c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 13:37:25 -0400 Subject: [PATCH 1/8] json marshal/unmarshal for stdtx msgcreateclient --- relayer/cli/src/commands.rs | 1 + relayer/relay/Cargo.toml | 7 +- relayer/relay/src/lib.rs | 1 + .../relay/src/query/client_consensus_state.rs | 2 +- relayer/relay/src/tx.rs | 186 ++++++++++++++++++ 5 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 relayer/relay/src/tx.rs diff --git a/relayer/cli/src/commands.rs b/relayer/cli/src/commands.rs index f1f7374a75..4289ea9c11 100644 --- a/relayer/cli/src/commands.rs +++ b/relayer/cli/src/commands.rs @@ -8,6 +8,7 @@ mod config; mod light; mod start; +mod tx; mod version; use self::{config::ConfigCmd, light::LightCmd, start::StartCmd, version::VersionCmd}; diff --git a/relayer/relay/Cargo.toml b/relayer/relay/Cargo.toml index bb9d1f32af..8065bd4ae5 100644 --- a/relayer/relay/Cargo.toml +++ b/relayer/relay/Cargo.toml @@ -9,18 +9,23 @@ authors = [ [dependencies] relayer-modules = { path = "../../modules" } -tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git" } +#tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git", branch="tendermint/v0.33"} +tendermint = { path = "../../../tendermint-rs/tendermint/" } anomaly = "0.2.0" async-trait = "0.1.24" humantime-serde = "1.0.0" serde = "1.0.97" +serde_json = "1" serde_cbor = "0.11.1" serde_derive = "1.0" sled = { version = "0.31.0", features = ["no_metrics", "no_logs"] } thiserror = "1.0.11" toml = "0.5" tracing = "0.1.13" +stdtx = { path = "../../../../iqlusioninc/crates/stdtx" } # "0.1.0" +subtle-encoding = { version = "0.5", features = ["bech32-preview"] } + [dev-dependencies] tokio = { version = "0.2.13", features = ["macros"] } diff --git a/relayer/relay/src/lib.rs b/relayer/relay/src/lib.rs index 0e9cf139fb..69de770576 100644 --- a/relayer/relay/src/lib.rs +++ b/relayer/relay/src/lib.rs @@ -17,4 +17,5 @@ pub mod config; pub mod error; pub mod query; pub mod store; +pub mod tx; pub mod util; diff --git a/relayer/relay/src/query/client_consensus_state.rs b/relayer/relay/src/query/client_consensus_state.rs index c2209a24b5..2795de6bf7 100644 --- a/relayer/relay/src/query/client_consensus_state.rs +++ b/relayer/relay/src/query/client_consensus_state.rs @@ -70,7 +70,7 @@ impl ConsensusStateResponse { abci_proof: abci::Proof, proof_height: Height, ) -> Self { - let proof = CommitmentProof::from_bytes(abci_proof.as_ref()); + let proof = CommitmentProof::from_bytes(&vec![]); // .as_ref()); let proof_path = CommitmentPath::from_path(ConsensusStatePath::new(client_id, proof_height)); diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs new file mode 100644 index 0000000000..7e3ab6b912 --- /dev/null +++ b/relayer/relay/src/tx.rs @@ -0,0 +1,186 @@ +use serde::{Deserialize, Serialize, Serializer}; +use serde_json; +use std::time::Duration; + +use tendermint::block::{Commit, Header}; +use tendermint::time::Time; +use tendermint::{account, serializers, validator}; + +use stdtx::amino_types::{StdFee, StdSignature}; +use stdtx::type_name::TypeName; + +// Work in progress for Amino and AminoJSON encoding of a CreateClient transaction. +// +// Current State: +// - Uses hard coded types - only MsgCreateClient +// - Only works for JSON +// - JSON marshalled StdTx can be signed by gaiacli (!) +// - Signed StdTx JSON can be unmarshalled +// +// TODO: +// - Generalize JSON to more Msg types +// - Marshal to Amino (make better use of Iqlusion's StdTx?) + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SignedHeaderVals { + SignedHeader: SignedHeader, // XXX: this should be unrolled in the Go ... + validator_set: validator::Set, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SignedHeader { + header: Header, + commit: Commit, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct MsgCreateClient { + #[serde(rename = "type")] + name: String, + value: MsgCreateClientInner, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct MsgCreateClientInner { + client_id: String, + header: SignedHeaderVals, + #[serde( + serialize_with = "serializers::serialize_duration", + deserialize_with = "serializers::parse_duration" + )] + trusting_period: Duration, + #[serde( + serialize_with = "serializers::serialize_duration", + deserialize_with = "serializers::parse_duration" + )] + unbonding_period: Duration, + address: String, // account::Id, +} + +impl MsgCreateClientInner { + // wrap it in the amino name + fn typed_msg(self) -> MsgCreateClient { + MsgCreateClient { + name: "ibc/client/MsgCreateClient".to_string(), + value: self, + } + } + + // turn it into a tx wrapped with the amino name + fn std_tx(self, gas: u64, memo: &str) -> StdTx { + let msg = self.typed_msg(); + let mut msgs = Vec::new(); + msgs.push(msg); + StdTx { + name: "cosmos-sdk/StdTx".to_string(), + value: StdTxInner { + msg: msgs, + fee: StdFee::for_gas(gas), + memo: memo.to_owned(), + signatures: Vec::new(), + }, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct StdTx { + #[serde(rename = "type")] + name: String, + value: StdTxInner, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct StdTxInner { + pub msg: Vec, // TODO generalize to Msgs. also the name should be msgs ... + pub fee: StdFee, + pub signatures: Vec, + pub memo: String, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::error; + use std::fs; + use std::fs::File; + use std::io::prelude::*; + use std::path::Path; + use std::str::FromStr; + + use subtle_encoding::hex; + + use tendermint::account::Id; + use tendermint::rpc::Client as RpcClient; + + // make a StdTx with MsgCreateClient from a local node. + // save it to "unsigned.json". + // this file can be successfully signed with gaiacli! + #[test] + fn test_create_client() { + let rpc_client = RpcClient::new("localhost:26657".parse().unwrap()); + let commit = block_on(rpc_client.latest_commit()) + .map_err(|e| error::Kind::Rpc.context(e)) + .unwrap(); + println!("{:?}", commit); + + let validators = block_on(rpc_client.validators(commit.signed_header.header.height)) + .map_err(|e| error::Kind::Rpc.context(e)) + .unwrap(); + println!("{:?}", validators); + + let shv = SignedHeaderVals { + SignedHeader: SignedHeader { + header: commit.signed_header.header, + commit: commit.signed_header.commit, + }, + validator_set: validator::Set::new(validators.validators), + }; + + let tp = Duration::new(10000, 0); + let up = Duration::new(1000000, 0); + let address = "cosmos1q6zae0v7jx5lq9ucu9qclls05enya987n684cd".to_string(); + + let msg = MsgCreateClientInner { + client_id: "someclient".to_string(), + header: shv, + trusting_period: tp, + unbonding_period: up, + address: address, //id, + }; + + let tx = msg.std_tx(3000, "mymemo"); + println!("{:?}", tx); + + let j = serde_json::to_string_pretty(&tx).unwrap(); + println!("JSON"); + println!("{}", j); + + let path = Path::new("unsigned.json"); + + let mut file = File::create(&path).unwrap(); + file.write_all(j.as_bytes()); + } + + // load signed.json from file and unmarshal it. + // the unmarshalling works. + #[test] + fn test_broadcast_create_client() { + //let rpc_client = RpcClient::new("localhost:26657".parse().unwrap()); + + let contents = fs::read_to_string("signed.json").unwrap(); + let tx: StdTx = serde_json::from_str(&contents).unwrap(); + println!("{:?}", tx); + } +} + +use std::future::Future; + +fn block_on(future: F) -> F::Output { + tokio::runtime::Builder::new() + .basic_scheduler() + .enable_all() + .build() + .unwrap() + .block_on(future) +} From c77e19a75cd20c8c052c91cb6eee7c315c752fc0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 13:56:18 -0400 Subject: [PATCH 2/8] use StdTx::Address --- relayer/relay/src/tx.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs index 7e3ab6b912..11d04e29d7 100644 --- a/relayer/relay/src/tx.rs +++ b/relayer/relay/src/tx.rs @@ -8,6 +8,7 @@ use tendermint::{account, serializers, validator}; use stdtx::amino_types::{StdFee, StdSignature}; use stdtx::type_name::TypeName; +use stdtx::Address; // Work in progress for Amino and AminoJSON encoding of a CreateClient transaction. // @@ -54,7 +55,7 @@ pub struct MsgCreateClientInner { deserialize_with = "serializers::parse_duration" )] unbonding_period: Duration, - address: String, // account::Id, + address: Address, // account::Id, } impl MsgCreateClientInner { @@ -139,14 +140,15 @@ mod tests { let tp = Duration::new(10000, 0); let up = Duration::new(1000000, 0); - let address = "cosmos1q6zae0v7jx5lq9ucu9qclls05enya987n684cd".to_string(); + let (_hrp, address) = + Address::from_bech32("cosmos1q6zae0v7jx5lq9ucu9qclls05enya987n684cd").unwrap(); let msg = MsgCreateClientInner { client_id: "someclient".to_string(), header: shv, trusting_period: tp, unbonding_period: up, - address: address, //id, + address: address, }; let tx = msg.std_tx(3000, "mymemo"); From c49726a0fec46420bef56dde87bff88d8d80ccb5 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 14:17:23 -0400 Subject: [PATCH 3/8] StdTx is generic over msg --- relayer/relay/src/tx.rs | 49 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs index 11d04e29d7..801bc905bf 100644 --- a/relayer/relay/src/tx.rs +++ b/relayer/relay/src/tx.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize, Serializer}; +use serde::{de::DeserializeOwned, Deserialize, Serialize, Serializer}; use serde_json; use std::time::Duration; @@ -6,20 +6,19 @@ use tendermint::block::{Commit, Header}; use tendermint::time::Time; use tendermint::{account, serializers, validator}; +use stdtx; use stdtx::amino_types::{StdFee, StdSignature}; use stdtx::type_name::TypeName; -use stdtx::Address; // Work in progress for Amino and AminoJSON encoding of a CreateClient transaction. // // Current State: -// - Uses hard coded types - only MsgCreateClient // - Only works for JSON // - JSON marshalled StdTx can be signed by gaiacli (!) // - Signed StdTx JSON can be unmarshalled // // TODO: -// - Generalize JSON to more Msg types +// - Generalize JSON decoding // - Marshal to Amino (make better use of Iqlusion's StdTx?) #[derive(Clone, Debug, Deserialize, Serialize)] @@ -55,7 +54,7 @@ pub struct MsgCreateClientInner { deserialize_with = "serializers::parse_duration" )] unbonding_period: Duration, - address: Address, // account::Id, + address: stdtx::Address, } impl MsgCreateClientInner { @@ -66,34 +65,32 @@ impl MsgCreateClientInner { value: self, } } +} - // turn it into a tx wrapped with the amino name - fn std_tx(self, gas: u64, memo: &str) -> StdTx { - let msg = self.typed_msg(); - let mut msgs = Vec::new(); - msgs.push(msg); - StdTx { - name: "cosmos-sdk/StdTx".to_string(), - value: StdTxInner { - msg: msgs, - fee: StdFee::for_gas(gas), - memo: memo.to_owned(), - signatures: Vec::new(), - }, - } +fn std_tx(msg: M, gas: u64, memo: &str) -> StdTx { + let mut msgs = Vec::new(); + msgs.push(msg); + StdTx { + name: "cosmos-sdk/StdTx".to_string(), + value: StdTxInner { + msg: msgs, + fee: StdFee::for_gas(gas), + memo: memo.to_owned(), + signatures: Vec::new(), + }, } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct StdTx { +pub struct StdTx { #[serde(rename = "type")] name: String, - value: StdTxInner, + value: StdTxInner, } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct StdTxInner { - pub msg: Vec, // TODO generalize to Msgs. also the name should be msgs ... +pub struct StdTxInner { + pub msg: Vec, // XXX name should be msgs ... pub fee: StdFee, pub signatures: Vec, pub memo: String, @@ -141,7 +138,7 @@ mod tests { let tp = Duration::new(10000, 0); let up = Duration::new(1000000, 0); let (_hrp, address) = - Address::from_bech32("cosmos1q6zae0v7jx5lq9ucu9qclls05enya987n684cd").unwrap(); + stdtx::Address::from_bech32("cosmos1q6zae0v7jx5lq9ucu9qclls05enya987n684cd").unwrap(); let msg = MsgCreateClientInner { client_id: "someclient".to_string(), @@ -151,7 +148,7 @@ mod tests { address: address, }; - let tx = msg.std_tx(3000, "mymemo"); + let tx = std_tx(msg.typed_msg(), 3000, "mymemo"); println!("{:?}", tx); let j = serde_json::to_string_pretty(&tx).unwrap(); @@ -171,7 +168,7 @@ mod tests { //let rpc_client = RpcClient::new("localhost:26657".parse().unwrap()); let contents = fs::read_to_string("signed.json").unwrap(); - let tx: StdTx = serde_json::from_str(&contents).unwrap(); + let tx: StdTx = serde_json::from_str(&contents).unwrap(); // TODO generalize println!("{:?}", tx); } } From 50f78ba0440d75c4fd57f115c26eb2fab52653df Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 16:58:23 -0400 Subject: [PATCH 4/8] amino encode --- relayer/relay/Cargo.toml | 2 + relayer/relay/src/amino.rs | 243 +++++++++++++++++++++++++++++++++++++ relayer/relay/src/lib.rs | 1 + relayer/relay/src/tx.rs | 69 ++++++----- 4 files changed, 288 insertions(+), 27 deletions(-) create mode 100644 relayer/relay/src/amino.rs diff --git a/relayer/relay/Cargo.toml b/relayer/relay/Cargo.toml index 8065bd4ae5..25ff4aeb00 100644 --- a/relayer/relay/Cargo.toml +++ b/relayer/relay/Cargo.toml @@ -15,6 +15,8 @@ tendermint = { path = "../../../tendermint-rs/tendermint/" } anomaly = "0.2.0" async-trait = "0.1.24" humantime-serde = "1.0.0" +prost-amino = "0.5" +prost-amino-derive = "0.5" serde = "1.0.97" serde_json = "1" serde_cbor = "0.11.1" diff --git a/relayer/relay/src/amino.rs b/relayer/relay/src/amino.rs new file mode 100644 index 0000000000..842152cbb9 --- /dev/null +++ b/relayer/relay/src/amino.rs @@ -0,0 +1,243 @@ +use crate::tx; +use prost_amino_derive::Message; + +use std::time::Duration; + +use tendermint::{amino_types, block, validator}; + +#[derive(Clone, Message)] +pub struct Version { + #[prost_amino(uint64, tag = "1")] + pub block: u64, + #[prost_amino(uint64)] + pub app: u64, +} + +#[derive(Clone, Message)] +pub struct Header { + #[prost_amino(message, tag = "1")] + pub version: Option, + #[prost_amino(string)] + pub chain_id: String, + #[prost_amino(int64)] + pub height: i64, + #[prost_amino(message)] + pub time: Option, + #[prost_amino(message)] + pub last_block_id: Option, + #[prost_amino(bytes)] + pub last_commit_hash: Vec, + #[prost_amino(bytes)] + pub data_hash: Vec, + #[prost_amino(bytes)] + pub validators_hash: Vec, + #[prost_amino(bytes)] + pub next_validators_hash: Vec, + #[prost_amino(bytes)] + pub consensus_hash: Vec, + #[prost_amino(bytes)] + pub app_hash: Vec, + #[prost_amino(bytes)] + pub last_results_hash: Vec, + #[prost_amino(bytes)] + pub evidence_hash: Vec, + #[prost_amino(bytes)] + pub proposer_address: Vec, +} + +impl From<&block::Header> for Header { + fn from(header: &block::Header) -> Header { + Header { + version: Some(Version { + block: header.version.block, + app: header.version.app, + }), + chain_id: header.chain_id.as_str().to_string(), + height: i64::from(header.height), + time: Some(amino_types::time::TimeMsg::from(header.time)), + last_block_id: match &header.last_block_id { + Some(id) => Some(amino_types::block_id::BlockId::from(id)), + None => None, + }, + last_commit_hash: option_hash(header.last_commit_hash), + data_hash: option_hash(header.data_hash), + validators_hash: hash(header.validators_hash), + next_validators_hash: hash(header.next_validators_hash), + consensus_hash: hash(header.consensus_hash), + app_hash: header.app_hash.clone(), // XXX + last_results_hash: option_hash(header.last_results_hash), + evidence_hash: option_hash(header.evidence_hash), + proposer_address: header.proposer_address.as_bytes().to_vec(), + } + } +} + +fn option_hash(oh: Option) -> Vec { + match oh { + Some(h) => hash(h), + None => Vec::new(), + } +} + +fn hash(h: tendermint::Hash) -> Vec { + h.as_bytes().to_vec() +} + +#[derive(Clone, Message)] +pub struct CommitSig { + #[prost_amino(uint32, tag = "1")] + block_id_flag: u32, + #[prost_amino(bytes)] + validator_address: Vec, + #[prost_amino(message)] + timestamp: Option, + #[prost_amino(bytes)] + signature: Vec, +} + +impl From<&block::CommitSig> for CommitSig { + fn from(cs: &block::CommitSig) -> Self { + CommitSig { + block_id_flag: cs.block_id_flag.to_u32(), + validator_address: match cs.validator_address { + Some(addr) => addr.as_bytes().to_vec(), + None => Vec::new(), + }, + timestamp: Some(amino_types::time::TimeMsg::from(cs.timestamp)), + signature: match &cs.signature { + Some(sig) => sig.as_bytes().to_vec(), + None => Vec::new(), + }, + } + } +} + +#[derive(Clone, Message)] +pub struct Commit { + #[prost_amino(int64, tag = "1")] + height: i64, + #[prost_amino(int64)] + round: i64, + #[prost_amino(message)] + block_id: Option, + #[prost_amino(message, repeated)] + signatures: Vec, +} + +impl From<&block::Commit> for Commit { + fn from(commit: &block::Commit) -> Commit { + let mut sigs = Vec::new(); + for sig in commit.signatures.iter() { + sigs.push(CommitSig::from(sig)) + } + Commit { + height: i64::from(commit.height), + round: commit.round as i64, // XXX + block_id: Some(amino_types::block_id::BlockId::from(&commit.block_id)), + signatures: sigs, + } + } +} + +#[derive(Clone, Message)] +pub struct Validator { + #[prost_amino(bytes, tag = "1")] + address: Vec, + #[prost_amino(bytes)] + pub_key: Vec, + #[prost_amino(int64)] + voting_power: i64, + + // XXX: the Go version also has ProposerPriority! +} + +impl From<&validator::Info> for Validator{ + fn from(val: &validator::Info) -> Self { + Validator{ + address: val.address.as_bytes().to_vec(), + pub_key: val.pub_key.as_bytes(), + voting_power: val.voting_power.value() as i64, // XXX + } + } +} + + +#[derive(Clone, Message)] +pub struct ValidatorSet { + #[prost_amino(message, repeated, tag = "1")] + validators: Vec, + + // XXX: the Go version also has the proposer +} + +impl From<&validator::Set> for ValidatorSet { + fn from(valset: &validator::Set) -> Self { + let mut vals = Vec::new(); + for val in valset.validators().iter(){ + vals.push(Validator::from(val)); + } + ValidatorSet{ + validators: vals, + } + } + +} + + + +#[derive(Clone, Message)] +pub struct SignedHeader { + #[prost_amino(message, tag = "1")] + header: Option
, + #[prost_amino(message)] + commit: Option, +} + +#[derive(Clone, Message)] +pub struct SignedHeaderVals { + #[prost_amino(message, tag = "1")] + SignedHeader: Option, + #[prost_amino(message)] + validator_set: Option, +} + +#[derive(Clone, Message)] +pub struct MsgCreateClient { + #[prost_amino(string, tag = "1")] + client_id: String, + #[prost_amino(message)] + header: Option, + #[prost_amino(message)] + trusting_period: Option, + #[prost_amino(message)] + unbonding_period: Option, + #[prost_amino(bytes)] + address: Vec, +} + +impl From<&tx::MsgCreateClientInner> for MsgCreateClient { + fn from(msg: &tx::MsgCreateClientInner) -> Self { + MsgCreateClient { + client_id: msg.client_id.clone(), + header: Some(SignedHeaderVals{ + SignedHeader: Some(SignedHeader{ + header: Some(Header::from(&msg.header.SignedHeader.header)), + commit: Some(Commit::from(&msg.header.SignedHeader.commit)), + }), + validator_set: Some(ValidatorSet::from(&msg.header.validator_set)), + }), + trusting_period: Some(duration(msg.trusting_period)), + unbonding_period: Some(duration(msg.unbonding_period)), + address: msg.address.as_ref().to_vec(), + } + + } +} + +fn duration(d: Duration) -> amino_types::time::TimeMsg{ + let seconds = d.as_secs() as i64; + let nanos = d.subsec_nanos() as i32; + amino_types::time::TimeMsg { seconds, nanos } +} + + diff --git a/relayer/relay/src/lib.rs b/relayer/relay/src/lib.rs index 69de770576..7fef777591 100644 --- a/relayer/relay/src/lib.rs +++ b/relayer/relay/src/lib.rs @@ -11,6 +11,7 @@ //! IBC Relayer implementation +pub mod amino; pub mod chain; pub mod client; pub mod config; diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs index 801bc905bf..d18e1ea103 100644 --- a/relayer/relay/src/tx.rs +++ b/relayer/relay/src/tx.rs @@ -1,10 +1,9 @@ -use serde::{de::DeserializeOwned, Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use serde_json; use std::time::Duration; use tendermint::block::{Commit, Header}; -use tendermint::time::Time; -use tendermint::{account, serializers, validator}; +use tendermint::{serializers, validator}; use stdtx; use stdtx::amino_types::{StdFee, StdSignature}; @@ -23,38 +22,38 @@ use stdtx::type_name::TypeName; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignedHeaderVals { - SignedHeader: SignedHeader, // XXX: this should be unrolled in the Go ... - validator_set: validator::Set, + pub SignedHeader: SignedHeader, // XXX: this should be unrolled in the Go ... + pub validator_set: validator::Set, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignedHeader { - header: Header, - commit: Commit, + pub header: Header, + pub commit: Commit, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct MsgCreateClient { #[serde(rename = "type")] - name: String, - value: MsgCreateClientInner, + pub name: String, + pub value: MsgCreateClientInner, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct MsgCreateClientInner { - client_id: String, - header: SignedHeaderVals, + pub client_id: String, + pub header: SignedHeaderVals, #[serde( serialize_with = "serializers::serialize_duration", deserialize_with = "serializers::parse_duration" )] - trusting_period: Duration, + pub trusting_period: Duration, #[serde( serialize_with = "serializers::serialize_duration", deserialize_with = "serializers::parse_duration" )] - unbonding_period: Duration, - address: stdtx::Address, + pub unbonding_period: Duration, + pub address: stdtx::Address, } impl MsgCreateClientInner { @@ -99,33 +98,28 @@ pub struct StdTxInner { #[cfg(test)] mod tests { use super::*; + + use crate::amino; use crate::error; + use std::fs; use std::fs::File; use std::io::prelude::*; use std::path::Path; - use std::str::FromStr; - use subtle_encoding::hex; - - use tendermint::account::Id; use tendermint::rpc::Client as RpcClient; - // make a StdTx with MsgCreateClient from a local node. - // save it to "unsigned.json". - // this file can be successfully signed with gaiacli! - #[test] - fn test_create_client() { + use prost_amino::Message; + + fn msg_create_client() -> MsgCreateClientInner { let rpc_client = RpcClient::new("localhost:26657".parse().unwrap()); let commit = block_on(rpc_client.latest_commit()) .map_err(|e| error::Kind::Rpc.context(e)) .unwrap(); - println!("{:?}", commit); let validators = block_on(rpc_client.validators(commit.signed_header.header.height)) .map_err(|e| error::Kind::Rpc.context(e)) .unwrap(); - println!("{:?}", validators); let shv = SignedHeaderVals { SignedHeader: SignedHeader { @@ -140,13 +134,34 @@ mod tests { let (_hrp, address) = stdtx::Address::from_bech32("cosmos1q6zae0v7jx5lq9ucu9qclls05enya987n684cd").unwrap(); - let msg = MsgCreateClientInner { + MsgCreateClientInner { client_id: "someclient".to_string(), header: shv, trusting_period: tp, unbonding_period: up, address: address, - }; + } + } + + // amino encode msg + #[test] + fn test_amino() { + let msg = msg_create_client(); + let amino_msg = amino::MsgCreateClient::from(&msg); + let type_name = TypeName::new("ibc/client/MsgCreateClient").unwrap(); + let mut amino_bytes = type_name.amino_prefix(); + amino_msg + .encode(&mut amino_bytes) + .expect("LEB128 encoding error"); + println!("{:?}", amino_bytes); + } + + // make a StdTx with MsgCreateClient from a local node. + // save it to "unsigned.json". + // this file can be successfully signed with gaiacli! + #[test] + fn test_create_client() { + let msg = msg_create_client(); let tx = std_tx(msg.typed_msg(), 3000, "mymemo"); println!("{:?}", tx); From 889a60ea11cd3e93cf528979e9a5bebd2251f54b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 18:36:25 -0400 Subject: [PATCH 5/8] amino fixes --- relayer/relay/src/amino.rs | 77 ++++++++++++++++---------------------- relayer/relay/src/tx.rs | 40 +++++++++++++++++++- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/relayer/relay/src/amino.rs b/relayer/relay/src/amino.rs index 842152cbb9..e0e9fcaa83 100644 --- a/relayer/relay/src/amino.rs +++ b/relayer/relay/src/amino.rs @@ -86,13 +86,13 @@ fn hash(h: tendermint::Hash) -> Vec { #[derive(Clone, Message)] pub struct CommitSig { #[prost_amino(uint32, tag = "1")] - block_id_flag: u32, + pub block_id_flag: u32, #[prost_amino(bytes)] - validator_address: Vec, + pub validator_address: Vec, #[prost_amino(message)] - timestamp: Option, + pub timestamp: Option, #[prost_amino(bytes)] - signature: Vec, + pub signature: Vec, } impl From<&block::CommitSig> for CommitSig { @@ -115,13 +115,13 @@ impl From<&block::CommitSig> for CommitSig { #[derive(Clone, Message)] pub struct Commit { #[prost_amino(int64, tag = "1")] - height: i64, + pub height: i64, #[prost_amino(int64)] - round: i64, + pub round: i64, #[prost_amino(message)] - block_id: Option, + pub block_id: Option, #[prost_amino(message, repeated)] - signatures: Vec, + pub signatures: Vec, } impl From<&block::Commit> for Commit { @@ -142,85 +142,77 @@ impl From<&block::Commit> for Commit { #[derive(Clone, Message)] pub struct Validator { #[prost_amino(bytes, tag = "1")] - address: Vec, + pub address: Vec, #[prost_amino(bytes)] - pub_key: Vec, - #[prost_amino(int64)] - voting_power: i64, - + pub pub_key: Vec, + #[prost_amino(int64)] + pub voting_power: i64, // XXX: the Go version also has ProposerPriority! } -impl From<&validator::Info> for Validator{ +impl From<&validator::Info> for Validator { fn from(val: &validator::Info) -> Self { - Validator{ + Validator { address: val.address.as_bytes().to_vec(), - pub_key: val.pub_key.as_bytes(), + pub_key: val.pub_key.to_amino_bytes(), voting_power: val.voting_power.value() as i64, // XXX } } } - #[derive(Clone, Message)] pub struct ValidatorSet { #[prost_amino(message, repeated, tag = "1")] - validators: Vec, - + pub validators: Vec, // XXX: the Go version also has the proposer } impl From<&validator::Set> for ValidatorSet { fn from(valset: &validator::Set) -> Self { let mut vals = Vec::new(); - for val in valset.validators().iter(){ + for val in valset.validators().iter() { vals.push(Validator::from(val)); } - ValidatorSet{ - validators: vals, - } + ValidatorSet { validators: vals } } - } - - #[derive(Clone, Message)] pub struct SignedHeader { #[prost_amino(message, tag = "1")] - header: Option
, + pub header: Option
, #[prost_amino(message)] - commit: Option, + pub commit: Option, } #[derive(Clone, Message)] pub struct SignedHeaderVals { #[prost_amino(message, tag = "1")] - SignedHeader: Option, + pub SignedHeader: Option, #[prost_amino(message)] - validator_set: Option, + pub validator_set: Option, } #[derive(Clone, Message)] pub struct MsgCreateClient { #[prost_amino(string, tag = "1")] - client_id: String, + pub client_id: String, #[prost_amino(message)] - header: Option, + pub header: Option, #[prost_amino(message)] - trusting_period: Option, + pub trusting_period: Option, #[prost_amino(message)] - unbonding_period: Option, + pub unbonding_period: Option, #[prost_amino(bytes)] - address: Vec, + pub address: Vec, } impl From<&tx::MsgCreateClientInner> for MsgCreateClient { fn from(msg: &tx::MsgCreateClientInner) -> Self { MsgCreateClient { client_id: msg.client_id.clone(), - header: Some(SignedHeaderVals{ - SignedHeader: Some(SignedHeader{ + header: Some(SignedHeaderVals { + SignedHeader: Some(SignedHeader { header: Some(Header::from(&msg.header.SignedHeader.header)), commit: Some(Commit::from(&msg.header.SignedHeader.commit)), }), @@ -230,14 +222,11 @@ impl From<&tx::MsgCreateClientInner> for MsgCreateClient { unbonding_period: Some(duration(msg.unbonding_period)), address: msg.address.as_ref().to_vec(), } - } } -fn duration(d: Duration) -> amino_types::time::TimeMsg{ - let seconds = d.as_secs() as i64; - let nanos = d.subsec_nanos() as i32; - amino_types::time::TimeMsg { seconds, nanos } +fn duration(d: Duration) -> amino_types::time::TimeMsg { + let seconds = d.as_secs() as i64; + let nanos = d.subsec_nanos() as i32; + amino_types::time::TimeMsg { seconds, nanos } } - - diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs index d18e1ea103..166529bf18 100644 --- a/relayer/relay/src/tx.rs +++ b/relayer/relay/src/tx.rs @@ -111,6 +111,13 @@ mod tests { use prost_amino::Message; + use subtle_encoding::hex; + + fn to_hex(bytes: &[u8]) -> String { + let hex_bytes = hex::encode(bytes); + String::from_utf8(hex_bytes).unwrap() + } + fn msg_create_client() -> MsgCreateClientInner { let rpc_client = RpcClient::new("localhost:26657".parse().unwrap()); let commit = block_on(rpc_client.latest_commit()) @@ -153,7 +160,7 @@ mod tests { amino_msg .encode(&mut amino_bytes) .expect("LEB128 encoding error"); - println!("{:?}", amino_bytes); + println!("{:?}", to_hex(&amino_bytes)); } // make a StdTx with MsgCreateClient from a local node. @@ -176,6 +183,15 @@ mod tests { file.write_all(j.as_bytes()); } + fn printer(name: &str, o: impl Message) { + // let type_name = TypeName::new("ibc/client/MsgCreateClient").unwrap(); + // let mut amino_bytes = type_name.amino_prefix(); + let mut amino_bytes = Vec::new(); + o.encode(&mut amino_bytes).expect("LEB128 encoding error"); + println!("{} -----------------------------", name); + println!("{:?}", to_hex(&amino_bytes)); + } + // load signed.json from file and unmarshal it. // the unmarshalling works. #[test] @@ -185,6 +201,28 @@ mod tests { let contents = fs::read_to_string("signed.json").unwrap(); let tx: StdTx = serde_json::from_str(&contents).unwrap(); // TODO generalize println!("{:?}", tx); + + let msg = &tx.value.msg[0].value; + let msg = &amino::MsgCreateClient::from(msg); + + let hcv = msg.header.as_ref().unwrap(); + let sh = hcv.SignedHeader.as_ref().unwrap(); + + let header = sh.header.as_ref().unwrap(); + let commit = sh.commit.as_ref().unwrap(); + let vals = hcv.validator_set.as_ref().unwrap(); + printer("header", header.clone()); + printer("commit", commit.clone()); + printer("vals", vals.clone()); + + printer("hcv", hcv.clone()); + printer("sh", sh.clone()); + + let type_name = TypeName::new("ibc/client/MsgCreateClient").unwrap(); + let mut amino_bytes = type_name.amino_prefix(); + msg.encode(&mut amino_bytes).expect("LEB128 encoding error"); + println!("MSG--------------------------"); + println!("{:?}", to_hex(&amino_bytes)); } } From 3a4172a99cbd08fa4acf2a2e7c7da7a41118df36 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 20:40:12 -0400 Subject: [PATCH 6/8] more amino wip --- relayer/relay/Cargo.toml | 4 ++-- relayer/relay/src/amino.rs | 1 + relayer/relay/src/tx.rs | 16 ++++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/relayer/relay/Cargo.toml b/relayer/relay/Cargo.toml index 25ff4aeb00..54dfed0c79 100644 --- a/relayer/relay/Cargo.toml +++ b/relayer/relay/Cargo.toml @@ -15,8 +15,8 @@ tendermint = { path = "../../../tendermint-rs/tendermint/" } anomaly = "0.2.0" async-trait = "0.1.24" humantime-serde = "1.0.0" -prost-amino = "0.5" -prost-amino-derive = "0.5" +prost-amino = { path = "../../../../tendermint/amino_rs" } # "0.5" +prost-amino-derive= { path = "../../../../tendermint/amino_rs/prost-amino-derive" } # "0.5" serde = "1.0.97" serde_json = "1" serde_cbor = "0.11.1" diff --git a/relayer/relay/src/amino.rs b/relayer/relay/src/amino.rs index e0e9fcaa83..af3c1071cf 100644 --- a/relayer/relay/src/amino.rs +++ b/relayer/relay/src/amino.rs @@ -186,6 +186,7 @@ pub struct SignedHeader { } #[derive(Clone, Message)] +#[amino_name = "ibc/client/tendermint/Header"] pub struct SignedHeaderVals { #[prost_amino(message, tag = "1")] pub SignedHeader: Option, diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs index 166529bf18..07bdcec525 100644 --- a/relayer/relay/src/tx.rs +++ b/relayer/relay/src/tx.rs @@ -12,13 +12,14 @@ use stdtx::type_name::TypeName; // Work in progress for Amino and AminoJSON encoding of a CreateClient transaction. // // Current State: -// - Only works for JSON // - JSON marshalled StdTx can be signed by gaiacli (!) // - Signed StdTx JSON can be unmarshalled +// - MsgCreateClient can be almost properly Amino encoded // // TODO: // - Generalize JSON decoding -// - Marshal to Amino (make better use of Iqlusion's StdTx?) +// - Fix Amino encoding for the Msg +// - Amino encode the StdTx (make better use of Iqlusion's StdTx?) #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignedHeaderVals { @@ -196,7 +197,6 @@ mod tests { // the unmarshalling works. #[test] fn test_broadcast_create_client() { - //let rpc_client = RpcClient::new("localhost:26657".parse().unwrap()); let contents = fs::read_to_string("signed.json").unwrap(); let tx: StdTx = serde_json::from_str(&contents).unwrap(); // TODO generalize @@ -208,16 +208,24 @@ mod tests { let hcv = msg.header.as_ref().unwrap(); let sh = hcv.SignedHeader.as_ref().unwrap(); + // these all encode perfectly matching the Go let header = sh.header.as_ref().unwrap(); let commit = sh.commit.as_ref().unwrap(); let vals = hcv.validator_set.as_ref().unwrap(); printer("header", header.clone()); printer("commit", commit.clone()); printer("vals", vals.clone()); - printer("hcv", hcv.clone()); printer("sh", sh.clone()); + // these dont yet match + printer("client", msg.client_id.clone()); + printer("trusting", msg.trusting_period.as_ref().unwrap().clone()); + printer("unbonding", msg.unbonding_period.as_ref().unwrap().clone()); + printer("address", msg.address.clone()); + + + // this doesn't yet match the Go either let type_name = TypeName::new("ibc/client/MsgCreateClient").unwrap(); let mut amino_bytes = type_name.amino_prefix(); msg.encode(&mut amino_bytes).expect("LEB128 encoding error"); From 80537c63b38d3b768ab0ceeed9237e2dc1bf9675 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 10 Apr 2020 23:53:08 -0400 Subject: [PATCH 7/8] local tendermint, minor fixes --- modules/Cargo.toml | 3 ++- relayer/relay/Cargo.toml | 4 ++-- relayer/relay/src/tx.rs | 29 ++++++++++++++--------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/modules/Cargo.toml b/modules/Cargo.toml index b983bae85e..290bea2cdb 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -15,7 +15,8 @@ default = ["paths-cosmos"] paths-cosmos = [] [dependencies] -tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git", branch = "tendermint/v0.33" } +tendermint = { path = "../../tendermint-rs/tendermint/" } +#tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git", branch = "tendermint/v0.33" } anomaly = "0.2.0" thiserror = "1.0.11" diff --git a/relayer/relay/Cargo.toml b/relayer/relay/Cargo.toml index 0154e307de..6e099f70f4 100644 --- a/relayer/relay/Cargo.toml +++ b/relayer/relay/Cargo.toml @@ -9,8 +9,8 @@ authors = [ [dependencies] relayer-modules = { path = "../../modules" } -#tendermint = { path = "../../../tendermint-rs/tendermint/" } -tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git", branch = "tendermint/v0.33" } +tendermint = { path = "../../../tendermint-rs/tendermint/" } +#tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git", branch = "tendermint/v0.33" } anomaly = "0.2.0" async-trait = "0.1.24" humantime-serde = "1.0.0" diff --git a/relayer/relay/src/tx.rs b/relayer/relay/src/tx.rs index 07bdcec525..7d5fd87378 100644 --- a/relayer/relay/src/tx.rs +++ b/relayer/relay/src/tx.rs @@ -23,7 +23,7 @@ use stdtx::type_name::TypeName; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignedHeaderVals { - pub SignedHeader: SignedHeader, // XXX: this should be unrolled in the Go ... + pub SignedHeader: SignedHeader, // XXX: matches the go. should be unrolled and camel cased pub validator_set: validator::Set, } @@ -114,6 +114,18 @@ mod tests { use subtle_encoding::hex; + use std::future::Future; + use tokio; + + fn block_on(future: F) -> F::Output { + tokio::runtime::Builder::new() + .basic_scheduler() + .enable_all() + .build() + .unwrap() + .block_on(future) + } + fn to_hex(bytes: &[u8]) -> String { let hex_bytes = hex::encode(bytes); String::from_utf8(hex_bytes).unwrap() @@ -197,7 +209,6 @@ mod tests { // the unmarshalling works. #[test] fn test_broadcast_create_client() { - let contents = fs::read_to_string("signed.json").unwrap(); let tx: StdTx = serde_json::from_str(&contents).unwrap(); // TODO generalize println!("{:?}", tx); @@ -218,13 +229,12 @@ mod tests { printer("hcv", hcv.clone()); printer("sh", sh.clone()); - // these dont yet match + // these dont yet match printer("client", msg.client_id.clone()); printer("trusting", msg.trusting_period.as_ref().unwrap().clone()); printer("unbonding", msg.unbonding_period.as_ref().unwrap().clone()); printer("address", msg.address.clone()); - // this doesn't yet match the Go either let type_name = TypeName::new("ibc/client/MsgCreateClient").unwrap(); let mut amino_bytes = type_name.amino_prefix(); @@ -233,14 +243,3 @@ mod tests { println!("{:?}", to_hex(&amino_bytes)); } } - -use std::future::Future; - -fn block_on(future: F) -> F::Output { - tokio::runtime::Builder::new() - .basic_scheduler() - .enable_all() - .build() - .unwrap() - .block_on(future) -} From ff34dd0649d1784175418e18feb94c7902e6a6dd Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 11 Apr 2020 00:06:54 -0400 Subject: [PATCH 8/8] amino.go script --- relayer/relay/src/amino.go | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 relayer/relay/src/amino.go diff --git a/relayer/relay/src/amino.go b/relayer/relay/src/amino.go new file mode 100644 index 0000000000..d0d57b5b1d --- /dev/null +++ b/relayer/relay/src/amino.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "io/ioutil" + + "github.com/cosmos/cosmos-sdk/codec" + codecstd "github.com/cosmos/cosmos-sdk/codec/std" + "github.com/cosmos/cosmos-sdk/x/auth" + ibc "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" + "github.com/cosmos/gaia/app" +) + +// NOTE: this should be run from within the gaia repo, on branch ibc-alpha + +func main() { + b, err := ioutil.ReadFile("signed.json") + if err != nil { + panic(err) + } + + cdc := codecstd.MakeCodec(app.ModuleBasics) + + var stdtx auth.StdTx + err = cdc.UnmarshalJSON(b, &stdtx) + if err != nil { + panic(err) + } + + msg := stdtx.Msgs[0].(ibc.MsgCreateClient) + hcv := msg.Header + header := hcv.SignedHeader.Header + commit := hcv.SignedHeader.Commit + vals := hcv.ValidatorSet + + printer(cdc, "header", header) + printer(cdc, "commit", commit) + printer(cdc, "vals", vals) + printer(cdc, "hcv", hcv) + printer(cdc, "sh", hcv.SignedHeader) + printer(cdc, "msg", msg) + printer(cdc, "client", msg.ClientID) + printer(cdc, "trusting_period", msg.TrustingPeriod) + printer(cdc, "unbonding_period", msg.UnbondingPeriod) + printer(cdc, "address", msg.Signer) + +} + +func printer(cdc *codec.Codec, name string, o interface{}) { + b, err := cdc.MarshalBinaryBare(o) + if err != nil { + panic(err) + } + fmt.Printf("%s --------------------------------------------\n", name) + fmt.Printf("%x\n", b) + +}