diff --git a/.changelog/unreleased/breaking-changes/628-split-msgupdateclient.md b/.changelog/unreleased/breaking-changes/628-split-msgupdateclient.md new file mode 100644 index 000000000..89856939a --- /dev/null +++ b/.changelog/unreleased/breaking-changes/628-split-msgupdateclient.md @@ -0,0 +1,2 @@ +- Split `MsgUpdateClient` back into `MsgUpdateClient` and `MsgSubmitMisbehaviour` + ([#628](https://github.com/cosmos/ibc-rs/issues/628)) diff --git a/crates/ibc/src/clients/ics07_tendermint/client_state.rs b/crates/ibc/src/clients/ics07_tendermint/client_state.rs index 22ba8fb86..94b3f74db 100644 --- a/crates/ibc/src/clients/ics07_tendermint/client_state.rs +++ b/crates/ibc/src/clients/ics07_tendermint/client_state.rs @@ -1,7 +1,6 @@ mod misbehaviour; mod update_client; -use crate::core::ics02_client::msgs::update_client::UpdateKind; use crate::prelude::*; use core::cmp::max; @@ -26,7 +25,9 @@ use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConse use crate::clients::ics07_tendermint::error::Error; use crate::clients::ics07_tendermint::header::Header as TmHeader; use crate::clients::ics07_tendermint::misbehaviour::Misbehaviour as TmMisbehaviour; -use crate::core::ics02_client::client_state::{ClientState as Ics2ClientState, UpdatedState}; +use crate::core::ics02_client::client_state::{ + ClientState as Ics2ClientState, UpdateKind, UpdatedState, +}; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics02_client::error::ClientError; @@ -306,13 +307,9 @@ impl Ics2ClientState for ClientState { &self, ctx: &mut dyn ExecutionContext, client_id: &ClientId, - client_message: Any, - _update_kind: &UpdateKind, + header: Any, ) -> Result, ClientError> { - // we only expect headers to make it here; if a TmMisbehaviour makes it to here, - // then it is not a valid misbehaviour (check_for_misbehaviour returned false), - // and so transaction should fail. - let header = TmHeader::try_from(client_message)?; + let header = TmHeader::try_from(header)?; let header_height = header.height(); let maybe_existing_consensus_state = { diff --git a/crates/ibc/src/core/context.rs b/crates/ibc/src/core/context.rs index a9d4f1fe9..daffc2e9e 100644 --- a/crates/ibc/src/core/context.rs +++ b/crates/ibc/src/core/context.rs @@ -22,6 +22,7 @@ use self::acknowledgement::{acknowledgement_packet_execute, acknowledgement_pack use self::recv_packet::{recv_packet_execute, recv_packet_validate}; use self::timeout::{timeout_packet_execute, timeout_packet_validate, TimeoutMsgType}; +use super::ics02_client::msgs::MsgUpdateOrMisbehaviour; use super::{ ics02_client::error::ClientError, ics03_connection::error::ConnectionError, @@ -173,7 +174,12 @@ pub trait ValidationContext: Router { match msg { MsgEnvelope::Client(msg) => match msg { ClientMsg::CreateClient(msg) => create_client::validate(self, msg), - ClientMsg::UpdateClient(msg) => update_client::validate(self, msg), + ClientMsg::UpdateClient(msg) => { + update_client::validate(self, MsgUpdateOrMisbehaviour::UpdateClient(msg)) + } + ClientMsg::Misbehaviour(msg) => { + update_client::validate(self, MsgUpdateOrMisbehaviour::Misbehaviour(msg)) + } ClientMsg::UpgradeClient(msg) => upgrade_client::validate(self, msg), } .map_err(RouterError::ContextError), @@ -381,7 +387,12 @@ pub trait ExecutionContext: ValidationContext { match msg { MsgEnvelope::Client(msg) => match msg { ClientMsg::CreateClient(msg) => create_client::execute(self, msg), - ClientMsg::UpdateClient(msg) => update_client::execute(self, msg), + ClientMsg::UpdateClient(msg) => { + update_client::execute(self, MsgUpdateOrMisbehaviour::UpdateClient(msg)) + } + ClientMsg::Misbehaviour(msg) => { + update_client::execute(self, MsgUpdateOrMisbehaviour::Misbehaviour(msg)) + } ClientMsg::UpgradeClient(msg) => upgrade_client::execute(self, msg), } .map_err(RouterError::ContextError), diff --git a/crates/ibc/src/core/handler.rs b/crates/ibc/src/core/handler.rs index 2783c2d26..d8b9facae 100644 --- a/crates/ibc/src/core/handler.rs +++ b/crates/ibc/src/core/handler.rs @@ -50,7 +50,6 @@ mod tests { msgs::transfer::test_util::get_dummy_msg_transfer, msgs::transfer::MsgTransfer, packet::PacketData, PrefixedCoin, MODULE_ID_STR, }; - use crate::core::ics02_client::msgs::update_client::UpdateKind; use crate::core::ics02_client::msgs::{ create_client::MsgCreateClient, update_client::MsgUpdateClient, upgrade_client::MsgUpgradeClient, ClientMsg, @@ -270,10 +269,9 @@ mod tests { name: "Client update successful".to_string(), msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height) + header: MockHeader::new(update_client_height) .with_timestamp(Timestamp::now()) .into(), - update_kind: UpdateKind::UpdateClient, signer: default_signer.clone(), })) .into(), @@ -284,8 +282,7 @@ mod tests { name: "Client update fails due to stale header".to_string(), msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height).into(), - update_kind: UpdateKind::UpdateClient, + header: MockHeader::new(update_client_height).into(), signer: default_signer.clone(), })) .into(), @@ -360,10 +357,9 @@ mod tests { name: "Client update successful #2".to_string(), msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height_after_send) + header: MockHeader::new(update_client_height_after_send) .with_timestamp(Timestamp::now()) .into(), - update_kind: UpdateKind::UpdateClient, signer: default_signer.clone(), })) .into(), @@ -406,8 +402,7 @@ mod tests { name: "Client update successful".to_string(), msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height_after_second_send).into(), - update_kind: UpdateKind::UpdateClient, + header: MockHeader::new(update_client_height_after_second_send).into(), signer: default_signer.clone(), })) .into(), diff --git a/crates/ibc/src/core/ics02_client/client_state.rs b/crates/ibc/src/core/ics02_client/client_state.rs index 04f689306..0ceb41a52 100644 --- a/crates/ibc/src/core/ics02_client/client_state.rs +++ b/crates/ibc/src/core/ics02_client/client_state.rs @@ -19,7 +19,6 @@ use crate::prelude::*; use crate::Height; use super::consensus_state::ConsensusState; -use super::msgs::update_client::UpdateKind; use crate::core::{ExecutionContext, ValidationContext}; @@ -97,14 +96,15 @@ pub trait ClientState: /// successful update, a list of consensus heights is returned. It assumes /// the client_message has already been verified. /// + /// Note that `header` is the field associated with `UpdateKind::UpdateClient`. + /// /// Post-condition: on success, the return value MUST contain at least one /// height. fn update_state( &self, ctx: &mut dyn ExecutionContext, client_id: &ClientId, - client_message: Any, - update_kind: &UpdateKind, + header: Any, ) -> Result, ClientError>; /// update_state_on_misbehaviour should perform appropriate state changes on @@ -189,6 +189,19 @@ pub fn downcast_client_state(h: &dyn ClientState) -> Option<&CS h.as_any().downcast_ref::() } +/// `UpdateKind` represents the 2 ways that a client can be updated +/// in IBC: either through a `MsgUpdateClient`, or a `MsgSubmitMisbehaviour`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UpdateKind { + /// this is the typical scenario where a new header is submitted to the client + /// to update the client. Note that light clients are free to define the type + /// of the object used to update them (e.g. could be a list of headers). + UpdateClient, + /// this is the scenario where misbehaviour is submitted to the client + /// (e.g 2 headers with the same height in Tendermint) + SubmitMisbehaviour, +} + pub struct UpdatedState { pub client_state: Box, pub consensus_state: Box, diff --git a/crates/ibc/src/core/ics02_client/handler/update_client.rs b/crates/ibc/src/core/ics02_client/handler/update_client.rs index e5a02c922..8a4fd5318 100644 --- a/crates/ibc/src/core/ics02_client/handler/update_client.rs +++ b/crates/ibc/src/core/ics02_client/handler/update_client.rs @@ -1,26 +1,27 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgUpdateAnyClient`. +use crate::core::ics02_client::client_state::UpdateKind; use crate::core::ics02_client::error::ClientError; +use crate::core::ics02_client::msgs::MsgUpdateOrMisbehaviour; use crate::prelude::*; use crate::core::ics02_client::events::{ClientMisbehaviour, UpdateClient}; -use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; use crate::events::{IbcEvent, MessageEvent}; use crate::core::context::ContextError; use crate::core::{ExecutionContext, ValidationContext}; -pub(crate) fn validate(ctx: &Ctx, msg: MsgUpdateClient) -> Result<(), ContextError> +pub(crate) fn validate(ctx: &Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ContextError> where Ctx: ValidationContext, { - let MsgUpdateClient { - client_id, - client_message, - update_kind, - signer: _, - } = msg; + let client_id = msg.client_id().clone(); + let update_kind = match msg { + MsgUpdateOrMisbehaviour::UpdateClient(_) => UpdateKind::UpdateClient, + MsgUpdateOrMisbehaviour::Misbehaviour(_) => UpdateKind::SubmitMisbehaviour, + }; + let client_message = msg.client_message(); // Read client state from the host chain store. The client should already exist. let client_state = ctx.client_state(&client_id)?; @@ -32,16 +33,16 @@ where Ok(()) } -pub(crate) fn execute(ctx: &mut Ctx, msg: MsgUpdateClient) -> Result<(), ContextError> +pub(crate) fn execute(ctx: &mut Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ContextError> where Ctx: ExecutionContext, { - let MsgUpdateClient { - client_id, - client_message, - update_kind, - signer: _, - } = msg; + let client_id = msg.client_id().clone(); + let update_kind = match msg { + MsgUpdateOrMisbehaviour::UpdateClient(_) => UpdateKind::UpdateClient, + MsgUpdateOrMisbehaviour::Misbehaviour(_) => UpdateKind::SubmitMisbehaviour, + }; + let client_message = msg.client_message(); let client_state = ctx.client_state(&client_id)?; @@ -62,19 +63,27 @@ where ctx.emit_ibc_event(IbcEvent::Message(MessageEvent::Client)); ctx.emit_ibc_event(event); } else { - let consensus_heights = - client_state.update_state(ctx, &client_id, client_message.clone(), &update_kind)?; + if !matches!(update_kind, UpdateKind::UpdateClient) { + return Err(ClientError::MisbehaviourHandlingFailure { + reason: "misbehaviour submitted, but none found".to_string(), + } + .into()); + } + + let header = client_message; + + let consensus_heights = client_state.update_state(ctx, &client_id, header.clone())?; let consensus_height = consensus_heights.get(0).ok_or(ClientError::Other { description: "client update state returned no updated height".to_string(), })?; let event = IbcEvent::UpdateClient(UpdateClient::new( - client_id, + client_id.clone(), client_state.client_type(), *consensus_height, consensus_heights, - client_message, + header, )); ctx.emit_ibc_event(IbcEvent::Message(MessageEvent::Client)); ctx.emit_ibc_event(event); @@ -100,7 +109,8 @@ mod tests { use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics02_client::handler::update_client::{execute, validate}; - use crate::core::ics02_client::msgs::update_client::{MsgUpdateClient, UpdateKind}; + use crate::core::ics02_client::msgs::misbehaviour::MsgSubmitMisbehaviour; + use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; use crate::core::ics23_commitment::specs::ProofSpecs; use crate::core::ics24_host::identifier::{ChainId, ClientId}; use crate::core::ValidationContext; @@ -128,16 +138,15 @@ mod tests { let height = Height::new(0, 46).unwrap(); let msg = MsgUpdateClient { client_id, - client_message: MockHeader::new(height).with_timestamp(timestamp).into(), - update_kind: UpdateKind::UpdateClient, + header: MockHeader::new(height).with_timestamp(timestamp).into(), signer, }; - let res = validate(&ctx, msg.clone()); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok(), "validation happy path"); - let res = execute(&mut ctx, msg.clone()); + let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok(), "execution happy path"); assert_eq!( @@ -155,12 +164,11 @@ mod tests { let msg = MsgUpdateClient { client_id: ClientId::from_str("nonexistingclient").unwrap(), - client_message: MockHeader::new(Height::new(0, 46).unwrap()).into(), - update_kind: UpdateKind::UpdateClient, + header: MockHeader::new(Height::new(0, 46).unwrap()).into(), signer, }; - let res = validate(&ctx, msg); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)); assert!(res.is_err()); } @@ -196,15 +204,14 @@ mod tests { let latest_header_height = block.height(); let msg = MsgUpdateClient { client_id, - client_message: block.into(), - update_kind: UpdateKind::UpdateClient, + header: block.into(), signer, }; - let res = validate(&ctx, msg.clone()); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok()); - let res = execute(&mut ctx, msg.clone()); + let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok(), "result: {res:?}"); let client_state = ctx.client_state(&msg.client_id).unwrap(); @@ -244,15 +251,14 @@ mod tests { let latest_header_height = block.height(); let msg = MsgUpdateClient { client_id, - client_message: block.into(), - update_kind: UpdateKind::UpdateClient, + header: block.into(), signer, }; - let res = validate(&ctx, msg.clone()); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok()); - let res = execute(&mut ctx, msg.clone()); + let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok(), "result: {res:?}"); let client_state = ctx.client_state(&msg.client_id).unwrap(); @@ -361,15 +367,17 @@ mod tests { let latest_header_height = block.height(); let msg = MsgUpdateClient { client_id, - client_message: block.into(), - update_kind: UpdateKind::UpdateClient, + header: block.into(), signer, }; - let res = validate(&ctx_a, msg.clone()); + let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); assert!(res.is_ok(), "result: {res:?}"); - let res = execute(&mut ctx_a, msg.clone()); + let res = execute( + &mut ctx_a, + MsgUpdateOrMisbehaviour::UpdateClient(msg.clone()), + ); assert!(res.is_ok(), "result: {res:?}"); let client_state = ctx_a.client_state(&msg.client_id).unwrap(); @@ -413,12 +421,11 @@ mod tests { let msg = MsgUpdateClient { client_id, - client_message: block_ref.clone().into(), - update_kind: UpdateKind::UpdateClient, + header: block_ref.clone().into(), signer, }; - let res = validate(&ctx, msg); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)); assert!(res.is_err()); } @@ -434,12 +441,11 @@ mod tests { let header: Any = MockHeader::new(height).with_timestamp(timestamp).into(); let msg = MsgUpdateClient { client_id: client_id.clone(), - client_message: header.clone(), - update_kind: UpdateKind::UpdateClient, + header: header.clone(), signer, }; - let res = execute(&mut ctx, msg); + let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)); assert!(res.is_ok()); assert!(matches!( @@ -480,23 +486,22 @@ mod tests { let client_id = ClientId::default(); let timestamp = Timestamp::now(); let height = Height::new(0, 46).unwrap(); - let msg = MsgUpdateClient { + let msg = MsgSubmitMisbehaviour { client_id: client_id.clone(), - client_message: MockMisbehaviour { + misbehaviour: MockMisbehaviour { client_id: client_id.clone(), header1: MockHeader::new(height).with_timestamp(timestamp), header2: MockHeader::new(height).with_timestamp(timestamp), } .into(), - update_kind: UpdateKind::SubmitMisbehaviour, signer: get_dummy_account_id(), }; let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - let res = validate(&ctx, msg.clone()); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg.clone())); assert!(res.is_ok()); - let res = execute(&mut ctx, msg); + let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); assert!(res.is_ok()); ensure_misbehaviour(&ctx, &client_id, &mock_client_type()); @@ -507,20 +512,19 @@ mod tests { fn test_misbehaviour_nonexisting_client() { let client_id = ClientId::from_str("mockclient1").unwrap(); let height = Height::new(0, 46).unwrap(); - let msg = MsgUpdateClient { + let msg = MsgSubmitMisbehaviour { client_id: ClientId::from_str("nonexistingclient").unwrap(), - client_message: MockMisbehaviour { + misbehaviour: MockMisbehaviour { client_id: client_id.clone(), header1: MockHeader::new(height), header2: MockHeader::new(height), } .into(), - update_kind: UpdateKind::SubmitMisbehaviour, signer: get_dummy_account_id(), }; let ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - let res = validate(&ctx, msg); + let res = validate(&ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); assert!(res.is_err()); } @@ -574,16 +578,15 @@ mod tests { tm_block.into() }; - let msg = MsgUpdateClient { + let msg = MsgSubmitMisbehaviour { client_id: client_id.clone(), - client_message: TmMisbehaviour::new(client_id.clone(), header1, header2).into(), - update_kind: UpdateKind::SubmitMisbehaviour, + misbehaviour: TmMisbehaviour::new(client_id.clone(), header1, header2).into(), signer: get_dummy_account_id(), }; - let res = validate(&ctx_a, msg.clone()); + let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg.clone())); assert!(res.is_ok()); - let res = execute(&mut ctx_a, msg); + let res = execute(&mut ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); assert!(res.is_ok()); ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); } @@ -636,17 +639,16 @@ mod tests { tm_block }; - let msg = MsgUpdateClient { + let msg = MsgSubmitMisbehaviour { client_id: client_id.clone(), - client_message: TmMisbehaviour::new(client_id.clone(), header1.into(), header2.into()) + misbehaviour: TmMisbehaviour::new(client_id.clone(), header1.into(), header2.into()) .into(), - update_kind: UpdateKind::SubmitMisbehaviour, signer: get_dummy_account_id(), }; - let res = validate(&ctx_a, msg.clone()); + let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg.clone())); assert!(res.is_ok()); - let res = execute(&mut ctx_a, msg); + let res = execute(&mut ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); assert!(res.is_ok()); ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); } diff --git a/crates/ibc/src/core/ics02_client/msgs.rs b/crates/ibc/src/core/ics02_client/msgs.rs index ba66213fd..f860623d0 100644 --- a/crates/ibc/src/core/ics02_client/msgs.rs +++ b/crates/ibc/src/core/ics02_client/msgs.rs @@ -4,11 +4,16 @@ //! subsequently calls into the chain-specific (e.g., ICS 07) client handler. See: //! . +use ibc_proto::google::protobuf::Any; + use crate::core::ics02_client::msgs::create_client::MsgCreateClient; +use crate::core::ics02_client::msgs::misbehaviour::MsgSubmitMisbehaviour; use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; use crate::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; +use crate::core::ics24_host::identifier::ClientId; pub mod create_client; +pub mod misbehaviour; pub mod update_client; pub mod upgrade_client; @@ -17,5 +22,27 @@ pub mod upgrade_client; pub enum ClientMsg { CreateClient(MsgCreateClient), UpdateClient(MsgUpdateClient), + Misbehaviour(MsgSubmitMisbehaviour), UpgradeClient(MsgUpgradeClient), } + +pub(crate) enum MsgUpdateOrMisbehaviour { + UpdateClient(MsgUpdateClient), + Misbehaviour(MsgSubmitMisbehaviour), +} + +impl MsgUpdateOrMisbehaviour { + pub(crate) fn client_id(&self) -> &ClientId { + match self { + MsgUpdateOrMisbehaviour::UpdateClient(msg) => &msg.client_id, + MsgUpdateOrMisbehaviour::Misbehaviour(msg) => &msg.client_id, + } + } + + pub(crate) fn client_message(self) -> Any { + match self { + MsgUpdateOrMisbehaviour::UpdateClient(msg) => msg.header, + MsgUpdateOrMisbehaviour::Misbehaviour(msg) => msg.misbehaviour, + } + } +} diff --git a/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs b/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs new file mode 100644 index 000000000..a2e6a5e32 --- /dev/null +++ b/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs @@ -0,0 +1,62 @@ +use crate::prelude::*; + +use ibc_proto::google::protobuf::Any as ProtoAny; +use ibc_proto::ibc::core::client::v1::MsgSubmitMisbehaviour as RawMsgSubmitMisbehaviour; +use ibc_proto::protobuf::Protobuf; + +use crate::core::ics02_client::error::ClientError; +use crate::core::ics24_host::identifier::ClientId; +use crate::signer::Signer; +use crate::tx_msg::Msg; + +pub const TYPE_URL: &str = "/ibc.core.client.v1.MsgSubmitMisbehaviour"; + +/// A type of message that submits client misbehaviour proof. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgSubmitMisbehaviour { + /// client unique identifier + pub client_id: ClientId, + /// misbehaviour used for freezing the light client + pub misbehaviour: ProtoAny, + /// signer address + pub signer: Signer, +} + +impl Msg for MsgSubmitMisbehaviour { + type Raw = RawMsgSubmitMisbehaviour; + + fn type_url(&self) -> String { + TYPE_URL.to_string() + } +} + +impl Protobuf for MsgSubmitMisbehaviour {} + +impl TryFrom for MsgSubmitMisbehaviour { + type Error = ClientError; + + fn try_from(raw: RawMsgSubmitMisbehaviour) -> Result { + let raw_misbehaviour = raw + .misbehaviour + .ok_or(ClientError::MissingRawMisbehaviour)?; + + Ok(MsgSubmitMisbehaviour { + client_id: raw + .client_id + .parse() + .map_err(ClientError::InvalidRawMisbehaviour)?, + misbehaviour: raw_misbehaviour, + signer: raw.signer.parse().map_err(ClientError::Signer)?, + }) + } +} + +impl From for RawMsgSubmitMisbehaviour { + fn from(ics_msg: MsgSubmitMisbehaviour) -> Self { + RawMsgSubmitMisbehaviour { + client_id: ics_msg.client_id.to_string(), + misbehaviour: Some(ics_msg.misbehaviour), + signer: ics_msg.signer.to_string(), + } + } +} diff --git a/crates/ibc/src/core/ics02_client/msgs/update_client.rs b/crates/ibc/src/core/ics02_client/msgs/update_client.rs index 16695f6ca..b0dd8d51d 100644 --- a/crates/ibc/src/core/ics02_client/msgs/update_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/update_client.rs @@ -1,9 +1,9 @@ //! Definition of domain type message `MsgUpdateAnyClient`. use crate::prelude::*; +use crate::tx_msg::Msg; use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::client::v1::MsgSubmitMisbehaviour as RawMsgSubmitMisbehaviour; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; use ibc_proto::protobuf::Protobuf; @@ -11,21 +11,7 @@ use crate::core::ics02_client::error::ClientError; use crate::core::ics24_host::identifier::ClientId; use crate::signer::Signer; -pub const UPDATE_CLIENT_TYPE_URL: &str = "/ibc.core.client.v1.MsgUpdateClient"; -pub const MISBEHAVIOUR_TYPE_URL: &str = "/ibc.core.client.v1.MsgSubmitMisbehaviour"; - -/// `UpdateKind` represents the 2 ways that a client can be updated -/// in IBC: either through a `MsgUpdateClient`, or a `MsgSubmitMisbehaviour`. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum UpdateKind { - /// this is the typical scenario where a new header is submitted to the client - /// to update the client. Note that light clients are free to define the type - /// of the object used to update them (e.g. could be a list of headers). - UpdateClient, - /// this is the scenario where misbehaviour is submitted to the client - /// (e.g 2 headers with the same height in Tendermint) - SubmitMisbehaviour, -} +pub const TYPE_URL: &str = "/ibc.core.client.v1.MsgUpdateClient"; /// Represents the message that triggers the update of an on-chain (IBC) client /// either with new headers, or evidence of misbehaviour. @@ -34,11 +20,18 @@ pub enum UpdateKind { #[derive(Clone, Debug, PartialEq, Eq)] pub struct MsgUpdateClient { pub client_id: ClientId, - pub client_message: Any, - pub update_kind: UpdateKind, + pub header: Any, pub signer: Signer, } +impl Msg for MsgUpdateClient { + type Raw = RawMsgUpdateClient; + + fn type_url(&self) -> String { + TYPE_URL.to_string() + } +} + impl Protobuf for MsgUpdateClient {} impl TryFrom for MsgUpdateClient { @@ -50,8 +43,7 @@ impl TryFrom for MsgUpdateClient { .client_id .parse() .map_err(ClientError::InvalidMsgUpdateClientId)?, - client_message: raw.header.ok_or(ClientError::MissingRawHeader)?, - update_kind: UpdateKind::UpdateClient, + header: raw.header.ok_or(ClientError::MissingRawHeader)?, signer: raw.signer.parse().map_err(ClientError::Signer)?, }) } @@ -61,39 +53,7 @@ impl From for RawMsgUpdateClient { fn from(ics_msg: MsgUpdateClient) -> Self { RawMsgUpdateClient { client_id: ics_msg.client_id.to_string(), - header: Some(ics_msg.client_message), - signer: ics_msg.signer.to_string(), - } - } -} - -impl Protobuf for MsgUpdateClient {} - -impl TryFrom for MsgUpdateClient { - type Error = ClientError; - - fn try_from(raw: RawMsgSubmitMisbehaviour) -> Result { - let raw_misbehaviour = raw - .misbehaviour - .ok_or(ClientError::MissingRawMisbehaviour)?; - - Ok(MsgUpdateClient { - client_id: raw - .client_id - .parse() - .map_err(ClientError::InvalidRawMisbehaviour)?, - client_message: raw_misbehaviour, - update_kind: UpdateKind::SubmitMisbehaviour, - signer: raw.signer.parse().map_err(ClientError::Signer)?, - }) - } -} - -impl From for RawMsgSubmitMisbehaviour { - fn from(ics_msg: MsgUpdateClient) -> Self { - RawMsgSubmitMisbehaviour { - client_id: ics_msg.client_id.to_string(), - misbehaviour: Some(ics_msg.client_message), + header: Some(ics_msg.header), signer: ics_msg.signer.to_string(), } } @@ -118,8 +78,7 @@ mod tests { pub fn new(client_id: ClientId, header: Any, signer: Signer) -> Self { MsgUpdateClient { client_id, - client_message: header, - update_kind: UpdateKind::UpdateClient, + header, signer, } } diff --git a/crates/ibc/src/core/ics26_routing/msgs.rs b/crates/ibc/src/core/ics26_routing/msgs.rs index 41a25a499..36c5c3384 100644 --- a/crates/ibc/src/core/ics26_routing/msgs.rs +++ b/crates/ibc/src/core/ics26_routing/msgs.rs @@ -1,10 +1,10 @@ use crate::prelude::*; use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::client::v1::MsgSubmitMisbehaviour as RawMsgSubmitMisbehaviour; -use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; -use crate::core::ics02_client::msgs::{create_client, update_client, upgrade_client, ClientMsg}; +use crate::core::ics02_client::msgs::{ + create_client, misbehaviour, update_client, upgrade_client, ClientMsg, +}; use crate::core::ics03_connection::msgs::{ conn_open_ack, conn_open_confirm, conn_open_init, conn_open_try, ConnectionMsg, }; @@ -36,11 +36,8 @@ impl TryFrom for MsgEnvelope { .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::Client(ClientMsg::CreateClient(domain_msg))) } - update_client::UPDATE_CLIENT_TYPE_URL => { - let domain_msg = - >::decode_vec( - &any_msg.value, - ) + update_client::TYPE_URL => { + let domain_msg = update_client::MsgUpdateClient::decode_vec(&any_msg.value) .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::Client(ClientMsg::UpdateClient(domain_msg))) } @@ -49,12 +46,10 @@ impl TryFrom for MsgEnvelope { .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::Client(ClientMsg::UpgradeClient(domain_msg))) } - update_client::MISBEHAVIOUR_TYPE_URL => { - let domain_msg = >::decode_vec(&any_msg.value) - .map_err(RouterError::MalformedMessageBytes)?; - Ok(MsgEnvelope::Client(ClientMsg::UpdateClient(domain_msg))) + misbehaviour::TYPE_URL => { + let domain_msg = misbehaviour::MsgSubmitMisbehaviour::decode_vec(&any_msg.value) + .map_err(RouterError::MalformedMessageBytes)?; + Ok(MsgEnvelope::Client(ClientMsg::Misbehaviour(domain_msg))) } // ICS03 diff --git a/crates/ibc/src/mock/client_state.rs b/crates/ibc/src/mock/client_state.rs index 63c0f19ce..28aca3ccc 100644 --- a/crates/ibc/src/mock/client_state.rs +++ b/crates/ibc/src/mock/client_state.rs @@ -1,4 +1,3 @@ -use crate::core::ics02_client::msgs::update_client::UpdateKind; use crate::core::ics24_host::path::{ClientConsensusStatePath, ClientStatePath}; use crate::prelude::*; @@ -10,7 +9,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::mock::ClientState as RawMockClientState; use ibc_proto::protobuf::Protobuf; -use crate::core::ics02_client::client_state::{ClientState, UpdatedState}; +use crate::core::ics02_client::client_state::{ClientState, UpdateKind, UpdatedState}; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics02_client::error::ClientError; @@ -294,10 +293,9 @@ impl ClientState for MockClientState { &self, ctx: &mut dyn ExecutionContext, client_id: &ClientId, - client_message: Any, - _update_kind: &UpdateKind, + header: Any, ) -> Result, ClientError> { - let header = MockHeader::try_from(client_message)?; + let header = MockHeader::try_from(header)?; let header_height = header.height; let new_client_state = MockClientState::new(header).into_box(); diff --git a/crates/ibc/src/mock/ics18_relayer/context.rs b/crates/ibc/src/mock/ics18_relayer/context.rs index ae27f6023..8282f5ae2 100644 --- a/crates/ibc/src/mock/ics18_relayer/context.rs +++ b/crates/ibc/src/mock/ics18_relayer/context.rs @@ -32,7 +32,7 @@ pub trait RelayerContext { mod tests { use crate::clients::ics07_tendermint::client_type as tm_client_type; use crate::core::ics02_client::header::{downcast_header, Header}; - use crate::core::ics02_client::msgs::update_client::{MsgUpdateClient, UpdateKind}; + use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; use crate::core::ics02_client::msgs::ClientMsg; use crate::core::ics24_host::identifier::{ChainId, ClientId}; use crate::core::ics26_routing::msgs::MsgEnvelope; @@ -86,8 +86,7 @@ mod tests { // Client on destination chain can be updated. Ok(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), - client_message: src_header.clone_into(), - update_kind: UpdateKind::UpdateClient, + header: src_header.clone_into(), signer: dest.signer(), })) }