From ac3b68845e244726f28d05626505cc3e78165392 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 25 Nov 2020 17:19:42 +0100 Subject: [PATCH 01/19] Attemptin integration at the level of Chain trait --- modules/Cargo.toml | 4 +- modules/src/ics02_client/client_def.rs | 5 +- modules/src/lib.rs | 2 +- relayer/Cargo.toml | 2 + relayer/src/chain.rs | 3 + relayer/src/chain/mock.rs | 129 +++++++++++++++++++++++++ 6 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 relayer/src/chain/mock.rs diff --git a/modules/Cargo.toml b/modules/Cargo.toml index 9efae2ed78..9192972d09 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -51,10 +51,10 @@ version = "=0.17.0-rc3" version = "=0.17.0-rc3" [dependencies.tendermint-testgen] -version = "0.17.0-rc2" +version = "0.17.0-rc3" optional = true [dev-dependencies] tokio = { version = "0.3", features = ["macros"] } subtle-encoding = { version = "0.5" } -tendermint-testgen = { version = "0.17.0-rc2" } # Needed for generating (synthetic) light blocks. +tendermint-testgen = { version = "0.17.0-rc3" } # Needed for generating (synthetic) light blocks. diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index dc615a2c63..98b9ddd3e0 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -9,7 +9,6 @@ use crate::ics02_client::error::{Error, Kind}; use crate::ics02_client::header::Header; use crate::ics02_client::state::{ClientState, ConsensusState}; use crate::ics03_connection::connection::ConnectionEnd; -use crate::ics07_tendermint as tendermint; use crate::ics07_tendermint::client_def::TendermintClient; use crate::ics07_tendermint::client_state::ClientState as TendermintClientState; use crate::ics07_tendermint::consensus_state::ConsensusState as TendermintConsensusState; @@ -93,7 +92,7 @@ pub trait ClientDef: Clone { #[derive(Clone, Debug, PartialEq)] // TODO: Add Eq bound once possible #[allow(clippy::large_enum_variant)] pub enum AnyHeader { - Tendermint(tendermint::header::Header), + Tendermint(TendermintHeader), #[cfg(any(test, feature = "mocks"))] Mock(MockHeader), @@ -258,7 +257,7 @@ impl ClientState for AnyClientState { #[derive(Clone, Debug, PartialEq, Eq)] pub enum AnyConsensusState { - Tendermint(crate::ics07_tendermint::consensus_state::ConsensusState), + Tendermint(TendermintConsensusState), #[cfg(any(test, feature = "mocks"))] Mock(MockConsensusState), diff --git a/modules/src/lib.rs b/modules/src/lib.rs index f3f6e5acc2..47315e384e 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -48,4 +48,4 @@ mod test; mod test_utils; #[cfg(any(test, feature = "mocks"))] -mod mock; // Context mock, the underlying host chain, and client types: for testing all handlers. +pub mod mock; // Context mock, the underlying host chain, and client types: for testing all handlers. diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index bd7f743c90..5dccf725b7 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -54,3 +54,5 @@ version = "=0.17.0-rc3" [dev-dependencies] serial_test = "0.5.0" +ibc = { path = "../modules", features = [ "mocks" ] } +tendermint-testgen = { version = "0.17.0-rc3" } \ No newline at end of file diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 8f02fb8699..712e5e9882 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -4,6 +4,9 @@ pub use cosmos::CosmosSDKChain; pub mod handle; pub mod runtime; +#[cfg(test)] +mod mock; + use prost_types::Any; use tendermint_proto::Protobuf; diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs new file mode 100644 index 0000000000..e36576d421 --- /dev/null +++ b/relayer/src/chain/mock.rs @@ -0,0 +1,129 @@ +#![allow(dead_code, unused_variables)] + +use prost_types::Any; +use tendermint::account::Id; +use tendermint_rpc::HttpClient; +use tendermint_testgen::light_block::TMLightBlock; + +use ibc::ics07_tendermint::client_state::ClientState as TendermintClientState; +use ibc::ics07_tendermint::consensus_state::ConsensusState as TendermintConsensusState; +use ibc::ics07_tendermint::header::Header as TendermintHeader; +use ibc::ics18_relayer::context::ICS18Context; +use ibc::ics23_commitment::merkle::MerkleProof; +use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc::ics24_host::Path; +use ibc::mock::context::MockContext; +use ibc::mock::host::HostType; +use ibc::Height; + +use crate::chain::{Chain, QueryResponse}; +use crate::config::ChainConfig; +use crate::error::Error; +use crate::keyring::store::{KeyEntry, KeyRing}; + +/// The representation of a mocked chain as the relayer sees it. +/// The relayer runtime and the light client will engage with the MockChain to query/send tx. +pub struct MockChain { + pub context: MockContext, +} + +impl MockChain { + pub fn new() -> MockChain { + let chain_version = 1; + MockChain { + context: MockContext::new( + ChainId::new("mockgaia".to_string(), chain_version), + HostType::SyntheticTendermint, + 50, + Height::new(chain_version, 20), + ), + } + } +} + +impl Chain for MockChain { + type LightBlock = TMLightBlock; + type Header = TendermintHeader; + type ConsensusState = TendermintConsensusState; + type ClientState = TendermintClientState; + type RpcClient = HttpClient; + + fn config(&self) -> &ChainConfig { + unimplemented!() + } + + fn keybase(&self) -> &KeyRing { + unimplemented!() + } + + fn rpc_client(&self) -> &Self::RpcClient { + unimplemented!() + } + + fn query(&self, data: Path, height: Height, prove: bool) -> Result { + unimplemented!() + } + + fn send_tx(&self, proto_msgs: Vec) -> Result { + unimplemented!() + } + + fn get_signer(&mut self) -> Result { + unimplemented!() + } + + fn get_key(&mut self) -> Result { + unimplemented!() + } + + fn build_client_state(&self, height: Height) -> Result { + unimplemented!() + } + + fn build_consensus_state( + &self, + light_block: Self::LightBlock, + ) -> Result { + unimplemented!() + } + + fn build_header( + &self, + trusted_light_block: Self::LightBlock, + target_light_block: Self::LightBlock, + ) -> Result { + unimplemented!() + } + + fn query_latest_height(&self) -> Result { + Ok(self.context.query_latest_height()) + } + + fn query_client_state( + &self, + client_id: &ClientId, + height: Height, + ) -> Result { + unimplemented!() + } + + fn proven_client_state( + &self, + client_id: &ClientId, + height: Height, + ) -> Result<(Self::ClientState, MerkleProof), Error> { + unimplemented!() + } + + fn proven_client_consensus( + &self, + client_id: &ClientId, + consensus_height: Height, + height: Height, + ) -> Result<(Self::ConsensusState, MerkleProof), Error> { + unimplemented!() + } +} + +// Integration tests with the modules +mod test {} From c501140ef714d43544ad990ad05443b7447e2b1e Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 25 Nov 2020 19:01:39 +0100 Subject: [PATCH 02/19] Removed rpc client from trait Chain --- relayer/src/chain.rs | 9 --------- relayer/src/chain/cosmos.rs | 10 +++++----- relayer/src/chain/mock.rs | 6 ------ 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 712e5e9882..a9f6abf620 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -15,8 +15,6 @@ use tendermint_proto::Protobuf; use tendermint::account::Id as AccountId; use tendermint::block::Height; -use tendermint_rpc::Client as RpcClient; - use ibc::ics02_client::header::Header; use ibc::ics02_client::state::{ClientState, ConsensusState}; @@ -61,9 +59,6 @@ pub trait Chain { /// Type of the client state for this chain type ClientState: ClientState; - /// Type of RPC requester (wrapper around low-level RPC client) for this chain - type RpcClient: RpcClient + Send + Sync; - /// Returns the chain's identifier fn id(&self) -> &ChainId { &self.config().id @@ -75,10 +70,6 @@ pub trait Chain { /// Returns the chain's keybase fn keybase(&self) -> &KeyRing; - /// Get a low-level RPC client for this chain - /// TODO - Should this be part of the Chain trait? - fn rpc_client(&self) -> &Self::RpcClient; - /// Perform a generic ICS `query`, and return the corresponding response data. fn query(&self, data: Path, height: ICSHeight, prove: bool) -> Result; diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index f040b31151..b1dd91a66c 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -111,6 +111,10 @@ impl CosmosSDKChain { Ok(Duration::from_secs(res.seconds as u64)) } + fn rpc_client(&self) -> &HttpClient { + &self.rpc_client + } + /// Query the consensus parameters via an RPC query /// Specific to the SDK and used only for Tendermint client create pub fn query_consensus_params(&self) -> Result { @@ -125,18 +129,14 @@ impl CosmosSDKChain { impl Chain for CosmosSDKChain { type Header = TMHeader; type LightBlock = TMLightBlock; - type RpcClient = HttpClient; type ConsensusState = ConsensusState; + type ClientState = ClientState; fn config(&self) -> &ChainConfig { &self.config } - fn rpc_client(&self) -> &HttpClient { - &self.rpc_client - } - fn query(&self, data: Path, height: ICSHeight, prove: bool) -> Result { let path = TendermintABCIPath::from_str(IBC_QUERY_PATH).unwrap(); diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index e36576d421..06d8db2c4d 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -2,7 +2,6 @@ use prost_types::Any; use tendermint::account::Id; -use tendermint_rpc::HttpClient; use tendermint_testgen::light_block::TMLightBlock; use ibc::ics07_tendermint::client_state::ClientState as TendermintClientState; @@ -46,7 +45,6 @@ impl Chain for MockChain { type Header = TendermintHeader; type ConsensusState = TendermintConsensusState; type ClientState = TendermintClientState; - type RpcClient = HttpClient; fn config(&self) -> &ChainConfig { unimplemented!() @@ -56,10 +54,6 @@ impl Chain for MockChain { unimplemented!() } - fn rpc_client(&self) -> &Self::RpcClient { - unimplemented!() - } - fn query(&self, data: Path, height: Height, prove: bool) -> Result { unimplemented!() } From 68771b6444d4f783868218a1c37c814ce9c96f1e Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 25 Nov 2020 19:20:35 +0100 Subject: [PATCH 03/19] Removed ChainConfig from trait Chain --- relayer/src/chain.rs | 15 ++------------- relayer/src/chain/cosmos.rs | 17 ++++++++++++++--- relayer/src/chain/mock.rs | 8 ++++++-- relayer/src/keys/add.rs | 2 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index a9f6abf620..39d85fe039 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -31,7 +31,6 @@ use ibc::ics04_channel::channel::ChannelEnd; use ibc::ics23_commitment::merkle::MerkleProof; use ibc::Height as ICSHeight; -use crate::config::ChainConfig; use crate::error::{Error, Kind}; use crate::keyring::store::{KeyEntry, KeyRing}; use crate::tx::connection::ConnectionMsgType; @@ -60,12 +59,7 @@ pub trait Chain { type ClientState: ClientState; /// Returns the chain's identifier - fn id(&self) -> &ChainId { - &self.config().id - } - - /// Returns the chain's configuration - fn config(&self) -> &ChainConfig; + fn id(&self) -> &ChainId; /// Returns the chain's keybase fn keybase(&self) -> &KeyRing; @@ -105,12 +99,7 @@ pub trait Chain { height: ICSHeight, ) -> Result; - fn query_commitment_prefix(&self) -> Result { - // TODO - do a real chain query - Ok(CommitmentPrefix::from( - self.config().store_prefix.as_bytes().to_vec(), - )) - } + fn query_commitment_prefix(&self) -> Result; fn query_compatible_versions(&self) -> Result, Error> { // TODO - do a real chain query diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index b1dd91a66c..94c6762e79 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -33,6 +33,7 @@ use ibc::ics07_tendermint::consensus_state::ConsensusState as TMConsensusState; use ibc::ics07_tendermint::consensus_state::ConsensusState; use ibc::ics07_tendermint::header::Header as TMHeader; +use ibc::ics23_commitment::commitment::CommitmentPrefix; use ibc::ics23_commitment::merkle::MerkleProof; use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc::ics24_host::Path::ClientConsensusState as ClientConsensusPath; @@ -115,6 +116,10 @@ impl CosmosSDKChain { &self.rpc_client } + pub fn config(&self) -> &ChainConfig { + &self.config + } + /// Query the consensus parameters via an RPC query /// Specific to the SDK and used only for Tendermint client create pub fn query_consensus_params(&self) -> Result { @@ -130,11 +135,10 @@ impl Chain for CosmosSDKChain { type Header = TMHeader; type LightBlock = TMLightBlock; type ConsensusState = ConsensusState; - type ClientState = ClientState; - fn config(&self) -> &ChainConfig { - &self.config + fn id(&self) -> &ChainId { + &self.config().id } fn query(&self, data: Path, height: ICSHeight, prove: bool) -> Result { @@ -295,6 +299,13 @@ impl Chain for CosmosSDKChain { Ok(client_state) } + fn query_commitment_prefix(&self) -> Result { + // TODO - do a real chain query + Ok(CommitmentPrefix::from( + self.config().store_prefix.as_bytes().to_vec(), + )) + } + fn proven_client_state( &self, client_id: &ClientId, diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 06d8db2c4d..abc3eafb62 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -16,9 +16,9 @@ use ibc::mock::host::HostType; use ibc::Height; use crate::chain::{Chain, QueryResponse}; -use crate::config::ChainConfig; use crate::error::Error; use crate::keyring::store::{KeyEntry, KeyRing}; +use ibc::ics23_commitment::commitment::CommitmentPrefix; /// The representation of a mocked chain as the relayer sees it. /// The relayer runtime and the light client will engage with the MockChain to query/send tx. @@ -46,7 +46,7 @@ impl Chain for MockChain { type ConsensusState = TendermintConsensusState; type ClientState = TendermintClientState; - fn config(&self) -> &ChainConfig { + fn id(&self) -> &ChainId { unimplemented!() } @@ -101,6 +101,10 @@ impl Chain for MockChain { unimplemented!() } + fn query_commitment_prefix(&self) -> Result { + unimplemented!() + } + fn proven_client_state( &self, client_id: &ClientId, diff --git a/relayer/src/keys/add.rs b/relayer/src/keys/add.rs index 9339e08636..75ead6aa9f 100644 --- a/relayer/src/keys/add.rs +++ b/relayer/src/keys/add.rs @@ -38,7 +38,7 @@ pub fn add_key(opts: KeysAddOptions) -> Result { "Added key {} ({}) on {} chain", opts.name.as_str(), k.account.as_str(), - chain.config().id.clone() + chain.id().clone() )) } Err(e) => Err(Kind::KeyBase.context(e).into()), From ba3031030a03cad24eca52ba8cf363ca288a8315 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 26 Nov 2020 15:20:15 +0100 Subject: [PATCH 04/19] Minor fixes as follow-up from #364 --- relayer/src/chain/cosmos.rs | 6 +----- relayer/src/chain/runtime.rs | 4 ++-- relayer/src/light_client/tendermint.rs | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index f7484cec44..c8ae21320d 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -64,12 +64,8 @@ pub struct CosmosSDKChain { impl CosmosSDKChain { pub fn from_config(config: ChainConfig, rt: Arc>) -> Result { - let primary = config - .primary() - .ok_or_else(|| Kind::LightClient.context("no primary peer specified"))?; - let rpc_client = - HttpClient::new(primary.address.clone()).map_err(|e| Kind::Rpc.context(e))?; + HttpClient::new(config.rpc_addr.clone()).map_err(|e| Kind::Rpc.context(e))?; // Initialize key store and load key let key_store = KeyRing::init(StoreBackend::Test, config.clone()) diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index f3624bbf30..e3aa5672ef 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -61,7 +61,7 @@ pub struct ChainRuntime { light_client: Box>, #[allow(dead_code)] - rt: Arc>, + rt: Arc>, // Making this future-proof, so we keep the runtime around. } impl ChainRuntime { @@ -96,7 +96,7 @@ impl ChainRuntime { // Spawn the event monitor let event_monitor_thread = thread::spawn(move || event_monitor.run()); - // Initialize the source and destination chain runtimes + // Initialize the chain runtime let chain_runtime = Self::cosmos_sdk(config, light_client, event_receiver, rt)?; // Get a handle to the runtime diff --git a/relayer/src/light_client/tendermint.rs b/relayer/src/light_client/tendermint.rs index e62ec8afab..73cbf49d7f 100644 --- a/relayer/src/light_client/tendermint.rs +++ b/relayer/src/light_client/tendermint.rs @@ -100,8 +100,6 @@ fn build_instance( } fn build_supervisor(config: &ChainConfig, reset: bool) -> Result { - let _id = config.id.clone(); - let options = light_client::Options { trust_threshold: config.trust_threshold, trusting_period: config.trusting_period, From be4da4ef0355b060ff38d3ba4018ceaa84e16c94 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 27 Nov 2020 15:49:03 +0100 Subject: [PATCH 05/19] Impl generic runtime instantiation. --- relayer/src/chain.rs | 28 +++++++++++++-- relayer/src/chain/cosmos.rs | 28 +++++++++++++++ relayer/src/chain/runtime.rs | 70 +++++++++++++++++++++++++++--------- 3 files changed, 107 insertions(+), 19 deletions(-) diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 39d85fe039..d91a3efe9b 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -4,8 +4,12 @@ pub use cosmos::CosmosSDKChain; pub mod handle; pub mod runtime; -#[cfg(test)] -mod mock; +// #[cfg(test)] +// mod mock; + +use crossbeam_channel as channel; +use std::sync::{Arc, Mutex}; +use tokio::runtime::Runtime as TokioRuntime; use prost_types::Any; @@ -14,6 +18,7 @@ use tendermint_proto::Protobuf; // TODO - tendermint deps should not be here use tendermint::account::Id as AccountId; use tendermint::block::Height; +use tendermint_light_client::supervisor::Supervisor; use ibc::ics02_client::header::Header; @@ -31,8 +36,11 @@ use ibc::ics04_channel::channel::ChannelEnd; use ibc::ics23_commitment::merkle::MerkleProof; use ibc::Height as ICSHeight; +use crate::config::ChainConfig; use crate::error::{Error, Kind}; +use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing}; +use crate::light_client::LightClient; use crate::tx::connection::ConnectionMsgType; /// Generic query response type @@ -45,7 +53,7 @@ pub struct QueryResponse { } /// Defines a blockchain as understood by the relayer -pub trait Chain { +pub trait Chain: Sized { /// Type of light blocks for this chain type LightBlock: Send + Sync; @@ -58,6 +66,20 @@ pub trait Chain { /// Type of the client state for this chain type ClientState: ClientState; + fn bootstrap(config: ChainConfig, rt: Arc>) -> Result; + + #[allow(clippy::type_complexity)] + /// Returns the light client (if any) associated with this chain. + /// This is primarily a helper method to be used by the runtime. + fn init_light_client(&self) -> Result<(Box>, Option), Error>; + + /// Returns the event monitor (if any) associated with this chain. + /// This is primarily a helper method to be used by the runtime. + fn init_event_monitor( + &self, + rt: Arc>, + ) -> Result<(Option, channel::Receiver), Error>; + /// Returns the chain's identifier fn id(&self) -> &ChainId; diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index c8ae21320d..a7af7b7a4a 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -7,6 +7,7 @@ use std::time::Duration; use anomaly::fail; use bitcoin::hashes::hex::ToHex; +use crossbeam_channel as channel; use prost::Message; use prost_types::Any; use tokio::runtime::Runtime as TokioRuntime; @@ -19,6 +20,7 @@ use tendermint::account::Id as AccountId; use tendermint::block::Height; use tendermint::consensus::Params; +use tendermint_light_client::supervisor::Supervisor; use tendermint_light_client::types::{LightBlock as TMLightBlock, ValidatorSet}; use tendermint_rpc::Client; use tendermint_rpc::HttpClient; @@ -49,7 +51,10 @@ use super::Chain; use crate::chain::QueryResponse; use crate::config::ChainConfig; use crate::error::{Error, Kind}; +use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing, KeyRingOperations, StoreBackend}; +use crate::light_client::tendermint::LightClient as TMLightClient; +use crate::light_client::LightClient; // Support for GRPC use ibc_proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountRequest}; @@ -137,6 +142,29 @@ impl Chain for CosmosSDKChain { type ConsensusState = ConsensusState; type ClientState = ClientState; + // TODO remove from_config or rename this method to from_config + fn bootstrap(config: ChainConfig, rt: Arc>) -> Result { + Self::from_config(config, rt) + } + + // TODO use a simpler approach to create the light client + #[allow(clippy::type_complexity)] + fn init_light_client(&self) -> Result<(Box>, Option), Error> { + let (lc, supervisor) = TMLightClient::from_config(&self.config, true)?; + + Ok((Box::new(lc), Some(supervisor))) + } + + fn init_event_monitor( + &self, + rt: Arc>, + ) -> Result<(Option, channel::Receiver), Error> { + let (event_monitor, event_receiver) = + EventMonitor::new(self.config.id.clone(), self.config.rpc_addr.clone(), rt)?; + + Ok((Some(event_monitor), event_receiver)) + } + fn id(&self) -> &ChainId { &self.config().id } diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index e3aa5672ef..424d75d6ef 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -27,8 +27,6 @@ use ibc::{ // FIXME: the handle should not depend on tendermint-specific types use tendermint::account::Id as AccountId; -// use crate::foreign_client::ForeignClient; - use crate::{ config::ChainConfig, error::{Error, Kind}, @@ -65,16 +63,6 @@ pub struct ChainRuntime { } impl ChainRuntime { - pub fn cosmos_sdk( - config: ChainConfig, - light_client: TMLightClient, - event_receiver: channel::Receiver, - rt: Arc>, - ) -> Result { - let chain = CosmosSDKChain::from_config(config, rt.clone())?; - Ok(Self::new(chain, light_client, event_receiver, rt)) - } - // TODO: Make this work for a generic Chain pub fn spawn(config: ChainConfig) -> Result<(impl ChainHandle, Threads), Error> { let rt = Arc::new(Mutex::new( @@ -97,7 +85,8 @@ impl ChainRuntime { let event_monitor_thread = thread::spawn(move || event_monitor.run()); // Initialize the chain runtime - let chain_runtime = Self::cosmos_sdk(config, light_client, event_receiver, rt)?; + let chain = CosmosSDKChain::from_config(config, rt.clone())?; + let chain_runtime = Self::new(chain, Box::new(light_client), event_receiver, rt); // Get a handle to the runtime let handle = chain_runtime.handle(); @@ -115,10 +104,59 @@ impl ChainRuntime { } } -impl ChainRuntime { +impl ChainRuntime { + pub fn initialize(config: ChainConfig) -> Result<(impl ChainHandle, Threads), Error> { + let rt = Arc::new(Mutex::new( + TokioRuntime::new().map_err(|e| Kind::Io.context(e))?, + )); + + // Similar to `from_config`... TODO + let chain = C::bootstrap(config, rt.clone())?; + + let (light_client_handler, supervisor_option) = chain.init_light_client()?; + + // If there is a light client supervisor, spawn it. + let light_client_thread = match supervisor_option { + Some(supervisor) => Some(thread::spawn(move || supervisor.run().unwrap())), + None => None, + }; + + let (event_monitor_option, event_receiver) = chain.init_event_monitor(rt.clone())?; + + let event_monitor_thread = match event_monitor_option { + // Spawn the event monitor + Some(mut event_monitor) => { + event_monitor.subscribe().unwrap(); + Some(thread::spawn(move || event_monitor.run())) + } + None => None, + }; + + // TODO: figure out how to deal with a missing light client thread & event monitor + assert!(light_client_thread.is_some()); + assert!(event_monitor_thread.is_some()); + + // Instantiate the runtime + let chain_runtime = Self::new(chain, light_client_handler, event_receiver, rt); + + // Get a handle to the runtime + let handle = chain_runtime.handle(); + + // Spawn the runtime + let runtime_thread = thread::spawn(move || chain_runtime.run().unwrap()); + + let threads = Threads { + light_client: light_client_thread.unwrap(), + chain_runtime: runtime_thread, + event_monitor: event_monitor_thread.unwrap(), + }; + + Ok((handle, threads)) + } + pub fn new( chain: C, - light_client: impl LightClient + 'static, + light_client: Box>, event_receiver: channel::Receiver, rt: Arc>, ) -> Self { @@ -131,7 +169,7 @@ impl ChainRuntime { receiver, event_bus: EventBus::new(), event_receiver, - light_client: Box::new(light_client), + light_client, } } From 71f7e377012c85b4f34aa137611c5e0ad52b45a4 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 27 Nov 2020 16:26:40 +0100 Subject: [PATCH 06/19] Migrated CLIs to use generic runtime spawn. --- relayer-cli/src/commands/tx/channel.rs | 7 ++- relayer-cli/src/commands/tx/client.rs | 9 ++-- relayer-cli/src/commands/tx/connection.rs | 7 ++- relayer-cli/src/commands/v0.rs | 5 +- relayer/src/chain.rs | 4 +- relayer/src/chain/mock.rs | 23 +++++++++ relayer/src/chain/runtime.rs | 60 +++-------------------- relayer/src/keys/restore.rs | 4 +- 8 files changed, 53 insertions(+), 66 deletions(-) diff --git a/relayer-cli/src/commands/tx/channel.rs b/relayer-cli/src/commands/tx/channel.rs index 4b4d3844af..eed31d1440 100644 --- a/relayer-cli/src/commands/tx/channel.rs +++ b/relayer-cli/src/commands/tx/channel.rs @@ -11,6 +11,7 @@ use relayer::channel::{ }; use relayer::chain::runtime::ChainRuntime; +use relayer::chain::CosmosSDKChain; use relayer::channel::{ChannelConfig, ChannelConfigSide}; macro_rules! chan_open_cmd { @@ -86,8 +87,10 @@ macro_rules! chan_open_cmd { status_info!("Message ", "{}: {:#?}", $dbg_string, opts); - let (src_chain, _) = ChainRuntime::spawn(src_chain_config.clone()).unwrap(); - let (dst_chain, _) = ChainRuntime::spawn(dst_chain_config.clone()).unwrap(); + let (src_chain, _) = + ChainRuntime::::spawn(src_chain_config.clone()).unwrap(); + let (dst_chain, _) = + ChainRuntime::::spawn(dst_chain_config.clone()).unwrap(); let res: Result = $func(dst_chain, src_chain, &opts).map_err(|e| Kind::Tx.context(e).into()); diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index 872d7950ca..4e838fea2e 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -6,6 +6,7 @@ use crate::application::app_config; use crate::error::{Error, Kind}; use crate::prelude::*; use relayer::chain::runtime::ChainRuntime; +use relayer::chain::CosmosSDKChain; use relayer::config::ChainConfig; use relayer::foreign_client::{ build_create_client_and_send, build_update_client_and_send, ForeignClientConfig, @@ -48,8 +49,8 @@ impl Runnable for TxCreateClientCmd { opts.chain_id() ); - let (src_chain, _) = ChainRuntime::spawn(src_chain_config).unwrap(); - let (dst_chain, _) = ChainRuntime::spawn(dst_chain_config).unwrap(); + let (src_chain, _) = ChainRuntime::::spawn(src_chain_config).unwrap(); + let (dst_chain, _) = ChainRuntime::::spawn(dst_chain_config).unwrap(); let res: Result = build_create_client_and_send(dst_chain, src_chain, &opts) .map_err(|e| Kind::Tx.context(e).into()); @@ -97,8 +98,8 @@ impl Runnable for TxUpdateClientCmd { opts.chain_id() ); - let (src_chain, _) = ChainRuntime::spawn(src_chain_config).unwrap(); - let (dst_chain, _) = ChainRuntime::spawn(dst_chain_config).unwrap(); + let (src_chain, _) = ChainRuntime::::spawn(src_chain_config).unwrap(); + let (dst_chain, _) = ChainRuntime::::spawn(dst_chain_config).unwrap(); let res: Result = build_update_client_and_send(dst_chain, src_chain, &opts) .map_err(|e| Kind::Tx.context(e).into()); diff --git a/relayer-cli/src/commands/tx/connection.rs b/relayer-cli/src/commands/tx/connection.rs index 42c9b24d87..ce9043380f 100644 --- a/relayer-cli/src/commands/tx/connection.rs +++ b/relayer-cli/src/commands/tx/connection.rs @@ -11,6 +11,7 @@ use relayer::connection::{ use crate::error::{Error, Kind}; use relayer::chain::runtime::ChainRuntime; +use relayer::chain::CosmosSDKChain; use relayer::connection::{ConnectionConfig, ConnectionSideConfig}; macro_rules! conn_open_cmd { @@ -75,8 +76,10 @@ macro_rules! conn_open_cmd { status_info!("Message ", "{}: {:#?}", $dbg_string, opts); - let (src_chain, _) = ChainRuntime::spawn(src_chain_config.clone()).unwrap(); - let (dst_chain, _) = ChainRuntime::spawn(dst_chain_config.clone()).unwrap(); + let (src_chain, _) = + ChainRuntime::::spawn(src_chain_config.clone()).unwrap(); + let (dst_chain, _) = + ChainRuntime::::spawn(dst_chain_config.clone()).unwrap(); let res: Result = $func(dst_chain, src_chain, &opts).map_err(|e| Kind::Tx.context(e).into()); diff --git a/relayer-cli/src/commands/v0.rs b/relayer-cli/src/commands/v0.rs index 728aa9a14e..451020b136 100644 --- a/relayer-cli/src/commands/v0.rs +++ b/relayer-cli/src/commands/v0.rs @@ -11,6 +11,7 @@ use relayer::relay::channel_relay; use crate::config::Config; use crate::prelude::*; +use relayer::chain::CosmosSDKChain; #[derive(Command, Debug, Options)] pub struct V0Cmd {} @@ -57,8 +58,8 @@ pub fn v0_task(config: &Config) -> Result<(), BoxError> { .find(|c| c.id == connection.dst().chain_id().clone()) .ok_or("Configuration for source chain not found")?; - let (src_chain_handle, _) = ChainRuntime::spawn(src_chain_config)?; - let (dst_chain_handle, _) = ChainRuntime::spawn(dst_chain_config)?; + let (src_chain_handle, _) = ChainRuntime::::spawn(src_chain_config)?; + let (dst_chain_handle, _) = ChainRuntime::::spawn(dst_chain_config)?; Ok(channel_relay( src_chain_handle, diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 1c17b6e2ae..c4a6671b4d 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -4,8 +4,8 @@ pub use cosmos::CosmosSDKChain; pub mod handle; pub mod runtime; -// #[cfg(test)] -// mod mock; +#[cfg(test)] +mod mock; use crossbeam_channel as channel; use std::sync::{Arc, Mutex}; diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index abc3eafb62..3c691ed2b4 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -16,9 +16,16 @@ use ibc::mock::host::HostType; use ibc::Height; use crate::chain::{Chain, QueryResponse}; +use crate::config::ChainConfig; use crate::error::Error; +use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing}; +use crate::light_client::LightClient; +use crossbeam_channel::Receiver; use ibc::ics23_commitment::commitment::CommitmentPrefix; +use std::sync::{Arc, Mutex}; +use tendermint_light_client::supervisor::Supervisor; +use tokio::runtime::Runtime; /// The representation of a mocked chain as the relayer sees it. /// The relayer runtime and the light client will engage with the MockChain to query/send tx. @@ -46,6 +53,22 @@ impl Chain for MockChain { type ConsensusState = TendermintConsensusState; type ClientState = TendermintClientState; + fn bootstrap(config: ChainConfig, rt: Arc>) -> Result { + unimplemented!() + } + + #[allow(clippy::type_complexity)] + fn init_light_client(&self) -> Result<(Box>, Option), Error> { + unimplemented!() + } + + fn init_event_monitor( + &self, + rt: Arc>, + ) -> Result<(Option, Receiver), Error> { + unimplemented!() + } + fn id(&self) -> &ChainId { unimplemented!() } diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index 03c0b805be..4dcd29c53d 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -31,17 +31,14 @@ use crate::{ config::ChainConfig, connection::ConnectionMsgType, error::{Error, Kind}, - event::{ - bus::EventBus, - monitor::{EventBatch, EventMonitor}, - }, + event::{bus::EventBus, monitor::EventBatch}, keyring::store::KeyEntry, - light_client::{tendermint::LightClient as TMLightClient, LightClient}, + light_client::LightClient, }; use super::{ handle::{ChainHandle, HandleInput, ProdChainHandle, ReplyTo, Subscription}, - Chain, CosmosSDKChain, QueryResponse, + Chain, QueryResponse, }; pub struct Threads { @@ -62,50 +59,9 @@ pub struct ChainRuntime { rt: Arc>, // Making this future-proof, so we keep the runtime around. } -impl ChainRuntime { - // TODO: Make this work for a generic Chain - pub fn spawn(config: ChainConfig) -> Result<(impl ChainHandle, Threads), Error> { - let rt = Arc::new(Mutex::new( - TokioRuntime::new().map_err(|e| Kind::Io.context(e))?, - )); - - // Initialize the light clients - let (light_client, supervisor) = TMLightClient::from_config(&config, true)?; - - // Spawn the light clients - let light_client_thread = thread::spawn(move || supervisor.run().unwrap()); - - let (mut event_monitor, event_receiver) = - EventMonitor::new(config.id.clone(), config.rpc_addr.clone(), rt.clone())?; - - // FIXME: Only connect/subscribe on demand + deal with error - event_monitor.subscribe().unwrap(); - - // Spawn the event monitor - let event_monitor_thread = thread::spawn(move || event_monitor.run()); - - // Initialize the chain runtime - let chain = CosmosSDKChain::from_config(config, rt.clone())?; - let chain_runtime = Self::new(chain, Box::new(light_client), event_receiver, rt); - - // Get a handle to the runtime - let handle = chain_runtime.handle(); - - // Spawn the runtime - let runtime_thread = thread::spawn(move || chain_runtime.run().unwrap()); - - let threads = Threads { - light_client: light_client_thread, - chain_runtime: runtime_thread, - event_monitor: event_monitor_thread, - }; - - Ok((handle, threads)) - } -} - impl ChainRuntime { - pub fn initialize(config: ChainConfig) -> Result<(impl ChainHandle, Threads), Error> { + /// Spawns a new runtime for a specific Chain implementation. + pub fn spawn(config: ChainConfig) -> Result<(impl ChainHandle, Threads), Error> { let rt = Arc::new(Mutex::new( TokioRuntime::new().map_err(|e| Kind::Io.context(e))?, )); @@ -137,7 +93,7 @@ impl ChainRuntime { assert!(event_monitor_thread.is_some()); // Instantiate the runtime - let chain_runtime = Self::new(chain, light_client_handler, event_receiver, rt); + let chain_runtime = Self::instantiate(chain, light_client_handler, event_receiver, rt); // Get a handle to the runtime let handle = chain_runtime.handle(); @@ -154,7 +110,7 @@ impl ChainRuntime { Ok((handle, threads)) } - pub fn new( + fn instantiate( chain: C, light_client: Box>, event_receiver: channel::Receiver, @@ -180,7 +136,7 @@ impl ChainRuntime { ProdChainHandle::new(chain_id, sender) } - pub fn run(mut self) -> Result<(), Error> { + fn run(mut self) -> Result<(), Error> { loop { channel::select! { recv(self.event_receiver) -> event_batch => { diff --git a/relayer/src/keys/restore.rs b/relayer/src/keys/restore.rs index c24d9d4b1d..1c7fe5e352 100644 --- a/relayer/src/keys/restore.rs +++ b/relayer/src/keys/restore.rs @@ -1,4 +1,4 @@ -use crate::chain::{handle::ChainHandle, runtime::ChainRuntime}; +use crate::chain::{handle::ChainHandle, runtime::ChainRuntime, CosmosSDKChain}; use crate::config::ChainConfig; use crate::error; use crate::error::Error; @@ -12,7 +12,7 @@ pub struct KeysRestoreOptions { pub fn restore_key(opts: KeysRestoreOptions) -> Result, Error> { // Get the destination chain - let (chain, _) = ChainRuntime::spawn(opts.chain_config)?; + let (chain, _) = ChainRuntime::::spawn(opts.chain_config)?; let address = chain .get_key() From b6963c974f6397f38d99997923988bb854c0acd4 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 27 Nov 2020 18:56:31 +0100 Subject: [PATCH 07/19] Added mock light client. First test (incomplete impl). --- relayer/src/chain.rs | 2 +- relayer/src/chain/cosmos.rs | 1 + relayer/src/chain/mock.rs | 80 ++++++++++++++++++++------------ relayer/src/chain/runtime.rs | 12 ++--- relayer/src/foreign_client.rs | 27 +++++++++++ relayer/src/light_client.rs | 3 ++ relayer/src/light_client/mock.rs | 42 +++++++++++++++++ 7 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 relayer/src/light_client/mock.rs diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index c4a6671b4d..fd48b05319 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -5,7 +5,7 @@ pub mod handle; pub mod runtime; #[cfg(test)] -mod mock; +pub mod mock; use crossbeam_channel as channel; use std::sync::{Arc, Mutex}; diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index ad53a3ab7f..469b8d1214 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -68,6 +68,7 @@ pub struct CosmosSDKChain { } impl CosmosSDKChain { + // TODO: This will be deprecated in favor of `Chain::bootstrap` method (see below) pub fn from_config(config: ChainConfig, rt: Arc>) -> Result { let rpc_client = HttpClient::new(config.rpc_addr.clone()).map_err(|e| Kind::Rpc.context(e))?; diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 3c691ed2b4..6520ff7f5a 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -1,13 +1,17 @@ -#![allow(dead_code, unused_variables)] +use std::sync::{Arc, Mutex}; +use crossbeam_channel as channel; use prost_types::Any; use tendermint::account::Id; +use tendermint_light_client::supervisor::Supervisor; use tendermint_testgen::light_block::TMLightBlock; +use tokio::runtime::Runtime; use ibc::ics07_tendermint::client_state::ClientState as TendermintClientState; use ibc::ics07_tendermint::consensus_state::ConsensusState as TendermintConsensusState; use ibc::ics07_tendermint::header::Header as TendermintHeader; use ibc::ics18_relayer::context::ICS18Context; +use ibc::ics23_commitment::commitment::CommitmentPrefix; use ibc::ics23_commitment::merkle::MerkleProof; use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc::ics24_host::Path; @@ -20,57 +24,51 @@ use crate::config::ChainConfig; use crate::error::Error; use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing}; -use crate::light_client::LightClient; -use crossbeam_channel::Receiver; -use ibc::ics23_commitment::commitment::CommitmentPrefix; -use std::sync::{Arc, Mutex}; -use tendermint_light_client::supervisor::Supervisor; -use tokio::runtime::Runtime; +use crate::light_client::{mock::LightClient as MockLightClient, LightClient}; /// The representation of a mocked chain as the relayer sees it. /// The relayer runtime and the light client will engage with the MockChain to query/send tx. pub struct MockChain { pub context: MockContext, + pub config: ChainConfig, } -impl MockChain { - pub fn new() -> MockChain { - let chain_version = 1; - MockChain { - context: MockContext::new( - ChainId::new("mockgaia".to_string(), chain_version), - HostType::SyntheticTendermint, - 50, - Height::new(chain_version, 20), - ), - } - } -} - +#[allow(unused_variables)] impl Chain for MockChain { type LightBlock = TMLightBlock; type Header = TendermintHeader; type ConsensusState = TendermintConsensusState; type ClientState = TendermintClientState; - fn bootstrap(config: ChainConfig, rt: Arc>) -> Result { - unimplemented!() + fn bootstrap(config: ChainConfig, _rt: Arc>) -> Result { + Ok(MockChain { + config: config.clone(), + context: MockContext::new( + config.id.clone(), + HostType::SyntheticTendermint, + 50, + Height::new(config.id.version(), 20), + ), + }) } #[allow(clippy::type_complexity)] fn init_light_client(&self) -> Result<(Box>, Option), Error> { - unimplemented!() + let light_client = MockLightClient::new(); + + Ok((Box::new(light_client), None)) } fn init_event_monitor( &self, rt: Arc>, - ) -> Result<(Option, Receiver), Error> { - unimplemented!() + ) -> Result<(Option, channel::Receiver), Error> { + let (_, rx) = channel::unbounded(); + Ok((None, rx)) } fn id(&self) -> &ChainId { - unimplemented!() + &self.config.id } fn keybase(&self) -> &KeyRing { @@ -146,5 +144,29 @@ impl Chain for MockChain { } } -// Integration tests with the modules -mod test {} +// For integration tests with the modules +#[cfg(test)] +pub mod test_utils { + use std::str::FromStr; + + use ibc::ics24_host::identifier::ChainId; + + use crate::config::ChainConfig; + + pub fn get_basic_chain_config(id: &str) -> ChainConfig { + ChainConfig { + id: ChainId::from_str(id).unwrap(), + rpc_addr: "35.192.61.41:26656".parse().unwrap(), + grpc_addr: "".to_string(), + account_prefix: "".to_string(), + key_name: "".to_string(), + store_prefix: "".to_string(), + client_ids: vec![], + gas: 0, + clock_drift: Default::default(), + trusting_period: Default::default(), + trust_threshold: Default::default(), + peers: None, + } + } +} diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index 4dcd29c53d..098ad785cd 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -42,9 +42,9 @@ use super::{ }; pub struct Threads { - pub light_client: thread::JoinHandle<()>, + pub light_client: Option>, pub chain_runtime: thread::JoinHandle<()>, - pub event_monitor: thread::JoinHandle<()>, + pub event_monitor: Option>, } pub struct ChainRuntime { @@ -88,10 +88,6 @@ impl ChainRuntime { None => None, }; - // TODO: figure out how to deal with a missing light client thread & event monitor - assert!(light_client_thread.is_some()); - assert!(event_monitor_thread.is_some()); - // Instantiate the runtime let chain_runtime = Self::instantiate(chain, light_client_handler, event_receiver, rt); @@ -102,9 +98,9 @@ impl ChainRuntime { let runtime_thread = thread::spawn(move || chain_runtime.run().unwrap()); let threads = Threads { - light_client: light_client_thread.unwrap(), + light_client: light_client_thread, chain_runtime: runtime_thread, - event_monitor: event_monitor_thread.unwrap(), + event_monitor: event_monitor_thread, }; Ok((handle, threads)) diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 726577a070..5609348ea5 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -211,3 +211,30 @@ pub fn build_update_client_and_send( Ok(dst_chain.send_tx(new_msgs)?) } + +#[cfg(test)] +mod test { + use std::str::FromStr; + + use ibc::ics24_host::identifier::ClientId; + + use crate::chain::mock::test_utils::get_basic_chain_config; + use crate::chain::mock::MockChain; + use crate::chain::runtime::ChainRuntime; + use crate::foreign_client::{build_create_client_and_send, ForeignClientConfig}; + + #[test] + #[ignore] // implementation is WIP + fn test_build_create_client_and_send() { + let client_id = ClientId::from_str("client_on_a_forb").unwrap(); + let a_cfg = get_basic_chain_config("chain_a"); + let b_cfg = get_basic_chain_config("chain_b"); + let opts = ForeignClientConfig::new(&a_cfg.id, &client_id); + + let (a_chain, _) = ChainRuntime::::spawn(a_cfg).unwrap(); + let (b_chain, _) = ChainRuntime::::spawn(b_cfg).unwrap(); + + let res = build_create_client_and_send(a_chain, b_chain, &opts); + assert!(res.is_err()) + } +} diff --git a/relayer/src/light_client.rs b/relayer/src/light_client.rs index 7d28f45b43..c9e3bc6d10 100644 --- a/relayer/src/light_client.rs +++ b/relayer/src/light_client.rs @@ -3,6 +3,9 @@ use crate::error; pub mod tendermint; +#[cfg(test)] +pub mod mock; + /// Defines a light block from the point of view of the relayer. pub trait LightBlock: Send + Sync { fn signed_header(&self) -> &C::Header; diff --git a/relayer/src/light_client/mock.rs b/relayer/src/light_client/mock.rs new file mode 100644 index 0000000000..1eff250470 --- /dev/null +++ b/relayer/src/light_client/mock.rs @@ -0,0 +1,42 @@ +use crate::chain::mock::MockChain; +use crate::chain::Chain; +use crate::error::Error; +use ibc::Height; + +/// A light client serving a mock chain. +pub struct LightClient {} + +#[allow(unused_variables)] +impl super::LightClient for LightClient { + fn latest_trusted(&self) -> Result::LightBlock>, Error> { + unimplemented!() + } + + fn verify_to_latest(&self) -> Result<::LightBlock, Error> { + unimplemented!() + } + + fn verify_to_target(&self, height: Height) -> Result<::LightBlock, Error> { + unimplemented!() + } + + fn get_minimal_set( + &self, + latest_client_state_height: Height, + target_height: Height, + ) -> Result, Error> { + unimplemented!() + } +} + +impl LightClient { + pub fn new() -> LightClient { + LightClient {} + } +} + +impl Default for LightClient { + fn default() -> Self { + Self::new() + } +} From df7be9ece96e5c1a2ffb5219ec2bb0648565012c Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 30 Nov 2020 10:45:58 +0100 Subject: [PATCH 08/19] Almost ready to send_tx (mock has to be mutable). --- modules/src/ics26_routing/handler.rs | 12 +++--- modules/src/lib.rs | 2 +- relayer/src/chain/mock.rs | 59 ++++++++++++++++++++++++---- relayer/src/foreign_client.rs | 8 +++- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index 34afc07bd5..77c4fec392 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -1,3 +1,5 @@ +use ibc_proto::cosmos::tx::v1beta1::TxRaw; + use crate::handler::HandlerOutput; use crate::ics02_client::handler::dispatch as ics2_msg_dispatcher; use crate::ics03_connection::handler::dispatch as ics3_msg_dispatcher; @@ -5,13 +7,11 @@ use crate::ics26_routing::context::ICS26Context; use crate::ics26_routing::error::{Error, Kind}; use crate::ics26_routing::msgs::ICS26Envelope; use crate::ics26_routing::msgs::ICS26Envelope::{ICS2Msg, ICS3Msg}; -use ibc_proto::cosmos::tx::v1beta1::Tx; -// TODO: Implement this (the tx type is probably wrong also). Rough sketch: -// 1. deserialize & validate each message in the tx -// 2. invoke dispatch(ctx, ms) -// 3. if all message in the tx pass through correctly, then apply the side-effects to the context -pub fn deliver_tx(_ctx: &mut Ctx, _tx: Tx) -> Result<(), Error> +/// Mimics the DeliverTx ABCI interface. +/// https://github.com/cosmos/cosmos-sdk/tree/master/docs/basics +#[allow(unused_variables)] +pub fn deliver_tx(ctx: &mut Ctx, tx: TxRaw) -> Result<(), Error> where Ctx: ICS26Context, { diff --git a/modules/src/lib.rs b/modules/src/lib.rs index 47315e384e..9d219cabe3 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -45,7 +45,7 @@ pub type Height = crate::ics02_client::height::Height; mod test; #[cfg(any(test, feature = "mocks"))] -mod test_utils; +pub mod test_utils; #[cfg(any(test, feature = "mocks"))] pub mod mock; // Context mock, the underlying host chain, and client types: for testing all handlers. diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 6520ff7f5a..0b707a3883 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -1,4 +1,6 @@ +use std::ops::Add; use std::sync::{Arc, Mutex}; +use std::time::Duration; use crossbeam_channel as channel; use prost_types::Any; @@ -7,6 +9,8 @@ use tendermint_light_client::supervisor::Supervisor; use tendermint_testgen::light_block::TMLightBlock; use tokio::runtime::Runtime; +use ibc::downcast; +use ibc::ics02_client::client_def::AnyClientState; use ibc::ics07_tendermint::client_state::ClientState as TendermintClientState; use ibc::ics07_tendermint::consensus_state::ConsensusState as TendermintConsensusState; use ibc::ics07_tendermint::header::Header as TendermintHeader; @@ -17,14 +21,16 @@ use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc::ics24_host::Path; use ibc::mock::context::MockContext; use ibc::mock::host::HostType; +use ibc::test_utils::{default_consensus_params, get_dummy_account_id}; use ibc::Height; use crate::chain::{Chain, QueryResponse}; use crate::config::ChainConfig; -use crate::error::Error; +use crate::error::{Error, Kind}; use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing}; use crate::light_client::{mock::LightClient as MockLightClient, LightClient}; +use ibc_proto::cosmos::tx::v1beta1::{TxBody, TxRaw}; /// The representation of a mocked chain as the relayer sees it. /// The relayer runtime and the light client will engage with the MockChain to query/send tx. @@ -80,11 +86,28 @@ impl Chain for MockChain { } fn send_tx(&self, proto_msgs: Vec) -> Result { + let body = TxBody { + messages: proto_msgs.to_vec(), + memo: "".to_string(), + timeout_height: 0, + extension_options: Vec::::new(), + non_critical_extension_options: Vec::::new(), + }; + let mut body_buf = Vec::new(); + prost::Message::encode(&body, &mut body_buf).unwrap(); + + let tx_raw = TxRaw { + body_bytes: body_buf, + auth_info_bytes: vec![], + signatures: vec![], + }; + + // Call into ICS26 `deliver_tx`. unimplemented!() } fn get_signer(&mut self) -> Result { - unimplemented!() + Ok(get_dummy_account_id()) } fn get_key(&mut self) -> Result { @@ -92,14 +115,29 @@ impl Chain for MockChain { } fn build_client_state(&self, height: Height) -> Result { - unimplemented!() + let client_state = Self::ClientState::new( + self.id().to_string(), + self.config.trust_threshold, + self.config.trusting_period, + self.config.trusting_period.add(Duration::from_secs(1000)), + Duration::from_millis(3000), + height, + Height::zero(), + default_consensus_params(), + "upgrade/upgradedClient".to_string(), + false, + false, + ) + .map_err(|e| Kind::BuildClientStateFailure.context(e))?; + + Ok(client_state) } fn build_consensus_state( &self, light_block: Self::LightBlock, ) -> Result { - unimplemented!() + Ok(Self::ConsensusState::from(light_block.signed_header.header)) } fn build_header( @@ -119,7 +157,13 @@ impl Chain for MockChain { client_id: &ClientId, height: Height, ) -> Result { - unimplemented!() + let any_state = self + .context + .query_client_full_state(client_id) + .ok_or(Kind::EmptyResponseValue)?; + let client_state = downcast!(any_state => AnyClientState::Tendermint) + .ok_or_else(|| Kind::Query.context("unexpected client state type"))?; + Ok(client_state) } fn query_commitment_prefix(&self) -> Result { @@ -148,6 +192,7 @@ impl Chain for MockChain { #[cfg(test)] pub mod test_utils { use std::str::FromStr; + use std::time::Duration; use ibc::ics24_host::identifier::ChainId; @@ -163,8 +208,8 @@ pub mod test_utils { store_prefix: "".to_string(), client_ids: vec![], gas: 0, - clock_drift: Default::default(), - trusting_period: Default::default(), + clock_drift: Duration::from_secs(5), + trusting_period: Duration::from_secs(14 * 24 * 60 * 60), // 14 days trust_threshold: Default::default(), peers: None, } diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 5609348ea5..8ab8f6aba9 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -224,7 +224,7 @@ mod test { use crate::foreign_client::{build_create_client_and_send, ForeignClientConfig}; #[test] - #[ignore] // implementation is WIP + #[ignore] // WIP fn test_build_create_client_and_send() { let client_id = ClientId::from_str("client_on_a_forb").unwrap(); let a_cfg = get_basic_chain_config("chain_a"); @@ -235,6 +235,10 @@ mod test { let (b_chain, _) = ChainRuntime::::spawn(b_cfg).unwrap(); let res = build_create_client_and_send(a_chain, b_chain, &opts); - assert!(res.is_err()) + assert!( + res.is_ok(), + "build_create_client_and_send failed with error {:?}", + res + ); } } From b7e83d29959ace2d3d802a21830d46ad866434fe Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 30 Nov 2020 20:39:05 +0100 Subject: [PATCH 09/19] Added chain executor. --- relayer/src/chain/cosmos.rs | 146 +++++++++++++++++------------------ relayer/src/chain/mock.rs | 52 ++++++++++--- relayer/src/chain/runtime.rs | 2 +- 3 files changed, 114 insertions(+), 86 deletions(-) diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index 469b8d1214..5ead52697c 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -138,8 +138,8 @@ impl CosmosSDKChain { } impl Chain for CosmosSDKChain { - type Header = TMHeader; type LightBlock = TMLightBlock; + type Header = TMHeader; type ConsensusState = ConsensusState; type ClientState = ClientState; @@ -170,6 +170,10 @@ impl Chain for CosmosSDKChain { &self.config().id } + fn keybase(&self) -> &KeyRing { + &self.keybase + } + fn query(&self, data: Path, height: ICSHeight, prove: bool) -> Result { let path = TendermintABCIPath::from_str(IBC_QUERY_PATH).unwrap(); @@ -285,6 +289,74 @@ impl Chain for CosmosSDKChain { Ok(response) } + /// Get the account for the signer + fn get_signer(&mut self) -> Result { + // Get the key from key seed file + let key = self + .keybase() + .get_key() + .map_err(|e| Kind::KeyBase.context(e))?; + + let signer: AccountId = + AccountId::from_str(&key.address.to_hex()).map_err(|e| Kind::KeyBase.context(e))?; + + Ok(signer) + } + + /// Get the signing key + fn get_key(&mut self) -> Result { + // Get the key from key seed file + let key = self + .keybase() + .get_key() + .map_err(|e| Kind::KeyBase.context(e))?; + + Ok(key) + } + + fn build_client_state(&self, height: ICSHeight) -> Result { + // Build the client state. + let client_state = ibc::ics07_tendermint::client_state::ClientState::new( + self.id().to_string(), + self.config.trust_threshold, + self.config.trusting_period, + self.unbonding_period()?, + Duration::from_millis(3000), // TODO - get it from src config when avail + height, + ICSHeight::zero(), + self.query_consensus_params()?, + "upgrade/upgradedClient".to_string(), + false, + false, + ) + .map_err(|e| Kind::BuildClientStateFailure.context(e))?; + + Ok(client_state) + } + + fn build_consensus_state( + &self, + light_block: Self::LightBlock, + ) -> Result { + Ok(TMConsensusState::from(light_block.signed_header.header)) + } + + fn build_header( + &self, + trusted_light_block: Self::LightBlock, + target_light_block: Self::LightBlock, + ) -> Result { + let trusted_height = + ICSHeight::new(self.id().version(), trusted_light_block.height().into()); + + Ok(TMHeader { + trusted_height, + signed_header: target_light_block.signed_header.clone(), + validator_set: fix_validator_set(&target_light_block)?, + trusted_validator_set: fix_validator_set(&trusted_light_block)?, + }) + } + /// Query the latest height the chain is at via a RPC query fn query_latest_height(&self) -> Result { let status = self @@ -373,78 +445,6 @@ impl Chain for CosmosSDKChain { Ok((consensus_state, res.proof)) } - - fn build_client_state(&self, height: ICSHeight) -> Result { - // Build the client state. - let client_state = ibc::ics07_tendermint::client_state::ClientState::new( - self.id().to_string(), - self.config.trust_threshold, - self.config.trusting_period, - self.unbonding_period()?, - Duration::from_millis(3000), // TODO - get it from src config when avail - height, - ICSHeight::zero(), - self.query_consensus_params()?, - "upgrade/upgradedClient".to_string(), - false, - false, - ) - .map_err(|e| Kind::BuildClientStateFailure.context(e))?; - - Ok(client_state) - } - - fn build_consensus_state( - &self, - light_block: Self::LightBlock, - ) -> Result { - Ok(TMConsensusState::from(light_block.signed_header.header)) - } - - fn build_header( - &self, - trusted_light_block: Self::LightBlock, - target_light_block: Self::LightBlock, - ) -> Result { - let trusted_height = - ICSHeight::new(self.id().version(), trusted_light_block.height().into()); - - Ok(TMHeader { - trusted_height, - signed_header: target_light_block.signed_header.clone(), - validator_set: fix_validator_set(&target_light_block)?, - trusted_validator_set: fix_validator_set(&trusted_light_block)?, - }) - } - - fn keybase(&self) -> &KeyRing { - &self.keybase - } - - /// Get the account for the signer - fn get_signer(&mut self) -> Result { - // Get the key from key seed file - let key = self - .keybase() - .get_key() - .map_err(|e| Kind::KeyBase.context(e))?; - - let signer: AccountId = - AccountId::from_str(&key.address.to_hex()).map_err(|e| Kind::KeyBase.context(e))?; - - Ok(signer) - } - - /// Get the signing key - fn get_key(&mut self) -> Result { - // Get the key from key seed file - let key = self - .keybase() - .get_key() - .map_err(|e| Kind::KeyBase.context(e))?; - - Ok(key) - } } fn fix_validator_set(light_block: &TMLightBlock) -> Result { diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 0b707a3883..deefe6f9aa 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports, dead_code)] + use std::ops::Add; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -31,12 +33,24 @@ use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing}; use crate::light_client::{mock::LightClient as MockLightClient, LightClient}; use ibc_proto::cosmos::tx::v1beta1::{TxBody, TxRaw}; +use std::thread; /// The representation of a mocked chain as the relayer sees it. /// The relayer runtime and the light client will engage with the MockChain to query/send tx. pub struct MockChain { - pub context: MockContext, - pub config: ChainConfig, + config: ChainConfig, + to_executor: channel::Sender, +} + +/// The executor of the mock chain: simulates a chain that produces blocks regularly and +/// consume IBC transactions that the relayer sends. +pub struct MockChainExecutor { + context: MockContext, + from_relayer: channel::Receiver, +} + +impl MockChainExecutor { + pub fn run(&mut self) {} } #[allow(unused_variables)] @@ -47,15 +61,26 @@ impl Chain for MockChain { type ClientState = TendermintClientState; fn bootstrap(config: ChainConfig, _rt: Arc>) -> Result { - Ok(MockChain { + let (sender, reciver) = channel::unbounded(); + + let chain = MockChain { config: config.clone(), + to_executor: sender, + }; + + let mut chain_exec = MockChainExecutor { + from_relayer: reciver, context: MockContext::new( config.id.clone(), HostType::SyntheticTendermint, 50, Height::new(config.id.version(), 20), ), - }) + }; + + thread::spawn(move || chain_exec.run()); + + Ok(chain) } #[allow(clippy::type_complexity)] @@ -149,7 +174,8 @@ impl Chain for MockChain { } fn query_latest_height(&self) -> Result { - Ok(self.context.query_latest_height()) + // Ok(self.context.query_latest_height()) + unimplemented!() } fn query_client_state( @@ -157,13 +183,14 @@ impl Chain for MockChain { client_id: &ClientId, height: Height, ) -> Result { - let any_state = self - .context - .query_client_full_state(client_id) - .ok_or(Kind::EmptyResponseValue)?; - let client_state = downcast!(any_state => AnyClientState::Tendermint) - .ok_or_else(|| Kind::Query.context("unexpected client state type"))?; - Ok(client_state) + // let any_state = self + // .context + // .query_client_full_state(client_id) + // .ok_or(Kind::EmptyResponseValue)?; + // let client_state = downcast!(any_state => AnyClientState::Tendermint) + // .ok_or_else(|| Kind::Query.context("unexpected client state type"))?; + // Ok(client_state) + unimplemented!() } fn query_commitment_prefix(&self) -> Result { @@ -198,6 +225,7 @@ pub mod test_utils { use crate::config::ChainConfig; + /// Returns a very minimal chain configuration, to be used in initializing `MockChain`s. pub fn get_basic_chain_config(id: &str) -> ChainConfig { ChainConfig { id: ChainId::from_str(id).unwrap(), diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index 098ad785cd..b9be45b631 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -66,7 +66,7 @@ impl ChainRuntime { TokioRuntime::new().map_err(|e| Kind::Io.context(e))?, )); - // Similar to `from_config`... TODO + // Similar to `from_config`. let chain = C::bootstrap(config, rt.clone())?; let (light_client_handler, supervisor_option) = chain.init_light_client()?; From 879daf2954be0b03f983a74b82a8c06117660528 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 1 Dec 2020 15:22:20 +0100 Subject: [PATCH 10/19] Mutable chain in send_tx, simplified spawn.After review w/ Anca & Romain --- relayer/src/chain.rs | 22 ++++++++---- relayer/src/chain/cosmos.rs | 39 +++++++++++++++------- relayer/src/chain/mock.rs | 65 +++++++++++++----------------------- relayer/src/chain/runtime.rs | 50 ++++++++++++++------------- 4 files changed, 92 insertions(+), 84 deletions(-) diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index fd48b05319..beb908931e 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -8,7 +8,10 @@ pub mod runtime; pub mod mock; use crossbeam_channel as channel; -use std::sync::{Arc, Mutex}; +use std::{ + sync::{Arc, Mutex}, + thread, +}; use tokio::runtime::Runtime as TokioRuntime; use prost_types::Any; @@ -18,7 +21,6 @@ use tendermint_proto::Protobuf; // TODO - tendermint deps should not be here use tendermint::account::Id as AccountId; use tendermint::block::Height; -use tendermint_light_client::supervisor::Supervisor; use ibc::ics02_client::header::Header; @@ -39,7 +41,7 @@ use ibc::Height as ICSHeight; use crate::config::ChainConfig; use crate::connection::ConnectionMsgType; use crate::error::{Error, Kind}; -use crate::event::monitor::{EventBatch, EventMonitor}; +use crate::event::monitor::EventBatch; use crate::keyring::store::{KeyEntry, KeyRing}; use crate::light_client::LightClient; @@ -71,14 +73,22 @@ pub trait Chain: Sized { #[allow(clippy::type_complexity)] /// Returns the light client (if any) associated with this chain. /// This is primarily a helper method to be used by the runtime. - fn init_light_client(&self) -> Result<(Box>, Option), Error>; + fn init_light_client( + &self, + ) -> Result<(Box>, Option>), Error>; /// Returns the event monitor (if any) associated with this chain. /// This is primarily a helper method to be used by the runtime. fn init_event_monitor( &self, rt: Arc>, - ) -> Result<(Option, channel::Receiver), Error>; + ) -> Result< + ( + channel::Receiver, + Option>, + ), + Error, + >; /// Returns the chain's identifier fn id(&self) -> &ChainId; @@ -90,7 +100,7 @@ pub trait Chain: Sized { fn query(&self, data: Path, height: ICSHeight, prove: bool) -> Result; /// Send a transaction with `msgs` to chain. - fn send_tx(&self, proto_msgs: Vec) -> Result; + fn send_tx(&mut self, proto_msgs: Vec) -> Result; fn get_signer(&mut self) -> Result; diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index 5ead52697c..93b1fc91c8 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -1,8 +1,11 @@ -use std::convert::{TryFrom, TryInto}; -use std::future::Future; -use std::str::FromStr; -use std::sync::{Arc, Mutex}; -use std::time::Duration; +use std::{ + convert::{TryFrom, TryInto}, + future::Future, + str::FromStr, + sync::{Arc, Mutex}, + thread, + time::Duration, +}; use anomaly::fail; use bitcoin::hashes::hex::ToHex; @@ -20,7 +23,6 @@ use tendermint::account::Id as AccountId; use tendermint::block::Height; use tendermint::consensus::Params; -use tendermint_light_client::supervisor::Supervisor; use tendermint_light_client::types::{LightBlock as TMLightBlock, ValidatorSet}; use tendermint_rpc::Client; use tendermint_rpc::HttpClient; @@ -150,20 +152,33 @@ impl Chain for CosmosSDKChain { // TODO use a simpler approach to create the light client #[allow(clippy::type_complexity)] - fn init_light_client(&self) -> Result<(Box>, Option), Error> { + fn init_light_client( + &self, + ) -> Result<(Box>, Option>), Error> { let (lc, supervisor) = TMLightClient::from_config(&self.config, true)?; - Ok((Box::new(lc), Some(supervisor))) + let supervisor_thread = thread::spawn(move || supervisor.run().unwrap()); + + Ok((Box::new(lc), Some(supervisor_thread))) } fn init_event_monitor( &self, rt: Arc>, - ) -> Result<(Option, channel::Receiver), Error> { - let (event_monitor, event_receiver) = + ) -> Result< + ( + channel::Receiver, + Option>, + ), + Error, + > { + let (mut event_monitor, event_receiver) = EventMonitor::new(self.config.id.clone(), self.config.rpc_addr.clone(), rt)?; - Ok((Some(event_monitor), event_receiver)) + event_monitor.subscribe().unwrap(); + let monitor_thread = thread::spawn(move || event_monitor.run()); + + Ok((event_receiver, Some(monitor_thread))) } fn id(&self) -> &ChainId { @@ -197,7 +212,7 @@ impl Chain for CosmosSDKChain { /// Send a transaction that includes the specified messages /// TODO - split the messages in multiple Tx-es such that they don't exceed some max size - fn send_tx(&self, proto_msgs: Vec) -> Result { + fn send_tx(&mut self, proto_msgs: Vec) -> Result { let key = self .keybase() .get_key() diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index deefe6f9aa..d5b90195a2 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -1,5 +1,3 @@ -#![allow(unused_imports, dead_code)] - use std::ops::Add; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -7,7 +5,6 @@ use std::time::Duration; use crossbeam_channel as channel; use prost_types::Any; use tendermint::account::Id; -use tendermint_light_client::supervisor::Supervisor; use tendermint_testgen::light_block::TMLightBlock; use tokio::runtime::Runtime; @@ -29,7 +26,7 @@ use ibc::Height; use crate::chain::{Chain, QueryResponse}; use crate::config::ChainConfig; use crate::error::{Error, Kind}; -use crate::event::monitor::{EventBatch, EventMonitor}; +use crate::event::monitor::EventBatch; use crate::keyring::store::{KeyEntry, KeyRing}; use crate::light_client::{mock::LightClient as MockLightClient, LightClient}; use ibc_proto::cosmos::tx::v1beta1::{TxBody, TxRaw}; @@ -39,18 +36,7 @@ use std::thread; /// The relayer runtime and the light client will engage with the MockChain to query/send tx. pub struct MockChain { config: ChainConfig, - to_executor: channel::Sender, -} - -/// The executor of the mock chain: simulates a chain that produces blocks regularly and -/// consume IBC transactions that the relayer sends. -pub struct MockChainExecutor { context: MockContext, - from_relayer: channel::Receiver, -} - -impl MockChainExecutor { - pub fn run(&mut self) {} } #[allow(unused_variables)] @@ -61,30 +47,21 @@ impl Chain for MockChain { type ClientState = TendermintClientState; fn bootstrap(config: ChainConfig, _rt: Arc>) -> Result { - let (sender, reciver) = channel::unbounded(); - - let chain = MockChain { + Ok(MockChain { config: config.clone(), - to_executor: sender, - }; - - let mut chain_exec = MockChainExecutor { - from_relayer: reciver, context: MockContext::new( config.id.clone(), HostType::SyntheticTendermint, 50, Height::new(config.id.version(), 20), ), - }; - - thread::spawn(move || chain_exec.run()); - - Ok(chain) + }) } #[allow(clippy::type_complexity)] - fn init_light_client(&self) -> Result<(Box>, Option), Error> { + fn init_light_client( + &self, + ) -> Result<(Box>, Option>), Error> { let light_client = MockLightClient::new(); Ok((Box::new(light_client), None)) @@ -93,9 +70,15 @@ impl Chain for MockChain { fn init_event_monitor( &self, rt: Arc>, - ) -> Result<(Option, channel::Receiver), Error> { + ) -> Result< + ( + channel::Receiver, + Option>, + ), + Error, + > { let (_, rx) = channel::unbounded(); - Ok((None, rx)) + Ok((rx, None)) } fn id(&self) -> &ChainId { @@ -110,7 +93,7 @@ impl Chain for MockChain { unimplemented!() } - fn send_tx(&self, proto_msgs: Vec) -> Result { + fn send_tx(&mut self, proto_msgs: Vec) -> Result { let body = TxBody { messages: proto_msgs.to_vec(), memo: "".to_string(), @@ -174,8 +157,7 @@ impl Chain for MockChain { } fn query_latest_height(&self) -> Result { - // Ok(self.context.query_latest_height()) - unimplemented!() + Ok(self.context.query_latest_height()) } fn query_client_state( @@ -183,14 +165,13 @@ impl Chain for MockChain { client_id: &ClientId, height: Height, ) -> Result { - // let any_state = self - // .context - // .query_client_full_state(client_id) - // .ok_or(Kind::EmptyResponseValue)?; - // let client_state = downcast!(any_state => AnyClientState::Tendermint) - // .ok_or_else(|| Kind::Query.context("unexpected client state type"))?; - // Ok(client_state) - unimplemented!() + let any_state = self + .context + .query_client_full_state(client_id) + .ok_or(Kind::EmptyResponseValue)?; + let client_state = downcast!(any_state => AnyClientState::Tendermint) + .ok_or_else(|| Kind::Query.context("unexpected client state type"))?; + Ok(client_state) } fn query_commitment_prefix(&self) -> Result { diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index b9be45b631..ef3580d7bc 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -69,33 +69,16 @@ impl ChainRuntime { // Similar to `from_config`. let chain = C::bootstrap(config, rt.clone())?; - let (light_client_handler, supervisor_option) = chain.init_light_client()?; + // Start the light client + let (light_client_handler, light_client_thread) = chain.init_light_client()?; - // If there is a light client supervisor, spawn it. - let light_client_thread = match supervisor_option { - Some(supervisor) => Some(thread::spawn(move || supervisor.run().unwrap())), - None => None, - }; - - let (event_monitor_option, event_receiver) = chain.init_event_monitor(rt.clone())?; - - let event_monitor_thread = match event_monitor_option { - // Spawn the event monitor - Some(mut event_monitor) => { - event_monitor.subscribe().unwrap(); - Some(thread::spawn(move || event_monitor.run())) - } - None => None, - }; + // Start the event monitor + let (event_receiver, event_monitor_thread) = chain.init_event_monitor(rt.clone())?; // Instantiate the runtime - let chain_runtime = Self::instantiate(chain, light_client_handler, event_receiver, rt); - - // Get a handle to the runtime - let handle = chain_runtime.handle(); // Spawn the runtime - let runtime_thread = thread::spawn(move || chain_runtime.run().unwrap()); + let (handle, runtime_thread) = Self::init(chain, light_client_handler, event_receiver, rt); let threads = Threads { light_client: light_client_thread, @@ -106,7 +89,26 @@ impl ChainRuntime { Ok((handle, threads)) } - fn instantiate( + /// Initializes a runtime for a given chain, and spawns the associated thread + fn init( + chain: C, + light_client: Box>, + event_receiver: channel::Receiver, + rt: Arc>, + ) -> (impl ChainHandle, thread::JoinHandle<()>) { + let chain_runtime = Self::new(chain, light_client, event_receiver, rt); + + // Get a handle to the runtime + let handle = chain_runtime.handle(); + + // Spawn the runtime & return + let thread = thread::spawn(move || chain_runtime.run().unwrap()); + + (handle, thread) + } + + /// Basic constructor + fn new( chain: C, light_client: Box>, event_receiver: channel::Receiver, @@ -291,7 +293,7 @@ impl ChainRuntime { } fn send_tx( - &self, + &mut self, proto_msgs: Vec, reply_to: ReplyTo, ) -> Result<(), Error> { From 5093d88d342c71f7d9cb3732744a7e18f232fc1f Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 1 Dec 2020 15:37:51 +0100 Subject: [PATCH 11/19] impl CosmosSDKChain cleanup --- relayer-cli/src/commands/query/channel.rs | 2 +- relayer-cli/src/commands/query/client.rs | 6 ++-- relayer-cli/src/commands/query/connection.rs | 2 +- relayer-cli/tests/integration.rs | 2 +- relayer/src/chain.rs | 7 ++--- relayer/src/chain/cosmos.rs | 32 ++++++++------------ relayer/src/chain/runtime.rs | 4 +-- relayer/src/keys/add.rs | 2 +- relayer/src/keys/list.rs | 2 +- 9 files changed, 25 insertions(+), 34 deletions(-) diff --git a/relayer-cli/src/commands/query/channel.rs b/relayer-cli/src/commands/query/channel.rs index 1fb18eaa8e..bf02e5d16b 100644 --- a/relayer-cli/src/commands/query/channel.rs +++ b/relayer-cli/src/commands/query/channel.rs @@ -100,7 +100,7 @@ impl Runnable for QueryChannelEndCmd { let rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); - let chain = CosmosSDKChain::from_config(chain_config, rt).unwrap(); + let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); let height = ibc::Height::new(chain.id().version(), opts.height); let res: Result = chain .query( diff --git a/relayer-cli/src/commands/query/client.rs b/relayer-cli/src/commands/query/client.rs index aa960744b6..0eaff5c1f9 100644 --- a/relayer-cli/src/commands/query/client.rs +++ b/relayer-cli/src/commands/query/client.rs @@ -79,7 +79,7 @@ impl Runnable for QueryClientStateCmd { status_info!("Options", "{:?}", opts); let rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); - let chain = CosmosSDKChain::from_config(chain_config, rt).unwrap(); + let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); let height = ibc::Height::new(chain.id().version(), opts.height); let res: Result = chain @@ -172,7 +172,7 @@ impl Runnable for QueryClientConsensusCmd { status_info!("Options", "{:?}", opts); let rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); - let chain = CosmosSDKChain::from_config(chain_config, rt).unwrap(); + let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); let height = ibc::Height::new(chain.id().version(), opts.height); let res: Result = chain @@ -286,7 +286,7 @@ impl Runnable for QueryClientConnectionsCmd { status_info!("Options", "{:?}", opts); let rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); - let chain = CosmosSDKChain::from_config(chain_config, rt).unwrap(); + let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); let height = ibc::Height::new(chain.id().version(), opts.height); let res: Result = chain diff --git a/relayer-cli/src/commands/query/connection.rs b/relayer-cli/src/commands/query/connection.rs index bd65c844ee..db330286bd 100644 --- a/relayer-cli/src/commands/query/connection.rs +++ b/relayer-cli/src/commands/query/connection.rs @@ -83,7 +83,7 @@ impl Runnable for QueryConnectionEndCmd { status_info!("Options", "{:?}", opts); let rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); - let chain = CosmosSDKChain::from_config(chain_config, rt).unwrap(); + let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); let height = ibc::Height::new(chain.id().version(), opts.height); // TODO - any value in querying with proof from the CLI? diff --git a/relayer-cli/tests/integration.rs b/relayer-cli/tests/integration.rs index 9c35ee4b82..f08c6a50e0 100644 --- a/relayer-cli/tests/integration.rs +++ b/relayer-cli/tests/integration.rs @@ -46,7 +46,7 @@ fn simd_config() -> Config { /// Chain created for the informaldev/simd DockerHub image running on localhost. fn simd_chain() -> CosmosSDKChain { let rt = Arc::new(Mutex::new(tokio::runtime::Runtime::new().unwrap())); - CosmosSDKChain::from_config(simd_config().chains[0].clone(), rt).unwrap() + CosmosSDKChain::bootstrap(simd_config().chains[0].clone(), rt).unwrap() } /// Query connection ID. Requires the informaldev/simd Docker image running on localhost. diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index beb908931e..c204b7f51c 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -68,17 +68,16 @@ pub trait Chain: Sized { /// Type of the client state for this chain type ClientState: ClientState; + /// Constructs the chain fn bootstrap(config: ChainConfig, rt: Arc>) -> Result; #[allow(clippy::type_complexity)] - /// Returns the light client (if any) associated with this chain. - /// This is primarily a helper method to be used by the runtime. + /// Initializes and returns the light client (if any) associated with this chain. fn init_light_client( &self, ) -> Result<(Box>, Option>), Error>; - /// Returns the event monitor (if any) associated with this chain. - /// This is primarily a helper method to be used by the runtime. + /// Initializes and returns the event monitor (if any) associated with this chain. fn init_event_monitor( &self, rt: Arc>, diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index 93b1fc91c8..3dc20e294b 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -70,23 +70,6 @@ pub struct CosmosSDKChain { } impl CosmosSDKChain { - // TODO: This will be deprecated in favor of `Chain::bootstrap` method (see below) - pub fn from_config(config: ChainConfig, rt: Arc>) -> Result { - let rpc_client = - HttpClient::new(config.rpc_addr.clone()).map_err(|e| Kind::Rpc.context(e))?; - - // Initialize key store and load key - let key_store = KeyRing::init(StoreBackend::Test, config.clone()) - .map_err(|e| Kind::KeyBase.context(e))?; - - Ok(Self { - rt, - config, - keybase: key_store, - rpc_client, - }) - } - /// The unbonding period of this chain pub fn unbonding_period(&self) -> Result { // TODO - generalize this @@ -145,9 +128,20 @@ impl Chain for CosmosSDKChain { type ConsensusState = ConsensusState; type ClientState = ClientState; - // TODO remove from_config or rename this method to from_config fn bootstrap(config: ChainConfig, rt: Arc>) -> Result { - Self::from_config(config, rt) + let rpc_client = + HttpClient::new(config.rpc_addr.clone()).map_err(|e| Kind::Rpc.context(e))?; + + // Initialize key store and load key + let key_store = KeyRing::init(StoreBackend::Test, config.clone()) + .map_err(|e| Kind::KeyBase.context(e))?; + + Ok(Self { + rt, + config, + keybase: key_store, + rpc_client, + }) } // TODO use a simpler approach to create the light client diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index ef3580d7bc..84533e36b2 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -75,9 +75,7 @@ impl ChainRuntime { // Start the event monitor let (event_receiver, event_monitor_thread) = chain.init_event_monitor(rt.clone())?; - // Instantiate the runtime - - // Spawn the runtime + // Instantiate the & spawn the runtime let (handle, runtime_thread) = Self::init(chain, light_client_handler, event_receiver, rt); let threads = Threads { diff --git a/relayer/src/keys/add.rs b/relayer/src/keys/add.rs index b3a77cc060..4fdcbd0bf2 100644 --- a/relayer/src/keys/add.rs +++ b/relayer/src/keys/add.rs @@ -20,7 +20,7 @@ pub fn add_key(opts: KeysAddOptions) -> Result { let rt = TokioRuntime::new().unwrap(); // Get the destination chain - let chain = CosmosSDKChain::from_config(opts.clone().chain_config, Arc::new(Mutex::new(rt)))?; + let chain = CosmosSDKChain::bootstrap(opts.clone().chain_config, Arc::new(Mutex::new(rt)))?; let key_contents = fs::read_to_string(&opts.file) .map_err(|_| Kind::KeyBase.context("error reading the key file"))?; diff --git a/relayer/src/keys/list.rs b/relayer/src/keys/list.rs index 7a4da84b89..9ac78835c2 100644 --- a/relayer/src/keys/list.rs +++ b/relayer/src/keys/list.rs @@ -16,7 +16,7 @@ pub fn list_keys(opts: KeysListOptions) -> Result { let rt = TokioRuntime::new().unwrap(); // Get the destination chain - let chain = CosmosSDKChain::from_config(opts.chain_config, Arc::new(Mutex::new(rt)))?; + let chain = CosmosSDKChain::bootstrap(opts.chain_config, Arc::new(Mutex::new(rt)))?; let key_entry = chain.keybase().get_key(); From a1f6368f097a108afc8da19387ef8d5e79059a10 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 1 Dec 2020 18:29:45 +0100 Subject: [PATCH 12/19] Adapted mock context, ICS18 & 26 to correct abstractions. --- modules/src/ics18_relayer/context.rs | 13 +++++++------ modules/src/ics18_relayer/utils.rs | 7 ++++--- modules/src/ics26_routing/context.rs | 3 ++- modules/src/ics26_routing/handler.rs | 7 ++++--- modules/src/mock/context.rs | 26 ++++++++++++++------------ relayer/src/chain/mock.rs | 27 +++++++-------------------- 6 files changed, 38 insertions(+), 45 deletions(-) diff --git a/modules/src/ics18_relayer/context.rs b/modules/src/ics18_relayer/context.rs index dfa7751c7e..3cfff8fcf0 100644 --- a/modules/src/ics18_relayer/context.rs +++ b/modules/src/ics18_relayer/context.rs @@ -1,15 +1,16 @@ +use prost_types::Any; +use tendermint::account::Id as AccountId; + use crate::ics02_client::client_def::{AnyClientState, AnyHeader}; use crate::ics18_relayer::error::Error; use crate::ics24_host::identifier::ClientId; -use crate::ics26_routing::msgs::ICS26Envelope; use crate::Height; -use tendermint::account::Id as AccountId; - /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to /// relay packets between chains. This trait comprises the dependencies towards a single chain. /// Most of the functions in this represent wrappers over the ABCI interface. -/// TODO -- eventually this trait should mirror the `Chain` trait. +/// This trait mimics the `Chain` trait, but at a lower level of abstraction (no networking, header +/// types, light client, RPC client, etc.) pub trait ICS18Context { /// Returns the latest height of the chain. fn query_latest_height(&self) -> Height; @@ -22,8 +23,8 @@ pub trait ICS18Context { fn query_latest_header(&self) -> Option; /// Interface that the relayer uses to submit a datagram to this chain. - /// Wraps around the `/broadcast_tx_async` ABCI endpoint. - fn send(&mut self, msg: ICS26Envelope) -> Result<(), Error>; + /// One can think of this as wrapping around the `/broadcast_tx_commit` ABCI endpoint. + fn send(&mut self, msgs: Vec) -> Result<(), Error>; /// Temporary solution. Similar to `CosmosSDKChain::key_and_signer()` but simpler. fn signer(&self) -> AccountId; diff --git a/modules/src/ics18_relayer/utils.rs b/modules/src/ics18_relayer/utils.rs index c1d918f861..7e673b8a4e 100644 --- a/modules/src/ics18_relayer/utils.rs +++ b/modules/src/ics18_relayer/utils.rs @@ -128,8 +128,9 @@ mod tests { ); let client_msg_b = client_msg_b_res.unwrap(); - // - send the message to B - let dispatch_res_b = ctx_b.send(ICS26Envelope::ICS2Msg(client_msg_b)); + // - send the message to B. We bypass ICS18 interface and call directly into + // MockContext `recv` method (to avoid additional serialization steps). + let dispatch_res_b = ctx_b.recv(ICS26Envelope::ICS2Msg(client_msg_b)); let validation_res = ctx_b.validate(); assert!( validation_res.is_ok(), @@ -173,7 +174,7 @@ mod tests { let client_msg_a = client_msg_a_res.unwrap(); // - send the message to A - let dispatch_res_a = ctx_a.send(ICS26Envelope::ICS2Msg(client_msg_a)); + let dispatch_res_a = ctx_a.recv(ICS26Envelope::ICS2Msg(client_msg_a)); let validation_res = ctx_a.validate(); assert!( validation_res.is_ok(), diff --git a/modules/src/ics26_routing/context.rs b/modules/src/ics26_routing/context.rs index f920d4794c..cc8477122f 100644 --- a/modules/src/ics26_routing/context.rs +++ b/modules/src/ics26_routing/context.rs @@ -2,5 +2,6 @@ use crate::ics02_client::context::{ClientKeeper, ClientReader}; use crate::ics03_connection::context::{ConnectionKeeper, ConnectionReader}; /// This trait captures all the functional dependencies (i.e., context) which the ICS26 module -/// requires to be able to dispatch messages to their corresponding ICS handler. +/// requires to be able to dispatch and process IBC messages. In other words, this is the +/// representation of a chain from the perspective of the IBC module of that chain. pub trait ICS26Context: ClientReader + ClientKeeper + ConnectionReader + ConnectionKeeper {} diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index 77c4fec392..ab1b6a54fe 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -1,4 +1,4 @@ -use ibc_proto::cosmos::tx::v1beta1::TxRaw; +use prost_types::Any; use crate::handler::HandlerOutput; use crate::ics02_client::handler::dispatch as ics2_msg_dispatcher; @@ -8,10 +8,11 @@ use crate::ics26_routing::error::{Error, Kind}; use crate::ics26_routing::msgs::ICS26Envelope; use crate::ics26_routing::msgs::ICS26Envelope::{ICS2Msg, ICS3Msg}; -/// Mimics the DeliverTx ABCI interface. +/// Mimics the DeliverTx ABCI interface, but a slightly lower level. No need for authentication +/// info or signature checks here. /// https://github.com/cosmos/cosmos-sdk/tree/master/docs/basics #[allow(unused_variables)] -pub fn deliver_tx(ctx: &mut Ctx, tx: TxRaw) -> Result<(), Error> +pub fn deliver(ctx: &mut Ctx, messages: Vec) -> Result<(), Error> where Ctx: ICS26Context, { diff --git a/modules/src/mock/context.rs b/modules/src/mock/context.rs index 87391b371c..8f028b8c9a 100644 --- a/modules/src/mock/context.rs +++ b/modules/src/mock/context.rs @@ -1,7 +1,12 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. -// TODO: remove this clippy exception (some code is not covered in `mocks` feature). -#![allow(dead_code)] +use std::cmp::min; +use std::collections::HashMap; +use std::error::Error; +use std::str::FromStr; + +use prost_types::Any; +use tendermint::account::Id; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState, AnyHeader}; use crate::ics02_client::client_type::ClientType; @@ -16,19 +21,13 @@ use crate::ics18_relayer::error::{Error as ICS18Error, Kind as ICS18ErrorKind}; use crate::ics23_commitment::commitment::CommitmentPrefix; use crate::ics24_host::identifier::{ChainId, ClientId, ConnectionId}; use crate::ics26_routing::context::ICS26Context; -use crate::ics26_routing::handler::dispatch; +use crate::ics26_routing::handler::{deliver, dispatch}; use crate::ics26_routing::msgs::ICS26Envelope; use crate::mock::client_state::{MockClientRecord, MockClientState, MockConsensusState}; use crate::mock::header::MockHeader; use crate::mock::host::{HostBlock, HostType}; use crate::Height; -use std::cmp::min; -use std::collections::HashMap; -use std::error::Error; -use std::str::FromStr; -use tendermint::account::Id; - /// A context implementing the dependencies necessary for testing any IBC module. #[derive(Clone, Debug)] pub struct MockContext { @@ -225,8 +224,9 @@ impl MockContext { } /// A datagram passes from the relayer to the IBC module (on host chain). + /// Alternative method to `ICS18Context::send` that does not exercise any serialization. /// Used in testing the ICS18 algorithms, hence this may return a ICS18Error. - fn recv(&mut self, msg: ICS26Envelope) -> Result<(), ICS18Error> { + pub fn recv(&mut self, msg: ICS26Envelope) -> Result<(), ICS18Error> { dispatch(self, msg).map_err(|e| ICS18ErrorKind::TransactionFailed.context(e))?; // Create a new block. self.advance_host_chain_height(); @@ -415,8 +415,10 @@ impl ICS18Context for MockContext { block_ref.cloned().map(Into::into) } - fn send(&mut self, msg: ICS26Envelope) -> Result<(), ICS18Error> { - self.recv(msg) + fn send(&mut self, msgs: Vec) -> Result<(), ICS18Error> { + deliver(self, msgs).map_err(|e| ICS18ErrorKind::TransactionFailed.context(e))?; // Forward call to ICS26 delivery method + self.advance_host_chain_height(); // Advance chain height + Ok(()) } fn signer(&self) -> Id { diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index d5b90195a2..7b2a64612e 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -29,11 +29,11 @@ use crate::error::{Error, Kind}; use crate::event::monitor::EventBatch; use crate::keyring::store::{KeyEntry, KeyRing}; use crate::light_client::{mock::LightClient as MockLightClient, LightClient}; -use ibc_proto::cosmos::tx::v1beta1::{TxBody, TxRaw}; use std::thread; /// The representation of a mocked chain as the relayer sees it. -/// The relayer runtime and the light client will engage with the MockChain to query/send tx. +/// The relayer runtime and the light client will engage with the MockChain to query/send tx; the +/// primary interface for doing so is captured by `ICS18Context` which this struct implements. pub struct MockChain { config: ChainConfig, context: MockContext, @@ -94,24 +94,11 @@ impl Chain for MockChain { } fn send_tx(&mut self, proto_msgs: Vec) -> Result { - let body = TxBody { - messages: proto_msgs.to_vec(), - memo: "".to_string(), - timeout_height: 0, - extension_options: Vec::::new(), - non_critical_extension_options: Vec::::new(), - }; - let mut body_buf = Vec::new(); - prost::Message::encode(&body, &mut body_buf).unwrap(); - - let tx_raw = TxRaw { - body_bytes: body_buf, - auth_info_bytes: vec![], - signatures: vec![], - }; - - // Call into ICS26 `deliver_tx`. - unimplemented!() + // Use the ICS18Context interface to submit the set of messages. + self.context + .send(proto_msgs) + .map(|_| "OK".to_string()) // TODO: establish success return codes. + .map_err(|e| Kind::Rpc.context(e).into()) } fn get_signer(&mut self) -> Result { From 5287e06628c218f01cc5ac9bf5e414d2a8d09631 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 2 Dec 2020 09:51:26 +0100 Subject: [PATCH 13/19] Cleaned up Msg trait, added const TYPE_URL. --- .../src/ics02_client/msgs/create_client.rs | 8 ++---- .../src/ics02_client/msgs/update_client.rs | 14 ++++------ .../ics03_connection/msgs/conn_open_ack.rs | 15 ++++------- .../msgs/conn_open_confirm.rs | 15 ++++------- .../ics03_connection/msgs/conn_open_init.rs | 16 +++++------ .../ics03_connection/msgs/conn_open_try.rs | 15 ++++------- .../src/ics04_channel/msgs/acknowledgement.rs | 7 ----- .../ics04_channel/msgs/chan_close_confirm.rs | 7 ----- .../src/ics04_channel/msgs/chan_close_init.rs | 7 ----- .../src/ics04_channel/msgs/chan_open_ack.rs | 10 +++---- .../ics04_channel/msgs/chan_open_confirm.rs | 9 ++----- .../src/ics04_channel/msgs/chan_open_init.rs | 9 ++----- .../src/ics04_channel/msgs/chan_open_try.rs | 9 ++----- modules/src/ics04_channel/msgs/recv_packet.rs | 7 ----- modules/src/ics04_channel/msgs/timeout.rs | 7 ----- modules/src/ics26_routing/error.rs | 6 +++++ modules/src/ics26_routing/handler.rs | 27 +++++++++++++++++-- modules/src/tx_msg.rs | 3 +-- 18 files changed, 69 insertions(+), 122 deletions(-) diff --git a/modules/src/ics02_client/msgs/create_client.rs b/modules/src/ics02_client/msgs/create_client.rs index 365e30ed73..5051c3dba8 100644 --- a/modules/src/ics02_client/msgs/create_client.rs +++ b/modules/src/ics02_client/msgs/create_client.rs @@ -19,7 +19,7 @@ use crate::ics02_client::error::{Error, Kind}; use crate::ics24_host::identifier::ClientId; use crate::tx_msg::Msg; -const TYPE_MSG_CREATE_CLIENT: &str = "create_client"; +pub const TYPE_URL: &str = "/ibc.core.client.v1.MsgCreateClient"; /// A type of message that triggers the creation of a new on-chain (IBC) client. #[derive(Clone, Debug, PartialEq, Eq)] @@ -69,17 +69,13 @@ impl Msg for MsgCreateAnyClient { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CREATE_CLIENT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate since all fields are validated on creation. Ok(()) } fn type_url(&self) -> String { - "/ibc.core.client.v1.MsgCreateClient".to_string() + TYPE_URL.to_string() } fn get_signers(&self) -> Vec { diff --git a/modules/src/ics02_client/msgs/update_client.rs b/modules/src/ics02_client/msgs/update_client.rs index dc2e043246..f0269b0b31 100644 --- a/modules/src/ics02_client/msgs/update_client.rs +++ b/modules/src/ics02_client/msgs/update_client.rs @@ -17,7 +17,7 @@ use crate::ics02_client::error::{Error, Kind}; use crate::ics24_host::identifier::ClientId; use crate::tx_msg::Msg; -const TYPE_MSG_UPDATE_CLIENT: &str = "update_client"; +pub const TYPE_URL: &str = "/ibc.core.client.v1.MsgUpdateClient"; /// A type of message that triggers the update of an on-chain (IBC) client with new headers. #[derive(Clone, Debug, PartialEq)] // TODO: Add Eq bound when possible @@ -44,21 +44,17 @@ impl Msg for MsgUpdateAnyClient { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_UPDATE_CLIENT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate since all fields are validated on creation. Ok(()) } - fn get_signers(&self) -> Vec { - vec![self.signer] + fn type_url(&self) -> String { + TYPE_URL.to_string() } - fn type_url(&self) -> String { - "/ibc.core.client.v1.MsgUpdateClient".to_string() + fn get_signers(&self) -> Vec { + vec![self.signer] } } diff --git a/modules/src/ics03_connection/msgs/conn_open_ack.rs b/modules/src/ics03_connection/msgs/conn_open_ack.rs index 02186b98de..9cf7d33e49 100644 --- a/modules/src/ics03_connection/msgs/conn_open_ack.rs +++ b/modules/src/ics03_connection/msgs/conn_open_ack.rs @@ -16,8 +16,7 @@ use crate::proofs::{ConsensusProof, Proofs}; use crate::tx_msg::Msg; use crate::Height; -/// Message type for the `MsgConnectionOpenAck` message. -pub const TYPE_MSG_CONNECTION_OPEN_ACK: &str = "connection_open_ack"; +pub const TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenAck"; /// Message definition `MsgConnectionOpenAck` (i.e., `ConnOpenAck` datagram). #[derive(Clone, Debug, PartialEq, Eq)] @@ -73,20 +72,16 @@ impl Msg for MsgConnectionOpenAck { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CONNECTION_OPEN_ACK.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { Ok(()) } - fn get_signers(&self) -> Vec { - vec![self.signer] + fn type_url(&self) -> String { + TYPE_URL.to_string() } - fn type_url(&self) -> String { - "/ibc.core.connection.v1.MsgConnectionOpenAck".to_string() + fn get_signers(&self) -> Vec { + vec![self.signer] } } diff --git a/modules/src/ics03_connection/msgs/conn_open_confirm.rs b/modules/src/ics03_connection/msgs/conn_open_confirm.rs index ddd71b0ec0..a36a55d97a 100644 --- a/modules/src/ics03_connection/msgs/conn_open_confirm.rs +++ b/modules/src/ics03_connection/msgs/conn_open_confirm.rs @@ -10,8 +10,7 @@ use crate::ics03_connection::error::{Error, Kind}; use crate::ics24_host::identifier::ConnectionId; use crate::{proofs::Proofs, tx_msg::Msg}; -/// Message type for the `MsgConnectionOpenConfirm` message. -pub const TYPE_MSG_CONNECTION_OPEN_CONFIRM: &str = "connection_open_confirm"; +pub const TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenConfirm"; /// /// Message definition for `MsgConnectionOpenConfirm` (i.e., `ConnOpenConfirm` datagram). @@ -42,20 +41,16 @@ impl Msg for MsgConnectionOpenConfirm { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CONNECTION_OPEN_CONFIRM.to_string() - } - fn validate_basic(&self) -> Result<(), Error> { Ok(()) } - fn get_signers(&self) -> Vec { - vec![self.signer] + fn type_url(&self) -> String { + TYPE_URL.to_string() } - fn type_url(&self) -> String { - "/ibc.core.connection.v1.MsgConnectionOpenConfirm".to_string() + fn get_signers(&self) -> Vec { + vec![self.signer] } } diff --git a/modules/src/ics03_connection/msgs/conn_open_init.rs b/modules/src/ics03_connection/msgs/conn_open_init.rs index 7731e2b1d0..72a1dd701f 100644 --- a/modules/src/ics03_connection/msgs/conn_open_init.rs +++ b/modules/src/ics03_connection/msgs/conn_open_init.rs @@ -12,8 +12,8 @@ use crate::ics03_connection::version::validate_version; use crate::ics24_host::identifier::{ClientId, ConnectionId}; use crate::tx_msg::Msg; -/// Message type for the `MsgConnectionOpenInit` message. -pub const TYPE_MSG_CONNECTION_OPEN_INIT: &str = "connection_open_init"; +pub const TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenInit"; + /// /// Message definition `MsgConnectionOpenInit` (i.e., the `ConnOpenInit` datagram). /// @@ -55,10 +55,6 @@ impl Msg for MsgConnectionOpenInit { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CONNECTION_OPEN_INIT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // All the validation is performed on creation self.counterparty @@ -66,12 +62,12 @@ impl Msg for MsgConnectionOpenInit { .map_err(|e| Kind::InvalidCounterparty.context(e).into()) } - fn get_signers(&self) -> Vec { - vec![self.signer] + fn type_url(&self) -> String { + TYPE_URL.to_string() } - fn type_url(&self) -> String { - "/ibc.core.connection.v1.MsgConnectionOpenInit".to_string() + fn get_signers(&self) -> Vec { + vec![self.signer] } } diff --git a/modules/src/ics03_connection/msgs/conn_open_try.rs b/modules/src/ics03_connection/msgs/conn_open_try.rs index cbf610d81e..7b6e5d0677 100644 --- a/modules/src/ics03_connection/msgs/conn_open_try.rs +++ b/modules/src/ics03_connection/msgs/conn_open_try.rs @@ -17,8 +17,7 @@ use crate::proofs::{ConsensusProof, Proofs}; use crate::tx_msg::Msg; use crate::Height; -/// Message type for the `MsgConnectionOpenTry` message. -pub const TYPE_MSG_CONNECTION_OPEN_TRY: &str = "connection_open_try"; +pub const TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenTry"; /// /// Message definition `MsgConnectionOpenTry` (i.e., `ConnOpenTry` datagram). @@ -88,22 +87,18 @@ impl Msg for MsgConnectionOpenTry { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CONNECTION_OPEN_TRY.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { self.counterparty .validate_basic() .map_err(|e| Kind::InvalidCounterparty.context(e).into()) } - fn get_signers(&self) -> Vec { - vec![self.signer] + fn type_url(&self) -> String { + TYPE_URL.to_string() } - fn type_url(&self) -> String { - "/ibc.core.connection.v1.MsgConnectionOpenTry".to_string() + fn get_signers(&self) -> Vec { + vec![self.signer] } } diff --git a/modules/src/ics04_channel/msgs/acknowledgement.rs b/modules/src/ics04_channel/msgs/acknowledgement.rs index 2a3b2e04d6..463a64a7ee 100644 --- a/modules/src/ics04_channel/msgs/acknowledgement.rs +++ b/modules/src/ics04_channel/msgs/acknowledgement.rs @@ -11,9 +11,6 @@ use crate::ics04_channel::packet::Packet; use crate::ics23_commitment::commitment::CommitmentProof; use crate::{proofs::Proofs, tx_msg::Msg, Height}; -/// Message type for the `MsgAcknowledgement` message. -const TYPE_MSG_ACKNOWLEDGEMENT: &str = "ics04/opaque"; - /// /// Message definition for packet acknowledgements. /// @@ -56,10 +53,6 @@ impl Msg for MsgAcknowledgement { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_ACKNOWLEDGEMENT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation diff --git a/modules/src/ics04_channel/msgs/chan_close_confirm.rs b/modules/src/ics04_channel/msgs/chan_close_confirm.rs index 252bc42286..caede9a93a 100644 --- a/modules/src/ics04_channel/msgs/chan_close_confirm.rs +++ b/modules/src/ics04_channel/msgs/chan_close_confirm.rs @@ -10,9 +10,6 @@ use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; -/// Message type for the `MsgChannelCloseConfirm` message. -const TYPE_MSG_CHANNEL_CLOSE_CONFIRM: &str = "channel_close_confirm"; - /// /// Message definition for the second step in the channel close handshake (the `ChanCloseConfirm` /// datagram). @@ -56,10 +53,6 @@ impl Msg for MsgChannelCloseConfirm { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CHANNEL_CLOSE_CONFIRM.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation diff --git a/modules/src/ics04_channel/msgs/chan_close_init.rs b/modules/src/ics04_channel/msgs/chan_close_init.rs index 7e5d70585c..7aea9fd749 100644 --- a/modules/src/ics04_channel/msgs/chan_close_init.rs +++ b/modules/src/ics04_channel/msgs/chan_close_init.rs @@ -9,9 +9,6 @@ use tendermint_proto::Protobuf; use std::convert::TryFrom; -/// Message type for the `MsgChannelCloseInit` message. -const TYPE_MSG_CHANNEL_CLOSE_INIT: &str = "channel_close_init"; - /// /// Message definition for the first step in the channel close handshake (`ChanCloseInit` datagram). /// @@ -49,10 +46,6 @@ impl Msg for MsgChannelCloseInit { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CHANNEL_CLOSE_INIT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation diff --git a/modules/src/ics04_channel/msgs/chan_open_ack.rs b/modules/src/ics04_channel/msgs/chan_open_ack.rs index d255cdb392..c6f29a2b15 100644 --- a/modules/src/ics04_channel/msgs/chan_open_ack.rs +++ b/modules/src/ics04_channel/msgs/chan_open_ack.rs @@ -11,8 +11,7 @@ use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; -/// Message type for the `MsgChannelOpenAck` message. -const TYPE_MSG_CHANNEL_OPEN_ACK: &str = "channel_open_ack"; +pub const TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenAck"; /// /// Message definition for the third step in the channel open handshake (`ChanOpenAck` datagram). @@ -62,17 +61,14 @@ impl Msg for MsgChannelOpenAck { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CHANNEL_OPEN_ACK.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation Ok(()) } + fn type_url(&self) -> String { - "/ibc.core.channel.v1.MsgChannelOpenAck".to_string() + TYPE_URL.to_string() } fn get_signers(&self) -> Vec { diff --git a/modules/src/ics04_channel/msgs/chan_open_confirm.rs b/modules/src/ics04_channel/msgs/chan_open_confirm.rs index 370ed84cbb..a202b6176c 100644 --- a/modules/src/ics04_channel/msgs/chan_open_confirm.rs +++ b/modules/src/ics04_channel/msgs/chan_open_confirm.rs @@ -10,8 +10,7 @@ use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; -/// Message type for the `MsgChannelOpenConfirm` message. -const TYPE_MSG_CHANNEL_OPEN_CONFIRM: &str = "channel_open_confirm"; +pub const TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenConfirm"; /// /// Message definition for the fourth step in the channel open handshake (`ChanOpenConfirm` @@ -56,10 +55,6 @@ impl Msg for MsgChannelOpenConfirm { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CHANNEL_OPEN_CONFIRM.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation @@ -67,7 +62,7 @@ impl Msg for MsgChannelOpenConfirm { } fn type_url(&self) -> String { - "/ibc.core.channel.v1.MsgChannelOpenConfirm".to_string() + TYPE_URL.to_string() } fn get_signers(&self) -> Vec { diff --git a/modules/src/ics04_channel/msgs/chan_open_init.rs b/modules/src/ics04_channel/msgs/chan_open_init.rs index 993f989f91..8f55172476 100644 --- a/modules/src/ics04_channel/msgs/chan_open_init.rs +++ b/modules/src/ics04_channel/msgs/chan_open_init.rs @@ -10,8 +10,7 @@ use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; -/// Message type for the `MsgChannelOpenInit` message. -const TYPE_MSG_CHANNEL_OPEN_INIT: &str = "channel_open_init"; +pub const TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenInit"; /// /// Message definition for the first step in the channel open handshake (`ChanOpenInit` datagram). @@ -31,16 +30,12 @@ impl Msg for MsgChannelOpenInit { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CHANNEL_OPEN_INIT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { self.channel.validate_basic() } fn type_url(&self) -> String { - "/ibc.core.channel.v1.MsgChannelOpenInit".to_string() + TYPE_URL.to_string() } fn get_signers(&self) -> Vec { diff --git a/modules/src/ics04_channel/msgs/chan_open_try.rs b/modules/src/ics04_channel/msgs/chan_open_try.rs index 4f591c0c53..f426cd8e46 100644 --- a/modules/src/ics04_channel/msgs/chan_open_try.rs +++ b/modules/src/ics04_channel/msgs/chan_open_try.rs @@ -11,8 +11,7 @@ use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; use std::str::FromStr; -/// Message type for the `MsgChannelOpenTry` message. -const TYPE_MSG_CHANNEL_OPEN_TRY: &str = "channel_open_try"; +pub const TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenTry"; /// /// Message definition for the second step in the channel open handshake (`ChanOpenTry` datagram). @@ -35,16 +34,12 @@ impl Msg for MsgChannelOpenTry { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_CHANNEL_OPEN_TRY.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { self.channel.validate_basic() } fn type_url(&self) -> String { - "/ibc.core.channel.v1.MsgChannelOpenTry".to_string() + TYPE_URL.to_string() } fn get_signers(&self) -> Vec { diff --git a/modules/src/ics04_channel/msgs/recv_packet.rs b/modules/src/ics04_channel/msgs/recv_packet.rs index 0dd4b28469..47bbc7b13c 100644 --- a/modules/src/ics04_channel/msgs/recv_packet.rs +++ b/modules/src/ics04_channel/msgs/recv_packet.rs @@ -11,9 +11,6 @@ use crate::ics04_channel::packet::Packet; use crate::ics23_commitment::commitment::CommitmentProof; use crate::{proofs::Proofs, tx_msg::Msg, Height}; -/// Message type for `MsgPacket`. -const TYPE_MSG_PACKET: &str = "ics04/opaque"; - /// /// Message definition for the "packet receiving" datagram. /// @@ -55,10 +52,6 @@ impl Msg for MsgRecvPacket { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_PACKET.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation diff --git a/modules/src/ics04_channel/msgs/timeout.rs b/modules/src/ics04_channel/msgs/timeout.rs index 521ceb67e7..60743cb68f 100644 --- a/modules/src/ics04_channel/msgs/timeout.rs +++ b/modules/src/ics04_channel/msgs/timeout.rs @@ -11,9 +11,6 @@ use crate::ics04_channel::packet::{Packet, Sequence}; use crate::ics23_commitment::commitment::CommitmentProof; use crate::{proofs::Proofs, tx_msg::Msg, Height}; -/// Message type for the `MsgTimeout` message. -const TYPE_MSG_TIMEOUT: &str = "ics04/timeout"; - /// /// Message definition for packet timeout domain type. /// @@ -52,10 +49,6 @@ impl Msg for MsgTimeout { crate::keys::ROUTER_KEY.to_string() } - fn get_type(&self) -> String { - TYPE_MSG_TIMEOUT.to_string() - } - fn validate_basic(&self) -> Result<(), Self::ValidationError> { // Nothing to validate // All the validation is performed on creation diff --git a/modules/src/ics26_routing/error.rs b/modules/src/ics26_routing/error.rs index 07b2f27cf6..02109780a3 100644 --- a/modules/src/ics26_routing/error.rs +++ b/modules/src/ics26_routing/error.rs @@ -10,6 +10,12 @@ pub enum Kind { #[error("error raised by the keeper functionality in message handler")] KeeperRaisedError, + + #[error("unknown type URL {0}")] + UnknownMessageTypeURL(String), + + #[error("the message is malformed and cannot be decoded")] + MalformedMessageBytes, } impl Kind { diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index ab1b6a54fe..959a83c0b1 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -1,7 +1,11 @@ use prost_types::Any; +use tendermint_proto::Protobuf; use crate::handler::HandlerOutput; use crate::ics02_client::handler::dispatch as ics2_msg_dispatcher; +use crate::ics02_client::msgs::create_client; +use crate::ics02_client::msgs::update_client; +use crate::ics02_client::msgs::ClientMsg; use crate::ics03_connection::handler::dispatch as ics3_msg_dispatcher; use crate::ics26_routing::context::ICS26Context; use crate::ics26_routing::error::{Error, Kind}; @@ -11,12 +15,31 @@ use crate::ics26_routing::msgs::ICS26Envelope::{ICS2Msg, ICS3Msg}; /// Mimics the DeliverTx ABCI interface, but a slightly lower level. No need for authentication /// info or signature checks here. /// https://github.com/cosmos/cosmos-sdk/tree/master/docs/basics -#[allow(unused_variables)] pub fn deliver(ctx: &mut Ctx, messages: Vec) -> Result<(), Error> where Ctx: ICS26Context, { - unimplemented!() + for any_msg in messages { + // Decode the proto message into a domain message, creating an ICS26 envelope. + let envelope = match any_msg.type_url.as_str() { + // ICS2 messages + create_client::TYPE_URL => { + // Pop out the message and then wrap it in the corresponding type + let domain_msg = create_client::MsgCreateAnyClient::decode_vec(&*any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(ICS2Msg(ClientMsg::CreateClient(domain_msg))) + } + update_client::TYPE_URL => { + let domain_msg = update_client::MsgUpdateAnyClient::decode_vec(&*any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(ICS2Msg(ClientMsg::UpdateClient(domain_msg))) + } + // TODO: ICS3 messages + _ => Err(Kind::UnknownMessageTypeURL(any_msg.type_url)), + }?; + dispatch(ctx, envelope)?; + } + Ok(()) } /// Top-level ICS dispatch function. Routes incoming IBC messages to their corresponding module. diff --git a/modules/src/tx_msg.rs b/modules/src/tx_msg.rs index 192f7cd1a5..4a70dd7568 100644 --- a/modules/src/tx_msg.rs +++ b/modules/src/tx_msg.rs @@ -7,8 +7,6 @@ pub trait Msg: Clone { // TODO -- clarify what is this function supposed to do & its connection to ICS26 routing mod. fn route(&self) -> String; - fn get_type(&self) -> String; - fn validate_basic(&self) -> Result<(), Self::ValidationError>; fn get_sign_bytes + prost::Message>(&self) -> Vec { @@ -18,6 +16,7 @@ pub trait Msg: Clone { buf } + /// Unique type identifier for this message, to support encoding to/from `prost_types::Any`. fn type_url(&self) -> String { unimplemented!() } From a82f53a1b58dc868f3e865d34313bf995e9d7291 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 2 Dec 2020 13:59:04 +0100 Subject: [PATCH 14/19] Basic light client impl to complete create client test. --- modules/src/ics02_client/height.rs | 3 +++ relayer/src/chain/mock.rs | 2 +- relayer/src/chain/runtime.rs | 2 +- relayer/src/foreign_client.rs | 22 ++++++++++++++++++-- relayer/src/light_client/mock.rs | 33 +++++++++++++++++------------- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/modules/src/ics02_client/height.rs b/modules/src/ics02_client/height.rs index 8ac0df86a3..a4e5291f17 100644 --- a/modules/src/ics02_client/height.rs +++ b/modules/src/ics02_client/height.rs @@ -7,7 +7,10 @@ use ibc_proto::ibc::core::client::v1::Height as RawHeight; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Height { + /// Previously known as "epoch", and will be renamed to "revision" soon pub version_number: u64, + + /// The height of a block pub version_height: u64, } diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 7b2a64612e..0373af545e 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -62,7 +62,7 @@ impl Chain for MockChain { fn init_light_client( &self, ) -> Result<(Box>, Option>), Error> { - let light_client = MockLightClient::new(); + let light_client = MockLightClient::new(self); Ok((Box::new(light_client), None)) } diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index 84533e36b2..b426d499ac 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -75,7 +75,7 @@ impl ChainRuntime { // Start the event monitor let (event_receiver, event_monitor_thread) = chain.init_event_monitor(rt.clone())?; - // Instantiate the & spawn the runtime + // Instantiate & spawn the runtime let (handle, runtime_thread) = Self::init(chain, light_client_handler, event_receiver, rt); let threads = Threads { diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 8ab8f6aba9..cfc7285f61 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -221,10 +221,11 @@ mod test { use crate::chain::mock::test_utils::get_basic_chain_config; use crate::chain::mock::MockChain; use crate::chain::runtime::ChainRuntime; - use crate::foreign_client::{build_create_client_and_send, ForeignClientConfig}; + use crate::foreign_client::{ + build_create_client_and_send, build_update_client_and_send, ForeignClientConfig, + }; #[test] - #[ignore] // WIP fn test_build_create_client_and_send() { let client_id = ClientId::from_str("client_on_a_forb").unwrap(); let a_cfg = get_basic_chain_config("chain_a"); @@ -241,4 +242,21 @@ mod test { res ); } + + #[test] + fn test_build_update_client_and_send() { + let client_id = ClientId::from_str("client_on_a_forb").unwrap(); + let a_cfg = get_basic_chain_config("chain_a"); + let b_cfg = get_basic_chain_config("chain_b"); + let opts = ForeignClientConfig::new(&a_cfg.id, &client_id); + + let (a_chain, _) = ChainRuntime::::spawn(a_cfg).unwrap(); + let (b_chain, _) = ChainRuntime::::spawn(b_cfg).unwrap(); + + let res = build_update_client_and_send(a_chain, b_chain, &opts); + assert!( + res.is_err(), + "build_update_client_and_send was supposed to fail (no client existed)" + ); + } } diff --git a/relayer/src/light_client/mock.rs b/relayer/src/light_client/mock.rs index 1eff250470..1f716ad2aa 100644 --- a/relayer/src/light_client/mock.rs +++ b/relayer/src/light_client/mock.rs @@ -1,10 +1,27 @@ use crate::chain::mock::MockChain; use crate::chain::Chain; use crate::error::Error; +use ibc::ics24_host::identifier::ChainId; +use ibc::mock::host::HostBlock; use ibc::Height; /// A light client serving a mock chain. -pub struct LightClient {} +pub struct LightClient { + chain_id: ChainId, +} + +impl LightClient { + pub fn new(chain: &MockChain) -> LightClient { + LightClient { + chain_id: chain.id().clone(), + } + } + + /// Returns a LightBlock at the requested height `h`. + fn light_block(&self, h: Height) -> ::LightBlock { + HostBlock::generate_tm_block(self.chain_id.clone(), h.version_height) + } +} #[allow(unused_variables)] impl super::LightClient for LightClient { @@ -17,7 +34,7 @@ impl super::LightClient for LightClient { } fn verify_to_target(&self, height: Height) -> Result<::LightBlock, Error> { - unimplemented!() + Ok(self.light_block(height)) } fn get_minimal_set( @@ -28,15 +45,3 @@ impl super::LightClient for LightClient { unimplemented!() } } - -impl LightClient { - pub fn new() -> LightClient { - LightClient {} - } -} - -impl Default for LightClient { - fn default() -> Self { - Self::new() - } -} From 14a1dbb20d637def2dbd01eff6a0026b33cfe03d Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 2 Dec 2020 14:11:51 +0100 Subject: [PATCH 15/19] Removed clippy allow exception --- relayer/src/chain/mock.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 0373af545e..b665675cda 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -33,13 +33,13 @@ use std::thread; /// The representation of a mocked chain as the relayer sees it. /// The relayer runtime and the light client will engage with the MockChain to query/send tx; the -/// primary interface for doing so is captured by `ICS18Context` which this struct implements. +/// primary interface for doing so is captured by `ICS18Context` which this struct can access via +/// the `context` field. pub struct MockChain { config: ChainConfig, context: MockContext, } -#[allow(unused_variables)] impl Chain for MockChain { type LightBlock = TMLightBlock; type Header = TendermintHeader; @@ -69,7 +69,7 @@ impl Chain for MockChain { fn init_event_monitor( &self, - rt: Arc>, + _rt: Arc>, ) -> Result< ( channel::Receiver, @@ -89,7 +89,7 @@ impl Chain for MockChain { unimplemented!() } - fn query(&self, data: Path, height: Height, prove: bool) -> Result { + fn query(&self, _data: Path, _height: Height, _prove: bool) -> Result { unimplemented!() } @@ -137,8 +137,8 @@ impl Chain for MockChain { fn build_header( &self, - trusted_light_block: Self::LightBlock, - target_light_block: Self::LightBlock, + _trusted_light_block: Self::LightBlock, + _target_light_block: Self::LightBlock, ) -> Result { unimplemented!() } @@ -150,8 +150,9 @@ impl Chain for MockChain { fn query_client_state( &self, client_id: &ClientId, - height: Height, + _height: Height, ) -> Result { + // TODO: unclear what are the scenarios where we need to take height into account. let any_state = self .context .query_client_full_state(client_id) @@ -167,17 +168,17 @@ impl Chain for MockChain { fn proven_client_state( &self, - client_id: &ClientId, - height: Height, + _client_id: &ClientId, + _height: Height, ) -> Result<(Self::ClientState, MerkleProof), Error> { unimplemented!() } fn proven_client_consensus( &self, - client_id: &ClientId, - consensus_height: Height, - height: Height, + _client_id: &ClientId, + _consensus_height: Height, + _height: Height, ) -> Result<(Self::ConsensusState, MerkleProof), Error> { unimplemented!() } From 43bd00876c29769b1a555eb4f109b8702b42c486 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 2 Dec 2020 14:43:50 +0100 Subject: [PATCH 16/19] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c1196dfb..7b78490ca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). - Implement the relayer CLI for connection handshake messages ([#358], [#359], [#360]) - Implement the relayer CLI for channel handshake messages ([#371], [#372], [#373], [#374]) - Implement commands to add and list keys for a chain ([#363]) + - Mock chain (implementing IBC handlers) and integration against CLI ([#158]) - [proto-compiler] - Refactor and allow specifying a commit at which the Cosmos SDK should be checked out ([#366]) - Add a `--tag` option to the `clone-sdk` command to check out a tag instead of a commit ([#369]) @@ -30,6 +31,7 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). [#95]: https://github.com/informalsystems/ibc-rs/issues/95 [#274]: https://github.com/informalsystems/ibc-rs/issues/274 +[#158]: https://github.com/informalsystems/ibc-rs/issues/158 [#315]: https://github.com/informalsystems/ibc-rs/issues/315 [#332]: https://github.com/informalsystems/ibc-rs/issues/332 [#335]: https://github.com/informalsystems/ibc-rs/pulls/335 From 188fc0f9dc8faafabda3bdd96989d96e784ecd27 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 3 Dec 2020 11:32:06 +0100 Subject: [PATCH 17/19] Revert "Updated changelog" This reverts commit 43bd00876c29769b1a555eb4f109b8702b42c486. In anticipation of merging master into this dev branch.. --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b78490ca6..95c1196dfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,6 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). - Implement the relayer CLI for connection handshake messages ([#358], [#359], [#360]) - Implement the relayer CLI for channel handshake messages ([#371], [#372], [#373], [#374]) - Implement commands to add and list keys for a chain ([#363]) - - Mock chain (implementing IBC handlers) and integration against CLI ([#158]) - [proto-compiler] - Refactor and allow specifying a commit at which the Cosmos SDK should be checked out ([#366]) - Add a `--tag` option to the `clone-sdk` command to check out a tag instead of a commit ([#369]) @@ -31,7 +30,6 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). [#95]: https://github.com/informalsystems/ibc-rs/issues/95 [#274]: https://github.com/informalsystems/ibc-rs/issues/274 -[#158]: https://github.com/informalsystems/ibc-rs/issues/158 [#315]: https://github.com/informalsystems/ibc-rs/issues/315 [#332]: https://github.com/informalsystems/ibc-rs/issues/332 [#335]: https://github.com/informalsystems/ibc-rs/pulls/335 From 3f1c2d78906efd7c2cb81ec608e5da1a4504f0b2 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 3 Dec 2020 11:58:02 +0100 Subject: [PATCH 18/19] Redid the changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f79db22df0..4d1b08d98d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased Changes +### FEATURES + +### IMPROVEMENTS + +- Mock chain (implementing IBC handlers) and integration against CLI ([#158]) + +[#158]: https://github.com/informalsystems/ibc-rs/issues/158 ## v0.0.5 *December 2, 2020* From c92fb0ad22eb3b52e70f66210189c50461732565 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 3 Dec 2020 15:18:56 +0100 Subject: [PATCH 19/19] After Anca's comments --- modules/src/ics18_relayer/utils.rs | 4 ++-- modules/src/ics26_routing/context.rs | 5 ++++- modules/src/ics26_routing/handler.rs | 8 +++++++- modules/src/mock/context.rs | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/src/ics18_relayer/utils.rs b/modules/src/ics18_relayer/utils.rs index 7e673b8a4e..0d616d25f9 100644 --- a/modules/src/ics18_relayer/utils.rs +++ b/modules/src/ics18_relayer/utils.rs @@ -130,7 +130,7 @@ mod tests { // - send the message to B. We bypass ICS18 interface and call directly into // MockContext `recv` method (to avoid additional serialization steps). - let dispatch_res_b = ctx_b.recv(ICS26Envelope::ICS2Msg(client_msg_b)); + let dispatch_res_b = ctx_b.deliver(ICS26Envelope::ICS2Msg(client_msg_b)); let validation_res = ctx_b.validate(); assert!( validation_res.is_ok(), @@ -174,7 +174,7 @@ mod tests { let client_msg_a = client_msg_a_res.unwrap(); // - send the message to A - let dispatch_res_a = ctx_a.recv(ICS26Envelope::ICS2Msg(client_msg_a)); + let dispatch_res_a = ctx_a.deliver(ICS26Envelope::ICS2Msg(client_msg_a)); let validation_res = ctx_a.validate(); assert!( validation_res.is_ok(), diff --git a/modules/src/ics26_routing/context.rs b/modules/src/ics26_routing/context.rs index cc8477122f..05320706b1 100644 --- a/modules/src/ics26_routing/context.rs +++ b/modules/src/ics26_routing/context.rs @@ -4,4 +4,7 @@ use crate::ics03_connection::context::{ConnectionKeeper, ConnectionReader}; /// This trait captures all the functional dependencies (i.e., context) which the ICS26 module /// requires to be able to dispatch and process IBC messages. In other words, this is the /// representation of a chain from the perspective of the IBC module of that chain. -pub trait ICS26Context: ClientReader + ClientKeeper + ConnectionReader + ConnectionKeeper {} +pub trait ICS26Context: + ClientReader + ClientKeeper + ConnectionReader + ConnectionKeeper + Clone +{ +} diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index 959a83c0b1..be6af0fdaf 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -19,6 +19,9 @@ pub fn deliver(ctx: &mut Ctx, messages: Vec) -> Result<(), Error> where Ctx: ICS26Context, { + // Create a clone, which will store each intermediary stage of applying txs. + let mut ctx_interim = ctx.clone(); + for any_msg in messages { // Decode the proto message into a domain message, creating an ICS26 envelope. let envelope = match any_msg.type_url.as_str() { @@ -37,8 +40,11 @@ where // TODO: ICS3 messages _ => Err(Kind::UnknownMessageTypeURL(any_msg.type_url)), }?; - dispatch(ctx, envelope)?; + dispatch(&mut ctx_interim, envelope)?; } + + // No error has surfaced, so we now apply the changes permanently to the original context. + *ctx = ctx_interim; Ok(()) } diff --git a/modules/src/mock/context.rs b/modules/src/mock/context.rs index 8f028b8c9a..c760f3e928 100644 --- a/modules/src/mock/context.rs +++ b/modules/src/mock/context.rs @@ -226,7 +226,7 @@ impl MockContext { /// A datagram passes from the relayer to the IBC module (on host chain). /// Alternative method to `ICS18Context::send` that does not exercise any serialization. /// Used in testing the ICS18 algorithms, hence this may return a ICS18Error. - pub fn recv(&mut self, msg: ICS26Envelope) -> Result<(), ICS18Error> { + pub fn deliver(&mut self, msg: ICS26Envelope) -> Result<(), ICS18Error> { dispatch(self, msg).map_err(|e| ICS18ErrorKind::TransactionFailed.context(e))?; // Create a new block. self.advance_host_chain_height();