Skip to content

Commit

Permalink
Compatibility with Cosmos SDK v0.43+ (informalsystems#948)
Browse files Browse the repository at this point in the history
* Adapted proto compiler. Regenerated Rust files

* Adapt to newer namespace for apps.

* Manual fix for Staking erroneous compilation

* Workaround: disabled chain upgrade

* Added specific GRPC-web port to prevent conflict on 9091.

* Upgrade protos

* Add support for new message action strings

* Improved output for one-chain script

* CLI for chain upgrade proposal adapted to gaia v5.

* Re-generated proto files with updated proto-compiler (fmt enabled).

* Manual fix for staking::Validators erroneous compilation; added the .applications.transfer generated file

* Quick feature for parametrizing upgraded chain

* Clippy fix. Meta for params for better display.

* Adapted query_upgraded_client_state

* Adapted query_upgraded_consensus_state to ibc-go-v1

* Moved from gRPC to ABCI

* Adapted send_tx_simulate to new def of SimulateRequest

* Added parametrizable upgrade plan name

* Fix unwraps. Retain unbonding period if none specified.

* Missing post-merge bracket

* Removed register and send match arms for rpc events

* Support for new message.actions strings of ICS27

Based on discussion from
cosmos/cosmos-sdk#9139 (comment)
This is not yet tested!

* Update protos sdk(v0.43.0-rc2) & ibc-go(v1.0.0-rc3)

* Revert "Added specific GRPC-web port to prevent conflict on 9091."

This reverts commit abf6dba.

* Add missing fields in upgrade Plan

* Fix clippy warnings

* Added another action field to match intertx MsgSend

* Update protos sdk(v0.43.0) & ibc-go(v1.0.0)

* Update cosmos SDK module version requirement in compatibility.rs

* Add legacy upgrade support (informalsystems#1289)

* Add legacy upgrade support

* Fix check for legacy

* Update .changelog

* Add TODO

* Apply suggestion

Co-authored-by: Adi Seredinschi <adi@informal.systems>

Co-authored-by: Adi Seredinschi <adi@informal.systems>

* Apply suggestions

* Update .changelog

* Fix .changelog entry

Co-authored-by: Adi Seredinschi <adi@informal.systems>
Co-authored-by: Shoaib Ahmed <sufialhussaini@gmail.com>
  • Loading branch information
3 people authored Aug 19, 2021
1 parent bb59bcc commit a29805c
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 154 deletions.
3 changes: 3 additions & 0 deletions .changelog/unreleased/features/1287-upgrade-legacy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Add `--legacy | -l` flag to support upgrades for chains built with Cosmos SDK < v0.43.0 ([#1287])

[#1287]: https://github.com/informalsystems/ibc-rs/issues/1287
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Upgrade to Cosmos SDK proto (v0.43.0) & ibc-go proto (v1.0.0) ([#948])

- [#948]: https://github.com/informalsystems/ibc-rs/pull/948
4 changes: 2 additions & 2 deletions relayer-cli/src/commands/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ pub enum TxRawCommands {
PacketAck(packet::TxRawPacketAckCmd),

/// The `tx raw upgrade-chain` subcommand
#[options(help = "Send an upgrade plan")]
UpgradeChain(upgrade::TxUpgradeChainCmd),
#[options(help = "Send an IBC upgrade plan")]
UpgradeChain(upgrade::TxIbcUpgradeChainCmd),
}

impl Override<Config> for TxCmd {
Expand Down
52 changes: 45 additions & 7 deletions relayer-cli/src/commands/tx/upgrade.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::sync::Arc;
use std::time::Duration;

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::upgrade_chain::{build_and_send_ibc_upgrade_proposal, UpgradePlanOptions};
use ibc_relayer::{
chain::{ChainEndpoint, CosmosSdkChain},
config::Config,
Expand All @@ -16,7 +17,7 @@ use crate::error::Error;
use crate::prelude::*;

#[derive(Clone, Command, Debug, Options)]
pub struct TxUpgradeChainCmd {
pub struct TxIbcUpgradeChainCmd {
#[options(free, required, help = "identifier of the chain to upgrade")]
dst_chain_id: ChainId,

Expand All @@ -39,10 +40,37 @@ pub struct TxUpgradeChainCmd {
help = "upgrade height offset in number of blocks since current"
)]
height_offset: u64,

#[options(
short = "c",
meta = "CHAIN-ID",
help = "new chain identifier to assign to the upgrading chain (optional)"
)]
new_chain_id: Option<ChainId>,

#[options(
short = "u",
meta = "PERIOD",
help = "new unbonding period to assign to the upgrading chain, in seconds (optional)"
)]
new_unbonding: Option<u64>,

#[options(
short = "n",
meta = "NAME",
help = "a string to name the upgrade proposal plan (default: 'plan')"
)]
upgrade_name: Option<String>,

#[options(
help = "use legacy upgrade proposal constructs (for chains built with Cosmos SDK < v0.43.0)",
short = "l"
)]
legacy: bool,
}

impl TxUpgradeChainCmd {
fn validate_options(&self, config: &Config) -> Result<UpdatePlanOptions, String> {
impl TxIbcUpgradeChainCmd {
fn validate_options(&self, config: &Config) -> Result<UpgradePlanOptions, String> {
let src_chain_config = config
.find_chain(&self.src_chain_id)
.ok_or_else(|| "missing src chain configuration".to_string())?;
Expand All @@ -51,19 +79,29 @@ impl TxUpgradeChainCmd {
.find_chain(&self.dst_chain_id)
.ok_or_else(|| "missing destination chain configuration".to_string())?;

let opts = UpdatePlanOptions {
let opts = UpgradePlanOptions {
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,
upgraded_chain_id: self
.new_chain_id
.clone()
.unwrap_or_else(|| self.dst_chain_id.clone()),
upgraded_unbonding_period: self.new_unbonding.map(Duration::from_secs),
upgrade_plan_name: self
.upgrade_name
.clone()
.unwrap_or_else(|| "plan".to_string()),
legacy: self.legacy,
};

Ok(opts)
}
}

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

Expand All @@ -90,7 +128,7 @@ impl Runnable for TxUpgradeChainCmd {
};

let res: Result<Vec<IbcEvent>, Error> =
build_and_send_upgrade_chain_message(dst_chain, src_chain, &opts)
build_and_send_ibc_upgrade_proposal(dst_chain, src_chain, &opts)
.map_err(Error::upgrade_chain);

match res {
Expand Down
121 changes: 43 additions & 78 deletions relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use ibc::ics02_client::client_consensus::{
AnyConsensusState, AnyConsensusStateWithHeight, QueryClientEventRequest,
};
use ibc::ics02_client::client_state::{AnyClientState, IdentifiedAnyClientState};
use ibc::ics02_client::client_type::ClientType;
use ibc::ics02_client::events as ClientEvents;
use ibc::ics03_connection::connection::{ConnectionEnd, IdentifiedConnectionEnd};
use ibc::ics04_channel::channel::{ChannelEnd, IdentifiedChannelEnd, QueryPacketEventDataRequest};
Expand All @@ -58,9 +59,6 @@ use ibc_proto::cosmos::tx::v1beta1::{
AuthInfo, Fee, ModeInfo, SignDoc, SignerInfo, SimulateRequest, SimulateResponse, Tx, TxBody,
TxRaw,
};
use ibc_proto::cosmos::upgrade::v1beta1::{
QueryCurrentPlanRequest, QueryUpgradedConsensusStateRequest,
};
use ibc_proto::ibc::core::channel::v1::{
PacketState, QueryChannelClientStateRequest, QueryChannelsRequest,
QueryConnectionChannelsRequest, QueryNextSequenceReceiveRequest,
Expand Down Expand Up @@ -335,12 +333,10 @@ impl CosmosSdkChain {
// if the batch is split in two TX-es, the second one will fail the simulation in `deliverTx` check
// In this case we just leave the gas un-adjusted, i.e. use `self.max_gas()`
let estimated_gas = self
.send_tx_simulate(SimulateRequest {
tx: Some(Tx {
body: Some(body),
auth_info: Some(auth_info),
signatures: vec![signed_doc],
}),
.send_tx_simulate(Tx {
body: Some(body),
auth_info: Some(auth_info),
signatures: vec![signed_doc],
})
.map_or(self.max_gas(), |sr| {
sr.gas_info.map_or(self.max_gas(), |g| g.gas_used)
Expand Down Expand Up @@ -452,12 +448,17 @@ impl CosmosSdkChain {
Ok(response)
}

// Perform an ABCI query against the client upgrade sub-store to fetch a proof.
fn query_client_upgrade_proof(
/// Perform an ABCI query against the client upgrade sub-store.
/// Fetches both the target data, as well as the proof.
///
/// The data is returned in its raw format `Vec<u8>`, and is either
/// the client state (if the target path is [`UpgradedClientState`]),
/// or the client consensus state ([`UpgradedClientConsensusState`]).
fn query_client_upgrade_state(
&self,
data: ClientUpgradePath,
height: Height,
) -> Result<(MerkleProof, ICSHeight), Error> {
) -> Result<(Vec<u8>, MerkleProof), Error> {
let prev_height = Height::try_from(height.value() - 1).map_err(Error::invalid_height)?;

let path = TendermintABCIPath::from_str(SDK_UPGRADE_QUERY_PATH).unwrap();
Expand All @@ -471,17 +472,19 @@ impl CosmosSdkChain {

let proof = response.proof.ok_or_else(Error::empty_response_proof)?;

let height = ICSHeight::new(
self.config.id.version(),
response.height.increment().value(),
);

Ok((proof, height))
Ok((response.value, proof))
}

fn send_tx_simulate(&self, request: SimulateRequest) -> Result<SimulateResponse, Error> {
fn send_tx_simulate(&self, tx: Tx) -> Result<SimulateResponse, Error> {
crate::time!("tx simulate");

// The `tx` field of `SimulateRequest` was deprecated
// in favor of `tx_bytes`
let mut tx_bytes = vec![];
prost::Message::encode(&tx, &mut tx_bytes).unwrap();
#[allow(deprecated)]
let req = SimulateRequest { tx: None, tx_bytes };

let mut client = self
.block_on(
ibc_proto::cosmos::tx::v1beta1::service_client::ServiceClient::connect(
Expand All @@ -490,7 +493,7 @@ impl CosmosSdkChain {
)
.map_err(Error::grpc_transport)?;

let request = tonic::Request::new(request);
let request = tonic::Request::new(req);
let response = self
.block_on(client.simulate(request))
.map_err(Error::grpc_status)?
Expand Down Expand Up @@ -954,39 +957,20 @@ impl ChainEndpoint for CosmosSdkChain {
) -> Result<(Self::ClientState, MerkleProof), Error> {
crate::time!("query_upgraded_client_state");

let mut client = self
.block_on(
ibc_proto::cosmos::upgrade::v1beta1::query_client::QueryClient::connect(
self.grpc_addr.clone(),
),
)
.map_err(Error::grpc_transport)?;

let req = tonic::Request::new(QueryCurrentPlanRequest {});
let response = self
.block_on(client.current_plan(req))
.map_err(Error::grpc_status)?;

let upgraded_client_state_raw = response
.into_inner()
.plan
.ok_or_else(Error::empty_response_value)?
.upgraded_client_state
.ok_or_else(Error::empty_upgraded_client_state)?;
let client_state = AnyClientState::try_from(upgraded_client_state_raw)
.map_err(Error::invalid_upgraded_client_state)?;

// TODO: Better error kinds here.
let tm_client_state = downcast!(client_state.clone() => AnyClientState::Tendermint)
.ok_or_else(|| Error::client_state_type(format!("{:?}", client_state)))?;

// Query for the proof.
// Query for the value and the proof.
let tm_height = Height::try_from(height.revision_height).map_err(Error::invalid_height)?;
let (proof, _proof_height) = self.query_client_upgrade_proof(
let (upgraded_client_state_raw, proof) = self.query_client_upgrade_state(
ClientUpgradePath::UpgradedClientState(height.revision_height),
tm_height,
)?;

let client_state = AnyClientState::decode_vec(&upgraded_client_state_raw)
.map_err(Error::conversion_from_any)?;

let client_type = client_state.client_type();
let tm_client_state = downcast!(client_state => AnyClientState::Tendermint)
.ok_or_else(|| Error::client_type_mismatch(ClientType::Tendermint, client_type))?;

Ok((tm_client_state, proof))
}

Expand All @@ -998,40 +982,21 @@ impl ChainEndpoint for CosmosSdkChain {

let tm_height = Height::try_from(height.revision_height).map_err(Error::invalid_height)?;

let mut client = self
.block_on(
ibc_proto::cosmos::upgrade::v1beta1::query_client::QueryClient::connect(
self.grpc_addr.clone(),
),
)
.map_err(Error::grpc_transport)?;

let req = tonic::Request::new(QueryUpgradedConsensusStateRequest {
last_height: tm_height.into(),
});
let response = self
.block_on(client.upgraded_consensus_state(req))
.map_err(Error::grpc_status)?;

let upgraded_consensus_state_raw = response
.into_inner()
.upgraded_consensus_state
.ok_or_else(Error::empty_response_value)?;

// 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(Error::ics02)?;

let tm_consensus_state =
downcast!(consensus_state.clone() => AnyConsensusState::Tendermint)
.ok_or_else(|| Error::client_state_type(format!("{:?}", consensus_state)))?;

// Fetch the proof.
let (proof, _proof_height) = self.query_client_upgrade_proof(
// Fetch the consensus state and its proof.
let (upgraded_consensus_state_raw, proof) = self.query_client_upgrade_state(
ClientUpgradePath::UpgradedClientConsensusState(height.revision_height),
tm_height,
)?;

let consensus_state = AnyConsensusState::decode_vec(&upgraded_consensus_state_raw)
.map_err(Error::conversion_from_any)?;

let cs_client_type = consensus_state.client_type();
let tm_consensus_state = downcast!(consensus_state => AnyConsensusState::Tendermint)
.ok_or_else(|| {
Error::consensus_state_type_mismatch(ClientType::Tendermint, cs_client_type)
})?;

Ok((tm_consensus_state, proof))
}

Expand Down
2 changes: 1 addition & 1 deletion relayer/src/chain/cosmos/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const SDK_MODULE_NAME: &str = "cosmos/cosmos-sdk";
/// # Note: Should be consistent with [features] guide page.
///
/// [features]: https://hermes.informal.systems/features.html
const SDK_MODULE_VERSION_REQ: &str = ">=0.41.3, <=0.42.9";
const SDK_MODULE_VERSION_REQ: &str = ">=0.41.3, <=0.43.0";

/// Helper struct to capture all the reported information of an
/// IBC application, e.g., `gaiad`.
Expand Down
16 changes: 11 additions & 5 deletions relayer/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,19 @@ define_error! {
Event
|_| { "Bad Notification" },

ConversionFromAny
[ TraceError<TendermintProtoError> ]
|_| { "Conversion from a protobuf `Any` into a domain type failed" },

EmptyUpgradedClientState
|_| { "The upgrade plan specifies no upgraded client state" },
|_| { "Found no upgraded client state" },

InvalidUpgradedClientState
[ client_error::Error ]
|e| { format!("the upgrade plan specifies an invalid upgraded client state: {}", e.source) },
ConsensusStateTypeMismatch
{
expected: ClientType,
got: ClientType,
}
|e| { format!("consensus state type mismatch; hint: expected client type '{0}', got '{1}'", e.expected, e.got) },

EmptyResponseValue
|_| { "Empty response value" },
Expand Down Expand Up @@ -415,7 +422,6 @@ define_error! {
format!("Hermes health check failed while verifying the application compatibility for chain {0}:{1}; caused by: {2}",
e.chain_id, e.address, e.cause)
},

}
}

Expand Down
Loading

0 comments on commit a29805c

Please sign in to comment.