From 12722c0b4e7bf6ff468eff979490ac105f54d5f2 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 26 Feb 2021 16:47:22 +0100 Subject: [PATCH 01/26] Added domain type def. Also cleaned-up the documentation, which looked very sloppy. https://docs.rs/ibc/0.1.1/ibc/ics02_client/msgs/index.html --- modules/src/ics02_client/msgs.rs | 3 ++ .../src/ics02_client/msgs/create_client.rs | 8 ++--- .../src/ics02_client/msgs/update_client.rs | 8 ++--- .../src/ics02_client/msgs/upgrade_client.rs | 36 +++++++++++++++++++ modules/src/ics18_relayer/mod.rs | 3 +- 5 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 modules/src/ics02_client/msgs/upgrade_client.rs diff --git a/modules/src/ics02_client/msgs.rs b/modules/src/ics02_client/msgs.rs index 4390a4489f..f9ec027328 100644 --- a/modules/src/ics02_client/msgs.rs +++ b/modules/src/ics02_client/msgs.rs @@ -6,13 +6,16 @@ use crate::ics02_client::msgs::create_client::MsgCreateAnyClient; use crate::ics02_client::msgs::update_client::MsgUpdateAnyClient; +use crate::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; pub mod create_client; pub mod update_client; +pub mod upgrade_client; #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] pub enum ClientMsg { CreateClient(MsgCreateAnyClient), UpdateClient(MsgUpdateAnyClient), + UpgradeClient(MsgUpgradeAnyClient) } diff --git a/modules/src/ics02_client/msgs/create_client.rs b/modules/src/ics02_client/msgs/create_client.rs index 0ce5677fdd..3ff79bf3e0 100644 --- a/modules/src/ics02_client/msgs/create_client.rs +++ b/modules/src/ics02_client/msgs/create_client.rs @@ -1,8 +1,4 @@ -//! These are definitions of messages that a relayer submits to a chain. Specific implementations of -//! these messages can be found, for instance, in ICS 07 for Tendermint-specific chains. A chain -//! handles these messages in two layers: first with the general ICS 02 client handler, which -//! subsequently calls into the chain-specific (e.g., ICS 07) client handler. See: -//! https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create. +//! Definition of domain type message `MsgCreateAnyClient`. use std::convert::TryFrom; @@ -17,7 +13,7 @@ use crate::ics02_client::error; use crate::ics02_client::error::{Error, Kind}; use crate::tx_msg::Msg; -pub const TYPE_URL: &str = "/ibc.core.client.v1.MsgCreateClient"; +pub(crate) 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)] diff --git a/modules/src/ics02_client/msgs/update_client.rs b/modules/src/ics02_client/msgs/update_client.rs index 23aa97363b..1cbf3d0059 100644 --- a/modules/src/ics02_client/msgs/update_client.rs +++ b/modules/src/ics02_client/msgs/update_client.rs @@ -1,8 +1,4 @@ -//! These are definitions of messages that a relayer submits to a chain. Specific implementations of -//! these messages can be found, for instance, in ICS 07 for Tendermint-specific chains. A chain -//! handles these messages in two layers: first with the general ICS 02 client handler, which -//! subsequently calls into the chain-specific (e.g., ICS 07) client handler. See: -//! https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create. +//! Definition of domain type message `MsgUpdateAnyClient`. use std::convert::TryFrom; @@ -17,7 +13,7 @@ use crate::ics02_client::error::{Error, Kind}; use crate::ics24_host::identifier::ClientId; use crate::tx_msg::Msg; -pub const TYPE_URL: &str = "/ibc.core.client.v1.MsgUpdateClient"; +pub(crate) 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 diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs new file mode 100644 index 0000000000..d1a880cb94 --- /dev/null +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -0,0 +1,36 @@ +//! Definition of domain type msg `MsgUpgradeAnyClient`. + +use tendermint::account::Id as AccountId; + +use crate::ics24_host::identifier::ClientId; +use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::tx_msg::Msg; + +pub(crate) const TYPE_URL: &str = "/ibc.core.client.v1.MsgUpgradeClient"; + +/// A type of message that triggers the upgrade of an on-chain (IBC) client. +#[derive(Clone, Debug, PartialEq)] +pub struct MsgUpgradeAnyClient { + pub client_id: ClientId, + pub client_state: AnyClientState, + pub consensus_state: AnyConsensusState, + pub proof_upgrade_client: String, + pub proof_upgrade_consensus_state: String, +} + + +impl Msg for MsgUpgradeAnyClient { + type ValidationError = crate::ics24_host::error::ValidationError; + + fn route(&self) -> String { + crate::keys::ROUTER_KEY.to_string() + } + + fn type_url(&self) -> String { + TYPE_URL.to_string() + } + + fn get_signers(&self) -> Vec { + unimplemented!() + } +} \ No newline at end of file diff --git a/modules/src/ics18_relayer/mod.rs b/modules/src/ics18_relayer/mod.rs index 71e8e42ed9..380a73836c 100644 --- a/modules/src/ics18_relayer/mod.rs +++ b/modules/src/ics18_relayer/mod.rs @@ -1,6 +1,5 @@ -//! - ICS 18: Implementation of basic relayer functions. +//! ICS 18: Implementation of basic relayer functions. pub mod context; pub mod error; - pub mod utils; From cf1dda74483f9ab1d37b9eaa533edd2a8dd359d9 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 3 Mar 2021 09:58:21 +0100 Subject: [PATCH 02/26] Added partial handler & command --- modules/src/events.rs | 1 + modules/src/ics02_client/client_def.rs | 2 + modules/src/ics02_client/error.rs | 3 + modules/src/ics02_client/events.rs | 5 ++ modules/src/ics02_client/handler.rs | 3 + .../ics02_client/handler/upgrade_client.rs | 58 +++++++++++++++++++ .../src/ics02_client/msgs/update_client.rs | 3 +- proto-compiler/src/cmd/compile.rs | 1 + relayer-cli/src/commands/tx.rs | 6 +- relayer-cli/src/commands/tx/client.rs | 47 ++++++++++++++- 10 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 modules/src/ics02_client/handler/upgrade_client.rs diff --git a/modules/src/events.rs b/modules/src/events.rs index 56163e76c5..3913b1492f 100644 --- a/modules/src/events.rs +++ b/modules/src/events.rs @@ -34,6 +34,7 @@ pub enum IbcEvent { CreateClient(ClientEvents::CreateClient), UpdateClient(ClientEvents::UpdateClient), + UpgradeClient(ClientEvents::UpgradeClient), ClientMisbehavior(ClientEvents::ClientMisbehavior), OpenInitConnection(ConnectionEvents::OpenInit), diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index 64f8f47fe8..6358ba65a5 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -603,6 +603,8 @@ impl ClientDef for AnyClient { } } } + + // TODO: Add upgrade method here } #[cfg(test)] diff --git a/modules/src/ics02_client/error.rs b/modules/src/ics02_client/error.rs index 2a735a9382..d89140c913 100644 --- a/modules/src/ics02_client/error.rs +++ b/modules/src/ics02_client/error.rs @@ -21,6 +21,9 @@ pub enum Kind { #[error("client not found: {0}")] ClientNotFound(ClientId), + #[error("client is frozen: {0}")] + ClientFrozen(ClientId), + #[error("consensus state not found at: {0} at height {1}")] ConsensusStateNotFound(ClientId, Height), diff --git a/modules/src/ics02_client/events.rs b/modules/src/ics02_client/events.rs index 9d97d07b60..bdae597871 100644 --- a/modules/src/ics02_client/events.rs +++ b/modules/src/ics02_client/events.rs @@ -214,3 +214,8 @@ impl From for IbcEvent { IbcEvent::ClientMisbehavior(v) } } + + +/// Signals a recent upgrade of an on-chain client (IBC Client). +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct UpgradeClient(Attributes); \ No newline at end of file diff --git a/modules/src/ics02_client/handler.rs b/modules/src/ics02_client/handler.rs index e2410cf960..481d621985 100644 --- a/modules/src/ics02_client/handler.rs +++ b/modules/src/ics02_client/handler.rs @@ -7,11 +7,13 @@ use crate::ics02_client::msgs::ClientMsg; pub mod create_client; pub mod update_client; +pub mod upgrade_client; #[derive(Clone, Debug, PartialEq, Eq)] pub enum ClientResult { Create(create_client::Result), Update(update_client::Result), + Upgrade(upgrade_client::Result), } /// General entry point for processing any message related to ICS2 (client functions) protocols. @@ -22,5 +24,6 @@ where match msg { ClientMsg::CreateClient(msg) => create_client::process(ctx, msg), ClientMsg::UpdateClient(msg) => update_client::process(ctx, msg), + ClientMsg::UpgradeClient(msg) => upgrade_client::process(ctx, msg), } } diff --git a/modules/src/ics02_client/handler/upgrade_client.rs b/modules/src/ics02_client/handler/upgrade_client.rs new file mode 100644 index 0000000000..e91a8e61eb --- /dev/null +++ b/modules/src/ics02_client/handler/upgrade_client.rs @@ -0,0 +1,58 @@ + +use crate::ics02_client::context::ClientReader; +use crate::ics02_client::error::{Error, Kind}; +use crate::ics02_client::handler::ClientResult; +use crate::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; +use crate::handler::HandlerResult; +use crate::ics24_host::identifier::ClientId; +use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::ics02_client::state::ClientState; +use crate::ics02_client::events::Attributes; +use crate::events::IbcEvent; + + +/// The result following the successful processing of a `MsgUpgradeAnyClient` message. +/// This data type should be used with a qualified name `upgrade_client::Result` to avoid ambiguity. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Result { + pub client_id: ClientId, + pub client_state: AnyClientState, + pub consensus_state: AnyConsensusState, +} + + +pub fn process( + ctx: &dyn ClientReader, + msg: MsgUpgradeAnyClient, +) -> HandlerResult { + + let MsgUpgradeAnyClient { + client_id, + .. + } = msg; + + // Read client state from the host chain store. + let client_state = ctx + .client_state(&client_id) + .ok_or_else(|| Kind::ClientNotFound(client_id.clone()))?; + + if client_state.is_frozen() { + return Err(Kind::ClientFrozen(client_id.clone()).into()); + } else { + return Err(Kind::ClientFrozen(client_id.clone()).into()); + } + + let result = ClientResult::Upgrade(Result { + client_id: client_id.clone(), + client_state: new_client_state, + consensus_state: new_consensus_state, + }); + + let event_attributes = Attributes { + client_id, + ..Default::default() + }; + output.emit(IbcEvent::UpgradeClient(event_attributes.into())); + + Ok(output.with_result(result)) +} \ No newline at end of file diff --git a/modules/src/ics02_client/msgs/update_client.rs b/modules/src/ics02_client/msgs/update_client.rs index 1cbf3d0059..e0adf77d41 100644 --- a/modules/src/ics02_client/msgs/update_client.rs +++ b/modules/src/ics02_client/msgs/update_client.rs @@ -84,9 +84,8 @@ mod tests { use crate::ics02_client::client_def::AnyHeader; use crate::ics02_client::msgs::MsgUpdateAnyClient; - use crate::ics24_host::identifier::ClientId; - use crate::ics07_tendermint::header::test_util::get_dummy_ics07_header; + use crate::ics24_host::identifier::ClientId; use crate::test_utils::get_dummy_account_id; #[test] diff --git a/proto-compiler/src/cmd/compile.rs b/proto-compiler/src/cmd/compile.rs index 0a3ea64b45..c794a5fb99 100644 --- a/proto-compiler/src/cmd/compile.rs +++ b/proto-compiler/src/cmd/compile.rs @@ -53,6 +53,7 @@ impl CompileCmd { format!("{}/proto/cosmos/tx", sdk_dir.display()), format!("{}/proto/cosmos/base", sdk_dir.display()), format!("{}/proto/cosmos/staking", sdk_dir.display()), + format!("{}/proto/cosmos/upgrade", sdk_dir.display()), ]; let proto_includes_paths = [ diff --git a/relayer-cli/src/commands/tx.rs b/relayer-cli/src/commands/tx.rs index 5f1304266f..cb64a1c63e 100644 --- a/relayer-cli/src/commands/tx.rs +++ b/relayer-cli/src/commands/tx.rs @@ -1,7 +1,7 @@ //! `tx` subcommand use abscissa_core::{Command, Help, Options, Runnable}; -use crate::commands::tx::client::{TxCreateClientCmd, TxUpdateClientCmd}; +use crate::commands::tx::client::{TxCreateClientCmd, TxUpdateClientCmd, TxUpgradeClientCmd}; mod channel; mod client; @@ -35,6 +35,10 @@ pub enum TxRawCommands { #[options(help = "Update the specified client on destination chain")] UpdateClient(TxUpdateClientCmd), + /// The `tx raw upgrade-client` subcommand. Submits a MsgUpgradeClient in a transaction to a chain. + #[options(help = "Upgrade the specified client on destination chain")] + UpgradeClient(TxUpgradeClientCmd), + /// The `tx raw conn-init` subcommand #[options(help = "Initialize a connection (ConnectionOpenInit)")] ConnInit(connection::TxRawConnInitCmd), diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index d2e3098bdf..af65accbac 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -7,7 +7,7 @@ use ibc_relayer::foreign_client::ForeignClient; use crate::application::app_config; use crate::commands::cli_utils::{ChainHandlePair, SpawnOptions}; -use crate::conclude::Output; +use crate::conclude::{Output, exit_with_unrecoverable_error}; use crate::error::{Error, Kind}; #[derive(Clone, Command, Debug, Options)] @@ -101,3 +101,48 @@ impl Runnable for TxUpdateClientCmd { } } } + +#[derive(Clone, Command, Debug, Options)] +pub struct TxUpgradeClientCmd { + #[options(free, required, help = "identifier of the destination chain")] + dst_chain_id: ChainId, + + #[options(free, required, help = "identifier of the source chain")] + src_chain_id: ChainId, + + #[options( + free, + required, + help = "identifier of the client to be updated on destination chain" + )] + dst_client_id: ClientId, +} + +impl Runnable for TxUpgradeClientCmd { + fn run(&self) { + let config = app_config(); + + let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); + let chains = ChainHandlePair::spawn_with( + spawn_options, + &config, + &self.src_chain_id, + &self.dst_chain_id, + ).unwrap_or_else(exit_with_unrecoverable_error); + + let client = ForeignClient { + dst_chain: chains.dst, + src_chain: chains.src, + id: self.dst_client_id.clone(), + }; + + let res: Result = client + .build_update_client_and_send() + .map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(receipt) => Output::success(receipt).exit(), + Err(e) => Output::error(format!("{}", e)).exit(), + } + } +} \ No newline at end of file From a2942de60126865228c1a47cc0af08ac5daf6054 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 3 Mar 2021 10:08:53 +0100 Subject: [PATCH 03/26] Added upgrade proto files. Added Cargo.lock for proto-compiler --- proto-compiler/Cargo.lock | 1094 +++++++++++++++++++++ proto/src/prost/cosmos.upgrade.v1beta1.rs | 98 ++ 2 files changed, 1192 insertions(+) create mode 100644 proto-compiler/Cargo.lock create mode 100644 proto/src/prost/cosmos.upgrade.v1beta1.rs diff --git a/proto-compiler/Cargo.lock b/proto-compiler/Cargo.lock new file mode 100644 index 0000000000..15de405c69 --- /dev/null +++ b/proto-compiler/Cargo.lock @@ -0,0 +1,1094 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "argh" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91792f088f87cdc7a2cfb1d617fa5ea18d7f1dc22ef0e1b5f82f3157cdc522be" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4eb0c0c120ad477412dc95a4ce31e38f2113e46bd13511253f79196ca68b067" +dependencies = [ + "argh_shared", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781f336cc9826dbaddb9754cb5db61e64cab4f69668bd19dcc4a0394a86f4cb1" + +[[package]] +name = "async-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures-channel" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" + +[[package]] +name = "futures-sink" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" + +[[package]] +name = "futures-task" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" + +[[package]] +name = "futures-util" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "git2" +version = "0.13.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d250f5f82326884bd39c2853577e70a121775db76818ffa452ed1e80de12986" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "h2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832b01df74254fe364568d6ddc294443f61cbec82816b60904303af87efae78" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "http" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "hyper" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "ibc-proto-compiler" +version = "0.2.0" +dependencies = [ + "argh", + "git2", + "prost-build", + "tempdir", + "tonic", + "tonic-build", + "walkdir", +] + +[[package]] +name = "idna" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" + +[[package]] +name = "libgit2-sys" +version = "0.12.18+1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da6a42da88fc37ee1ecda212ffa254c25713532980005d5f7c0b0fbe7e6e885" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mio" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi", +] + +[[package]] +name = "multimap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cf491442e4b033ed1c722cb9f0df5fcfcf4de682466c46469c36bc47dc5548a" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prost" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.2", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if", + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand 0.8.3", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1981ad97df782ab506a1f43bf82c967326960d278acf3bf8279809648c3ff3ea" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ba8f479158947373b6df40cf48f4779bb25c99ca3c661bd95e0ab1963ad8b0e" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e8546fd40d56d28089835c0a81bb396848103b00f888aea42d46eb5974df07" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f715efe02c0862926eb463e49368d38ddb119383475686178e32e26d15d06a66" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "rand 0.8.3", + "slab", + "tokio", + "tokio-stream", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "which" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" +dependencies = [ + "libc", + "thiserror", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/proto/src/prost/cosmos.upgrade.v1beta1.rs b/proto/src/prost/cosmos.upgrade.v1beta1.rs new file mode 100644 index 0000000000..3d0deec5a5 --- /dev/null +++ b/proto/src/prost/cosmos.upgrade.v1beta1.rs @@ -0,0 +1,98 @@ +/// Plan specifies information about a planned upgrade and when it should occur. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Plan { + /// Sets the name for the upgrade. This name will be used by the upgraded + /// version of the software to apply any special "on-upgrade" commands during + /// the first BeginBlock method after the upgrade is applied. It is also used + /// to detect whether a software version can handle a given upgrade. If no + /// upgrade handler with this name has been set in the software, it will be + /// assumed that the software is out-of-date when the upgrade Time or Height is + /// reached and the software will exit. + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + /// The time after which the upgrade must be performed. + /// Leave set to its zero value to use a pre-defined Height instead. + #[prost(message, optional, tag="2")] + pub time: ::core::option::Option<::prost_types::Timestamp>, + /// The height at which the upgrade must be performed. + /// Only used if Time is not set. + #[prost(int64, tag="3")] + pub height: i64, + /// Any application specific upgrade info to be included on-chain + /// such as a git commit that validators could automatically upgrade to + #[prost(string, tag="4")] + pub info: ::prost::alloc::string::String, + /// IBC-enabled chains can opt-in to including the upgraded client state in its upgrade plan + /// This will make the chain commit to the correct upgraded (self) client state before the upgrade occurs, + /// so that connecting chains can verify that the new upgraded client is valid by verifying a proof on the + /// previous version of the chain. + /// This will allow IBC connections to persist smoothly across planned chain upgrades + #[prost(message, optional, tag="5")] + pub upgraded_client_state: ::core::option::Option<::prost_types::Any>, +} +/// SoftwareUpgradeProposal is a gov Content type for initiating a software +/// upgrade. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SoftwareUpgradeProposal { + #[prost(string, tag="1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub description: ::prost::alloc::string::String, + #[prost(message, optional, tag="3")] + pub plan: ::core::option::Option, +} +/// CancelSoftwareUpgradeProposal is a gov Content type for cancelling a software +/// upgrade. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CancelSoftwareUpgradeProposal { + #[prost(string, tag="1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub description: ::prost::alloc::string::String, +} +/// QueryCurrentPlanRequest is the request type for the Query/CurrentPlan RPC +/// method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryCurrentPlanRequest { +} +/// QueryCurrentPlanResponse is the response type for the Query/CurrentPlan RPC +/// method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryCurrentPlanResponse { + /// plan is the current upgrade plan. + #[prost(message, optional, tag="1")] + pub plan: ::core::option::Option, +} +/// QueryCurrentPlanRequest is the request type for the Query/AppliedPlan RPC +/// method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryAppliedPlanRequest { + /// name is the name of the applied plan to query for. + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, +} +/// QueryAppliedPlanResponse is the response type for the Query/AppliedPlan RPC +/// method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryAppliedPlanResponse { + /// height is the block height at which the plan was applied. + #[prost(int64, tag="1")] + pub height: i64, +} +/// QueryUpgradedConsensusStateRequest is the request type for the Query/UpgradedConsensusState +/// RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryUpgradedConsensusStateRequest { + /// last height of the current chain must be sent in request + /// as this is the height under which next consensus state is stored + #[prost(int64, tag="1")] + pub last_height: i64, +} +/// QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState +/// RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryUpgradedConsensusStateResponse { + #[prost(message, optional, tag="1")] + pub upgraded_consensus_state: ::core::option::Option<::prost_types::Any>, +} +# [doc = r" Generated client implementations."] pub mod query_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Query defines the gRPC upgrade querier service."] pub struct QueryClient < T > { inner : tonic :: client :: Grpc < T > , } impl QueryClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > QueryClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } # [doc = " CurrentPlan queries the current upgrade plan."] pub async fn current_plan (& mut self , request : impl tonic :: IntoRequest < super :: QueryCurrentPlanRequest > ,) -> Result < tonic :: Response < super :: QueryCurrentPlanResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.upgrade.v1beta1.Query/CurrentPlan") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " AppliedPlan queries a previously applied upgrade plan by its name."] pub async fn applied_plan (& mut self , request : impl tonic :: IntoRequest < super :: QueryAppliedPlanRequest > ,) -> Result < tonic :: Response < super :: QueryAppliedPlanResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.upgrade.v1beta1.Query/AppliedPlan") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " UpgradedConsensusState queries the consensus state that will serve"] # [doc = " as a trusted kernel for the next version of this chain. It will only be"] # [doc = " stored at the last height of this chain."] # [doc = " UpgradedConsensusState RPC not supported with legacy querier"] pub async fn upgraded_consensus_state (& mut self , request : impl tonic :: IntoRequest < super :: QueryUpgradedConsensusStateRequest > ,) -> Result < tonic :: Response < super :: QueryUpgradedConsensusStateResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.upgrade.v1beta1.Query/UpgradedConsensusState") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for QueryClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for QueryClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "QueryClient {{ ... }}") } } } \ No newline at end of file From 172a620e21fadedfb740a777dfc12de3490044e5 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 3 Mar 2021 11:12:51 +0100 Subject: [PATCH 04/26] Prep for query_upgraded_client_state --- modules/src/ics02_client/context.rs | 5 +- modules/src/ics02_client/events.rs | 3 +- .../src/ics02_client/handler/update_client.rs | 4 +- .../ics02_client/handler/upgrade_client.rs | 54 +++++++++---------- modules/src/ics02_client/msgs.rs | 2 +- .../src/ics02_client/msgs/upgrade_client.rs | 5 +- proto-compiler/README.md | 8 ++- proto/src/lib.rs | 5 ++ relayer-cli/src/commands/tx/client.rs | 49 +++++++++-------- relayer/src/chain/handle.rs | 6 +++ relayer/src/chain/handle/prod.rs | 8 +++ 11 files changed, 86 insertions(+), 63 deletions(-) diff --git a/modules/src/ics02_client/context.rs b/modules/src/ics02_client/context.rs index 3129a8fe7a..d0cde5738a 100644 --- a/modules/src/ics02_client/context.rs +++ b/modules/src/ics02_client/context.rs @@ -5,7 +5,7 @@ use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; use crate::ics02_client::error::Error; -use crate::ics02_client::handler::ClientResult::{self, Create, Update}; +use crate::ics02_client::handler::ClientResult::{self, Create, Update, Upgrade}; use crate::ics24_host::identifier::ClientId; use crate::Height; @@ -46,6 +46,9 @@ pub trait ClientKeeper { )?; Ok(()) } + Upgrade(_) => { + unimplemented!() + } } } diff --git a/modules/src/ics02_client/events.rs b/modules/src/ics02_client/events.rs index bdae597871..e1093292ea 100644 --- a/modules/src/ics02_client/events.rs +++ b/modules/src/ics02_client/events.rs @@ -215,7 +215,6 @@ impl From for IbcEvent { } } - /// Signals a recent upgrade of an on-chain client (IBC Client). #[derive(Debug, Deserialize, Serialize, Clone)] -pub struct UpgradeClient(Attributes); \ No newline at end of file +pub struct UpgradeClient(Attributes); diff --git a/modules/src/ics02_client/handler/update_client.rs b/modules/src/ics02_client/handler/update_client.rs index 625d400c59..c314e485f1 100644 --- a/modules/src/ics02_client/handler/update_client.rs +++ b/modules/src/ics02_client/handler/update_client.rs @@ -78,7 +78,7 @@ mod tests { use crate::ics02_client::client_def::AnyClientState; use crate::ics02_client::error::Kind; use crate::ics02_client::handler::dispatch; - use crate::ics02_client::handler::ClientResult::{Create, Update}; + use crate::ics02_client::handler::ClientResult::Update; use crate::ics02_client::header::Header; use crate::ics02_client::msgs::update_client::MsgUpdateAnyClient; use crate::ics02_client::msgs::ClientMsg; @@ -125,7 +125,7 @@ mod tests { AnyClientState::Mock(MockClientState(MockHeader(msg.header.height()))) ) } - Create(_) => panic!("update handler result has type CreateResult"), + _ => panic!("update handler result has incorrect type"), } } Err(err) => { diff --git a/modules/src/ics02_client/handler/upgrade_client.rs b/modules/src/ics02_client/handler/upgrade_client.rs index e91a8e61eb..bb7931908a 100644 --- a/modules/src/ics02_client/handler/upgrade_client.rs +++ b/modules/src/ics02_client/handler/upgrade_client.rs @@ -1,15 +1,13 @@ - +//! Protocol logic specific to processing ICS2 messages of type `MsgUpgradeAnyClient`. +//! +use crate::handler::HandlerResult; +use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::context::ClientReader; use crate::ics02_client::error::{Error, Kind}; use crate::ics02_client::handler::ClientResult; use crate::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; -use crate::handler::HandlerResult; -use crate::ics24_host::identifier::ClientId; -use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::state::ClientState; -use crate::ics02_client::events::Attributes; -use crate::events::IbcEvent; - +use crate::ics24_host::identifier::ClientId; /// The result following the successful processing of a `MsgUpgradeAnyClient` message. /// This data type should be used with a qualified name `upgrade_client::Result` to avoid ambiguity. @@ -20,16 +18,11 @@ pub struct Result { pub consensus_state: AnyConsensusState, } - pub fn process( ctx: &dyn ClientReader, msg: MsgUpgradeAnyClient, ) -> HandlerResult { - - let MsgUpgradeAnyClient { - client_id, - .. - } = msg; + let MsgUpgradeAnyClient { client_id, .. } = msg; // Read client state from the host chain store. let client_state = ctx @@ -37,22 +30,23 @@ pub fn process( .ok_or_else(|| Kind::ClientNotFound(client_id.clone()))?; if client_state.is_frozen() { - return Err(Kind::ClientFrozen(client_id.clone()).into()); - } else { - return Err(Kind::ClientFrozen(client_id.clone()).into()); + return Err(Kind::ClientFrozen(client_id).into()); } - let result = ClientResult::Upgrade(Result { - client_id: client_id.clone(), - client_state: new_client_state, - consensus_state: new_consensus_state, - }); - - let event_attributes = Attributes { - client_id, - ..Default::default() - }; - output.emit(IbcEvent::UpgradeClient(event_attributes.into())); - - Ok(output.with_result(result)) -} \ No newline at end of file + // Not implemented yet: https://github.com/informalsystems/ibc-rs/issues/722 + todo!() + + // let result = ClientResult::Upgrade(Result { + // client_id: client_id.clone(), + // client_state: new_client_state, + // consensus_state: new_consensus_state, + // }); + // + // let event_attributes = Attributes { + // client_id, + // ..Default::default() + // }; + // output.emit(IbcEvent::UpgradeClient(event_attributes.into())); + // + // Ok(output.with_result(result)) +} diff --git a/modules/src/ics02_client/msgs.rs b/modules/src/ics02_client/msgs.rs index f9ec027328..9d27533507 100644 --- a/modules/src/ics02_client/msgs.rs +++ b/modules/src/ics02_client/msgs.rs @@ -17,5 +17,5 @@ pub mod upgrade_client; pub enum ClientMsg { CreateClient(MsgCreateAnyClient), UpdateClient(MsgUpdateAnyClient), - UpgradeClient(MsgUpgradeAnyClient) + UpgradeClient(MsgUpgradeAnyClient), } diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs index d1a880cb94..b92e76b732 100644 --- a/modules/src/ics02_client/msgs/upgrade_client.rs +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -2,8 +2,8 @@ use tendermint::account::Id as AccountId; -use crate::ics24_host::identifier::ClientId; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::ics24_host::identifier::ClientId; use crate::tx_msg::Msg; pub(crate) const TYPE_URL: &str = "/ibc.core.client.v1.MsgUpgradeClient"; @@ -18,7 +18,6 @@ pub struct MsgUpgradeAnyClient { pub proof_upgrade_consensus_state: String, } - impl Msg for MsgUpgradeAnyClient { type ValidationError = crate::ics24_host::error::ValidationError; @@ -33,4 +32,4 @@ impl Msg for MsgUpgradeAnyClient { fn get_signers(&self) -> Vec { unimplemented!() } -} \ No newline at end of file +} diff --git a/proto-compiler/README.md b/proto-compiler/README.md index be44392d1a..774d47bf22 100644 --- a/proto-compiler/README.md +++ b/proto-compiler/README.md @@ -6,7 +6,13 @@ The `ibc-proto-compiler` is a simple command-line tool to automate the compilati ### Clone the Cosmos SDK -From within the `proto-compiler` directory, run the following command to clone the Cosmos SDK repository, and check out a specific commit: +From within the `proto-compiler` directory, compile the binary using the `--locked` flag: + +```bash +cargo build --locked +``` + +Run the following command to clone the Cosmos SDK repository, and check out a specific commit: ```bash $ cargo run -- clone-sdk --out /tmp/sdk --commit f4ce6860d23a95e063f8b5f0bc340b2408a8604c diff --git a/proto/src/lib.rs b/proto/src/lib.rs index 56b9bdd633..1fafc37a3b 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -69,6 +69,11 @@ pub mod cosmos { include!("prost/cosmos.tx.v1beta1.rs"); } } + pub mod upgrade { + pub mod v1beta1 { + include!("prost/cosmos.upgrade.v1beta1.rs"); + } + } } pub mod ibc { diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index af65accbac..6a7fb17f3d 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -7,8 +7,9 @@ use ibc_relayer::foreign_client::ForeignClient; use crate::application::app_config; use crate::commands::cli_utils::{ChainHandlePair, SpawnOptions}; -use crate::conclude::{Output, exit_with_unrecoverable_error}; +use crate::conclude::{exit_with_unrecoverable_error, Output}; use crate::error::{Error, Kind}; +use ibc::ics02_client::height::Height; #[derive(Clone, Command, Debug, Options)] pub struct TxCreateClientCmd { @@ -107,15 +108,19 @@ pub struct TxUpgradeClientCmd { #[options(free, required, help = "identifier of the destination chain")] dst_chain_id: ChainId, - #[options(free, required, help = "identifier of the source chain")] + #[options( + free, + required, + help = "identifier of the chain which underwent upgrade (source chain)" + )] src_chain_id: ChainId, #[options( - free, - required, - help = "identifier of the client to be updated on destination chain" + free, + required, + help = "identifier of the client to be upgraded on destination chain" )] - dst_client_id: ClientId, + client_id: ClientId, } impl Runnable for TxUpgradeClientCmd { @@ -128,21 +133,19 @@ impl Runnable for TxUpgradeClientCmd { &config, &self.src_chain_id, &self.dst_chain_id, - ).unwrap_or_else(exit_with_unrecoverable_error); - - let client = ForeignClient { - dst_chain: chains.dst, - src_chain: chains.src, - id: self.dst_client_id.clone(), - }; - - let res: Result = client - .build_update_client_and_send() - .map_err(|e| Kind::Tx.context(e).into()); - - match res { - Ok(receipt) => Output::success(receipt).exit(), - Err(e) => Output::error(format!("{}", e)).exit(), - } + ) + .unwrap_or_else(exit_with_unrecoverable_error); + + // Query the source chain for the upgraded client state, consensus state & proofs + let (client_state, proof) = chains + .src + .query_upgraded_client_state(&self.client_id, Height::default()) + .map_err(|e| Kind::Tx.context(e)) + .unwrap_or_else(exit_with_unrecoverable_error); + + // match res { + // Ok(receipt) => Output::success(receipt).exit(), + // Err(e) => Output::error(format!("{}", e)).exit(), + // } } -} \ No newline at end of file +} diff --git a/relayer/src/chain/handle.rs b/relayer/src/chain/handle.rs index d982a34188..cd7f4b6a0f 100644 --- a/relayer/src/chain/handle.rs +++ b/relayer/src/chain/handle.rs @@ -224,6 +224,12 @@ pub trait ChainHandle: DynClone + Send + Sync + Debug { height: Height, ) -> Result; + fn query_upgraded_client_state( + &self, + client_id: &ClientId, + height: Height, + ) -> Result<(AnyClientState, MerkleProof), Error>; + fn query_commitment_prefix(&self) -> Result; fn query_compatible_versions(&self) -> Result, Error>; diff --git a/relayer/src/chain/handle/prod.rs b/relayer/src/chain/handle/prod.rs index 4e6bcce88a..5e3ff06230 100644 --- a/relayer/src/chain/handle/prod.rs +++ b/relayer/src/chain/handle/prod.rs @@ -116,6 +116,14 @@ impl ChainHandle for ProdChainHandle { }) } + fn query_upgraded_client_state( + &self, + _client_id: &ClientId, + _height: Height, + ) -> Result<(AnyClientState, MerkleProof), Error> { + unimplemented!() + } + fn query_commitment_prefix(&self) -> Result { self.send(|reply_to| ChainRequest::QueryCommitmentPrefix { reply_to }) } From 85f4fee72595fdd5885e32d20ec81a5cb07aea60 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 4 Mar 2021 10:26:56 +0100 Subject: [PATCH 05/26] Method & support for querying ugpraded client state --- modules/src/ics24_host/mod.rs | 3 +- modules/src/ics24_host/path.rs | 31 ++++++++++-- relayer-cli/src/commands/tx/client.rs | 4 +- relayer/src/chain.rs | 5 ++ relayer/src/chain/cosmos.rs | 73 ++++++++++++++++++++++++++- relayer/src/chain/handle.rs | 6 ++- relayer/src/chain/handle/prod.rs | 5 +- relayer/src/chain/mock.rs | 7 +++ relayer/src/chain/runtime.rs | 21 ++++++++ relayer/src/error.rs | 4 ++ 10 files changed, 148 insertions(+), 11 deletions(-) diff --git a/modules/src/ics24_host/mod.rs b/modules/src/ics24_host/mod.rs index 90c28b9033..1720ae40df 100644 --- a/modules/src/ics24_host/mod.rs +++ b/modules/src/ics24_host/mod.rs @@ -1,7 +1,8 @@ //! ICS 24: Host Requirements +pub use path::{Path, IBC_QUERY_PATH, SDK_CLIENT_UPGRADE_PATH}; + pub mod error; pub mod identifier; mod path; -pub use path::{Path, IBC_QUERY_PATH}; pub mod validate; diff --git a/modules/src/ics24_host/path.rs b/modules/src/ics24_host/path.rs index 69f73c94b4..96a643f1db 100644 --- a/modules/src/ics24_host/path.rs +++ b/modules/src/ics24_host/path.rs @@ -1,14 +1,27 @@ -use crate::ics04_channel::packet::Sequence; /// Path-space as listed in ICS-024 /// https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements#path-space /// Some of these are implemented in other ICSs, but ICS-024 has a nice summary table. /// -use crate::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use std::fmt::{Display, Formatter, Result}; -/// IBC Query Path is hard-coded +use crate::ics04_channel::packet::Sequence; +use crate::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; + +/// ABCI Query path for the IBC sub-store pub const IBC_QUERY_PATH: &str = "store/ibc/key"; +/// ABCI Query path for the upgrade sub-store +/// ## Note: This is SDK/Tendermint specific! +pub const SDK_CLIENT_UPGRADE_PATH: &str = "store/upgrade/key"; + +/// ABCI client upgrade keys +/// - The key identifying the upgraded IBC state within the upgrade sub-store +const UPGRADED_IBC_STATE: &str = "upgradedIBCState"; +///- The key identifying the upgraded client state +const UPGRADED_CLIENT_STATE: &str = "upgradedClient"; +/// - The key identifying the upgraded consensus state +const UPGRADED_CLIENT_CONSENSUS_STATE: &str = "upgradedConsState"; + /// The Path enum abstracts out the different sub-paths #[derive(Clone, Debug)] pub enum Path { @@ -19,6 +32,8 @@ pub enum Path { epoch: u64, height: u64, }, + UpgradedClientState(u64), + UpgradedClientConsensusState(u64), ClientConnections(ClientId), Connections(ConnectionId), Ports(PortId), @@ -71,6 +86,16 @@ impl Display for Path { "clients/{}/consensusStates/{}-{}", client_id, epoch, height ), + Path::UpgradedClientState(height) => write!( + f, + "{}/{}/{}", + UPGRADED_IBC_STATE, height, UPGRADED_CLIENT_STATE + ), + Path::UpgradedClientConsensusState(height) => write!( + f, + "{}/{}/{}", + UPGRADED_IBC_STATE, height, UPGRADED_CLIENT_CONSENSUS_STATE + ), Path::ClientConnections(client_id) => write!(f, "clients/{}/connections", client_id), Path::Connections(connection_id) => write!(f, "connections/{}", connection_id), Path::Ports(port_id) => write!(f, "ports/{}", port_id), diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index 6a7fb17f3d..015eb04d46 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -1,6 +1,7 @@ use abscissa_core::{Command, Options, Runnable}; use ibc::events::IbcEvent; +use ibc::ics02_client::height::Height; use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc_relayer::config::StoreConfig; use ibc_relayer::foreign_client::ForeignClient; @@ -9,7 +10,6 @@ use crate::application::app_config; use crate::commands::cli_utils::{ChainHandlePair, SpawnOptions}; use crate::conclude::{exit_with_unrecoverable_error, Output}; use crate::error::{Error, Kind}; -use ibc::ics02_client::height::Height; #[derive(Clone, Command, Debug, Options)] pub struct TxCreateClientCmd { @@ -139,7 +139,7 @@ impl Runnable for TxUpgradeClientCmd { // Query the source chain for the upgraded client state, consensus state & proofs let (client_state, proof) = chains .src - .query_upgraded_client_state(&self.client_id, Height::default()) + .query_upgraded_client_state(Height::default()) .map_err(|e| Kind::Tx.context(e)) .unwrap_or_else(exit_with_unrecoverable_error); diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index b65781f1b8..27a8b524b8 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -130,6 +130,11 @@ pub trait Chain: Sized { height: ICSHeight, ) -> Result; + fn query_upgraded_client_state( + &self, + height: ICSHeight, + ) -> Result<(Self::ClientState, MerkleProof), Error>; + /// Performs a query to retrieve the identifiers of all connections. fn query_connections( &self, diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index 19e1361aed..c4ef8ea6ec 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -34,13 +34,14 @@ use ibc::ics23_commitment::merkle::convert_tm_to_ics_merkle_proof; use ibc::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; use ibc::ics24_host::Path::ClientConsensusState as ClientConsensusPath; use ibc::ics24_host::Path::ClientState as ClientStatePath; -use ibc::ics24_host::{Path, IBC_QUERY_PATH}; +use ibc::ics24_host::{Path, IBC_QUERY_PATH, SDK_CLIENT_UPGRADE_PATH}; use ibc::Height as ICSHeight; // Support for GRPC use ibc_proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountRequest}; use ibc_proto::cosmos::base::v1beta1::Coin; use ibc_proto::cosmos::tx::v1beta1::mode_info::{Single, Sum}; use ibc_proto::cosmos::tx::v1beta1::{AuthInfo, Fee, ModeInfo, SignDoc, SignerInfo, TxBody, TxRaw}; +use ibc_proto::cosmos::upgrade::v1beta1::QueryCurrentPlanRequest; use ibc_proto::ibc::core::channel::v1::{ PacketState, QueryChannelsRequest, QueryConnectionChannelsRequest, QueryNextSequenceReceiveRequest, QueryPacketAcknowledgementsRequest, @@ -261,6 +262,28 @@ impl CosmosSdkChain { Ok(response) } + + fn query_client_upgrade_proof( + &self, + data: Path, + height: Height, + ) -> Result<(MerkleProof, ICSHeight), Error> { + let prev_height = + Height::try_from(height.value() - 1).map_err(|e| Kind::InvalidHeight.context(e))?; + + let path = TendermintABCIPath::from_str(SDK_CLIENT_UPGRADE_PATH).unwrap(); + let response = + self.block_on(abci_query(&self, path, data.to_string(), prev_height, true))?; + + let proof = response.proof.ok_or(Kind::EmptyResponseProof)?; + + let height = ICSHeight::new( + self.config.id.version(), + response.height.increment().value(), + ); + + Ok((proof, height)) + } } impl Chain for CosmosSdkChain { @@ -471,6 +494,54 @@ impl Chain for CosmosSdkChain { Ok(client_state) } + fn query_upgraded_client_state( + &self, + height: ICSHeight, + ) -> Result<(Self::ClientState, MerkleProof), Error> { + crate::time!("query_upgraded_client_state"); + + let grpc_address = + Uri::from_str(&self.config.grpc_addr).map_err(|e| Kind::Grpc.context(e))?; + + let mut client = self + .block_on( + ibc_proto::cosmos::upgrade::v1beta1::query_client::QueryClient::connect( + grpc_address, + ), + ) + .map_err(|e| Kind::Grpc.context(e))?; + + let req = tonic::Request::new(QueryCurrentPlanRequest {}); + let response = self + .block_on(client.current_plan(req)) + .map_err(|e| Kind::Grpc.context(e))?; + + let upgraded_client_state_raw = response + .into_inner() + .plan + .ok_or(Kind::EmptyResponseValue)? + .upgraded_client_state + .ok_or(Kind::EmptyUpgradedClientState)?; + let client_state = AnyClientState::try_from(upgraded_client_state_raw) + .map_err(|e| Kind::Grpc.context(e))?; + + // TODO: Better error kinds here. + let tm_client_state = + downcast!(client_state => AnyClientState::Tendermint).ok_or_else(|| { + Kind::Query("client state".into()).context("unexpected client state type") + })?; + + // Query for the proof. + let tm_height = + Height::try_from(height.revision_height).map_err(|e| Kind::InvalidHeight.context(e))?; + let (proof, _proof_height) = self.query_client_upgrade_proof( + Path::UpgradedClientState(height.revision_height), + tm_height, + )?; + + Ok((tm_client_state, proof)) + } + /// Performs a query to retrieve the identifiers of all connections. fn query_client_connections( &self, diff --git a/relayer/src/chain/handle.rs b/relayer/src/chain/handle.rs index cd7f4b6a0f..79bd48b9b0 100644 --- a/relayer/src/chain/handle.rs +++ b/relayer/src/chain/handle.rs @@ -110,6 +110,11 @@ pub enum ChainRequest { reply_to: ReplyTo, }, + QueryUpgradedClientState { + height: Height, + reply_to: ReplyTo<(AnyClientState, MerkleProof)>, + }, + QueryCommitmentPrefix { reply_to: ReplyTo, }, @@ -226,7 +231,6 @@ pub trait ChainHandle: DynClone + Send + Sync + Debug { fn query_upgraded_client_state( &self, - client_id: &ClientId, height: Height, ) -> Result<(AnyClientState, MerkleProof), Error>; diff --git a/relayer/src/chain/handle/prod.rs b/relayer/src/chain/handle/prod.rs index 5e3ff06230..f52048eeb9 100644 --- a/relayer/src/chain/handle/prod.rs +++ b/relayer/src/chain/handle/prod.rs @@ -118,10 +118,9 @@ impl ChainHandle for ProdChainHandle { fn query_upgraded_client_state( &self, - _client_id: &ClientId, - _height: Height, + height: Height, ) -> Result<(AnyClientState, MerkleProof), Error> { - unimplemented!() + self.send(|reply_to| ChainRequest::QueryUpgradedClientState { height, reply_to }) } fn query_commitment_prefix(&self) -> Result { diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 425c89f05e..178290fb1a 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -147,6 +147,13 @@ impl Chain for MockChain { Ok(client_state) } + fn query_upgraded_client_state( + &self, + _height: Height, + ) -> Result<(Self::ClientState, MerkleProof), Error> { + unimplemented!() + } + fn query_connection( &self, _connection_id: &ConnectionId, diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index e68ebed523..a266647785 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -216,6 +216,10 @@ impl ChainRuntime { self.query_client_state(client_id, height, reply_to)? }, + Ok(ChainRequest::QueryUpgradedClientState { height, reply_to }) => { + self.query_upgraded_client_state(height, reply_to)? + } + Ok(ChainRequest::QueryCommitmentPrefix { reply_to }) => { self.query_commitment_prefix(reply_to)? }, @@ -465,6 +469,23 @@ impl ChainRuntime { Ok(()) } + fn query_upgraded_client_state( + &self, + height: Height, + reply_to: ReplyTo<(AnyClientState, MerkleProof)>, + ) -> Result<(), Error> { + let result = self + .chain + .query_upgraded_client_state(height) + .map(|(cs, proof)| (cs.wrap_any(), proof)); + + reply_to + .send(result) + .map_err(|e| Kind::Channel.context(e))?; + + Ok(()) + } + fn query_commitment_prefix(&self, reply_to: ReplyTo) -> Result<(), Error> { let prefix = self.chain.query_commitment_prefix(); diff --git a/relayer/src/error.rs b/relayer/src/error.rs index cbc73adb09..19b378c403 100644 --- a/relayer/src/error.rs +++ b/relayer/src/error.rs @@ -48,6 +48,10 @@ pub enum Kind { #[error("Bad Notification")] Event, + /// Missing ClientState in the upgrade CurrentPlan + #[error("The upgrade plan specifies no upgraded client state")] + EmptyUpgradedClientState, + /// Response does not contain data #[error("Empty response value")] EmptyResponseValue, From 83b7ff5ebe5108bc45b954227e2a0323d72d154d Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 4 Mar 2021 11:35:56 +0100 Subject: [PATCH 06/26] Support for querying the upgraded consensus state --- modules/src/ics24_host/mod.rs | 2 +- modules/src/ics24_host/path.rs | 32 +++++++----- relayer-cli/src/commands/tx/client.rs | 19 +++++-- relayer/src/chain.rs | 5 ++ relayer/src/chain/cosmos.rs | 74 ++++++++++++++++++++++++--- relayer/src/chain/handle.rs | 10 ++++ relayer/src/chain/handle/prod.rs | 7 +++ relayer/src/chain/runtime.rs | 21 ++++++++ 8 files changed, 144 insertions(+), 26 deletions(-) diff --git a/modules/src/ics24_host/mod.rs b/modules/src/ics24_host/mod.rs index 1720ae40df..3f3fea5298 100644 --- a/modules/src/ics24_host/mod.rs +++ b/modules/src/ics24_host/mod.rs @@ -1,6 +1,6 @@ //! ICS 24: Host Requirements -pub use path::{Path, IBC_QUERY_PATH, SDK_CLIENT_UPGRADE_PATH}; +pub use path::{ClientUpgradePath, Path, IBC_QUERY_PATH, SDK_UPGRADE_QUERY_PATH}; pub mod error; pub mod identifier; diff --git a/modules/src/ics24_host/path.rs b/modules/src/ics24_host/path.rs index 96a643f1db..ffcda8accd 100644 --- a/modules/src/ics24_host/path.rs +++ b/modules/src/ics24_host/path.rs @@ -12,7 +12,7 @@ pub const IBC_QUERY_PATH: &str = "store/ibc/key"; /// ABCI Query path for the upgrade sub-store /// ## Note: This is SDK/Tendermint specific! -pub const SDK_CLIENT_UPGRADE_PATH: &str = "store/upgrade/key"; +pub const SDK_UPGRADE_QUERY_PATH: &str = "store/upgrade/key"; /// ABCI client upgrade keys /// - The key identifying the upgraded IBC state within the upgrade sub-store @@ -32,8 +32,6 @@ pub enum Path { epoch: u64, height: u64, }, - UpgradedClientState(u64), - UpgradedClientConsensusState(u64), ClientConnections(ClientId), Connections(ConnectionId), Ports(PortId), @@ -56,6 +54,14 @@ pub enum Path { channel_id: ChannelId, sequence: Sequence, }, + Upgrade(ClientUpgradePath), +} + +/// Paths that are specific for client upgrades. +#[derive(Clone, Debug)] +pub enum ClientUpgradePath { + UpgradedClientState(u64), + UpgradedClientConsensusState(u64), } impl Path { @@ -86,16 +92,6 @@ impl Display for Path { "clients/{}/consensusStates/{}-{}", client_id, epoch, height ), - Path::UpgradedClientState(height) => write!( - f, - "{}/{}/{}", - UPGRADED_IBC_STATE, height, UPGRADED_CLIENT_STATE - ), - Path::UpgradedClientConsensusState(height) => write!( - f, - "{}/{}/{}", - UPGRADED_IBC_STATE, height, UPGRADED_CLIENT_CONSENSUS_STATE - ), Path::ClientConnections(client_id) => write!(f, "clients/{}/connections", client_id), Path::Connections(connection_id) => write!(f, "connections/{}", connection_id), Path::Ports(port_id) => write!(f, "ports/{}", port_id), @@ -144,6 +140,16 @@ impl Display for Path { "receipts/ports/{}/channels/{}/sequences/{}", port_id, channel_id, sequence ), + Path::Upgrade(ClientUpgradePath::UpgradedClientState(height)) => write!( + f, + "{}/{}/{}", + UPGRADED_IBC_STATE, height, UPGRADED_CLIENT_STATE + ), + Path::Upgrade(ClientUpgradePath::UpgradedClientConsensusState(height)) => write!( + f, + "{}/{}/{}", + UPGRADED_IBC_STATE, height, UPGRADED_CLIENT_CONSENSUS_STATE + ), } } } diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index 015eb04d46..4262baad30 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -1,7 +1,6 @@ use abscissa_core::{Command, Options, Runnable}; use ibc::events::IbcEvent; -use ibc::ics02_client::height::Height; use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc_relayer::config::StoreConfig; use ibc_relayer::foreign_client::ForeignClient; @@ -136,10 +135,22 @@ impl Runnable for TxUpgradeClientCmd { ) .unwrap_or_else(exit_with_unrecoverable_error); - // Query the source chain for the upgraded client state, consensus state & proofs - let (client_state, proof) = chains + // Fetch the latest height of the source chain. + let src_height = chains .src - .query_upgraded_client_state(Height::default()) + .query_latest_height() + .unwrap_or_else(exit_with_unrecoverable_error); + + // Query the source chain for the upgraded client state, consensus state & their proofs. + let (client_state, proof_client_state) = chains + .src + .query_upgraded_client_state(src_height) + .map_err(|e| Kind::Tx.context(e)) + .unwrap_or_else(exit_with_unrecoverable_error); + + let (consensus_state, proof_consensus_state) = chains + .src + .query_upgraded_consensus_state(src_height) .map_err(|e| Kind::Tx.context(e)) .unwrap_or_else(exit_with_unrecoverable_error); diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 27a8b524b8..16728b167f 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -135,6 +135,11 @@ pub trait Chain: Sized { height: ICSHeight, ) -> Result<(Self::ClientState, MerkleProof), Error>; + fn query_upgraded_consensus_state( + &self, + height: ICSHeight, + ) -> Result<(Self::ConsensusState, MerkleProof), Error>; + /// Performs a query to retrieve the identifiers of all connections. fn query_connections( &self, diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index c4ef8ea6ec..77b7853195 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -34,14 +34,16 @@ use ibc::ics23_commitment::merkle::convert_tm_to_ics_merkle_proof; use ibc::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; use ibc::ics24_host::Path::ClientConsensusState as ClientConsensusPath; use ibc::ics24_host::Path::ClientState as ClientStatePath; -use ibc::ics24_host::{Path, IBC_QUERY_PATH, SDK_CLIENT_UPGRADE_PATH}; +use ibc::ics24_host::{ClientUpgradePath, Path, IBC_QUERY_PATH, SDK_UPGRADE_QUERY_PATH}; use ibc::Height as ICSHeight; // Support for GRPC use ibc_proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountRequest}; use ibc_proto::cosmos::base::v1beta1::Coin; use ibc_proto::cosmos::tx::v1beta1::mode_info::{Single, Sum}; use ibc_proto::cosmos::tx::v1beta1::{AuthInfo, Fee, ModeInfo, SignDoc, SignerInfo, TxBody, TxRaw}; -use ibc_proto::cosmos::upgrade::v1beta1::QueryCurrentPlanRequest; +use ibc_proto::cosmos::upgrade::v1beta1::{ + QueryCurrentPlanRequest, QueryUpgradedConsensusStateRequest, +}; use ibc_proto::ibc::core::channel::v1::{ PacketState, QueryChannelsRequest, QueryConnectionChannelsRequest, QueryNextSequenceReceiveRequest, QueryPacketAcknowledgementsRequest, @@ -263,17 +265,23 @@ impl CosmosSdkChain { Ok(response) } + // Perform an ABCI query against the client upgrade sub-store. fn query_client_upgrade_proof( &self, - data: Path, + data: ClientUpgradePath, height: Height, ) -> Result<(MerkleProof, ICSHeight), Error> { let prev_height = Height::try_from(height.value() - 1).map_err(|e| Kind::InvalidHeight.context(e))?; - let path = TendermintABCIPath::from_str(SDK_CLIENT_UPGRADE_PATH).unwrap(); - let response = - self.block_on(abci_query(&self, path, data.to_string(), prev_height, true))?; + let path = TendermintABCIPath::from_str(SDK_UPGRADE_QUERY_PATH).unwrap(); + let response = self.block_on(abci_query( + &self, + path, + Path::Upgrade(data).to_string(), + prev_height, + true, + ))?; let proof = response.proof.ok_or(Kind::EmptyResponseProof)?; @@ -528,20 +536,70 @@ impl Chain for CosmosSdkChain { // TODO: Better error kinds here. let tm_client_state = downcast!(client_state => AnyClientState::Tendermint).ok_or_else(|| { - Kind::Query("client state".into()).context("unexpected client state type") + Kind::Query("upgraded client state".into()).context("unexpected client state type") })?; // Query for the proof. let tm_height = Height::try_from(height.revision_height).map_err(|e| Kind::InvalidHeight.context(e))?; let (proof, _proof_height) = self.query_client_upgrade_proof( - Path::UpgradedClientState(height.revision_height), + ClientUpgradePath::UpgradedClientState(height.revision_height), tm_height, )?; Ok((tm_client_state, proof)) } + fn query_upgraded_consensus_state( + &self, + height: ICSHeight, + ) -> Result<(Self::ConsensusState, MerkleProof), Error> { + crate::time!("query_upgraded_consensus_state"); + + let tm_height = + Height::try_from(height.revision_height).map_err(|e| Kind::InvalidHeight.context(e))?; + + let grpc_address = + Uri::from_str(&self.config.grpc_addr).map_err(|e| Kind::Grpc.context(e))?; + + let mut client = self + .block_on( + ibc_proto::cosmos::upgrade::v1beta1::query_client::QueryClient::connect( + grpc_address, + ), + ) + .map_err(|e| Kind::Grpc.context(e))?; + + let req = tonic::Request::new(QueryUpgradedConsensusStateRequest { + last_height: tm_height.into(), + }); + let response = self + .block_on(client.upgraded_consensus_state(req)) + .map_err(|e| Kind::Grpc.context(e))?; + + let upgraded_consensus_state_raw = response + .into_inner() + .upgraded_consensus_state + .ok_or(Kind::EmptyResponseValue)?; + // TODO: More explicit error kinds (should not reuse Grpc all over the place. + let consensus_state = AnyConsensusState::try_from(upgraded_consensus_state_raw) + .map_err(|e| Kind::Grpc.context(e))?; + + let tm_consensus_state = downcast!(consensus_state => AnyConsensusState::Tendermint) + .ok_or_else(|| { + Kind::Query("upgraded consensus state".into()) + .context("unexpected consensus state type") + })?; + + // Fetch the proof. + let (proof, _proof_height) = self.query_client_upgrade_proof( + ClientUpgradePath::UpgradedClientConsensusState(height.revision_height), + tm_height, + )?; + + Ok((tm_consensus_state, proof)) + } + /// Performs a query to retrieve the identifiers of all connections. fn query_client_connections( &self, diff --git a/relayer/src/chain/handle.rs b/relayer/src/chain/handle.rs index 79bd48b9b0..ccdfb6cef7 100644 --- a/relayer/src/chain/handle.rs +++ b/relayer/src/chain/handle.rs @@ -115,6 +115,11 @@ pub enum ChainRequest { reply_to: ReplyTo<(AnyClientState, MerkleProof)>, }, + QueryUpgradedConsensusState { + height: Height, + reply_to: ReplyTo<(AnyConsensusState, MerkleProof)>, + }, + QueryCommitmentPrefix { reply_to: ReplyTo, }, @@ -234,6 +239,11 @@ pub trait ChainHandle: DynClone + Send + Sync + Debug { height: Height, ) -> Result<(AnyClientState, MerkleProof), Error>; + fn query_upgraded_consensus_state( + &self, + height: Height, + ) -> Result<(AnyConsensusState, MerkleProof), Error>; + fn query_commitment_prefix(&self) -> Result; fn query_compatible_versions(&self) -> Result, Error>; diff --git a/relayer/src/chain/handle/prod.rs b/relayer/src/chain/handle/prod.rs index f52048eeb9..fffc219452 100644 --- a/relayer/src/chain/handle/prod.rs +++ b/relayer/src/chain/handle/prod.rs @@ -123,6 +123,13 @@ impl ChainHandle for ProdChainHandle { self.send(|reply_to| ChainRequest::QueryUpgradedClientState { height, reply_to }) } + fn query_upgraded_consensus_state( + &self, + height: Height, + ) -> Result<(AnyConsensusState, MerkleProof), Error> { + self.send(|reply_to| ChainRequest::QueryUpgradedConsensusState { height, reply_to }) + } + fn query_commitment_prefix(&self) -> Result { self.send(|reply_to| ChainRequest::QueryCommitmentPrefix { reply_to }) } diff --git a/relayer/src/chain/runtime.rs b/relayer/src/chain/runtime.rs index a266647785..b67ee8d0aa 100644 --- a/relayer/src/chain/runtime.rs +++ b/relayer/src/chain/runtime.rs @@ -220,6 +220,10 @@ impl ChainRuntime { self.query_upgraded_client_state(height, reply_to)? } + Ok(ChainRequest::QueryUpgradedConsensusState { height, reply_to }) => { + self.query_upgraded_consensus_state(height, reply_to)? + } + Ok(ChainRequest::QueryCommitmentPrefix { reply_to }) => { self.query_commitment_prefix(reply_to)? }, @@ -477,6 +481,23 @@ impl ChainRuntime { let result = self .chain .query_upgraded_client_state(height) + .map(|(cl, proof)| (cl.wrap_any(), proof)); + + reply_to + .send(result) + .map_err(|e| Kind::Channel.context(e))?; + + Ok(()) + } + + fn query_upgraded_consensus_state( + &self, + height: Height, + reply_to: ReplyTo<(AnyConsensusState, MerkleProof)>, + ) -> Result<(), Error> { + let result = self + .chain + .query_upgraded_consensus_state(height) .map(|(cs, proof)| (cs.wrap_any(), proof)); reply_to From 861a4ff1d1028ecb83d58b0d5d5b8e6449abd75f Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 5 Mar 2021 09:41:09 +0100 Subject: [PATCH 07/26] Minor dev scripts enhancements & bugs --- scripts/dev-env | 4 ++-- scripts/one-chain | 2 ++ scripts/setup-chains | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/dev-env b/scripts/dev-env index a5631a1c6f..1bda9bac2e 100755 --- a/scripts/dev-env +++ b/scripts/dev-env @@ -11,8 +11,8 @@ missing() { usage } -if [ -z "$1" ]; then - missing "CONFIG_FILE" +if [ ! -r "$1" ]; then + missing "CONFIG_FILE ($1)" fi if [ -z "$2" ]; then diff --git a/scripts/one-chain b/scripts/one-chain index ede5bbd35b..2f9f10badc 100755 --- a/scripts/one-chain +++ b/scripts/one-chain @@ -133,5 +133,7 @@ $BINARY --home $CHAIN_DIR/$CHAIN_ID start --pruning=nothing --grpc.address="0.0. # Show validator's and user's balance sleep 3 RPC_ADDR="tcp://localhost:$RPC_PORT" +echo "Balances for validator '$VALIDATOR' @ '$RPC_ADDR'" $BINARY --node $RPC_ADDR query bank balances $VALIDATOR +echo "Balances for user '$USER' @ '$RPC_ADDR'" $BINARY --node $RPC_ADDR query bank balances $USER diff --git a/scripts/setup-chains b/scripts/setup-chains index 8906f255b0..0f71bb4c17 100755 --- a/scripts/setup-chains +++ b/scripts/setup-chains @@ -63,9 +63,9 @@ mkdir -p "$GAIA_DATA" && cd "$GAIA_DATA" && cd ../ ONE_CHAIN="$(dirname "$0")/one-chain" CHAIN_0_RPC_PORT=26657 -CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" +#CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" CHAIN_1_RPC_PORT=26557 -CHAIN_1_RPC_ADDR="localhost:$CHAIN_1_RPC_PORT" +#CHAIN_1_RPC_ADDR="localhost:$CHAIN_1_RPC_PORT" CHAIN_0_SAMOLEANS=100000000000 CHAIN_1_SAMOLEANS=100000000000 From 1948fa4f63c4fa779f3d342ebd9d2114be23cd7a Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 8 Mar 2021 14:23:17 +0100 Subject: [PATCH 08/26] Added guide. Uses patched Go relayer --- guide/src/upgrade_test.md | 111 ++++++++++++++++++++++++++ relayer-cli/src/commands/tx/client.rs | 10 +++ 2 files changed, 121 insertions(+) create mode 100644 guide/src/upgrade_test.md diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md new file mode 100644 index 0000000000..4d9e395999 --- /dev/null +++ b/guide/src/upgrade_test.md @@ -0,0 +1,111 @@ +1. Patch the `one-chain` script of rrly: + + +```diff +diff --git a/scripts/one-chain b/scripts/one-chain +index d0995fe..3702a88 100755 +--- a/scripts/one-chain ++++ b/scripts/one-chain +@@ -99,6 +99,7 @@ if [ $platform = 'linux' ]; then + sed -i 's/index_all_keys = false/index_all_keys = true/g' $CHAINDIR/$CHAINID/config/config.toml + # sed -i '' 's#index-events = \[\]#index-events = \["message.action","send_packet.packet_src_channel","send_packet.packet_sequence"\]#g' $CHAINDIR/$CHAINID/config/app.toml + else ++ sed -i '' 's#"172800s"#"200s"#g' $CHAINDIR/$CHAINID/config/genesis.json + sed -i '' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPCPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml + sed -i '' 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml + sed -i '' 's#"localhost:6060"#"localhost:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml +``` + + +2. Start two gaia instances using the patched developer environment: + + +```shell +./scripts/two-chainz +``` + +3. Setup the Go relayer for these chains: +```shell +rly tx link demo -d -o 3s +``` + +Check that everything went fine so far: + +```shell +$ rly paths list + 0: demo -> chns(✔) clnts(✔) conn(✔) chan(✔) (ibc-0:transfer<>ibc-1:transfer) +``` + +4. Create the upgrade plan for chain ibc-0: + +It's important that we parametrize the upgrade plan with a height parameter that +is at least 300 heights ahead of the current height of chain ibc-0. + +First, obtain the current height: +```shell +gaiad query block | jq | grep height + "height": "470", +``` + +Now create the upgrade plan for height 800: +```shell +echo '{ + "Name": "test", + "Height": 800, + "Info": "" +}' > ./upgrade-plan.json +``` + +5. Query for the upgrade plan, check that it was submitted correctly + +```shell +$ gaiad query gov proposal 1 --home data/ibc-0/ + +content: + '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal + description: upgrade the chain's software and unbonding period + plan: + height: "800" + info: "" + name: test +.... +proposal_id: "1" +status: PROPOSAL_STATUS_VOTING_PERIOD +submit_time: "2021-03-08T13:07:01.417163Z" +total_deposit: +- amount: "10000000" + denom: stake +voting_end_time: "2021-03-08T13:10:21.417163Z" +voting_start_time: "2021-03-08T13:07:01.417163Z" +``` + +6. Vote on the proposal + +The parameter "1" should match the "proposal_id:" from the upgrade proposal +we submitted at step 5. + +```shell +gaiad tx gov vote 1 yes --home data/ibc-0/data/ --keyring-backend test --keyring-dir data/ibc-0/ --chain-id ibc-0 --from validator +``` + +Once ibc-0 reaches height 800, it should stop executing. + + +7. Initialize and test Hermes + + +```shell +./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 + Building the Rust relayer... + Removing light client peers from configuration... + Adding primary peers to light client configuration... + Adding secondary peers to light client configuration... + Importing keys... + Done! +``` + +The following command should output the upgraded client and consensus states +with their proofs: +```shell +hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 +``` \ No newline at end of file diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index 4262baad30..af8f49609c 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -1,4 +1,5 @@ use abscissa_core::{Command, Options, Runnable}; +use tracing::info; use ibc::events::IbcEvent; use ibc::ics24_host::identifier::{ChainId, ClientId}; @@ -122,6 +123,7 @@ pub struct TxUpgradeClientCmd { client_id: ClientId, } + impl Runnable for TxUpgradeClientCmd { fn run(&self) { let config = app_config(); @@ -135,12 +137,16 @@ impl Runnable for TxUpgradeClientCmd { ) .unwrap_or_else(exit_with_unrecoverable_error); + info!("Started the chain runtimes"); + // Fetch the latest height of the source chain. let src_height = chains .src .query_latest_height() .unwrap_or_else(exit_with_unrecoverable_error); + info!("Height: {}", src_height); + // Query the source chain for the upgraded client state, consensus state & their proofs. let (client_state, proof_client_state) = chains .src @@ -148,12 +154,16 @@ impl Runnable for TxUpgradeClientCmd { .map_err(|e| Kind::Tx.context(e)) .unwrap_or_else(exit_with_unrecoverable_error); + info!("Client state {:?} & proof {:?}", client_state, proof_client_state); + let (consensus_state, proof_consensus_state) = chains .src .query_upgraded_consensus_state(src_height) .map_err(|e| Kind::Tx.context(e)) .unwrap_or_else(exit_with_unrecoverable_error); + info!("Client consensus state {:?} & proof {:?}", consensus_state, proof_consensus_state); + // match res { // Ok(receipt) => Output::success(receipt).exit(), // Err(e) => Output::error(format!("{}", e)).exit(), From b41560d7db1f6a5a34f05db0504179ebf3930f24 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 8 Mar 2021 16:55:38 +0100 Subject: [PATCH 09/26] Proto conversion for upgrade msg. Refactored upgrade() impl --- .../src/ics02_client/msgs/upgrade_client.rs | 24 +++++- modules/src/tx_msg.rs | 3 +- relayer-cli/src/commands/tx/client.rs | 35 ++------ relayer/src/chain/cosmos.rs | 2 +- relayer/src/chain/mock.rs | 4 + relayer/src/foreign_client.rs | 79 +++++++++++++++++++ scripts/setup-chains | 2 - 7 files changed, 116 insertions(+), 33 deletions(-) diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs index b92e76b732..1ccef2a239 100644 --- a/modules/src/ics02_client/msgs/upgrade_client.rs +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -2,7 +2,11 @@ use tendermint::account::Id as AccountId; +use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; +use ibc_proto::ibc::core::commitment::v1::MerkleProof; + use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::ics23_commitment::commitment::CommitmentProofBytes; use crate::ics24_host::identifier::ClientId; use crate::tx_msg::Msg; @@ -14,8 +18,8 @@ pub struct MsgUpgradeAnyClient { pub client_id: ClientId, pub client_state: AnyClientState, pub consensus_state: AnyConsensusState, - pub proof_upgrade_client: String, - pub proof_upgrade_consensus_state: String, + pub proof_upgrade_client: MerkleProof, + pub proof_upgrade_consensus_state: MerkleProof, } impl Msg for MsgUpgradeAnyClient { @@ -33,3 +37,19 @@ impl Msg for MsgUpgradeAnyClient { unimplemented!() } } + +impl From for RawMsgUpgradeClient { + fn from(dm_msg: MsgUpgradeAnyClient) -> RawMsgUpgradeClient { + let c_bytes: CommitmentProofBytes = dm_msg.proof_upgrade_client.into(); + let cs_bytes: CommitmentProofBytes = dm_msg.proof_upgrade_consensus_state.into(); + + RawMsgUpgradeClient { + client_id: dm_msg.client_id.to_string(), + client_state: Some(dm_msg.client_state.into()), + consensus_state: Some(dm_msg.consensus_state.into()), + proof_upgrade_client: c_bytes.into(), + proof_upgrade_consensus_state: cs_bytes.into(), + signer: "".into(), + } + } +} diff --git a/modules/src/tx_msg.rs b/modules/src/tx_msg.rs index 14e80b501b..382bef509e 100644 --- a/modules/src/tx_msg.rs +++ b/modules/src/tx_msg.rs @@ -1,7 +1,8 @@ -use crate::ics24_host::error::ValidationError; use prost_types::Any; use tendermint::account::Id as AccountId; +use crate::ics24_host::error::ValidationError; + pub trait Msg: Clone { type ValidationError: std::error::Error; diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index af8f49609c..204a67aa73 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -123,7 +123,6 @@ pub struct TxUpgradeClientCmd { client_id: ClientId, } - impl Runnable for TxUpgradeClientCmd { fn run(&self) { let config = app_config(); @@ -139,34 +138,16 @@ impl Runnable for TxUpgradeClientCmd { info!("Started the chain runtimes"); - // Fetch the latest height of the source chain. - let src_height = chains - .src - .query_latest_height() + // Instantiate the client hosted on the destination chain, which is targeting headers for + // the source chain. + let client = ForeignClient::find(chains.src, chains.dst, &self.client_id) .unwrap_or_else(exit_with_unrecoverable_error); - info!("Height: {}", src_height); - - // Query the source chain for the upgraded client state, consensus state & their proofs. - let (client_state, proof_client_state) = chains - .src - .query_upgraded_client_state(src_height) - .map_err(|e| Kind::Tx.context(e)) - .unwrap_or_else(exit_with_unrecoverable_error); + let outcome = client.upgrade(); - info!("Client state {:?} & proof {:?}", client_state, proof_client_state); - - let (consensus_state, proof_consensus_state) = chains - .src - .query_upgraded_consensus_state(src_height) - .map_err(|e| Kind::Tx.context(e)) - .unwrap_or_else(exit_with_unrecoverable_error); - - info!("Client consensus state {:?} & proof {:?}", consensus_state, proof_consensus_state); - - // match res { - // Ok(receipt) => Output::success(receipt).exit(), - // Err(e) => Output::error(format!("{}", e)).exit(), - // } + match outcome { + Ok(receipt) => Output::success(format!("")).exit(), + Err(e) => Output::error(format!("{}", e)).exit(), + } } } diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index 77b7853195..d39bfd3194 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -265,7 +265,7 @@ impl CosmosSdkChain { Ok(response) } - // Perform an ABCI query against the client upgrade sub-store. + // Perform an ABCI query against the client upgrade sub-store to fetch a proof. fn query_client_upgrade_proof( &self, data: ClientUpgradePath, diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 178290fb1a..de625d0efe 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -320,6 +320,10 @@ impl Chain for MockChain { trusted_validator_set: trusted_light_block.validators, }) } + + fn query_upgraded_consensus_state(&self, _height: Height) -> Result<(Self::ConsensusState, MerkleProof), Error> { + unimplemented!() + } } // For integration tests with the modules diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index dce1a523cc..3996e5cec2 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -8,6 +8,7 @@ use ibc::events::IbcEvent; use ibc::ics02_client::header::Header; use ibc::ics02_client::msgs::create_client::MsgCreateAnyClient; use ibc::ics02_client::msgs::update_client::MsgUpdateAnyClient; +use ibc::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; use ibc::ics02_client::state::ClientState; use ibc::ics02_client::state::ConsensusState; use ibc::ics24_host::identifier::{ChainId, ClientId}; @@ -15,6 +16,7 @@ use ibc::tx_msg::Msg; use ibc::Height; use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; +use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; use crate::chain::handle::ChainHandle; @@ -31,6 +33,9 @@ pub enum ForeignClientError { #[error("failed while finding client {0}: expected chain_id in client state: {1}; actual chain_id: {2}")] ClientFind(ClientId, ChainId, ChainId), + + #[error("failed while trying to upgrade client id {0} with error: {1}")] + ClientUpgrade(ClientId, String), } #[derive(Clone, Debug)] @@ -111,6 +116,80 @@ impl ForeignClient { } } + pub fn upgrade(&self) -> Result<(), ForeignClientError> { + // Fetch the latest height of the source chain. + let src_height = self.src_chain.query_latest_height().map_err(|e| { + ForeignClientError::ClientUpgrade( + self.id.clone(), + format!( + "failed while querying src chain ({}) for latest height: {}", + self.src_chain.id(), + e + ), + ) + })?; + + info!("Height: {}", src_height); + + // Query the host chain for the upgraded client state, consensus state & their proofs. + let (client_state, proof_upgrade_client) = self + .src_chain + .query_upgraded_client_state(src_height) + .map_err(|e| { + ForeignClientError::ClientUpgrade( + self.id.clone(), + format!( + "failed while fetching from chain {} the upgraded client state: {}", + self.src_chain.id(), + e + ), + ) + })?; + + info!( + "Upgraded client state {:?} & proof {:?}", + client_state, proof_upgrade_client + ); + + let (consensus_state, proof_upgrade_consensus_state) = self + .src_chain + .query_upgraded_consensus_state(src_height) + .map_err(|e| ForeignClientError::ClientUpgrade(self.id.clone(), format!( + "failed while fetching from chain {} the upgraded client consensus state: {}", self.src_chain.id(), e))) + ?; + + info!( + "Upgraded client consensus state {:?} & proof {:?}", + consensus_state, proof_upgrade_consensus_state + ); + + let m = MsgUpgradeAnyClient { + client_id: self.id.clone(), + client_state, + consensus_state, + proof_upgrade_client, + proof_upgrade_consensus_state, + }; + + let res = self + .dst_chain + .send_msgs(vec![m.to_any::()]) + .map_err(|e| { + ForeignClientError::ClientUpgrade( + self.id.clone(), + format!( + "failed while sending message to destination chain {} with err: {}", + self.dst_chain.id(), + e + ), + ) + })?; + + info!("Res: {:?}", res); + + Ok(()) + } + /// Returns a handle to the chain hosting this client. pub fn dst_chain(&self) -> Box { self.dst_chain.clone() diff --git a/scripts/setup-chains b/scripts/setup-chains index 0f71bb4c17..79ac29125a 100755 --- a/scripts/setup-chains +++ b/scripts/setup-chains @@ -63,9 +63,7 @@ mkdir -p "$GAIA_DATA" && cd "$GAIA_DATA" && cd ../ ONE_CHAIN="$(dirname "$0")/one-chain" CHAIN_0_RPC_PORT=26657 -#CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" CHAIN_1_RPC_PORT=26557 -#CHAIN_1_RPC_ADDR="localhost:$CHAIN_1_RPC_PORT" CHAIN_0_SAMOLEANS=100000000000 CHAIN_1_SAMOLEANS=100000000000 From bb99800b02c71dca564e2e5c2d235f8c743311cd Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 9 Mar 2021 11:35:08 +0100 Subject: [PATCH 10/26] Fix missing signer bug --- guide/src/upgrade_test.md | 29 +++++++++++++++++-- .../src/ics02_client/msgs/upgrade_client.rs | 4 ++- relayer/src/chain/cosmos.rs | 2 ++ relayer/src/chain/mock.rs | 5 +++- relayer/src/foreign_client.rs | 23 +++++++++++++-- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index 4d9e395999..467c3be6dc 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -26,7 +26,7 @@ index d0995fe..3702a88 100755 3. Setup the Go relayer for these chains: ```shell -rly tx link demo -d -o 3s +$ rly tx link demo -d -o 3s ``` Check that everything went fine so far: @@ -56,7 +56,14 @@ echo '{ }' > ./upgrade-plan.json ``` -5. Query for the upgrade plan, check that it was submitted correctly + +5. Submit the upgrade plan + +```shell +rly tx upgrade-chain demo ibc-0 400h 10000000stake ./upgrade-plan.json +``` + +Query for the upgrade plan, check that it was submitted correctly: ```shell $ gaiad query gov proposal 1 --home data/ibc-0/ @@ -93,9 +100,25 @@ Once ibc-0 reaches height 800, it should stop executing. 7. Initialize and test Hermes +Patch the hardcoded gaia directory to direct Hermes to the correct gaia dir: +```diff +diff --git a/scripts/init-clients b/scripts/init-clients +index 6cf1a674..bfff9721 100755 +--- a/scripts/init-clients ++++ b/scripts/init-clients +@@ -49,7 +49,7 @@ if ! grep -q -s "$CHAIN_1_ID" "$CONFIG_FILE"; then + usage + fi + +-GAIA_DATA="$(pwd)/data" ++GAIA_DATA="/Users/adi/go/src/github.com/cosmos/relayer/data" + + CHAIN_0_RPC_PORT=26657 + CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" +``` ```shell -./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 +$ ./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 Building the Rust relayer... Removing light client peers from configuration... Adding primary peers to light client configuration... diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs index 1ccef2a239..3c25c508c0 100644 --- a/modules/src/ics02_client/msgs/upgrade_client.rs +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -5,6 +5,7 @@ use tendermint::account::Id as AccountId; use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; use ibc_proto::ibc::core::commitment::v1::MerkleProof; +use crate::address::account_to_string; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics23_commitment::commitment::CommitmentProofBytes; use crate::ics24_host::identifier::ClientId; @@ -20,6 +21,7 @@ pub struct MsgUpgradeAnyClient { pub consensus_state: AnyConsensusState, pub proof_upgrade_client: MerkleProof, pub proof_upgrade_consensus_state: MerkleProof, + pub signer: AccountId, } impl Msg for MsgUpgradeAnyClient { @@ -49,7 +51,7 @@ impl From for RawMsgUpgradeClient { consensus_state: Some(dm_msg.consensus_state.into()), proof_upgrade_client: c_bytes.into(), proof_upgrade_consensus_state: cs_bytes.into(), - signer: "".into(), + signer: account_to_string(dm_msg.signer).unwrap(), } } } diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index d39bfd3194..ec848f4ac8 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -508,6 +508,8 @@ impl Chain for CosmosSdkChain { ) -> Result<(Self::ClientState, MerkleProof), Error> { crate::time!("query_upgraded_client_state"); + println!("Query upgraded client state @ {}", height); + let grpc_address = Uri::from_str(&self.config.grpc_addr).map_err(|e| Kind::Grpc.context(e))?; diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index de625d0efe..76228bd997 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -321,7 +321,10 @@ impl Chain for MockChain { }) } - fn query_upgraded_consensus_state(&self, _height: Height) -> Result<(Self::ConsensusState, MerkleProof), Error> { + fn query_upgraded_consensus_state( + &self, + _height: Height, + ) -> Result<(Self::ConsensusState, MerkleProof), Error> { unimplemented!() } } diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 3996e5cec2..59fdabaa3c 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -163,17 +163,36 @@ impl ForeignClient { consensus_state, proof_upgrade_consensus_state ); - let m = MsgUpgradeAnyClient { + // Get signer + let signer = self.dst_chain.get_signer().map_err(|e| { + ForeignClientError::ClientUpgrade( + self.id.clone(), + format!( + "failed while fetching the destination chain ({}) signer: {}", + self.dst_chain.id(), + e + ), + ) + })?; + + info!( + "Using signer for chain id ({}): {}", + self.dst_chain.id(), + signer.to_string() + ); + + let msg_upgrade = MsgUpgradeAnyClient { client_id: self.id.clone(), client_state, consensus_state, proof_upgrade_client, proof_upgrade_consensus_state, + signer, }; let res = self .dst_chain - .send_msgs(vec![m.to_any::()]) + .send_msgs(vec![msg_upgrade.to_any::()]) .map_err(|e| { ForeignClientError::ClientUpgrade( self.id.clone(), From 8825acc4b68378ff1501546cb217b3428ada58bd Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 9 Mar 2021 12:28:32 +0100 Subject: [PATCH 11/26] Update msg bf. upgrade. Event parsing --- modules/src/events.rs | 1 + modules/src/ics02_client/events.rs | 16 ++++++++ relayer-cli/src/commands/tx/client.rs | 2 +- relayer/src/chain/cosmos.rs | 2 - relayer/src/foreign_client.rs | 54 +++++++++++---------------- 5 files changed, 39 insertions(+), 36 deletions(-) diff --git a/modules/src/events.rs b/modules/src/events.rs index be91855493..d0e41aed51 100644 --- a/modules/src/events.rs +++ b/modules/src/events.rs @@ -112,6 +112,7 @@ impl IbcEvent { IbcEvent::NewBlock(ev) => ev.set_height(height), IbcEvent::CreateClient(ev) => ev.set_height(height), IbcEvent::UpdateClient(ev) => ev.set_height(height), + IbcEvent::UpgradeClient(ev) => ev.set_height(height), IbcEvent::ClientMisbehavior(ev) => ev.set_height(height), IbcEvent::OpenInitConnection(ev) => ev.set_height(height), IbcEvent::OpenTryConnection(ev) => ev.set_height(height), diff --git a/modules/src/ics02_client/events.rs b/modules/src/ics02_client/events.rs index b08af0a6ba..8b93f3768f 100644 --- a/modules/src/ics02_client/events.rs +++ b/modules/src/ics02_client/events.rs @@ -12,6 +12,7 @@ use std::convert::{TryFrom, TryInto}; /// The content of the `type` field for the event that a chain produces upon executing the create client transaction. const CREATE_EVENT_TYPE: &str = "create_client"; const UPDATE_EVENT_TYPE: &str = "update_client"; +const UPGRADE_EVENT_TYPE: &str = "upgrade_client"; /// The content of the `key` field for the attribute containing the client identifier. const CLIENT_ID_ATTRIBUTE_KEY: &str = "client_id"; @@ -30,6 +31,9 @@ pub fn try_from_tx(event: &tendermint::abci::Event) -> Option { UPDATE_EVENT_TYPE => Some(IbcEvent::UpdateClient(UpdateClient( extract_attributes_from_tx(event), ))), + UPGRADE_EVENT_TYPE => Some(IbcEvent::UpgradeClient(UpgradeClient( + extract_attributes_from_tx(event), + ))), _ => None, } } @@ -218,3 +222,15 @@ impl From for IbcEvent { /// Signals a recent upgrade of an on-chain client (IBC Client). #[derive(Debug, Deserialize, Serialize, Clone)] pub struct UpgradeClient(Attributes); + +impl UpgradeClient { + pub fn set_height(&mut self, height: Height) { + self.0.height = height; + } +} + +impl From for UpgradeClient { + fn from(attrs: Attributes) -> Self { + UpgradeClient(attrs) + } +} diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index 204a67aa73..a405d8bf7a 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -146,7 +146,7 @@ impl Runnable for TxUpgradeClientCmd { let outcome = client.upgrade(); match outcome { - Ok(receipt) => Output::success(format!("")).exit(), + Ok(receipt) => Output::success(receipt).exit(), Err(e) => Output::error(format!("{}", e)).exit(), } } diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index ec848f4ac8..d39bfd3194 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -508,8 +508,6 @@ impl Chain for CosmosSdkChain { ) -> Result<(Self::ClientState, MerkleProof), Error> { crate::time!("query_upgraded_client_state"); - println!("Query upgraded client state @ {}", height); - let grpc_address = Uri::from_str(&self.config.grpc_addr).map_err(|e| Kind::Grpc.context(e))?; diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 59fdabaa3c..7fe4ba8248 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -2,7 +2,7 @@ use std::{thread, time::Duration}; use prost_types::Any; use thiserror::Error; -use tracing::{error, info}; +use tracing::{debug, error, info}; use ibc::events::IbcEvent; use ibc::ics02_client::header::Header; @@ -116,7 +116,7 @@ impl ForeignClient { } } - pub fn upgrade(&self) -> Result<(), ForeignClientError> { + pub fn upgrade(&self) -> Result, ForeignClientError> { // Fetch the latest height of the source chain. let src_height = self.src_chain.query_latest_height().map_err(|e| { ForeignClientError::ClientUpgrade( @@ -129,7 +129,9 @@ impl ForeignClient { ) })?; - info!("Height: {}", src_height); + info!("Upgrade Height: {}", src_height); + + let mut msgs = self.build_update_client(src_height)?; // Query the host chain for the upgraded client state, consensus state & their proofs. let (client_state, proof_upgrade_client) = self @@ -146,10 +148,7 @@ impl ForeignClient { ) })?; - info!( - "Upgraded client state {:?} & proof {:?}", - client_state, proof_upgrade_client - ); + debug!("Upgraded client state {:?}", client_state); let (consensus_state, proof_upgrade_consensus_state) = self .src_chain @@ -158,10 +157,7 @@ impl ForeignClient { "failed while fetching from chain {} the upgraded client consensus state: {}", self.src_chain.id(), e))) ?; - info!( - "Upgraded client consensus state {:?} & proof {:?}", - consensus_state, proof_upgrade_consensus_state - ); + debug!("Upgraded client consensus state {:?}", consensus_state); // Get signer let signer = self.dst_chain.get_signer().map_err(|e| { @@ -175,12 +171,6 @@ impl ForeignClient { ) })?; - info!( - "Using signer for chain id ({}): {}", - self.dst_chain.id(), - signer.to_string() - ); - let msg_upgrade = MsgUpgradeAnyClient { client_id: self.id.clone(), client_state, @@ -188,25 +178,23 @@ impl ForeignClient { proof_upgrade_client, proof_upgrade_consensus_state, signer, - }; + } + .to_any::(); - let res = self - .dst_chain - .send_msgs(vec![msg_upgrade.to_any::()]) - .map_err(|e| { - ForeignClientError::ClientUpgrade( - self.id.clone(), - format!( - "failed while sending message to destination chain {} with err: {}", - self.dst_chain.id(), - e - ), - ) - })?; + msgs.push(msg_upgrade); - info!("Res: {:?}", res); + let res = self.dst_chain.send_msgs(msgs).map_err(|e| { + ForeignClientError::ClientUpgrade( + self.id.clone(), + format!( + "failed while sending message to destination chain {} with err: {}", + self.dst_chain.id(), + e + ), + ) + })?; - Ok(()) + Ok(res) } /// Returns a handle to the chain hosting this client. From 892ac8fc30c96e145d69eb4069716df27fb32553 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 9 Mar 2021 14:35:41 +0100 Subject: [PATCH 12/26] Changelog. Revised guide --- CHANGELOG.md | 2 ++ guide/src/upgrade_test.md | 46 +++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf03418ea1..24b8d27f4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - [ibc-relayer-cli] - Added `create connection` CLI ([#630]) - Proposed ADR 006 to describe Hermes v0.2.0 use-cases ([#637]) + - Added `client-upgrade` CLI ([#357]) ### IMPROVEMENTS @@ -56,6 +57,7 @@ - [nothing yet] [#352]: https://github.com/informalsystems/ibc-rs/issues/352 +[#357]: https://github.com/informalsystems/ibc-rs/issues/357 [#561]: https://github.com/informalsystems/ibc-rs/issues/561 [#599]: https://github.com/informalsystems/ibc-rs/issues/599 [#630]: https://github.com/informalsystems/ibc-rs/issues/630 diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index 467c3be6dc..e739f0c747 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -1,5 +1,28 @@ -1. Patch the `one-chain` script of rrly: +## Prerequisites +- gaiad +``` +$ gaiad version --long | head -n4 +name: gaia +server_name: gaiad +version: 4.0.0 +commit: a279d091c6f66f8a91c87943139ebaecdd84f689 +``` + +- Go relayer + +## Testing procedure + +1. Patch the `one-chain` script of the Go relayer. + +```shell +cd ~/go/src/github.com/cosmos/relayer/ +``` + +This pathing step is necessary to short-circuit the upgrading of a chain. +With the changes below, a chain will be able to undergo an upgrade within +~200 seconds of the upgrade proposal (instead of the default of 2 days). +With this patch, we can test the upgrade functionality in a matter of minutes. ```diff diff --git a/scripts/one-chain b/scripts/one-chain @@ -16,10 +39,8 @@ index d0995fe..3702a88 100755 sed -i '' 's#"localhost:6060"#"localhost:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml ``` - 2. Start two gaia instances using the patched developer environment: - ```shell ./scripts/two-chainz ``` @@ -100,7 +121,12 @@ Once ibc-0 reaches height 800, it should stop executing. 7. Initialize and test Hermes -Patch the hardcoded gaia directory to direct Hermes to the correct gaia dir: +```shell +cd ~/rust/ibc-rs +``` + + +Patch the developer env of Hermes, to redirect to the correct Gaia directory: ```diff diff --git a/scripts/init-clients b/scripts/init-clients index 6cf1a674..bfff9721 100755 @@ -111,12 +137,14 @@ index 6cf1a674..bfff9721 100755 fi -GAIA_DATA="$(pwd)/data" -+GAIA_DATA="/Users/adi/go/src/github.com/cosmos/relayer/data" ++GAIA_DATA="~/go/src/github.com/cosmos/relayer/data" CHAIN_0_RPC_PORT=26657 CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" ``` +No setup the clients for Hermes to use: + ```shell $ ./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 Building the Rust relayer... @@ -127,8 +155,12 @@ $ ./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 Done! ``` -The following command should output the upgraded client and consensus states -with their proofs: +8. Test the `upgrade-client` CLI + +The following command will perform the upgrade for client `07-tendermint-0`. It +will output two events, one for the updated client state, and another for the +upgraded state. + ```shell hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 ``` \ No newline at end of file From 942bfff2484f6cb7476b40b5351cacc6b96f44e6 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 9 Mar 2021 14:48:37 +0100 Subject: [PATCH 13/26] Aesthetic nits based on file review --- modules/src/ics02_client/client_def.rs | 2 -- relayer/src/chain/cosmos.rs | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index f745347e3e..86c62d2751 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -618,8 +618,6 @@ impl ClientDef for AnyClient { } } } - - // TODO: Add upgrade method here } #[cfg(test)] diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index d39bfd3194..1d67d74394 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -581,7 +581,8 @@ impl Chain for CosmosSdkChain { .into_inner() .upgraded_consensus_state .ok_or(Kind::EmptyResponseValue)?; - // TODO: More explicit error kinds (should not reuse Grpc all over the place. + + // TODO: More explicit error kinds (should not reuse Grpc all over the place) let consensus_state = AnyConsensusState::try_from(upgraded_consensus_state_raw) .map_err(|e| Kind::Grpc.context(e))?; From 6ba2e0d5a6f4b5146dd4e15695b60e0f69026fb4 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 9 Mar 2021 16:58:10 +0100 Subject: [PATCH 14/26] Clarifications in the test instructions --- guide/src/upgrade_test.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index e739f0c747..4380b538ef 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -19,11 +19,13 @@ commit: a279d091c6f66f8a91c87943139ebaecdd84f689 cd ~/go/src/github.com/cosmos/relayer/ ``` -This pathing step is necessary to short-circuit the upgrading of a chain. +This patching step is necessary to short-circuit the upgrading of a chain. With the changes below, a chain will be able to undergo an upgrade within ~200 seconds of the upgrade proposal (instead of the default of 2 days). With this patch, we can test the upgrade functionality in a matter of minutes. +Note this only works for MacOS ("Darwin" platform), not tests on Linux. + ```diff diff --git a/scripts/one-chain b/scripts/one-chain index d0995fe..3702a88 100755 @@ -39,7 +41,7 @@ index d0995fe..3702a88 100755 sed -i '' 's#"localhost:6060"#"localhost:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml ``` -2. Start two gaia instances using the patched developer environment: +2. Start two gaia instances using the patched developer environment in the Go relayer codebase: ```shell ./scripts/two-chainz @@ -116,7 +118,7 @@ we submitted at step 5. gaiad tx gov vote 1 yes --home data/ibc-0/data/ --keyring-backend test --keyring-dir data/ibc-0/ --chain-id ibc-0 --from validator ``` -Once ibc-0 reaches height 800, it should stop executing. +Once ibc-0 reaches height 800 (the upgrade height specified in the plan at step 4), the chain should stop executing. 7. Initialize and test Hermes @@ -163,4 +165,4 @@ upgraded state. ```shell hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 -``` \ No newline at end of file +``` From c59224f194728de6849dad17eb19f751bbb6b97c Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 10 Mar 2021 15:13:19 +0100 Subject: [PATCH 15/26] Documented Go relayer version in testing instructions --- guide/src/upgrade_test.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index 4380b538ef..1d0f6ef23e 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -11,6 +11,14 @@ commit: a279d091c6f66f8a91c87943139ebaecdd84f689 - Go relayer +```shell +$ rly version +version: 0.8.2 +commit: 489607fa6de093d90fd2f8ac8eb52be3ccf3f145 +cosmos-sdk: v0.41.3 +go: go1.15.6 darwin/amd64 +``` + ## Testing procedure 1. Patch the `one-chain` script of the Go relayer. From 466f28162a0a251eaf11c897355e49819fa8e371 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 11 Mar 2021 09:37:25 +0100 Subject: [PATCH 16/26] Possible fix for #734 --- relayer/src/foreign_client.rs | 18 +++++++++++++++++- scripts/init-clients | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 7fe4ba8248..6e576cc623 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -2,7 +2,7 @@ use std::{thread, time::Duration}; use prost_types::Any; use thiserror::Error; -use tracing::{debug, error, info}; +use tracing::{debug, error, info, warn}; use ibc::events::IbcEvent; use ibc::ics02_client::header::Header; @@ -324,6 +324,14 @@ impl ForeignClient { })? .latest_height(); + if trusted_height >= target_height { + warn!( + "Client height ({}) >= chain target height ({}). Cannot build update message.", + trusted_height, target_height + ); + return Ok(vec![]); + } + let header = self .src_chain() .build_header(trusted_height, target_height) @@ -360,6 +368,14 @@ impl ForeignClient { )) })?; let new_msgs = self.build_update_client(h)?; + if new_msgs.is_empty() { + return Err(ForeignClientError::ClientUpdate(format!( + "Client {} is already up-to-date with chain {}@{}", + self.id, + self.src_chain.id(), + h + ))); + } let mut events = self.dst_chain().send_msgs(new_msgs).map_err(|e| { ForeignClientError::ClientUpdate(format!( diff --git a/scripts/init-clients b/scripts/init-clients index 6cf1a674ac..333779e9ef 100755 --- a/scripts/init-clients +++ b/scripts/init-clients @@ -66,8 +66,8 @@ cargo run --bin hermes -- -c "$CONFIG_FILE" light rm -c "$CHAIN_1_ID" --all -y & # set the primary peers for clients on each chain echo "Adding primary peers to light client configuration..." -cargo run --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_0_RPC_ADDR -c "$CHAIN_0_ID" -f -p -s "$GAIA_DATA/$CHAIN_0_ID/data" -y &>/dev/null -cargo run --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_1_RPC_ADDR -c "$CHAIN_1_ID" -f -p -s "$GAIA_DATA/$CHAIN_1_ID/data" -y &>/dev/null +cargo run --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_0_RPC_ADDR -c "$CHAIN_0_ID" -f -p -s "$GAIA_DATA/$CHAIN_0_ID/data" -y &>/dev/null || printf "\tError adding primary peer to %s\n" "$CHAIN_0_RPC_ADDR" +cargo run --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_1_RPC_ADDR -c "$CHAIN_1_ID" -f -p -s "$GAIA_DATA/$CHAIN_1_ID/data" -y &>/dev/null || printf "\tError adding primary peer to %s\n" "$CHAIN_1_RPC_ADDR" # set the secondary peers for clients on each chain echo "Adding secondary peers to light client configuration..." From e02d508bcff37c8c7d73dfc4c5df57dfbb972653 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 11 Mar 2021 09:47:12 +0100 Subject: [PATCH 17/26] changelog & method documentation --- CHANGELOG.md | 3 ++- relayer/src/foreign_client.rs | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b8d27f4c..4f3ac64079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ - [nothing yet] - [ibc-relayer-cli] - - [nothing yet] + - Clarified success path for updating a client that is already up-to-date ([#734]) ### BUG FIXES @@ -67,6 +67,7 @@ [#695]: https://github.com/informalsystems/ibc-rs/issues/695 [#699]: https://github.com/informalsystems/ibc-rs/issues/699 [#700]: https://github.com/informalsystems/ibc-rs/pull/700 +[#734]: https://github.com/informalsystems/ibc-rs/issues/734 ## v0.1.1 *February 17, 2021* diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 6e576cc623..0081f2ebb2 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -297,6 +297,8 @@ impl ForeignClient { Ok(()) } + /// Returns a vector with a message for updating the client to height `target_height`. + /// If the client already stores consensus states for this height, returns an empty vector. pub fn build_update_client( &self, target_height: Height, @@ -367,6 +369,7 @@ impl ForeignClient { e )) })?; + let new_msgs = self.build_update_client(h)?; if new_msgs.is_empty() { return Err(ForeignClientError::ClientUpdate(format!( From 6f3b09ac1534511e09ff63568516be0926fb791a Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 15 Mar 2021 13:51:40 +0100 Subject: [PATCH 18/26] Apply suggestions from code review Co-authored-by: Romain Ruetschi --- guide/src/upgrade_test.md | 2 +- modules/src/ics02_client/events.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index 1d0f6ef23e..49dec35281 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -153,7 +153,7 @@ index 6cf1a674..bfff9721 100755 CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" ``` -No setup the clients for Hermes to use: +Now setup the clients for Hermes to use: ```shell $ ./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 diff --git a/modules/src/ics02_client/events.rs b/modules/src/ics02_client/events.rs index 8b93f3768f..5f0ae0cbd0 100644 --- a/modules/src/ics02_client/events.rs +++ b/modules/src/ics02_client/events.rs @@ -220,7 +220,7 @@ impl From for IbcEvent { } /// Signals a recent upgrade of an on-chain client (IBC Client). -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] pub struct UpgradeClient(Attributes); impl UpgradeClient { From d2fae93c5183dcc0e0c755f22c2999160df681e6 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 15 Mar 2021 13:57:00 +0100 Subject: [PATCH 19/26] Added more derived trait bounds on module events --- modules/src/ics02_client/events.rs | 2 +- modules/src/ics03_connection/events.rs | 2 +- modules/src/ics04_channel/events.rs | 2 +- modules/src/ics04_channel/packet.rs | 2 +- modules/src/ics24_host/path.rs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/src/ics02_client/events.rs b/modules/src/ics02_client/events.rs index 5f0ae0cbd0..9fede49bf4 100644 --- a/modules/src/ics02_client/events.rs +++ b/modules/src/ics02_client/events.rs @@ -81,7 +81,7 @@ impl From for IbcEvent { } } -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Attributes { pub height: Height, pub client_id: ClientId, diff --git a/modules/src/ics03_connection/events.rs b/modules/src/ics03_connection/events.rs index a4e9cb360e..da3cf99614 100644 --- a/modules/src/ics03_connection/events.rs +++ b/modules/src/ics03_connection/events.rs @@ -60,7 +60,7 @@ fn extract_attributes_from_tx(event: &tendermint::abci::Event) -> Attributes { attr } -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Attributes { pub height: Height, pub connection_id: Option, diff --git a/modules/src/ics04_channel/events.rs b/modules/src/ics04_channel/events.rs index 971708057a..5254287b44 100644 --- a/modules/src/ics04_channel/events.rs +++ b/modules/src/ics04_channel/events.rs @@ -151,7 +151,7 @@ fn extract_packet_and_write_ack_from_tx( (packet, write_ack) } -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Attributes { pub height: Height, pub port_id: PortId, diff --git a/modules/src/ics04_channel/packet.rs b/modules/src/ics04_channel/packet.rs index d777608961..0a5e9e5b99 100644 --- a/modules/src/ics04_channel/packet.rs +++ b/modules/src/ics04_channel/packet.rs @@ -38,7 +38,7 @@ impl std::fmt::Display for PacketMsgType { } /// The sequence number of a packet enforces ordering among packets from the same source. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)] pub struct Sequence(u64); impl Sequence { diff --git a/modules/src/ics24_host/path.rs b/modules/src/ics24_host/path.rs index ffcda8accd..2bbae20888 100644 --- a/modules/src/ics24_host/path.rs +++ b/modules/src/ics24_host/path.rs @@ -23,7 +23,7 @@ const UPGRADED_CLIENT_STATE: &str = "upgradedClient"; const UPGRADED_CLIENT_CONSENSUS_STATE: &str = "upgradedConsState"; /// The Path enum abstracts out the different sub-paths -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Path { ClientType(ClientId), ClientState(ClientId), @@ -58,7 +58,7 @@ pub enum Path { } /// Paths that are specific for client upgrades. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ClientUpgradePath { UpgradedClientState(u64), UpgradedClientConsensusState(u64), From 5fc61f7053d1c18a7e312941e12e8a93efc6bac1 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 15 Mar 2021 14:10:05 +0100 Subject: [PATCH 20/26] Adapt to newer Ics02 structure. --- modules/src/ics02_client/handler/upgrade_client.rs | 5 +++-- modules/src/ics02_client/msgs/upgrade_client.rs | 3 ++- relayer/src/foreign_client.rs | 2 -- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/src/ics02_client/handler/upgrade_client.rs b/modules/src/ics02_client/handler/upgrade_client.rs index bb7931908a..5579fe14fb 100644 --- a/modules/src/ics02_client/handler/upgrade_client.rs +++ b/modules/src/ics02_client/handler/upgrade_client.rs @@ -1,12 +1,13 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgUpgradeAnyClient`. //! use crate::handler::HandlerResult; -use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::ics02_client::client_consensus::AnyConsensusState; +use crate::ics02_client::client_state::AnyClientState; +use crate::ics02_client::client_state::ClientState; use crate::ics02_client::context::ClientReader; use crate::ics02_client::error::{Error, Kind}; use crate::ics02_client::handler::ClientResult; use crate::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; -use crate::ics02_client::state::ClientState; use crate::ics24_host::identifier::ClientId; /// The result following the successful processing of a `MsgUpgradeAnyClient` message. diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs index 5ca6fc11e2..95c0356553 100644 --- a/modules/src/ics02_client/msgs/upgrade_client.rs +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -3,7 +3,8 @@ use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; use ibc_proto::ibc::core::commitment::v1::MerkleProof; -use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::ics02_client::client_consensus::AnyConsensusState; +use crate::ics02_client::client_state::AnyClientState; use crate::ics23_commitment::commitment::CommitmentProofBytes; use crate::ics24_host::identifier::ClientId; use crate::signer::Signer; diff --git a/relayer/src/foreign_client.rs b/relayer/src/foreign_client.rs index 99053acaa7..83de334202 100644 --- a/relayer/src/foreign_client.rs +++ b/relayer/src/foreign_client.rs @@ -11,8 +11,6 @@ use ibc::ics02_client::header::Header; use ibc::ics02_client::msgs::create_client::MsgCreateAnyClient; use ibc::ics02_client::msgs::update_client::MsgUpdateAnyClient; use ibc::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; -use ibc::ics02_client::state::ClientState; -use ibc::ics02_client::state::ConsensusState; use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc::tx_msg::Msg; use ibc::Height; From 77691624ffcd0121924bf55ea83a28fdeeb5e557 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 15 Mar 2021 16:08:52 +0100 Subject: [PATCH 21/26] Added Protobuf impl for MsgUpgradeAnyClient --- modules/src/ics02_client/error.rs | 12 +++++- .../src/ics02_client/msgs/upgrade_client.rs | 38 +++++++++++++++++++ modules/src/ics23_commitment/error.rs | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/modules/src/ics02_client/error.rs b/modules/src/ics02_client/error.rs index 59c328dcbf..7fca139325 100644 --- a/modules/src/ics02_client/error.rs +++ b/modules/src/ics02_client/error.rs @@ -2,6 +2,8 @@ use anomaly::{BoxError, Context}; use thiserror::Error; use crate::ics02_client::client_type::ClientType; +use crate::ics23_commitment::error::Kind as Ics23Kind; +use crate::ics24_host::error::ValidationKind; use crate::ics24_host::identifier::ClientId; use crate::Height; @@ -57,8 +59,8 @@ pub enum Kind { #[error("invalid raw client consensus state")] InvalidRawConsensusState, - #[error("invalid identifer")] - InvalidIdentifier, + #[error("invalid client identifier: validation error: {0}")] + InvalidClientIdentifier(ValidationKind), #[error("invalid raw header")] InvalidRawHeader, @@ -69,6 +71,12 @@ pub enum Kind { #[error("invalid address")] InvalidAddress, + #[error("invalid proof for the upgraded client state")] + InvalidUpgradeClientProof(Ics23Kind), + + #[error("invalid proof for the upgraded consensus state")] + InvalidUpgradeConsensusStateProof(Ics23Kind), + #[error("mismatch between client and arguments types, expected: {0:?}")] ClientArgsTypeMismatch(ClientType), diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs index 95c0356553..d3d299f5cd 100644 --- a/modules/src/ics02_client/msgs/upgrade_client.rs +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -1,10 +1,16 @@ //! Definition of domain type msg `MsgUpgradeAnyClient`. +use std::convert::TryFrom; +use std::str::FromStr; + +use tendermint_proto::Protobuf; + use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; use ibc_proto::ibc::core::commitment::v1::MerkleProof; use crate::ics02_client::client_consensus::AnyConsensusState; use crate::ics02_client::client_state::AnyClientState; +use crate::ics02_client::error::Kind; use crate::ics23_commitment::commitment::CommitmentProofBytes; use crate::ics24_host::identifier::ClientId; use crate::signer::Signer; @@ -36,6 +42,8 @@ impl Msg for MsgUpgradeAnyClient { } } +impl Protobuf for MsgUpgradeAnyClient {} + impl From for RawMsgUpgradeClient { fn from(dm_msg: MsgUpgradeAnyClient) -> RawMsgUpgradeClient { let c_bytes: CommitmentProofBytes = dm_msg.proof_upgrade_client.into(); @@ -51,3 +59,33 @@ impl From for RawMsgUpgradeClient { } } } + +impl TryFrom for MsgUpgradeAnyClient { + type Error = Kind; + + fn try_from(proto_msg: RawMsgUpgradeClient) -> Result { + let raw_client_state = proto_msg + .client_state + .ok_or(Kind::InvalidRawClientState)?; + let raw_consensus_state = proto_msg + .consensus_state + .ok_or(Kind::InvalidRawConsensusState)?; + + let c_bytes = CommitmentProofBytes::from(proto_msg.proof_upgrade_client); + let cs_bytes = CommitmentProofBytes::from(proto_msg.proof_upgrade_consensus_state); + + Ok(MsgUpgradeAnyClient { + client_id: ClientId::from_str(&proto_msg.client_id) + .map_err(|e| Kind::InvalidClientIdentifier(e.kind().clone()))?, + client_state: AnyClientState::try_from(raw_client_state) + .map_err(|_| Kind::InvalidRawClientState)?, + consensus_state: AnyConsensusState::try_from(raw_consensus_state) + .map_err(|_| Kind::InvalidRawConsensusState)?, + proof_upgrade_client: MerkleProof::try_from(c_bytes) + .map_err(|e| Kind::InvalidUpgradeClientProof(e.kind().clone()))?, + proof_upgrade_consensus_state: MerkleProof::try_from(cs_bytes) + .map_err(|e| Kind::InvalidUpgradeConsensusStateProof(e.kind().clone()))?, + signer: proto_msg.signer.into(), + }) + } +} diff --git a/modules/src/ics23_commitment/error.rs b/modules/src/ics23_commitment/error.rs index 96ef1dc79a..07d4490c80 100644 --- a/modules/src/ics23_commitment/error.rs +++ b/modules/src/ics23_commitment/error.rs @@ -3,7 +3,7 @@ use thiserror::Error; pub type Error = anomaly::Error; -#[derive(Clone, Debug, Error)] +#[derive(Clone, Debug, Error, PartialEq, Eq)] pub enum Kind { #[error("invalid raw merkle proof")] InvalidRawMerkleProof, From 959f8dd9f9100c4dcbf0b4b368a5f581ba31de5e Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Mon, 15 Mar 2021 16:10:36 +0100 Subject: [PATCH 22/26] FMT --- modules/src/ics02_client/msgs/upgrade_client.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/src/ics02_client/msgs/upgrade_client.rs b/modules/src/ics02_client/msgs/upgrade_client.rs index d3d299f5cd..4379b8f4ad 100644 --- a/modules/src/ics02_client/msgs/upgrade_client.rs +++ b/modules/src/ics02_client/msgs/upgrade_client.rs @@ -64,9 +64,7 @@ impl TryFrom for MsgUpgradeAnyClient { type Error = Kind; fn try_from(proto_msg: RawMsgUpgradeClient) -> Result { - let raw_client_state = proto_msg - .client_state - .ok_or(Kind::InvalidRawClientState)?; + let raw_client_state = proto_msg.client_state.ok_or(Kind::InvalidRawClientState)?; let raw_consensus_state = proto_msg .consensus_state .ok_or(Kind::InvalidRawConsensusState)?; From 42b326a54375b69553fd0889ebca718af03d2c82 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Tue, 23 Mar 2021 18:56:39 +0100 Subject: [PATCH 23/26] Added upgrade-chain CLI and updated instructions --- guide/src/upgrade_test.md | 324 +++++++++-------- modules/src/ics07_tendermint/client_state.rs | 20 +- proto-compiler/src/cmd/compile.rs | 1 + proto/src/lib.rs | 5 + proto/src/prost/cosmos.gov.v1beta1.rs | 352 +++++++++++++++++++ relayer-cli/src/commands/tx.rs | 5 + relayer-cli/src/commands/tx/client.rs | 4 +- relayer-cli/src/commands/tx/upgrade.rs | 101 ++++++ relayer/src/lib.rs | 1 + relayer/src/upgrade_chain.rs | 124 +++++++ scripts/one-chain | 2 + 11 files changed, 769 insertions(+), 170 deletions(-) create mode 100644 proto/src/prost/cosmos.gov.v1beta1.rs create mode 100644 relayer-cli/src/commands/tx/upgrade.rs create mode 100644 relayer/src/upgrade_chain.rs diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index 49dec35281..aaf06fbb50 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -1,176 +1,166 @@ ## Prerequisites -- gaiad -``` +- gaiad `(v4.1.*)`, for example: + +```shell $ gaiad version --long | head -n4 name: gaia server_name: gaiad -version: 4.0.0 -commit: a279d091c6f66f8a91c87943139ebaecdd84f689 -``` - -- Go relayer - -```shell -$ rly version -version: 0.8.2 -commit: 489607fa6de093d90fd2f8ac8eb52be3ccf3f145 -cosmos-sdk: v0.41.3 -go: go1.15.6 darwin/amd64 +version: 4.1.2 +commit: 95b07e641d1f69ee12dd911e92b1679f2c64d385 ``` ## Testing procedure -1. Patch the `one-chain` script of the Go relayer. - -```shell -cd ~/go/src/github.com/cosmos/relayer/ -``` - -This patching step is necessary to short-circuit the upgrading of a chain. -With the changes below, a chain will be able to undergo an upgrade within -~200 seconds of the upgrade proposal (instead of the default of 2 days). -With this patch, we can test the upgrade functionality in a matter of minutes. - -Note this only works for MacOS ("Darwin" platform), not tests on Linux. - -```diff -diff --git a/scripts/one-chain b/scripts/one-chain -index d0995fe..3702a88 100755 ---- a/scripts/one-chain -+++ b/scripts/one-chain -@@ -99,6 +99,7 @@ if [ $platform = 'linux' ]; then - sed -i 's/index_all_keys = false/index_all_keys = true/g' $CHAINDIR/$CHAINID/config/config.toml - # sed -i '' 's#index-events = \[\]#index-events = \["message.action","send_packet.packet_src_channel","send_packet.packet_sequence"\]#g' $CHAINDIR/$CHAINID/config/app.toml - else -+ sed -i '' 's#"172800s"#"200s"#g' $CHAINDIR/$CHAINID/config/genesis.json - sed -i '' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPCPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml - sed -i '' 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml - sed -i '' 's#"localhost:6060"#"localhost:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml -``` - -2. Start two gaia instances using the patched developer environment in the Go relayer codebase: - -```shell -./scripts/two-chainz -``` - -3. Setup the Go relayer for these chains: -```shell -$ rly tx link demo -d -o 3s -``` - -Check that everything went fine so far: - -```shell -$ rly paths list - 0: demo -> chns(✔) clnts(✔) conn(✔) chan(✔) (ibc-0:transfer<>ibc-1:transfer) -``` - -4. Create the upgrade plan for chain ibc-0: - -It's important that we parametrize the upgrade plan with a height parameter that -is at least 300 heights ahead of the current height of chain ibc-0. - -First, obtain the current height: -```shell -gaiad query block | jq | grep height - "height": "470", -``` - -Now create the upgrade plan for height 800: -```shell -echo '{ - "Name": "test", - "Height": 800, - "Info": "" -}' > ./upgrade-plan.json -``` - - -5. Submit the upgrade plan - -```shell -rly tx upgrade-chain demo ibc-0 400h 10000000stake ./upgrade-plan.json -``` - -Query for the upgrade plan, check that it was submitted correctly: - -```shell -$ gaiad query gov proposal 1 --home data/ibc-0/ - -content: - '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal - description: upgrade the chain's software and unbonding period - plan: - height: "800" - info: "" - name: test -.... -proposal_id: "1" -status: PROPOSAL_STATUS_VOTING_PERIOD -submit_time: "2021-03-08T13:07:01.417163Z" -total_deposit: -- amount: "10000000" - denom: stake -voting_end_time: "2021-03-08T13:10:21.417163Z" -voting_start_time: "2021-03-08T13:07:01.417163Z" -``` - -6. Vote on the proposal - -The parameter "1" should match the "proposal_id:" from the upgrade proposal -we submitted at step 5. - -```shell -gaiad tx gov vote 1 yes --home data/ibc-0/data/ --keyring-backend test --keyring-dir data/ibc-0/ --chain-id ibc-0 --from validator -``` - -Once ibc-0 reaches height 800 (the upgrade height specified in the plan at step 4), the chain should stop executing. - - -7. Initialize and test Hermes - -```shell -cd ~/rust/ibc-rs -``` - - -Patch the developer env of Hermes, to redirect to the correct Gaia directory: -```diff -diff --git a/scripts/init-clients b/scripts/init-clients -index 6cf1a674..bfff9721 100755 ---- a/scripts/init-clients -+++ b/scripts/init-clients -@@ -49,7 +49,7 @@ if ! grep -q -s "$CHAIN_1_ID" "$CONFIG_FILE"; then - usage - fi - --GAIA_DATA="$(pwd)/data" -+GAIA_DATA="~/go/src/github.com/cosmos/relayer/data" - - CHAIN_0_RPC_PORT=26657 - CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" -``` - -Now setup the clients for Hermes to use: - -```shell -$ ./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 - Building the Rust relayer... - Removing light client peers from configuration... - Adding primary peers to light client configuration... - Adding secondary peers to light client configuration... - Importing keys... - Done! -``` - -8. Test the `upgrade-client` CLI - -The following command will perform the upgrade for client `07-tendermint-0`. It -will output two events, one for the updated client state, and another for the -upgraded state. - -```shell -hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 -``` +1. Start two gaia instances and initialize hermes: + + ```shell + $ ./scripts/dev-env ~/.hermes/config.toml ibc-0 ibc-1 + ``` + The `one-chain` script is invoked for each chain and modifies the `genesis.json` file to use a short window for governance proposals (`200s` for `max_deposit_period` and `voting_period`). Therefore, an upgrade proposal can be submitted, voted on and accepted within a short time. + +2. Create one client on `ibc-1` for `ibc-0`: + + ```shell + $ hermes tx raw create-client ibc-1 ibc-0 + ``` + +3. Create and submit an upgrade plan for chain `ibc-0`: + + Use the hermes test command to make an upgrade proposal. In the example below a software upgrade proposal is made for `ibc-0`, for the height `300` blocks from latest height. `10000000stake` is deposited. + The proposal includes the upgraded client state constructed from the state of `07-tendermint-0` client on `ibc-1` that was created in the previous step. In addition, the `unbonding_period` of the client is set to some new value (`400h`) + + ```shell + $ hermes tx raw upgrade-chain ibc-0 ibc-1 07-tendermint-0 10000000 300 + ``` + + Note that the height offset should be picked such that the proposal plan height is reached after the `200s` voting period. + + 4. Verify that the proposal was accepted: + + Query the upgrade plan to check that it was submitted correctly. Note the `height` at which the proposal will take effect (chain halts). Also `status: PROPOSAL_STATUS_VOTING_PERIOD`. + + ```shell + $ gaiad query gov proposal 1 --home data/ibc-0/ + + content: + '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal + description: upgrade the chain software and unbonding period + plan: + height: "382" + info: upgrade the chain software and unbonding period + name: test + time: "0001-01-01T00:00:00Z" + upgraded_client_state: + '@type': /ibc.lightclients.tendermint.v1.ClientState + allow_update_after_expiry: false + allow_update_after_misbehaviour: false + chain_id: ibc-0 + frozen_height: + revision_height: "0" + revision_number: "0" + latest_height: + revision_height: "383" + revision_number: "0" + max_clock_drift: 0s + proof_specs: + ... + trust_level: + denominator: "0" + numerator: "0" + trusting_period: 0s + unbonding_period: 1440000s + upgrade_path: + - upgrade + - upgradedIBCState + title: upgrade_ibc_clients + deposit_end_time: "2021-03-23T17:25:42.543572Z" + final_tally_result: + abstain: "0" + "no": "0" + no_with_veto: "0" + "yes": "0" + proposal_id: "1" + status: PROPOSAL_STATUS_VOTING_PERIOD + submit_time: "2021-03-23T17:22:22.543572Z" + total_deposit: + - amount: "10000000" + denom: stake + voting_end_time: "2021-03-23T17:25:42.543572Z" + voting_start_time: "2021-03-23T17:22:22.543572Z" + ``` + + 5. Vote on the proposal + + The parameter `1` should match the `proposal_id:` from the upgrade proposal submitted at step 3. This command must be issued while the proposal status is `PROPOSAL_STATUS_VOTING_PERIOD`. + + ```shell + gaiad tx gov vote 1 yes --home data/ibc-0/data/ --keyring-backend test --keyring-dir data/ibc-0/ --chain-id ibc-0 --from validator + ``` + + Wait approximately 200 seconds until the proposal changes status to `PROPOSAL_STATUS_PASSED`. Note the `final tally_result` that includes the vote submitted in previous step. + + ```shell + $ gaiad query gov proposal 1 --home data/ibc-0/ + + content: + '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal + description: upgrade the chain software and unbonding period + plan: + ... + final_tally_result: + abstain: "0" + "no": "0" + no_with_veto: "0" + "yes": "100000000000" + proposal_id: "1" + status: PROPOSAL_STATUS_PASSED + submit_time: "2021-03-23T17:22:22.543572Z" + total_deposit: + - amount: "10000000" + denom: stake + voting_end_time: "2021-03-23T17:25:42.543572Z" + voting_start_time: "2021-03-23T17:22:22.543572Z" + ``` + +6. Test the `upgrade-client` CLI + + The following command performs the upgrade for client `07-tendermint-0`. It outputs two events, one for the updated client state, and another for the upgraded state. + + ```shell + $ hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 + + { + "status": "success", + "result": [ + { + "UpdateClient": { + "client_id": "07-tendermint-0", + "client_type": "Tendermint", + "consensus_height": { + "revision_height": 332, + "revision_number": 0 + }, + "height": { + "revision_height": 404, + "revision_number": 1 + } + } + }, + { + "UpgradeClient": { + "client_id": "07-tendermint-0", + "client_type": "Tendermint", + "consensus_height": { + "revision_height": 333, + "revision_number": 0 + }, + "height": { + "revision_height": 404, + "revision_number": 1 + } + } + } + ] + } + ``` diff --git a/modules/src/ics07_tendermint/client_state.rs b/modules/src/ics07_tendermint/client_state.rs index db77540163..88fcc9db4b 100644 --- a/modules/src/ics07_tendermint/client_state.rs +++ b/modules/src/ics07_tendermint/client_state.rs @@ -2,7 +2,9 @@ use std::convert::{TryFrom, TryInto}; use std::time::Duration; use serde::Serialize; -use tendermint::trust_threshold::TrustThresholdFraction as TrustThreshold; +use tendermint::trust_threshold::{ + TrustThresholdFraction as TrustThreshold, TrustThresholdFraction, +}; use tendermint_proto::Protobuf; use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawClientState, Fraction}; @@ -103,6 +105,22 @@ impl ClientState { ..self } } + + // Helper function to verify the upgrade client procedure, it resets all fields except the + // blockchain specific ones. + pub fn zero_custom_fields(&self) -> ClientState { + let mut client_state = self.clone(); + client_state.trusting_period = Duration::default(); + client_state.trust_level = TrustThresholdFraction { + numerator: 0, + denominator: 0, + }; + client_state.allow_update_after_expiry = false; + client_state.allow_update_after_misbehaviour = false; + client_state.frozen_height = Height::zero(); + client_state.max_clock_drift = Duration::default(); + client_state + } } impl crate::ics02_client::client_state::ClientState for ClientState { diff --git a/proto-compiler/src/cmd/compile.rs b/proto-compiler/src/cmd/compile.rs index 145cd67a90..733ee8b5ee 100644 --- a/proto-compiler/src/cmd/compile.rs +++ b/proto-compiler/src/cmd/compile.rs @@ -122,6 +122,7 @@ impl CompileCmd { let proto_paths = [ format!("{}/../proto/definitions/mock", root), format!("{}/proto/cosmos/auth", sdk_dir.display()), + format!("{}/proto/cosmos/gov", sdk_dir.display()), format!("{}/proto/cosmos/tx", sdk_dir.display()), format!("{}/proto/cosmos/base", sdk_dir.display()), format!("{}/proto/cosmos/staking", sdk_dir.display()), diff --git a/proto/src/lib.rs b/proto/src/lib.rs index 15b2a20bdb..43a8f37742 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -75,6 +75,11 @@ pub mod cosmos { include!("prost/cosmos.upgrade.v1beta1.rs"); } } + pub mod gov { + pub mod v1beta1 { + include!("prost/cosmos.gov.v1beta1.rs"); + } + } } pub mod ibc { diff --git a/proto/src/prost/cosmos.gov.v1beta1.rs b/proto/src/prost/cosmos.gov.v1beta1.rs new file mode 100644 index 0000000000..6bf3e94f9a --- /dev/null +++ b/proto/src/prost/cosmos.gov.v1beta1.rs @@ -0,0 +1,352 @@ +/// TextProposal defines a standard text proposal whose changes need to be +/// manually updated in case of approval. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TextProposal { + #[prost(string, tag="1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub description: ::prost::alloc::string::String, +} +/// Deposit defines an amount deposited by an account address to an active +/// proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Deposit { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub depositor: ::prost::alloc::string::String, + #[prost(message, repeated, tag="3")] + pub amount: ::prost::alloc::vec::Vec, +} +/// Proposal defines the core field members of a governance proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Proposal { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(message, optional, tag="2")] + pub content: ::core::option::Option<::prost_types::Any>, + #[prost(enumeration="ProposalStatus", tag="3")] + pub status: i32, + #[prost(message, optional, tag="4")] + pub final_tally_result: ::core::option::Option, + #[prost(message, optional, tag="5")] + pub submit_time: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, optional, tag="6")] + pub deposit_end_time: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, repeated, tag="7")] + pub total_deposit: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag="8")] + pub voting_start_time: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, optional, tag="9")] + pub voting_end_time: ::core::option::Option<::prost_types::Timestamp>, +} +/// TallyResult defines a standard tally for a governance proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TallyResult { + #[prost(string, tag="1")] + pub yes: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub abstain: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub no: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub no_with_veto: ::prost::alloc::string::String, +} +/// Vote defines a vote on a governance proposal. +/// A Vote consists of a proposal ID, the voter, and the vote option. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Vote { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, + #[prost(enumeration="VoteOption", tag="3")] + pub option: i32, +} +/// DepositParams defines the params for deposits on governance proposals. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DepositParams { + /// Minimum deposit for a proposal to enter voting period. + #[prost(message, repeated, tag="1")] + pub min_deposit: ::prost::alloc::vec::Vec, + /// Maximum period for Atom holders to deposit on a proposal. Initial value: 2 + /// months. + #[prost(message, optional, tag="2")] + pub max_deposit_period: ::core::option::Option<::prost_types::Duration>, +} +/// VotingParams defines the params for voting on governance proposals. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VotingParams { + /// Length of the voting period. + #[prost(message, optional, tag="1")] + pub voting_period: ::core::option::Option<::prost_types::Duration>, +} +/// TallyParams defines the params for tallying votes on governance proposals. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TallyParams { + /// Minimum percentage of total stake needed to vote for a result to be + /// considered valid. + #[prost(bytes="vec", tag="1")] + pub quorum: ::prost::alloc::vec::Vec, + /// Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + #[prost(bytes="vec", tag="2")] + pub threshold: ::prost::alloc::vec::Vec, + /// Minimum value of Veto votes to Total votes ratio for proposal to be + /// vetoed. Default value: 1/3. + #[prost(bytes="vec", tag="3")] + pub veto_threshold: ::prost::alloc::vec::Vec, +} +/// VoteOption enumerates the valid vote options for a given governance proposal. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum VoteOption { + /// VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + Unspecified = 0, + /// VOTE_OPTION_YES defines a yes vote option. + Yes = 1, + /// VOTE_OPTION_ABSTAIN defines an abstain vote option. + Abstain = 2, + /// VOTE_OPTION_NO defines a no vote option. + No = 3, + /// VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + NoWithVeto = 4, +} +/// ProposalStatus enumerates the valid statuses of a proposal. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ProposalStatus { + /// PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + Unspecified = 0, + /// PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + /// period. + DepositPeriod = 1, + /// PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + /// period. + VotingPeriod = 2, + /// PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + /// passed. + Passed = 3, + /// PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + /// been rejected. + Rejected = 4, + /// PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + /// failed. + Failed = 5, +} +/// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary +/// proposal Content. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgSubmitProposal { + #[prost(message, optional, tag="1")] + pub content: ::core::option::Option<::prost_types::Any>, + #[prost(message, repeated, tag="2")] + pub initial_deposit: ::prost::alloc::vec::Vec, + #[prost(string, tag="3")] + pub proposer: ::prost::alloc::string::String, +} +/// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgSubmitProposalResponse { + #[prost(uint64, tag="1")] + pub proposal_id: u64, +} +/// MsgVote defines a message to cast a vote. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgVote { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, + #[prost(enumeration="VoteOption", tag="3")] + pub option: i32, +} +/// MsgVoteResponse defines the Msg/Vote response type. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgVoteResponse { +} +/// MsgDeposit defines a message to submit a deposit to an existing proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgDeposit { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub depositor: ::prost::alloc::string::String, + #[prost(message, repeated, tag="3")] + pub amount: ::prost::alloc::vec::Vec, +} +/// MsgDepositResponse defines the Msg/Deposit response type. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgDepositResponse { +} +# [doc = r" Generated client implementations."] pub mod msg_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Msg defines the bank Msg service."] pub struct MsgClient < T > { inner : tonic :: client :: Grpc < T > , } impl MsgClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > MsgClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } # [doc = " SubmitProposal defines a method to create new proposal given a content."] pub async fn submit_proposal (& mut self , request : impl tonic :: IntoRequest < super :: MsgSubmitProposal > ,) -> Result < tonic :: Response < super :: MsgSubmitProposalResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Msg/SubmitProposal") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Vote defines a method to add a vote on a specific proposal."] pub async fn vote (& mut self , request : impl tonic :: IntoRequest < super :: MsgVote > ,) -> Result < tonic :: Response < super :: MsgVoteResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Msg/Vote") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Deposit defines a method to add deposit on a specific proposal."] pub async fn deposit (& mut self , request : impl tonic :: IntoRequest < super :: MsgDeposit > ,) -> Result < tonic :: Response < super :: MsgDepositResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Msg/Deposit") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for MsgClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for MsgClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "MsgClient {{ ... }}") } } }/// QueryProposalRequest is the request type for the Query/Proposal RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, +} +/// QueryProposalResponse is the response type for the Query/Proposal RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalResponse { + #[prost(message, optional, tag="1")] + pub proposal: ::core::option::Option, +} +/// QueryProposalsRequest is the request type for the Query/Proposals RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalsRequest { + /// proposal_status defines the status of the proposals. + #[prost(enumeration="ProposalStatus", tag="1")] + pub proposal_status: i32, + /// voter defines the voter address for the proposals. + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, + /// depositor defines the deposit addresses from the proposals. + #[prost(string, tag="3")] + pub depositor: ::prost::alloc::string::String, + /// pagination defines an optional pagination for the request. + #[prost(message, optional, tag="4")] + pub pagination: ::core::option::Option, +} +/// QueryProposalsResponse is the response type for the Query/Proposals RPC +/// method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalsResponse { + #[prost(message, repeated, tag="1")] + pub proposals: ::prost::alloc::vec::Vec, + /// pagination defines the pagination in the response. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryVoteRequest is the request type for the Query/Vote RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVoteRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// voter defines the oter address for the proposals. + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, +} +/// QueryVoteResponse is the response type for the Query/Vote RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVoteResponse { + /// vote defined the queried vote. + #[prost(message, optional, tag="1")] + pub vote: ::core::option::Option, +} +/// QueryVotesRequest is the request type for the Query/Votes RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVotesRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// pagination defines an optional pagination for the request. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryVotesResponse is the response type for the Query/Votes RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVotesResponse { + /// votes defined the queried votes. + #[prost(message, repeated, tag="1")] + pub votes: ::prost::alloc::vec::Vec, + /// pagination defines the pagination in the response. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryParamsRequest is the request type for the Query/Params RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryParamsRequest { + /// params_type defines which parameters to query for, can be one of "voting", + /// "tallying" or "deposit". + #[prost(string, tag="1")] + pub params_type: ::prost::alloc::string::String, +} +/// QueryParamsResponse is the response type for the Query/Params RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryParamsResponse { + /// voting_params defines the parameters related to voting. + #[prost(message, optional, tag="1")] + pub voting_params: ::core::option::Option, + /// deposit_params defines the parameters related to deposit. + #[prost(message, optional, tag="2")] + pub deposit_params: ::core::option::Option, + /// tally_params defines the parameters related to tally. + #[prost(message, optional, tag="3")] + pub tally_params: ::core::option::Option, +} +/// QueryDepositRequest is the request type for the Query/Deposit RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// depositor defines the deposit addresses from the proposals. + #[prost(string, tag="2")] + pub depositor: ::prost::alloc::string::String, +} +/// QueryDepositResponse is the response type for the Query/Deposit RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositResponse { + /// deposit defines the requested deposit. + #[prost(message, optional, tag="1")] + pub deposit: ::core::option::Option, +} +/// QueryDepositsRequest is the request type for the Query/Deposits RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositsRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// pagination defines an optional pagination for the request. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryDepositsResponse is the response type for the Query/Deposits RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositsResponse { + #[prost(message, repeated, tag="1")] + pub deposits: ::prost::alloc::vec::Vec, + /// pagination defines the pagination in the response. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryTallyResultRequest is the request type for the Query/Tally RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryTallyResultRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, +} +/// QueryTallyResultResponse is the response type for the Query/Tally RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryTallyResultResponse { + /// tally defines the requested tally. + #[prost(message, optional, tag="1")] + pub tally: ::core::option::Option, +} +# [doc = r" Generated client implementations."] pub mod query_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Query defines the gRPC querier service for gov module"] pub struct QueryClient < T > { inner : tonic :: client :: Grpc < T > , } impl QueryClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > QueryClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } # [doc = " Proposal queries proposal details based on ProposalID."] pub async fn proposal (& mut self , request : impl tonic :: IntoRequest < super :: QueryProposalRequest > ,) -> Result < tonic :: Response < super :: QueryProposalResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Proposal") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Proposals queries all proposals based on given status."] pub async fn proposals (& mut self , request : impl tonic :: IntoRequest < super :: QueryProposalsRequest > ,) -> Result < tonic :: Response < super :: QueryProposalsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Proposals") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Vote queries voted information based on proposalID, voterAddr."] pub async fn vote (& mut self , request : impl tonic :: IntoRequest < super :: QueryVoteRequest > ,) -> Result < tonic :: Response < super :: QueryVoteResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Vote") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Votes queries votes of a given proposal."] pub async fn votes (& mut self , request : impl tonic :: IntoRequest < super :: QueryVotesRequest > ,) -> Result < tonic :: Response < super :: QueryVotesResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Votes") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Params queries all parameters of the gov module."] pub async fn params (& mut self , request : impl tonic :: IntoRequest < super :: QueryParamsRequest > ,) -> Result < tonic :: Response < super :: QueryParamsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Params") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Deposit queries single deposit information based proposalID, depositAddr."] pub async fn deposit (& mut self , request : impl tonic :: IntoRequest < super :: QueryDepositRequest > ,) -> Result < tonic :: Response < super :: QueryDepositResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Deposit") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Deposits queries all deposits of a single proposal."] pub async fn deposits (& mut self , request : impl tonic :: IntoRequest < super :: QueryDepositsRequest > ,) -> Result < tonic :: Response < super :: QueryDepositsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Deposits") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " TallyResult queries the tally of a proposal vote."] pub async fn tally_result (& mut self , request : impl tonic :: IntoRequest < super :: QueryTallyResultRequest > ,) -> Result < tonic :: Response < super :: QueryTallyResultResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/TallyResult") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for QueryClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for QueryClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "QueryClient {{ ... }}") } } }/// GenesisState defines the gov module's genesis state. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GenesisState { + /// starting_proposal_id is the ID of the starting proposal. + #[prost(uint64, tag="1")] + pub starting_proposal_id: u64, + /// deposits defines all the deposits present at genesis. + #[prost(message, repeated, tag="2")] + pub deposits: ::prost::alloc::vec::Vec, + /// votes defines all the votes present at genesis. + #[prost(message, repeated, tag="3")] + pub votes: ::prost::alloc::vec::Vec, + /// proposals defines all the proposals present at genesis. + #[prost(message, repeated, tag="4")] + pub proposals: ::prost::alloc::vec::Vec, + /// params defines all the paramaters of related to deposit. + #[prost(message, optional, tag="5")] + pub deposit_params: ::core::option::Option, + /// params defines all the paramaters of related to voting. + #[prost(message, optional, tag="6")] + pub voting_params: ::core::option::Option, + /// params defines all the paramaters of related to tally. + #[prost(message, optional, tag="7")] + pub tally_params: ::core::option::Option, +} diff --git a/relayer-cli/src/commands/tx.rs b/relayer-cli/src/commands/tx.rs index cb64a1c63e..b0008a7fe0 100644 --- a/relayer-cli/src/commands/tx.rs +++ b/relayer-cli/src/commands/tx.rs @@ -8,6 +8,7 @@ mod client; mod connection; mod packet; mod transfer; +mod upgrade; /// `tx` subcommand #[derive(Command, Debug, Options, Runnable)] @@ -90,4 +91,8 @@ pub enum TxRawCommands { /// The `tx raw packet-ack` subcommand #[options(help = "Relay acknowledgment packets")] PacketAck(packet::TxRawPacketAckCmd), + + /// The `tx raw upgrade-chain` subcommand + #[options(help = "Send an upgrade plan")] + UpgradeChain(upgrade::TxUpgradeChainCmd), } diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index a405d8bf7a..5f8a6363a8 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -120,7 +120,7 @@ pub struct TxUpgradeClientCmd { required, help = "identifier of the client to be upgraded on destination chain" )] - client_id: ClientId, + dst_client_id: ClientId, } impl Runnable for TxUpgradeClientCmd { @@ -140,7 +140,7 @@ impl Runnable for TxUpgradeClientCmd { // Instantiate the client hosted on the destination chain, which is targeting headers for // the source chain. - let client = ForeignClient::find(chains.src, chains.dst, &self.client_id) + let client = ForeignClient::find(chains.src, chains.dst, &self.dst_client_id) .unwrap_or_else(exit_with_unrecoverable_error); let outcome = client.upgrade(); diff --git a/relayer-cli/src/commands/tx/upgrade.rs b/relayer-cli/src/commands/tx/upgrade.rs new file mode 100644 index 0000000000..7f2d890677 --- /dev/null +++ b/relayer-cli/src/commands/tx/upgrade.rs @@ -0,0 +1,101 @@ +use std::sync::Arc; + +use abscissa_core::{Command, Options, Runnable}; +use tokio::runtime::Runtime as TokioRuntime; + +use ibc::events::IbcEvent; +use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc_relayer::upgrade_chain::{build_and_send_upgrade_chain_message, UpdatePlanOptions}; +use ibc_relayer::{ + chain::{Chain, CosmosSdkChain}, + config::Config, +}; + +use crate::conclude::Output; +use crate::error::{Error, Kind}; +use crate::prelude::*; + +#[derive(Clone, Command, Debug, Options)] +pub struct TxUpgradeChainCmd { + #[options(free, required, help = "identifier of the chain to upgrade")] + dst_chain_id: ChainId, + + #[options(free, required, help = "identifier of the source chain")] + src_chain_id: ChainId, + + #[options( + free, + required, + help = "identifier of the client on source chain from which the plan is created" + )] + src_client_id: ClientId, + + #[options(free, required, help = "amount of stake")] + amount: u64, + + #[options( + free, + required, + help = "upgrade height offset in number of blocks since current" + )] + height_offset: u64, +} + +impl TxUpgradeChainCmd { + fn validate_options(&self, config: &Config) -> Result { + let src_chain_config = config + .find_chain(&self.src_chain_id) + .ok_or_else(|| "missing src chain configuration".to_string())?; + + let dst_chain_config = config + .find_chain(&self.dst_chain_id) + .ok_or_else(|| "missing destination chain configuration".to_string())?; + + let opts = UpdatePlanOptions { + dst_chain_config: dst_chain_config.clone(), + src_chain_config: src_chain_config.clone(), + src_client_id: self.src_client_id.clone(), + amount: self.amount, + height_offset: self.height_offset, + }; + + Ok(opts) + } +} + +impl Runnable for TxUpgradeChainCmd { + fn run(&self) { + let config = app_config(); + + let opts = match self.validate_options(&config) { + Err(err) => return Output::error(err).exit(), + Ok(result) => result, + }; + info!("Message {:?}", opts); + + let rt = Arc::new(TokioRuntime::new().unwrap()); + + let src_chain_res = CosmosSdkChain::bootstrap(opts.src_chain_config.clone(), rt.clone()) + .map_err(|e| Kind::Runtime.context(e)); + let src_chain = match src_chain_res { + Ok(chain) => chain, + Err(e) => return Output::error(format!("{}", e)).exit(), + }; + + let dst_chain_res = CosmosSdkChain::bootstrap(opts.dst_chain_config.clone(), rt) + .map_err(|e| Kind::Runtime.context(e)); + let dst_chain = match dst_chain_res { + Ok(chain) => chain, + Err(e) => return Output::error(format!("{}", e)).exit(), + }; + + let res: Result, Error> = + build_and_send_upgrade_chain_message(dst_chain, src_chain, &opts) + .map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(ev) => Output::success(ev).exit(), + Err(e) => Output::error(format!("{}", e)).exit(), + } + } +} diff --git a/relayer/src/lib.rs b/relayer/src/lib.rs index 472f9496fd..e14841c1fb 100644 --- a/relayer/src/lib.rs +++ b/relayer/src/lib.rs @@ -27,4 +27,5 @@ pub mod macros; pub mod relay; pub mod supervisor; pub mod transfer; +pub mod upgrade_chain; pub mod util; diff --git a/relayer/src/upgrade_chain.rs b/relayer/src/upgrade_chain.rs new file mode 100644 index 0000000000..963ba26444 --- /dev/null +++ b/relayer/src/upgrade_chain.rs @@ -0,0 +1,124 @@ +use bitcoin::hashes::core::time::Duration; +use prost_types::Any; +use thiserror::Error; +use tracing::error; + +use ibc::events::IbcEvent; +use ibc::ics02_client::client_state::AnyClientState; +use ibc::ics02_client::height::Height; +use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc_proto::cosmos::gov::v1beta1::MsgSubmitProposal; +use ibc_proto::cosmos::upgrade::v1beta1::{Plan, SoftwareUpgradeProposal}; + +use crate::chain::{Chain, CosmosSdkChain}; +use crate::config::ChainConfig; +use crate::error::Error; + +#[derive(Debug, Error)] +pub enum UpgradeChainError { + #[error("failed with underlying cause: {0}")] + Failed(String), + + #[error("key error with underlying cause: {0}")] + KeyError(Error), + + #[error( + "failed during a transaction submission step to chain id {0} with underlying error: {1}" + )] + SubmitError(ChainId, Error), +} + +#[derive(Clone, Debug)] +pub struct UpdatePlanOptions { + pub src_chain_config: ChainConfig, + pub dst_chain_config: ChainConfig, + pub src_client_id: ClientId, + pub amount: u64, + pub height_offset: u64, +} + +pub fn build_and_send_upgrade_chain_message( + mut dst_chain: CosmosSdkChain, // the chain whose account is debited + src_chain: CosmosSdkChain, // the chain where the transfer is sent + opts: &UpdatePlanOptions, +) -> Result, UpgradeChainError> { + // build a proposal Plan + let upgrade_height = dst_chain + .query_latest_height() + .unwrap() + .add(opts.height_offset); + let client_state = src_chain + .query_client_state(&opts.src_client_id, Height::zero()) + .unwrap(); + let mut upgraded_client_state = client_state.zero_custom_fields(); + upgraded_client_state.latest_height = upgrade_height.increment(); + upgraded_client_state.unbonding_period = Duration::from_secs(400 * 3600); // TODO add to options + + let raw_client_state = AnyClientState::Tendermint(upgraded_client_state); + let plan = Plan { + name: "test".to_string(), + time: None, + height: upgrade_height.revision_height as i64, + info: "upgrade the chain software and unbonding period".to_string(), + upgraded_client_state: Some(Any::from(raw_client_state)), + }; + + // build the proposal + let proposal = SoftwareUpgradeProposal { + title: "upgrade_ibc_clients".to_string(), + description: "upgrade the chain software and unbonding period".to_string(), + plan: Some(plan), + }; + let mut buf_proposal = Vec::new(); + prost::Message::encode(&proposal, &mut buf_proposal).unwrap(); + let any_proposal = Any { + type_url: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal".to_string(), + value: buf_proposal, + }; + + // build the msg submit proposal + let proposer = dst_chain + .get_signer() + .map_err(UpgradeChainError::KeyError)?; + + let coins = ibc_proto::cosmos::base::v1beta1::Coin { + denom: "stake".to_string(), + amount: opts.amount.to_string(), + }; + + let msg = MsgSubmitProposal { + content: Some(any_proposal), + initial_deposit: vec![coins], + proposer: proposer.to_string(), + }; + + let mut buf_msg = Vec::new(); + prost::Message::encode(&msg, &mut buf_msg).unwrap(); + let any_msg = Any { + type_url: "/cosmos.gov.v1beta1.MsgSubmitProposal".to_string(), + value: buf_msg, + }; + + let events = dst_chain + .send_msgs(vec![any_msg]) + .map_err(|e| UpgradeChainError::SubmitError(dst_chain.id().clone(), e))?; + + // Check if the chain rejected the transaction + let result = events + .iter() + .find(|event| matches!(event, IbcEvent::ChainError(_))); + + match result { + None => Ok(events), + Some(err) => { + if let IbcEvent::ChainError(err) = err { + Err(UpgradeChainError::Failed(err.to_string())) + } else { + panic!( + "internal error, expected IBCEvent::ChainError, got {:?}", + err + ) + } + } + } +} diff --git a/scripts/one-chain b/scripts/one-chain index ece3958310..e7e0fed600 100755 --- a/scripts/one-chain +++ b/scripts/one-chain @@ -105,6 +105,7 @@ fi # Set proper defaults and change ports (use a different sed for Mac or Linux) echo "Change settings in config.toml file..." if [ $platform = 'linux' ]; then + sed -i 's#"172800s"#"200s"#g' $CHAIN_DIR/$CHAIN_ID/config/genesis.json sed -i 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPC_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2P_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i 's#"localhost:6060"#"localhost:'"$PROF_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml @@ -113,6 +114,7 @@ if [ $platform = 'linux' ]; then sed -i 's/index_all_keys = false/index_all_keys = true/g' $CHAIN_DIR/$CHAIN_ID/config/config.toml # sed -i '' 's#index-events = \[\]#index-events = \["message.action","send_packet.packet_src_channel","send_packet.packet_sequence"\]#g' $CHAIN_DIR/$CHAIN_ID/config/app.toml else + sed -i '' 's#"172800s"#"200s"#g' $CHAIN_DIR/$CHAIN_ID/config/genesis.json sed -i '' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPC_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i '' 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2P_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i '' 's#"localhost:6060"#"localhost:'"$PROF_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml From d3827ec8cff9da602e43ecc85d67889c70f87fee Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 23 Mar 2021 20:22:23 +0100 Subject: [PATCH 24/26] Remove a clone and turn zero_custom_fields into a static method --- modules/src/ics07_tendermint/client_state.rs | 11 +++++------ relayer/src/upgrade_chain.rs | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/src/ics07_tendermint/client_state.rs b/modules/src/ics07_tendermint/client_state.rs index 88fcc9db4b..6a3058f3fd 100644 --- a/modules/src/ics07_tendermint/client_state.rs +++ b/modules/src/ics07_tendermint/client_state.rs @@ -106,11 +106,10 @@ impl ClientState { } } - // Helper function to verify the upgrade client procedure, it resets all fields except the - // blockchain specific ones. - pub fn zero_custom_fields(&self) -> ClientState { - let mut client_state = self.clone(); - client_state.trusting_period = Duration::default(); + /// Helper function to verify the upgrade client procedure. + /// Resets all fields except the blockchain-specific ones. + pub fn zero_custom_fields(mut client_state: Self) -> Self { + client_state.trusting_period = Duration::from_secs(0); client_state.trust_level = TrustThresholdFraction { numerator: 0, denominator: 0, @@ -118,7 +117,7 @@ impl ClientState { client_state.allow_update_after_expiry = false; client_state.allow_update_after_misbehaviour = false; client_state.frozen_height = Height::zero(); - client_state.max_clock_drift = Duration::default(); + client_state.max_clock_drift = Duration::from_secs(0); client_state } } diff --git a/relayer/src/upgrade_chain.rs b/relayer/src/upgrade_chain.rs index 963ba26444..fe1c5159fd 100644 --- a/relayer/src/upgrade_chain.rs +++ b/relayer/src/upgrade_chain.rs @@ -3,10 +3,10 @@ use prost_types::Any; use thiserror::Error; use tracing::error; -use ibc::events::IbcEvent; use ibc::ics02_client::client_state::AnyClientState; use ibc::ics02_client::height::Height; use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc::{events::IbcEvent, ics07_tendermint::client_state::ClientState}; use ibc_proto::cosmos::gov::v1beta1::MsgSubmitProposal; use ibc_proto::cosmos::upgrade::v1beta1::{Plan, SoftwareUpgradeProposal}; @@ -50,7 +50,7 @@ pub fn build_and_send_upgrade_chain_message( let client_state = src_chain .query_client_state(&opts.src_client_id, Height::zero()) .unwrap(); - let mut upgraded_client_state = client_state.zero_custom_fields(); + let mut upgraded_client_state = ClientState::zero_custom_fields(client_state); upgraded_client_state.latest_height = upgrade_height.increment(); upgraded_client_state.unbonding_period = Duration::from_secs(400 * 3600); // TODO add to options From 5f751b0f8c441e2ea5dda4910a5a26dc9d9aac1d Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 23 Mar 2021 20:27:49 +0100 Subject: [PATCH 25/26] Whitespace and nitpick --- relayer/src/upgrade_chain.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/relayer/src/upgrade_chain.rs b/relayer/src/upgrade_chain.rs index fe1c5159fd..27b3e7d9db 100644 --- a/relayer/src/upgrade_chain.rs +++ b/relayer/src/upgrade_chain.rs @@ -47,9 +47,11 @@ pub fn build_and_send_upgrade_chain_message( .query_latest_height() .unwrap() .add(opts.height_offset); + let client_state = src_chain .query_client_state(&opts.src_client_id, Height::zero()) .unwrap(); + let mut upgraded_client_state = ClientState::zero_custom_fields(client_state); upgraded_client_state.latest_height = upgrade_height.increment(); upgraded_client_state.unbonding_period = Duration::from_secs(400 * 3600); // TODO add to options @@ -69,8 +71,10 @@ pub fn build_and_send_upgrade_chain_message( description: "upgrade the chain software and unbonding period".to_string(), plan: Some(plan), }; + let mut buf_proposal = Vec::new(); prost::Message::encode(&proposal, &mut buf_proposal).unwrap(); + let any_proposal = Any { type_url: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal".to_string(), value: buf_proposal, @@ -104,21 +108,13 @@ pub fn build_and_send_upgrade_chain_message( .map_err(|e| UpgradeChainError::SubmitError(dst_chain.id().clone(), e))?; // Check if the chain rejected the transaction - let result = events - .iter() - .find(|event| matches!(event, IbcEvent::ChainError(_))); + let result = events.iter().find_map(|event| match event { + IbcEvent::ChainError(reason) => Some(reason.clone()), + _ => None, + }); match result { None => Ok(events), - Some(err) => { - if let IbcEvent::ChainError(err) = err { - Err(UpgradeChainError::Failed(err.to_string())) - } else { - panic!( - "internal error, expected IBCEvent::ChainError, got {:?}", - err - ) - } - } + Some(reason) => Err(UpgradeChainError::Failed(reason)), } } From 4187c40ce2064f2d0604b7d3de12f7e7e4bd7978 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 23 Mar 2021 21:04:13 +0100 Subject: [PATCH 26/26] Remove obsolete TODO --- relayer/src/upgrade_chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/src/upgrade_chain.rs b/relayer/src/upgrade_chain.rs index 27b3e7d9db..bb214597f3 100644 --- a/relayer/src/upgrade_chain.rs +++ b/relayer/src/upgrade_chain.rs @@ -54,7 +54,7 @@ pub fn build_and_send_upgrade_chain_message( let mut upgraded_client_state = ClientState::zero_custom_fields(client_state); upgraded_client_state.latest_height = upgrade_height.increment(); - upgraded_client_state.unbonding_period = Duration::from_secs(400 * 3600); // TODO add to options + upgraded_client_state.unbonding_period = Duration::from_secs(400 * 3600); let raw_client_state = AnyClientState::Tendermint(upgraded_client_state); let plan = Plan {