diff --git a/ibc-core/ics03-connection/Cargo.toml b/ibc-core/ics03-connection/Cargo.toml deleted file mode 100644 index a6b19b7ab..000000000 --- a/ibc-core/ics03-connection/Cargo.toml +++ /dev/null @@ -1,73 +0,0 @@ -[package] -name = "ibc-core-connection" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -rust-version = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -keywords = [ "blockchain", "cosmos", "ibc", "connection" ] -readme = "./../README.md" - -description = """ - Maintained by `ibc-rs`, contains the implementation of the ICS-03 Connection Semantics and - re-exports essential data structures and domain types from `ibc-core-connection-types` crate. -""" - -[package.metadata.docs.rs] -all-features = true - -[dependencies] -ibc-core-client = { workspace = true } -ibc-core-connection-types = { workspace = true } -ibc-core-host = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-client-wasm-types = { workspace = true, optional = true } - -prost = { workspace = true, optional = true } - -[features] -default = [ "std" ] -std = [ - "ibc-core-client/std", - "ibc-core-connection-types/std", - "ibc-core-host/std", - "ibc-core-handler-types/std", - "ibc-primitives/std", - "wasm-client", -] -serde = [ - "ibc-core-client/serde", - "ibc-core-connection-types/serde", - "ibc-core-host/serde", - "ibc-core-handler-types/serde", - "ibc-primitives/serde", -] -schema = [ - "ibc-core-client/schema", - "ibc-core-connection-types/schema", - "ibc-core-host/schema", - "ibc-core-handler-types/schema", - "ibc-primitives/schema", - "serde", - "std", -] -borsh = [ - "ibc-core-client/borsh", - "ibc-core-connection-types/borsh", - "ibc-core-host/borsh", - "ibc-core-handler-types/borsh", - "ibc-primitives/borsh", -] -parity-scale-codec = [ - "ibc-core-client/parity-scale-codec", - "ibc-core-connection-types/parity-scale-codec", - "ibc-core-host/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", - "ibc-primitives/parity-scale-codec", -] -wasm-client = [ - "dep:ibc-client-wasm-types", - "dep:prost", -] diff --git a/ibc-core/ics03-connection/src/delay.rs b/ibc-core/ics03-connection/src/delay.rs deleted file mode 100644 index b0f427b22..000000000 --- a/ibc-core/ics03-connection/src/delay.rs +++ /dev/null @@ -1,48 +0,0 @@ -use ibc_core_client::context::ClientValidationContext; -use ibc_core_client::types::Height; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::ConnectionEnd; -use ibc_core_host::ValidationContext; - -pub fn verify_conn_delay_passed( - ctx: &Ctx, - packet_proof_height: Height, - connection_end: &ConnectionEnd, -) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, -{ - // Fetch the current host chain time and height. - let current_host_time = ctx.host_timestamp()?; - let current_host_height = ctx.host_height()?; - - // Fetch the latest time and height that the counterparty client was updated on the host chain. - let client_id = connection_end.client_id(); - let last_client_update = ctx - .get_client_validation_context() - .client_update_meta(client_id, &packet_proof_height)?; - - // Fetch the connection delay time and height periods. - let conn_delay_time_period = connection_end.delay_period(); - let conn_delay_height_period = ctx.block_delay(&conn_delay_time_period); - - // Verify that the current host chain time is later than the last client update time - let earliest_valid_time = (last_client_update.0 + conn_delay_time_period)?; - if current_host_time < earliest_valid_time { - return Err(ConnectionError::InsufficientTimeElapsed { - current_host_time, - earliest_valid_time, - }); - } - - // Verify that the current host chain height is later than the last client update height - let earliest_valid_height = last_client_update.1.add(conn_delay_height_period); - if current_host_height < earliest_valid_height { - return Err(ConnectionError::InsufficientBlocksElapsed { - current_host_height, - earliest_valid_height, - }); - }; - - Ok(()) -} diff --git a/ibc-core/ics03-connection/src/handler/conn_open_ack.rs b/ibc-core/ics03-connection/src/handler/conn_open_ack.rs deleted file mode 100644 index a91a16c76..000000000 --- a/ibc-core/ics03-connection/src/handler/conn_open_ack.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenAck`. - -use ibc_core_client::context::prelude::*; -use ibc_core_client::types::error::ClientError; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenAck; -use ibc_core_connection_types::msgs::MsgConnectionOpenAck; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::ClientId; -use ibc_core_host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_primitives::prelude::*; -use ibc_primitives::proto::{Any, Protobuf}; -use ibc_primitives::ToVec; - -use crate::handler::{pack_host_consensus_state, unpack_host_client_state}; - -pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenAck) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, - >::Error: Into, -{ - let vars = LocalVars::new(ctx_a, &msg)?; - validate_impl(ctx_a, &msg, &vars) -} - -fn validate_impl( - ctx_a: &Ctx, - msg: &MsgConnectionOpenAck, - vars: &LocalVars, -) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, - >::Error: Into, -{ - ctx_a.validate_message_signer(&msg.signer)?; - - let host_height = ctx_a.host_height()?; - - if msg.consensus_height_of_a_on_b > host_height { - return Err(ConnectionError::InsufficientConsensusHeight { - target_height: msg.consensus_height_of_a_on_b, - current_height: host_height, - }); - } - - let client_val_ctx_a = ctx_a.get_client_validation_context(); - - let client_state_of_a_on_b = unpack_host_client_state::( - msg.client_state_of_a_on_b.clone(), - vars.client_id_on_b(), - )?; - - ctx_a.validate_self_client(client_state_of_a_on_b)?; - - msg.version - .verify_is_supported(vars.conn_end_on_a.versions())?; - - vars.conn_end_on_a.verify_state_matches(&State::Init)?; - - // Proof verification. - { - let client_state_of_b_on_a = client_val_ctx_a.client_state(vars.client_id_on_a())?; - - client_state_of_b_on_a - .status(client_val_ctx_a, vars.client_id_on_a())? - .verify_is_active()?; - - client_state_of_b_on_a.validate_proof_height(msg.proofs_height_on_b)?; - - let client_cons_state_path_on_a = ClientConsensusStatePath::new( - vars.client_id_on_a().clone(), - msg.proofs_height_on_b.revision_number(), - msg.proofs_height_on_b.revision_height(), - ); - - let consensus_state_of_b_on_a = - client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; - - let prefix_on_a = ctx_a.commitment_prefix(); - let prefix_on_b = vars.conn_end_on_a.counterparty().prefix(); - - { - let expected_conn_end_on_b = ConnectionEnd::new( - State::TryOpen, - vars.client_id_on_b().clone(), - Counterparty::new( - vars.client_id_on_a().clone(), - Some(msg.conn_id_on_a.clone()), - prefix_on_a, - ), - vec![msg.version.clone()], - vars.conn_end_on_a.delay_period(), - )?; - - client_state_of_b_on_a.verify_membership( - prefix_on_b, - &msg.proof_conn_end_on_b, - consensus_state_of_b_on_a.root(), - Path::Connection(ConnectionPath::new(&msg.conn_id_on_b)), - expected_conn_end_on_b.encode_vec(), - )?; - } - - client_state_of_b_on_a.verify_membership( - prefix_on_b, - &msg.proof_client_state_of_a_on_b, - consensus_state_of_b_on_a.root(), - Path::ClientState(ClientStatePath::new(vars.client_id_on_b().clone())), - msg.client_state_of_a_on_b.to_vec(), - )?; - - let expected_consensus_state_of_a_on_b = - ctx_a.host_consensus_state(&msg.consensus_height_of_a_on_b)?; - - let stored_consensus_state_of_a_on_b = - pack_host_consensus_state(expected_consensus_state_of_a_on_b, vars.client_id_on_b()); - - let client_cons_state_path_on_b = ClientConsensusStatePath::new( - vars.client_id_on_b().clone(), - msg.consensus_height_of_a_on_b.revision_number(), - msg.consensus_height_of_a_on_b.revision_height(), - ); - - client_state_of_b_on_a.verify_membership( - prefix_on_b, - &msg.proof_consensus_state_of_a_on_b, - consensus_state_of_b_on_a.root(), - Path::ClientConsensusState(client_cons_state_path_on_b), - stored_consensus_state_of_a_on_b.to_vec(), - )?; - } - - Ok(()) -} - -pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenAck) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let vars = LocalVars::new(ctx_a, &msg)?; - execute_impl(ctx_a, msg, vars) -} - -fn execute_impl( - ctx_a: &mut Ctx, - msg: MsgConnectionOpenAck, - vars: LocalVars, -) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let event = IbcEvent::OpenAckConnection(OpenAck::new( - msg.conn_id_on_a.clone(), - vars.client_id_on_a().clone(), - msg.conn_id_on_b.clone(), - vars.client_id_on_b().clone(), - )); - ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; - ctx_a.emit_ibc_event(event)?; - - ctx_a.log_message("success: conn_open_ack verification passed".to_string())?; - - { - let new_conn_end_on_a = { - let mut counterparty = vars.conn_end_on_a.counterparty().clone(); - counterparty.connection_id = Some(msg.conn_id_on_b.clone()); - - let mut new_conn_end_on_a = vars.conn_end_on_a; - new_conn_end_on_a.set_state(State::Open); - new_conn_end_on_a.set_version(msg.version.clone()); - new_conn_end_on_a.set_counterparty(counterparty); - new_conn_end_on_a - }; - - ctx_a.store_connection(&ConnectionPath::new(&msg.conn_id_on_a), new_conn_end_on_a)?; - } - - Ok(()) -} - -struct LocalVars { - conn_end_on_a: ConnectionEnd, -} - -impl LocalVars { - fn new(ctx_a: &Ctx, msg: &MsgConnectionOpenAck) -> Result - where - Ctx: ValidationContext, - { - Ok(LocalVars { - conn_end_on_a: ctx_a.connection_end(&msg.conn_id_on_a)?, - }) - } - - fn client_id_on_a(&self) -> &ClientId { - self.conn_end_on_a.client_id() - } - - fn client_id_on_b(&self) -> &ClientId { - self.conn_end_on_a.counterparty().client_id() - } -} diff --git a/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs b/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs deleted file mode 100644 index ba934493d..000000000 --- a/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenConfirm`. - -use ibc_core_client::context::prelude::*; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenConfirm; -use ibc_core_connection_types::msgs::MsgConnectionOpenConfirm; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; -use ibc_core_host::types::path::{ClientConsensusStatePath, ConnectionPath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_primitives::prelude::*; -use ibc_primitives::proto::Protobuf; - -pub fn validate(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, -{ - let vars = LocalVars::new(ctx_b, msg)?; - validate_impl(ctx_b, msg, &vars) -} - -fn validate_impl( - ctx_b: &Ctx, - msg: &MsgConnectionOpenConfirm, - vars: &LocalVars, -) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, -{ - ctx_b.validate_message_signer(&msg.signer)?; - - let conn_end_on_b = vars.conn_end_on_b(); - - conn_end_on_b.verify_state_matches(&State::TryOpen)?; - - let client_id_on_a = vars.client_id_on_a(); - let client_id_on_b = vars.client_id_on_b(); - let conn_id_on_a = vars.conn_id_on_a()?; - - // Verify proofs - { - let client_val_ctx_b = ctx_b.get_client_validation_context(); - - let client_state_of_a_on_b = client_val_ctx_b.client_state(client_id_on_b)?; - - client_state_of_a_on_b - .status(client_val_ctx_b, client_id_on_b)? - .verify_is_active()?; - - client_state_of_a_on_b.validate_proof_height(msg.proof_height_on_a)?; - - let client_cons_state_path_on_b = ClientConsensusStatePath::new( - client_id_on_b.clone(), - msg.proof_height_on_a.revision_number(), - msg.proof_height_on_a.revision_height(), - ); - let consensus_state_of_a_on_b = - client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; - - let prefix_on_a = conn_end_on_b.counterparty().prefix(); - let prefix_on_b = ctx_b.commitment_prefix(); - - let expected_conn_end_on_a = ConnectionEnd::new( - State::Open, - client_id_on_a.clone(), - Counterparty::new( - client_id_on_b.clone(), - Some(msg.conn_id_on_b.clone()), - prefix_on_b, - ), - conn_end_on_b.versions().to_vec(), - conn_end_on_b.delay_period(), - )?; - - client_state_of_a_on_b.verify_membership( - prefix_on_a, - &msg.proof_conn_end_on_a, - consensus_state_of_a_on_b.root(), - Path::Connection(ConnectionPath::new(conn_id_on_a)), - expected_conn_end_on_a.encode_vec(), - )?; - } - - Ok(()) -} - -pub fn execute(ctx_b: &mut Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let vars = LocalVars::new(ctx_b, msg)?; - execute_impl(ctx_b, msg, vars) -} - -fn execute_impl( - ctx_b: &mut Ctx, - msg: &MsgConnectionOpenConfirm, - vars: LocalVars, -) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let client_id_on_a = vars.client_id_on_a(); - let client_id_on_b = vars.client_id_on_b(); - let conn_id_on_a = vars.conn_id_on_a()?; - - let event = IbcEvent::OpenConfirmConnection(OpenConfirm::new( - msg.conn_id_on_b.clone(), - client_id_on_b.clone(), - conn_id_on_a.clone(), - client_id_on_a.clone(), - )); - ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; - ctx_b.emit_ibc_event(event)?; - ctx_b.log_message("success: conn_open_confirm verification passed".to_string())?; - - { - let new_conn_end_on_b = { - let mut new_conn_end_on_b = vars.conn_end_on_b; - - new_conn_end_on_b.set_state(State::Open); - new_conn_end_on_b - }; - - ctx_b.store_connection(&ConnectionPath(msg.conn_id_on_b.clone()), new_conn_end_on_b)?; - } - - Ok(()) -} - -struct LocalVars { - conn_end_on_b: ConnectionEnd, -} - -impl LocalVars { - fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result - where - Ctx: ValidationContext, - { - Ok(Self { - conn_end_on_b: ctx_b.connection_end(&msg.conn_id_on_b)?, - }) - } - - fn conn_end_on_b(&self) -> &ConnectionEnd { - &self.conn_end_on_b - } - - fn client_id_on_a(&self) -> &ClientId { - self.conn_end_on_b.counterparty().client_id() - } - - fn client_id_on_b(&self) -> &ClientId { - self.conn_end_on_b.client_id() - } - - fn conn_id_on_a(&self) -> Result<&ConnectionId, ConnectionError> { - self.conn_end_on_b - .counterparty() - .connection_id() - .ok_or(ConnectionError::InvalidCounterparty) - } -} diff --git a/ibc-core/ics03-connection/src/handler/conn_open_init.rs b/ibc-core/ics03-connection/src/handler/conn_open_init.rs deleted file mode 100644 index 267603a7f..000000000 --- a/ibc-core/ics03-connection/src/handler/conn_open_init.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Protocol logic specific to ICS3 messages of type `MsgConnectionOpenInit`. -use ibc_core_client::context::prelude::*; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenInit; -use ibc_core_connection_types::msgs::MsgConnectionOpenInit; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::ConnectionId; -use ibc_core_host::types::path::{ClientConnectionPath, ConnectionPath}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_primitives::prelude::*; - -pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, -{ - ctx_a.validate_message_signer(&msg.signer)?; - - let client_val_ctx_a = ctx_a.get_client_validation_context(); - - // An IBC client running on the local (host) chain should exist. - let client_state_of_b_on_a = client_val_ctx_a.client_state(&msg.client_id_on_a)?; - - client_state_of_b_on_a - .status(client_val_ctx_a, &msg.client_id_on_a)? - .verify_is_active()?; - - if let Some(version) = msg.version { - version.verify_is_supported(&ctx_a.get_compatible_versions())?; - } - - Ok(()) -} - -pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let versions = if let Some(version) = msg.version { - version.verify_is_supported(&ctx_a.get_compatible_versions())?; - vec![version] - } else { - ctx_a.get_compatible_versions() - }; - - let conn_end_on_a = ConnectionEnd::new( - State::Init, - msg.client_id_on_a.clone(), - Counterparty::new( - msg.counterparty.client_id().clone(), - None, - msg.counterparty.prefix().clone(), - ), - versions, - msg.delay_period, - )?; - - // Construct the identifier for the new connection. - let conn_id_on_a = ConnectionId::new(ctx_a.connection_counter()?); - - ctx_a.log_message(format!( - "success: conn_open_init: generated new connection identifier: {conn_id_on_a}" - ))?; - - { - let client_id_on_b = msg.counterparty.client_id().clone(); - - let event = IbcEvent::OpenInitConnection(OpenInit::new( - conn_id_on_a.clone(), - msg.client_id_on_a.clone(), - client_id_on_b, - )); - ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; - ctx_a.emit_ibc_event(event)?; - } - - ctx_a.increase_connection_counter()?; - ctx_a.store_connection_to_client( - &ClientConnectionPath::new(msg.client_id_on_a), - conn_id_on_a.clone(), - )?; - ctx_a.store_connection(&ConnectionPath::new(&conn_id_on_a), conn_end_on_a)?; - - Ok(()) -} diff --git a/ibc-core/ics03-connection/src/handler/conn_open_try.rs b/ibc-core/ics03-connection/src/handler/conn_open_try.rs deleted file mode 100644 index 2b47e488d..000000000 --- a/ibc-core/ics03-connection/src/handler/conn_open_try.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenTry`.; -use ibc_core_client::context::prelude::*; -use ibc_core_client::types::error::ClientError; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenTry; -use ibc_core_connection_types::msgs::MsgConnectionOpenTry; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; -use ibc_core_host::types::path::{ - ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path, -}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_primitives::prelude::*; -use ibc_primitives::proto::{Any, Protobuf}; -use ibc_primitives::ToVec; - -use crate::handler::{pack_host_consensus_state, unpack_host_client_state}; - -pub fn validate(ctx_b: &Ctx, msg: MsgConnectionOpenTry) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, - >::Error: Into, -{ - let vars = LocalVars::new(ctx_b, &msg)?; - validate_impl(ctx_b, &msg, &vars) -} - -fn validate_impl( - ctx_b: &Ctx, - msg: &MsgConnectionOpenTry, - vars: &LocalVars, -) -> Result<(), ConnectionError> -where - Ctx: ValidationContext, - >::Error: Into, -{ - ctx_b.validate_message_signer(&msg.signer)?; - - let client_val_ctx_b = ctx_b.get_client_validation_context(); - - let client_state_of_b_on_a = unpack_host_client_state::( - msg.client_state_of_b_on_a.clone(), - &vars.client_id_on_a, - )?; - - ctx_b.validate_self_client(client_state_of_b_on_a)?; - - let host_height = ctx_b.host_height()?; - - if msg.consensus_height_of_b_on_a > host_height { - // Fail if the consensus height is too advanced. - return Err(ConnectionError::InsufficientConsensusHeight { - target_height: msg.consensus_height_of_b_on_a, - current_height: host_height, - }); - } - - let client_id_on_a = msg.counterparty.client_id(); - - // Verify proofs - { - let client_state_of_a_on_b = - client_val_ctx_b.client_state(vars.conn_end_on_b.client_id())?; - - client_state_of_a_on_b - .status(client_val_ctx_b, &msg.client_id_on_b)? - .verify_is_active()?; - - client_state_of_a_on_b.validate_proof_height(msg.proofs_height_on_a)?; - - let client_cons_state_path_on_b = ClientConsensusStatePath::new( - msg.client_id_on_b.clone(), - msg.proofs_height_on_a.revision_number(), - msg.proofs_height_on_a.revision_height(), - ); - - let consensus_state_of_a_on_b = - client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; - - let prefix_on_a = vars.conn_end_on_b.counterparty().prefix(); - let prefix_on_b = ctx_b.commitment_prefix(); - - { - let expected_conn_end_on_a = ConnectionEnd::new( - State::Init, - client_id_on_a.clone(), - Counterparty::new(msg.client_id_on_b.clone(), None, prefix_on_b), - msg.versions_on_a.clone(), - msg.delay_period, - )?; - - client_state_of_a_on_b.verify_membership( - prefix_on_a, - &msg.proof_conn_end_on_a, - consensus_state_of_a_on_b.root(), - Path::Connection(ConnectionPath::new(&vars.conn_id_on_a)), - expected_conn_end_on_a.encode_vec(), - )?; - } - - client_state_of_a_on_b.verify_membership( - prefix_on_a, - &msg.proof_client_state_of_b_on_a, - consensus_state_of_a_on_b.root(), - Path::ClientState(ClientStatePath::new(client_id_on_a.clone())), - msg.client_state_of_b_on_a.to_vec(), - )?; - - let expected_consensus_state_of_b_on_a = - ctx_b.host_consensus_state(&msg.consensus_height_of_b_on_a)?; - - let stored_consensus_state_of_b_on_a = - pack_host_consensus_state(expected_consensus_state_of_b_on_a, &vars.client_id_on_a); - - let client_cons_state_path_on_a = ClientConsensusStatePath::new( - client_id_on_a.clone(), - msg.consensus_height_of_b_on_a.revision_number(), - msg.consensus_height_of_b_on_a.revision_height(), - ); - - client_state_of_a_on_b.verify_membership( - prefix_on_a, - &msg.proof_consensus_state_of_b_on_a, - consensus_state_of_a_on_b.root(), - Path::ClientConsensusState(client_cons_state_path_on_a), - stored_consensus_state_of_b_on_a.to_vec(), - )?; - } - - Ok(()) -} - -pub fn execute(ctx_b: &mut Ctx, msg: MsgConnectionOpenTry) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let vars = LocalVars::new(ctx_b, &msg)?; - execute_impl(ctx_b, msg, vars) -} - -fn execute_impl( - ctx_b: &mut Ctx, - msg: MsgConnectionOpenTry, - vars: LocalVars, -) -> Result<(), ConnectionError> -where - Ctx: ExecutionContext, -{ - let conn_id_on_a = vars - .conn_end_on_b - .counterparty() - .connection_id() - .ok_or(ConnectionError::InvalidCounterparty)?; - let event = IbcEvent::OpenTryConnection(OpenTry::new( - vars.conn_id_on_b.clone(), - msg.client_id_on_b.clone(), - conn_id_on_a.clone(), - vars.client_id_on_a.clone(), - )); - ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; - ctx_b.emit_ibc_event(event)?; - ctx_b.log_message("success: conn_open_try verification passed".to_string())?; - - ctx_b.increase_connection_counter()?; - ctx_b.store_connection_to_client( - &ClientConnectionPath::new(msg.client_id_on_b), - vars.conn_id_on_b.clone(), - )?; - ctx_b.store_connection(&ConnectionPath::new(&vars.conn_id_on_b), vars.conn_end_on_b)?; - - Ok(()) -} - -struct LocalVars { - conn_id_on_b: ConnectionId, - conn_end_on_b: ConnectionEnd, - client_id_on_a: ClientId, - conn_id_on_a: ConnectionId, -} - -impl LocalVars { - fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenTry) -> Result - where - Ctx: ValidationContext, - { - let version_on_b = ctx_b.pick_version(&msg.versions_on_a)?; - - Ok(Self { - conn_id_on_b: ConnectionId::new(ctx_b.connection_counter()?), - conn_end_on_b: ConnectionEnd::new( - State::TryOpen, - msg.client_id_on_b.clone(), - msg.counterparty.clone(), - vec![version_on_b], - msg.delay_period, - )?, - client_id_on_a: msg.counterparty.client_id().clone(), - conn_id_on_a: msg - .counterparty - .connection_id() - .ok_or(ConnectionError::InvalidCounterparty)? - .clone(), - }) - } -} diff --git a/ibc-core/ics03-connection/src/handler/mod.rs b/ibc-core/ics03-connection/src/handler/mod.rs deleted file mode 100644 index 791d1c778..000000000 --- a/ibc-core/ics03-connection/src/handler/mod.rs +++ /dev/null @@ -1,81 +0,0 @@ -use ibc_core_client::types::error::ClientError; -use ibc_core_connection_types::error::ConnectionError; -#[cfg(feature = "wasm-client")] -use ibc_core_host::types::error::DecodingError; -use ibc_core_host::types::identifiers::ClientId; -use ibc_primitives::proto::Any; - -pub mod conn_open_ack; -pub mod conn_open_confirm; -pub mod conn_open_init; -pub mod conn_open_try; - -/// Unpacks the client state from the format that is stored at the counterparty chain. -/// -/// Currently, the IBC-go enabled chains stores Wasm LightClient states in a WasmClientState -/// wrapper. This function unpacks the client state from the WasmClientState wrapper -/// if the client identifier at counterparty is of Wasm client type. -pub(crate) fn unpack_host_client_state( - value: Any, - host_client_id_at_counterparty: &ClientId, -) -> Result -where - CS: TryFrom, - >::Error: Into, -{ - #[cfg(feature = "wasm-client")] - if host_client_id_at_counterparty.is_wasm_client_id() { - use ibc_client_wasm_types::client_state::ClientState as WasmClientState; - use prost::Message; - - let wasm_client_state = WasmClientState::try_from(value)?; - - let any_client_state = ::decode(wasm_client_state.data.as_slice()) - .map_err(|e| ConnectionError::Decoding(DecodingError::Prost(e)))?; - - Ok(CS::try_from(any_client_state).map_err(Into::::into)?) - } else { - Ok(CS::try_from(value).map_err(Into::::into)?) - } - - #[cfg(not(feature = "wasm-client"))] - { - // this avoids lint warning for unused variable. - let _ = host_client_id_at_counterparty; - Ok(CS::try_from(value).map_err(Into::::into)?) - } -} - -/// Pack the host consensus state in the expected format stored at the counterparty chain. -/// -/// Currently, the IBC-go enabled chains stores Wasm LightClient states in a WasmConsensusState -/// wrapper. This function packs the consensus state in the WasmConsensusState wrapper -/// if the client identifier at counterparty is of Wasm client type. -pub(crate) fn pack_host_consensus_state( - value: CS, - host_client_id_at_counterparty: &ClientId, -) -> Any -where - CS: Into, -{ - let any_value = value.into(); - - #[cfg(feature = "wasm-client")] - if host_client_id_at_counterparty.is_wasm_client_id() { - use ibc_client_wasm_types::consensus_state::ConsensusState as WasmConsensusState; - use prost::Message; - - let wasm_consensus_state = WasmConsensusState::new(any_value.encode_to_vec()); - - wasm_consensus_state.into() - } else { - any_value - } - - #[cfg(not(feature = "wasm-client"))] - { - // this avoids lint warning for unused variable. - let _ = host_client_id_at_counterparty; - any_value - } -} diff --git a/ibc-core/ics03-connection/src/lib.rs b/ibc-core/ics03-connection/src/lib.rs deleted file mode 100644 index 0892189d1..000000000 --- a/ibc-core/ics03-connection/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! ICS-03: Connection Semantics implementation to process connection open -//! handshake. Exports data structures and implementations of IBC core -//! connection module. -#![no_std] -#![forbid(unsafe_code)] -#![cfg_attr(not(test), deny(clippy::unwrap_used))] -#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] -#![deny( - warnings, - trivial_numeric_casts, - unused_import_braces, - unused_qualifications, - rust_2018_idioms -)] - -#[cfg(feature = "std")] -extern crate std; - -pub mod delay; -pub mod handler; - -/// Re-exports ICS-03 data structures from the `ibc-core-connection-types` crate -pub mod types { - #[doc(inline)] - pub use ibc_core_connection_types::*; -} diff --git a/ibc-core/ics03-connection/types/Cargo.toml b/ibc-core/ics03-connection/types/Cargo.toml deleted file mode 100644 index 39ecc8485..000000000 --- a/ibc-core/ics03-connection/types/Cargo.toml +++ /dev/null @@ -1,91 +0,0 @@ -[package] -name = "ibc-core-connection-types" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -rust-version = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -keywords = [ "blockchain", "cosmos", "ibc", "connection", "types" ] -readme = "./../../README.md" - -description = """ - Maintained by `ibc-rs`, encapsulates essential ICS-03 Connection Semantics data structures and domain types, - as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability - to facilitate development and integration across diverse IBC-enabled projects. -""" - -[package.metadata.docs.rs] -all-features = true - -[dependencies] -# external dependencies -borsh = { workspace = true, optional = true } -derive_more = { workspace = true } -displaydoc = { workspace = true } -schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } -subtle-encoding = { workspace = true } - -# ibc dependencies -ibc-core-client-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } - -# cosmos dependencies -tendermint = { workspace = true } - -# parity dependencies -parity-scale-codec = { workspace = true, optional = true } -scale-info = { workspace = true, optional = true } - -[features] -default = [ "std" ] -std = [ - "displaydoc/std", - "subtle-encoding/std", - "serde/std", - "ibc-core-client-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", - "ibc-primitives/std", - "ibc-proto/std", - "tendermint/std", -] -serde = [ - "dep:serde", - "ibc-core-client-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host-types/serde", - "ibc-primitives/serde", - "ibc-proto/serde", -] -schema = [ - "dep:schemars", - "ibc-core-client-types/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host-types/schema", - "ibc-primitives/schema", - "ibc-proto/json-schema", - "serde", - "std", -] -borsh = [ - "dep:borsh", - "ibc-core-client-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", - "ibc-primitives/borsh", - "ibc-proto/borsh", -] -parity-scale-codec = [ - "dep:parity-scale-codec", - "dep:scale-info", - "ibc-core-client-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", - "ibc-primitives/parity-scale-codec", - "ibc-proto/parity-scale-codec", -] diff --git a/ibc-core/ics03-connection/types/src/connection.rs b/ibc-core/ics03-connection/types/src/connection.rs deleted file mode 100644 index f518f86a5..000000000 --- a/ibc-core/ics03-connection/types/src/connection.rs +++ /dev/null @@ -1,523 +0,0 @@ -//! Defines the types that define a connection - -use core::fmt::{Display, Error as FmtError, Formatter}; -use core::time::Duration; - -use ibc_core_commitment_types::commitment::CommitmentPrefix; -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; -use ibc_primitives::prelude::*; -use ibc_proto::ibc::core::connection::v1::{ - ConnectionEnd as RawConnectionEnd, Counterparty as RawCounterparty, - IdentifiedConnection as RawIdentifiedConnection, -}; -use ibc_proto::Protobuf; - -use crate::error::ConnectionError; -use crate::version::Version; - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct IdentifiedConnectionEnd { - pub connection_id: ConnectionId, - pub connection_end: ConnectionEnd, -} - -impl IdentifiedConnectionEnd { - pub fn new(connection_id: ConnectionId, connection_end: ConnectionEnd) -> Self { - IdentifiedConnectionEnd { - connection_id, - connection_end, - } - } - - pub fn id(&self) -> &ConnectionId { - &self.connection_id - } - - pub fn end(&self) -> &ConnectionEnd { - &self.connection_end - } -} - -impl Protobuf for IdentifiedConnectionEnd {} - -impl TryFrom for IdentifiedConnectionEnd { - type Error = DecodingError; - - fn try_from(value: RawIdentifiedConnection) -> Result { - let raw_connection_end = RawConnectionEnd { - client_id: value.client_id.to_string(), - versions: value.versions, - state: value.state, - counterparty: value.counterparty, - delay_period: value.delay_period, - }; - - Ok(IdentifiedConnectionEnd { - connection_id: value.id.parse()?, - connection_end: raw_connection_end.try_into()?, - }) - } -} - -impl From for RawIdentifiedConnection { - fn from(value: IdentifiedConnectionEnd) -> Self { - RawIdentifiedConnection { - id: value.connection_id.to_string(), - client_id: value.connection_end.client_id.to_string(), - versions: value - .connection_end - .versions - .iter() - .map(|v| From::from(v.clone())) - .collect(), - state: value.connection_end.state as i32, - delay_period: value.connection_end.delay_period.as_nanos() as u64, - counterparty: Some(value.connection_end.counterparty().clone().into()), - } - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive(parity_scale_codec::Encode, parity_scale_codec::Decode,) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct ConnectionEnd { - pub state: State, - client_id: ClientId, - counterparty: Counterparty, - versions: Vec, - delay_period: Duration, -} - -mod sealed { - use super::*; - - #[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) - )] - struct InnerConnectionEnd { - pub state: State, - client_id: ClientId, - counterparty: Counterparty, - versions: Vec, - delay_period_secs: u64, - delay_period_nanos: u32, - } - - impl From for ConnectionEnd { - fn from(value: InnerConnectionEnd) -> Self { - Self { - state: value.state, - client_id: value.client_id, - counterparty: value.counterparty, - versions: value.versions, - delay_period: Duration::new(value.delay_period_secs, value.delay_period_nanos), - } - } - } - - impl From for InnerConnectionEnd { - fn from(value: ConnectionEnd) -> Self { - Self { - state: value.state, - client_id: value.client_id, - counterparty: value.counterparty, - versions: value.versions, - delay_period_secs: value.delay_period.as_secs(), - delay_period_nanos: value.delay_period.subsec_nanos(), - } - } - } - - #[cfg(feature = "borsh")] - impl borsh::BorshSerialize for ConnectionEnd { - fn serialize(&self, writer: &mut W) -> borsh::io::Result<()> { - let value = InnerConnectionEnd::from(self.clone()); - borsh::BorshSerialize::serialize(&value, writer) - } - } - - #[cfg(feature = "borsh")] - impl borsh::BorshDeserialize for ConnectionEnd { - fn deserialize_reader(reader: &mut R) -> borsh::io::Result { - let inner_conn_end = InnerConnectionEnd::deserialize_reader(reader)?; - Ok(ConnectionEnd::from(inner_conn_end)) - } - } - - #[cfg(feature = "parity-scale-codec")] - impl scale_info::TypeInfo for ConnectionEnd { - type Identity = Self; - - fn type_info() -> scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("ConnectionEnd", module_path!())) - .composite( - scale_info::build::Fields::named() - .field(|f| f.ty::().name("state").type_name("State")) - .field(|f| f.ty::().name("client_id").type_name("ClientId")) - .field(|f| { - f.ty::() - .name("counterparty") - .type_name("Counterparty") - }) - .field(|f| { - f.ty::>() - .name("versions") - .type_name("Vec") - }) - .field(|f| f.ty::().name("delay_period_secs").type_name("u64")) - .field(|f| f.ty::().name("delay_period_nanos").type_name("u32")), - ) - } - } -} - -impl Protobuf for ConnectionEnd {} - -impl TryFrom for ConnectionEnd { - type Error = DecodingError; - - fn try_from(value: RawConnectionEnd) -> Result { - let state = value.state.try_into()?; - - if value.client_id.is_empty() { - return Err(DecodingError::missing_raw_data("connection end client ID"))?; - } - - if value.versions.is_empty() { - return Err(DecodingError::missing_raw_data("connection end versions"))?; - } - - Self::new( - state, - value.client_id.parse()?, - value - .counterparty - .ok_or(DecodingError::missing_raw_data("counterparty"))? - .try_into()?, - value - .versions - .into_iter() - .map(Version::try_from) - .collect::, _>>()?, - Duration::from_nanos(value.delay_period), - ) - .map_err(|_| DecodingError::invalid_raw_data("connection end")) - } -} - -impl From for RawConnectionEnd { - fn from(value: ConnectionEnd) -> Self { - RawConnectionEnd { - client_id: value.client_id.to_string(), - versions: value - .versions - .iter() - .map(|v| From::from(v.clone())) - .collect(), - state: value.state as i32, - counterparty: Some(value.counterparty.into()), - delay_period: value.delay_period.as_nanos() as u64, - } - } -} - -impl ConnectionEnd { - pub fn new( - state: State, - client_id: ClientId, - counterparty: Counterparty, - versions: Vec, - delay_period: Duration, - ) -> Result { - // Note: `versions`'s semantics vary based on the `State` of the connection: - // + Init: contains the set of compatible versions, - // + TryOpen/Open: contains the single version chosen by the handshake protocol. - if state != State::Init && versions.len() != 1 { - return Err(ConnectionError::InvalidState { description: "failed to initialize new ConnectionEnd; expected `Init` connection state and a single version".to_string() }); - } - - Ok(Self { - state, - client_id, - counterparty, - versions, - delay_period, - }) - } - - /// Getter for the state of this connection end. - pub fn state(&self) -> &State { - &self.state - } - - /// Setter for the `state` field. - pub fn set_state(&mut self, new_state: State) { - self.state = new_state; - } - - /// Setter for the `counterparty` field. - pub fn set_counterparty(&mut self, new_cparty: Counterparty) { - self.counterparty = new_cparty; - } - - /// Setter for the `version` field. - pub fn set_version(&mut self, new_version: Version) { - self.versions = vec![new_version]; - } - - /// Helper function to compare the counterparty of this end with another counterparty. - pub fn counterparty_matches(&self, other: &Counterparty) -> bool { - self.counterparty.eq(other) - } - - /// Helper function to compare the client id of this end with another client identifier. - pub fn client_id_matches(&self, other: &ClientId) -> bool { - self.client_id.eq(other) - } - - /// Helper function to determine whether the connection is open. - pub fn is_open(&self) -> bool { - self.state == State::Open - } - - /// Helper function to determine whether the connection is uninitialized. - pub fn is_uninitialized(&self) -> bool { - self.state == State::Uninitialized - } - - /// Checks if the state of this connection end matches with an expected state. - pub fn verify_state_matches(&self, expected: &State) -> Result<(), ConnectionError> { - if !self.state.eq(expected) { - return Err(ConnectionError::MismatchedConnectionStates { - expected: expected.to_string(), - actual: self.state.to_string(), - }); - } - Ok(()) - } - - /// Getter for the client id on the local party of this connection end. - pub fn client_id(&self) -> &ClientId { - &self.client_id - } - - /// Getter for the list of versions in this connection end. - pub fn versions(&self) -> &[Version] { - &self.versions - } - - /// Getter for the counterparty. - pub fn counterparty(&self) -> &Counterparty { - &self.counterparty - } - - /// Getter for the delay_period field. This represents the duration, at minimum, - /// to delay the sending of a packet after the client update for that packet has been submitted. - pub fn delay_period(&self) -> Duration { - self.delay_period - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Counterparty { - pub client_id: ClientId, - pub connection_id: Option, - pub prefix: CommitmentPrefix, -} - -impl Protobuf for Counterparty {} - -// Converts from the wire format RawCounterparty. Typically used from the relayer side -// during queries for response validation and to extract the Counterparty structure. -impl TryFrom for Counterparty { - type Error = DecodingError; - - fn try_from(raw_counterparty: RawCounterparty) -> Result { - let connection_id: Option = if raw_counterparty.connection_id.is_empty() { - None - } else { - Some(raw_counterparty.connection_id.parse()?) - }; - Ok(Counterparty::new( - raw_counterparty.client_id.parse()?, - connection_id, - raw_counterparty - .prefix - .ok_or(DecodingError::missing_raw_data("counterparty prefix"))? - .key_prefix - .into(), - )) - } -} - -impl From for RawCounterparty { - fn from(value: Counterparty) -> Self { - RawCounterparty { - client_id: value.client_id.as_str().to_string(), - connection_id: value - .connection_id - .map_or_else(|| "".to_string(), |v| v.as_str().to_string()), - prefix: Some(ibc_proto::ibc::core::commitment::v1::MerklePrefix { - key_prefix: value.prefix.into_vec(), - }), - } - } -} - -impl Counterparty { - pub fn new( - client_id: ClientId, - connection_id: Option, - prefix: CommitmentPrefix, - ) -> Self { - Self { - client_id, - connection_id, - prefix, - } - } - - /// Getter for the client id. - pub fn client_id(&self) -> &ClientId { - &self.client_id - } - - /// Getter for connection id. - pub fn connection_id(&self) -> Option<&ConnectionId> { - self.connection_id.as_ref() - } - - pub fn prefix(&self) -> &CommitmentPrefix { - &self.prefix - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize), - borsh(use_discriminant = false) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum State { - Uninitialized = 0isize, - Init = 1isize, - TryOpen = 2isize, - Open = 3isize, -} - -impl State { - /// Yields the State as a string. - pub fn as_str(&self) -> &'static str { - match self { - Self::Uninitialized => "UNINITIALIZED", - Self::Init => "INIT", - Self::TryOpen => "TRYOPEN", - Self::Open => "OPEN", - } - } - - /// Parses the State out from an i32. - pub fn from_i32(s: i32) -> Result { - match s { - 0 => Ok(Self::Uninitialized), - 1 => Ok(Self::Init), - 2 => Ok(Self::TryOpen), - 3 => Ok(Self::Open), - _ => Err(ConnectionError::MismatchedConnectionStates { - expected: "0, 1, 2, or 3".to_string(), - actual: s.to_string(), - }), - } - } - - /// Returns if this connection state is `Open`. - pub fn is_open(self) -> bool { - self == State::Open - } - - /// Returns if this connection with this state - /// has progressed less than or the same as the argument. - /// - /// # Example - /// ```rust,ignore - /// assert!(State::Init.less_or_equal_progress(State::Open)); - /// assert!(State::TryOpen.less_or_equal_progress(State::TryOpen)); - /// assert!(!State::Open.less_or_equal_progress(State::Uninitialized)); - /// ``` - pub fn less_or_equal_progress(self, other: Self) -> bool { - self as u32 <= other as u32 - } -} - -impl Display for State { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - write!(f, "{}", self.as_str()) - } -} - -impl TryFrom for State { - type Error = DecodingError; - - fn try_from(value: i32) -> Result { - match value { - 0 => Ok(Self::Uninitialized), - 1 => Ok(Self::Init), - 2 => Ok(Self::TryOpen), - 3 => Ok(Self::Open), - _ => Err(DecodingError::invalid_raw_data(format!( - "connection state expected to be 0, 1, 2, or 3, actual {value}", - ))), - } - } -} - -impl From for i32 { - fn from(value: State) -> Self { - value as i32 - } -} diff --git a/ibc-core/ics03-connection/types/src/error.rs b/ibc-core/ics03-connection/types/src/error.rs deleted file mode 100644 index fa4ad6297..000000000 --- a/ibc-core/ics03-connection/types/src/error.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Defines the connection error type - -use displaydoc::Display; -use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::Height; -use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; -use ibc_primitives::prelude::*; -use ibc_primitives::{Timestamp, TimestampError}; - -#[derive(Debug, Display)] -pub enum ConnectionError { - /// client error: {0} - Client(ClientError), - /// decoding error: {0} - Decoding(DecodingError), - /// host error: {0} - Host(HostError), - /// timestamp error: {0} - Timestamp(TimestampError), - /// invalid counterparty - InvalidCounterparty, - /// invalid connection state: {description} - InvalidState { description: String }, - /// mismatched connection states: expected `{expected}`, actual `{actual}` - MismatchedConnectionStates { expected: String, actual: String }, - /// missing supported features - MissingFeatures, - /// missing common version - MissingCommonVersion, - /// missing counterparty - MissingCounterparty, - /// insufficient consensus height `{current_height}` for host chain; needs to meet counterparty's height `{target_height}` - InsufficientConsensusHeight { - target_height: Height, - current_height: Height, - }, - /// insufficient blocks elapsed: current height `{current_host_height}` needs to meet `{earliest_valid_height}` - InsufficientBlocksElapsed { - current_host_height: Height, - earliest_valid_height: Height, - }, - /// insufficient time elapsed: current timestamp `{current_host_time}` needs to meet `{earliest_valid_time}` - InsufficientTimeElapsed { - current_host_time: Timestamp, - earliest_valid_time: Timestamp, - }, -} - -impl From for ConnectionError { - fn from(e: DecodingError) -> Self { - Self::Decoding(e) - } -} - -impl From for ConnectionError { - fn from(e: IdentifierError) -> Self { - Self::Decoding(DecodingError::Identifier(e)) - } -} - -impl From for ConnectionError { - fn from(e: TimestampError) -> Self { - Self::Timestamp(e) - } -} - -impl From for ConnectionError { - fn from(e: ClientError) -> Self { - Self::Client(e) - } -} - -impl From for ConnectionError { - fn from(e: HostError) -> Self { - Self::Host(e) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for ConnectionError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self { - Self::Host(e) => Some(e), - Self::Client(e) => Some(e), - Self::Decoding(e) => Some(e), - Self::Timestamp(e) => Some(e), - _ => None, - } - } -} diff --git a/ibc-core/ics03-connection/types/src/events.rs b/ibc-core/ics03-connection/types/src/events.rs deleted file mode 100644 index 08f64d9e1..000000000 --- a/ibc-core/ics03-connection/types/src/events.rs +++ /dev/null @@ -1,413 +0,0 @@ -//! Types for the IBC events emitted from Tendermint Websocket by the connection module. - -use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; -use ibc_primitives::prelude::*; -use tendermint::abci; - -/// Connection event types -const CONNECTION_OPEN_INIT_EVENT: &str = "connection_open_init"; -const CONNECTION_OPEN_TRY_EVENT: &str = "connection_open_try"; -const CONNECTION_OPEN_ACK_EVENT: &str = "connection_open_ack"; -const CONNECTION_OPEN_CONFIRM_EVENT: &str = "connection_open_confirm"; - -/// The content of the `key` field for the attribute containing the connection identifier. -pub const CONN_ID_ATTRIBUTE_KEY: &str = "connection_id"; -pub const CLIENT_ID_ATTRIBUTE_KEY: &str = "client_id"; -pub const COUNTERPARTY_CONN_ID_ATTRIBUTE_KEY: &str = "counterparty_connection_id"; -pub const COUNTERPARTY_CLIENT_ID_ATTRIBUTE_KEY: &str = "counterparty_client_id"; - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct Attributes { - pub connection_id: ConnectionId, - pub client_id: ClientId, - pub counterparty_connection_id: Option, - pub counterparty_client_id: ClientId, -} - -/// Convert attributes to Tendermint ABCI tags -impl From for Vec { - fn from(a: Attributes) -> Self { - let conn_id = (CONN_ID_ATTRIBUTE_KEY, a.connection_id.as_str()).into(); - let client_id = (CLIENT_ID_ATTRIBUTE_KEY, a.client_id.as_str()).into(); - - let counterparty_conn_id = ( - COUNTERPARTY_CONN_ID_ATTRIBUTE_KEY, - a.counterparty_connection_id - .as_ref() - .map(|id| id.as_str()) - .unwrap_or(""), - ) - .into(); - - let counterparty_client_id = ( - COUNTERPARTY_CLIENT_ID_ATTRIBUTE_KEY, - a.counterparty_client_id.as_str(), - ) - .into(); - - vec![ - conn_id, - client_id, - counterparty_client_id, - counterparty_conn_id, - ] - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct OpenInit(Attributes); - -impl OpenInit { - /// Per our convention, this event is generated on chain A. - pub fn new( - conn_id_on_a: ConnectionId, - client_id_on_a: ClientId, - client_id_on_b: ClientId, - ) -> Self { - Self(Attributes { - connection_id: conn_id_on_a, - client_id: client_id_on_a, - counterparty_connection_id: None, - counterparty_client_id: client_id_on_b, - }) - } - - pub fn conn_id_on_a(&self) -> &ConnectionId { - &self.0.connection_id - } - pub fn client_id_on_a(&self) -> &ClientId { - &self.0.client_id - } - pub fn conn_id_on_b(&self) -> Option<&ConnectionId> { - self.0.counterparty_connection_id.as_ref() - } - pub fn client_id_on_b(&self) -> &ClientId { - &self.0.counterparty_client_id - } - - pub fn event_type(&self) -> &str { - CONNECTION_OPEN_INIT_EVENT - } -} - -impl From for abci::Event { - fn from(v: OpenInit) -> Self { - abci::Event { - kind: CONNECTION_OPEN_INIT_EVENT.to_string(), - attributes: v.0.into(), - } - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct OpenTry(Attributes); - -impl OpenTry { - /// Per our convention, this event is generated on chain B. - pub fn new( - conn_id_on_b: ConnectionId, - client_id_on_b: ClientId, - conn_id_on_a: ConnectionId, - client_id_on_a: ClientId, - ) -> Self { - Self(Attributes { - connection_id: conn_id_on_b, - client_id: client_id_on_b, - counterparty_connection_id: Some(conn_id_on_a), - counterparty_client_id: client_id_on_a, - }) - } - - pub fn conn_id_on_b(&self) -> &ConnectionId { - &self.0.connection_id - } - pub fn client_id_on_b(&self) -> &ClientId { - &self.0.client_id - } - pub fn conn_id_on_a(&self) -> Option<&ConnectionId> { - self.0.counterparty_connection_id.as_ref() - } - pub fn client_id_on_a(&self) -> &ClientId { - &self.0.counterparty_client_id - } - - pub fn event_type(&self) -> &str { - CONNECTION_OPEN_TRY_EVENT - } -} - -impl From for abci::Event { - fn from(v: OpenTry) -> Self { - abci::Event { - kind: CONNECTION_OPEN_TRY_EVENT.to_string(), - attributes: v.0.into(), - } - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct OpenAck(Attributes); - -impl OpenAck { - /// Per our convention, this event is generated on chain A. - pub fn new( - conn_id_on_a: ConnectionId, - client_id_on_a: ClientId, - conn_id_on_b: ConnectionId, - client_id_on_b: ClientId, - ) -> Self { - Self(Attributes { - connection_id: conn_id_on_a, - client_id: client_id_on_a, - counterparty_connection_id: Some(conn_id_on_b), - counterparty_client_id: client_id_on_b, - }) - } - - pub fn conn_id_on_a(&self) -> &ConnectionId { - &self.0.connection_id - } - pub fn client_id_on_a(&self) -> &ClientId { - &self.0.client_id - } - pub fn conn_id_on_b(&self) -> Option<&ConnectionId> { - self.0.counterparty_connection_id.as_ref() - } - pub fn client_id_on_b(&self) -> &ClientId { - &self.0.counterparty_client_id - } - - pub fn event_type(&self) -> &str { - CONNECTION_OPEN_ACK_EVENT - } -} - -impl From for abci::Event { - fn from(v: OpenAck) -> Self { - abci::Event { - kind: CONNECTION_OPEN_ACK_EVENT.to_string(), - attributes: v.0.into(), - } - } -} - -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct OpenConfirm(Attributes); - -impl OpenConfirm { - /// Per our convention, this event is generated on chain B. - pub fn new( - conn_id_on_b: ConnectionId, - client_id_on_b: ClientId, - conn_id_on_a: ConnectionId, - client_id_on_a: ClientId, - ) -> Self { - Self(Attributes { - connection_id: conn_id_on_b, - client_id: client_id_on_b, - counterparty_connection_id: Some(conn_id_on_a), - counterparty_client_id: client_id_on_a, - }) - } - - pub fn conn_id_on_b(&self) -> &ConnectionId { - &self.0.connection_id - } - pub fn client_id_on_b(&self) -> &ClientId { - &self.0.client_id - } - pub fn conn_id_on_a(&self) -> Option<&ConnectionId> { - self.0.counterparty_connection_id.as_ref() - } - pub fn client_id_on_a(&self) -> &ClientId { - &self.0.counterparty_client_id - } - - pub fn event_type(&self) -> &str { - CONNECTION_OPEN_CONFIRM_EVENT - } -} - -impl From for abci::Event { - fn from(v: OpenConfirm) -> Self { - abci::Event { - kind: CONNECTION_OPEN_CONFIRM_EVENT.to_string(), - attributes: v.0.into(), - } - } -} - -#[cfg(test)] -mod tests { - - use core::str::FromStr; - - use ibc_core_host_types::identifiers::ClientType; - use tendermint::abci::Event as AbciEvent; - - use super::*; - - #[test] - fn ibc_to_abci_connection_events() { - struct Test { - kind: &'static str, - event: AbciEvent, - expected_keys: Vec<&'static str>, - expected_values: Vec<&'static str>, - } - - let client_type = ClientType::from_str("07-tendermint") - .expect("never fails because it's a valid client type"); - let conn_id_on_a = ConnectionId::zero(); - let client_id_on_a = client_type.build_client_id(0); - let conn_id_on_b = ConnectionId::new(1); - let client_id_on_b = client_type.build_client_id(1); - let expected_keys = vec![ - "connection_id", - "client_id", - "counterparty_client_id", - "counterparty_connection_id", - ]; - let expected_values = vec![ - "connection-0", - "07-tendermint-0", - "07-tendermint-1", - "connection-1", - ]; - - let tests: Vec = vec![ - Test { - kind: CONNECTION_OPEN_INIT_EVENT, - event: OpenInit::new( - conn_id_on_a.clone(), - client_id_on_a.clone(), - client_id_on_b.clone(), - ) - .into(), - expected_keys: expected_keys.clone(), - expected_values: expected_values - .iter() - .enumerate() - .map(|(i, v)| if i == 3 { "" } else { v }) - .collect(), - }, - Test { - kind: CONNECTION_OPEN_TRY_EVENT, - event: OpenTry::new( - conn_id_on_b.clone(), - client_id_on_b.clone(), - conn_id_on_a.clone(), - client_id_on_a.clone(), - ) - .into(), - expected_keys: expected_keys.clone(), - expected_values: expected_values.iter().rev().copied().collect(), - }, - Test { - kind: CONNECTION_OPEN_ACK_EVENT, - event: OpenAck::new( - conn_id_on_a.clone(), - client_id_on_a.clone(), - conn_id_on_b.clone(), - client_id_on_b.clone(), - ) - .into(), - expected_keys: expected_keys.clone(), - expected_values: expected_values.clone(), - }, - Test { - kind: CONNECTION_OPEN_CONFIRM_EVENT, - event: OpenConfirm::new(conn_id_on_b, client_id_on_b, conn_id_on_a, client_id_on_a) - .into(), - expected_keys: expected_keys.clone(), - expected_values: expected_values.iter().rev().copied().collect(), - }, - ]; - - for t in tests { - assert_eq!(t.kind, t.event.kind); - assert_eq!(t.expected_keys.len(), t.event.attributes.len()); - for (i, e) in t.event.attributes.iter().enumerate() { - assert_eq!( - e.key_str().unwrap(), - t.expected_keys[i], - "key mismatch for {:?}", - t.kind - ); - } - for (i, e) in t.event.attributes.iter().enumerate() { - assert_eq!( - e.value_str().unwrap(), - t.expected_values[i], - "value mismatch for {:?}", - t.kind - ); - } - } - } -} diff --git a/ibc-core/ics03-connection/types/src/lib.rs b/ibc-core/ics03-connection/types/src/lib.rs deleted file mode 100644 index 345e7eee1..000000000 --- a/ibc-core/ics03-connection/types/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Implementation of the Connection Semantics (ICS-03) data structures. -#![no_std] -#![forbid(unsafe_code)] -#![cfg_attr(not(test), deny(clippy::unwrap_used))] -#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] -#![deny( - warnings, - trivial_numeric_casts, - unused_import_braces, - unused_qualifications, - rust_2018_idioms -)] - -#[cfg(feature = "std")] -extern crate std; - -mod connection; -pub use connection::*; - -pub mod error; -pub mod events; -pub mod msgs; -pub mod version; - -/// Re-exports ICS-03 proto types from the `ibc-proto` crate for added -/// convenience -pub mod proto { - pub use ibc_proto::ibc::core::connection::*; -} diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs deleted file mode 100644 index 9379c25e3..000000000 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs +++ /dev/null @@ -1,103 +0,0 @@ -use ibc_core_client_types::Height; -use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ConnectionId; -use ibc_primitives::prelude::*; -use ibc_primitives::Signer; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; -use ibc_proto::Protobuf; - -use crate::version::Version; - -pub const CONN_OPEN_ACK_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenAck"; - -/// Per our convention, this message is sent to chain A. -/// The handler will check proofs of chain B. -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MsgConnectionOpenAck { - /// ConnectionId that chain A has chosen for its ConnectionEnd - pub conn_id_on_a: ConnectionId, - /// ConnectionId that chain B has chosen for its ConnectionEnd - pub conn_id_on_b: ConnectionId, - /// ClientState of client tracking chain A on chain B - pub client_state_of_a_on_b: Any, - /// proof of ConnectionEnd stored on Chain B during ConnOpenTry - pub proof_conn_end_on_b: CommitmentProofBytes, - /// proof of ClientState tracking chain A on chain B - pub proof_client_state_of_a_on_b: CommitmentProofBytes, - /// proof that chain B has stored ConsensusState of chain A on its client - pub proof_consensus_state_of_a_on_b: CommitmentProofBytes, - /// Height at which all proofs in this message were taken - pub proofs_height_on_b: Height, - /// height of latest header of chain A that updated the client on chain B - pub consensus_height_of_a_on_b: Height, - pub version: Version, - pub signer: Signer, - /// optional proof of host state machines (chain A) that are unable to - /// introspect their own consensus state - pub proof_consensus_state_of_a: Option, -} - -impl Protobuf for MsgConnectionOpenAck {} - -impl TryFrom for MsgConnectionOpenAck { - type Error = DecodingError; - - fn try_from(msg: RawMsgConnectionOpenAck) -> Result { - Ok(Self { - conn_id_on_a: msg.connection_id.parse()?, - conn_id_on_b: msg.counterparty_connection_id.parse()?, - client_state_of_a_on_b: msg - .client_state - .ok_or(DecodingError::missing_raw_data("client state"))?, - version: msg - .version - .ok_or(DecodingError::missing_raw_data("connection version"))? - .try_into()?, - proof_conn_end_on_b: msg.proof_try.try_into()?, - proof_client_state_of_a_on_b: msg.proof_client.try_into()?, - proof_consensus_state_of_a_on_b: msg.proof_consensus.try_into()?, - proofs_height_on_b: msg - .proof_height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(DecodingError::missing_raw_data("proof height"))?, - consensus_height_of_a_on_b: msg - .consensus_height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(DecodingError::missing_raw_data("consensus height"))?, - signer: msg.signer.into(), - proof_consensus_state_of_a: if msg.host_consensus_state_proof.is_empty() { - None - } else { - Some(msg.host_consensus_state_proof.try_into()?) - }, - }) - } -} - -impl From for RawMsgConnectionOpenAck { - fn from(msg: MsgConnectionOpenAck) -> Self { - RawMsgConnectionOpenAck { - connection_id: msg.conn_id_on_a.as_str().to_string(), - counterparty_connection_id: msg.conn_id_on_b.as_str().to_string(), - client_state: Some(msg.client_state_of_a_on_b), - proof_height: Some(msg.proofs_height_on_b.into()), - proof_try: msg.proof_conn_end_on_b.into(), - proof_client: msg.proof_client_state_of_a_on_b.into(), - proof_consensus: msg.proof_consensus_state_of_a_on_b.into(), - consensus_height: Some(msg.consensus_height_of_a_on_b.into()), - version: Some(msg.version.into()), - signer: msg.signer.to_string(), - host_consensus_state_proof: match msg.proof_consensus_state_of_a { - Some(proof) => proof.into(), - None => vec![], - }, - } - } -} diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs deleted file mode 100644 index 24a58b87e..000000000 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs +++ /dev/null @@ -1,59 +0,0 @@ -use ibc_core_client_types::Height; -use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ConnectionId; -use ibc_primitives::prelude::*; -use ibc_primitives::Signer; -use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; -use ibc_proto::Protobuf; - -pub const CONN_OPEN_CONFIRM_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenConfirm"; - -/// Per our convention, this message is sent to chain B. -/// The handler will check proofs of chain A. -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MsgConnectionOpenConfirm { - /// ConnectionId that chain B has chosen for its ConnectionEnd - pub conn_id_on_b: ConnectionId, - /// proof of ConnectionEnd stored on Chain A during ConnOpenInit - pub proof_conn_end_on_a: CommitmentProofBytes, - /// Height at which `proof_conn_end_on_a` in this message was taken - pub proof_height_on_a: Height, - pub signer: Signer, -} - -impl Protobuf for MsgConnectionOpenConfirm {} - -impl TryFrom for MsgConnectionOpenConfirm { - type Error = DecodingError; - - fn try_from(msg: RawMsgConnectionOpenConfirm) -> Result { - Ok(Self { - conn_id_on_b: msg.connection_id.parse()?, - proof_conn_end_on_a: msg.proof_ack.try_into()?, - proof_height_on_a: msg - .proof_height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(DecodingError::invalid_raw_data( - "msg conn open confirm proof height", - ))?, - signer: msg.signer.into(), - }) - } -} - -impl From for RawMsgConnectionOpenConfirm { - fn from(msg: MsgConnectionOpenConfirm) -> Self { - RawMsgConnectionOpenConfirm { - connection_id: msg.conn_id_on_b.as_str().to_string(), - proof_ack: msg.proof_conn_end_on_a.into(), - proof_height: Some(msg.proof_height_on_a.into()), - signer: msg.signer.to_string(), - } - } -} diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs deleted file mode 100644 index 57790a387..000000000 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs +++ /dev/null @@ -1,126 +0,0 @@ -use core::time::Duration; - -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; -use ibc_primitives::prelude::*; -use ibc_primitives::Signer; -use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnectionOpenInit; -use ibc_proto::Protobuf; - -use crate::connection::Counterparty; -use crate::version::Version; - -pub const CONN_OPEN_INIT_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenInit"; - -/// Per our convention, this message is sent to chain A. -/// The handler will check proofs of chain B. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -pub struct MsgConnectionOpenInit { - /// ClientId on chain A that the connection is being opened for - pub client_id_on_a: ClientId, - pub counterparty: Counterparty, - pub version: Option, - pub delay_period: Duration, - pub signer: Signer, -} - -/// This module encapsulates the workarounds we need to do to implement -/// `BorshSerialize` and `BorshDeserialize` on `MsgConnectionOpenInit` -#[cfg(feature = "borsh")] -mod borsh_impls { - use borsh::io::{self, Read}; - use borsh::{BorshDeserialize, BorshSerialize}; - - use super::*; - - #[derive(BorshSerialize, BorshDeserialize)] - pub struct InnerMsgConnectionOpenInit { - /// ClientId on chain A that the connection is being opened for - pub client_id_on_a: ClientId, - pub counterparty: Counterparty, - pub version: Option, - pub delay_period_nanos: u64, - pub signer: Signer, - } - - impl BorshSerialize for MsgConnectionOpenInit { - fn serialize(&self, writer: &mut W) -> io::Result<()> { - let delay_period_nanos: u64 = - self.delay_period.as_nanos().try_into().map_err(|_| { - io::Error::new( - io::ErrorKind::Other, - format!( - "Duration too long: `{}` nanos", - self.delay_period.as_nanos() - ), - ) - })?; - - let inner = InnerMsgConnectionOpenInit { - client_id_on_a: self.client_id_on_a.clone(), - counterparty: self.counterparty.clone(), - version: self.version.clone(), - delay_period_nanos, - signer: self.signer.clone(), - }; - - inner.serialize(writer) - } - } - - impl BorshDeserialize for MsgConnectionOpenInit { - fn deserialize_reader(reader: &mut R) -> io::Result { - let inner = InnerMsgConnectionOpenInit::deserialize_reader(reader)?; - - Ok(MsgConnectionOpenInit { - client_id_on_a: inner.client_id_on_a, - counterparty: inner.counterparty, - version: inner.version, - delay_period: Duration::from_nanos(inner.delay_period_nanos), - signer: inner.signer, - }) - } - } -} - -impl Protobuf for MsgConnectionOpenInit {} - -impl TryFrom for MsgConnectionOpenInit { - type Error = DecodingError; - - fn try_from(msg: RawMsgConnectionOpenInit) -> Result { - let counterparty: Counterparty = msg - .counterparty - .ok_or(DecodingError::missing_raw_data( - "msg conn open init counterparty", - ))? - .try_into()?; - - if let Some(cid) = counterparty.connection_id() { - return Err(DecodingError::invalid_raw_data(format!( - "expected msg conn open init connection ID to be empty, actual `{cid}`", - ))); - } - - Ok(Self { - client_id_on_a: msg.client_id.parse()?, - counterparty, - version: msg.version.map(TryInto::try_into).transpose()?, - delay_period: Duration::from_nanos(msg.delay_period), - signer: msg.signer.into(), - }) - } -} - -impl From for RawMsgConnectionOpenInit { - fn from(ics_msg: MsgConnectionOpenInit) -> Self { - RawMsgConnectionOpenInit { - client_id: ics_msg.client_id_on_a.as_str().to_string(), - counterparty: Some(ics_msg.counterparty.into()), - version: ics_msg.version.map(Into::into), - delay_period: ics_msg.delay_period.as_nanos() as u64, - signer: ics_msg.signer.to_string(), - } - } -} diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs deleted file mode 100644 index ea5f22f03..000000000 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs +++ /dev/null @@ -1,226 +0,0 @@ -use core::time::Duration; - -use ibc_core_client_types::Height; -use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; -use ibc_primitives::prelude::*; -use ibc_primitives::Signer; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenTry as RawMsgConnectionOpenTry; -use ibc_proto::Protobuf; - -use crate::connection::Counterparty; -use crate::version::Version; - -pub const CONN_OPEN_TRY_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenTry"; - -/// Per our convention, this message is sent to chain B. -/// The handler will check proofs of chain A. -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MsgConnectionOpenTry { - /// ClientId on B that the connection is being opened for - pub client_id_on_b: ClientId, - /// ClientState of client tracking chain B on chain A - pub client_state_of_b_on_a: Any, - /// ClientId, ConnectionId and prefix of chain A - pub counterparty: Counterparty, - /// Versions supported by chain A - pub versions_on_a: Vec, - /// proof of ConnectionEnd stored on Chain A during ConnOpenInit - pub proof_conn_end_on_a: CommitmentProofBytes, - /// proof that chain A has stored ClientState of chain B on its client - pub proof_client_state_of_b_on_a: CommitmentProofBytes, - /// proof that chain A has stored ConsensusState of chain B on its client - pub proof_consensus_state_of_b_on_a: CommitmentProofBytes, - /// Height at which all proofs in this message were taken - pub proofs_height_on_a: Height, - /// height of latest header of chain A that updated the client on chain B - pub consensus_height_of_b_on_a: Height, - pub delay_period: Duration, - pub signer: Signer, - /// optional proof of host state machines (chain B) that are unable to - /// introspect their own consensus state - pub proof_consensus_state_of_b: Option, - - #[deprecated(since = "0.22.0")] - /// Only kept here for proper conversion to/from the raw type - pub previous_connection_id: String, -} - -#[allow(deprecated)] -#[cfg(feature = "borsh")] -mod borsh_impls { - use borsh::io::{self, Read}; - use borsh::{BorshDeserialize, BorshSerialize}; - - use super::*; - - #[derive(BorshSerialize, BorshDeserialize)] - pub struct InnerMsgConnectionOpenTry { - /// ClientId on B that the connection is being opened for - pub client_id_on_b: ClientId, - /// ClientState of client tracking chain B on chain A - pub client_state_of_b_on_a: Any, - /// ClientId, ConnectionId and prefix of chain A - pub counterparty: Counterparty, - /// Versions supported by chain A - pub versions_on_a: Vec, - /// proof of ConnectionEnd stored on Chain A during ConnOpenInit - pub proof_conn_end_on_a: CommitmentProofBytes, - /// proof that chain A has stored ClientState of chain B on its client - pub proof_client_state_of_b_on_a: CommitmentProofBytes, - /// proof that chain A has stored ConsensusState of chain B on its client - pub proof_consensus_state_of_b_on_a: CommitmentProofBytes, - /// Height at which all proofs in this message were taken - pub proofs_height_on_a: Height, - /// height of latest header of chain A that updated the client on chain B - pub consensus_height_of_b_on_a: Height, - pub delay_period_nanos: u64, - pub signer: Signer, - /// optional proof of host state machines (chain B) that are unable to - /// introspect their own consensus state - pub proof_consensus_state_of_b: Option, - - #[deprecated(since = "0.22.0")] - /// Only kept here for proper conversion to/from the raw type - previous_connection_id: String, - } - - impl BorshSerialize for MsgConnectionOpenTry { - fn serialize(&self, writer: &mut W) -> io::Result<()> { - let delay_period_nanos: u64 = - self.delay_period.as_nanos().try_into().map_err(|_| { - io::Error::new( - io::ErrorKind::Other, - format!("Duration too long: {} nanos", self.delay_period.as_nanos()), - ) - })?; - - let inner = InnerMsgConnectionOpenTry { - client_id_on_b: self.client_id_on_b.clone(), - client_state_of_b_on_a: self.client_state_of_b_on_a.clone(), - counterparty: self.counterparty.clone(), - versions_on_a: self.versions_on_a.clone(), - proof_conn_end_on_a: self.proof_conn_end_on_a.clone(), - proof_client_state_of_b_on_a: self.proof_client_state_of_b_on_a.clone(), - proof_consensus_state_of_b_on_a: self.proof_consensus_state_of_b_on_a.clone(), - proofs_height_on_a: self.proofs_height_on_a, - consensus_height_of_b_on_a: self.consensus_height_of_b_on_a, - delay_period_nanos, - signer: self.signer.clone(), - proof_consensus_state_of_b: self.proof_consensus_state_of_b.clone(), - previous_connection_id: self.previous_connection_id.clone(), - }; - - inner.serialize(writer) - } - } - - impl BorshDeserialize for MsgConnectionOpenTry { - fn deserialize_reader(reader: &mut R) -> io::Result { - let inner = InnerMsgConnectionOpenTry::deserialize_reader(reader)?; - - Ok(MsgConnectionOpenTry { - client_id_on_b: inner.client_id_on_b, - client_state_of_b_on_a: inner.client_state_of_b_on_a, - counterparty: inner.counterparty, - versions_on_a: inner.versions_on_a, - proof_conn_end_on_a: inner.proof_conn_end_on_a, - proof_client_state_of_b_on_a: inner.proof_client_state_of_b_on_a, - proof_consensus_state_of_b_on_a: inner.proof_consensus_state_of_b_on_a, - proofs_height_on_a: inner.proofs_height_on_a, - consensus_height_of_b_on_a: inner.consensus_height_of_b_on_a, - delay_period: Duration::from_nanos(inner.delay_period_nanos), - signer: inner.signer, - proof_consensus_state_of_b: inner.proof_consensus_state_of_b, - previous_connection_id: inner.previous_connection_id, - }) - } - } -} - -impl Protobuf for MsgConnectionOpenTry {} - -impl TryFrom for MsgConnectionOpenTry { - type Error = DecodingError; - - fn try_from(msg: RawMsgConnectionOpenTry) -> Result { - let counterparty_versions = msg - .counterparty_versions - .into_iter() - .map(Version::try_from) - .collect::, _>>()?; - - if counterparty_versions.is_empty() { - return Err(DecodingError::missing_raw_data( - "msg conn open try connection versions", - )); - } - - // We set the deprecated `previous_connection_id` field so that we can - // properly convert `MsgConnectionOpenTry` into its raw form - #[allow(deprecated)] - Ok(Self { - previous_connection_id: msg.previous_connection_id, - client_id_on_b: msg.client_id.parse()?, - client_state_of_b_on_a: msg.client_state.ok_or(DecodingError::missing_raw_data( - "msg conn open try client state", - ))?, - counterparty: msg - .counterparty - .ok_or(DecodingError::missing_raw_data( - "msg conn open try counterparty", - ))? - .try_into()?, - versions_on_a: counterparty_versions, - proof_conn_end_on_a: msg.proof_init.try_into()?, - proof_client_state_of_b_on_a: msg.proof_client.try_into()?, - proof_consensus_state_of_b_on_a: msg.proof_consensus.try_into()?, - proofs_height_on_a: msg - .proof_height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(DecodingError::invalid_raw_data( - "msg conn open try proof height", - ))?, - consensus_height_of_b_on_a: msg - .consensus_height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(DecodingError::invalid_raw_data( - "msg conn open try consensus height", - ))?, - delay_period: Duration::from_nanos(msg.delay_period), - signer: msg.signer.into(), - proof_consensus_state_of_b: if msg.host_consensus_state_proof.is_empty() { - None - } else { - Some(msg.host_consensus_state_proof.try_into()?) - }, - }) - } -} - -impl From for RawMsgConnectionOpenTry { - fn from(msg: MsgConnectionOpenTry) -> Self { - #[allow(deprecated)] - RawMsgConnectionOpenTry { - client_id: msg.client_id_on_b.as_str().to_string(), - previous_connection_id: msg.previous_connection_id, - client_state: Some(msg.client_state_of_b_on_a), - counterparty: Some(msg.counterparty.into()), - delay_period: msg.delay_period.as_nanos() as u64, - counterparty_versions: msg.versions_on_a.iter().map(|v| v.clone().into()).collect(), - proof_height: Some(msg.proofs_height_on_a.into()), - proof_init: msg.proof_conn_end_on_a.into(), - proof_client: msg.proof_client_state_of_b_on_a.into(), - proof_consensus: msg.proof_consensus_state_of_b_on_a.into(), - consensus_height: Some(msg.consensus_height_of_b_on_a.into()), - signer: msg.signer.to_string(), - host_consensus_state_proof: match msg.proof_consensus_state_of_b { - Some(proof) => proof.into(), - None => vec![], - }, - } - } -} diff --git a/ibc-core/ics03-connection/types/src/msgs/mod.rs b/ibc-core/ics03-connection/types/src/msgs/mod.rs deleted file mode 100644 index d3be3dcf6..000000000 --- a/ibc-core/ics03-connection/types/src/msgs/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Message definitions for the connection handshake datagrams. -//! -//! We define each of the four messages in the connection handshake protocol as a `struct`. -//! Each such message comprises the same fields as the datagrams defined in ICS3 English spec: -//! . -//! -//! One departure from ICS3 is that we abstract the three counterparty fields (connection id, -//! prefix, and client id) into a single field of type `Counterparty`; this applies to messages -//! `MsgConnectionOpenInit` and `MsgConnectionOpenTry`. One other difference with regard to -//! abstraction is that all proof-related attributes in a message are encapsulated in `Proofs` type. -//! -//! Another difference to ICS3 specs is that each message comprises an additional field called -//! `signer` which is specific to Cosmos-SDK. - -use ibc_primitives::prelude::*; - -mod conn_open_ack; -mod conn_open_confirm; -mod conn_open_init; -mod conn_open_try; - -pub use conn_open_ack::*; -pub use conn_open_confirm::*; -pub use conn_open_init::*; -pub use conn_open_try::*; - -/// Enumeration of all possible messages that the ICS3 protocol processes. -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq, derive_more::From)] -pub enum ConnectionMsg { - OpenInit(MsgConnectionOpenInit), - OpenTry(MsgConnectionOpenTry), - OpenAck(MsgConnectionOpenAck), - OpenConfirm(MsgConnectionOpenConfirm), -} diff --git a/ibc-core/ics03-connection/types/src/version.rs b/ibc-core/ics03-connection/types/src/version.rs deleted file mode 100644 index 89f577cc0..000000000 --- a/ibc-core/ics03-connection/types/src/version.rs +++ /dev/null @@ -1,403 +0,0 @@ -//! Defines connection versioning type and functions - -use core::fmt::Display; - -use ibc_core_host_types::error::DecodingError; -use ibc_primitives::prelude::*; -use ibc_primitives::utils::PrettySlice; -use ibc_proto::ibc::core::connection::v1::Version as RawVersion; -use ibc_proto::Protobuf; - -use crate::error::ConnectionError; - -/// Stores the identifier and the features supported by a version -#[cfg_attr( - feature = "parity-scale-codec", - derive( - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo - ) -)] -#[cfg_attr( - feature = "borsh", - derive(borsh::BorshSerialize, borsh::BorshDeserialize) -)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Version { - /// unique version identifier - identifier: String, - /// list of features compatible with the specified identifier - features: Vec, -} - -impl Version { - /// Checks whether the version has a matching version identifier and its - /// feature set is a subset of the supported features - pub fn verify_is_supported( - &self, - supported_versions: &[Version], - ) -> Result<(), ConnectionError> { - let maybe_supported_version = find_supported_version(self, supported_versions)?; - - if self.features.is_empty() { - return Err(ConnectionError::MissingFeatures); - } - - for feature in self.features.iter() { - maybe_supported_version.verify_feature_supported(feature.to_string())?; - } - Ok(()) - } - - /// Checks whether the given feature is supported in this version - pub fn verify_feature_supported(&self, feature: String) -> Result<(), ConnectionError> { - if !self.features.contains(&feature) { - return Err(ConnectionError::MissingFeatures); - } - Ok(()) - } - - /// Returns the lists of supported versions - pub fn compatibles() -> Vec { - vec![Self { - identifier: "1".to_string(), - features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], - }] - } -} - -impl Protobuf for Version {} - -impl TryFrom for Version { - type Error = DecodingError; - - fn try_from(value: RawVersion) -> Result { - if value.identifier.trim().is_empty() { - return Err(DecodingError::missing_raw_data("version identifier")); - } - for feature in value.features.iter() { - if feature.trim().is_empty() { - return Err(DecodingError::missing_raw_data("version features")); - } - } - Ok(Version { - identifier: value.identifier, - features: value.features, - }) - } -} - -impl From for RawVersion { - fn from(value: Version) -> Self { - Self { - identifier: value.identifier, - features: value.features, - } - } -} - -impl Display for Version { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "Version {{ identifier: {}, features: {} }}", - self.identifier, - PrettySlice(&self.features) - ) - } -} - -/// Iterates over the descending ordered set of compatible IBC versions and -/// selects the first version with a version identifier that is supported by the -/// counterparty. The returned version contains a feature set with the -/// intersection of the features supported by the source and counterparty -/// chains. If the feature set intersection is nil, the search for a -/// compatible version continues. This function is called in the `conn_open_try` -/// handshake procedure. -/// -/// NOTE: Empty feature sets are not currently allowed for a chosen version. -pub fn pick_version( - supported_versions: &[Version], - counterparty_versions: &[Version], -) -> Result { - let mut intersection: Vec = Vec::new(); - - for sv in supported_versions.iter() { - if let Ok(cv) = find_supported_version(sv, counterparty_versions) { - if let Ok(feature_set) = get_feature_set_intersection(&sv.features, &cv.features) { - intersection.push(Version { - identifier: cv.identifier, - features: feature_set, - }) - } - } - } - - if intersection.is_empty() { - return Err(ConnectionError::MissingCommonVersion); - } - - intersection.sort_by(|a, b| a.identifier.cmp(&b.identifier)); - - Ok(intersection[0].clone()) -} - -/// Returns the version from the list of supported versions that matches the -/// given reference version. -fn find_supported_version( - version: &Version, - supported_versions: &[Version], -) -> Result { - supported_versions - .iter() - .find(|sv| sv.identifier == version.identifier) - .ok_or(ConnectionError::MissingCommonVersion) - .cloned() -} - -/// Returns the intersections of supported features by a host and the -/// counterparty features. This is done by iterating over all the features in -/// the host supported version and seeing if they exist in the feature set for -/// the counterparty version. -fn get_feature_set_intersection( - supported_features: &[String], - counterparty_features: &[String], -) -> Result, ConnectionError> { - let feature_set_intersection: Vec = supported_features - .iter() - .filter(|f| counterparty_features.contains(f)) - .cloned() - .collect(); - - if feature_set_intersection.is_empty() { - return Err(ConnectionError::MissingFeatures); - } - - Ok(feature_set_intersection) -} - -#[cfg(test)] -mod tests { - use ibc_primitives::prelude::*; - use ibc_proto::ibc::core::connection::v1::Version as RawVersion; - - use crate::error::ConnectionError; - use crate::version::{pick_version, Version}; - - fn get_dummy_features() -> Vec { - vec!["ORDER_RANDOM".to_string(), "ORDER_UNORDERED".to_string()] - } - - fn good_versions() -> Vec { - vec![ - Version { - identifier: "1".to_string(), - features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], - } - .into(), - RawVersion { - identifier: "2".to_string(), - features: get_dummy_features(), - }, - ] - .into_iter() - .collect() - } - - fn bad_versions_identifier() -> Vec { - vec![RawVersion { - identifier: "".to_string(), - features: get_dummy_features(), - }] - .into_iter() - .collect() - } - - fn bad_versions_features() -> Vec { - vec![RawVersion { - identifier: "2".to_string(), - features: vec!["".to_string()], - }] - .into_iter() - .collect() - } - - fn overlapping() -> (Vec, Vec, Version) { - ( - vec![ - Version { - identifier: "1".to_string(), - features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], - }, - Version { - identifier: "3".to_string(), - features: get_dummy_features(), - }, - Version { - identifier: "4".to_string(), - features: get_dummy_features(), - }, - ] - .into_iter() - .collect(), - vec![ - Version { - identifier: "2".to_string(), - features: get_dummy_features(), - }, - Version { - identifier: "4".to_string(), - features: get_dummy_features(), - }, - Version { - identifier: "3".to_string(), - features: get_dummy_features(), - }, - ] - .into_iter() - .collect(), - // Should pick version 3 as it's the lowest of the intersection {3, 4} - Version { - identifier: "3".to_string(), - features: get_dummy_features(), - }, - ) - } - - fn disjoint() -> (Vec, Vec) { - ( - vec![Version { - identifier: "1".to_string(), - features: Vec::new(), - }] - .into_iter() - .collect(), - vec![Version { - identifier: "2".to_string(), - features: Vec::new(), - }] - .into_iter() - .collect(), - ) - } - - #[test] - fn verify() { - struct Test { - name: String, - versions: Vec, - want_pass: bool, - } - let tests: Vec = vec![ - Test { - name: "Compatible versions".to_string(), - versions: vec![Version { - identifier: "1".to_string(), - features: get_dummy_features(), - } - .into()], - want_pass: true, - }, - Test { - name: "Multiple versions".to_string(), - versions: good_versions(), - want_pass: true, - }, - Test { - name: "Bad version identifier".to_string(), - versions: bad_versions_identifier(), - want_pass: false, - }, - Test { - name: "Bad version feature".to_string(), - versions: bad_versions_features(), - want_pass: false, - }, - Test { - name: "Bad versions empty".to_string(), - versions: Vec::new(), - want_pass: true, - }, - ]; - - for test in tests { - let versions = test - .versions - .into_iter() - .map(Version::try_from) - .collect::, _>>(); - - assert_eq!( - test.want_pass, - versions.is_ok(), - "Validate versions failed for test {} with error {:?}", - test.name, - versions.err(), - ); - } - } - #[test] - fn pick() { - struct Test { - name: String, - supported: Vec, - counterparty: Vec, - picked: Result, - want_pass: bool, - } - let tests: Vec = vec![ - Test { - name: "Compatible versions".to_string(), - supported: Version::compatibles(), - counterparty: Version::compatibles(), - picked: Ok(Version { - identifier: "1".to_string(), - features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], - }), - want_pass: true, - }, - Test { - name: "Overlapping versions".to_string(), - supported: overlapping().0, - counterparty: overlapping().1, - picked: Ok(overlapping().2), - want_pass: true, - }, - Test { - name: "Disjoint versions".to_string(), - supported: disjoint().0, - counterparty: disjoint().1, - picked: Err(ConnectionError::MissingCommonVersion), - want_pass: false, - }, - ]; - - for test in tests { - let version = pick_version(&test.supported, &test.counterparty); - - assert_eq!( - test.want_pass, - version.is_ok(), - "Validate versions failed for test {}", - test.name, - ); - - if test.want_pass { - assert_eq!(version.unwrap(), test.picked.unwrap()); - } - } - } - #[test] - fn serialize() { - let def = Version { - identifier: "1".to_string(), - features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], - }; - let def_raw: RawVersion = def.clone().into(); - let def_back = def_raw.try_into().unwrap(); - assert_eq!(def, def_back); - } -}