diff --git a/.changelog/unreleased/enhancement/164-refactor-error-system.md b/.changelog/unreleased/enhancement/164-refactor-error-system.md new file mode 100644 index 000000000..571f327f8 --- /dev/null +++ b/.changelog/unreleased/enhancement/164-refactor-error-system.md @@ -0,0 +1 @@ +- Remove `flex-error` and remove unused error variants([#164](https://github.com/cosmos/ibc-rs/issues/164)) \ No newline at end of file diff --git a/crates/ibc/Cargo.toml b/crates/ibc/Cargo.toml index 47bf87769..d4e56a5d2 100644 --- a/crates/ibc/Cargo.toml +++ b/crates/ibc/Cargo.toml @@ -18,7 +18,24 @@ all-features = true [features] default = ["std"] -std = ["flex-error/std", "flex-error/eyre_tracer", "ibc-proto/std", "clock"] +std = [ + "ibc-proto/std", + "ics23/std", + "serde/std", + "serde_json/std", + "erased-serde/std", + "tracing/std", + "prost/std", + "bytes/std", + "subtle-encoding/std", + "sha2/std", + "displaydoc/std", + "num-traits/std", + "uint/std", + "primitive-types/std", + "clock", + "tendermint/std", +] clock = ["tendermint/clock", "time/std"] # This feature grants access to development-time mocking libraries, such as `MockContext` or `MockHeader`. @@ -40,7 +57,7 @@ bytes = { version = "1.2.1", default-features = false } safe-regex = { version = "0.2.5", default-features = false } subtle-encoding = { version = "0.5", default-features = false } sha2 = { version = "0.10.6", default-features = false } -flex-error = { version = "0.4.4", default-features = false } +displaydoc = { version = "0.2", default-features = false } num-traits = { version = "0.2.15", default-features = false } derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display"] } uint = { version = "0.9", default-features = false } diff --git a/crates/ibc/src/applications/transfer/acknowledgement.rs b/crates/ibc/src/applications/transfer/acknowledgement.rs index a7a19b5ed..b17cfd826 100644 --- a/crates/ibc/src/applications/transfer/acknowledgement.rs +++ b/crates/ibc/src/applications/transfer/acknowledgement.rs @@ -2,7 +2,7 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use serde::{Deserialize, Serialize}; -use super::error::Error; +use super::error::TokenTransferError; use crate::core::ics26_routing::context::Acknowledgement as AckTrait; use crate::prelude::*; @@ -36,7 +36,7 @@ impl Acknowledgement { Self::Success(ConstAckSuccess::Success) } - pub fn from_error(err: Error) -> Self { + pub fn from_error(err: TokenTransferError) -> Self { Self::Error(format!("{}: {}", ACK_ERR_STR, err)) } } diff --git a/crates/ibc/src/applications/transfer/amount.rs b/crates/ibc/src/applications/transfer/amount.rs index c250e9190..674ee266c 100644 --- a/crates/ibc/src/applications/transfer/amount.rs +++ b/crates/ibc/src/applications/transfer/amount.rs @@ -2,7 +2,7 @@ use core::str::FromStr; use derive_more::{Display, From, Into}; use serde::{Deserialize, Serialize}; -use super::error::Error; +use super::error::TokenTransferError; use crate::bigint::U256; /// A type for representing token transfer amounts. @@ -22,10 +22,10 @@ impl Amount { } impl FromStr for Amount { - type Err = Error; + type Err = TokenTransferError; fn from_str(s: &str) -> Result { - let amount = U256::from_dec_str(s).map_err(Error::invalid_amount)?; + let amount = U256::from_dec_str(s).map_err(TokenTransferError::InvalidAmount)?; Ok(Self(amount)) } } diff --git a/crates/ibc/src/applications/transfer/coin.rs b/crates/ibc/src/applications/transfer/coin.rs index b6581c6fd..b6e9441e1 100644 --- a/crates/ibc/src/applications/transfer/coin.rs +++ b/crates/ibc/src/applications/transfer/coin.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use super::amount::Amount; use super::denom::{BaseDenom, PrefixedDenom}; -use super::error::Error; +use super::error::TokenTransferError; use crate::prelude::*; use crate::serializers::serde_string; @@ -30,35 +30,39 @@ pub struct Coin { impl Coin where - D::Err: Into, + D::Err: Into, { - pub fn from_string_list(coin_str: &str) -> Result, Error> { + pub fn from_string_list(coin_str: &str) -> Result, TokenTransferError> { coin_str.split(',').map(FromStr::from_str).collect() } } impl FromStr for Coin where - D::Err: Into, + D::Err: Into, { - type Err = Error; + type Err = TokenTransferError; #[allow(clippy::assign_op_pattern)] - fn from_str(coin_str: &str) -> Result { + fn from_str(coin_str: &str) -> Result { // Denominations can be 3 ~ 128 characters long and support letters, followed by either // a letter, a number or a separator ('/', ':', '.', '_' or '-'). // Loosely copy the regex from here: // https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/types/coin.go#L760-L762 let matcher = regex!(br"([0-9]+)([a-zA-Z0-9/:\\._\x2d]+)"); - let (m1, m2) = matcher - .match_slices(coin_str.as_bytes()) - .ok_or_else(|| Error::invalid_coin(coin_str.to_string()))?; + let (m1, m2) = matcher.match_slices(coin_str.as_bytes()).ok_or_else(|| { + TokenTransferError::InvalidCoin { + coin: coin_str.to_string(), + } + })?; - let amount = from_utf8(m1).map_err(Error::utf8_decode)?.parse()?; + let amount = from_utf8(m1) + .map_err(TokenTransferError::Utf8Decode)? + .parse()?; let denom = from_utf8(m2) - .map_err(Error::utf8_decode)? + .map_err(TokenTransferError::Utf8Decode)? .parse() .map_err(Into::into)?; @@ -68,9 +72,9 @@ where impl TryFrom for Coin where - D::Err: Into, + D::Err: Into, { - type Error = Error; + type Error = TokenTransferError; fn try_from(proto: ProtoCoin) -> Result, Self::Error> { let denom = D::from_str(&proto.denom).map_err(Into::into)?; @@ -108,7 +112,7 @@ mod tests { use super::*; #[test] - fn test_parse_raw_coin() -> Result<(), Error> { + fn test_parse_raw_coin() -> Result<(), TokenTransferError> { { let coin = RawCoin::from_str("123stake")?; assert_eq!(coin.denom, "stake"); @@ -137,7 +141,7 @@ mod tests { } #[test] - fn test_parse_raw_coin_list() -> Result<(), Error> { + fn test_parse_raw_coin_list() -> Result<(), TokenTransferError> { { let coins = RawCoin::from_string_list("123stake,1a1,999den0m")?; assert_eq!(coins.len(), 3); diff --git a/crates/ibc/src/applications/transfer/context.rs b/crates/ibc/src/applications/transfer/context.rs index abeb72baf..3deec331d 100644 --- a/crates/ibc/src/applications/transfer/context.rs +++ b/crates/ibc/src/applications/transfer/context.rs @@ -1,6 +1,6 @@ use sha2::{Digest, Sha256}; -use super::error::Error as Ics20Error; +use super::error::TokenTransferError; use crate::applications::transfer::acknowledgement::Acknowledgement; use crate::applications::transfer::events::{AckEvent, AckStatusEvent, RecvEvent, TimeoutEvent}; use crate::applications::transfer::packet::PacketData; @@ -11,7 +11,7 @@ use crate::applications::transfer::{PrefixedCoin, PrefixedDenom, VERSION}; use crate::core::ics04_channel::channel::{Counterparty, Order}; use crate::core::ics04_channel::commitment::PacketCommitment; use crate::core::ics04_channel::context::{ChannelKeeper, SendPacketReader}; -use crate::core::ics04_channel::error::Error as Ics04Error; +use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::handler::send_packet::SendPacketResult; use crate::core::ics04_channel::handler::ModuleExtras; use crate::core::ics04_channel::msgs::acknowledgement::Acknowledgement as GenericAcknowledgement; @@ -23,7 +23,7 @@ use crate::prelude::*; use crate::signer::Signer; pub trait TokenTransferKeeper: BankKeeper { - fn store_send_packet_result(&mut self, result: SendPacketResult) -> Result<(), Ics04Error> { + fn store_send_packet_result(&mut self, result: SendPacketResult) -> Result<(), PacketError> { self.store_next_sequence_send( result.port_id.clone(), result.channel_id.clone(), @@ -45,28 +45,28 @@ pub trait TokenTransferKeeper: BankKeeper { channel_id: ChannelId, sequence: Sequence, commitment: PacketCommitment, - ) -> Result<(), Ics04Error>; + ) -> Result<(), PacketError>; fn store_next_sequence_send( &mut self, port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error>; + ) -> Result<(), PacketError>; } pub trait TokenTransferReader: SendPacketReader { type AccountId: TryFrom; /// get_port returns the portID for the transfer module. - fn get_port(&self) -> Result; + fn get_port(&self) -> Result; /// Returns the escrow account id for a port and channel combination fn get_channel_escrow_address( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result<::AccountId, Ics20Error>; + ) -> Result<::AccountId, TokenTransferError>; /// Returns true iff send is enabled. fn is_send_enabled(&self) -> bool; @@ -91,7 +91,7 @@ where channel_id: ChannelId, sequence: Sequence, commitment: PacketCommitment, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { ChannelKeeper::store_packet_commitment(self, port_id, channel_id, sequence, commitment) } @@ -100,7 +100,7 @@ where port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { ChannelKeeper::store_next_sequence_send(self, port_id, channel_id, seq) } } @@ -128,21 +128,21 @@ pub trait BankKeeper { from: &Self::AccountId, to: &Self::AccountId, amt: &PrefixedCoin, - ) -> Result<(), Ics20Error>; + ) -> Result<(), TokenTransferError>; /// This function to enable minting ibc tokens to a user account fn mint_coins( &mut self, account: &Self::AccountId, amt: &PrefixedCoin, - ) -> Result<(), Ics20Error>; + ) -> Result<(), TokenTransferError>; /// This function should enable burning of minted tokens in a user account fn burn_coins( &mut self, account: &Self::AccountId, amt: &PrefixedCoin, - ) -> Result<(), Ics20Error>; + ) -> Result<(), TokenTransferError>; } /// Captures all the dependencies which the ICS20 module requires to be able to dispatch and @@ -163,17 +163,26 @@ pub fn on_chan_open_init( _channel_id: &ChannelId, _counterparty: &Counterparty, version: &Version, -) -> Result<(ModuleExtras, Version), Ics20Error> { +) -> Result<(ModuleExtras, Version), TokenTransferError> { if order != Order::Unordered { - return Err(Ics20Error::channel_not_unordered(order)); + return Err(TokenTransferError::ChannelNotUnordered { + expect_order: Order::Unordered, + got_order: order, + }); } let bound_port = ctx.get_port()?; if port_id != &bound_port { - return Err(Ics20Error::invalid_port(port_id.clone(), bound_port)); + return Err(TokenTransferError::InvalidPort { + port_id: port_id.clone(), + exp_port_id: bound_port, + }); } if !version.is_empty() && version != &Version::ics20() { - return Err(Ics20Error::invalid_version(version.clone())); + return Err(TokenTransferError::InvalidVersion { + expect_version: Version::ics20(), + got_version: version.clone(), + }); } Ok((ModuleExtras::empty(), Version::ics20())) @@ -188,14 +197,18 @@ pub fn on_chan_open_try( _channel_id: &ChannelId, _counterparty: &Counterparty, counterparty_version: &Version, -) -> Result<(ModuleExtras, Version), Ics20Error> { +) -> Result<(ModuleExtras, Version), TokenTransferError> { if order != Order::Unordered { - return Err(Ics20Error::channel_not_unordered(order)); + return Err(TokenTransferError::ChannelNotUnordered { + expect_order: Order::Unordered, + got_order: order, + }); } if counterparty_version != &Version::ics20() { - return Err(Ics20Error::invalid_counterparty_version( - counterparty_version.clone(), - )); + return Err(TokenTransferError::InvalidCounterpartyVersion { + expect_version: Version::ics20(), + got_version: counterparty_version.clone(), + }); } Ok((ModuleExtras::empty(), Version::ics20())) @@ -206,11 +219,12 @@ pub fn on_chan_open_ack( _port_id: &PortId, _channel_id: &ChannelId, counterparty_version: &Version, -) -> Result { +) -> Result { if counterparty_version != &Version::ics20() { - return Err(Ics20Error::invalid_counterparty_version( - counterparty_version.clone(), - )); + return Err(TokenTransferError::InvalidCounterpartyVersion { + expect_version: Version::ics20(), + got_version: counterparty_version.clone(), + }); } Ok(ModuleExtras::empty()) @@ -220,7 +234,7 @@ pub fn on_chan_open_confirm( _ctx: &mut impl TokenTransferContext, _port_id: &PortId, _channel_id: &ChannelId, -) -> Result { +) -> Result { Ok(ModuleExtras::empty()) } @@ -228,15 +242,15 @@ pub fn on_chan_close_init( _ctx: &mut impl TokenTransferContext, _port_id: &PortId, _channel_id: &ChannelId, -) -> Result { - Err(Ics20Error::cant_close_channel()) +) -> Result { + Err(TokenTransferError::CantCloseChannel) } pub fn on_chan_close_confirm( _ctx: &mut impl TokenTransferContext, _port_id: &PortId, _channel_id: &ChannelId, -) -> Result { +) -> Result { Ok(ModuleExtras::empty()) } @@ -250,7 +264,7 @@ pub fn on_recv_packet( Ok(data) => data, Err(_) => { return OnRecvPacketAck::Failed(Box::new(Acknowledgement::Error( - Ics20Error::packet_data_deserialization().to_string(), + TokenTransferError::PacketDataDeserialization.to_string(), ))); } }; @@ -277,12 +291,12 @@ pub fn on_acknowledgement_packet( packet: &Packet, acknowledgement: &GenericAcknowledgement, _relayer: &Signer, -) -> Result<(), Ics20Error> { +) -> Result<(), TokenTransferError> { let data = serde_json::from_slice::(&packet.data) - .map_err(|_| Ics20Error::packet_data_deserialization())?; + .map_err(|_| TokenTransferError::PacketDataDeserialization)?; let acknowledgement = serde_json::from_slice::(acknowledgement.as_ref()) - .map_err(|_| Ics20Error::ack_deserialization())?; + .map_err(|_| TokenTransferError::AckDeserialization)?; process_ack_packet(ctx, packet, &data, &acknowledgement)?; @@ -303,9 +317,9 @@ pub fn on_timeout_packet( output: &mut ModuleOutputBuilder, packet: &Packet, _relayer: &Signer, -) -> Result<(), Ics20Error> { +) -> Result<(), TokenTransferError> { let data = serde_json::from_slice::(&packet.data) - .map_err(|_| Ics20Error::packet_data_deserialization())?; + .map_err(|_| TokenTransferError::PacketDataDeserialization)?; process_timeout_packet(ctx, packet, &data)?; @@ -324,12 +338,12 @@ pub(crate) mod test { use subtle_encoding::bech32; use crate::applications::transfer::context::{cosmos_adr028_escrow_address, on_chan_open_try}; - use crate::applications::transfer::error::Error as Ics20Error; + use crate::applications::transfer::error::TokenTransferError; use crate::applications::transfer::msgs::transfer::MsgTransfer; use crate::applications::transfer::relay::send_transfer::send_transfer; use crate::applications::transfer::PrefixedCoin; use crate::core::ics04_channel::channel::{Counterparty, Order}; - use crate::core::ics04_channel::error::Error; + use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::Version; use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; use crate::handler::HandlerOutputBuilder; @@ -342,8 +356,10 @@ pub(crate) mod test { ctx: &mut DummyTransferModule, output: &mut HandlerOutputBuilder<()>, msg: MsgTransfer, - ) -> Result<(), Error> { - send_transfer(ctx, output, msg).map_err(|e: Ics20Error| Error::app_module(e.to_string())) + ) -> Result<(), ChannelError> { + send_transfer(ctx, output, msg).map_err(|e: TokenTransferError| ChannelError::AppModule { + description: e.to_string(), + }) } fn get_defaults() -> ( diff --git a/crates/ibc/src/applications/transfer/denom.rs b/crates/ibc/src/applications/transfer/denom.rs index 4192ba821..c90bf38c5 100644 --- a/crates/ibc/src/applications/transfer/denom.rs +++ b/crates/ibc/src/applications/transfer/denom.rs @@ -5,7 +5,7 @@ use derive_more::{Display, From}; use ibc_proto::ibc::applications::transfer::v1::DenomTrace as RawDenomTrace; use serde::{Deserialize, Serialize}; -use super::error::Error; +use super::error::TokenTransferError; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::prelude::*; use crate::serializers::serde_string; @@ -22,11 +22,11 @@ impl BaseDenom { } impl FromStr for BaseDenom { - type Err = Error; + type Err = TokenTransferError; fn from_str(s: &str) -> Result { if s.trim().is_empty() { - Err(Error::empty_base_denom()) + Err(TokenTransferError::EmptyBaseDenom) } else { Ok(BaseDenom(s.to_owned())) } @@ -86,20 +86,27 @@ impl TracePath { } impl<'a> TryFrom> for TracePath { - type Error = Error; + type Error = TokenTransferError; fn try_from(v: Vec<&'a str>) -> Result { if v.len() % 2 != 0 { - return Err(Error::invalid_trace_length(v.len())); + return Err(TokenTransferError::InvalidTraceLength { len: v.len() }); } let mut trace = vec![]; let id_pairs = v.chunks_exact(2).map(|paths| (paths[0], paths[1])); for (pos, (port_id, channel_id)) in id_pairs.rev().enumerate() { let port_id = - PortId::from_str(port_id).map_err(|e| Error::invalid_trace_port_id(pos, e))?; - let channel_id = ChannelId::from_str(channel_id) - .map_err(|e| Error::invalid_trace_channel_id(pos, e))?; + PortId::from_str(port_id).map_err(|e| TokenTransferError::InvalidTracePortId { + pos, + validation_error: e, + })?; + let channel_id = ChannelId::from_str(channel_id).map_err(|e| { + TokenTransferError::InvalidTraceChannelId { + pos, + validation_error: e, + } + })?; trace.push(TracePrefix { port_id, channel_id, @@ -111,7 +118,7 @@ impl<'a> TryFrom> for TracePath { } impl FromStr for TracePath { - type Err = Error; + type Err = TokenTransferError; fn from_str(s: &str) -> Result { let parts = { @@ -211,7 +218,7 @@ pub fn is_receiver_chain_source( } impl FromStr for PrefixedDenom { - type Err = Error; + type Err = TokenTransferError; fn from_str(s: &str) -> Result { let mut parts: Vec<&str> = s.split('/').collect(); @@ -235,7 +242,7 @@ impl FromStr for PrefixedDenom { } impl TryFrom for PrefixedDenom { - type Error = Error; + type Error = TokenTransferError; fn try_from(value: RawDenomTrace) -> Result { let base_denom = BaseDenom::from_str(&value.base_denom)?; @@ -280,7 +287,7 @@ mod tests { use super::*; #[test] - fn test_denom_validation() -> Result<(), Error> { + fn test_denom_validation() -> Result<(), TokenTransferError> { assert!(BaseDenom::from_str("").is_err(), "empty base denom"); assert!(BaseDenom::from_str("uatom").is_ok(), "valid base denom"); assert!(PrefixedDenom::from_str("").is_err(), "empty denom trace"); @@ -319,7 +326,7 @@ mod tests { } #[test] - fn test_denom_trace() -> Result<(), Error> { + fn test_denom_trace() -> Result<(), TokenTransferError> { assert_eq!( PrefixedDenom::from_str("transfer/channel-0/uatom")?, PrefixedDenom { @@ -341,7 +348,7 @@ mod tests { } #[test] - fn test_denom_serde() -> Result<(), Error> { + fn test_denom_serde() -> Result<(), TokenTransferError> { let dt_str = "transfer/channel-0/uatom"; let dt = PrefixedDenom::from_str(dt_str)?; assert_eq!(dt.to_string(), dt_str, "valid single trace info"); @@ -354,7 +361,7 @@ mod tests { } #[test] - fn test_trace_path() -> Result<(), Error> { + fn test_trace_path() -> Result<(), TokenTransferError> { assert!(TracePath::from_str("").is_ok(), "empty trace path"); assert!( TracePath::from_str("transfer/uatom").is_err(), diff --git a/crates/ibc/src/applications/transfer/error.rs b/crates/ibc/src/applications/transfer/error.rs index e13cb14fe..d2f89830d 100644 --- a/crates/ibc/src/applications/transfer/error.rs +++ b/crates/ibc/src/applications/transfer/error.rs @@ -1,10 +1,7 @@ -use alloc::string::FromUtf8Error; - use core::convert::Infallible; use core::str::Utf8Error; -use flex_error::{define_error, DisplayOnly, TraceError}; +use displaydoc::Display; use ibc_proto::protobuf::Error as TendermintProtoError; -use subtle_encoding::Error as EncodingError; use uint::FromDecStrErr; use crate::core::ics04_channel::channel::Order; @@ -15,137 +12,122 @@ use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::prelude::*; use crate::signer::SignerError; -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - UnknowMessageTypeUrl - { url: String } - | e | { format_args!("unrecognized ICS-20 transfer message type URL {0}", e.url) }, - - Ics04Channel - [ channel_error::Error ] - |_ | { "Ics04 channel error" }, - - DestinationChannelNotFound - { port_id: PortId, channel_id: ChannelId } - | e | { format_args!("destination channel not found in the counterparty of port_id {0} and channel_id {1} ", e.port_id, e.channel_id) }, - - InvalidPortId - { context: String } - [ ValidationError ] - | _ | { "invalid port identifier" }, - - InvalidChannelId - { context: String } - [ ValidationError ] - | _ | { "invalid channel identifier" }, - - InvalidPacketTimeoutHeight - { context: String } - | _ | { "invalid packet timeout height value" }, - - InvalidPacketTimeoutTimestamp - { timestamp: u64 } - | _ | { "invalid packet timeout timestamp value" }, - - Utf8 - [ DisplayOnly ] - | _ | { "utf8 decoding error" }, - - EmptyBaseDenom - |_| { "base denomination is empty" }, - - InvalidTracePortId - { pos: usize } - [ ValidationError ] - | e | { format_args!("invalid port id in trace at position: {0}", e.pos) }, - - InvalidTraceChannelId - { pos: usize } - [ ValidationError ] - | e | { format_args!("invalid channel id in trace at position: {0}", e.pos) }, - - InvalidTraceLength - { len: usize } - | e | { format_args!("trace length must be even but got: {0}", e.len) }, - - InvalidAmount - [ TraceError ] - | _ | { "invalid amount" }, - - InvalidToken - | _ | { "invalid token" }, - - Signer - [ SignerError ] - | _ | { "failed to parse signer" }, - - MissingDenomIbcPrefix - | _ | { "missing 'ibc/' prefix in denomination" }, - - MalformedHashDenom - | _ | { "hashed denom must be of the form 'ibc/{Hash}'" }, - - ParseHex - [ TraceError ] - | _ | { "invalid hex string" }, - - ChannelNotUnordered - { order: Order } - | e | { format_args!("expected '{0}' channel, got '{1}'", Order::Unordered, e.order) }, - - InvalidVersion - { version: Version } - | e | { format_args!("expected version '{0}', got '{1}'", Version::ics20(), e.version) }, - - InvalidCounterpartyVersion - { version: Version } - | e | { format_args!("expected counterparty version '{0}', got '{1}'", Version::ics20(), e.version) }, - - CantCloseChannel - | _ | { "channel cannot be closed" }, - - PacketDataDeserialization - | _ | { "failed to deserialize packet data" }, - - AckDeserialization - | _ | { "failed to deserialize acknowledgement" }, - - ReceiveDisabled - | _ | { "receive is not enabled" }, - - SendDisabled - | _ | { "send is not enabled" }, - - ParseAccountFailure - | _ | { "failed to parse as AccountId" }, - - InvalidPort - { port_id: PortId, exp_port_id: PortId } - | e | { format_args!("invalid port: '{0}', expected '{1}'", e.port_id, e.exp_port_id) }, - - TraceNotFound - | _ | { "no trace associated with specified hash" }, - - DecodeRawMsg - [ TraceError ] - | _ | { "error decoding raw msg" }, - - UnknownMsgType - { msg_type: String } - | e | { format_args!("unknown msg type: {0}", e.msg_type) }, - - InvalidCoin - { coin: String } - | e | { format_args!("invalid coin string: {}", e.coin) }, +#[derive(Display, Debug)] +pub enum TokenTransferError { + /// packet error: `{0}` + PacketError(channel_error::PacketError), + /// destination channel not found in the counterparty of port_id `{port_id}` and channel_id `{channel_id}` + DestinationChannelNotFound { + port_id: PortId, + channel_id: ChannelId, + }, + /// invalid port identifier `{context}`, validation error: `{validation_error}` + InvalidPortId { + context: String, + validation_error: ValidationError, + }, + /// invalid channel identifier `{context}`, validation error: `{validation_error}` + InvalidChannelId { + context: String, + validation_error: ValidationError, + }, + /// invalid packet timeout height value `{context}` + InvalidPacketTimeoutHeight { context: String }, + /// invalid packet timeout timestamp value `{timestamp}` + InvalidPacketTimeoutTimestamp { timestamp: u64 }, + /// base denomination is empty + EmptyBaseDenom, + /// invalid prot id n trace at postion: `{pos}`, validation error: `{validation_error}` + InvalidTracePortId { + pos: usize, + validation_error: ValidationError, + }, + /// invalid channel id in trace at position: `{pos}`, validation error: `{validation_error}` + InvalidTraceChannelId { + pos: usize, + validation_error: ValidationError, + }, + /// trace length must be even but got: `{len}` + InvalidTraceLength { len: usize }, + /// invalid amount error: `{0}` + InvalidAmount(FromDecStrErr), + /// invalid token + InvalidToken, + /// failed to parse signer error: `{0}` + Signer(SignerError), + /// expected `{expect_order}` channel, got `{got_order}` + ChannelNotUnordered { + expect_order: Order, + got_order: Order, + }, + /// expected version `{expect_version}` , got `{got_version}` + InvalidVersion { + expect_version: Version, + got_version: Version, + }, + /// expected counterparty version `{expect_version}`, got `{got_version}` + InvalidCounterpartyVersion { + expect_version: Version, + got_version: Version, + }, + /// channel cannot be closed + CantCloseChannel, + /// failed to deserialize packet data + PacketDataDeserialization, + /// failed to deserialize acknowledgement + AckDeserialization, + /// receive is not enabled + ReceiveDisabled, + /// send is not enabled + SendDisabled, + /// failed to parse as AccountId + ParseAccountFailure, + /// invalid port: `{port_id}`, expected `{exp_port_id}` + InvalidPort { + port_id: PortId, + exp_port_id: PortId, + }, + /// decoding raw msg error: `{0}` + DecodeRawMsg(TendermintProtoError), + /// unknown msg type: `{msg_type}` + UnknownMsgType { msg_type: String }, + /// invalid coin string: `{coin}` + InvalidCoin { coin: String }, + /// decoding raw bytes as UTF8 string error: `{0}` + Utf8Decode(Utf8Error), +} - Utf8Decode - [ TraceError ] - | _ | { "error decoding raw bytes as UTF8 string" }, +#[cfg(feature = "std")] +impl std::error::Error for TokenTransferError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::PacketError(e) => Some(e), + Self::InvalidPortId { + validation_error: e, + .. + } => Some(e), + Self::InvalidChannelId { + validation_error: e, + .. + } => Some(e), + Self::InvalidTracePortId { + validation_error: e, + .. + } => Some(e), + Self::InvalidTraceChannelId { + validation_error: e, + .. + } => Some(e), + Self::InvalidAmount(e) => Some(e), + Self::Signer(e) => Some(e), + Self::DecodeRawMsg(e) => Some(e), + Self::Utf8Decode(e) => Some(e), + _ => None, + } } } -impl From for Error { +impl From for TokenTransferError { fn from(e: Infallible) -> Self { match e {} } diff --git a/crates/ibc/src/applications/transfer/msgs/transfer.rs b/crates/ibc/src/applications/transfer/msgs/transfer.rs index 1964d54df..f0ffc7b4e 100644 --- a/crates/ibc/src/applications/transfer/msgs/transfer.rs +++ b/crates/ibc/src/applications/transfer/msgs/transfer.rs @@ -7,7 +7,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::applications::transfer::v1::MsgTransfer as RawMsgTransfer; use ibc_proto::protobuf::Protobuf; -use crate::applications::transfer::error::Error; +use crate::applications::transfer::error::TokenTransferError; use crate::core::ics04_channel::timeout::TimeoutHeight; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::signer::Signer; @@ -44,7 +44,7 @@ pub struct MsgTransfer { } impl Msg for MsgTransfer { - type ValidationError = Error; + type ValidationError = TokenTransferError; type Raw = RawMsgTransfer; fn route(&self) -> String { @@ -57,28 +57,41 @@ impl Msg for MsgTransfer { } impl TryFrom for MsgTransfer { - type Error = Error; + type Error = TokenTransferError; fn try_from(raw_msg: RawMsgTransfer) -> Result { - let timeout_timestamp = Timestamp::from_nanoseconds(raw_msg.timeout_timestamp) - .map_err(|_| Error::invalid_packet_timeout_timestamp(raw_msg.timeout_timestamp))?; + let timeout_timestamp = + Timestamp::from_nanoseconds(raw_msg.timeout_timestamp).map_err(|_| { + TokenTransferError::InvalidPacketTimeoutTimestamp { + timestamp: raw_msg.timeout_timestamp, + } + })?; let timeout_height: TimeoutHeight = raw_msg.timeout_height.try_into().map_err(|e| { - Error::invalid_packet_timeout_height(format!("invalid timeout height {}", e)) + TokenTransferError::InvalidPacketTimeoutHeight { + context: format!("invalid timeout height {}", e), + } })?; Ok(MsgTransfer { - source_port: raw_msg - .source_port - .parse() - .map_err(|e| Error::invalid_port_id(raw_msg.source_port.clone(), e))?, - source_channel: raw_msg - .source_channel + source_port: raw_msg.source_port.parse().map_err(|e| { + TokenTransferError::InvalidPortId { + context: raw_msg.source_port.clone(), + validation_error: e, + } + })?, + source_channel: raw_msg.source_channel.parse().map_err(|e| { + TokenTransferError::InvalidChannelId { + context: raw_msg.source_channel.clone(), + validation_error: e, + } + })?, + token: raw_msg.token.ok_or(TokenTransferError::InvalidToken)?, + sender: raw_msg.sender.parse().map_err(TokenTransferError::Signer)?, + receiver: raw_msg + .receiver .parse() - .map_err(|e| Error::invalid_channel_id(raw_msg.source_channel.clone(), e))?, - token: raw_msg.token.ok_or_else(Error::invalid_token)?, - sender: raw_msg.sender.parse().map_err(Error::signer)?, - receiver: raw_msg.receiver.parse().map_err(Error::signer)?, + .map_err(TokenTransferError::Signer)?, timeout_height, timeout_timestamp, }) @@ -102,12 +115,16 @@ impl From for RawMsgTransfer { impl Protobuf for MsgTransfer {} impl TryFrom for MsgTransfer { - type Error = Error; + type Error = TokenTransferError; fn try_from(raw: Any) -> Result { match raw.type_url.as_str() { - TYPE_URL => MsgTransfer::decode_vec(&raw.value).map_err(Error::decode_raw_msg), - _ => Err(Error::unknown_msg_type(raw.type_url)), + TYPE_URL => { + MsgTransfer::decode_vec(&raw.value).map_err(TokenTransferError::DecodeRawMsg) + } + _ => Err(TokenTransferError::UnknownMsgType { + msg_type: raw.type_url, + }), } } } diff --git a/crates/ibc/src/applications/transfer/packet.rs b/crates/ibc/src/applications/transfer/packet.rs index 587f7608b..525a098c0 100644 --- a/crates/ibc/src/applications/transfer/packet.rs +++ b/crates/ibc/src/applications/transfer/packet.rs @@ -5,7 +5,7 @@ use core::str::FromStr; use ibc_proto::ibc::applications::transfer::v2::FungibleTokenPacketData as RawPacketData; use serde::{Deserialize, Serialize}; -use super::error::Error; +use super::error::TokenTransferError; use super::{Amount, PrefixedCoin, PrefixedDenom}; use crate::signer::Signer; @@ -18,7 +18,7 @@ pub struct PacketData { } impl TryFrom for PacketData { - type Error = Error; + type Error = TokenTransferError; fn try_from(raw_pkt_data: RawPacketData) -> Result { // This denom may be prefixed or unprefixed. @@ -26,8 +26,14 @@ impl TryFrom for PacketData { let amount = Amount::from_str(&raw_pkt_data.amount)?; Ok(Self { token: PrefixedCoin { denom, amount }, - sender: raw_pkt_data.sender.parse().map_err(Error::signer)?, - receiver: raw_pkt_data.receiver.parse().map_err(Error::signer)?, + sender: raw_pkt_data + .sender + .parse() + .map_err(TokenTransferError::Signer)?, + receiver: raw_pkt_data + .receiver + .parse() + .map_err(TokenTransferError::Signer)?, }) } } diff --git a/crates/ibc/src/applications/transfer/relay.rs b/crates/ibc/src/applications/transfer/relay.rs index f5a9196d5..8660b24f2 100644 --- a/crates/ibc/src/applications/transfer/relay.rs +++ b/crates/ibc/src/applications/transfer/relay.rs @@ -1,6 +1,6 @@ //! This module implements the processing logic for ICS20 (token transfer) message. use crate::applications::transfer::context::TokenTransferContext; -use crate::applications::transfer::error::Error as Ics20Error; +use crate::applications::transfer::error::TokenTransferError; use crate::applications::transfer::is_sender_chain_source; use crate::applications::transfer::packet::PacketData; use crate::core::ics04_channel::packet::Packet; @@ -15,12 +15,12 @@ fn refund_packet_token( ctx: &mut impl TokenTransferContext, packet: &Packet, data: &PacketData, -) -> Result<(), Ics20Error> { +) -> Result<(), TokenTransferError> { let sender = data .sender .clone() .try_into() - .map_err(|_| Ics20Error::parse_account_failure())?; + .map_err(|_| TokenTransferError::ParseAccountFailure)?; if is_sender_chain_source( packet.source_port.clone(), diff --git a/crates/ibc/src/applications/transfer/relay/on_ack_packet.rs b/crates/ibc/src/applications/transfer/relay/on_ack_packet.rs index 72e147a97..14c48a4e4 100644 --- a/crates/ibc/src/applications/transfer/relay/on_ack_packet.rs +++ b/crates/ibc/src/applications/transfer/relay/on_ack_packet.rs @@ -1,6 +1,6 @@ use crate::applications::transfer::acknowledgement::Acknowledgement; use crate::applications::transfer::context::TokenTransferContext; -use crate::applications::transfer::error::Error as Ics20Error; +use crate::applications::transfer::error::TokenTransferError; use crate::applications::transfer::packet::PacketData; use crate::applications::transfer::relay::refund_packet_token; use crate::core::ics04_channel::packet::Packet; @@ -10,7 +10,7 @@ pub fn process_ack_packet( packet: &Packet, data: &PacketData, ack: &Acknowledgement, -) -> Result<(), Ics20Error> { +) -> Result<(), TokenTransferError> { if matches!(ack, Acknowledgement::Error(_)) { refund_packet_token(ctx, packet, data)?; } diff --git a/crates/ibc/src/applications/transfer/relay/on_recv_packet.rs b/crates/ibc/src/applications/transfer/relay/on_recv_packet.rs index cbdb7b980..2eb2928ca 100644 --- a/crates/ibc/src/applications/transfer/relay/on_recv_packet.rs +++ b/crates/ibc/src/applications/transfer/relay/on_recv_packet.rs @@ -1,5 +1,5 @@ use crate::applications::transfer::context::TokenTransferContext; -use crate::applications::transfer::error::Error as Ics20Error; +use crate::applications::transfer::error::TokenTransferError; use crate::applications::transfer::events::DenomTraceEvent; use crate::applications::transfer::packet::PacketData; use crate::applications::transfer::{is_receiver_chain_source, TracePrefix}; @@ -12,16 +12,16 @@ pub fn process_recv_packet( output: &mut ModuleOutputBuilder, packet: &Packet, data: PacketData, -) -> Result, Ics20Error> { +) -> Result, TokenTransferError> { if !ctx.is_receive_enabled() { - return Err(Ics20Error::receive_disabled()); + return Err(TokenTransferError::ReceiveDisabled); } let receiver_account = data .receiver .clone() .try_into() - .map_err(|_| Ics20Error::parse_account_failure())?; + .map_err(|_| TokenTransferError::ParseAccountFailure)?; if is_receiver_chain_source( packet.source_port.clone(), diff --git a/crates/ibc/src/applications/transfer/relay/on_timeout_packet.rs b/crates/ibc/src/applications/transfer/relay/on_timeout_packet.rs index e35e22c6d..0f54016a4 100644 --- a/crates/ibc/src/applications/transfer/relay/on_timeout_packet.rs +++ b/crates/ibc/src/applications/transfer/relay/on_timeout_packet.rs @@ -1,5 +1,5 @@ use crate::applications::transfer::context::TokenTransferContext; -use crate::applications::transfer::error::Error as Ics20Error; +use crate::applications::transfer::error::TokenTransferError; use crate::applications::transfer::packet::PacketData; use crate::applications::transfer::relay::refund_packet_token; use crate::core::ics04_channel::packet::Packet; @@ -8,6 +8,6 @@ pub fn process_timeout_packet( ctx: &mut impl TokenTransferContext, packet: &Packet, data: &PacketData, -) -> Result<(), Ics20Error> { +) -> Result<(), TokenTransferError> { refund_packet_token(ctx, packet, data) } diff --git a/crates/ibc/src/applications/transfer/relay/send_transfer.rs b/crates/ibc/src/applications/transfer/relay/send_transfer.rs index 5d17e03d3..1f9aeffb7 100644 --- a/crates/ibc/src/applications/transfer/relay/send_transfer.rs +++ b/crates/ibc/src/applications/transfer/relay/send_transfer.rs @@ -1,5 +1,5 @@ use crate::applications::transfer::context::TokenTransferContext; -use crate::applications::transfer::error::Error; +use crate::applications::transfer::error::TokenTransferError; use crate::applications::transfer::events::TransferEvent; use crate::applications::transfer::msgs::transfer::MsgTransfer; use crate::applications::transfer::packet::PacketData; @@ -17,37 +17,38 @@ pub fn send_transfer( ctx: &mut Ctx, output: &mut HandlerOutputBuilder<()>, msg: MsgTransfer, -) -> Result<(), Error> +) -> Result<(), TokenTransferError> where Ctx: TokenTransferContext, C: TryInto, { if !ctx.is_send_enabled() { - return Err(Error::send_disabled()); + return Err(TokenTransferError::SendDisabled); } let source_channel_end = ctx .channel_end(&msg.source_port, &msg.source_channel) - .map_err(Error::ics04_channel)?; + .map_err(TokenTransferError::PacketError)?; let destination_port = source_channel_end.counterparty().port_id().clone(); let destination_channel = source_channel_end .counterparty() .channel_id() - .ok_or_else(|| { - Error::destination_channel_not_found( - msg.source_port.clone(), - msg.source_channel.clone(), - ) + .ok_or_else(|| TokenTransferError::DestinationChannelNotFound { + port_id: msg.source_port.clone(), + channel_id: msg.source_channel.clone(), })? .clone(); // get the next sequence let sequence = ctx .get_next_sequence_send(&msg.source_port, &msg.source_channel) - .map_err(Error::ics04_channel)?; + .map_err(TokenTransferError::PacketError)?; - let token = msg.token.try_into().map_err(|_| Error::invalid_token())?; + let token = msg + .token + .try_into() + .map_err(|_| TokenTransferError::InvalidToken)?; let denom = token.denom.clone(); let coin = Coin { denom: denom.clone(), @@ -58,7 +59,7 @@ where .sender .clone() .try_into() - .map_err(|_| Error::parse_account_failure())?; + .map_err(|_| TokenTransferError::ParseAccountFailure)?; if is_sender_chain_source(msg.source_port.clone(), msg.source_channel.clone(), &denom) { let escrow_address = @@ -92,10 +93,10 @@ where result, log, events, - } = send_packet(ctx, packet).map_err(Error::ics04_channel)?; + } = send_packet(ctx, packet).map_err(TokenTransferError::PacketError)?; ctx.store_send_packet_result(result) - .map_err(Error::ics04_channel)?; + .map_err(TokenTransferError::PacketError)?; output.merge_output( HandlerOutput::builder() diff --git a/crates/ibc/src/clients/ics07_tendermint/client_state.rs b/crates/ibc/src/clients/ics07_tendermint/client_state.rs index 5280f85b5..6ede0b581 100644 --- a/crates/ibc/src/clients/ics07_tendermint/client_state.rs +++ b/crates/ibc/src/clients/ics07_tendermint/client_state.rs @@ -1,3 +1,4 @@ +use crate::core::context::ContextError; use crate::core::ics02_client::context::ClientReader; use crate::core::ics03_connection::connection::ConnectionEnd; use crate::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCommitment}; @@ -38,7 +39,7 @@ use crate::core::ics02_client::client_state::{ }; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::{Error as Ics02Error, ErrorDetail as Ics02ErrorDetail}; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::trust_threshold::TrustThreshold; use crate::core::ics04_channel::context::ChannelReader; use crate::core::ics23_commitment::specs::ProofSpecs; @@ -87,75 +88,84 @@ impl ClientState { frozen_height: Option, ) -> Result { if chain_id.as_str().len() > MaxChainIdLen { - return Err(Error::chain_id_too_long( - chain_id.to_string(), - chain_id.as_str().len(), - MaxChainIdLen, - )); + return Err(Error::ChainIdTooLong { + chain_id: chain_id.clone(), + len: chain_id.as_str().len(), + max_len: MaxChainIdLen, + }); } // `TrustThreshold` is guaranteed to be in the range `[0, 1)`, but a `TrustThreshold::ZERO` // value is invalid in this context if trust_level == TrustThreshold::ZERO { - return Err(Error::invalid_trust_threshold( - "ClientState trust-level cannot be zero".to_string(), - )); + return Err(Error::InvalidTrustThreshold { + reason: "ClientState trust-level cannot be zero".to_string(), + }); } let _ = TendermintTrustThresholdFraction::new( trust_level.numerator(), trust_level.denominator(), ) - .map_err(Error::invalid_tendermint_trust_threshold)?; + .map_err(Error::InvalidTendermintTrustThreshold)?; // Basic validation of trusting period and unbonding period: each should be non-zero. if trusting_period <= Duration::new(0, 0) { - return Err(Error::invalid_trusting_period(format!( - "ClientState trusting period ({:?}) must be greater than zero", - trusting_period - ))); + return Err(Error::InvalidTrustThreshold { + reason: format!( + "ClientState trusting period ({:?}) must be greater than zero", + trusting_period + ), + }); } if unbonding_period <= Duration::new(0, 0) { - return Err(Error::invalid_unbonding_period(format!( - "ClientState unbonding period ({:?}) must be greater than zero", - unbonding_period - ))); + return Err(Error::InvalidTrustThreshold { + reason: format!( + "ClientState unbonding period ({:?}) must be greater than zero", + unbonding_period + ), + }); } if trusting_period >= unbonding_period { - return Err(Error::invalid_trusting_period(format!( + return Err(Error::InvalidTrustThreshold { + reason: format!( "ClientState trusting period ({:?}) must be smaller than unbonding period ({:?})", trusting_period, unbonding_period, - ))); + ), + }); } if max_clock_drift <= Duration::new(0, 0) { - return Err(Error::invalid_max_clock_drift( - "ClientState max-clock-drift must be greater than zero".to_string(), - )); + return Err(Error::InvalidMaxClockDrift { + reason: "ClientState max-clock-drift must be greater than zero".to_string(), + }); } if latest_height.revision_number() != chain_id.version() { - return Err(Error::invalid_latest_height( - "ClientState latest-height revision number must match chain-id version".to_string(), - )); + return Err(Error::InvalidLatestHeight { + reason: "ClientState latest-height revision number must match chain-id version" + .to_string(), + }); } // Disallow empty proof-specs if proof_specs.is_empty() { - return Err(Error::validation( - "ClientState proof-specs cannot be empty".to_string(), - )); + return Err(Error::Validation { + reason: "ClientState proof-specs cannot be empty".to_string(), + }); } // `upgrade_path` itself may be empty, but if not then each key must be non-empty for (idx, key) in upgrade_path.iter().enumerate() { if key.trim().is_empty() { - return Err(Error::validation(format!( - "ClientState upgrade-path key at index {:?} cannot be empty", - idx - ))); + return Err(Error::Validation { + reason: format!( + "ClientState upgrade-path key at index {:?} cannot be empty", + idx + ), + }); } } @@ -184,7 +194,9 @@ impl ClientState { self.latest_height.revision_number(), h.signed_header.header.height.into(), ) - .map_err(|_| Error::invalid_header_height(h.signed_header.header.height.value()))?, + .map_err(|_| Error::InvalidHeaderHeight { + height: h.signed_header.header.height.value(), + })?, ..self }) } @@ -205,10 +217,11 @@ impl ClientState { /// Tendermint-specific light client verification. pub fn as_light_client_options(&self) -> Result { Ok(Options { - trust_threshold: self - .trust_level - .try_into() - .map_err(|e: Ics02Error| Error::invalid_trust_threshold(e.to_string()))?, + trust_threshold: self.trust_level.try_into().map_err(|e: ClientError| { + Error::InvalidTrustThreshold { + reason: e.to_string(), + } + })?, trusting_period: self.trusting_period, clock_drift: self.max_clock_drift, }) @@ -224,17 +237,20 @@ impl ClientState { delay_period_blocks: u64, ) -> Result<(), Error> { let earliest_time = - (processed_time + delay_period_time).map_err(Error::timestamp_overflow)?; + (processed_time + delay_period_time).map_err(Error::TimestampOverflow)?; if !(current_time == earliest_time || current_time.after(&earliest_time)) { - return Err(Error::not_enough_time_elapsed(current_time, earliest_time)); + return Err(Error::NotEnoughTimeElapsed { + current_time, + earliest_time, + }); } let earliest_height = processed_height.add(delay_period_blocks); if current_height < earliest_height { - return Err(Error::not_enough_blocks_elapsed( + return Err(Error::NotEnoughBlocksElapsed { current_height, earliest_height, - )); + }); } Ok(()) @@ -243,13 +259,17 @@ impl ClientState { /// Verify that the client is at a sufficient height and unfrozen at the given height pub fn verify_height(&self, height: Height) -> Result<(), Error> { if self.latest_height < height { - return Err(Error::insufficient_height(self.latest_height(), height)); + return Err(Error::InsufficientHeight { + latest_height: self.latest_height(), + target_height: height, + }); } match self.frozen_height { - Some(frozen_height) if frozen_height <= height => { - Err(Error::client_frozen(frozen_height, height)) - } + Some(frozen_height) if frozen_height <= height => Err(Error::ClientFrozen { + frozen_height, + target_height: height, + }), _ => Ok(()), } } @@ -308,7 +328,7 @@ impl Ics2ClientState for ClientState { elapsed > self.trusting_period } - fn initialise(&self, consensus_state: Any) -> Result, Ics02Error> { + fn initialise(&self, consensus_state: Any) -> Result, ClientError> { TmConsensusState::try_from(consensus_state).map(TmConsensusState::into_box) } @@ -317,16 +337,19 @@ impl Ics2ClientState for ClientState { ctx: &dyn ClientReader, client_id: ClientId, header: Any, - ) -> Result { + ) -> Result { fn maybe_consensus_state( ctx: &dyn ClientReader, client_id: &ClientId, height: Height, - ) -> Result>, Ics02Error> { + ) -> Result>, ClientError> { match ctx.consensus_state(client_id, height) { Ok(cs) => Ok(Some(cs)), - Err(e) => match e.detail() { - Ics02ErrorDetail::ConsensusStateNotFound(_) => Ok(None), + Err(e) => match e { + ClientError::ConsensusStateNotFound { + client_id: _, + height: _, + } => Ok(None), _ => Err(e), }, } @@ -336,13 +359,13 @@ impl Ics2ClientState for ClientState { let header = TmHeader::try_from(header)?; if header.height().revision_number() != client_state.chain_id().version() { - return Err(Ics02Error::client_specific( - Error::mismatched_revisions( - client_state.chain_id().version(), - header.height().revision_number(), - ) + return Err(ClientError::ClientSpecific { + description: Error::MismatchedRevisions { + current_revision: client_state.chain_id().version(), + update_revision: header.height().revision_number(), + } .to_string(), - )); + }); } // Check if a consensus state is already installed; if so it should @@ -378,11 +401,11 @@ impl Ics2ClientState for ClientState { .trusted_height .revision_height() .try_into() - .map_err(|_| { - Ics02Error::client_specific( - Error::invalid_header_height(header.trusted_height.revision_height()) - .to_string(), - ) + .map_err(|_| ClientError::ClientSpecific { + description: Error::InvalidHeaderHeight { + height: header.trusted_height.revision_height(), + } + .to_string(), })?, next_validators: &header.trusted_validator_set, next_validators_hash: trusted_consensus_state.next_validators_hash, @@ -409,13 +432,12 @@ impl Ics2ClientState for ClientState { match verdict { Verdict::Success => {} Verdict::NotEnoughTrust(voting_power_tally) => { - return Err(Error::not_enough_trusted_vals_signed(format!( - "voting power tally: {}", - voting_power_tally - )) + return Err(Error::NotEnoughTrustedValsSigned { + reason: format!("voting power tally: {}", voting_power_tally), + } .into()); } - Verdict::Invalid(detail) => return Err(Error::verification_error(detail).into()), + Verdict::Invalid(detail) => return Err(Error::VerificationError { detail }.into()), } // If the header has verified, but its corresponding consensus state @@ -443,13 +465,13 @@ impl Ics2ClientState for ClientState { // New (untrusted) header timestamp cannot occur after next // consensus state's height if header.signed_header.header().time > next_cs.timestamp { - return Err(Ics02Error::client_specific( - Error::header_timestamp_too_high( - header.signed_header.header().time.to_string(), - next_cs.timestamp.to_string(), - ) + return Err(ClientError::ClientSpecific { + description: Error::HeaderTimestampTooHigh { + actual: header.signed_header.header().time.to_string(), + max: next_cs.timestamp.to_string(), + } .to_string(), - )); + }); } } } @@ -466,13 +488,13 @@ impl Ics2ClientState for ClientState { // New (untrusted) header timestamp cannot occur before the // previous consensus state's height if header.signed_header.header().time < prev_cs.timestamp { - return Err(Ics02Error::client_specific( - Error::header_timestamp_too_low( - header.signed_header.header().time.to_string(), - prev_cs.timestamp.to_string(), - ) + return Err(ClientError::ClientSpecific { + description: Error::HeaderTimestampTooLow { + actual: header.signed_header.header().time.to_string(), + min: prev_cs.timestamp.to_string(), + } .to_string(), - )); + }); } } } @@ -488,17 +510,17 @@ impl Ics2ClientState for ClientState { ctx: &dyn ValidationContext, client_id: ClientId, header: Any, - ) -> Result { + ) -> Result { fn maybe_consensus_state( ctx: &dyn ValidationContext, client_id: &ClientId, height: Height, - ) -> Result>, Ics02Error> { + ) -> Result>, ClientError> { match ctx.consensus_state(client_id, height) { Ok(cs) => Ok(Some(cs)), - Err(e) => match e.detail() { - Ics02ErrorDetail::ConsensusStateNotFound(_) => Ok(None), - _ => Err(e), + Err(e) => match e { + ContextError::ClientError(e) => Err(e), + _ => Ok(None), }, } } @@ -507,13 +529,13 @@ impl Ics2ClientState for ClientState { let header = TmHeader::try_from(header)?; if header.height().revision_number() != client_state.chain_id().version() { - return Err(Ics02Error::client_specific( - Error::mismatched_revisions( - client_state.chain_id().version(), - header.height().revision_number(), - ) + return Err(ClientError::ClientSpecific { + description: Error::MismatchedRevisions { + current_revision: client_state.chain_id().version(), + update_revision: header.height().revision_number(), + } .to_string(), - )); + }); } // Check if a consensus state is already installed; if so it should @@ -539,7 +561,11 @@ impl Ics2ClientState for ClientState { }; let trusted_consensus_state = downcast_tm_consensus_state( - ctx.consensus_state(&client_id, header.trusted_height)? + ctx.consensus_state(&client_id, header.trusted_height) + .map_err(|e| match e { + ContextError::ClientError(e) => e, + _ => todo!(), + })? .as_ref(), )?; @@ -549,11 +575,11 @@ impl Ics2ClientState for ClientState { .trusted_height .revision_height() .try_into() - .map_err(|_| { - Ics02Error::client_specific( - Error::invalid_header_height(header.trusted_height.revision_height()) - .to_string(), - ) + .map_err(|_| ClientError::ClientSpecific { + description: Error::InvalidHeaderHeight { + height: header.trusted_height.revision_height(), + } + .to_string(), })?, next_validators: &header.trusted_validator_set, next_validators_hash: trusted_consensus_state.next_validators_hash, @@ -574,19 +600,24 @@ impl Ics2ClientState for ClientState { untrusted_state, trusted_state, &options, - ctx.host_timestamp()?.into_tm_time().unwrap(), + ctx.host_timestamp() + .map_err(|e| match e { + ContextError::ClientError(e) => e, + _ => todo!(), + })? + .into_tm_time() + .unwrap(), ); match verdict { Verdict::Success => {} Verdict::NotEnoughTrust(voting_power_tally) => { - return Err(Error::not_enough_trusted_vals_signed(format!( - "voting power tally: {}", - voting_power_tally - )) + return Err(Error::NotEnoughTrustedValsSigned { + reason: format!("voting power tally: {}", voting_power_tally), + } .into()); } - Verdict::Invalid(detail) => return Err(Error::verification_error(detail).into()), + Verdict::Invalid(detail) => return Err(Error::VerificationError { detail }.into()), } // If the header has verified, but its corresponding consensus state @@ -605,7 +636,11 @@ impl Ics2ClientState for ClientState { // (cs-new, cs-next, cs-latest) if header.height() < client_state.latest_height() { let maybe_next_cs = ctx - .next_consensus_state(&client_id, header.height())? + .next_consensus_state(&client_id, header.height()) + .map_err(|e| match e { + ContextError::ClientError(e) => e, + _ => todo!(), + })? .as_ref() .map(|cs| downcast_tm_consensus_state(cs.as_ref())) .transpose()?; @@ -614,13 +649,13 @@ impl Ics2ClientState for ClientState { // New (untrusted) header timestamp cannot occur after next // consensus state's height if header.signed_header.header().time > next_cs.timestamp { - return Err(Ics02Error::client_specific( - Error::header_timestamp_too_high( - header.signed_header.header().time.to_string(), - next_cs.timestamp.to_string(), - ) + return Err(ClientError::ClientSpecific { + description: Error::HeaderTimestampTooHigh { + actual: header.signed_header.header().time.to_string(), + max: next_cs.timestamp.to_string(), + } .to_string(), - )); + }); } } } @@ -628,7 +663,11 @@ impl Ics2ClientState for ClientState { // (cs-trusted, cs-prev, cs-new) if header.trusted_height < header.height() { let maybe_prev_cs = ctx - .prev_consensus_state(&client_id, header.height())? + .prev_consensus_state(&client_id, header.height()) + .map_err(|e| match e { + ContextError::ClientError(e) => e, + _ => todo!(), + })? .as_ref() .map(|cs| downcast_tm_consensus_state(cs.as_ref())) .transpose()?; @@ -637,13 +676,13 @@ impl Ics2ClientState for ClientState { // New (untrusted) header timestamp cannot occur before the // previous consensus state's height if header.signed_header.header().time < prev_cs.timestamp { - return Err(Ics02Error::client_specific( - Error::header_timestamp_too_low( - header.signed_header.header().time.to_string(), - prev_cs.timestamp.to_string(), - ) + return Err(ClientError::ClientSpecific { + description: Error::HeaderTimestampTooLow { + actual: header.signed_header.header().time.to_string(), + min: prev_cs.timestamp.to_string(), + } .to_string(), - )); + }); } } } @@ -659,7 +698,7 @@ impl Ics2ClientState for ClientState { _consensus_state: Any, _proof_upgrade_client: RawMerkleProof, _proof_upgrade_consensus_state: RawMerkleProof, - ) -> Result { + ) -> Result { unimplemented!() } @@ -672,7 +711,7 @@ impl Ics2ClientState for ClientState { client_id: &ClientId, consensus_height: Height, expected_consensus_state: &dyn ConsensusState, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; @@ -683,7 +722,7 @@ impl Ics2ClientState for ClientState { }; let value = expected_consensus_state .encode_vec() - .map_err(Ics02Error::invalid_any_consensus_state)?; + .map_err(ClientError::InvalidAnyConsensusState)?; verify_membership(client_state, prefix, proof, root, path, value) } @@ -696,14 +735,14 @@ impl Ics2ClientState for ClientState { root: &CommitmentRoot, connection_id: &ConnectionId, expected_connection_end: &ConnectionEnd, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; let path = ConnectionsPath(connection_id.clone()); let value = expected_connection_end .encode_vec() - .map_err(Ics02Error::invalid_connection_end)?; + .map_err(ClientError::InvalidConnectionEnd)?; verify_membership(client_state, prefix, proof, root, path, value) } @@ -716,14 +755,14 @@ impl Ics2ClientState for ClientState { port_id: &PortId, channel_id: &ChannelId, expected_channel_end: &crate::core::ics04_channel::channel::ChannelEnd, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; let path = ChannelEndsPath(port_id.clone(), channel_id.clone()); let value = expected_channel_end .encode_vec() - .map_err(Ics02Error::invalid_channel_end)?; + .map_err(ClientError::InvalidChannelEnd)?; verify_membership(client_state, prefix, proof, root, path, value) } @@ -735,7 +774,7 @@ impl Ics2ClientState for ClientState { root: &CommitmentRoot, client_id: &ClientId, expected_client_state: Any, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; @@ -755,7 +794,7 @@ impl Ics2ClientState for ClientState { channel_id: &ChannelId, sequence: Sequence, commitment: PacketCommitment, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; verify_delay_passed(ctx, height, connection_end)?; @@ -787,7 +826,7 @@ impl Ics2ClientState for ClientState { channel_id: &ChannelId, sequence: Sequence, ack_commitment: AcknowledgementCommitment, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; verify_delay_passed(ctx, height, connection_end)?; @@ -817,7 +856,7 @@ impl Ics2ClientState for ClientState { port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; verify_delay_passed(ctx, height, connection_end)?; @@ -849,7 +888,7 @@ impl Ics2ClientState for ClientState { port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let client_state = downcast_tm_client_state(self)?; client_state.verify_height(height)?; verify_delay_passed(ctx, height, connection_end)?; @@ -876,10 +915,10 @@ fn verify_membership( root: &CommitmentRoot, path: impl Into, value: Vec, -) -> Result<(), Ics02Error> { +) -> Result<(), ClientError> { let merkle_path = apply_prefix(prefix, vec![path.into().to_string()]); let merkle_proof: MerkleProof = RawMerkleProof::try_from(proof.clone()) - .map_err(Ics02Error::invalid_commitment_proof)? + .map_err(ClientError::InvalidCommitmentProof)? .into(); merkle_proof @@ -890,7 +929,7 @@ fn verify_membership( value, 0, ) - .map_err(Ics02Error::ics23_verification) + .map_err(ClientError::Ics23Verification) } fn verify_non_membership( @@ -899,36 +938,42 @@ fn verify_non_membership( proof: &CommitmentProofBytes, root: &CommitmentRoot, path: impl Into, -) -> Result<(), Ics02Error> { +) -> Result<(), ClientError> { let merkle_path = apply_prefix(prefix, vec![path.into().to_string()]); let merkle_proof: MerkleProof = RawMerkleProof::try_from(proof.clone()) - .map_err(Ics02Error::invalid_commitment_proof)? + .map_err(ClientError::InvalidCommitmentProof)? .into(); merkle_proof .verify_non_membership(&client_state.proof_specs, root.clone().into(), merkle_path) - .map_err(Ics02Error::ics23_verification) + .map_err(ClientError::Ics23Verification) } fn verify_delay_passed( ctx: &dyn ChannelReader, height: Height, connection_end: &ConnectionEnd, -) -> Result<(), Ics02Error> { - let current_timestamp = ctx - .host_timestamp() - .map_err(|e| Ics02Error::other(e.to_string()))?; - let current_height = ctx - .host_height() - .map_err(|e| Ics02Error::other(e.to_string()))?; +) -> Result<(), ClientError> { + let current_timestamp = ctx.host_timestamp().map_err(|e| ClientError::Other { + description: e.to_string(), + })?; + let current_height = ctx.host_height().map_err(|e| ClientError::Other { + description: e.to_string(), + })?; let client_id = connection_end.client_id(); - let processed_time = ctx - .client_update_time(client_id, height) - .map_err(|_| Error::processed_time_not_found(client_id.clone(), height))?; - let processed_height = ctx - .client_update_height(client_id, height) - .map_err(|_| Error::processed_height_not_found(client_id.clone(), height))?; + let processed_time = + ctx.client_update_time(client_id, height) + .map_err(|_| Error::ProcessedTimeNotFound { + client_id: client_id.clone(), + height, + })?; + let processed_height = ctx.client_update_height(client_id, height).map_err(|_| { + Error::ProcessedHeightNotFound { + client_id: client_id.clone(), + height, + } + })?; let delay_period_time = connection_end.delay_period(); let delay_period_height = ctx.block_delay(delay_period_time); @@ -944,16 +989,20 @@ fn verify_delay_passed( .map_err(|e| e.into()) } -fn downcast_tm_client_state(cs: &dyn Ics2ClientState) -> Result<&ClientState, Ics02Error> { +fn downcast_tm_client_state(cs: &dyn Ics2ClientState) -> Result<&ClientState, ClientError> { cs.as_any() .downcast_ref::() - .ok_or_else(|| Ics02Error::client_args_type_mismatch(tm_client_type())) + .ok_or_else(|| ClientError::ClientArgsTypeMismatch { + client_type: tm_client_type(), + }) } -fn downcast_tm_consensus_state(cs: &dyn ConsensusState) -> Result { +fn downcast_tm_consensus_state(cs: &dyn ConsensusState) -> Result { cs.as_any() .downcast_ref::() - .ok_or_else(|| Ics02Error::client_args_type_mismatch(tm_client_type())) + .ok_or_else(|| ClientError::ClientArgsTypeMismatch { + client_type: tm_client_type(), + }) .map(Clone::clone) } @@ -969,35 +1018,37 @@ impl TryFrom for ClientState { let trust_level = raw .trust_level .clone() - .ok_or_else(Error::missing_trusting_period)?; + .ok_or(Error::MissingTrustingPeriod)?; trust_level .try_into() - .map_err(|e| Error::invalid_trust_threshold(format!("{}", e)))? + .map_err(|e| Error::InvalidTrustThreshold { + reason: format!("{}", e), + })? }; let trusting_period = raw .trusting_period - .ok_or_else(Error::missing_trusting_period)? + .ok_or(Error::MissingTrustingPeriod)? .try_into() - .map_err(|_| Error::negative_trusting_period())?; + .map_err(|_| Error::MissingTrustingPeriod)?; let unbonding_period = raw .unbonding_period - .ok_or_else(Error::missing_unbonding_period)? + .ok_or(Error::MissingUnbondingPeriod)? .try_into() - .map_err(|_| Error::negative_unbonding_period())?; + .map_err(|_| Error::MissingUnbondingPeriod)?; let max_clock_drift = raw .max_clock_drift - .ok_or_else(Error::missing_max_clock_drift)? + .ok_or(Error::NegativeMaxClockDrift)? .try_into() - .map_err(|_| Error::negative_max_clock_drift())?; + .map_err(|_| Error::NegativeMaxClockDrift)?; let latest_height = raw .latest_height - .ok_or_else(Error::missing_latest_height)? + .ok_or(Error::MissingLatestHeight)? .try_into() - .map_err(|_| Error::missing_latest_height())?; + .map_err(|_| Error::MissingLatestHeight)?; // In `RawClientState`, a `frozen_height` of `0` means "not frozen". // See: @@ -1058,7 +1109,7 @@ impl From for RawTmClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = Ics02Error; + type Error = ClientError; fn try_from(raw: Any) -> Result { use bytes::Buf; @@ -1066,7 +1117,7 @@ impl TryFrom for ClientState { fn decode_client_state(buf: B) -> Result { RawTmClientState::decode(buf) - .map_err(Error::decode)? + .map_err(Error::Decode)? .try_into() } @@ -1074,7 +1125,9 @@ impl TryFrom for ClientState { TENDERMINT_CLIENT_STATE_TYPE_URL => { decode_client_state(raw.value.deref()).map_err(Into::into) } - _ => Err(Ics02Error::unknown_client_state_type(raw.type_url)), + _ => Err(ClientError::UnknownClientStateType { + client_state_type: raw.type_url, + }), } } } diff --git a/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs b/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs index f6e4ea001..53e600ccd 100644 --- a/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs +++ b/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs @@ -10,7 +10,7 @@ use tendermint_proto::google::protobuf as tpb; use crate::clients::ics07_tendermint::error::Error; use crate::clients::ics07_tendermint::header::Header; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::Error as Ics02Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics23_commitment::commitment::CommitmentRoot; use crate::timestamp::Timestamp; @@ -56,27 +56,32 @@ impl TryFrom for ConsensusState { type Error = Error; fn try_from(raw: RawConsensusState) -> Result { - let ibc_proto::google::protobuf::Timestamp { seconds, nanos } = raw - .timestamp - .ok_or_else(|| Error::invalid_raw_consensus_state("missing timestamp".into()))?; + let ibc_proto::google::protobuf::Timestamp { seconds, nanos } = + raw.timestamp.ok_or(Error::InvalidRawClientState { + reason: "missing timestamp".into(), + })?; // FIXME: shunts like this are necessary due to // https://github.com/informalsystems/tendermint-rs/issues/1053 let proto_timestamp = tpb::Timestamp { seconds, nanos }; let timestamp = proto_timestamp .try_into() - .map_err(|e| Error::invalid_raw_consensus_state(format!("invalid timestamp: {}", e)))?; + .map_err(|e| Error::InvalidRawClientState { + reason: format!("invalid timestamp: {}", e), + })?; Ok(Self { root: raw .root - .ok_or_else(|| { - Error::invalid_raw_consensus_state("missing commitment root".into()) + .ok_or(Error::InvalidRawClientState { + reason: "missing commitment root".into(), })? .hash .into(), timestamp, next_validators_hash: Hash::from_bytes(Algorithm::Sha256, &raw.next_validators_hash) - .map_err(|e| Error::invalid_raw_consensus_state(e.to_string()))?, + .map_err(|e| Error::InvalidRawClientState { + reason: e.to_string(), + })?, }) } } @@ -101,7 +106,7 @@ impl From for RawConsensusState { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = Ics02Error; + type Error = ClientError; fn try_from(raw: Any) -> Result { use bytes::Buf; @@ -110,7 +115,7 @@ impl TryFrom for ConsensusState { fn decode_consensus_state(buf: B) -> Result { RawConsensusState::decode(buf) - .map_err(Error::decode)? + .map_err(Error::Decode)? .try_into() } @@ -118,7 +123,9 @@ impl TryFrom for ConsensusState { TENDERMINT_CONSENSUS_STATE_TYPE_URL => { decode_consensus_state(raw.value.deref()).map_err(Into::into) } - _ => Err(Ics02Error::unknown_consensus_state_type(raw.type_url)), + _ => Err(ClientError::UnknownConsensusStateType { + consensus_state_type: raw.type_url, + }), } } } diff --git a/crates/ibc/src/clients/ics07_tendermint/error.rs b/crates/ibc/src/clients/ics07_tendermint/error.rs index 38f497865..5b18f2067 100644 --- a/crates/ibc/src/clients/ics07_tendermint/error.rs +++ b/crates/ibc/src/clients/ics07_tendermint/error.rs @@ -1,316 +1,136 @@ use crate::prelude::*; -use flex_error::{define_error, TraceError}; - -use crate::core::ics02_client::error::Error as Ics02Error; -use crate::core::ics24_host::error::ValidationError; -use crate::core::ics24_host::identifier::ClientId; +use crate::core::ics02_client::error::ClientError; +use crate::core::ics24_host::identifier::{ChainId, ClientId}; use crate::timestamp::{Timestamp, TimestampOverflowError}; +use displaydoc::Display; use crate::Height; use tendermint::account::Id; -use tendermint::hash::Hash; use tendermint::Error as TendermintError; use tendermint_light_client_verifier::errors::VerificationErrorDetail as LightClientErrorDetail; -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - ChainIdTooLong - { - chain_id: String, - len: usize, - max_len: usize, - } - |e| { format_args!("chain-id is ({0}) is too long, got: {1}, max allowed: {2}", e.chain_id, e.len, e.max_len) }, - - InvalidTrustingPeriod - { reason: String } - |e| { format_args!("invalid trusting period: {}", e.reason) }, - - InvalidUnbondingPeriod - { reason: String } - |e| { format_args!("invalid unbonding period: {}", e.reason) }, - - InvalidAddress - |_| { "invalid address" }, - - InvalidHeader - { reason: String } - [ TendermintError ] - |e| { format_args!("invalid header, failed basic validation: {}", e.reason) }, - - InvalidTrustThreshold - { reason: String } - |e| { format_args!("invalid client state trust threshold: {}", e.reason) }, - - InvalidTendermintTrustThreshold - [ TendermintError ] - |_| { "invalid tendermint client state trust threshold" }, - - InvalidMaxClockDrift - { reason: String } - |e| { format_args!("invalid client state max clock drift: {}", e.reason) }, - - InvalidLatestHeight - { reason: String } - |e| { format_args!("invalid client state latest height: {}", e.reason) }, - - MissingSignedHeader - |_| { "missing signed header" }, - - Validation - { reason: String } - |e| { format_args!("invalid header, failed basic validation: {}", e.reason) }, - - InvalidRawClientState - { reason: String } - |e| { format_args!("invalid raw client state: {}", e.reason) }, - - MissingValidatorSet - |_| { "missing validator set" }, - - MissingTrustedValidatorSet - |_| { "missing trusted validator set" }, - - MissingTrustedHeight - |_| { "missing trusted height" }, - - MissingTrustingPeriod - |_| { "missing trusting period" }, - - MissingUnbondingPeriod - |_| { "missing unbonding period" }, - - InvalidChainIdentifier - [ ValidationError ] - |_| { "invalid chain identifier" }, - - NegativeTrustingPeriod - |_| { "negative trusting period" }, - - NegativeUnbondingPeriod - |_| { "negative unbonding period" }, - - MissingMaxClockDrift - |_| { "missing max clock drift" }, - - NegativeMaxClockDrift - |_| { "negative max clock drift" }, - - MissingLatestHeight - |_| { "missing latest height" }, - - InvalidFrozenHeight - |_| { "invalid frozen height" }, - - InvalidChainId - { raw_value: String } - [ ValidationError ] - |e| { format_args!("invalid chain identifier: {}", e.raw_value) }, - - InvalidRawHeight - { raw_height: u64 } - |e| { format_args!("invalid raw height: {}", e.raw_height) }, - - InvalidRawConsensusState - { reason: String } - | e | { format_args!("invalid raw client consensus state: {}", e.reason) }, - - InvalidRawHeader - [ TendermintError ] - | _ | { "invalid raw header" }, - - InvalidRawMisbehaviour - { reason: String } - | e | { format_args!("invalid raw misbehaviour: {}", e.reason) }, - - Decode - [ TraceError ] - | _ | { "decode error" }, - - InsufficientVotingPower - { reason: String } - | e | { - format_args!("insufficient overlap: {}", e.reason) - }, - - LowUpdateTimestamp - { - low: String, - high: String - } - | e | { - format_args!("header timestamp {0} must be greater than current client consensus state timestamp {1}", e.low, e.high) - }, - - HeaderTimestampOutsideTrustingTime - { - low: String, - high: String - } - | e | { - format_args!("header timestamp {0} is outside the trusting period w.r.t. consensus state timestamp {1}", e.low, e.high) - }, - - HeaderTimestampTooHigh - { - actual: String, - max: String, - } - | e | { - format_args!("given other previous updates, header timestamp should be at most {0}, but was {1}", e.max, e.actual) - }, - - HeaderTimestampTooLow - { - actual: String, - min: String, - } - | e | { - format_args!("given other previous updates, header timestamp should be at least {0}, but was {1}", e.min, e.actual) - }, - - TimestampOverflow - [ TimestampOverflowError ] - |_| { "timestamp overflowed" }, - - NotEnoughTimeElapsed - { - current_time: Timestamp, - earliest_time: Timestamp, - } - | e | { - format_args!("not enough time elapsed, current timestamp {0} is still less than earliest acceptable timestamp {1}", e.current_time, e.earliest_time) - }, - - NotEnoughBlocksElapsed - { - current_height: Height, - earliest_height: Height, - } - | e | { - format_args!("not enough blocks elapsed, current height {0} is still less than earliest acceptable height {1}", e.current_height, e.earliest_height) - }, - - InvalidHeaderHeight - { height: u64 } - | e | { - format_args!("header revision height = {0} is invalid", e.height) - }, - - InvalidTrustedHeaderHeight - { - trusted_header_height: Height, - height_header: Height - } - | e | { - format_args!("header height is {0} and is lower than the trusted header height, which is {1} ", e.height_header, e.trusted_header_height) - }, - - LowUpdateHeight - { - low: Height, - high: Height - } - | e | { - format_args!("header height is {0} but it must be greater than the current client height which is {1}", e.low, e.high) - }, - - MismatchedRevisions - { - current_revision: u64, - update_revision: u64, - } - | e | { - format_args!("the header's current/trusted revision number ({0}) and the update's revision number ({1}) should be the same", e.current_revision, e.update_revision) - }, - - InvalidValidatorSet - { - hash1: Hash, - hash2: Hash, - } - | e | { - format_args!("invalid validator set: header_validators_hash={} and validators_hash={}", e.hash1, e.hash2) - }, - - NotEnoughTrustedValsSigned - { reason: String } - | e | { - format_args!("not enough trust because insufficient validators overlap: {}", e.reason) - }, - - VerificationError - { detail: LightClientErrorDetail } - | e | { - format_args!("verification failed: {}", e.detail) - }, - - ProcessedTimeNotFound - { - client_id: ClientId, - height: Height, - } - | e | { - format_args!( - "Processed time for the client {0} at height {1} not found", - e.client_id, e.height) - }, - - ProcessedHeightNotFound - { - client_id: ClientId, - height: Height, - } - | e | { - format_args!( - "Processed height for the client {0} at height {1} not found", - e.client_id, e.height) - }, - - InsufficientHeight - { - latest_height: Height, - target_height: Height, - } - | e | { - format_args!("the height is insufficient: latest_height={0} target_height={1}", e.latest_height, e.target_height) - }, +#[derive(Debug, Display)] +pub enum Error { + /// chain-id is (`{chain_id}`) is too long, got: `{len}`, max allowed: `{max_len}` + ChainIdTooLong { + chain_id: ChainId, + len: usize, + max_len: usize, + }, + /// invalid header, failed basic validation: `{reason}`, error: `{error}` + InvalidHeader { + reason: String, + error: TendermintError, + }, + /// invalid client state trust threshold: `{reason}` + InvalidTrustThreshold { reason: String }, + /// invalid tendermint client state trust threshold error: `{0}` + InvalidTendermintTrustThreshold(TendermintError), + /// invalid client state max clock drift: `{reason}` + InvalidMaxClockDrift { reason: String }, + /// invalid client state latest height: `{reason}` + InvalidLatestHeight { reason: String }, + /// missing signed header + MissingSignedHeader, + /// invalid header, failed basic validation: `{reason}` + Validation { reason: String }, + /// invalid raw client state: `{reason}` + InvalidRawClientState { reason: String }, + /// missing validator set + MissingValidatorSet, + /// missing trusted validator set + MissingTrustedValidatorSet, + /// missing trusted height + MissingTrustedHeight, + /// missing trusting period + MissingTrustingPeriod, + /// missing unbonding period + MissingUnbondingPeriod, + /// negative max clock drift + NegativeMaxClockDrift, + /// missing latest height + MissingLatestHeight, + /// invalid raw header error: `{0}` + InvalidRawHeader(TendermintError), + /// invalid raw misbehaviour: `{reason}` + InvalidRawMisbehaviour { reason: String }, + /// decode error: `{0}` + Decode(prost::DecodeError), + /// given other previous updates, header timestamp should be at most `{max}`, but was `{actual}` + HeaderTimestampTooHigh { actual: String, max: String }, + /// given other previous updates, header timestamp should be at least `{min}`, but was `{actual}` + HeaderTimestampTooLow { actual: String, min: String }, + /// timestamp overflowed error: `{0}` + TimestampOverflow(TimestampOverflowError), + /// not enough time elapsed, current timestamp `{current_time}` is still less than earliest acceptable timestamp `{earliest_time}` + NotEnoughTimeElapsed { + current_time: Timestamp, + earliest_time: Timestamp, + }, + /// not enough blocks elapsed, current height `{current_height}` is still less than earliest acceptable height `{earliest_height}` + NotEnoughBlocksElapsed { + current_height: Height, + earliest_height: Height, + }, + /// header revision height = `{height}` is invalid + InvalidHeaderHeight { height: u64 }, + /// the header's current/trusted revision number (`{current_revision}`) and the update's revision number (`{update_revision}`) should be the same + MismatchedRevisions { + current_revision: u64, + update_revision: u64, + }, + /// not enough trust because insufficient validators overlap: `{reason}` + NotEnoughTrustedValsSigned { reason: String }, + /// verification failed: `{detail}` + VerificationError { detail: LightClientErrorDetail }, + /// Processed time for the client `{client_id}` at height `{height}` not found + ProcessedTimeNotFound { client_id: ClientId, height: Height }, + /// Processed height for the client `{client_id}` at height `{height}` not found + ProcessedHeightNotFound { client_id: ClientId, height: Height }, + /// the height is insufficient: latest_height=`{latest_height}` target_height=`{target_height}` + InsufficientHeight { + latest_height: Height, + target_height: Height, + }, + /// the client is frozen: frozen_height=`{frozen_height}` target_height=`{target_height}` + ClientFrozen { + frozen_height: Height, + target_height: Height, + }, +} - ClientFrozen - { - frozen_height: Height, - target_height: Height, - } - | e | { - format_args!("the client is frozen: frozen_height={0} target_height={1}", e.frozen_height, e.target_height) - }, +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::InvalidHeader { error: e, .. } => Some(e), + Self::InvalidTendermintTrustThreshold(e) => Some(e), + Self::InvalidRawHeader(e) => Some(e), + Self::Decode(e) => Some(e), + Self::TimestampOverflow(e) => Some(e), + _ => None, + } } } -define_error! { - #[derive(Debug, PartialEq, Eq)] - VerificationError { - InvalidSignature - | _ | { "couldn't verify validator signature" }, - - DuplicateValidator - { id: Id } - | e | { - format_args!("duplicate validator in commit signatures with address {}", e.id) - }, - - InsufficientOverlap - { q1: u64, q2: u64 } - | e | { - format_args!("insufficient signers overlap between {0} and {1}", e.q1, e.q2) - }, - } +#[cfg(feature = "std")] +impl std::error::Error for VerificationError {} + +#[derive(Debug, Display)] +pub enum VerificationError { + /// couldn't verify validator signature + InvalidSignature, + /// duplicate validator in commit signatures with address `{id}` + DuplicateValidator { id: Id }, + /// insufficient signers overlap between `{q1}` and `{q2}` + InsufficientOverlap { q1: u64, q2: u64 }, } -impl From for Ics02Error { +impl From for ClientError { fn from(e: Error) -> Self { - Self::client_specific(e.to_string()) + Self::ClientSpecific { + description: e.to_string(), + } } } diff --git a/crates/ibc/src/clients/ics07_tendermint/header.rs b/crates/ibc/src/clients/ics07_tendermint/header.rs index a1d5f1c10..8816edb11 100644 --- a/crates/ibc/src/clients/ics07_tendermint/header.rs +++ b/crates/ibc/src/clients/ics07_tendermint/header.rs @@ -13,7 +13,7 @@ use tendermint::validator::Set as ValidatorSet; use crate::clients::ics07_tendermint::error::Error; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::Error as Ics02Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics24_host::identifier::ChainId; use crate::timestamp::Timestamp; use crate::utils::pretty::{PrettySignedHeader, PrettyValidatorSet}; @@ -102,30 +102,33 @@ impl TryFrom for Header { let header = Self { signed_header: raw .signed_header - .ok_or_else(Error::missing_signed_header)? + .ok_or(Error::MissingSignedHeader)? .try_into() - .map_err(|e| Error::invalid_header("signed header conversion".to_string(), e))?, + .map_err(|e| Error::InvalidHeader { + reason: "signed header conversion".to_string(), + error: e, + })?, validator_set: raw .validator_set - .ok_or_else(Error::missing_validator_set)? + .ok_or(Error::MissingValidatorSet)? .try_into() - .map_err(Error::invalid_raw_header)?, + .map_err(Error::InvalidRawHeader)?, trusted_height: raw .trusted_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_trusted_height)?, + .ok_or(Error::MissingTrustedHeight)?, trusted_validator_set: raw .trusted_validators - .ok_or_else(Error::missing_trusted_validator_set)? + .ok_or(Error::MissingTrustedValidatorSet)? .try_into() - .map_err(Error::invalid_raw_header)?, + .map_err(Error::InvalidRawHeader)?, }; if header.height().revision_number() != header.trusted_height.revision_number() { - return Err(Error::mismatched_revisions( - header.trusted_height.revision_number(), - header.height().revision_number(), - )); + return Err(Error::MismatchedRevisions { + current_revision: header.trusted_height.revision_number(), + update_revision: header.height().revision_number(), + }); } Ok(header) @@ -135,18 +138,20 @@ impl TryFrom for Header { impl Protobuf for Header {} impl TryFrom for Header { - type Error = Ics02Error; + type Error = ClientError; - fn try_from(raw: Any) -> Result { + fn try_from(raw: Any) -> Result { use core::ops::Deref; fn decode_header(buf: B) -> Result { - RawHeader::decode(buf).map_err(Error::decode)?.try_into() + RawHeader::decode(buf).map_err(Error::Decode)?.try_into() } match raw.type_url.as_str() { TENDERMINT_HEADER_TYPE_URL => decode_header(raw.value.deref()).map_err(Into::into), - _ => Err(Ics02Error::unknown_header_type(raw.type_url)), + _ => Err(ClientError::UnknownHeaderType { + header_type: raw.type_url, + }), } } } @@ -162,7 +167,7 @@ impl From
for Any { } pub fn decode_header(buf: B) -> Result { - RawHeader::decode(buf).map_err(Error::decode)?.try_into() + RawHeader::decode(buf).map_err(Error::Decode)?.try_into() } impl From
for RawHeader { diff --git a/crates/ibc/src/clients/ics07_tendermint/host_helpers.rs b/crates/ibc/src/clients/ics07_tendermint/host_helpers.rs index 32fc73d72..9a822bfd0 100644 --- a/crates/ibc/src/clients/ics07_tendermint/host_helpers.rs +++ b/crates/ibc/src/clients/ics07_tendermint/host_helpers.rs @@ -6,7 +6,7 @@ use ibc_proto::google::protobuf::Any; use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState; use crate::core::ics02_client::client_state::ClientState; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics23_commitment::specs::ProofSpecs; use crate::core::ics24_host::identifier::ChainId; use crate::Height; @@ -16,47 +16,57 @@ use tendermint::trust_threshold::TrustThresholdFraction as TendermintTrustThresh /// Provides an implementation of `ConnectionReader::validate_self_client` for /// Tendermint-based hosts. pub trait ValidateSelfClientContext { - fn validate_self_client(&self, counterparty_client_state: Any) -> Result<(), Error> { + fn validate_self_client(&self, counterparty_client_state: Any) -> Result<(), ConnectionError> { let counterparty_client_state = TmClientState::try_from(counterparty_client_state) - .map_err(|_| { - Error::invalid_client_state("client must be a tendermint client".to_string()) + .map_err(|_| ConnectionError::InvalidClientState { + reason: "client must be a tendermint client".to_string(), })?; if counterparty_client_state.is_frozen() { - return Err(Error::invalid_client_state("client is frozen".to_string())); + return Err(ConnectionError::InvalidClientState { + reason: "client is frozen".to_string(), + }); } let self_chain_id = self.chain_id(); if self_chain_id != &counterparty_client_state.chain_id { - return Err(Error::invalid_client_state(format!( - "invalid chain-id. expected: {}, got: {}", - self_chain_id, counterparty_client_state.chain_id - ))); + return Err(ConnectionError::InvalidClientState { + reason: format!( + "invalid chain-id. expected: {}, got: {}", + self_chain_id, counterparty_client_state.chain_id + ), + }); } let self_revision_number = self_chain_id.version(); if self_revision_number != counterparty_client_state.latest_height().revision_number() { - return Err(Error::invalid_client_state(format!( - "client is not in the same revision as the chain. expected: {}, got: {}", - self_revision_number, - counterparty_client_state.latest_height().revision_number() - ))); + return Err(ConnectionError::InvalidClientState { + reason: format!( + "client is not in the same revision as the chain. expected: {}, got: {}", + self_revision_number, + counterparty_client_state.latest_height().revision_number() + ), + }); } if counterparty_client_state.latest_height() >= self.host_current_height() { - return Err(Error::invalid_client_state(format!( - "client has latest height {} greater than or equal to chain height {}", - counterparty_client_state.latest_height(), - self.host_current_height() - ))); + return Err(ConnectionError::InvalidClientState { + reason: format!( + "client has latest height {} greater than or equal to chain height {}", + counterparty_client_state.latest_height(), + self.host_current_height() + ), + }); } if self.proof_specs() != &counterparty_client_state.proof_specs { - return Err(Error::invalid_client_state(format!( - "client has invalid proof specs. expected: {:?}, got: {:?}", - self.proof_specs(), - counterparty_client_state.proof_specs - ))); + return Err(ConnectionError::InvalidClientState { + reason: format!( + "client has invalid proof specs. expected: {:?}, got: {:?}", + self.proof_specs(), + counterparty_client_state.proof_specs + ), + }); } let _ = { @@ -66,33 +76,39 @@ pub trait ValidateSelfClientContext { trust_level.numerator(), trust_level.denominator(), ) - .map_err(|_| Error::invalid_client_state("invalid trust level".to_string()))? + .map_err(|_| ConnectionError::InvalidClientState { + reason: "invalid trust level".to_string(), + })? }; if self.unbonding_period() != counterparty_client_state.unbonding_period { - return Err(Error::invalid_client_state(format!( - "invalid unbonding period. expected: {:?}, got: {:?}", - self.unbonding_period(), - counterparty_client_state.unbonding_period, - ))); + return Err(ConnectionError::InvalidClientState { + reason: format!( + "invalid unbonding period. expected: {:?}, got: {:?}", + self.unbonding_period(), + counterparty_client_state.unbonding_period, + ), + }); } if counterparty_client_state.unbonding_period < counterparty_client_state.trusting_period { - return Err(Error::invalid_client_state(format!( + return Err(ConnectionError::InvalidClientState{ reason: format!( "unbonding period must be greater than trusting period. unbonding period ({:?}) < trusting period ({:?})", counterparty_client_state.unbonding_period, counterparty_client_state.trusting_period - ))); + )}); } if !counterparty_client_state.upgrade_path.is_empty() && self.upgrade_path() != counterparty_client_state.upgrade_path { - return Err(Error::invalid_client_state(format!( - "invalid upgrade path. expected: {:?}, got: {:?}", - self.upgrade_path(), - counterparty_client_state.upgrade_path - ))); + return Err(ConnectionError::InvalidClientState { + reason: format!( + "invalid upgrade path. expected: {:?}, got: {:?}", + self.upgrade_path(), + counterparty_client_state.upgrade_path + ), + }); } Ok(()) diff --git a/crates/ibc/src/clients/ics07_tendermint/misbehaviour.rs b/crates/ibc/src/clients/ics07_tendermint/misbehaviour.rs index afe161cfd..7368421fb 100644 --- a/crates/ibc/src/clients/ics07_tendermint/misbehaviour.rs +++ b/crates/ibc/src/clients/ics07_tendermint/misbehaviour.rs @@ -38,11 +38,15 @@ impl TryFrom for Misbehaviour { client_id: Default::default(), header1: raw .header_1 - .ok_or_else(|| Error::invalid_raw_misbehaviour("missing header1".into()))? + .ok_or_else(|| Error::InvalidRawMisbehaviour { + reason: "missing header1".into(), + })? .try_into()?, header2: raw .header_2 - .ok_or_else(|| Error::invalid_raw_misbehaviour("missing header2".into()))? + .ok_or_else(|| Error::InvalidRawMisbehaviour { + reason: "missing header2".into(), + })? .try_into()?, }) } diff --git a/crates/ibc/src/core/context.rs b/crates/ibc/src/core/context.rs index 7fead02c4..78203cd8e 100644 --- a/crates/ibc/src/core/context.rs +++ b/crates/ibc/src/core/context.rs @@ -3,7 +3,7 @@ use core::time::Duration; use crate::events::IbcEvent; use crate::{prelude::*, timestamp::Timestamp, Height}; -use crate::core::ics26_routing::error::Error as RouterError; +use crate::core::ics26_routing::error::RouterError; use ibc_proto::google::protobuf::Any; @@ -17,18 +17,18 @@ use super::ics24_host::path::{ use super::ics26_routing::msgs::MsgEnvelope; use super::{ ics02_client::{ - client_state::ClientState, consensus_state::ConsensusState, error::Error as ClientError, + client_state::ClientState, consensus_state::ConsensusState, error::ClientError, }, ics03_connection::{ connection::ConnectionEnd, - error::Error as ConnectionError, + error::ConnectionError, version::{get_compatible_versions, pick_version, Version as ConnectionVersion}, }, ics04_channel::{ channel::ChannelEnd, commitment::{AcknowledgementCommitment, PacketCommitment}, context::calculate_block_delay, - error::Error as ChannelError, + error::{ChannelError, PacketError}, msgs::acknowledgement::Acknowledgement, packet::{Receipt, Sequence}, timeout::TimeoutHeight, @@ -36,6 +36,55 @@ use super::{ ics23_commitment::commitment::CommitmentPrefix, ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}, }; +use displaydoc::Display; + +#[derive(Debug, Display)] +pub enum ContextError { + /// ICS02 Client error + ClientError(ClientError), + /// ICS03 Connection error + ConnectionError(ConnectionError), + /// Ics04 Channel error + ChannelError(ChannelError), + /// ICS04 Packet error + PacketError(PacketError), +} + +impl From for ContextError { + fn from(err: ClientError) -> ContextError { + Self::ClientError(err) + } +} + +impl From for ContextError { + fn from(err: ConnectionError) -> ContextError { + Self::ConnectionError(err) + } +} + +impl From for ContextError { + fn from(err: ChannelError) -> ContextError { + Self::ChannelError(err) + } +} + +impl From for ContextError { + fn from(err: PacketError) -> ContextError { + Self::PacketError(err) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ContextError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::ClientError(e) => Some(e), + Self::ConnectionError(e) => Some(e), + Self::ChannelError(e) => Some(e), + Self::PacketError(e) => Some(e), + } + } +} pub trait ValidationContext { /// Validation entrypoint. @@ -52,7 +101,7 @@ pub trait ValidationContext { ClientMsg::Misbehaviour(_message) => unimplemented!(), ClientMsg::UpgradeClient(message) => upgrade_client::validate(self, message), } - .map_err(RouterError::ics02_client), + .map_err(RouterError::ContextError), MsgEnvelope::ConnectionMsg(_message) => todo!(), MsgEnvelope::ChannelMsg(_message) => todo!(), MsgEnvelope::PacketMsg(_message) => todo!(), @@ -60,10 +109,10 @@ pub trait ValidationContext { } /// Returns the ClientState for the given identifier `client_id`. - fn client_state(&self, client_id: &ClientId) -> Result, ClientError>; + fn client_state(&self, client_id: &ClientId) -> Result, ContextError>; /// Tries to decode the given `client_state` into a concrete light client state. - fn decode_client_state(&self, client_state: Any) -> Result, ClientError>; + fn decode_client_state(&self, client_state: Any) -> Result, ContextError>; /// Retrieve the consensus state for the given client ID at the specified /// height. @@ -73,27 +122,27 @@ pub trait ValidationContext { &self, client_id: &ClientId, height: Height, - ) -> Result, ClientError>; + ) -> Result, ContextError>; /// Search for the lowest consensus state higher than `height`. fn next_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result>, ClientError>; + ) -> Result>, ContextError>; /// Search for the highest consensus state lower than `height`. fn prev_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result>, ClientError>; + ) -> Result>, ContextError>; /// Returns the current height of the local chain. - fn host_height(&self) -> Result; + fn host_height(&self) -> Result; /// Returns the current timestamp of the local chain. - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { let pending_consensus_state = self .pending_host_consensus_state() .expect("host must have pending consensus state"); @@ -101,17 +150,18 @@ pub trait ValidationContext { } /// Returns the pending `ConsensusState` of the host (local) chain. - fn pending_host_consensus_state(&self) -> Result, ClientError>; + fn pending_host_consensus_state(&self) -> Result, ContextError>; /// Returns the `ConsensusState` of the host (local) chain at a specific height. - fn host_consensus_state(&self, height: Height) -> Result, ClientError>; + fn host_consensus_state(&self, height: Height) + -> Result, ContextError>; /// Returns a natural number, counting how many clients have been created thus far. /// The value of this counter should increase only via method `ClientKeeper::increase_client_counter`. - fn client_counter(&self) -> Result; + fn client_counter(&self) -> Result; /// Returns the ConnectionEnd for the given identifier `conn_id`. - fn connection_end(&self, conn_id: &ConnectionId) -> Result; + fn connection_end(&self, conn_id: &ConnectionId) -> Result; /// Returns the oldest height available on the local chain. fn host_oldest_height(&self) -> Height; @@ -120,7 +170,7 @@ pub trait ValidationContext { fn commitment_prefix(&self) -> CommitmentPrefix; /// Returns a counter on how many connections have been created thus far. - fn connection_counter(&self) -> Result; + fn connection_counter(&self) -> Result; /// Function required by ICS 03. Returns the list of all possible versions that the connection /// handshake protocol supports. @@ -134,50 +184,51 @@ pub trait ValidationContext { &self, supported_versions: Vec, counterparty_candidate_versions: Vec, - ) -> Result { + ) -> Result { pick_version(supported_versions, counterparty_candidate_versions) + .map_err(ContextError::ConnectionError) } /// Returns the ChannelEnd for the given `port_id` and `chan_id`. fn channel_end( &self, port_channel_id: &(PortId, ChannelId), - ) -> Result; + ) -> Result; fn connection_channels( &self, cid: &ConnectionId, - ) -> Result, ChannelError>; + ) -> Result, ContextError>; fn get_next_sequence_send( &self, port_channel_id: &(PortId, ChannelId), - ) -> Result; + ) -> Result; fn get_next_sequence_recv( &self, port_channel_id: &(PortId, ChannelId), - ) -> Result; + ) -> Result; fn get_next_sequence_ack( &self, port_channel_id: &(PortId, ChannelId), - ) -> Result; + ) -> Result; fn get_packet_commitment( &self, key: &(PortId, ChannelId, Sequence), - ) -> Result; + ) -> Result; fn get_packet_receipt( &self, key: &(PortId, ChannelId, Sequence), - ) -> Result; + ) -> Result; fn get_packet_acknowledgement( &self, key: &(PortId, ChannelId, Sequence), - ) -> Result; + ) -> Result; /// Compute the commitment for a packet. /// Note that the absence of `timeout_height` is treated as @@ -216,19 +267,19 @@ pub trait ValidationContext { &self, client_id: &ClientId, height: Height, - ) -> Result; + ) -> Result; /// Returns the height when the client state for the given [`ClientId`] was updated with a header for the given [`Height`] fn client_update_height( &self, client_id: &ClientId, height: Height, - ) -> Result; + ) -> Result; /// Returns a counter on the number of channel ids have been created thus far. /// The value of this counter should increase only via method /// `ChannelKeeper::increase_channel_counter`. - fn channel_counter(&self) -> Result; + fn channel_counter(&self) -> Result; /// Returns the maximum expected time per block fn max_expected_time_per_block(&self) -> Duration; @@ -255,7 +306,7 @@ pub trait ExecutionContext: ValidationContext { ClientMsg::Misbehaviour(_message) => unimplemented!(), ClientMsg::UpgradeClient(message) => upgrade_client::execute(self, message), } - .map_err(RouterError::ics02_client), + .map_err(RouterError::ContextError), MsgEnvelope::ConnectionMsg(_message) => todo!(), MsgEnvelope::ChannelMsg(_message) => todo!(), MsgEnvelope::PacketMsg(_message) => todo!(), @@ -267,21 +318,21 @@ pub trait ExecutionContext: ValidationContext { &mut self, client_type_path: ClientTypePath, client_type: ClientType, - ) -> Result<(), ClientError>; + ) -> Result<(), ContextError>; /// Called upon successful client creation and update fn store_client_state( &mut self, client_state_path: ClientStatePath, client_state: Box, - ) -> Result<(), ClientError>; + ) -> Result<(), ContextError>; /// Called upon successful client creation and update fn store_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, consensus_state: Box, - ) -> Result<(), ClientError>; + ) -> Result<(), ContextError>; /// Called upon client creation. /// Increases the counter which keeps track of how many clients have been created. @@ -296,7 +347,7 @@ pub trait ExecutionContext: ValidationContext { client_id: ClientId, height: Height, timestamp: Timestamp, - ) -> Result<(), ClientError>; + ) -> Result<(), ContextError>; /// Called upon successful client update. /// Implementations are expected to use this to record the specified height as the height at @@ -306,21 +357,21 @@ pub trait ExecutionContext: ValidationContext { client_id: ClientId, height: Height, host_height: Height, - ) -> Result<(), ClientError>; + ) -> Result<(), ContextError>; /// Stores the given connection_end at path fn store_connection( &mut self, connections_path: ConnectionsPath, connection_end: &ConnectionEnd, - ) -> Result<(), ConnectionError>; + ) -> Result<(), ContextError>; /// Stores the given connection_id at a path associated with the client_id. fn store_connection_to_client( &mut self, client_connections_path: ClientConnectionsPath, client_id: &ClientId, - ) -> Result<(), ConnectionError>; + ) -> Result<(), ContextError>; /// Called upon connection identifier creation (Init or Try process). /// Increases the counter which keeps track of how many connections have been created. @@ -331,57 +382,57 @@ pub trait ExecutionContext: ValidationContext { &mut self, commitments_path: CommitmentsPath, commitment: PacketCommitment, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; - fn delete_packet_commitment(&mut self, key: CommitmentsPath) -> Result<(), ChannelError>; + fn delete_packet_commitment(&mut self, key: CommitmentsPath) -> Result<(), ContextError>; fn store_packet_receipt( &mut self, path: ReceiptsPath, receipt: Receipt, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; fn store_packet_acknowledgement( &mut self, key: (PortId, ChannelId, Sequence), ack_commitment: AcknowledgementCommitment, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; fn delete_packet_acknowledgement( &mut self, key: (PortId, ChannelId, Sequence), - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; fn store_connection_channels( &mut self, conn_id: ConnectionId, port_channel_id: &(PortId, ChannelId), - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; /// Stores the given channel_end at a path associated with the port_id and channel_id. fn store_channel( &mut self, port_channel_id: (PortId, ChannelId), channel_end: &ChannelEnd, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; fn store_next_sequence_send( &mut self, port_channel_id: (PortId, ChannelId), seq: Sequence, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; fn store_next_sequence_recv( &mut self, port_channel_id: (PortId, ChannelId), seq: Sequence, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; fn store_next_sequence_ack( &mut self, port_channel_id: (PortId, ChannelId), seq: Sequence, - ) -> Result<(), ChannelError>; + ) -> Result<(), ContextError>; /// Called upon channel identifier creation (Init or Try message processing). /// Increases the counter which keeps track of how many channels have been created. diff --git a/crates/ibc/src/core/ics02_client/client_state.rs b/crates/ibc/src/core/ics02_client/client_state.rs index 2167c2f4a..42bf9a939 100644 --- a/crates/ibc/src/core/ics02_client/client_state.rs +++ b/crates/ibc/src/core/ics02_client/client_state.rs @@ -8,7 +8,7 @@ use ibc_proto::ibc::core::commitment::v1::MerkleProof; use ibc_proto::protobuf::Protobuf as ErasedProtobuf; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics03_connection::connection::ConnectionEnd; use crate::core::ics04_channel::channel::ChannelEnd; use crate::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCommitment}; @@ -31,7 +31,7 @@ pub trait ClientState: + sealed::ErasedPartialEqClientState + DynClone + ErasedSerialize - + ErasedProtobuf + + ErasedProtobuf + core::fmt::Debug + Send + Sync @@ -76,7 +76,7 @@ pub trait ClientState: Box::new(self) } - fn initialise(&self, consensus_state: Any) -> Result, Error>; + fn initialise(&self, consensus_state: Any) -> Result, ClientError>; /// XXX: temporary solution until we get rid of `ClientReader` fn old_check_header_and_update_state( @@ -84,21 +84,21 @@ pub trait ClientState: ctx: &dyn ClientReader, client_id: ClientId, header: Any, - ) -> Result; + ) -> Result; fn check_header_and_update_state( &self, ctx: &dyn ValidationContext, client_id: ClientId, header: Any, - ) -> Result; + ) -> Result; fn verify_upgrade_and_update_state( &self, consensus_state: Any, proof_upgrade_client: MerkleProof, proof_upgrade_consensus_state: MerkleProof, - ) -> Result; + ) -> Result; /// Verification functions as specified in: /// @@ -117,7 +117,7 @@ pub trait ClientState: counterparty_client_id: &ClientId, consensus_height: Height, expected_consensus_state: &dyn ConsensusState, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify a `proof` that a connection state matches that of the input `connection_end`. #[allow(clippy::too_many_arguments)] @@ -129,7 +129,7 @@ pub trait ClientState: root: &CommitmentRoot, counterparty_connection_id: &ConnectionId, expected_counterparty_connection_end: &ConnectionEnd, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify a `proof` that a channel state matches that of the input `channel_end`. #[allow(clippy::too_many_arguments)] @@ -142,7 +142,7 @@ pub trait ClientState: counterparty_port_id: &PortId, counterparty_channel_id: &ChannelId, expected_counterparty_channel_end: &ChannelEnd, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify the client state for this chain that it is stored on the counterparty chain. #[allow(clippy::too_many_arguments)] @@ -154,7 +154,7 @@ pub trait ClientState: root: &CommitmentRoot, client_id: &ClientId, expected_client_state: Any, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify a `proof` that a packet has been commited. #[allow(clippy::too_many_arguments)] @@ -169,7 +169,7 @@ pub trait ClientState: channel_id: &ChannelId, sequence: Sequence, commitment: PacketCommitment, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify a `proof` that a packet has been commited. #[allow(clippy::too_many_arguments)] @@ -184,7 +184,7 @@ pub trait ClientState: channel_id: &ChannelId, sequence: Sequence, ack: AcknowledgementCommitment, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify a `proof` that of the next_seq_received. #[allow(clippy::too_many_arguments)] @@ -198,7 +198,7 @@ pub trait ClientState: port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Verify a `proof` that a packet has not been received. #[allow(clippy::too_many_arguments)] @@ -212,7 +212,7 @@ pub trait ClientState: port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; } // Implements `Clone` for `Box` diff --git a/crates/ibc/src/core/ics02_client/consensus_state.rs b/crates/ibc/src/core/ics02_client/consensus_state.rs index 4d7c0adac..e7ee8f03b 100644 --- a/crates/ibc/src/core/ics02_client/consensus_state.rs +++ b/crates/ibc/src/core/ics02_client/consensus_state.rs @@ -8,7 +8,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::protobuf::Protobuf as ErasedProtobuf; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics23_commitment::commitment::CommitmentRoot; use crate::dynamic_typing::AsAny; use crate::timestamp::Timestamp; @@ -25,7 +25,7 @@ pub trait ConsensusState: + sealed::ErasedPartialEqConsensusState + DynClone + ErasedSerialize - + ErasedProtobuf + + ErasedProtobuf + core::fmt::Debug + Send + Sync diff --git a/crates/ibc/src/core/ics02_client/context.rs b/crates/ibc/src/core/ics02_client/context.rs index 77d0aed88..a5cfc30ba 100644 --- a/crates/ibc/src/core/ics02_client/context.rs +++ b/crates/ibc/src/core/ics02_client/context.rs @@ -9,7 +9,7 @@ use ibc_proto::google::protobuf::Any; use crate::core::ics02_client::client_state::ClientState; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::handler::ClientResult::{self, Create, Update, Upgrade}; use crate::core::ics24_host::identifier::ClientId; use crate::timestamp::Timestamp; @@ -18,13 +18,13 @@ use crate::Height; /// Defines the read-only part of ICS2 (client functions) context. pub trait ClientReader { /// Returns the ClientType for the given identifier `client_id`. - fn client_type(&self, client_id: &ClientId) -> Result; + fn client_type(&self, client_id: &ClientId) -> Result; /// Returns the ClientState for the given identifier `client_id`. - fn client_state(&self, client_id: &ClientId) -> Result, Error>; + fn client_state(&self, client_id: &ClientId) -> Result, ClientError>; /// Tries to decode the given `client_state` into a concrete light client state. - fn decode_client_state(&self, client_state: Any) -> Result, Error>; + fn decode_client_state(&self, client_state: Any) -> Result, ClientError>; /// Retrieve the consensus state for the given client ID at the specified /// height. @@ -34,27 +34,27 @@ pub trait ClientReader { &self, client_id: &ClientId, height: Height, - ) -> Result, Error>; + ) -> Result, ClientError>; /// Search for the lowest consensus state higher than `height`. fn next_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result>, Error>; + ) -> Result>, ClientError>; /// Search for the highest consensus state lower than `height`. fn prev_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result>, Error>; + ) -> Result>, ClientError>; /// Returns the current height of the local chain. - fn host_height(&self) -> Result; + fn host_height(&self) -> Result; /// Returns the current timestamp of the local chain. - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { let pending_consensus_state = self .pending_host_consensus_state() .expect("host must have pending consensus state"); @@ -62,19 +62,19 @@ pub trait ClientReader { } /// Returns the `ConsensusState` of the host (local) chain at a specific height. - fn host_consensus_state(&self, height: Height) -> Result, Error>; + fn host_consensus_state(&self, height: Height) -> Result, ClientError>; /// Returns the pending `ConsensusState` of the host (local) chain. - fn pending_host_consensus_state(&self) -> Result, Error>; + fn pending_host_consensus_state(&self) -> Result, ClientError>; /// Returns a natural number, counting how many clients have been created thus far. /// The value of this counter should increase only via method `ClientKeeper::increase_client_counter`. - fn client_counter(&self) -> Result; + fn client_counter(&self) -> Result; } /// Defines the write-only part of ICS2 (client functions) context. pub trait ClientKeeper { - fn store_client_result(&mut self, handler_res: ClientResult) -> Result<(), Error> { + fn store_client_result(&mut self, handler_res: ClientResult) -> Result<(), ClientError> { match handler_res { Create(res) => { self.store_client_type(res.client_id.clone(), res.client_type)?; @@ -133,14 +133,14 @@ pub trait ClientKeeper { &mut self, client_id: ClientId, client_type: ClientType, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Called upon successful client creation and update fn store_client_state( &mut self, client_id: ClientId, client_state: Box, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Called upon successful client creation and update fn store_consensus_state( @@ -148,7 +148,7 @@ pub trait ClientKeeper { client_id: ClientId, height: Height, consensus_state: Box, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Called upon client creation. /// Increases the counter which keeps track of how many clients have been created. @@ -163,7 +163,7 @@ pub trait ClientKeeper { client_id: ClientId, height: Height, timestamp: Timestamp, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; /// Called upon successful client update. /// Implementations are expected to use this to record the specified height as the height at @@ -173,5 +173,5 @@ pub trait ClientKeeper { client_id: ClientId, height: Height, host_height: Height, - ) -> Result<(), Error>; + ) -> Result<(), ClientError>; } diff --git a/crates/ibc/src/core/ics02_client/error.rs b/crates/ibc/src/core/ics02_client/error.rs index b4e1dd105..914c330d2 100644 --- a/crates/ibc/src/core/ics02_client/error.rs +++ b/crates/ibc/src/core/ics02_client/error.rs @@ -1,280 +1,134 @@ use crate::prelude::*; - -use flex_error::{define_error, TraceError}; +use displaydoc::Display; use ibc_proto::protobuf::Error as TendermintProtoError; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::height::HeightError; -use crate::core::ics23_commitment::error::Error as Ics23Error; +use crate::core::ics23_commitment::error::CommitmentError; use crate::core::ics24_host::error::ValidationError; use crate::core::ics24_host::identifier::ClientId; use crate::signer::SignerError; use crate::timestamp::Timestamp; use crate::Height; -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - UnknownClientType - { client_type: String } - | e | { format_args!("unknown client type: {0}", e.client_type) }, - - ClientIdentifierConstructor - { client_type: ClientType, counter: u64 } - [ ValidationError ] - | e | { - format_args!("Client identifier constructor failed for type {0} with counter {1}", - e.client_type, e.counter) - }, - - ClientAlreadyExists - { client_id: ClientId } - | e | { format_args!("client already exists: {0}", e.client_id) }, - - ClientNotFound - { client_id: ClientId } - | e | { format_args!("client not found: {0}", e.client_id) }, - - ClientFrozen - { client_id: ClientId } - | e | { format_args!("client is frozen: {0}", e.client_id) }, - - ConsensusStateNotFound - { client_id: ClientId, height: Height } - | e | { - format_args!("consensus state not found at: {0} at height {1}", - e.client_id, e.height) - }, - - ImplementationSpecific - | _ | { "implementation specific error" }, - - HeaderVerificationFailure - { reason: String } - | e | { format_args!("header verification failed with reason: {}", e.reason) }, - - InvalidTrustThreshold - { numerator: u64, denominator: u64 } - | e | { format_args!("failed to build trust threshold from fraction: {}/{}", e.numerator, e.denominator) }, - - FailedTrustThresholdConversion - { numerator: u64, denominator: u64 } - | e | { format_args!("failed to build Tendermint domain type trust threshold from fraction: {}/{}", e.numerator, e.denominator) }, - - UnknownClientStateType - { client_state_type: String } - | e | { format_args!("unknown client state type: {0}", e.client_state_type) }, - - EmptyClientStateResponse - | _ | { "the client state was not found" }, - - EmptyPrefix - | _ | { "empty prefix" }, - - UnknownConsensusStateType - { consensus_state_type: String } - | e | { - format_args!("unknown client consensus state type: {0}", - e.consensus_state_type) - }, - - EmptyConsensusStateResponse - | _ | { "the client consensus state was not found" }, - - UnknownHeaderType - { header_type: String } - | e | { - format_args!("unknown header type: {0}", - e.header_type) - }, - - UnknownMisbehaviourType - { misbehavior_type: String } - | e | { - format_args!("unknown misbehaviour type: {0}", - e.misbehavior_type) - }, - - InvalidRawClientId - { client_id: String } - [ ValidationError ] - | e | { - format_args!("invalid raw client identifier {0}", - e.client_id) - }, - - DecodeRawClientState - [ TraceError ] - | _ | { "error decoding raw client state" }, - - MissingRawClientState - | _ | { "missing raw client state" }, - - InvalidRawConsensusState - [ TraceError ] - | _ | { "invalid raw client consensus state" }, - - MissingRawConsensusState - | _ | { "missing raw client consensus state" }, - - InvalidMsgUpdateClientId - [ ValidationError ] - | _ | { "invalid client id in the update client message" }, - - Decode - [ TraceError ] - | _ | { "decode error" }, - - MissingHeight - | _ | { "invalid raw client consensus state: the height field is missing" }, - - InvalidClientIdentifier - [ ValidationError ] - | _ | { "invalid client identifier" }, - - InvalidRawHeader - [ TraceError ] - | _ | { "invalid raw header" }, - - MissingRawHeader - | _ | { "missing raw header" }, - - DecodeRawMisbehaviour - [ TraceError ] - | _ | { "invalid raw misbehaviour" }, - - InvalidRawMisbehaviour - [ ValidationError ] - | _ | { "invalid raw misbehaviour" }, - - MissingRawMisbehaviour - | _ | { "missing raw misbehaviour" }, - - InvalidStringAsHeight - { value: String } - [ HeightError ] - | e | { format_args!("String {0} cannnot be converted to height", e.value) }, - - InvalidHeight - | _ | { "revision height cannot be zero" }, - - InvalidHeightResult - | _ | { "height cannot end up zero or negative" }, - - InvalidAddress - | _ | { "invalid address" }, - - InvalidUpgradeClientProof - [ Ics23Error ] - | _ | { "invalid proof for the upgraded client state" }, - - InvalidUpgradeConsensusStateProof - [ Ics23Error ] - | _ | { "invalid proof for the upgraded consensus state" }, - - InvalidCommitmentProof - [ Ics23Error ] - | _ | { "invalid commitment proof bytes" }, - - InvalidPacketTimestamp - [ crate::timestamp::ParseTimestampError ] - | _ | { "invalid packet timeout timestamp value" }, - - ClientArgsTypeMismatch - { client_type: ClientType } - | e | { - format_args!("mismatch between client and arguments types, expected: {0:?}", - e.client_type) - }, - - InsufficientVotingPower - { reason: String } - | e | { - format_args!("Insufficient overlap {}", e.reason) - }, - - RawClientAndConsensusStateTypesMismatch - { - state_type: ClientType, - consensus_type: ClientType, - } - | e | { - format_args!("mismatch in raw client consensus state {} with expected state {}", - e.state_type, e.consensus_type) - }, - - LowHeaderHeight - { - header_height: Height, - latest_height: Height - } - | e | { - format!("received header height ({}) is lower than (or equal to) client latest height ({})", - e.header_height, e.latest_height) - }, - - LowUpgradeHeight - { - upgraded_height: Height, - client_height: Height, - } - | e | { - format_args!("upgraded client height {} must be at greater than current client height {}", - e.upgraded_height, e.client_height) - }, - - InvalidConsensusStateTimestamp - { - time1: Timestamp, - time2: Timestamp, - } - | e | { - format_args!("timestamp is invalid or missing, timestamp={0}, now={1}", e.time1, e.time2) - }, - - HeaderNotWithinTrustPeriod - { - latest_time:Timestamp, - update_time: Timestamp, - } - | e | { - format_args!("header not withing trusting period: expires_at={0} now={1}", e.latest_time, e.update_time) - }, - - MissingLocalConsensusState - { height: Height } - | e | { format_args!("the local consensus state could not be retrieved for height {}", e.height) }, - - InvalidConnectionEnd - [ TraceError] - | _ | { "invalid connection end" }, - - InvalidChannelEnd - [ TraceError] - | _ | { "invalid channel end" }, - - InvalidAnyClientState - [ TraceError] - | _ | { "invalid any client state" }, - - InvalidAnyConsensusState - [ TraceError ] - | _ | { "invalid any client consensus state" }, - - Signer - [ SignerError ] - | _ | { "failed to parse signer" }, - - Ics23Verification - [ Ics23Error ] - | _ | { "ics23 verification failure" }, - - ClientSpecific - { description: String } - | e | { format_args!("client specific error: {0}", e.description) }, +#[derive(Debug, Display)] +pub enum ClientError { + /// Client identifier constructor failed for type `{client_type}` with counter `{counter}`, validation error: `{validation_error}` + ClientIdentifierConstructor { + client_type: ClientType, + counter: u64, + validation_error: ValidationError, + }, + /// client not found: `{client_id}` + ClientNotFound { client_id: ClientId }, + /// client is frozen: `{client_id}` + ClientFrozen { client_id: ClientId }, + /// consensus state not found at: `{client_id}` at height `{height}` + ConsensusStateNotFound { client_id: ClientId, height: Height }, + /// implementation specific error + ImplementationSpecific, + /// header verification failed with reason: `{reaseon}` + HeaderVerificationFailure { reaseon: String }, + /// failed to build trust threshold from fraction: `{numerator}`/`{denominator}` + InvalidTrustThreshold { numerator: u64, denominator: u64 }, + /// failed to build Tendermint domain type trust threshold from fraction: `{numerator}`/`{denominator}` + FailedTrustThresholdConversion { numerator: u64, denominator: u64 }, + /// unknown client state type: `{client_state_type}` + UnknownClientStateType { client_state_type: String }, + /// empty prefix + EmptyPrefix, + /// unknown client consensus state type: `{consensus_state_type}` + UnknownConsensusStateType { consensus_state_type: String }, + /// unknown header type: `{header_type}` + UnknownHeaderType { header_type: String }, + /// missing raw client state + MissingRawClientState, + /// missing raw client consensus state + MissingRawConsensusState, + /// invalid client id in the update client message: `{0}` + InvalidMsgUpdateClientId(ValidationError), + /// decode error: `{0}` + Decode(prost::DecodeError), + /// invalid client identifier error: `{0}` + InvalidClientIdentifier(ValidationError), + /// invalid raw header error: `{0}` + InvalidRawHeader(TendermintProtoError), + /// missing raw header + MissingRawHeader, + /// invalid raw misbehaviour error: `{0}` + InvalidRawMisbehaviour(ValidationError), + /// missing raw misbehaviour + MissingRawMisbehaviour, + /// revision height cannot be zero + InvalidHeight, + /// height cannot end up zero or negative + InvalidHeightResult, + /// invalid proof for the upgraded client state error: `{0}` + InvalidUpgradeClientProof(CommitmentError), + /// invalid proof for the upgraded consensus state error: `{0}` + InvalidUpgradeConsensusStateProof(CommitmentError), + /// invalid commitment proof bytes error: `{0}` + InvalidCommitmentProof(CommitmentError), + /// invalid packet timeout timestamp value error: `{0}` + InvalidPacketTimestamp(crate::timestamp::ParseTimestampError), + /// mismatch between client and arguments types + ClientArgsTypeMismatch { client_type: ClientType }, + /// received header height (`{header_height}`) is lower than (or equal to) client latest height (`{latest_height}`) + LowHeaderHeight { + header_height: Height, + latest_height: Height, + }, + /// upgraded client height `{upgraded_height}` must be at greater than current client height `{client_height}` + LowUpgradeHeight { + upgraded_height: Height, + client_height: Height, + }, + /// timestamp is invalid or missing, timestamp=`{time1}`, now=`{time2}` + InvalidConsensusStateTimestamp { time1: Timestamp, time2: Timestamp }, + /// header not withing trusting period: expires_at=`{latest_time}` now=`{update_time}` + HeaderNotWithinTrustPeriod { + latest_time: Timestamp, + update_time: Timestamp, + }, + /// the local consensus state could not be retrieved for height `{height}` + MissingLocalConsensusState { height: Height }, + /// invalid connection end error: `{0}` + InvalidConnectionEnd(TendermintProtoError), + /// invalid channel end error: `{0}` + InvalidChannelEnd(TendermintProtoError), + /// invalid any client consensus state error: `{0}` + InvalidAnyConsensusState(TendermintProtoError), + /// failed to parse signer error: `{0}` + Signer(SignerError), + /// ics23 verification failure error: `{0}` + Ics23Verification(CommitmentError), + /// client specific error: `{description}` + ClientSpecific { description: String }, + /// other error: `{description}` + Other { description: String }, +} - Other - { description: String } - | e| { format_args!("other error: {0}", e.description) }, +#[cfg(feature = "std")] +impl std::error::Error for ClientError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::ClientIdentifierConstructor { + validation_error: e, + .. + } => Some(e), + Self::InvalidMsgUpdateClientId(e) => Some(e), + Self::InvalidClientIdentifier(e) => Some(e), + Self::InvalidRawHeader(e) => Some(e), + Self::InvalidRawMisbehaviour(e) => Some(e), + Self::InvalidUpgradeClientProof(e) => Some(e), + Self::InvalidUpgradeConsensusStateProof(e) => Some(e), + Self::InvalidCommitmentProof(e) => Some(e), + Self::InvalidPacketTimestamp(e) => Some(e), + Self::InvalidConnectionEnd(e) => Some(e), + Self::InvalidChannelEnd(e) => Some(e), + Self::InvalidAnyConsensusState(e) => Some(e), + Self::Signer(e) => Some(e), + Self::Ics23Verification(e) => Some(e), + _ => None, + } } } diff --git a/crates/ibc/src/core/ics02_client/handler.rs b/crates/ibc/src/core/ics02_client/handler.rs index 80ffe91b1..8b89d22a0 100644 --- a/crates/ibc/src/core/ics02_client/handler.rs +++ b/crates/ibc/src/core/ics02_client/handler.rs @@ -1,7 +1,7 @@ //! This module implements the processing logic for ICS2 (client abstractions and functions) msgs. use crate::core::ics02_client::context::ClientReader; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::msgs::ClientMsg; use crate::handler::HandlerOutput; @@ -17,7 +17,7 @@ pub enum ClientResult { } /// General entry point for processing any message related to ICS2 (client functions) protocols. -pub fn dispatch(ctx: &Ctx, msg: ClientMsg) -> Result, Error> +pub fn dispatch(ctx: &Ctx, msg: ClientMsg) -> Result, ClientError> where Ctx: ClientReader, { diff --git a/crates/ibc/src/core/ics02_client/handler/create_client.rs b/crates/ibc/src/core/ics02_client/handler/create_client.rs index 900eb5dd3..e7bac8d61 100644 --- a/crates/ibc/src/core/ics02_client/handler/create_client.rs +++ b/crates/ibc/src/core/ics02_client/handler/create_client.rs @@ -1,5 +1,6 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgCreateClient`. +use crate::core::context::ContextError; use crate::core::ics24_host::path::ClientConsensusStatePath; use crate::core::ics24_host::path::ClientStatePath; use crate::core::ics24_host::path::ClientTypePath; @@ -12,7 +13,7 @@ use crate::core::ics02_client::client_state::ClientState; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics02_client::context::ClientReader; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::events::CreateClient; use crate::core::ics02_client::handler::ClientResult; use crate::core::ics02_client::height::Height; @@ -34,7 +35,7 @@ pub struct CreateClientResult { pub processed_height: Height, } -pub(crate) fn validate(ctx: &Ctx, msg: MsgCreateClient) -> Result<(), Error> +pub(crate) fn validate(ctx: &Ctx, msg: MsgCreateClient) -> Result<(), ContextError> where Ctx: ValidationContext, { @@ -52,13 +53,17 @@ where let client_type = client_state.client_type(); let _client_id = ClientId::new(client_type, id_counter).map_err(|e| { - Error::client_identifier_constructor(client_state.client_type(), id_counter, e) + ClientError::ClientIdentifierConstructor { + client_type: client_state.client_type(), + counter: id_counter, + validation_error: e, + } })?; Ok(()) } -pub(crate) fn execute(ctx: &mut Ctx, msg: MsgCreateClient) -> Result<(), Error> +pub(crate) fn execute(ctx: &mut Ctx, msg: MsgCreateClient) -> Result<(), ContextError> where Ctx: ExecutionContext, { @@ -76,7 +81,11 @@ where let client_type = client_state.client_type(); let client_id = ClientId::new(client_type.clone(), id_counter).map_err(|e| { - Error::client_identifier_constructor(client_state.client_type(), id_counter, e) + ContextError::from(ClientError::ClientIdentifierConstructor { + client_type: client_state.client_type(), + counter: id_counter, + validation_error: e, + }) })?; let consensus_state = client_state.initialise(consensus_state)?; @@ -112,7 +121,10 @@ where Ok(()) } -pub fn process(ctx: &dyn ClientReader, msg: MsgCreateClient) -> HandlerResult { +pub fn process( + ctx: &dyn ClientReader, + msg: MsgCreateClient, +) -> HandlerResult { let mut output = HandlerOutput::builder(); let MsgCreateClient { @@ -129,7 +141,11 @@ pub fn process(ctx: &dyn ClientReader, msg: MsgCreateClient) -> HandlerResult(ctx: &Ctx, msg: MsgUpdateClient) -> Result<(), Error> +pub(crate) fn validate(ctx: &Ctx, msg: MsgUpdateClient) -> Result<(), ContextError> where Ctx: ValidationContext, { @@ -44,14 +45,15 @@ where let client_state = ctx.client_state(&client_id)?; if client_state.is_frozen() { - return Err(Error::client_frozen(client_id)); + return Err(ClientError::ClientFrozen { client_id }.into()); } // Read consensus state from the host chain store. let latest_consensus_state = ctx .consensus_state(&client_id, client_state.latest_height()) - .map_err(|_| { - Error::consensus_state_not_found(client_id.clone(), client_state.latest_height()) + .map_err(|_| ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: client_state.latest_height(), })?; debug!("latest consensus state: {:?}", latest_consensus_state); @@ -59,15 +61,17 @@ where let now = ctx.host_timestamp()?; let duration = now .duration_since(&latest_consensus_state.timestamp()) - .ok_or_else(|| { - Error::invalid_consensus_state_timestamp(latest_consensus_state.timestamp(), now) + .ok_or_else(|| ClientError::InvalidConsensusStateTimestamp { + time1: latest_consensus_state.timestamp(), + time2: now, })?; if client_state.expired(duration) { - return Err(Error::header_not_within_trust_period( - latest_consensus_state.timestamp(), - now, - )); + return Err(ClientError::HeaderNotWithinTrustPeriod { + latest_time: latest_consensus_state.timestamp(), + update_time: now, + } + .into()); } // Use client_state to validate the new header against the latest consensus_state. @@ -75,12 +79,14 @@ where // consensus_state obtained from header. These will be later persisted by the keeper. let _ = client_state .check_header_and_update_state(ctx, client_id.clone(), header) - .map_err(|e| Error::header_verification_failure(e.to_string()))?; + .map_err(|e| ClientError::HeaderVerificationFailure { + reaseon: e.to_string(), + })?; Ok(()) } -pub(crate) fn execute(ctx: &mut Ctx, msg: MsgUpdateClient) -> Result<(), Error> +pub(crate) fn execute(ctx: &mut Ctx, msg: MsgUpdateClient) -> Result<(), ContextError> where Ctx: ExecutionContext, { @@ -99,7 +105,9 @@ where consensus_state, } = client_state .check_header_and_update_state(ctx, client_id.clone(), header.clone()) - .map_err(|e| Error::header_verification_failure(e.to_string()))?; + .map_err(|e| ClientError::HeaderVerificationFailure { + reaseon: e.to_string(), + })?; ctx.store_client_state(ClientStatePath(client_id.clone()), client_state.clone())?; ctx.store_consensus_state( @@ -135,7 +143,7 @@ where pub fn process( ctx: &Ctx, msg: MsgUpdateClient, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let MsgUpdateClient { @@ -149,13 +157,16 @@ pub fn process( let client_state = ctx.client_state(&client_id)?; if client_state.is_frozen() { - return Err(Error::client_frozen(client_id)); + return Err(ClientError::ClientFrozen { client_id }); } // Read consensus state from the host chain store. let latest_consensus_state = ClientReader::consensus_state(ctx, &client_id, client_state.latest_height()).map_err( - |_| Error::consensus_state_not_found(client_id.clone(), client_state.latest_height()), + |_| ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: client_state.latest_height(), + }, )?; debug!("latest consensus state: {:?}", latest_consensus_state); @@ -163,15 +174,16 @@ pub fn process( let now = ClientReader::host_timestamp(ctx)?; let duration = now .duration_since(&latest_consensus_state.timestamp()) - .ok_or_else(|| { - Error::invalid_consensus_state_timestamp(latest_consensus_state.timestamp(), now) + .ok_or_else(|| ClientError::InvalidConsensusStateTimestamp { + time1: latest_consensus_state.timestamp(), + time2: now, })?; if client_state.expired(duration) { - return Err(Error::header_not_within_trust_period( - latest_consensus_state.timestamp(), - now, - )); + return Err(ClientError::HeaderNotWithinTrustPeriod { + latest_time: latest_consensus_state.timestamp(), + update_time: now, + }); } // Use client_state to validate the new header against the latest consensus_state. @@ -182,7 +194,9 @@ pub fn process( consensus_state, } = client_state .old_check_header_and_update_state(ctx, client_id.clone(), header.clone()) - .map_err(|e| Error::header_verification_failure(e.to_string()))?; + .map_err(|e| ClientError::HeaderVerificationFailure { + reaseon: e.to_string(), + })?; let client_type = client_state.client_type(); let consensus_height = client_state.latest_height(); @@ -216,7 +230,7 @@ mod tests { use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; use crate::core::ics02_client::client_state::ClientState; use crate::core::ics02_client::consensus_state::downcast_consensus_state; - use crate::core::ics02_client::error::{Error, ErrorDetail}; + use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::handler::dispatch; use crate::core::ics02_client::handler::ClientResult::Update; use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; @@ -272,7 +286,7 @@ mod tests { } } Err(err) => { - panic!("unexpected error: {}", err); + panic!("unexpected error: {:?}", err); } } } @@ -293,8 +307,8 @@ mod tests { let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); match output { - Err(Error(ErrorDetail::ClientNotFound(e), _)) => { - assert_eq!(e.client_id, msg.client_id); + Err(ClientError::ClientNotFound { client_id }) => { + assert_eq!(client_id, msg.client_id); } _ => { panic!("expected ClientNotFound error, instead got {:?}", output) @@ -337,7 +351,7 @@ mod tests { assert!(log.is_empty()); } Err(err) => { - panic!("unexpected error: {}", err); + panic!("unexpected error: {:?}", err); } } } @@ -401,7 +415,7 @@ mod tests { } } Err(err) => { - panic!("unexpected error: {}", err); + panic!("unexpected error: {:?}", err); } } } @@ -465,7 +479,7 @@ mod tests { } } Err(err) => { - panic!("unexpected error: {}", err); + panic!("unexpected error: {:?}", err); } } } @@ -591,8 +605,8 @@ mod tests { Ok(_) => { panic!("update handler result has incorrect type"); } - Err(err) => match err.detail() { - ErrorDetail::HeaderVerificationFailure(_) => {} + Err(err) => match err { + ClientError::HeaderVerificationFailure { reaseon: _ } => {} _ => panic!("unexpected error: {:?}", err), }, } diff --git a/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs b/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs index 53f510045..67653a7be 100644 --- a/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs +++ b/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs @@ -1,9 +1,10 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgUpgradeAnyClient`. //! +use crate::core::context::ContextError; use crate::core::ics02_client::client_state::{ClientState, UpdatedState}; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics02_client::context::ClientReader; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::events::UpgradeClient; use crate::core::ics02_client::handler::ClientResult; use crate::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; @@ -23,7 +24,7 @@ pub struct UpgradeClientResult { pub consensus_state: Box, } -pub(crate) fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), Error> +pub(crate) fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), ContextError> where Ctx: ValidationContext, { @@ -33,22 +34,23 @@ where let old_client_state = ctx.client_state(&client_id)?; if old_client_state.is_frozen() { - return Err(Error::client_frozen(client_id)); + return Err(ClientError::ClientFrozen { client_id }.into()); } let upgrade_client_state = ctx.decode_client_state(msg.client_state)?; if old_client_state.latest_height() >= upgrade_client_state.latest_height() { - return Err(Error::low_upgrade_height( - old_client_state.latest_height(), - upgrade_client_state.latest_height(), - )); + return Err(ClientError::LowUpgradeHeight { + upgraded_height: old_client_state.latest_height(), + client_height: upgrade_client_state.latest_height(), + } + .into()); } Ok(()) } -pub(crate) fn execute(ctx: &mut Ctx, msg: MsgUpgradeClient) -> Result<(), Error> +pub(crate) fn execute(ctx: &mut Ctx, msg: MsgUpgradeClient) -> Result<(), ContextError> where Ctx: ExecutionContext, { @@ -86,7 +88,7 @@ where pub fn process( ctx: &dyn ClientReader, msg: MsgUpgradeClient, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let MsgUpgradeClient { client_id, .. } = msg; @@ -94,16 +96,16 @@ pub fn process( let old_client_state = ctx.client_state(&client_id)?; if old_client_state.is_frozen() { - return Err(Error::client_frozen(client_id)); + return Err(ClientError::ClientFrozen { client_id }); } let upgrade_client_state = ctx.decode_client_state(msg.client_state)?; if old_client_state.latest_height() >= upgrade_client_state.latest_height() { - return Err(Error::low_upgrade_height( - old_client_state.latest_height(), - upgrade_client_state.latest_height(), - )); + return Err(ClientError::LowUpgradeHeight { + upgraded_height: old_client_state.latest_height(), + client_height: upgrade_client_state.latest_height(), + }); } let UpdatedState { @@ -143,7 +145,7 @@ mod tests { use core::str::FromStr; - use crate::core::ics02_client::error::{Error, ErrorDetail}; + use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::handler::dispatch; use crate::core::ics02_client::handler::ClientResult::Upgrade; use crate::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; @@ -219,8 +221,8 @@ mod tests { let output = dispatch(&ctx, ClientMsg::UpgradeClient(msg.clone())); match output { - Err(Error(ErrorDetail::ClientNotFound(e), _)) => { - assert_eq!(e.client_id, msg.client_id); + Err(ClientError::ClientNotFound { client_id }) => { + assert_eq!(client_id, msg.client_id); } _ => { panic!("expected ClientNotFound error, instead got {:?}", output); @@ -248,10 +250,13 @@ mod tests { let output = dispatch(&ctx, ClientMsg::UpgradeClient(msg.clone())); match output { - Err(Error(ErrorDetail::LowUpgradeHeight(e), _)) => { - assert_eq!(e.upgraded_height, Height::new(0, 42).unwrap()); + Err(ClientError::LowUpgradeHeight { + upgraded_height, + client_height, + }) => { + assert_eq!(upgraded_height, Height::new(0, 42).unwrap()); assert_eq!( - e.client_height, + client_height, MockClientState::try_from(msg.client_state) .unwrap() .latest_height() diff --git a/crates/ibc/src/core/ics02_client/header.rs b/crates/ibc/src/core/ics02_client/header.rs index 99f05c886..0174ddea1 100644 --- a/crates/ibc/src/core/ics02_client/header.rs +++ b/crates/ibc/src/core/ics02_client/header.rs @@ -6,7 +6,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::protobuf::Protobuf as ErasedProtobuf; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::dynamic_typing::AsAny; use crate::timestamp::Timestamp; use crate::Height; @@ -22,7 +22,7 @@ pub trait Header: + sealed::ErasedPartialEqHeader + DynClone + ErasedSerialize - + ErasedProtobuf + + ErasedProtobuf + core::fmt::Debug + Send + Sync diff --git a/crates/ibc/src/core/ics02_client/height.rs b/crates/ibc/src/core/ics02_client/height.rs index 9dee17454..08d30d67c 100644 --- a/crates/ibc/src/core/ics02_client/height.rs +++ b/crates/ibc/src/core/ics02_client/height.rs @@ -4,13 +4,13 @@ use core::cmp::Ordering; use core::num::ParseIntError; use core::str::FromStr; -use flex_error::{define_error, TraceError}; +use displaydoc::Display; use ibc_proto::protobuf::Protobuf; use serde_derive::{Deserialize, Serialize}; use ibc_proto::ibc::core::client::v1::Height as RawHeight; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; #[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Height { @@ -22,9 +22,9 @@ pub struct Height { } impl Height { - pub fn new(revision_number: u64, revision_height: u64) -> Result { + pub fn new(revision_number: u64, revision_height: u64) -> Result { if revision_height == 0 { - return Err(Error::invalid_height()); + return Err(ClientError::InvalidHeight); } Ok(Self { @@ -52,9 +52,9 @@ impl Height { self.add(1) } - pub fn sub(&self, delta: u64) -> Result { + pub fn sub(&self, delta: u64) -> Result { if self.revision_height <= delta { - return Err(Error::invalid_height_result()); + return Err(ClientError::InvalidHeightResult); } Ok(Height { @@ -63,7 +63,7 @@ impl Height { }) } - pub fn decrement(&self) -> Result { + pub fn decrement(&self) -> Result { self.sub(1) } } @@ -93,7 +93,7 @@ impl Ord for Height { impl Protobuf for Height {} impl TryFrom for Height { - type Error = Error; + type Error = ClientError; fn try_from(raw_height: RawHeight) -> Result { Height::new(raw_height.revision_number, raw_height.revision_height) @@ -125,18 +125,24 @@ impl core::fmt::Display for Height { } } -define_error! { - #[derive(Debug, PartialEq, Eq)] - HeightError { - HeightConversion - { height: String } - [ TraceError ] - | e | { - format_args!("cannot convert into a `Height` type from string {0}", - e.height) - }, - ZeroHeight - |_| { "attempted to parse an invalid zero height" } +#[derive(Debug, Display)] +pub enum HeightError { + /// cannot convert into a `Height` type from string `{height}` + HeightConversion { + height: String, + error: ParseIntError, + }, + /// attempted to parse an invalid zero height + ZeroHeight, +} + +#[cfg(feature = "std")] +impl std::error::Error for HeightError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + HeightError::HeightConversion { error: e, .. } => Some(e), + HeightError::ZeroHeight => None, + } } } @@ -146,14 +152,22 @@ impl TryFrom<&str> for Height { fn try_from(value: &str) -> Result { let split: Vec<&str> = value.split('-').collect(); - let revision_number = split[0] - .parse::() - .map_err(|e| HeightError::height_conversion(value.to_owned(), e))?; - let revision_height = split[1] - .parse::() - .map_err(|e| HeightError::height_conversion(value.to_owned(), e))?; - - Height::new(revision_number, revision_height).map_err(|_| HeightError::zero_height()) + let revision_number = + split[0] + .parse::() + .map_err(|e| HeightError::HeightConversion { + height: value.to_owned(), + error: e, + })?; + let revision_height = + split[1] + .parse::() + .map_err(|e| HeightError::HeightConversion { + height: value.to_owned(), + error: e, + })?; + + Height::new(revision_number, revision_height).map_err(|_| HeightError::ZeroHeight) } } diff --git a/crates/ibc/src/core/ics02_client/msgs/create_client.rs b/crates/ibc/src/core/ics02_client/msgs/create_client.rs index 496e28638..d3210cb0f 100644 --- a/crates/ibc/src/core/ics02_client/msgs/create_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/create_client.rs @@ -6,7 +6,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient; use ibc_proto::protobuf::Protobuf; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::signer::Signer; use crate::tx_msg::Msg; @@ -21,7 +21,11 @@ pub struct MsgCreateClient { } impl MsgCreateClient { - pub fn new(client_state: Any, consensus_state: Any, signer: Signer) -> Result { + pub fn new( + client_state: Any, + consensus_state: Any, + signer: Signer, + ) -> Result { Ok(MsgCreateClient { client_state, consensus_state, @@ -46,21 +50,19 @@ impl Msg for MsgCreateClient { impl Protobuf for MsgCreateClient {} impl TryFrom for MsgCreateClient { - type Error = Error; + type Error = ClientError; - fn try_from(raw: RawMsgCreateClient) -> Result { - let raw_client_state = raw - .client_state - .ok_or_else(Error::missing_raw_client_state)?; + fn try_from(raw: RawMsgCreateClient) -> Result { + let raw_client_state = raw.client_state.ok_or(ClientError::MissingRawClientState)?; let raw_consensus_state = raw .consensus_state - .ok_or_else(Error::missing_raw_client_state)?; + .ok_or(ClientError::MissingRawConsensusState)?; MsgCreateClient::new( raw_client_state, raw_consensus_state, - raw.signer.parse().map_err(Error::signer)?, + raw.signer.parse().map_err(ClientError::Signer)?, ) } } diff --git a/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs b/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs index 07923bae8..51bc0011c 100644 --- a/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs +++ b/crates/ibc/src/core/ics02_client/msgs/misbehaviour.rs @@ -4,7 +4,7 @@ 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::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics24_host::identifier::ClientId; use crate::signer::Signer; use crate::tx_msg::Msg; @@ -38,20 +38,20 @@ impl Msg for MsgSubmitMisbehaviour { impl Protobuf for MsgSubmitMisbehaviour {} impl TryFrom for MsgSubmitMisbehaviour { - type Error = Error; + type Error = ClientError; fn try_from(raw: RawMsgSubmitMisbehaviour) -> Result { let raw_misbehaviour = raw .misbehaviour - .ok_or_else(Error::missing_raw_misbehaviour)?; + .ok_or(ClientError::MissingRawMisbehaviour)?; Ok(MsgSubmitMisbehaviour { client_id: raw .client_id .parse() - .map_err(Error::invalid_raw_misbehaviour)?, + .map_err(ClientError::InvalidRawMisbehaviour)?, misbehaviour: raw_misbehaviour, - signer: raw.signer.parse().map_err(Error::signer)?, + signer: raw.signer.parse().map_err(ClientError::Signer)?, }) } } 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 dcc3dc975..d2fd43609 100644 --- a/crates/ibc/src/core/ics02_client/msgs/update_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/update_client.rs @@ -6,7 +6,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; use ibc_proto::protobuf::Protobuf; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics24_host::error::ValidationError; use crate::core::ics24_host::identifier::ClientId; use crate::signer::Signer; @@ -48,16 +48,16 @@ impl Msg for MsgUpdateClient { impl Protobuf for MsgUpdateClient {} impl TryFrom for MsgUpdateClient { - type Error = Error; + type Error = ClientError; fn try_from(raw: RawMsgUpdateClient) -> Result { Ok(MsgUpdateClient { client_id: raw .client_id .parse() - .map_err(Error::invalid_msg_update_client_id)?, - header: raw.header.ok_or_else(Error::missing_raw_header)?, - signer: raw.signer.parse().map_err(Error::signer)?, + .map_err(ClientError::InvalidMsgUpdateClientId)?, + header: raw.header.ok_or(ClientError::MissingRawHeader)?, + signer: raw.signer.parse().map_err(ClientError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs b/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs index f8d1b9fa1..594af2edb 100644 --- a/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs @@ -9,9 +9,9 @@ use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; use ibc_proto::ibc::core::commitment::v1::MerkleProof as RawMerkleProof; use ibc_proto::protobuf::Protobuf; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; -use crate::core::ics23_commitment::error::Error as Ics23Error; +use crate::core::ics23_commitment::error::CommitmentError; use crate::core::ics24_host::identifier::ClientId; use crate::signer::Signer; use crate::tx_msg::Msg; @@ -83,34 +83,36 @@ impl From for RawMsgUpgradeClient { } impl TryFrom for MsgUpgradeClient { - type Error = Error; + type Error = ClientError; fn try_from(proto_msg: RawMsgUpgradeClient) -> Result { let raw_client_state = proto_msg .client_state - .ok_or_else(Error::missing_raw_client_state)?; + .ok_or(ClientError::MissingRawClientState)?; let raw_consensus_state = proto_msg .consensus_state - .ok_or_else(Error::missing_raw_client_state)?; + .ok_or(ClientError::MissingRawConsensusState)?; - let c_bytes = CommitmentProofBytes::try_from(proto_msg.proof_upgrade_client) - .map_err(|_| Error::invalid_upgrade_client_proof(Ics23Error::empty_merkle_proof()))?; + let c_bytes = + CommitmentProofBytes::try_from(proto_msg.proof_upgrade_client).map_err(|_| { + ClientError::InvalidUpgradeClientProof(CommitmentError::EmptyMerkleProof) + })?; let cs_bytes = CommitmentProofBytes::try_from(proto_msg.proof_upgrade_consensus_state) .map_err(|_| { - Error::invalid_upgrade_consensus_state_proof(Ics23Error::empty_merkle_proof()) + ClientError::InvalidUpgradeConsensusStateProof(CommitmentError::EmptyMerkleProof) })?; Ok(MsgUpgradeClient { client_id: ClientId::from_str(&proto_msg.client_id) - .map_err(Error::invalid_client_identifier)?, + .map_err(ClientError::InvalidClientIdentifier)?, client_state: raw_client_state, consensus_state: raw_consensus_state, proof_upgrade_client: RawMerkleProof::try_from(c_bytes) - .map_err(Error::invalid_upgrade_client_proof)?, + .map_err(ClientError::InvalidUpgradeClientProof)?, proof_upgrade_consensus_state: RawMerkleProof::try_from(cs_bytes) - .map_err(Error::invalid_upgrade_consensus_state_proof)?, - signer: proto_msg.signer.parse().map_err(Error::signer)?, + .map_err(ClientError::InvalidUpgradeConsensusStateProof)?, + signer: proto_msg.signer.parse().map_err(ClientError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics02_client/trust_threshold.rs b/crates/ibc/src/core/ics02_client/trust_threshold.rs index 4202b6611..dd6cfeb64 100644 --- a/crates/ibc/src/core/ics02_client/trust_threshold.rs +++ b/crates/ibc/src/core/ics02_client/trust_threshold.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use ibc_proto::ibc::lightclients::tendermint::v1::Fraction; use tendermint::trust_threshold::TrustThresholdFraction; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; /// [`TrustThreshold`] defines the level of trust that a client has /// towards a set of validators of a chain. @@ -53,13 +53,16 @@ impl TrustThreshold { /// /// The constructor succeeds if long as the resulting fraction /// is in the range`[0, 1)`. - pub fn new(numerator: u64, denominator: u64) -> Result { + pub fn new(numerator: u64, denominator: u64) -> Result { // The two parameters cannot yield a fraction that is bigger or equal to 1 if (numerator > denominator) || (denominator == 0 && numerator != 0) || (numerator == denominator && numerator != 0) { - return Err(Error::invalid_trust_threshold(numerator, denominator)); + return Err(ClientError::InvalidTrustThreshold { + numerator, + denominator, + }); } Ok(Self { @@ -93,11 +96,15 @@ impl From for TrustThreshold { /// Conversion from IBC domain type into /// Tendermint domain type. impl TryFrom for TrustThresholdFraction { - type Error = Error; - - fn try_from(t: TrustThreshold) -> Result { - Self::new(t.numerator, t.denominator) - .map_err(|_| Error::failed_trust_threshold_conversion(t.numerator, t.denominator)) + type Error = ClientError; + + fn try_from(t: TrustThreshold) -> Result { + Self::new(t.numerator, t.denominator).map_err(|_| { + ClientError::FailedTrustThresholdConversion { + numerator: t.numerator, + denominator: t.denominator, + } + }) } } @@ -113,7 +120,7 @@ impl From for Fraction { } impl TryFrom for TrustThreshold { - type Error = Error; + type Error = ClientError; fn try_from(value: Fraction) -> Result { Self::new(value.numerator, value.denominator) diff --git a/crates/ibc/src/core/ics03_connection/connection.rs b/crates/ibc/src/core/ics03_connection/connection.rs index 7b0a84afa..8c7fd76e4 100644 --- a/crates/ibc/src/core/ics03_connection/connection.rs +++ b/crates/ibc/src/core/ics03_connection/connection.rs @@ -15,8 +15,8 @@ use ibc_proto::ibc::core::connection::v1::{ IdentifiedConnection as RawIdentifiedConnection, }; -use crate::core::ics02_client::error::Error as ClientError; -use crate::core::ics03_connection::error::Error; +use crate::core::ics02_client::error::ClientError; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::version::Version; use crate::core::ics23_commitment::commitment::CommitmentPrefix; use crate::core::ics24_host::error::ValidationError; @@ -49,7 +49,7 @@ impl IdentifiedConnectionEnd { impl Protobuf for IdentifiedConnectionEnd {} impl TryFrom for IdentifiedConnectionEnd { - type Error = Error; + type Error = ConnectionError; fn try_from(value: RawIdentifiedConnection) -> Result { let raw_connection_end = RawConnectionEnd { @@ -61,7 +61,10 @@ impl TryFrom for IdentifiedConnectionEnd { }; Ok(IdentifiedConnectionEnd { - connection_id: value.id.parse().map_err(Error::invalid_identifier)?, + connection_id: value + .id + .parse() + .map_err(ConnectionError::InvalidIdentifier)?, connection_end: raw_connection_end.try_into()?, }) } @@ -109,22 +112,25 @@ impl Default for ConnectionEnd { impl Protobuf for ConnectionEnd {} impl TryFrom for ConnectionEnd { - type Error = Error; + type Error = ConnectionError; fn try_from(value: RawConnectionEnd) -> Result { let state = value.state.try_into()?; if state == State::Uninitialized { return Ok(ConnectionEnd::default()); } if value.client_id.is_empty() { - return Err(Error::empty_proto_connection_end()); + return Err(ConnectionError::EmptyProtoConnectionEnd); } Ok(Self::new( state, - value.client_id.parse().map_err(Error::invalid_identifier)?, + value + .client_id + .parse() + .map_err(ConnectionError::InvalidIdentifier)?, value .counterparty - .ok_or_else(Error::missing_counterparty)? + .ok_or(ConnectionError::MissingCounterparty)? .try_into()?, value .versions @@ -253,23 +259,26 @@ 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 = Error; + type Error = ConnectionError; fn try_from(value: RawCounterparty) -> Result { let connection_id = Some(value.connection_id) .filter(|x| !x.is_empty()) .map(|v| FromStr::from_str(v.as_str())) .transpose() - .map_err(Error::invalid_identifier)?; + .map_err(ConnectionError::InvalidIdentifier)?; Ok(Counterparty::new( - value.client_id.parse().map_err(Error::invalid_identifier)?, + value + .client_id + .parse() + .map_err(ConnectionError::InvalidIdentifier)?, connection_id, value .prefix - .ok_or_else(Error::missing_counterparty)? + .ok_or(ConnectionError::MissingCounterparty)? .key_prefix .try_into() - .map_err(|_| Error::ics02_client(ClientError::empty_prefix()))?, + .map_err(|_| ConnectionError::Client(ClientError::EmptyPrefix))?, )) } } @@ -340,13 +349,13 @@ impl State { } /// Parses the State out from a i32. - pub fn from_i32(s: i32) -> Result { + 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(Error::invalid_state(s)), + _ => Err(ConnectionError::InvalidState { state: s }), } } @@ -376,14 +385,14 @@ impl Display for State { } impl TryFrom for State { - type Error = Error; + type Error = ConnectionError; 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(Error::invalid_state(value)), + _ => Err(ConnectionError::InvalidState { state: value }), } } } diff --git a/crates/ibc/src/core/ics03_connection/context.rs b/crates/ibc/src/core/ics03_connection/context.rs index 3e33775ed..ac31e1563 100644 --- a/crates/ibc/src/core/ics03_connection/context.rs +++ b/crates/ibc/src/core/ics03_connection/context.rs @@ -5,7 +5,7 @@ use crate::core::ics02_client::client_state::ClientState; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics03_connection::connection::ConnectionEnd; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::handler::ConnectionResult; use crate::core::ics03_connection::version::{get_compatible_versions, pick_version, Version}; use crate::core::ics23_commitment::commitment::CommitmentPrefix; @@ -19,20 +19,23 @@ use super::handler::ConnectionIdState; /// A context supplying all the necessary read-only dependencies for processing any `ConnectionMsg`. pub trait ConnectionReader { /// Returns the ConnectionEnd for the given identifier `conn_id`. - fn connection_end(&self, conn_id: &ConnectionId) -> Result; + fn connection_end(&self, conn_id: &ConnectionId) -> Result; /// Returns the ClientState for the given identifier `client_id`. - fn client_state(&self, client_id: &ClientId) -> Result, Error>; + fn client_state(&self, client_id: &ClientId) -> Result, ConnectionError>; /// Tries to decode the given `client_state` into a concrete light client state. - fn decode_client_state(&self, client_state: Any) -> Result, Error>; + fn decode_client_state( + &self, + client_state: Any, + ) -> Result, ConnectionError>; /// Returns the current height of the local chain. - fn host_current_height(&self) -> Result; + fn host_current_height(&self) -> Result; #[deprecated(since = "0.20.0")] /// Returns the oldest height available on the local chain. - fn host_oldest_height(&self) -> Result; + fn host_oldest_height(&self) -> Result; /// Returns the prefix that the local chain uses in the KV store. fn commitment_prefix(&self) -> CommitmentPrefix; @@ -42,10 +45,13 @@ pub trait ConnectionReader { &self, client_id: &ClientId, height: Height, - ) -> Result, Error>; + ) -> Result, ConnectionError>; /// Returns the ConsensusState of the host (local) chain at a specific height. - fn host_consensus_state(&self, height: Height) -> Result, Error>; + fn host_consensus_state( + &self, + height: Height, + ) -> Result, ConnectionError>; /// Function required by ICS 03. Returns the list of all possible versions that the connection /// handshake protocol supports. @@ -59,23 +65,23 @@ pub trait ConnectionReader { &self, supported_versions: Vec, counterparty_candidate_versions: Vec, - ) -> Result { + ) -> Result { pick_version(supported_versions, counterparty_candidate_versions) } /// Returns a counter on how many connections have been created thus far. /// The value of this counter should increase only via method /// `ConnectionKeeper::increase_connection_counter`. - fn connection_counter(&self) -> Result; + fn connection_counter(&self) -> Result; /// Validates the `ClientState` of the client on the counterparty chain. - fn validate_self_client(&self, counterparty_client_state: Any) -> Result<(), Error>; + fn validate_self_client(&self, counterparty_client_state: Any) -> Result<(), ConnectionError>; } /// A context supplying all the necessary write-only dependencies (i.e., storage writing facility) /// for processing any `ConnectionMsg`. pub trait ConnectionKeeper { - fn store_connection_result(&mut self, result: ConnectionResult) -> Result<(), Error> { + fn store_connection_result(&mut self, result: ConnectionResult) -> Result<(), ConnectionError> { self.store_connection(result.connection_id.clone(), &result.connection_end)?; // If we generated an identifier, increase the counter & associate this new identifier @@ -98,14 +104,14 @@ pub trait ConnectionKeeper { &mut self, connection_id: ConnectionId, connection_end: &ConnectionEnd, - ) -> Result<(), Error>; + ) -> Result<(), ConnectionError>; /// Stores the given connection_id at a path associated with the client_id. fn store_connection_to_client( &mut self, connection_id: ConnectionId, client_id: &ClientId, - ) -> Result<(), Error>; + ) -> Result<(), ConnectionError>; /// Called upon connection identifier creation (Init or Try process). /// Increases the counter which keeps track of how many connections have been created. diff --git a/crates/ibc/src/core/ics03_connection/error.rs b/crates/ibc/src/core/ics03_connection/error.rs index 6acd3921e..0b7da9b82 100644 --- a/crates/ibc/src/core/ics03_connection/error.rs +++ b/crates/ibc/src/core/ics03_connection/error.rs @@ -7,168 +7,84 @@ use crate::signer::SignerError; use crate::Height; use alloc::string::String; -use flex_error::define_error; - -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - Ics02Client - [ client_error::Error ] - | _ | { "ics02 client error" }, - - InvalidState - { state: i32 } - | e | { format_args!("connection state is unknown: {}", e.state) }, - - ConnectionExistsAlready - { connection_id: ConnectionId } - | e | { - format_args!("connection exists (was initialized) already: {0}", - e.connection_id) - }, - - ConnectionMismatch - { connection_id: ConnectionId } - | e | { - format_args!("connection end for identifier {0} was never initialized", - e.connection_id) - }, - - InvalidConsensusHeight - { - target_height: Height, - currrent_height: Height - } - | e | { - format_args!("consensus height claimed by the client on the other party is too advanced: {0} (host chain current height: {1})", - e.target_height, e.currrent_height) - }, - - StaleConsensusHeight - { - target_height: Height, - oldest_height: Height - } - | e | { - format_args!("consensus height claimed by the client on the other party has been pruned: {0} (host chain oldest height: {1})", - e.target_height, e.oldest_height) - }, - - InvalidIdentifier - [ ValidationError ] - | _ | { "identifier error" }, - - EmptyProtoConnectionEnd - | _ | { "ConnectionEnd domain object could not be constructed out of empty proto object" }, - - EmptyVersions - | _ | { "empty supported versions" }, - - EmptyFeatures - | _ | { "empty supported features" }, - - NoCommonVersion - | _ | { "no common version" }, - - VersionNotSupported - { - version: Version, - } - | e | { format_args!("version \"{}\" not supported", e.version) }, - - InvalidAddress - | _ | { "invalid address" }, - - MissingProofHeight - | _ | { "missing proof height" }, - - MissingConsensusHeight - | _ | { "missing consensus height" }, - - InvalidProof - [ ProofError ] - | _ | { "invalid connection proof" }, - - VerifyConnectionState - [ client_error::Error ] - | _ | { "error verifying connnection state" }, - - Signer - [ SignerError ] - | _ | { "invalid signer" }, - - ConnectionNotFound - { connection_id: ConnectionId } - | e | { - format_args!("no connection was found for the previous connection id provided {0}", - e.connection_id) - }, - - InvalidCounterparty - | _ | { "invalid signer" }, - - ConnectionIdMismatch - { - connection_id: ConnectionId, - counterparty_connection_id: ConnectionId, - } - | e | { - format_args!("counterparty chosen connection id {0} is different than the connection id {1}", - e.connection_id, e.counterparty_connection_id) - }, - - MissingCounterparty - | _ | { "missing counterparty" }, - - - MissingCounterpartyPrefix - | _ | { "missing counterparty prefix" }, - MissingClientState - | _ | { "missing client state" }, - - NullClientProof - | _ | { "client proof must be present" }, - - FrozenClient - { client_id: ClientId } - | e | { - format_args!("the client id does not match any client state: {0}", - e.client_id) - }, - - ConnectionVerificationFailure - | _ | { "the connection proof verification failed" }, - - ConsensusStateVerificationFailure - { height: Height } - [ client_error::Error ] - | e | { - format_args!("the consensus proof verification failed (height: {0})", - e.height) - }, - +use displaydoc::Display; + +#[derive(Debug, Display)] +pub enum ConnectionError { + /// client error: `{0}` + Client(client_error::ClientError), + /// connection state is unknown: `{state}` + InvalidState { state: i32 }, + /// connection end for identifier `{connection_id}` was never initialized + ConnectionMismatch { connection_id: ConnectionId }, + /// consensus height claimed by the client on the other party is too advanced: `{target_height}` (host chain current height: `{current_height}`) + InvalidConsensusHeight { + target_height: Height, + current_height: Height, + }, + /// identifier error: `{0}` + InvalidIdentifier(ValidationError), + /// ConnectionEnd domain object could not be constructed out of empty proto object + EmptyProtoConnectionEnd, + /// empty supported versions + EmptyVersions, + /// empty supported features + EmptyFeatures, + /// no common version + NoCommonVersion, + /// version \"`{version}`\" not supported + VersionNotSupported { version: Version }, + /// missing proof height + MissingProofHeight, + /// missing consensus height + MissingConsensusHeight, + /// invalid connection proof error: `{0}` + InvalidProof(ProofError), + /// verifying connnection state error: `{0}` + VerifyConnectionState(client_error::ClientError), + /// invalid signer error: `{0}` + Signer(SignerError), + /// no connection was found for the previous connection id provided `{connection_id}` + ConnectionNotFound { connection_id: ConnectionId }, + /// invalid counterparty + InvalidCounterparty, + /// missing counterparty + MissingCounterparty, + /// missing client state + MissingClientState, + /// the consensus proof verification failed (height: `{height}`), client error: `{client_error}` + ConsensusStateVerificationFailure { + height: Height, + client_error: client_error::ClientError, + }, + /// the client state proof verification failed for client id `{client_id}`, client error: `{client_error}` + ClientStateVerificationFailure { // TODO: use more specific error source - ClientStateVerificationFailure - { - client_id: ClientId, - } - [ client_error::Error ] - | e | { - format_args!("the client state proof verification failed for client id {0}", - e.client_id) - }, - - ImplementationSpecific - | _ | { "implementation specific error" }, - - InvalidClientState - { - reason: String, - } - | e | { format_args!("invalid client state: {0}", e.reason) }, + client_id: ClientId, + client_error: client_error::ClientError, + }, + /// invalid client state: `{reason}` + InvalidClientState { reason: String }, + /// other error: `{description}` + Other { description: String }, +} - Other - { description: String } - | e| { format_args!("other error: {0}", e.description) }, +#[cfg(feature = "std")] +impl std::error::Error for ConnectionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Client(e) => Some(e), + Self::InvalidIdentifier(e) => Some(e), + Self::InvalidProof(e) => Some(e), + Self::VerifyConnectionState(e) => Some(e), + Self::Signer(e) => Some(e), + Self::ConsensusStateVerificationFailure { + client_error: e, .. + } => Some(e), + Self::ClientStateVerificationFailure { + client_error: e, .. + } => Some(e), + _ => None, + } } } diff --git a/crates/ibc/src/core/ics03_connection/handler.rs b/crates/ibc/src/core/ics03_connection/handler.rs index 5c9d67993..bcf627f62 100644 --- a/crates/ibc/src/core/ics03_connection/handler.rs +++ b/crates/ibc/src/core/ics03_connection/handler.rs @@ -3,7 +3,7 @@ use crate::core::ics03_connection::connection::ConnectionEnd; use crate::core::ics03_connection::context::ConnectionReader; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::msgs::ConnectionMsg; use crate::core::ics24_host::identifier::ConnectionId; use crate::handler::HandlerOutput; @@ -43,7 +43,7 @@ pub struct ConnectionResult { pub fn dispatch( ctx: &Ctx, msg: ConnectionMsg, -) -> Result, Error> +) -> Result, ConnectionError> where Ctx: ConnectionReader, { diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs index ebc41ed4c..21467775e 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; use crate::core::ics03_connection::context::ConnectionReader; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::events::OpenAck; use crate::core::ics03_connection::handler::ConnectionResult; use crate::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; @@ -16,14 +16,14 @@ use super::ConnectionIdState; pub(crate) fn process( ctx_a: &dyn ConnectionReader, msg: MsgConnectionOpenAck, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); if msg.consensus_height_of_a_on_b > ctx_a.host_current_height()? { - return Err(Error::invalid_consensus_height( - msg.consensus_height_of_a_on_b, - ctx_a.host_current_height()?, - )); + return Err(ConnectionError::InvalidConsensusHeight { + target_height: msg.consensus_height_of_a_on_b, + current_height: ctx_a.host_current_height()?, + }); } ctx_a.validate_self_client(msg.client_state_of_a_on_b.clone())?; @@ -32,7 +32,9 @@ pub(crate) fn process( if !(conn_end_on_a.state_matches(&State::Init) && conn_end_on_a.versions().contains(&msg.version)) { - return Err(Error::connection_mismatch(msg.conn_id_on_a)); + return Err(ConnectionError::ConnectionMismatch { + connection_id: msg.conn_id_on_a, + }); } let client_id_on_a = conn_end_on_a.client_id(); @@ -41,7 +43,7 @@ pub(crate) fn process( let conn_id_on_b = conn_end_on_a .counterparty() .connection_id() - .ok_or_else(Error::invalid_counterparty)?; + .ok_or(ConnectionError::InvalidCounterparty)?; // Proof verification. { @@ -74,7 +76,7 @@ pub(crate) fn process( conn_id_on_b, &expected_conn_end_on_b, ) - .map_err(Error::verify_connection_state)?; + .map_err(ConnectionError::VerifyConnectionState)?; } client_state_of_b_on_a @@ -86,8 +88,9 @@ pub(crate) fn process( client_id_on_b, msg.client_state_of_a_on_b, ) - .map_err(|e| { - Error::client_state_verification_failure(conn_end_on_a.client_id().clone(), e) + .map_err(|e| ConnectionError::ClientStateVerificationFailure { + client_id: conn_end_on_a.client_id().clone(), + client_error: e, })?; let expected_consensus_state_of_a_on_b = @@ -102,7 +105,10 @@ pub(crate) fn process( msg.consensus_height_of_a_on_b, expected_consensus_state_of_a_on_b.as_ref(), ) - .map_err(|e| Error::consensus_state_verification_failure(msg.proofs_height_on_b, e))?; + .map_err(|e| ConnectionError::ConsensusStateVerificationFailure { + height: msg.proofs_height_on_b, + client_error: e, + })?; } // Success @@ -163,7 +169,7 @@ mod tests { ctx: MockContext, msg: ConnectionMsg, want_pass: bool, - match_error: Box, + match_error: Box, } let msg_ack = @@ -221,10 +227,10 @@ mod tests { msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack.clone())), want_pass: false, match_error: { - let connection_id = conn_id.clone(); - Box::new(move |e| match e.detail() { - error::ErrorDetail::ConnectionNotFound(e) => { - assert_eq!(e.connection_id, connection_id) + let right_connection_id = conn_id.clone(); + Box::new(move |e| match e { + error::ConnectionError::ConnectionNotFound { connection_id } => { + assert_eq!(connection_id, right_connection_id) } _ => { panic!("Expected ConnectionNotFound error"); @@ -241,10 +247,10 @@ mod tests { msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack)), want_pass: false, match_error: { - let connection_id = conn_id; - Box::new(move |e| match e.detail() { - error::ErrorDetail::ConnectionMismatch(e) => { - assert_eq!(e.connection_id, connection_id); + let right_connection_id = conn_id; + Box::new(move |e| match e { + error::ConnectionError::ConnectionMismatch { connection_id } => { + assert_eq!(connection_id, right_connection_id); } _ => { panic!("Expected ConnectionMismatch error"); diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs index 1e157b8ae..f0867f158 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; use crate::core::ics03_connection::context::ConnectionReader; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::events::OpenConfirm; use crate::core::ics03_connection::handler::{ConnectionIdState, ConnectionResult}; use crate::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; @@ -14,19 +14,21 @@ use crate::prelude::*; pub(crate) fn process( ctx_b: &dyn ConnectionReader, msg: MsgConnectionOpenConfirm, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let conn_end_on_b = ctx_b.connection_end(&msg.conn_id_on_b)?; if !conn_end_on_b.state_matches(&State::TryOpen) { - return Err(Error::connection_mismatch(msg.conn_id_on_b)); + return Err(ConnectionError::ConnectionMismatch { + connection_id: msg.conn_id_on_b, + }); } let client_id_on_a = conn_end_on_b.counterparty().client_id(); let client_id_on_b = conn_end_on_b.client_id(); let conn_id_on_a = conn_end_on_b .counterparty() .connection_id() - .ok_or_else(Error::invalid_counterparty)?; + .ok_or(ConnectionError::InvalidCounterparty)?; // Verify proofs { @@ -58,7 +60,7 @@ pub(crate) fn process( conn_id_on_a, &expected_conn_end_on_a, ) - .map_err(Error::verify_connection_state)?; + .map_err(ConnectionError::VerifyConnectionState)?; } // Success diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs index 6760e202a..65911d8ef 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::{ConnectionEnd, State}; use crate::core::ics03_connection::context::ConnectionReader; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::events::OpenInit; use crate::core::ics03_connection::handler::ConnectionResult; use crate::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; @@ -17,7 +17,7 @@ use super::ConnectionIdState; pub(crate) fn process( ctx_a: &dyn ConnectionReader, msg: MsgConnectionOpenInit, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); // An IBC client running on the local (host) chain should exist. @@ -28,7 +28,7 @@ pub(crate) fn process( if ctx_a.get_compatible_versions().contains(&version) { Ok(vec![version]) } else { - Err(Error::version_not_supported(version)) + Err(ConnectionError::VersionNotSupported { version }) } } None => Ok(ctx_a.get_compatible_versions()), diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs index 41d984567..96dfe5956 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; use crate::core::ics03_connection::context::ConnectionReader; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::events::OpenTry; use crate::core::ics03_connection::handler::ConnectionResult; use crate::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; @@ -17,7 +17,7 @@ use super::ConnectionIdState; pub(crate) fn process( ctx_b: &dyn ConnectionReader, msg: MsgConnectionOpenTry, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let conn_id_on_b = ConnectionId::new(ctx_b.connection_counter()?); @@ -26,10 +26,10 @@ pub(crate) fn process( if msg.consensus_height_of_b_on_a > ctx_b.host_current_height()? { // Fail if the consensus height is too advanced. - return Err(Error::invalid_consensus_height( - msg.consensus_height_of_b_on_a, - ctx_b.host_current_height()?, - )); + return Err(ConnectionError::InvalidConsensusHeight { + target_height: msg.consensus_height_of_b_on_a, + current_height: ctx_b.host_current_height()?, + }); } let version_on_b = @@ -47,7 +47,7 @@ pub(crate) fn process( let conn_id_on_a = conn_end_on_b .counterparty() .connection_id() - .ok_or_else(Error::invalid_counterparty)?; + .ok_or(ConnectionError::InvalidCounterparty)?; // Verify proofs { @@ -77,7 +77,7 @@ pub(crate) fn process( conn_id_on_a, &expected_conn_end_on_a, ) - .map_err(Error::verify_connection_state)?; + .map_err(ConnectionError::VerifyConnectionState)?; } client_state_of_a_on_b @@ -89,8 +89,9 @@ pub(crate) fn process( client_id_on_a, msg.client_state_of_b_on_a, ) - .map_err(|e| { - Error::client_state_verification_failure(conn_end_on_b.client_id().clone(), e) + .map_err(|e| ConnectionError::ClientStateVerificationFailure { + client_id: conn_end_on_b.client_id().clone(), + client_error: e, })?; let expected_consensus_state_of_b_on_a = @@ -105,7 +106,10 @@ pub(crate) fn process( msg.consensus_height_of_b_on_a, expected_consensus_state_of_b_on_a.as_ref(), ) - .map_err(|e| Error::consensus_state_verification_failure(msg.proofs_height_on_a, e))?; + .map_err(|e| ConnectionError::ConsensusStateVerificationFailure { + height: msg.proofs_height_on_a, + client_error: e, + })?; } // Success diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs index b929d4da0..3fc90775c 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs @@ -4,7 +4,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; use ibc_proto::protobuf::Protobuf; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::version::Version; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; use crate::core::ics24_host::identifier::ConnectionId; @@ -39,7 +39,7 @@ pub struct MsgConnectionOpenAck { } impl Msg for MsgConnectionOpenAck { - type ValidationError = Error; + type ValidationError = ConnectionError; type Raw = RawMsgConnectionOpenAck; fn route(&self) -> String { @@ -54,38 +54,46 @@ impl Msg for MsgConnectionOpenAck { impl Protobuf for MsgConnectionOpenAck {} impl TryFrom for MsgConnectionOpenAck { - type Error = Error; + type Error = ConnectionError; fn try_from(msg: RawMsgConnectionOpenAck) -> Result { Ok(Self { conn_id_on_a: msg .connection_id .parse() - .map_err(Error::invalid_identifier)?, + .map_err(ConnectionError::InvalidIdentifier)?, conn_id_on_b: msg .counterparty_connection_id .parse() - .map_err(Error::invalid_identifier)?, - client_state_of_a_on_b: msg.client_state.ok_or_else(Error::missing_client_state)?, - version: msg.version.ok_or_else(Error::empty_versions)?.try_into()?, - proof_conn_end_on_b: msg.proof_try.try_into().map_err(Error::invalid_proof)?, + .map_err(ConnectionError::InvalidIdentifier)?, + client_state_of_a_on_b: msg + .client_state + .ok_or(ConnectionError::MissingClientState)?, + version: msg + .version + .ok_or(ConnectionError::EmptyVersions)? + .try_into()?, + proof_conn_end_on_b: msg + .proof_try + .try_into() + .map_err(ConnectionError::InvalidProof)?, proof_client_state_of_a_on_b: msg .proof_client .try_into() - .map_err(Error::invalid_proof)?, + .map_err(ConnectionError::InvalidProof)?, proof_consensus_state_of_a_on_b: msg .proof_consensus .try_into() - .map_err(Error::invalid_proof)?, + .map_err(ConnectionError::InvalidProof)?, proofs_height_on_b: msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_proof_height)?, + .ok_or(ConnectionError::MissingProofHeight)?, consensus_height_of_a_on_b: msg .consensus_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_consensus_height)?, - signer: msg.signer.parse().map_err(Error::signer)?, + .ok_or(ConnectionError::MissingConsensusHeight)?, + signer: msg.signer.parse().map_err(ConnectionError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs index a52673e11..2ca8419e2 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs @@ -5,7 +5,7 @@ use ibc_proto::protobuf::Protobuf; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics24_host::identifier::ConnectionId; use crate::signer::Signer; use crate::tx_msg::Msg; @@ -26,7 +26,7 @@ pub struct MsgConnectionOpenConfirm { } impl Msg for MsgConnectionOpenConfirm { - type ValidationError = Error; + type ValidationError = ConnectionError; type Raw = RawMsgConnectionOpenConfirm; fn route(&self) -> String { @@ -41,20 +41,23 @@ impl Msg for MsgConnectionOpenConfirm { impl Protobuf for MsgConnectionOpenConfirm {} impl TryFrom for MsgConnectionOpenConfirm { - type Error = Error; + type Error = ConnectionError; fn try_from(msg: RawMsgConnectionOpenConfirm) -> Result { Ok(Self { conn_id_on_b: msg .connection_id .parse() - .map_err(Error::invalid_identifier)?, - proof_conn_end_on_a: msg.proof_ack.try_into().map_err(Error::invalid_proof)?, + .map_err(ConnectionError::InvalidIdentifier)?, + proof_conn_end_on_a: msg + .proof_ack + .try_into() + .map_err(ConnectionError::InvalidProof)?, proof_height_on_a: msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_proof_height)?, - signer: msg.signer.parse().map_err(Error::signer)?, + .ok_or(ConnectionError::MissingProofHeight)?, + signer: msg.signer.parse().map_err(ConnectionError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs index a3ae47e50..2dcc6fa29 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs @@ -6,7 +6,7 @@ use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnect use ibc_proto::protobuf::Protobuf; use crate::core::ics03_connection::connection::Counterparty; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::version::Version; use crate::core::ics24_host::identifier::ClientId; use crate::signer::Signer; @@ -27,7 +27,7 @@ pub struct MsgConnectionOpenInit { } impl Msg for MsgConnectionOpenInit { - type ValidationError = Error; + type ValidationError = ConnectionError; type Raw = RawMsgConnectionOpenInit; fn route(&self) -> String { @@ -42,18 +42,21 @@ impl Msg for MsgConnectionOpenInit { impl Protobuf for MsgConnectionOpenInit {} impl TryFrom for MsgConnectionOpenInit { - type Error = Error; + type Error = ConnectionError; fn try_from(msg: RawMsgConnectionOpenInit) -> Result { Ok(Self { - client_id_on_a: msg.client_id.parse().map_err(Error::invalid_identifier)?, + client_id_on_a: msg + .client_id + .parse() + .map_err(ConnectionError::InvalidIdentifier)?, counterparty: msg .counterparty - .ok_or_else(Error::missing_counterparty)? + .ok_or(ConnectionError::MissingCounterparty)? .try_into()?, version: msg.version.map(|version| version.try_into()).transpose()?, delay_period: Duration::from_nanos(msg.delay_period), - signer: msg.signer.parse().map_err(Error::signer)?, + signer: msg.signer.parse().map_err(ConnectionError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs index 3b04ae899..2e828ff70 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs @@ -10,7 +10,7 @@ use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenTry as RawMsgConnecti use ibc_proto::protobuf::Protobuf; use crate::core::ics03_connection::connection::Counterparty; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::version::Version; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; use crate::core::ics24_host::identifier::ClientId; @@ -51,7 +51,7 @@ pub struct MsgConnectionOpenTry { } impl Msg for MsgConnectionOpenTry { - type ValidationError = Error; + type ValidationError = ConnectionError; type Raw = RawMsgConnectionOpenTry; fn route(&self) -> String { @@ -66,7 +66,7 @@ impl Msg for MsgConnectionOpenTry { impl Protobuf for MsgConnectionOpenTry {} impl TryFrom for MsgConnectionOpenTry { - type Error = Error; + type Error = ConnectionError; fn try_from(msg: RawMsgConnectionOpenTry) -> Result { let counterparty_versions = msg @@ -76,7 +76,7 @@ impl TryFrom for MsgConnectionOpenTry { .collect::, _>>()?; if counterparty_versions.is_empty() { - return Err(Error::empty_versions()); + return Err(ConnectionError::EmptyVersions); } // We set the deprecated `previous_connection_id` field so that we can @@ -84,32 +84,40 @@ impl TryFrom for MsgConnectionOpenTry { #[allow(deprecated)] Ok(Self { previous_connection_id: msg.previous_connection_id, - client_id_on_b: msg.client_id.parse().map_err(Error::invalid_identifier)?, - client_state_of_b_on_a: msg.client_state.ok_or_else(Error::missing_client_state)?, + client_id_on_b: msg + .client_id + .parse() + .map_err(ConnectionError::InvalidIdentifier)?, + client_state_of_b_on_a: msg + .client_state + .ok_or(ConnectionError::MissingClientState)?, counterparty: msg .counterparty - .ok_or_else(Error::missing_counterparty)? + .ok_or(ConnectionError::MissingCounterparty)? .try_into()?, versions_on_a: counterparty_versions, - proof_conn_end_on_a: msg.proof_init.try_into().map_err(Error::invalid_proof)?, + proof_conn_end_on_a: msg + .proof_init + .try_into() + .map_err(ConnectionError::InvalidProof)?, proof_client_state_of_b_on_a: msg .proof_client .try_into() - .map_err(Error::invalid_proof)?, + .map_err(ConnectionError::InvalidProof)?, proof_consensus_state_of_b_on_a: msg .proof_consensus .try_into() - .map_err(Error::invalid_proof)?, + .map_err(ConnectionError::InvalidProof)?, proofs_height_on_a: msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_proof_height)?, + .ok_or(ConnectionError::MissingProofHeight)?, consensus_height_of_b_on_a: msg .consensus_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_consensus_height)?, + .ok_or(ConnectionError::MissingConsensusHeight)?, delay_period: Duration::from_nanos(msg.delay_period), - signer: msg.signer.parse().map_err(Error::signer)?, + signer: msg.signer.parse().map_err(ConnectionError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics03_connection/version.rs b/crates/ibc/src/core/ics03_connection/version.rs index 4109aade3..6f188599e 100644 --- a/crates/ibc/src/core/ics03_connection/version.rs +++ b/crates/ibc/src/core/ics03_connection/version.rs @@ -7,7 +7,7 @@ use ibc_proto::ibc::core::connection::v1::Version as RawVersion; use ibc_proto::protobuf::Protobuf; use serde::{Deserialize, Serialize}; -use crate::core::ics03_connection::error::Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics04_channel::channel::Order; /// Stores the identifier and the features supported by a version @@ -29,14 +29,14 @@ impl Version { impl Protobuf for Version {} impl TryFrom for Version { - type Error = Error; + type Error = ConnectionError; fn try_from(value: RawVersion) -> Result { if value.identifier.trim().is_empty() { - return Err(Error::empty_versions()); + return Err(ConnectionError::EmptyVersions); } for feature in value.features.iter() { if feature.trim().is_empty() { - return Err(Error::empty_features()); + return Err(ConnectionError::EmptyFeatures); } } Ok(Version { @@ -87,7 +87,7 @@ pub fn get_compatible_versions() -> Vec { pub fn pick_version( supported_versions: Vec, counterparty_versions: Vec, -) -> Result { +) -> Result { let mut intersection: Vec = Vec::new(); for s in supported_versions.iter() { for c in counterparty_versions.iter() { @@ -96,7 +96,7 @@ pub fn pick_version( } for feature in c.features.iter() { if feature.trim().is_empty() { - return Err(Error::empty_features()); + return Err(ConnectionError::EmptyFeatures); } } intersection.append(&mut vec![s.clone()]); @@ -104,7 +104,7 @@ pub fn pick_version( } intersection.sort_by(|a, b| a.identifier.cmp(&b.identifier)); if intersection.is_empty() { - return Err(Error::no_common_version()); + return Err(ConnectionError::NoCommonVersion); } Ok(intersection[0].clone()) } @@ -117,7 +117,7 @@ mod tests { use ibc_proto::ibc::core::connection::v1::Version as RawVersion; - use crate::core::ics03_connection::error::Error; + use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics03_connection::version::{get_compatible_versions, pick_version, Version}; fn good_versions() -> Vec { @@ -263,7 +263,7 @@ mod tests { name: String, supported: Vec, counterparty: Vec, - picked: Result, + picked: Result, want_pass: bool, } let tests: Vec = vec![ @@ -285,7 +285,7 @@ mod tests { name: "Disjoint versions".to_string(), supported: disjoint().0, counterparty: disjoint().1, - picked: Err(Error::no_common_version()), + picked: Err(ConnectionError::NoCommonVersion), want_pass: false, }, ]; diff --git a/crates/ibc/src/core/ics04_channel/channel.rs b/crates/ibc/src/core/ics04_channel/channel.rs index 032161521..2ddf222af 100644 --- a/crates/ibc/src/core/ics04_channel/channel.rs +++ b/crates/ibc/src/core/ics04_channel/channel.rs @@ -12,7 +12,7 @@ use ibc_proto::ibc::core::channel::v1::{ IdentifiedChannel as RawIdentifiedChannel, }; -use crate::core::ics04_channel::{error::Error, Version}; +use crate::core::ics04_channel::{error::ChannelError, Version}; use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -35,7 +35,7 @@ impl IdentifiedChannelEnd { impl Protobuf for IdentifiedChannelEnd {} impl TryFrom for IdentifiedChannelEnd { - type Error = Error; + type Error = ChannelError; fn try_from(value: RawIdentifiedChannel) -> Result { let raw_channel_end = RawChannel { @@ -47,8 +47,8 @@ impl TryFrom for IdentifiedChannelEnd { }; Ok(IdentifiedChannelEnd { - port_id: value.port_id.parse().map_err(Error::identifier)?, - channel_id: value.channel_id.parse().map_err(Error::identifier)?, + port_id: value.port_id.parse().map_err(ChannelError::Identifier)?, + channel_id: value.channel_id.parse().map_err(ChannelError::Identifier)?, channel_end: raw_channel_end.try_into()?, }) } @@ -107,7 +107,7 @@ impl Default for ChannelEnd { impl Protobuf for ChannelEnd {} impl TryFrom for ChannelEnd { - type Error = Error; + type Error = ChannelError; fn try_from(value: RawChannel) -> Result { let chan_state: State = State::from_i32(value.state)?; @@ -121,7 +121,7 @@ impl TryFrom for ChannelEnd { // Assemble the 'remote' attribute of the Channel, which represents the Counterparty. let remote = value .counterparty - .ok_or_else(Error::missing_counterparty)? + .ok_or(ChannelError::MissingCounterparty)? .try_into()?; // Parse each item in connection_hops into a ConnectionId. @@ -130,7 +130,7 @@ impl TryFrom for ChannelEnd { .into_iter() .map(|conn_id| ConnectionId::from_str(conn_id.as_str())) .collect::, _>>() - .map_err(Error::identifier)?; + .map_err(ChannelError::Identifier)?; let version = value.version.into(); @@ -216,12 +216,12 @@ impl ChannelEnd { &self.version } - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic(&self) -> Result<(), ChannelError> { if self.connection_hops.len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - self.connection_hops.len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: self.connection_hops.len(), + }); } self.counterparty().validate_basic() } @@ -272,7 +272,7 @@ impl Counterparty { self.channel_id.as_ref() } - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic(&self) -> Result<(), ChannelError> { Ok(()) } } @@ -297,16 +297,16 @@ impl Display for Counterparty { impl Protobuf for Counterparty {} impl TryFrom for Counterparty { - type Error = Error; + type Error = ChannelError; fn try_from(value: RawCounterparty) -> Result { let channel_id = Some(value.channel_id) .filter(|x| !x.is_empty()) .map(|v| FromStr::from_str(v.as_str())) .transpose() - .map_err(Error::identifier)?; + .map_err(ChannelError::Identifier)?; Ok(Counterparty::new( - value.port_id.parse().map_err(Error::identifier)?, + value.port_id.parse().map_err(ChannelError::Identifier)?, channel_id, )) } @@ -354,25 +354,29 @@ impl Order { } // Parses the Order out from a i32. - pub fn from_i32(nr: i32) -> Result { + pub fn from_i32(nr: i32) -> Result { match nr { 0 => Ok(Self::None), 1 => Ok(Self::Unordered), 2 => Ok(Self::Ordered), - _ => Err(Error::unknown_order_type(nr.to_string())), + _ => Err(ChannelError::UnknownOrderType { + type_id: nr.to_string(), + }), } } } impl FromStr for Order { - type Err = Error; + type Err = ChannelError; fn from_str(s: &str) -> Result { match s.to_lowercase().trim_start_matches("order_") { "uninitialized" => Ok(Self::None), "unordered" => Ok(Self::Unordered), "ordered" => Ok(Self::Ordered), - _ => Err(Error::unknown_order_type(s.to_string())), + _ => Err(ChannelError::UnknownOrderType { + type_id: s.to_string(), + }), } } } @@ -399,14 +403,14 @@ impl State { } // Parses the State out from a i32. - pub fn from_i32(s: i32) -> Result { + 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), 4 => Ok(Self::Closed), - _ => Err(Error::unknown_state(s)), + _ => Err(ChannelError::UnknownState { state: s }), } } diff --git a/crates/ibc/src/core/ics04_channel/context.rs b/crates/ibc/src/core/ics04_channel/context.rs index e153c5cef..908dfafa6 100644 --- a/crates/ibc/src/core/ics04_channel/context.rs +++ b/crates/ibc/src/core/ics04_channel/context.rs @@ -12,7 +12,10 @@ use crate::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCo use crate::core::ics04_channel::handler::recv_packet::RecvPacketResult; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::acknowledgement::Acknowledgement; -use crate::core::ics04_channel::{error::Error, packet::Receipt}; +use crate::core::ics04_channel::{ + error::{ChannelError, PacketError}, + packet::Receipt, +}; use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use crate::prelude::*; use crate::timestamp::Timestamp; @@ -24,61 +27,68 @@ use super::timeout::TimeoutHeight; /// A context supplying all the necessary read-only dependencies for processing any `ChannelMsg`. pub trait ChannelReader { /// Returns the ChannelEnd for the given `port_id` and `chan_id`. - fn channel_end(&self, port_id: &PortId, channel_id: &ChannelId) -> Result; + fn channel_end( + &self, + port_id: &PortId, + channel_id: &ChannelId, + ) -> Result; /// Returns the ConnectionState for the given identifier `connection_id`. - fn connection_end(&self, connection_id: &ConnectionId) -> Result; + fn connection_end(&self, connection_id: &ConnectionId) -> Result; - fn connection_channels(&self, cid: &ConnectionId) -> Result, Error>; + fn connection_channels( + &self, + cid: &ConnectionId, + ) -> Result, ChannelError>; /// Returns the ClientState for the given identifier `client_id`. Necessary dependency towards /// proof verification. - fn client_state(&self, client_id: &ClientId) -> Result, Error>; + fn client_state(&self, client_id: &ClientId) -> Result, ChannelError>; fn client_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result, Error>; + ) -> Result, ChannelError>; fn get_next_sequence_send( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result; + ) -> Result; fn get_next_sequence_recv( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result; + ) -> Result; fn get_next_sequence_ack( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result; + ) -> Result; fn get_packet_commitment( &self, port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result; + ) -> Result; fn get_packet_receipt( &self, port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result; + ) -> Result; fn get_packet_acknowledgement( &self, port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result; + ) -> Result; /// Compute the commitment for a packet. /// Note that the absence of `timeout_height` is treated as @@ -113,10 +123,10 @@ pub trait ChannelReader { fn hash(&self, value: Vec) -> Vec; /// Returns the current height of the local chain. - fn host_height(&self) -> Result; + fn host_height(&self) -> Result; /// Returns the current timestamp of the local chain. - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { let pending_consensus_state = self .pending_host_consensus_state() .expect("host must have pending consensus state"); @@ -124,21 +134,30 @@ pub trait ChannelReader { } /// Returns the `ConsensusState` of the host (local) chain at a specific height. - fn host_consensus_state(&self, height: Height) -> Result, Error>; + fn host_consensus_state(&self, height: Height) + -> Result, ChannelError>; /// Returns the pending `ConsensusState` of the host (local) chain. - fn pending_host_consensus_state(&self) -> Result, Error>; + fn pending_host_consensus_state(&self) -> Result, ChannelError>; /// Returns the time when the client state for the given [`ClientId`] was updated with a header for the given [`Height`] - fn client_update_time(&self, client_id: &ClientId, height: Height) -> Result; + fn client_update_time( + &self, + client_id: &ClientId, + height: Height, + ) -> Result; /// Returns the height when the client state for the given [`ClientId`] was updated with a header for the given [`Height`] - fn client_update_height(&self, client_id: &ClientId, height: Height) -> Result; + fn client_update_height( + &self, + client_id: &ClientId, + height: Height, + ) -> Result; /// Returns a counter on the number of channel ids have been created thus far. /// The value of this counter should increase only via method /// `ChannelKeeper::increase_channel_counter`. - fn channel_counter(&self) -> Result; + fn channel_counter(&self) -> Result; /// Returns the maximum expected time per block fn max_expected_time_per_block(&self) -> Duration; @@ -152,26 +171,30 @@ pub trait ChannelReader { pub trait SendPacketReader { /// Returns the ChannelEnd for the given `port_id` and `chan_id`. - fn channel_end(&self, port_id: &PortId, channel_id: &ChannelId) -> Result; + fn channel_end( + &self, + port_id: &PortId, + channel_id: &ChannelId, + ) -> Result; /// Returns the ConnectionState for the given identifier `connection_id`. - fn connection_end(&self, connection_id: &ConnectionId) -> Result; + fn connection_end(&self, connection_id: &ConnectionId) -> Result; /// Returns the ClientState for the given identifier `client_id`. Necessary dependency towards /// proof verification. - fn client_state(&self, client_id: &ClientId) -> Result, Error>; + fn client_state(&self, client_id: &ClientId) -> Result, PacketError>; fn client_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result, Error>; + ) -> Result, PacketError>; fn get_next_sequence_send( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result; + ) -> Result; fn hash(&self, value: Vec) -> Vec; @@ -200,31 +223,35 @@ impl SendPacketReader for T where T: ChannelReader, { - fn channel_end(&self, port_id: &PortId, channel_id: &ChannelId) -> Result { - ChannelReader::channel_end(self, port_id, channel_id) + fn channel_end( + &self, + port_id: &PortId, + channel_id: &ChannelId, + ) -> Result { + ChannelReader::channel_end(self, port_id, channel_id).map_err(PacketError::Channel) } - fn connection_end(&self, connection_id: &ConnectionId) -> Result { - ChannelReader::connection_end(self, connection_id) + fn connection_end(&self, connection_id: &ConnectionId) -> Result { + ChannelReader::connection_end(self, connection_id).map_err(PacketError::Channel) } - fn client_state(&self, client_id: &ClientId) -> Result, Error> { - ChannelReader::client_state(self, client_id) + fn client_state(&self, client_id: &ClientId) -> Result, PacketError> { + ChannelReader::client_state(self, client_id).map_err(PacketError::Channel) } fn client_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result, Error> { - ChannelReader::client_consensus_state(self, client_id, height) + ) -> Result, PacketError> { + ChannelReader::client_consensus_state(self, client_id, height).map_err(PacketError::Channel) } fn get_next_sequence_send( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result { + ) -> Result { ChannelReader::get_next_sequence_send(self, port_id, channel_id) } @@ -236,7 +263,7 @@ where /// A context supplying all the necessary write-only dependencies (i.e., storage writing facility) /// for processing any `ChannelMsg`. pub trait ChannelKeeper { - fn store_channel_result(&mut self, result: ChannelResult) -> Result<(), Error> { + fn store_channel_result(&mut self, result: ChannelResult) -> Result<(), PacketError> { let connection_id = result.channel_end.connection_hops()[0].clone(); // The handler processed this channel & some modifications occurred, store the new end. @@ -244,7 +271,8 @@ pub trait ChannelKeeper { result.port_id.clone(), result.channel_id.clone(), result.channel_end, - )?; + ) + .map_err(PacketError::Channel)?; // The channel identifier was freshly brewed. // Increase counter & initialize seq. nrs. @@ -256,7 +284,8 @@ pub trait ChannelKeeper { connection_id, result.port_id.clone(), result.channel_id.clone(), - )?; + ) + .map_err(PacketError::Channel)?; // Initialize send, recv, and ack sequence numbers. self.store_next_sequence_send( @@ -275,7 +304,7 @@ pub trait ChannelKeeper { Ok(()) } - fn store_packet_result(&mut self, general_result: PacketResult) -> Result<(), Error> { + fn store_packet_result(&mut self, general_result: PacketResult) -> Result<(), PacketError> { match general_result { PacketResult::Send(res) => { self.store_next_sequence_send( @@ -319,7 +348,8 @@ pub trait ChannelKeeper { self.delete_packet_commitment(&res.port_id, &res.channel_id, res.seq)?; if let Some(c) = res.channel { // Ordered Channel: closes channel - self.store_channel(res.port_id, res.channel_id, c)?; + self.store_channel(res.port_id, res.channel_id, c) + .map_err(PacketError::Channel)?; } } } @@ -332,14 +362,14 @@ pub trait ChannelKeeper { channel_id: ChannelId, sequence: Sequence, commitment: PacketCommitment, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn delete_packet_commitment( &mut self, port_id: &PortId, channel_id: &ChannelId, seq: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn store_packet_receipt( &mut self, @@ -347,7 +377,7 @@ pub trait ChannelKeeper { channel_id: ChannelId, sequence: Sequence, receipt: Receipt, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn store_packet_acknowledgement( &mut self, @@ -355,21 +385,21 @@ pub trait ChannelKeeper { channel_id: ChannelId, sequence: Sequence, ack_commitment: AcknowledgementCommitment, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn delete_packet_acknowledgement( &mut self, port_id: &PortId, channel_id: &ChannelId, sequence: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn store_connection_channels( &mut self, conn_id: ConnectionId, port_id: PortId, channel_id: ChannelId, - ) -> Result<(), Error>; + ) -> Result<(), ChannelError>; /// Stores the given channel_end at a path associated with the port_id and channel_id. fn store_channel( @@ -377,28 +407,28 @@ pub trait ChannelKeeper { port_id: PortId, channel_id: ChannelId, channel_end: ChannelEnd, - ) -> Result<(), Error>; + ) -> Result<(), ChannelError>; fn store_next_sequence_send( &mut self, port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn store_next_sequence_recv( &mut self, port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; fn store_next_sequence_ack( &mut self, port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Error>; + ) -> Result<(), PacketError>; /// Called upon channel identifier creation (Init or Try message processing). /// Increases the counter which keeps track of how many channels have been created. diff --git a/crates/ibc/src/core/ics04_channel/error.rs b/crates/ibc/src/core/ics04_channel/error.rs index 469fbf8c2..4f39b6c44 100644 --- a/crates/ibc/src/core/ics04_channel/error.rs +++ b/crates/ibc/src/core/ics04_channel/error.rs @@ -12,354 +12,210 @@ use crate::signer::SignerError; use crate::timestamp::Timestamp; use crate::Height; -use flex_error::{define_error, TraceError}; -use ibc_proto::protobuf::Error as TendermintError; - -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - Ics03Connection - [ connection_error::Error ] - | _ | { "ics03 connection error" }, - - Ics05Port - [ port_error::Error ] - | _ | { "ics05 port error" }, - - UnknownState - { state: i32 } - | e | { format_args!("channel state unknown: {}", e.state) }, - - Identifier - [ ValidationError ] - | _ | { "identifier error" }, - - UnknownOrderType - { type_id: String } - | e | { format_args!("channel order type unknown: {}", e.type_id) }, - - InvalidConnectionHopsLength - { expected: usize, actual: usize } - | e | { - format_args!( - "invalid connection hops length: expected {0}; actual {1}", - e.expected, e.actual) - }, - - InvalidPacketCounterparty - { port_id: PortId, channel_id: ChannelId } - | e | { - format_args!( - "packet destination port {} and channel {} doesn't match the counterparty's port/channel", - e.port_id, e.channel_id) - }, - - InvalidVersion - [ TraceError ] - | _ | { "invalid version" }, - - Signer - [ SignerError ] - | _ | { "invalid signer address" }, - - InvalidProof - [ ProofError ] - | _ | { "invalid proof" }, - - MissingHeight - | _ | { "invalid proof: missing height" }, - - MissingNextRecvSeq - { port_id: PortId, channel_id: ChannelId } - | e | { - format_args!("Missing sequence number for receiving packets on port {0} and channel {1}", - e.port_id, - e.channel_id) - }, - - ZeroPacketSequence - | _ | { "packet sequence cannot be 0" }, - - ZeroPacketData - | _ | { "packet data bytes cannot be empty" }, - - NonUtf8PacketData - | _ | { "packet data bytes must be valid UTF-8 (this restriction will be lifted in the future)" }, - - InvalidTimeoutHeight - | _ | { "invalid timeout height for the packet" }, - - InvalidPacket - | _ | { "invalid packet" }, - - MissingPacket - | _ | { "there is no packet in this message" }, - - MissingChannelId - | _ | { "missing channel id" }, - - MissingCounterparty - | _ | { "missing counterparty" }, - - NoCommonVersion - | _ | { "no commong version" }, - - MissingChannel - | _ | { "missing channel end" }, - - InvalidVersionLengthConnection - | _ | { "single version must be negociated on connection before opening channel" }, - - ChannelFeatureNotSuportedByConnection - | _ | { "the channel ordering is not supported by connection" }, - - ChannelNotFound - { port_id: PortId, channel_id: ChannelId } - | e | { - format_args!( - "the channel end ({0}, {1}) does not exist", - e.port_id, e.channel_id) - }, - - ChannelMismatch - { channel_id: ChannelId } - | e | { - format_args!( - "a different channel exists (was initialized) already for the same channel identifier {0}", - e.channel_id) - }, - - ConnectionNotOpen - { connection_id: ConnectionId } - | e | { - format_args!( - "the associated connection {0} is not OPEN", - e.connection_id) - }, - - UndefinedConnectionCounterparty - { connection_id: ConnectionId } - | e | { - format_args!( - "Undefined counterparty connection for {0}", - e.connection_id) - }, - - PacketVerificationFailed - { sequence: Sequence } - [ client_error::Error ] - | e | { - format_args!( - "Verification fails for the packet with the sequence number {0}", - e.sequence) - }, - - VerifyChannelFailed - [ client_error::Error ] - | _ | { - "Error verifying channel state" - }, - - InvalidAcknowledgement - | _ | { "Acknowledgment cannot be empty" }, - - AcknowledgementExists - { sequence: Sequence } - | e | { - format_args!( - "Packet acknowledgement exists for the packet with the sequence {0}", - e.sequence) - }, - - MissingNextSendSeq - { port_id: PortId, channel_id: ChannelId } - | e | { - format_args!("Missing sequence number for sending packets on port {0} and channel {1}", - e.port_id, - e.channel_id) - }, - - InvalidStringAsSequence - { value: String } - [ TraceError ] - | e | { - format_args!( - "String {0} cannot be converted to packet sequence", - e.value) - }, - - InvalidPacketSequence - { - given_sequence: Sequence, - next_sequence: Sequence - } - | e | { - format_args!( - "Invalid packet sequence {0} ≠ next send sequence {1}", - e.given_sequence, e.next_sequence) - }, - - LowPacketHeight - { - chain_height: Height, - timeout_height: TimeoutHeight - } - | e | { - format_args!( - "Receiving chain block height {0} >= packet timeout height {1}", - e.chain_height, e.timeout_height) - }, - - PacketTimeoutHeightNotReached - { - timeout_height: TimeoutHeight, - chain_height: Height, - } - | e | { - format_args!( - "Packet timeout height {0} > chain height {1}", - e.timeout_height, e.chain_height) - }, - - PacketTimeoutTimestampNotReached - { - timeout_timestamp: Timestamp, - chain_timestamp: Timestamp, - } - | e | { - format_args!( - "Packet timeout timestamp {0} > chain timestamp {1}", - e.timeout_timestamp, e.chain_timestamp) - }, - - LowPacketTimestamp - | _ | { "Receiving chain block timestamp >= packet timeout timestamp" }, - - InvalidPacketTimestamp - [ crate::timestamp::ParseTimestampError ] - | _ | { "Invalid packet timeout timestamp value" }, - - ErrorInvalidConsensusState - | _ | { "Invalid timestamp in consensus state; timestamp must be a positive value" }, - - FrozenClient - { client_id: ClientId } - | e | { - format_args!( - "Client with id {0} is frozen", - e.client_id) - }, - - InvalidCounterpartyChannelId - | _ | { "Invalid channel id in counterparty" }, - - InvalidChannelState - { channel_id: ChannelId, state: State } - | e | { - format_args!( - "Channel {0} should not be state {1}", - e.channel_id, e.state) - }, - - ChannelClosed - { channel_id: ChannelId } - | e | { - format_args!( - "Channel {0} is Closed", - e.channel_id) - }, - - ChanOpenAckProofVerification - | _ | { "Handshake proof verification fails at ChannelOpenAck" }, - - PacketCommitmentNotFound - { sequence: Sequence } - | e | { - format_args!( - "Commitment for the packet {0} not found", - e.sequence) - }, - - IncorrectPacketCommitment - { sequence: Sequence } - | e | { - format_args!( - "The stored commitment of the packet {0} is incorrect", - e.sequence) - }, - - PacketReceiptNotFound - { sequence: Sequence } - | e | { - format_args!( - "Receipt for the packet {0} not found", - e.sequence) - }, - - PacketAcknowledgementNotFound - { sequence: Sequence } - | e | { - format_args!( - "Acknowledgment for the packet {0} not found", - e.sequence) - }, - - MissingNextAckSeq - { port_id: PortId, channel_id: ChannelId } - | e | { - format_args!("Missing sequence number for ack packets on port {0} and channel {1}", - e.port_id, - e.channel_id) - }, - - ProcessedTimeNotFound - { - client_id: ClientId, - height: Height, - } - | e | { - format_args!( - "Processed time for the client {0} at height {1} not found", - e.client_id, e.height) - }, - - ProcessedHeightNotFound - { - client_id: ClientId, - height: Height, - } - | e | { - format_args!( - "Processed height for the client {0} at height {1} not found", - e.client_id, e.height) - }, - - RouteNotFound - | _ | { "route not found" }, - - ImplementationSpecific - | _ | { "implementation specific error" }, - - AppModule - { description: String } - | e | { - format_args!( - "application module error: {0}", - e.description) - }, +use displaydoc::Display; + +#[derive(Debug, Display)] +pub enum ChannelError { + /// connection error: `{0}` + Connection(connection_error::ConnectionError), + /// port error: `{0}` + Port(port_error::PortError), + /// channel state unknown: `{state}` + UnknownState { state: i32 }, + /// channel order type unknown: `{type_id}` + UnknownOrderType { type_id: String }, + /// invalid connection hops length: expected `{expected}`; actual `{actual}` + InvalidConnectionHopsLength { expected: usize, actual: usize }, + /// invalid signer address error: `{0}` + Signer(SignerError), + /// invalid proof: missing height + MissingHeight, + /// packet data bytes must be valid UTF-8 (this restriction will be lifted in the future) + NonUtf8PacketData, + /// missing counterparty + MissingCounterparty, + /// no commong version + NoCommonVersion, + /// missing channel end + MissingChannel, + /// single version must be negociated on connection before opening channel + InvalidVersionLengthConnection, + /// the channel ordering is not supported by connection + ChannelFeatureNotSuportedByConnection, + /// the channel end (`{port_id}`, `{channel_id}`) does not exist + ChannelNotFound { + port_id: PortId, + channel_id: ChannelId, + }, + /// Verification fails for the packet with the sequence number `{sequence}`, error: `{client_error}` + PacketVerificationFailed { + sequence: Sequence, + client_error: client_error::ClientError, + }, + /// Error verifying channel state error: `{0}` + VerifyChannelFailed(client_error::ClientError), + /// String `{value}` cannot be converted to packet sequence, error: `{error}` + InvalidStringAsSequence { + value: String, + error: core::num::ParseIntError, + }, + /// Invalid channel id in counterparty + InvalidCounterpartyChannelId, + /// Processed time for the client `{client_id}` at height `{height}` not found + ProcessedTimeNotFound { client_id: ClientId, height: Height }, + /// Processed height for the client `{client_id}` at height `{height}` not found + ProcessedHeightNotFound { client_id: ClientId, height: Height }, + /// route not found + RouteNotFound, + /// application module error: `{description}` + AppModule { description: String }, + /// other error: `{description}` + Other { description: String }, + /// Channel `{channel_id}` is Closed + ChannelClosed { channel_id: ChannelId }, + /// the associated connection `{connection_id}` is not OPEN + ConnectionNotOpen { connection_id: ConnectionId }, + /// Undefined counterparty connection for `{connection_id}` + UndefinedConnectionCounterparty { connection_id: ConnectionId }, + /// Client with id `{client_id}` is frozen + FrozenClient { client_id: ClientId }, + /// Channel `{channel_id}` should not be state `{state}` + InvalidChannelState { channel_id: ChannelId, state: State }, + /// invalid proof error: `{0}` + InvalidProof(ProofError), + /// identifier error: `{0}` + Identifier(ValidationError), +} - AbciConversionFailed - { abci_event: String } - | e | { format_args!("Failed to convert abci event to IbcEvent: {}", e.abci_event)}, +#[derive(Debug, Display)] +pub enum PacketError { + /// connection error: `{0}` + Connection(connection_error::ConnectionError), + /// channel error: `{0}` + Channel(ChannelError), + /// Channel `{channel_id}` is Closed + ChannelClosed { channel_id: ChannelId }, + /// packet destination port `{port_id}` and channel `{channel_id}` doesn't match the counterparty's port/channel + InvalidPacketCounterparty { + port_id: PortId, + channel_id: ChannelId, + }, + /// Client with id `{client_id}` is frozen + FrozenClient { client_id: ClientId }, + /// Receiving chain block height `{chain_height}` >= packet timeout height `{timeout_height}` + LowPacketHeight { + chain_height: Height, + timeout_height: TimeoutHeight, + }, + /// Receiving chain block timestamp >= packet timeout timestamp + LowPacketTimestamp, + /// Invalid packet sequence `{given_sequence}` ≠ next send sequence `{next_sequence}` + InvalidPacketSequence { + given_sequence: Sequence, + next_sequence: Sequence, + }, + /// Channel `{channel_id}` should not be state `{state}` + InvalidChannelState { channel_id: ChannelId, state: State }, + /// the associated connection `{connection_id}` is not OPEN + ConnectionNotOpen { connection_id: ConnectionId }, + /// Receipt for the packet `{sequence}` not found + PacketReceiptNotFound { sequence: Sequence }, + /// The stored commitment of the packet `{sequence}` is incorrect + IncorrectPacketCommitment { sequence: Sequence }, + /// implementation specific error + ImplementationSpecific, + /// Undefined counterparty connection for `{connection_id}` + UndefinedConnectionCounterparty { connection_id: ConnectionId }, + /// invalid proof: `{0}` + InvalidProof(ProofError), + /// Packet timeout height `{timeout_height}` > chain height `{chain_height}` + PacketTimeoutHeightNotReached { + timeout_height: TimeoutHeight, + chain_height: Height, + }, + /// Packet timeout timestamp `{timeout_timestamp}` > chain timestamp `{chain_timestamp}` + PacketTimeoutTimestampNotReached { + timeout_timestamp: Timestamp, + chain_timestamp: Timestamp, + }, + /// Packet acknowledgement exists for the packet with the sequence `{sequence}` + AcknowledgementExists { sequence: Sequence }, + /// Acknowledgment cannot be empty + InvalidAcknowledgement, + /// Acknowledgment for the packet `{sequence}` not found + PacketAcknowledgementNotFound { sequence: Sequence }, + /// invalid proof: missing height + MissingHeight, + /// there is no packet in this message + MissingPacket, + /// invalid signer address error: `{0}` + Signer(SignerError), + /// application module error: `{description}` + AppModule { description: String }, + /// route not found + RouteNotFound, + /// packet sequence cannot be 0 + ZeroPacketSequence, + /// invalid timeout height for the packet + InvalidTimeoutHeight, + /// packet data bytes cannot be empty + ZeroPacketData, + /// Invalid packet timeout timestamp value error: `{0}` + InvalidPacketTimestamp(crate::timestamp::ParseTimestampError), + /// identifier error: `{0}` + Identifier(ValidationError), + /// Missing sequence number for sending packets on port `{port_id}` and channel `{channel_id}` + MissingNextSendSeq { + port_id: PortId, + channel_id: ChannelId, + }, + /// the channel end (`{port_id}`, `{channel_id}`) does not exist + ChannelNotFound { + port_id: PortId, + channel_id: ChannelId, + }, + /// Commitment for the packet `{sequence}` not found + PacketCommitmentNotFound { sequence: Sequence }, + /// Missing sequence number for receiving packets on port `{port_id}` and channel `{channel_id}` + MissingNextRecvSeq { + port_id: PortId, + channel_id: ChannelId, + }, + /// Missing sequence number for ack packets on port `{port_id}` and channel `{channel_id}` + MissingNextAckSeq { + port_id: PortId, + channel_id: ChannelId, + }, +} - Other - { description: String } - | e| { format_args!("other error: {0}", e.description) }, +#[cfg(feature = "std")] +impl std::error::Error for PacketError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Connection(e) => Some(e), + Self::Channel(e) => Some(e), + Self::InvalidProof(e) => Some(e), + Self::Signer(e) => Some(e), + Self::Identifier(e) => Some(e), + _ => None, + } } } -impl Error { - pub fn chan_open_confirm_proof_verification(e: Error) -> Error { - e.add_trace(&"Handshake proof verification fails at ChannelOpenConfirm") +#[cfg(feature = "std")] +impl std::error::Error for ChannelError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Connection(e) => Some(e), + Self::Port(e) => Some(e), + Self::Identifier(e) => Some(e), + Self::Signer(e) => Some(e), + Self::InvalidProof(e) => Some(e), + Self::PacketVerificationFailed { + client_error: e, .. + } => Some(e), + Self::InvalidStringAsSequence { error: e, .. } => Some(e), + _ => None, + } } } diff --git a/crates/ibc/src/core/ics04_channel/events.rs b/crates/ibc/src/core/ics04_channel/events.rs index e77f03339..533fbd41a 100644 --- a/crates/ibc/src/core/ics04_channel/events.rs +++ b/crates/ibc/src/core/ics04_channel/events.rs @@ -5,7 +5,7 @@ mod packet_attributes; use tendermint::abci; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::packet::Packet; use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; use crate::events::IbcEventType; @@ -524,7 +524,7 @@ impl SendPacket { } impl TryFrom for abci::Event { - type Error = Error; + type Error = ChannelError; fn try_from(v: SendPacket) -> Result { let mut attributes = Vec::with_capacity(11); @@ -618,7 +618,7 @@ impl ReceivePacket { } impl TryFrom for abci::Event { - type Error = Error; + type Error = ChannelError; fn try_from(v: ReceivePacket) -> Result { let mut attributes = Vec::with_capacity(11); @@ -716,7 +716,7 @@ impl WriteAcknowledgement { } impl TryFrom for abci::Event { - type Error = Error; + type Error = ChannelError; fn try_from(v: WriteAcknowledgement) -> Result { let mut attributes = Vec::with_capacity(11); @@ -804,7 +804,7 @@ impl AcknowledgePacket { } impl TryFrom for abci::Event { - type Error = Error; + type Error = ChannelError; fn try_from(v: AcknowledgePacket) -> Result { Ok(abci::Event { @@ -884,7 +884,7 @@ impl TimeoutPacket { } impl TryFrom for abci::Event { - type Error = Error; + type Error = ChannelError; fn try_from(v: TimeoutPacket) -> Result { Ok(abci::Event { diff --git a/crates/ibc/src/core/ics04_channel/events/packet_attributes.rs b/crates/ibc/src/core/ics04_channel/events/packet_attributes.rs index 8e2251aa0..d7028f5da 100644 --- a/crates/ibc/src/core/ics04_channel/events/packet_attributes.rs +++ b/crates/ibc/src/core/ics04_channel/events/packet_attributes.rs @@ -1,8 +1,8 @@ use crate::{ core::{ ics04_channel::{ - channel::Order, error::Error, msgs::acknowledgement::Acknowledgement, packet::Sequence, - timeout::TimeoutHeight, + channel::Order, error::ChannelError, msgs::acknowledgement::Acknowledgement, + packet::Sequence, timeout::TimeoutHeight, }, ics24_host::identifier::{ChannelId, ConnectionId, PortId}, }, @@ -38,13 +38,13 @@ pub struct PacketDataAttribute { } impl TryFrom for Vec { - type Error = Error; + type Error = ChannelError; fn try_from(attr: PacketDataAttribute) -> Result { let tags = vec![ ( PKT_DATA_ATTRIBUTE_KEY, - str::from_utf8(&attr.packet_data).map_err(|_| Error::non_utf8_packet_data())?, + str::from_utf8(&attr.packet_data).map_err(|_| ChannelError::NonUtf8PacketData)?, ) .into(), ( @@ -172,7 +172,7 @@ pub struct AcknowledgementAttribute { } impl TryFrom for Vec { - type Error = Error; + type Error = ChannelError; fn try_from(attr: AcknowledgementAttribute) -> Result { let tags = vec![ @@ -183,7 +183,7 @@ impl TryFrom for Vec { // it. It has been deprecated in ibc-go. It will be removed // in the future. str::from_utf8(attr.acknowledgement.as_bytes()) - .map_err(|_| Error::non_utf8_packet_data())?, + .map_err(|_| ChannelError::NonUtf8PacketData)?, ) .into(), ( diff --git a/crates/ibc/src/core/ics04_channel/handler.rs b/crates/ibc/src/core/ics04_channel/handler.rs index bdbcb8e23..687a1f9ce 100644 --- a/crates/ibc/src/core/ics04_channel/handler.rs +++ b/crates/ibc/src/core/ics04_channel/handler.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use crate::core::ics04_channel::channel::ChannelEnd; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::{ChannelError, PacketError}; use crate::core::ics04_channel::msgs::ChannelMsg; use crate::core::ics04_channel::packet::Packet; use crate::core::ics04_channel::{msgs::PacketMsg, packet::PacketResult}; @@ -65,7 +65,7 @@ impl ModuleExtras { } } -pub fn channel_validate(ctx: &Ctx, msg: &ChannelMsg) -> Result +pub fn channel_validate(ctx: &Ctx, msg: &ChannelMsg) -> Result where Ctx: RouterContext, { @@ -73,7 +73,7 @@ where if ctx.router().has_route(&module_id) { Ok(module_id) } else { - Err(Error::route_not_found()) + Err(ChannelError::RouteNotFound) } } @@ -82,7 +82,7 @@ where pub fn channel_dispatch( ctx: &Ctx, msg: &ChannelMsg, -) -> Result<(Vec, ChannelResult), Error> +) -> Result<(Vec, ChannelResult), ChannelError> where Ctx: ChannelReader, { @@ -104,14 +104,14 @@ pub fn channel_callback( module_id: &ModuleId, msg: &ChannelMsg, result: &mut ChannelResult, -) -> Result +) -> Result where Ctx: RouterContext, { let cb = ctx .router_mut() .get_route_mut(module_id) - .ok_or_else(Error::route_not_found)?; + .ok_or(ChannelError::RouteNotFound)?; match msg { ChannelMsg::ChannelOpenInit(msg) => { @@ -222,29 +222,29 @@ pub fn channel_events( vec![event] } -pub fn get_module_for_packet_msg(ctx: &Ctx, msg: &PacketMsg) -> Result +pub fn get_module_for_packet_msg(ctx: &Ctx, msg: &PacketMsg) -> Result where Ctx: RouterContext, { let module_id = match msg { PacketMsg::RecvPacket(msg) => ctx .lookup_module_by_port(&msg.packet.destination_port) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, PacketMsg::AckPacket(msg) => ctx .lookup_module_by_port(&msg.packet.source_port) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, PacketMsg::TimeoutPacket(msg) => ctx .lookup_module_by_port(&msg.packet.source_port) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, PacketMsg::TimeoutOnClosePacket(msg) => ctx .lookup_module_by_port(&msg.packet.source_port) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, }; if ctx.router().has_route(&module_id) { Ok(module_id) } else { - Err(Error::route_not_found()) + Err(ChannelError::RouteNotFound) } } @@ -252,7 +252,7 @@ where pub fn packet_dispatch( ctx: &Ctx, msg: &PacketMsg, -) -> Result<(HandlerOutputBuilder<()>, PacketResult), Error> +) -> Result<(HandlerOutputBuilder<()>, PacketResult), PacketError> where Ctx: ChannelReader, { @@ -276,7 +276,7 @@ pub fn packet_callback( module_id: &ModuleId, msg: &PacketMsg, output: &mut HandlerOutputBuilder<()>, -) -> Result<(), Error> +) -> Result<(), PacketError> where Ctx: RouterContext, { @@ -296,21 +296,22 @@ fn do_packet_callback( msg: &PacketMsg, module_output: &mut ModuleOutputBuilder, core_output: &mut HandlerOutputBuilder<()>, -) -> Result<(), Error> { +) -> Result<(), PacketError> { let cb = ctx .router_mut() .get_route_mut(module_id) - .ok_or_else(Error::route_not_found)?; + .ok_or(PacketError::RouteNotFound)?; match msg { PacketMsg::RecvPacket(msg) => { let result = cb.on_recv_packet(module_output, &msg.packet, &msg.signer); match result { OnRecvPacketAck::Nil(write_fn) => { - write_fn(cb.as_any_mut()).map_err(Error::app_module) + write_fn(cb.as_any_mut()).map_err(|e| PacketError::AppModule { description: e }) } OnRecvPacketAck::Successful(ack, write_fn) => { - write_fn(cb.as_any_mut()).map_err(Error::app_module)?; + write_fn(cb.as_any_mut()) + .map_err(|e| PacketError::AppModule { description: e })?; process_write_ack(ctx, msg.packet.clone(), ack.as_ref(), core_output) } @@ -339,7 +340,7 @@ fn process_write_ack( packet: Packet, acknowledgement: &dyn Acknowledgement, core_output: &mut HandlerOutputBuilder<()>, -) -> Result<(), Error> { +) -> Result<(), PacketError> { let HandlerOutput { result, log, diff --git a/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs b/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs index 717a85122..fa4e4d6d8 100644 --- a/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs +++ b/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs @@ -5,7 +5,7 @@ use crate::core::ics04_channel::events::AcknowledgePacket; use crate::core::ics04_channel::handler::verify::verify_packet_acknowledgement_proofs; use crate::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; use crate::core::ics04_channel::packet::{PacketResult, Sequence}; -use crate::core::ics04_channel::{context::ChannelReader, error::Error}; +use crate::core::ics04_channel::{context::ChannelReader, error::PacketError}; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::events::IbcEvent; use crate::handler::{HandlerOutput, HandlerResult}; @@ -22,15 +22,19 @@ pub struct AckPacketResult { pub fn process( ctx: &Ctx, msg: &MsgAcknowledgement, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let packet = &msg.packet; - let source_channel_end = ctx.channel_end(&packet.source_port, &packet.source_channel)?; + let source_channel_end = ctx + .channel_end(&packet.source_port, &packet.source_channel) + .map_err(PacketError::Channel)?; if !source_channel_end.state_matches(&State::Open) { - return Err(Error::channel_closed(packet.source_channel.clone())); + return Err(PacketError::ChannelClosed { + channel_id: packet.source_channel.clone(), + }); } let counterparty = Counterparty::new( @@ -39,19 +43,21 @@ pub fn process( ); if !source_channel_end.counterparty_matches(&counterparty) { - return Err(Error::invalid_packet_counterparty( - packet.destination_port.clone(), - packet.destination_channel.clone(), - )); + return Err(PacketError::InvalidPacketCounterparty { + port_id: packet.destination_port.clone(), + channel_id: packet.destination_channel.clone(), + }); } let source_connection_id = &source_channel_end.connection_hops()[0]; - let connection_end = ctx.connection_end(source_connection_id)?; + let connection_end = ctx + .connection_end(source_connection_id) + .map_err(PacketError::Channel)?; if !connection_end.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - source_channel_end.connection_hops()[0].clone(), - )); + return Err(PacketError::ConnectionNotOpen { + connection_id: source_channel_end.connection_hops()[0].clone(), + }); } // Verify packet commitment @@ -65,7 +71,9 @@ pub fn process( packet.timeout_timestamp, ) { - return Err(Error::incorrect_packet_commitment(packet.sequence)); + return Err(PacketError::IncorrectPacketCommitment { + sequence: packet.sequence, + }); } // Verify the acknowledgement proof @@ -76,17 +84,18 @@ pub fn process( msg.acknowledgement.clone(), &connection_end, &msg.proofs, - )?; + ) + .map_err(PacketError::Channel)?; let result = if source_channel_end.order_matches(&Order::Ordered) { let next_seq_ack = ctx.get_next_sequence_ack(&packet.source_port, &packet.source_channel)?; if packet.sequence != next_seq_ack { - return Err(Error::invalid_packet_sequence( - packet.sequence, - next_seq_ack, - )); + return Err(PacketError::InvalidPacketSequence { + given_sequence: packet.sequence, + next_sequence: next_seq_ack, + }); } PacketResult::Ack(AckPacketResult { diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs b/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs index bd99e5070..760eacde2 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::State as ConnectionState; use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, State}; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; use crate::handler::{HandlerOutput, HandlerResult}; @@ -12,7 +12,7 @@ use crate::prelude::*; pub(crate) fn process( ctx_b: &Ctx, msg: &MsgChannelCloseConfirm, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); // Retrieve the old channel end and validate it against the message. @@ -20,23 +20,25 @@ pub(crate) fn process( // Validate that the channel end is in a state where it can be closed. if chan_end_on_b.state_matches(&State::Closed) { - return Err(Error::channel_closed(msg.chan_id_on_b.clone())); + return Err(ChannelError::ChannelClosed { + channel_id: msg.chan_id_on_b.clone(), + }); } // An OPEN IBC connection running on the local (host) chain should exist. if chan_end_on_b.connection_hops().len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - chan_end_on_b.connection_hops().len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: chan_end_on_b.connection_hops().len(), + }); } let conn_end_on_b = ctx_b.connection_end(&chan_end_on_b.connection_hops()[0])?; if !conn_end_on_b.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - chan_end_on_b.connection_hops()[0].clone(), - )); + return Err(ChannelError::ConnectionNotOpen { + connection_id: chan_end_on_b.connection_hops()[0].clone(), + }); } // Verify proofs @@ -50,17 +52,18 @@ pub(crate) fn process( let chan_id_on_a = chan_end_on_b .counterparty() .channel_id() - .ok_or_else(Error::invalid_counterparty_channel_id)?; - let conn_id_on_a = conn_end_on_b - .counterparty() - .connection_id() - .ok_or_else(|| { - Error::undefined_connection_counterparty(chan_end_on_b.connection_hops()[0].clone()) - })?; + .ok_or(ChannelError::InvalidCounterpartyChannelId)?; + let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( + ChannelError::UndefinedConnectionCounterparty { + connection_id: chan_end_on_b.connection_hops()[0].clone(), + }, + )?; // The client must not be frozen. if client_state_of_a_on_b.is_frozen() { - return Err(Error::frozen_client(client_id_on_b)); + return Err(ChannelError::FrozenClient { + client_id: client_id_on_b, + }); } let expected_chan_end_on_a = ChannelEnd::new( @@ -83,7 +86,7 @@ pub(crate) fn process( chan_id_on_a, &expected_chan_end_on_a, ) - .map_err(Error::verify_channel_failed)?; + .map_err(ChannelError::VerifyChannelFailed)?; } output.log("success: channel close confirm"); diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs b/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs index 829fd2b86..745a5b261 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::State as ConnectionState; use crate::core::ics04_channel::channel::State; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; use crate::handler::{HandlerOutput, HandlerResult}; @@ -11,33 +11,33 @@ use crate::handler::{HandlerOutput, HandlerResult}; pub(crate) fn process( ctx_a: &Ctx, msg: &MsgChannelCloseInit, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let chan_end_on_a = ctx_a.channel_end(&msg.port_id_on_a, &msg.chan_id_on_a)?; // Validate that the channel end is in a state where it can be closed. if chan_end_on_a.state_matches(&State::Closed) { - return Err(Error::invalid_channel_state( - msg.chan_id_on_a.clone(), - chan_end_on_a.state, - )); + return Err(ChannelError::InvalidChannelState { + channel_id: msg.chan_id_on_a.clone(), + state: chan_end_on_a.state, + }); } // An OPEN IBC connection running on the local (host) chain should exist. if chan_end_on_a.connection_hops().len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - chan_end_on_a.connection_hops().len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: chan_end_on_a.connection_hops().len(), + }); } let conn_end_on_a = ctx_a.connection_end(&chan_end_on_a.connection_hops()[0])?; if !conn_end_on_a.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - chan_end_on_a.connection_hops()[0].clone(), - )); + return Err(ChannelError::ConnectionNotOpen { + connection_id: chan_end_on_a.connection_hops()[0].clone(), + }); } output.log("success: channel close init"); diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs index e1b4fc36f..43dde0b90 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::State as ConnectionState; use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, State}; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; use crate::handler::{HandlerOutput, HandlerResult}; @@ -12,7 +12,7 @@ use crate::prelude::*; pub(crate) fn process( ctx_a: &Ctx, msg: &MsgChannelOpenAck, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); // Unwrap the old channel end and validate it against the message. @@ -20,27 +20,27 @@ pub(crate) fn process( // Validate that the channel end is in a state where it can be ack. if !chan_end_on_a.state_matches(&State::Init) { - return Err(Error::invalid_channel_state( - msg.chan_id_on_a.clone(), - chan_end_on_a.state, - )); + return Err(ChannelError::InvalidChannelState { + channel_id: msg.chan_id_on_a.clone(), + state: chan_end_on_a.state, + }); } // An OPEN IBC connection running on the local (host) chain should exist. if chan_end_on_a.connection_hops().len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - chan_end_on_a.connection_hops().len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: chan_end_on_a.connection_hops().len(), + }); } let conn_end_on_a = ctx_a.connection_end(&chan_end_on_a.connection_hops()[0])?; if !conn_end_on_a.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - chan_end_on_a.connection_hops()[0].clone(), - )); + return Err(ChannelError::ConnectionNotOpen { + connection_id: chan_end_on_a.connection_hops()[0].clone(), + }); } // Verify proofs @@ -51,16 +51,17 @@ pub(crate) fn process( ctx_a.client_consensus_state(&client_id_on_a, msg.proof_height_on_b)?; let prefix_on_b = conn_end_on_a.counterparty().prefix(); let port_id_on_b = &chan_end_on_a.counterparty().port_id; - let conn_id_on_b = conn_end_on_a - .counterparty() - .connection_id() - .ok_or_else(|| { - Error::undefined_connection_counterparty(chan_end_on_a.connection_hops()[0].clone()) - })?; + let conn_id_on_b = conn_end_on_a.counterparty().connection_id().ok_or( + ChannelError::UndefinedConnectionCounterparty { + connection_id: chan_end_on_a.connection_hops()[0].clone(), + }, + )?; // The client must not be frozen. if client_state_of_b_on_a.is_frozen() { - return Err(Error::frozen_client(client_id_on_a)); + return Err(ChannelError::FrozenClient { + client_id: client_id_on_a, + }); } let expected_chan_end_on_b = ChannelEnd::new( @@ -85,7 +86,7 @@ pub(crate) fn process( &msg.chan_id_on_b, &expected_chan_end_on_b, ) - .map_err(Error::verify_channel_failed)?; + .map_err(ChannelError::VerifyChannelFailed)?; } output.log("success: channel open ack"); diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs index 4f7901d35..c2f37710c 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs @@ -2,7 +2,7 @@ use crate::core::ics03_connection::connection::State as ConnectionState; use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, State}; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; use crate::handler::{HandlerOutput, HandlerResult}; @@ -12,7 +12,7 @@ use crate::prelude::*; pub(crate) fn process( ctx_b: &Ctx, msg: &MsgChannelOpenConfirm, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); // Unwrap the old channel end and validate it against the message. @@ -20,26 +20,26 @@ pub(crate) fn process( // Validate that the channel end is in a state where it can be confirmed. if !chan_end_on_b.state_matches(&State::TryOpen) { - return Err(Error::invalid_channel_state( - msg.chan_id_on_b.clone(), - chan_end_on_b.state, - )); + return Err(ChannelError::InvalidChannelState { + channel_id: msg.chan_id_on_b.clone(), + state: chan_end_on_b.state, + }); } // An OPEN IBC connection running on the local (host) chain should exist. if chan_end_on_b.connection_hops().len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - chan_end_on_b.connection_hops().len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: chan_end_on_b.connection_hops().len(), + }); } let conn_end_on_b = ctx_b.connection_end(&chan_end_on_b.connection_hops()[0])?; if !conn_end_on_b.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - chan_end_on_b.connection_hops()[0].clone(), - )); + return Err(ChannelError::ConnectionNotOpen { + connection_id: chan_end_on_b.connection_hops()[0].clone(), + }); } // Verify proofs @@ -53,17 +53,18 @@ pub(crate) fn process( let chan_id_on_a = chan_end_on_b .counterparty() .channel_id() - .ok_or_else(Error::invalid_counterparty_channel_id)?; - let conn_id_on_a = conn_end_on_b - .counterparty() - .connection_id() - .ok_or_else(|| { - Error::undefined_connection_counterparty(chan_end_on_b.connection_hops()[0].clone()) - })?; + .ok_or(ChannelError::InvalidCounterpartyChannelId)?; + let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( + ChannelError::UndefinedConnectionCounterparty { + connection_id: chan_end_on_b.connection_hops()[0].clone(), + }, + )?; // The client must not be frozen. if client_state_of_a_on_b.is_frozen() { - return Err(Error::frozen_client(client_id_on_b)); + return Err(ChannelError::FrozenClient { + client_id: client_id_on_b, + }); } let expected_chan_end_on_a = ChannelEnd::new( @@ -86,7 +87,7 @@ pub(crate) fn process( chan_id_on_a, &expected_chan_end_on_a, ) - .map_err(Error::verify_channel_failed)?; + .map_err(ChannelError::VerifyChannelFailed)?; } output.log("success: channel open confirm "); diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs index 3868f6bd9..e90a07a31 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs @@ -2,7 +2,7 @@ use crate::core::ics04_channel::channel::{ChannelEnd, State}; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; use crate::core::ics24_host::identifier::ChannelId; @@ -13,14 +13,14 @@ use crate::prelude::*; pub(crate) fn process( ctx_a: &Ctx, msg: &MsgChannelOpenInit, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); if msg.chan_end_on_a.connection_hops().len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - msg.chan_end_on_a.connection_hops().len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: msg.chan_end_on_a.connection_hops().len(), + }); } // An IBC connection running on the local (host) chain should exist. @@ -28,12 +28,12 @@ pub(crate) fn process( let conn_version = match conn_end_on_a.versions() { [version] => version, - _ => return Err(Error::invalid_version_length_connection()), + _ => return Err(ChannelError::InvalidVersionLengthConnection), }; let channel_feature = msg.chan_end_on_a.ordering().to_string(); if !conn_version.is_supported_feature(channel_feature) { - return Err(Error::channel_feature_not_suported_by_connection()); + return Err(ChannelError::ChannelFeatureNotSuportedByConnection); } let chan_end_on_a = ChannelEnd::new( diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs index b26c630fa..046012260 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs @@ -3,7 +3,7 @@ use crate::core::ics03_connection::connection::State as ConnectionState; use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, State}; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::{ChannelIdState, ChannelResult}; use crate::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; use crate::core::ics04_channel::Version; @@ -15,32 +15,32 @@ use crate::prelude::*; pub(crate) fn process( ctx_b: &Ctx, msg: &MsgChannelOpenTry, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); // An IBC connection running on the local (host) chain should exist. if msg.chan_end_on_b.connection_hops().len() != 1 { - return Err(Error::invalid_connection_hops_length( - 1, - msg.chan_end_on_b.connection_hops().len(), - )); + return Err(ChannelError::InvalidConnectionHopsLength { + expected: 1, + actual: msg.chan_end_on_b.connection_hops().len(), + }); } let conn_end_on_b = ctx_b.connection_end(&msg.chan_end_on_b.connection_hops()[0])?; if !conn_end_on_b.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - msg.chan_end_on_b.connection_hops()[0].clone(), - )); + return Err(ChannelError::ConnectionNotOpen { + connection_id: msg.chan_end_on_b.connection_hops()[0].clone(), + }); } let conn_version = match conn_end_on_b.versions() { [version] => version, - _ => return Err(Error::invalid_version_length_connection()), + _ => return Err(ChannelError::InvalidVersionLengthConnection), }; let channel_feature = msg.chan_end_on_b.ordering().to_string(); if !conn_version.is_supported_feature(channel_feature) { - return Err(Error::channel_feature_not_suported_by_connection()); + return Err(ChannelError::ChannelFeatureNotSuportedByConnection); } // Verify proofs @@ -55,19 +55,18 @@ pub(crate) fn process( .chan_end_on_b .counterparty() .channel_id() - .ok_or_else(Error::invalid_counterparty_channel_id)?; - let conn_id_on_a = conn_end_on_b - .counterparty() - .connection_id() - .ok_or_else(|| { - Error::undefined_connection_counterparty( - msg.chan_end_on_b.connection_hops()[0].clone(), - ) - })?; + .ok_or(ChannelError::InvalidCounterpartyChannelId)?; + let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( + ChannelError::UndefinedConnectionCounterparty { + connection_id: msg.chan_end_on_b.connection_hops()[0].clone(), + }, + )?; // The client must not be frozen. if client_state_of_a_on_b.is_frozen() { - return Err(Error::frozen_client(client_id_on_b)); + return Err(ChannelError::FrozenClient { + client_id: client_id_on_b, + }); } let expected_chan_end_on_a = ChannelEnd::new( @@ -90,7 +89,7 @@ pub(crate) fn process( chan_id_on_a, &expected_chan_end_on_a, ) - .map_err(Error::verify_channel_failed)?; + .map_err(ChannelError::VerifyChannelFailed)?; } let chan_end_on_b = ChannelEnd::new( @@ -152,7 +151,7 @@ mod tests { ctx: MockContext, msg: ChannelMsg, want_pass: bool, - match_error: Box, + match_error: Box, } // Some general-purpose variable to parametrize the messages and the context. @@ -199,12 +198,11 @@ mod tests { match_error: { let connection_id = msg.chan_end_on_b.connection_hops()[0].clone(); Box::new(move |e| match e { - error::ErrorDetail::Ics03Connection(e) => { + error::ChannelError::Connection(e) => { assert_eq!( - e.source, - ics03_error::ErrorDetail::ConnectionNotFound( - ics03_error::ConnectionNotFoundSubdetail { connection_id } - ) + e.to_string(), + ics03_error::ConnectionError::ConnectionNotFound { connection_id } + .to_string() ); } _ => { @@ -226,19 +224,15 @@ mod tests { msg: ChannelMsg::ChannelOpenTry(msg.clone()), want_pass: false, match_error: Box::new(|e| match e { - error::ErrorDetail::Ics03Connection(e) => { + error::ChannelError::Connection(e) => { assert_eq!( - e.source, - ics03_error::ErrorDetail::Ics02Client( - ics03_error::Ics02ClientSubdetail { - source: ics02_error::ErrorDetail::ClientNotFound( - ics02_error::ClientNotFoundSubdetail { - client_id: ClientId::new(mock_client_type(), 45) - .unwrap() - } - ) + e.to_string(), + ics03_error::ConnectionError::Client( + ics02_error::ClientError::ClientNotFound { + client_id: ClientId::new(mock_client_type(), 45).unwrap() } ) + .to_string() ); } _ => { @@ -301,7 +295,7 @@ mod tests { e, ); - (test.match_error)(e.0); + (test.match_error)(e); } } } diff --git a/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs b/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs index 320ec85f8..2ebba2f37 100644 --- a/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs +++ b/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs @@ -1,7 +1,7 @@ use crate::core::ics03_connection::connection::State as ConnectionState; use crate::core::ics04_channel::channel::{Counterparty, Order, State}; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::events::ReceivePacket; use crate::core::ics04_channel::handler::verify::verify_packet_recv_proofs; use crate::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; @@ -10,6 +10,7 @@ use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::events::IbcEvent; use crate::handler::{HandlerOutput, HandlerResult}; use crate::timestamp::Expiry; +use alloc::string::ToString; #[derive(Clone, Debug)] pub enum RecvPacketResult { @@ -30,19 +31,20 @@ pub enum RecvPacketResult { pub fn process( ctx: &Ctx, msg: &MsgRecvPacket, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let packet = &msg.packet; - let dest_channel_end = - ctx.channel_end(&packet.destination_port, &packet.destination_channel)?; + let dest_channel_end = ctx + .channel_end(&packet.destination_port, &packet.destination_channel) + .map_err(PacketError::Channel)?; if !dest_channel_end.state_matches(&State::Open) { - return Err(Error::invalid_channel_state( - packet.source_channel.clone(), - dest_channel_end.state, - )); + return Err(PacketError::InvalidChannelState { + channel_id: packet.source_channel.clone(), + state: dest_channel_end.state, + }); } let counterparty = Counterparty::new( @@ -51,32 +53,34 @@ pub fn process( ); if !dest_channel_end.counterparty_matches(&counterparty) { - return Err(Error::invalid_packet_counterparty( - packet.source_port.clone(), - packet.source_channel.clone(), - )); + return Err(PacketError::InvalidPacketCounterparty { + port_id: packet.source_port.clone(), + channel_id: packet.source_channel.clone(), + }); } let dest_connection_id = &dest_channel_end.connection_hops()[0]; - let connection_end = ctx.connection_end(dest_connection_id)?; + let connection_end = ctx + .connection_end(dest_connection_id) + .map_err(PacketError::Channel)?; if !connection_end.state_matches(&ConnectionState::Open) { - return Err(Error::connection_not_open( - dest_channel_end.connection_hops()[0].clone(), - )); + return Err(PacketError::ConnectionNotOpen { + connection_id: dest_channel_end.connection_hops()[0].clone(), + }); } - let latest_height = ChannelReader::host_height(ctx)?; + let latest_height = ChannelReader::host_height(ctx).map_err(PacketError::Channel)?; if packet.timeout_height.has_expired(latest_height) { - return Err(Error::low_packet_height( - latest_height, - packet.timeout_height, - )); + return Err(PacketError::LowPacketHeight { + chain_height: latest_height, + timeout_height: packet.timeout_height, + }); } - let latest_timestamp = ChannelReader::host_timestamp(ctx)?; + let latest_timestamp = ChannelReader::host_timestamp(ctx).map_err(PacketError::Channel)?; if let Expiry::Expired = latest_timestamp.check_expiry(&packet.timeout_timestamp) { - return Err(Error::low_packet_timestamp()); + return Err(PacketError::LowPacketTimestamp); } verify_packet_recv_proofs( @@ -85,7 +89,8 @@ pub fn process( packet, &connection_end, &msg.proofs, - )?; + ) + .map_err(PacketError::Channel)?; let result = if dest_channel_end.order_matches(&Order::Ordered) { let next_seq_recv = @@ -100,10 +105,10 @@ pub fn process( return Ok(output.with_result(PacketResult::Recv(RecvPacketResult::NoOp))); } else if packet.sequence != next_seq_recv { - return Err(Error::invalid_packet_sequence( - packet.sequence, - next_seq_recv, - )); + return Err(PacketError::InvalidPacketSequence { + given_sequence: packet.sequence, + next_sequence: next_seq_recv, + }); } PacketResult::Recv(RecvPacketResult::Ordered { @@ -128,7 +133,13 @@ pub fn process( return Ok(output.with_result(PacketResult::Recv(RecvPacketResult::NoOp))); } - Err(e) if e.detail() == Error::packet_receipt_not_found(packet.sequence).detail() => { + Err(e) + if e.to_string() + == PacketError::PacketReceiptNotFound { + sequence: packet.sequence, + } + .to_string() => + { // store a receipt that does not contain any data PacketResult::Recv(RecvPacketResult::Unordered { port_id: packet.destination_port.clone(), @@ -137,7 +148,7 @@ pub fn process( receipt: Receipt::Ok, }) } - Err(_) => return Err(Error::implementation_specific()), + Err(_) => return Err(PacketError::ImplementationSpecific), } }; diff --git a/crates/ibc/src/core/ics04_channel/handler/send_packet.rs b/crates/ibc/src/core/ics04_channel/handler/send_packet.rs index 9b610dc8f..81cae8f80 100644 --- a/crates/ibc/src/core/ics04_channel/handler/send_packet.rs +++ b/crates/ibc/src/core/ics04_channel/handler/send_packet.rs @@ -3,7 +3,7 @@ use crate::core::ics04_channel::channel::State; use crate::core::ics04_channel::commitment::PacketCommitment; use crate::core::ics04_channel::events::SendPacket; use crate::core::ics04_channel::packet::Sequence; -use crate::core::ics04_channel::{context::SendPacketReader, error::Error, packet::Packet}; +use crate::core::ics04_channel::{context::SendPacketReader, error::PacketError, packet::Packet}; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::events::IbcEvent; use crate::handler::{HandlerOutput, HandlerResult}; @@ -22,13 +22,15 @@ pub struct SendPacketResult { pub fn send_packet( ctx: &impl SendPacketReader, packet: Packet, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let source_channel_end = ctx.channel_end(&packet.source_port, &packet.source_channel)?; if source_channel_end.state_matches(&State::Closed) { - return Err(Error::channel_closed(packet.source_channel)); + return Err(PacketError::ChannelClosed { + channel_id: packet.source_channel, + }); } let counterparty = Counterparty::new( @@ -37,10 +39,10 @@ pub fn send_packet( ); if !source_channel_end.counterparty_matches(&counterparty) { - return Err(Error::invalid_packet_counterparty( - packet.destination_port.clone(), - packet.destination_channel, - )); + return Err(PacketError::InvalidPacketCounterparty { + port_id: packet.destination_port.clone(), + channel_id: packet.destination_channel, + }); } let source_connection_id = &source_channel_end.connection_hops()[0]; let connection_end = ctx.connection_end(source_connection_id)?; @@ -51,32 +53,34 @@ pub fn send_packet( // prevent accidental sends with clients that cannot be updated if client_state.is_frozen() { - return Err(Error::frozen_client(connection_end.client_id().clone())); + return Err(PacketError::FrozenClient { + client_id: connection_end.client_id().clone(), + }); } let latest_height = client_state.latest_height(); if packet.timeout_height.has_expired(latest_height) { - return Err(Error::low_packet_height( - latest_height, - packet.timeout_height, - )); + return Err(PacketError::LowPacketHeight { + chain_height: latest_height, + timeout_height: packet.timeout_height, + }); } let consensus_state = ctx.client_consensus_state(&client_id, latest_height)?; let latest_timestamp = consensus_state.timestamp(); let packet_timestamp = packet.timeout_timestamp; if let Expiry::Expired = latest_timestamp.check_expiry(&packet_timestamp) { - return Err(Error::low_packet_timestamp()); + return Err(PacketError::LowPacketTimestamp); } let next_seq_send = ctx.get_next_sequence_send(&packet.source_port, &packet.source_channel)?; if packet.sequence != next_seq_send { - return Err(Error::invalid_packet_sequence( - packet.sequence, - next_seq_send, - )); + return Err(PacketError::InvalidPacketSequence { + given_sequence: packet.sequence, + next_sequence: next_seq_send, + }); } output.log("success: packet send "); diff --git a/crates/ibc/src/core/ics04_channel/handler/timeout.rs b/crates/ibc/src/core/ics04_channel/handler/timeout.rs index eab4b8d12..b040dad31 100644 --- a/crates/ibc/src/core/ics04_channel/handler/timeout.rs +++ b/crates/ibc/src/core/ics04_channel/handler/timeout.rs @@ -6,7 +6,7 @@ use crate::core::ics04_channel::handler::verify::{ }; use crate::core::ics04_channel::msgs::timeout::MsgTimeout; use crate::core::ics04_channel::packet::{PacketResult, Sequence}; -use crate::core::ics04_channel::{context::ChannelReader, error::Error}; +use crate::core::ics04_channel::{context::ChannelReader, error::PacketError}; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::events::IbcEvent; use crate::handler::{HandlerOutput, HandlerResult}; @@ -29,15 +29,19 @@ pub struct TimeoutPacketResult { pub fn process( ctx: &Ctx, msg: &MsgTimeout, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let packet = &msg.packet; - let mut source_channel_end = ctx.channel_end(&packet.source_port, &packet.source_channel)?; + let mut source_channel_end = ctx + .channel_end(&packet.source_port, &packet.source_channel) + .map_err(PacketError::Channel)?; if !source_channel_end.state_matches(&State::Open) { - return Err(Error::channel_closed(packet.source_channel.clone())); + return Err(PacketError::ChannelClosed { + channel_id: packet.source_channel.clone(), + }); } let counterparty = Counterparty::new( @@ -46,14 +50,16 @@ pub fn process( ); if !source_channel_end.counterparty_matches(&counterparty) { - return Err(Error::invalid_packet_counterparty( - packet.destination_port.clone(), - packet.destination_channel.clone(), - )); + return Err(PacketError::InvalidPacketCounterparty { + port_id: packet.destination_port.clone(), + channel_id: packet.destination_channel.clone(), + }); } let source_connection_id = source_channel_end.connection_hops()[0].clone(); - let connection_end = ctx.connection_end(&source_connection_id)?; + let connection_end = ctx + .connection_end(&source_connection_id) + .map_err(PacketError::Channel)?; let client_id = connection_end.client_id().clone(); @@ -61,22 +67,24 @@ pub fn process( let proof_height = msg.proofs.height(); if packet.timeout_height.has_expired(proof_height) { - return Err(Error::packet_timeout_height_not_reached( - packet.timeout_height, - proof_height, - )); + return Err(PacketError::PacketTimeoutHeightNotReached { + timeout_height: packet.timeout_height, + chain_height: proof_height, + }); } - let consensus_state = ctx.client_consensus_state(&client_id, proof_height)?; + let consensus_state = ctx + .client_consensus_state(&client_id, proof_height) + .map_err(PacketError::Channel)?; let proof_timestamp = consensus_state.timestamp(); let packet_timestamp = packet.timeout_timestamp; if let Expiry::Expired = packet_timestamp.check_expiry(&proof_timestamp) { - return Err(Error::packet_timeout_timestamp_not_reached( - packet_timestamp, - proof_timestamp, - )); + return Err(PacketError::PacketTimeoutTimestampNotReached { + timeout_timestamp: packet_timestamp, + chain_timestamp: proof_timestamp, + }); } //verify packet commitment @@ -89,15 +97,17 @@ pub fn process( packet.timeout_timestamp, ); if packet_commitment != expected_commitment { - return Err(Error::incorrect_packet_commitment(packet.sequence)); + return Err(PacketError::IncorrectPacketCommitment { + sequence: packet.sequence, + }); } let result = if source_channel_end.order_matches(&Order::Ordered) { if packet.sequence < msg.next_sequence_recv { - return Err(Error::invalid_packet_sequence( - packet.sequence, - msg.next_sequence_recv, - )); + return Err(PacketError::InvalidPacketSequence { + given_sequence: packet.sequence, + next_sequence: msg.next_sequence_recv, + }); } verify_next_sequence_recv( ctx, @@ -106,7 +116,8 @@ pub fn process( packet.clone(), msg.next_sequence_recv, &msg.proofs, - )?; + ) + .map_err(PacketError::Channel)?; source_channel_end.state = State::Closed; PacketResult::Timeout(TimeoutPacketResult { @@ -122,7 +133,8 @@ pub fn process( &connection_end, packet.clone(), &msg.proofs, - )?; + ) + .map_err(PacketError::Channel)?; PacketResult::Timeout(TimeoutPacketResult { port_id: packet.source_port.clone(), diff --git a/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs b/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs index 1ae91ec70..9b3036230 100644 --- a/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs +++ b/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs @@ -8,7 +8,7 @@ use crate::core::ics04_channel::handler::verify::{ use crate::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; use crate::core::ics04_channel::packet::PacketResult; use crate::core::ics04_channel::{ - context::ChannelReader, error::Error, handler::timeout::TimeoutPacketResult, + context::ChannelReader, error::PacketError, handler::timeout::TimeoutPacketResult, }; use crate::events::IbcEvent; use crate::handler::{HandlerOutput, HandlerResult}; @@ -18,12 +18,14 @@ use crate::proofs::{ProofError, Proofs}; pub fn process( ctx: &Ctx, msg: &MsgTimeoutOnClose, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); let packet = &msg.packet; - let source_channel_end = ctx.channel_end(&packet.source_port, &packet.source_channel)?; + let source_channel_end = ctx + .channel_end(&packet.source_port, &packet.source_channel) + .map_err(PacketError::Channel)?; let counterparty = Counterparty::new( packet.destination_port.clone(), @@ -31,14 +33,16 @@ pub fn process( ); if !source_channel_end.counterparty_matches(&counterparty) { - return Err(Error::invalid_packet_counterparty( - packet.destination_port.clone(), - packet.destination_channel.clone(), - )); + return Err(PacketError::InvalidPacketCounterparty { + port_id: packet.destination_port.clone(), + channel_id: packet.destination_channel.clone(), + }); } let source_connection_id = source_channel_end.connection_hops()[0].clone(); - let connection_end = ctx.connection_end(&source_connection_id)?; + let connection_end = ctx + .connection_end(&source_connection_id) + .map_err(PacketError::Channel)?; //verify the packet was sent, check the store let packet_commitment = @@ -50,7 +54,9 @@ pub fn process( packet.timeout_timestamp, ); if packet_commitment != expected_commitment { - return Err(Error::incorrect_packet_commitment(packet.sequence)); + return Err(PacketError::IncorrectPacketCommitment { + sequence: packet.sequence, + }); } let expected_counterparty = Counterparty::new( @@ -59,9 +65,12 @@ pub fn process( ); let counterparty = connection_end.counterparty(); - let ccid = counterparty.connection_id().ok_or_else(|| { - Error::undefined_connection_counterparty(source_channel_end.connection_hops()[0].clone()) - })?; + let ccid = + counterparty + .connection_id() + .ok_or(PacketError::UndefinedConnectionCounterparty { + connection_id: source_channel_end.connection_hops()[0].clone(), + })?; let expected_connection_hops = vec![ccid.clone()]; @@ -76,10 +85,10 @@ pub fn process( // The message's proofs have the channel proof as `other_proof` let proof_close = match msg.proofs.other_proof() { Some(p) => p.clone(), - None => return Err(Error::invalid_proof(ProofError::empty_proof())), + None => return Err(PacketError::InvalidProof(ProofError::EmptyProof)), }; let proofs = Proofs::new(proof_close, None, None, None, msg.proofs.height()) - .map_err(Error::invalid_proof)?; + .map_err(PacketError::InvalidProof)?; verify_channel_proofs( ctx, msg.proofs.height(), @@ -87,14 +96,15 @@ pub fn process( &connection_end, &expected_channel_end, &proofs, - )?; + ) + .map_err(PacketError::Channel)?; let result = if source_channel_end.order_matches(&Order::Ordered) { if packet.sequence < msg.next_sequence_recv { - return Err(Error::invalid_packet_sequence( - packet.sequence, - msg.next_sequence_recv, - )); + return Err(PacketError::InvalidPacketSequence { + given_sequence: packet.sequence, + next_sequence: msg.next_sequence_recv, + }); } verify_next_sequence_recv( ctx, @@ -103,7 +113,8 @@ pub fn process( packet.clone(), msg.next_sequence_recv, &msg.proofs, - )?; + ) + .map_err(PacketError::Channel)?; PacketResult::Timeout(TimeoutPacketResult { port_id: packet.source_port.clone(), @@ -118,7 +129,8 @@ pub fn process( &connection_end, packet.clone(), &msg.proofs, - )?; + ) + .map_err(PacketError::Channel)?; PacketResult::Timeout(TimeoutPacketResult { port_id: packet.source_port.clone(), diff --git a/crates/ibc/src/core/ics04_channel/handler/verify.rs b/crates/ibc/src/core/ics04_channel/handler/verify.rs index 07a07d8ae..f52b8951c 100644 --- a/crates/ibc/src/core/ics04_channel/handler/verify.rs +++ b/crates/ibc/src/core/ics04_channel/handler/verify.rs @@ -1,7 +1,7 @@ use crate::core::ics03_connection::connection::ConnectionEnd; use crate::core::ics04_channel::channel::ChannelEnd; use crate::core::ics04_channel::context::ChannelReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::msgs::acknowledgement::Acknowledgement; use crate::core::ics04_channel::packet::{Packet, Sequence}; use crate::prelude::*; @@ -16,7 +16,7 @@ pub fn verify_channel_proofs( connection_end: &ConnectionEnd, expected_chan: &ChannelEnd, proofs: &Proofs, -) -> Result<(), Error> { +) -> Result<(), ChannelError> { // This is the client which will perform proof verification. let client_id = connection_end.client_id().clone(); @@ -24,7 +24,7 @@ pub fn verify_channel_proofs( // The client must not be frozen. if client_state.is_frozen() { - return Err(Error::frozen_client(client_id)); + return Err(ChannelError::FrozenClient { client_id }); } let consensus_state = ctx.client_consensus_state(&client_id, proofs.height())?; @@ -41,10 +41,10 @@ pub fn verify_channel_proofs( channel_end .counterparty() .channel_id() - .ok_or_else(Error::invalid_counterparty_channel_id)?, + .ok_or(ChannelError::InvalidCounterpartyChannelId)?, expected_chan, ) - .map_err(Error::verify_channel_failed) + .map_err(ChannelError::VerifyChannelFailed) } /// Entry point for verifying all proofs bundled in a ICS4 packet recv. message. @@ -54,13 +54,15 @@ pub fn verify_packet_recv_proofs( packet: &Packet, connection_end: &ConnectionEnd, proofs: &Proofs, -) -> Result<(), Error> { +) -> Result<(), ChannelError> { let client_id = connection_end.client_id(); let client_state = ctx.client_state(client_id)?; // The client must not be frozen. if client_state.is_frozen() { - return Err(Error::frozen_client(client_id.clone())); + return Err(ChannelError::FrozenClient { + client_id: client_id.clone(), + }); } let consensus_state = ctx.client_consensus_state(client_id, proofs.height())?; @@ -84,7 +86,10 @@ pub fn verify_packet_recv_proofs( packet.sequence, commitment, ) - .map_err(|e| Error::packet_verification_failed(packet.sequence, e))?; + .map_err(|e| ChannelError::PacketVerificationFailed { + sequence: packet.sequence, + client_error: e, + })?; Ok(()) } @@ -97,13 +102,15 @@ pub fn verify_packet_acknowledgement_proofs( acknowledgement: Acknowledgement, connection_end: &ConnectionEnd, proofs: &Proofs, -) -> Result<(), Error> { +) -> Result<(), ChannelError> { let client_id = connection_end.client_id(); let client_state = ctx.client_state(client_id)?; // The client must not be frozen. if client_state.is_frozen() { - return Err(Error::frozen_client(client_id.clone())); + return Err(ChannelError::FrozenClient { + client_id: client_id.clone(), + }); } let consensus_state = ctx.client_consensus_state(client_id, proofs.height())?; @@ -123,7 +130,10 @@ pub fn verify_packet_acknowledgement_proofs( packet.sequence, ack_commitment, ) - .map_err(|e| Error::packet_verification_failed(packet.sequence, e))?; + .map_err(|e| ChannelError::PacketVerificationFailed { + sequence: packet.sequence, + client_error: e, + })?; Ok(()) } @@ -136,13 +146,15 @@ pub fn verify_next_sequence_recv( packet: Packet, seq: Sequence, proofs: &Proofs, -) -> Result<(), Error> { +) -> Result<(), ChannelError> { let client_id = connection_end.client_id(); let client_state = ctx.client_state(client_id)?; // The client must not be frozen. if client_state.is_frozen() { - return Err(Error::frozen_client(client_id.clone())); + return Err(ChannelError::FrozenClient { + client_id: client_id.clone(), + }); } let consensus_state = ctx.client_consensus_state(client_id, proofs.height())?; @@ -159,7 +171,10 @@ pub fn verify_next_sequence_recv( &packet.destination_channel, packet.sequence, ) - .map_err(|e| Error::packet_verification_failed(seq, e))?; + .map_err(|e| ChannelError::PacketVerificationFailed { + sequence: seq, + client_error: e, + })?; Ok(()) } @@ -170,13 +185,15 @@ pub fn verify_packet_receipt_absence( connection_end: &ConnectionEnd, packet: Packet, proofs: &Proofs, -) -> Result<(), Error> { +) -> Result<(), ChannelError> { let client_id = connection_end.client_id(); let client_state = ctx.client_state(client_id)?; // The client must not be frozen. if client_state.is_frozen() { - return Err(Error::frozen_client(client_id.clone())); + return Err(ChannelError::FrozenClient { + client_id: client_id.clone(), + }); } let consensus_state = ctx.client_consensus_state(client_id, proofs.height())?; @@ -193,7 +210,10 @@ pub fn verify_packet_receipt_absence( &packet.destination_channel, packet.sequence, ) - .map_err(|e| Error::packet_verification_failed(packet.sequence, e))?; + .map_err(|e| ChannelError::PacketVerificationFailed { + sequence: packet.sequence, + client_error: e, + })?; Ok(()) } diff --git a/crates/ibc/src/core/ics04_channel/handler/write_acknowledgement.rs b/crates/ibc/src/core/ics04_channel/handler/write_acknowledgement.rs index 68ec0919e..21b87e6db 100644 --- a/crates/ibc/src/core/ics04_channel/handler/write_acknowledgement.rs +++ b/crates/ibc/src/core/ics04_channel/handler/write_acknowledgement.rs @@ -3,7 +3,7 @@ use crate::core::ics04_channel::commitment::AcknowledgementCommitment; use crate::core::ics04_channel::events::WriteAcknowledgement; use crate::core::ics04_channel::msgs::acknowledgement::Acknowledgement; use crate::core::ics04_channel::packet::{Packet, PacketResult, Sequence}; -use crate::core::ics04_channel::{context::ChannelReader, error::Error}; +use crate::core::ics04_channel::{context::ChannelReader, error::PacketError}; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::prelude::*; use crate::{ @@ -23,17 +23,18 @@ pub fn process( ctx: &Ctx, packet: Packet, ack: Acknowledgement, -) -> HandlerResult { +) -> HandlerResult { let mut output = HandlerOutput::builder(); - let dest_channel_end = - ctx.channel_end(&packet.destination_port, &packet.destination_channel)?; + let dest_channel_end = ctx + .channel_end(&packet.destination_port, &packet.destination_channel) + .map_err(PacketError::Channel)?; if !dest_channel_end.state_matches(&State::Open) { - return Err(Error::invalid_channel_state( - packet.source_channel, - dest_channel_end.state, - )); + return Err(PacketError::InvalidChannelState { + channel_id: packet.source_channel, + state: dest_channel_end.state, + }); } // NOTE: IBC app modules might have written the acknowledgement synchronously on @@ -44,14 +45,22 @@ pub fn process( &packet.destination_channel, packet.sequence, ) { - Ok(_) => return Err(Error::acknowledgement_exists(packet.sequence)), + Ok(_) => { + return Err(PacketError::AcknowledgementExists { + sequence: packet.sequence, + }) + } Err(e) - if e.detail() == Error::packet_acknowledgement_not_found(packet.sequence).detail() => {} + if e.to_string() + == PacketError::PacketAcknowledgementNotFound { + sequence: packet.sequence, + } + .to_string() => {} Err(e) => return Err(e), } if ack.is_empty() { - return Err(Error::invalid_acknowledgement()); + return Err(PacketError::InvalidAcknowledgement); } let result = PacketResult::WriteAck(WriteAckPacketResult { diff --git a/crates/ibc/src/core/ics04_channel/msgs.rs b/crates/ibc/src/core/ics04_channel/msgs.rs index 54bbbfd61..e85e127a3 100644 --- a/crates/ibc/src/core/ics04_channel/msgs.rs +++ b/crates/ibc/src/core/ics04_channel/msgs.rs @@ -1,7 +1,7 @@ //! Message definitions for all ICS4 domain types: channel open & close handshake datagrams, as well //! as packets. -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; use crate::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; use crate::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; @@ -42,26 +42,26 @@ pub enum ChannelMsg { } impl ChannelMsg { - pub(super) fn lookup_module(&self, ctx: &impl RouterContext) -> Result { + pub(super) fn lookup_module(&self, ctx: &impl RouterContext) -> Result { let module_id = match self { ChannelMsg::ChannelOpenInit(msg) => ctx .lookup_module_by_port(&msg.port_id_on_a) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, ChannelMsg::ChannelOpenTry(msg) => ctx .lookup_module_by_port(&msg.port_id_on_b) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, ChannelMsg::ChannelOpenAck(msg) => ctx .lookup_module_by_port(&msg.port_id_on_a) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, ChannelMsg::ChannelOpenConfirm(msg) => ctx .lookup_module_by_port(&msg.port_id_on_b) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, ChannelMsg::ChannelCloseInit(msg) => ctx .lookup_module_by_port(&msg.port_id_on_a) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, ChannelMsg::ChannelCloseConfirm(msg) => ctx .lookup_module_by_port(&msg.port_id_on_b) - .map_err(Error::ics05_port)?, + .map_err(ChannelError::Port)?, }; Ok(module_id) } diff --git a/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs b/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs index 3259d48b1..a903a29e9 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs @@ -4,7 +4,7 @@ use derive_more::{From, Into}; use ibc_proto::ibc::core::channel::v1::MsgAcknowledgement as RawMsgAcknowledgement; use ibc_proto::protobuf::Protobuf; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::packet::Packet; use crate::proofs::Proofs; use crate::signer::Signer; @@ -69,7 +69,7 @@ impl MsgAcknowledgement { } impl Msg for MsgAcknowledgement { - type ValidationError = Error; + type ValidationError = PacketError; type Raw = RawMsgAcknowledgement; fn route(&self) -> String { @@ -84,31 +84,31 @@ impl Msg for MsgAcknowledgement { impl Protobuf for MsgAcknowledgement {} impl TryFrom for MsgAcknowledgement { - type Error = Error; + type Error = PacketError; fn try_from(raw_msg: RawMsgAcknowledgement) -> Result { let proofs = Proofs::new( raw_msg .proof_acked .try_into() - .map_err(Error::invalid_proof)?, + .map_err(PacketError::InvalidProof)?, None, None, None, raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, + .ok_or(PacketError::MissingHeight)?, ) - .map_err(Error::invalid_proof)?; + .map_err(PacketError::InvalidProof)?; Ok(MsgAcknowledgement { packet: raw_msg .packet - .ok_or_else(Error::missing_packet)? + .ok_or(PacketError::MissingPacket)? .try_into()?, acknowledgement: raw_msg.acknowledgement.into(), - signer: raw_msg.signer.parse().map_err(Error::signer)?, + signer: raw_msg.signer.parse().map_err(PacketError::Signer)?, proofs, }) } @@ -166,7 +166,7 @@ mod test { use ibc_proto::ibc::core::channel::v1::MsgAcknowledgement as RawMsgAcknowledgement; - use crate::core::ics04_channel::error::Error; + use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::msgs::acknowledgement::test_util::get_dummy_raw_msg_acknowledgement; use crate::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; use crate::test_utils::get_dummy_bech32_account; @@ -223,7 +223,7 @@ mod test { ]; for test in tests { - let res_msg: Result = test.raw.clone().try_into(); + let res_msg: Result = test.raw.clone().try_into(); assert_eq!( res_msg.is_ok(), diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs index 4b7b25daa..213e02f81 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs @@ -5,7 +5,7 @@ use ibc_proto::protobuf::Protobuf; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::signer::Signer; use crate::tx_msg::Msg; @@ -45,7 +45,7 @@ impl MsgChannelCloseConfirm { } impl Msg for MsgChannelCloseConfirm { - type ValidationError = Error; + type ValidationError = ChannelError; type Raw = RawMsgChannelCloseConfirm; fn route(&self) -> String { @@ -60,21 +60,24 @@ impl Msg for MsgChannelCloseConfirm { impl Protobuf for MsgChannelCloseConfirm {} impl TryFrom for MsgChannelCloseConfirm { - type Error = Error; + type Error = ChannelError; fn try_from(raw_msg: RawMsgChannelCloseConfirm) -> Result { Ok(MsgChannelCloseConfirm { - port_id_on_b: raw_msg.port_id.parse().map_err(Error::identifier)?, - chan_id_on_b: raw_msg.channel_id.parse().map_err(Error::identifier)?, + port_id_on_b: raw_msg.port_id.parse().map_err(ChannelError::Identifier)?, + chan_id_on_b: raw_msg + .channel_id + .parse() + .map_err(ChannelError::Identifier)?, proof_chan_end_on_a: raw_msg .proof_init .try_into() - .map_err(Error::invalid_proof)?, + .map_err(ChannelError::InvalidProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, - signer: raw_msg.signer.parse().map_err(Error::signer)?, + .ok_or(ChannelError::MissingHeight)?, + signer: raw_msg.signer.parse().map_err(ChannelError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs index b447d1b58..c3b60e7be 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs @@ -4,7 +4,7 @@ use ibc_proto::protobuf::Protobuf; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::signer::Signer; use crate::tx_msg::Msg; @@ -33,7 +33,7 @@ impl MsgChannelCloseInit { } impl Msg for MsgChannelCloseInit { - type ValidationError = Error; + type ValidationError = ChannelError; type Raw = RawMsgChannelCloseInit; fn route(&self) -> String { @@ -48,13 +48,16 @@ impl Msg for MsgChannelCloseInit { impl Protobuf for MsgChannelCloseInit {} impl TryFrom for MsgChannelCloseInit { - type Error = Error; + type Error = ChannelError; fn try_from(raw_msg: RawMsgChannelCloseInit) -> Result { Ok(MsgChannelCloseInit { - port_id_on_a: raw_msg.port_id.parse().map_err(Error::identifier)?, - chan_id_on_a: raw_msg.channel_id.parse().map_err(Error::identifier)?, - signer: raw_msg.signer.parse().map_err(Error::signer)?, + port_id_on_a: raw_msg.port_id.parse().map_err(ChannelError::Identifier)?, + chan_id_on_a: raw_msg + .channel_id + .parse() + .map_err(ChannelError::Identifier)?, + signer: raw_msg.signer.parse().map_err(ChannelError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs index 703e5504b..956a9c2b0 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs @@ -1,4 +1,4 @@ -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::Version; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; use crate::core::ics24_host::identifier::{ChannelId, PortId}; @@ -49,7 +49,7 @@ impl MsgChannelOpenAck { } impl Msg for MsgChannelOpenAck { - type ValidationError = Error; + type ValidationError = ChannelError; type Raw = RawMsgChannelOpenAck; fn route(&self) -> String { @@ -64,23 +64,29 @@ impl Msg for MsgChannelOpenAck { impl Protobuf for MsgChannelOpenAck {} impl TryFrom for MsgChannelOpenAck { - type Error = Error; + type Error = ChannelError; fn try_from(raw_msg: RawMsgChannelOpenAck) -> Result { Ok(MsgChannelOpenAck { - port_id_on_a: raw_msg.port_id.parse().map_err(Error::identifier)?, - chan_id_on_a: raw_msg.channel_id.parse().map_err(Error::identifier)?, + port_id_on_a: raw_msg.port_id.parse().map_err(ChannelError::Identifier)?, + chan_id_on_a: raw_msg + .channel_id + .parse() + .map_err(ChannelError::Identifier)?, chan_id_on_b: raw_msg .counterparty_channel_id .parse() - .map_err(Error::identifier)?, + .map_err(ChannelError::Identifier)?, version_on_b: raw_msg.counterparty_version.into(), - proof_chan_end_on_b: raw_msg.proof_try.try_into().map_err(Error::invalid_proof)?, + proof_chan_end_on_b: raw_msg + .proof_try + .try_into() + .map_err(ChannelError::InvalidProof)?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, - signer: raw_msg.signer.parse().map_err(Error::signer)?, + .ok_or(ChannelError::MissingHeight)?, + signer: raw_msg.signer.parse().map_err(ChannelError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs index 9e2e7ef63..a31684ce1 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs @@ -1,4 +1,4 @@ -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::signer::Signer; @@ -43,7 +43,7 @@ impl MsgChannelOpenConfirm { } impl Msg for MsgChannelOpenConfirm { - type ValidationError = Error; + type ValidationError = ChannelError; type Raw = RawMsgChannelOpenConfirm; fn route(&self) -> String { @@ -58,18 +58,24 @@ impl Msg for MsgChannelOpenConfirm { impl Protobuf for MsgChannelOpenConfirm {} impl TryFrom for MsgChannelOpenConfirm { - type Error = Error; + type Error = ChannelError; fn try_from(raw_msg: RawMsgChannelOpenConfirm) -> Result { Ok(MsgChannelOpenConfirm { - port_id_on_b: raw_msg.port_id.parse().map_err(Error::identifier)?, - chan_id_on_b: raw_msg.channel_id.parse().map_err(Error::identifier)?, - proof_chan_end_on_a: raw_msg.proof_ack.try_into().map_err(Error::invalid_proof)?, + port_id_on_b: raw_msg.port_id.parse().map_err(ChannelError::Identifier)?, + chan_id_on_b: raw_msg + .channel_id + .parse() + .map_err(ChannelError::Identifier)?, + proof_chan_end_on_a: raw_msg + .proof_ack + .try_into() + .map_err(ChannelError::InvalidProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, - signer: raw_msg.signer.parse().map_err(Error::signer)?, + .ok_or(ChannelError::MissingHeight)?, + signer: raw_msg.signer.parse().map_err(ChannelError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs index 77e16382e..9a8fa1aed 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs @@ -1,5 +1,5 @@ use crate::core::ics04_channel::channel::ChannelEnd; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics24_host::identifier::PortId; use crate::prelude::*; use crate::signer::Signer; @@ -32,7 +32,7 @@ impl MsgChannelOpenInit { } impl Msg for MsgChannelOpenInit { - type ValidationError = Error; + type ValidationError = ChannelError; type Raw = RawMsgChannelOpenInit; fn route(&self) -> String { @@ -47,16 +47,16 @@ impl Msg for MsgChannelOpenInit { impl Protobuf for MsgChannelOpenInit {} impl TryFrom for MsgChannelOpenInit { - type Error = Error; + type Error = ChannelError; fn try_from(raw_msg: RawMsgChannelOpenInit) -> Result { Ok(MsgChannelOpenInit { - port_id_on_a: raw_msg.port_id.parse().map_err(Error::identifier)?, + port_id_on_a: raw_msg.port_id.parse().map_err(ChannelError::Identifier)?, chan_end_on_a: raw_msg .channel - .ok_or_else(Error::missing_channel)? + .ok_or(ChannelError::MissingChannel)? .try_into()?, - signer: raw_msg.signer.parse().map_err(Error::signer)?, + signer: raw_msg.signer.parse().map_err(ChannelError::Signer)?, }) } } diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs index dd1bcb9a7..868a924f1 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs @@ -1,5 +1,5 @@ use crate::core::ics04_channel::channel::ChannelEnd; -use crate::core::ics04_channel::error::Error as ChannelError; +use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::Version; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; use crate::core::ics24_host::error::ValidationError; @@ -67,7 +67,7 @@ impl Msg for MsgChannelOpenTry { fn validate_basic(&self) -> Result<(), ValidationError> { match self.chan_end_on_b.counterparty().channel_id() { - None => Err(ValidationError::invalid_counterparty_channel_id()), + None => Err(ValidationError::InvalidCounterpartyChannelId), Some(_c) => Ok(()), } } @@ -81,26 +81,26 @@ impl TryFrom for MsgChannelOpenTry { fn try_from(raw_msg: RawMsgChannelOpenTry) -> Result { #[allow(deprecated)] let msg = MsgChannelOpenTry { - port_id_on_b: raw_msg.port_id.parse().map_err(ChannelError::identifier)?, + port_id_on_b: raw_msg.port_id.parse().map_err(ChannelError::Identifier)?, previous_channel_id: raw_msg.previous_channel_id, chan_end_on_b: raw_msg .channel - .ok_or_else(ChannelError::missing_channel)? + .ok_or(ChannelError::MissingChannel)? .try_into()?, version_on_a: raw_msg.counterparty_version.into(), proof_chan_end_on_a: raw_msg .proof_init .try_into() - .map_err(ChannelError::invalid_proof)?, + .map_err(ChannelError::InvalidProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(ChannelError::missing_height)?, - signer: raw_msg.signer.parse().map_err(ChannelError::signer)?, + .ok_or(ChannelError::MissingHeight)?, + signer: raw_msg.signer.parse().map_err(ChannelError::Signer)?, }; msg.validate_basic() - .map_err(|_| ChannelError::invalid_counterparty_channel_id())?; + .map_err(|_| ChannelError::InvalidCounterpartyChannelId)?; Ok(msg) } diff --git a/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs b/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs index 274ffa3bb..3bd655622 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs @@ -4,7 +4,7 @@ use ibc_proto::protobuf::Protobuf; use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::packet::Packet; use crate::proofs::Proofs; use crate::signer::Signer; @@ -33,7 +33,7 @@ impl MsgRecvPacket { } impl Msg for MsgRecvPacket { - type ValidationError = Error; + type ValidationError = PacketError; type Raw = RawMsgRecvPacket; fn route(&self) -> String { @@ -48,31 +48,31 @@ impl Msg for MsgRecvPacket { impl Protobuf for MsgRecvPacket {} impl TryFrom for MsgRecvPacket { - type Error = Error; + type Error = PacketError; fn try_from(raw_msg: RawMsgRecvPacket) -> Result { let proofs = Proofs::new( raw_msg .proof_commitment .try_into() - .map_err(Error::invalid_proof)?, + .map_err(PacketError::InvalidProof)?, None, None, None, raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, + .ok_or(PacketError::MissingHeight)?, ) - .map_err(Error::invalid_proof)?; + .map_err(PacketError::InvalidProof)?; Ok(MsgRecvPacket { packet: raw_msg .packet - .ok_or_else(Error::missing_packet)? + .ok_or(PacketError::MissingPacket)? .try_into()?, proofs, - signer: raw_msg.signer.parse().map_err(Error::signer)?, + signer: raw_msg.signer.parse().map_err(PacketError::Signer)?, }) } } @@ -126,7 +126,7 @@ mod test { use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; - use crate::core::ics04_channel::error::Error; + use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::msgs::recv_packet::test_util::get_dummy_raw_msg_recv_packet; use crate::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; use crate::test_utils::get_dummy_bech32_account; @@ -174,7 +174,7 @@ mod test { ]; for test in tests { - let res_msg: Result = test.raw.clone().try_into(); + let res_msg: Result = test.raw.clone().try_into(); assert_eq!( res_msg.is_ok(), diff --git a/crates/ibc/src/core/ics04_channel/msgs/timeout.rs b/crates/ibc/src/core/ics04_channel/msgs/timeout.rs index 58d5761e9..5bc97ce2e 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/timeout.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/timeout.rs @@ -4,7 +4,7 @@ use ibc_proto::protobuf::Protobuf; use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::packet::{Packet, Sequence}; use crate::proofs::Proofs; use crate::signer::Signer; @@ -40,7 +40,7 @@ impl MsgTimeout { } impl Msg for MsgTimeout { - type ValidationError = Error; + type ValidationError = PacketError; type Raw = RawMsgTimeout; fn route(&self) -> String { @@ -55,33 +55,33 @@ impl Msg for MsgTimeout { impl Protobuf for MsgTimeout {} impl TryFrom for MsgTimeout { - type Error = Error; + type Error = PacketError; fn try_from(raw_msg: RawMsgTimeout) -> Result { let proofs = Proofs::new( raw_msg .proof_unreceived .try_into() - .map_err(Error::invalid_proof)?, + .map_err(PacketError::InvalidProof)?, None, None, None, raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, + .ok_or(PacketError::MissingHeight)?, ) - .map_err(Error::invalid_proof)?; + .map_err(PacketError::InvalidProof)?; // TODO: Domain type verification for the next sequence: this should probably be > 0. Ok(MsgTimeout { packet: raw_msg .packet - .ok_or_else(Error::missing_packet)? + .ok_or(PacketError::MissingPacket)? .try_into()?, next_sequence_recv: Sequence::from(raw_msg.next_sequence_recv), - signer: raw_msg.signer.parse().map_err(Error::signer)?, + signer: raw_msg.signer.parse().map_err(PacketError::Signer)?, proofs, }) } @@ -135,7 +135,7 @@ mod test { use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; - use crate::core::ics04_channel::error::Error; + use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::msgs::timeout::test_util::get_dummy_raw_msg_timeout; use crate::core::ics04_channel::msgs::timeout::MsgTimeout; use crate::test_utils::get_dummy_bech32_account; @@ -195,7 +195,7 @@ mod test { ]; for test in tests { - let res_msg: Result = test.raw.clone().try_into(); + let res_msg: Result = test.raw.clone().try_into(); assert_eq!( res_msg.is_ok(), diff --git a/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs b/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs index 82b10928b..e0eda5006 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use ibc_proto::ibc::core::channel::v1::MsgTimeoutOnClose as RawMsgTimeoutOnClose; use ibc_proto::protobuf::Protobuf; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::PacketError; use crate::core::ics04_channel::packet::{Packet, Sequence}; use crate::proofs::Proofs; use crate::signer::Signer; @@ -39,7 +39,7 @@ impl MsgTimeoutOnClose { } impl Msg for MsgTimeoutOnClose { - type ValidationError = Error; + type ValidationError = PacketError; type Raw = RawMsgTimeoutOnClose; fn route(&self) -> String { @@ -54,38 +54,38 @@ impl Msg for MsgTimeoutOnClose { impl Protobuf for MsgTimeoutOnClose {} impl TryFrom for MsgTimeoutOnClose { - type Error = Error; + type Error = PacketError; fn try_from(raw_msg: RawMsgTimeoutOnClose) -> Result { let proofs = Proofs::new( raw_msg .proof_unreceived .try_into() - .map_err(Error::invalid_proof)?, + .map_err(PacketError::InvalidProof)?, None, None, Some( raw_msg .proof_close .try_into() - .map_err(Error::invalid_proof)?, + .map_err(PacketError::InvalidProof)?, ), raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_height)?, + .ok_or(PacketError::MissingHeight)?, ) - .map_err(Error::invalid_proof)?; + .map_err(PacketError::InvalidProof)?; // TODO: Domain type verification for the next sequence: this should probably be > 0. Ok(MsgTimeoutOnClose { packet: raw_msg .packet - .ok_or_else(Error::missing_packet)? + .ok_or(PacketError::MissingPacket)? .try_into()?, next_sequence_recv: Sequence::from(raw_msg.next_sequence_recv), - signer: raw_msg.signer.parse().map_err(Error::signer)?, + signer: raw_msg.signer.parse().map_err(PacketError::Signer)?, proofs, }) } diff --git a/crates/ibc/src/core/ics04_channel/packet.rs b/crates/ibc/src/core/ics04_channel/packet.rs index 585564caa..fde42440a 100644 --- a/crates/ibc/src/core/ics04_channel/packet.rs +++ b/crates/ibc/src/core/ics04_channel/packet.rs @@ -11,7 +11,7 @@ use super::handler::{ timeout::TimeoutPacketResult, write_acknowledgement::WriteAckPacketResult, }; use super::timeout::TimeoutHeight; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::{ChannelError, PacketError}; use crate::core::ics24_host::identifier::{ChannelId, PortId}; use crate::timestamp::{Expiry::Expired, Timestamp}; use crate::Height; @@ -59,11 +59,14 @@ impl core::fmt::Display for PacketMsgType { pub struct Sequence(u64); impl FromStr for Sequence { - type Err = Error; + type Err = ChannelError; fn from_str(s: &str) -> Result { Ok(Self::from(s.parse::().map_err(|e| { - Error::invalid_string_as_sequence(s.to_string(), e) + ChannelError::InvalidStringAsSequence { + value: s.to_string(), + error: e, + } })?)) } } @@ -191,11 +194,11 @@ impl core::fmt::Display for Packet { } impl TryFrom for Packet { - type Error = Error; + type Error = PacketError; fn try_from(raw_pkt: RawPacket) -> Result { if Sequence::from(raw_pkt.sequence).is_zero() { - return Err(Error::zero_packet_sequence()); + return Err(PacketError::ZeroPacketSequence); } // Note: ibc-go currently (July 2022) incorrectly treats the timeout @@ -209,27 +212,33 @@ impl TryFrom for Packet { let packet_timeout_height: TimeoutHeight = raw_pkt .timeout_height .try_into() - .map_err(|_| Error::invalid_timeout_height())?; + .map_err(|_| PacketError::InvalidTimeoutHeight)?; if raw_pkt.data.is_empty() { - return Err(Error::zero_packet_data()); + return Err(PacketError::ZeroPacketData); } let timeout_timestamp = Timestamp::from_nanoseconds(raw_pkt.timeout_timestamp) - .map_err(Error::invalid_packet_timestamp)?; + .map_err(PacketError::InvalidPacketTimestamp)?; Ok(Packet { sequence: Sequence::from(raw_pkt.sequence), - source_port: raw_pkt.source_port.parse().map_err(Error::identifier)?, - source_channel: raw_pkt.source_channel.parse().map_err(Error::identifier)?, + source_port: raw_pkt + .source_port + .parse() + .map_err(PacketError::Identifier)?, + source_channel: raw_pkt + .source_channel + .parse() + .map_err(PacketError::Identifier)?, destination_port: raw_pkt .destination_port .parse() - .map_err(Error::identifier)?, + .map_err(PacketError::Identifier)?, destination_channel: raw_pkt .destination_channel .parse() - .map_err(Error::identifier)?, + .map_err(PacketError::Identifier)?, data: raw_pkt.data, timeout_height: packet_timeout_height, timeout_timestamp, diff --git a/crates/ibc/src/core/ics04_channel/timeout.rs b/crates/ibc/src/core/ics04_channel/timeout.rs index d72537140..12dcc3248 100644 --- a/crates/ibc/src/core/ics04_channel/timeout.rs +++ b/crates/ibc/src/core/ics04_channel/timeout.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use ibc_proto::ibc::core::client::v1::Height as RawHeight; -use crate::core::ics02_client::{error::Error as ICS2Error, height::Height}; +use crate::core::ics02_client::{error::ClientError, height::Height}; use crate::prelude::*; /// Indicates a consensus height on the destination chain after which the packet @@ -70,7 +70,7 @@ impl Default for TimeoutHeight { } impl TryFrom for TimeoutHeight { - type Error = ICS2Error; + type Error = ClientError; // Note: it is important for `revision_number` to also be `0`, otherwise // packet commitment proofs will be incorrect (proof construction in @@ -87,7 +87,7 @@ impl TryFrom for TimeoutHeight { } impl TryFrom> for TimeoutHeight { - type Error = ICS2Error; + type Error = ClientError; fn try_from(maybe_raw_height: Option) -> Result { match maybe_raw_height { diff --git a/crates/ibc/src/core/ics05_port/context.rs b/crates/ibc/src/core/ics05_port/context.rs index 2c59d4a59..a1bbfb02d 100644 --- a/crates/ibc/src/core/ics05_port/context.rs +++ b/crates/ibc/src/core/ics05_port/context.rs @@ -1,4 +1,4 @@ -use crate::core::ics05_port::error::Error; +use crate::core::ics05_port::error::PortError; use crate::core::ics24_host::identifier::PortId; use crate::core::ics26_routing::context::ModuleId; use crate::prelude::*; @@ -6,5 +6,5 @@ use crate::prelude::*; /// A context supplying all the necessary read-only dependencies for processing any information regarding a port. pub trait PortReader { /// Return the module_id associated with a given port_id - fn lookup_module_by_port(&self, port_id: &PortId) -> Result; + fn lookup_module_by_port(&self, port_id: &PortId) -> Result; } diff --git a/crates/ibc/src/core/ics05_port/error.rs b/crates/ibc/src/core/ics05_port/error.rs index 4c49d3d80..df60125f2 100644 --- a/crates/ibc/src/core/ics05_port/error.rs +++ b/crates/ibc/src/core/ics05_port/error.rs @@ -1,22 +1,13 @@ use crate::core::ics24_host::identifier::PortId; -use flex_error::define_error; - -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - UnknownPort - { port_id: PortId } - | e | { format_args!("port '{0}' is unknown", e.port_id) }, - - PortAlreadyBound - { port_id: PortId } - | e | { format_args!("port '{0}' is already bound", e.port_id) }, - - ModuleNotFound - { port_id: PortId } - | e | { format_args!("could not retrieve module from port '{0}'", e.port_id) }, - - ImplementationSpecific - | _ | { "implementation specific error" }, - } +use displaydoc::Display; + +#[derive(Debug, Display)] +pub enum PortError { + /// port `{port_id}` is unknown + UnknownPort { port_id: PortId }, + /// implementation specific error + ImplementationSpecific, } + +#[cfg(feature = "std")] +impl std::error::Error for PortError {} diff --git a/crates/ibc/src/core/ics23_commitment/commitment.rs b/crates/ibc/src/core/ics23_commitment/commitment.rs index 59a9df743..a74816f54 100644 --- a/crates/ibc/src/core/ics23_commitment/commitment.rs +++ b/crates/ibc/src/core/ics23_commitment/commitment.rs @@ -1,4 +1,4 @@ -use crate::core::ics23_commitment::error::Error; +use crate::core::ics23_commitment::error::CommitmentError; use crate::prelude::*; use crate::proofs::ProofError; @@ -67,7 +67,7 @@ impl TryFrom> for CommitmentProofBytes { fn try_from(bytes: Vec) -> Result { if bytes.is_empty() { - Err(Self::Error::empty_proof()) + Err(Self::Error::EmptyProof) } else { Ok(Self { bytes }) } @@ -99,12 +99,12 @@ impl TryFrom for CommitmentProofBytes { } impl TryFrom for RawMerkleProof { - type Error = Error; + type Error = CommitmentError; fn try_from(value: CommitmentProofBytes) -> Result { let value: Vec = value.into(); - let res: RawMerkleProof = - prost::Message::decode(value.as_ref()).map_err(Error::invalid_raw_merkle_proof)?; + let res: RawMerkleProof = prost::Message::decode(value.as_ref()) + .map_err(CommitmentError::InvalidRawMerkleProof)?; Ok(res) } } @@ -125,11 +125,11 @@ impl CommitmentPrefix { } impl TryFrom> for CommitmentPrefix { - type Error = Error; + type Error = CommitmentError; fn try_from(bytes: Vec) -> Result { if bytes.is_empty() { - Err(Self::Error::empty_commitment_prefix()) + Err(Self::Error::EmptyCommitmentPrefix) } else { Ok(Self { bytes }) } diff --git a/crates/ibc/src/core/ics23_commitment/error.rs b/crates/ibc/src/core/ics23_commitment/error.rs index 260c9557b..4f598bfe8 100644 --- a/crates/ibc/src/core/ics23_commitment/error.rs +++ b/crates/ibc/src/core/ics23_commitment/error.rs @@ -1,39 +1,37 @@ -use flex_error::{define_error, TraceError}; +use displaydoc::Display; use prost::DecodeError; -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - InvalidRawMerkleProof - [ TraceError ] - |_| { "invalid raw merkle proof" }, - - CommitmentProofDecodingFailed - [ TraceError ] - |_| { "failed to decode commitment proof" }, - - EmptyCommitmentPrefix - |_| { "empty commitment prefix" }, - - EmptyMerkleProof - |_| { "empty merkle proof" }, - - EmptyMerkleRoot - |_| { "empty merkle root" }, - - EmptyVerifiedValue - |_| { "empty verified value" }, - - NumberOfSpecsMismatch - |_| { "mismatch between the number of proofs with that of specs" }, - - NumberOfKeysMismatch - |_| { "mismatch between the number of proofs with that of keys" }, - - InvalidMerkleProof - |_| { "invalid merkle proof" }, +#[derive(Debug, Display)] +pub enum CommitmentError { + /// invalid raw merkle proof error: `{0}` + InvalidRawMerkleProof(DecodeError), + /// failed to decode commitment proof error: `{0}` + CommitmentProofDecodingFailed(DecodeError), + /// empty commitment prefix + EmptyCommitmentPrefix, + /// empty merkle proof + EmptyMerkleProof, + /// empty merkle root + EmptyMerkleRoot, + /// empty verified value + EmptyVerifiedValue, + /// mismatch between the number of proofs with that of specs + NumberOfSpecsMismatch, + /// mismatch between the number of proofs with that of keys + NumberOfKeysMismatch, + /// invalid merkle proof + InvalidMerkleProof, + /// proof verification failed + VerificationFailure, +} - VerificationFailure - |_| { "proof verification failed" } +#[cfg(feature = "std")] +impl std::error::Error for CommitmentError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::InvalidRawMerkleProof(e) => Some(e), + Self::CommitmentProofDecodingFailed(e) => Some(e), + _ => None, + } } } diff --git a/crates/ibc/src/core/ics23_commitment/merkle.rs b/crates/ibc/src/core/ics23_commitment/merkle.rs index 74e990d76..7890a15ab 100644 --- a/crates/ibc/src/core/ics23_commitment/merkle.rs +++ b/crates/ibc/src/core/ics23_commitment/merkle.rs @@ -11,7 +11,7 @@ use ics23::{ }; use crate::core::ics23_commitment::commitment::{CommitmentPrefix, CommitmentRoot}; -use crate::core::ics23_commitment::error::Error; +use crate::core::ics23_commitment::error::CommitmentError; use crate::core::ics23_commitment::specs::ProofSpecs; pub fn apply_prefix(prefix: &CommitmentPrefix, mut path: Vec) -> MerklePath { @@ -75,24 +75,24 @@ impl MerkleProof { keys: MerklePath, value: Vec, start_index: usize, - ) -> Result<(), Error> { + ) -> Result<(), CommitmentError> { // validate arguments if self.proofs.is_empty() { - return Err(Error::empty_merkle_proof()); + return Err(CommitmentError::EmptyMerkleProof); } if root.hash.is_empty() { - return Err(Error::empty_merkle_root()); + return Err(CommitmentError::EmptyMerkleRoot); } let num = self.proofs.len(); let ics23_specs = Vec::::from(specs.clone()); if ics23_specs.len() != num { - return Err(Error::number_of_specs_mismatch()); + return Err(CommitmentError::NumberOfSpecsMismatch); } if keys.key_path.len() != num { - return Err(Error::number_of_keys_mismatch()); + return Err(CommitmentError::NumberOfKeysMismatch); } if value.is_empty() { - return Err(Error::empty_verified_value()); + return Err(CommitmentError::EmptyVerifiedValue); } let mut subroot = value.clone(); @@ -109,7 +109,7 @@ impl MerkleProof { Some(Proof::Exist(existence_proof)) => { subroot = calculate_existence_root::(existence_proof) - .map_err(|_| Error::invalid_merkle_proof())?; + .map_err(|_| CommitmentError::InvalidMerkleProof)?; if !verify_membership::( proof, @@ -118,16 +118,16 @@ impl MerkleProof { key.as_bytes(), &value, ) { - return Err(Error::verification_failure()); + return Err(CommitmentError::VerificationFailure); } value = subroot.clone(); } - _ => return Err(Error::invalid_merkle_proof()), + _ => return Err(CommitmentError::InvalidMerkleProof), } } if root.hash != subroot { - return Err(Error::verification_failure()); + return Err(CommitmentError::VerificationFailure); } Ok(()) @@ -138,31 +138,36 @@ impl MerkleProof { specs: &ProofSpecs, root: MerkleRoot, keys: MerklePath, - ) -> Result<(), Error> { + ) -> Result<(), CommitmentError> { // validate arguments if self.proofs.is_empty() { - return Err(Error::empty_merkle_proof()); + return Err(CommitmentError::EmptyMerkleProof); } if root.hash.is_empty() { - return Err(Error::empty_merkle_root()); + return Err(CommitmentError::EmptyMerkleRoot); } let num = self.proofs.len(); let ics23_specs = Vec::::from(specs.clone()); if ics23_specs.len() != num { - return Err(Error::number_of_specs_mismatch()); + return Err(CommitmentError::NumberOfSpecsMismatch); } if keys.key_path.len() != num { - return Err(Error::number_of_keys_mismatch()); + return Err(CommitmentError::NumberOfKeysMismatch); } // verify the absence of key in lowest subtree - let proof = self.proofs.get(0).ok_or_else(Error::invalid_merkle_proof)?; - let spec = ics23_specs.get(0).ok_or_else(Error::invalid_merkle_proof)?; + let proof = self + .proofs + .get(0) + .ok_or(CommitmentError::InvalidMerkleProof)?; + let spec = ics23_specs + .get(0) + .ok_or(CommitmentError::InvalidMerkleProof)?; // keys are represented from root-to-leaf let key = keys .key_path .get(num - 1) - .ok_or_else(Error::invalid_merkle_proof)?; + .ok_or(CommitmentError::InvalidMerkleProof)?; match &proof.proof { Some(Proof::Nonexist(non_existence_proof)) => { let subroot = calculate_non_existence_root(non_existence_proof)?; @@ -173,27 +178,27 @@ impl MerkleProof { &subroot, key.as_bytes(), ) { - return Err(Error::verification_failure()); + return Err(CommitmentError::VerificationFailure); } // verify membership proofs starting from index 1 with value = subroot self.verify_membership(specs, root, keys, subroot, 1) } - _ => Err(Error::invalid_merkle_proof()), + _ => Err(CommitmentError::InvalidMerkleProof), } } } // TODO move to ics23 -fn calculate_non_existence_root(proof: &NonExistenceProof) -> Result, Error> { +fn calculate_non_existence_root(proof: &NonExistenceProof) -> Result, CommitmentError> { if let Some(left) = &proof.left { calculate_existence_root::(left) - .map_err(|_| Error::invalid_merkle_proof()) + .map_err(|_| CommitmentError::InvalidMerkleProof) } else if let Some(right) = &proof.right { calculate_existence_root::(right) - .map_err(|_| Error::invalid_merkle_proof()) + .map_err(|_| CommitmentError::InvalidMerkleProof) } else { - Err(Error::invalid_merkle_proof()) + Err(CommitmentError::InvalidMerkleProof) } } @@ -249,13 +254,15 @@ fn calculate_non_existence_root(proof: &NonExistenceProof) -> Result, Er // } // } -pub fn convert_tm_to_ics_merkle_proof(tm_proof: &TendermintProof) -> Result { +pub fn convert_tm_to_ics_merkle_proof( + tm_proof: &TendermintProof, +) -> Result { let mut proofs = Vec::new(); for op in &tm_proof.ops { let mut parsed = ibc_proto::ics23::CommitmentProof { proof: None }; prost::Message::merge(&mut parsed, op.data.as_slice()) - .map_err(Error::commitment_proof_decoding_failed)?; + .map_err(CommitmentError::CommitmentProofDecodingFailed)?; proofs.push(parsed); } diff --git a/crates/ibc/src/core/ics24_host/error.rs b/crates/ibc/src/core/ics24_host/error.rs index 022141421..dfba80610 100644 --- a/crates/ibc/src/core/ics24_host/error.rs +++ b/crates/ibc/src/core/ics24_host/error.rs @@ -1,36 +1,25 @@ -use flex_error::define_error; -use serde::Serialize; - use crate::prelude::*; +use displaydoc::Display; +use serde::Serialize; -define_error! { - #[derive(Debug, PartialEq, Eq, Serialize)] - ValidationError { - ContainSeparator - { id : String } - | e | { format_args!("identifier {0} cannot contain separator '/'", e.id) }, - - InvalidLength - { - id: String, - length: usize, - min: usize, - max: usize, - } - | e | { format_args!("identifier {0} has invalid length {1} must be between {2}-{3} characters", e.id, e.length, e.min, e.max) }, - - InvalidCharacter - { id: String } - | e | { format_args!("identifier {0} must only contain alphanumeric characters or `.`, `_`, `+`, `-`, `#`, - `[`, `]`, `<`, `>`", e.id) }, - - Empty - | _ | { "identifier cannot be empty" }, - - ChainIdInvalidFormat - { id: String } - | e | { format_args!("chain identifiers are expected to be in epoch format {0}", e.id) }, - - InvalidCounterpartyChannelId - |_| { "Invalid channel id in counterparty" } - } +#[derive(Debug, Display, Serialize)] +pub enum ValidationError { + /// identifier `{id}` cannot contain separator '/' + ContainSeparator { id: String }, + /// identifier `{id}` has invalid length `{length}` must be between `{min}`-`{max}` characters + InvalidLength { + id: String, + length: usize, + min: usize, + max: usize, + }, + /// identifier `{id}` must only contain alphanumeric characters or `.`, `_`, `+`, `-`, `#`, - `[`, `]`, `<`, `>` + InvalidCharacter { id: String }, + /// identifier cannot be empty + Empty, + /// Invalid channel id in counterparty + InvalidCounterpartyChannelId, } + +#[cfg(feature = "std")] +impl std::error::Error for ValidationError {} diff --git a/crates/ibc/src/core/ics24_host/path.rs b/crates/ibc/src/core/ics24_host/path.rs index efa001c79..06abfa264 100644 --- a/crates/ibc/src/core/ics24_host/path.rs +++ b/crates/ibc/src/core/ics24_host/path.rs @@ -10,7 +10,6 @@ use crate::core::ics04_channel::packet::Sequence; use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use derive_more::{Display, From}; -use flex_error::define_error; /// ABCI Query path for the IBC sub-store pub const IBC_QUERY_PATH: &str = "store/ibc/key"; @@ -178,15 +177,15 @@ impl Path { } } -define_error! { - #[derive(Eq, PartialEq)] - PathError { - ParseFailure - { path: String } - | e | { format!("'{}' could not be parsed into a Path", e.path) }, - } +#[derive(Debug, displaydoc::Display)] +pub enum PathError { + /// `{path}` could not be parsed into a Path + ParseFailure { path: String }, } +#[cfg(feature = "std")] +impl std::error::Error for PathError {} + /// The FromStr trait allows paths encoded as strings to be parsed into Paths. impl FromStr for Path { type Err = PathError; @@ -203,7 +202,9 @@ impl FromStr for Path { .or_else(|| parse_acks(&components)) .or_else(|| parse_receipts(&components)) .or_else(|| parse_upgrades(&components)) - .ok_or_else(|| PathError::parse_failure(s.to_string())) + .ok_or(PathError::ParseFailure { + path: s.to_string(), + }) } } diff --git a/crates/ibc/src/core/ics24_host/validate.rs b/crates/ibc/src/core/ics24_host/validate.rs index 0bc4163bb..bfa2d04ab 100644 --- a/crates/ibc/src/core/ics24_host/validate.rs +++ b/crates/ibc/src/core/ics24_host/validate.rs @@ -15,17 +15,22 @@ pub fn validate_identifier(id: &str, min: usize, max: usize) -> Result<(), Error // Check identifier is not empty if id.is_empty() { - return Err(Error::empty()); + return Err(Error::Empty); } // Check identifier does not contain path separators if id.contains(PATH_SEPARATOR) { - return Err(Error::contain_separator(id.to_string())); + return Err(Error::ContainSeparator { id: id.into() }); } // Check identifier length is between given min/max if id.len() < min || id.len() > max { - return Err(Error::invalid_length(id.to_string(), id.len(), min, max)); + return Err(Error::InvalidLength { + id: id.into(), + length: id.len(), + min, + max, + }); } // Check that the identifier comprises only valid characters: @@ -36,7 +41,7 @@ pub fn validate_identifier(id: &str, min: usize, max: usize) -> Result<(), Error .chars() .all(|c| c.is_alphanumeric() || VALID_SPECIAL_CHARS.contains(c)) { - return Err(Error::invalid_character(id.to_string())); + return Err(Error::InvalidCharacter { id: id.into() }); } // All good! diff --git a/crates/ibc/src/core/ics26_routing/context.rs b/crates/ibc/src/core/ics26_routing/context.rs index 60f72d227..3e5a677a5 100644 --- a/crates/ibc/src/core/ics26_routing/context.rs +++ b/crates/ibc/src/core/ics26_routing/context.rs @@ -14,7 +14,7 @@ use crate::core::ics02_client::context::{ClientKeeper, ClientReader}; use crate::core::ics03_connection::context::{ConnectionKeeper, ConnectionReader}; use crate::core::ics04_channel::channel::{Counterparty, Order}; use crate::core::ics04_channel::context::{ChannelKeeper, ChannelReader}; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::{ChannelError, PacketError}; use crate::core::ics04_channel::msgs::acknowledgement::Acknowledgement as GenericAcknowledgement; use crate::core::ics04_channel::packet::Packet; use crate::core::ics04_channel::Version; @@ -108,7 +108,7 @@ pub trait Module: Send + Sync + AsAnyMut { channel_id: &ChannelId, counterparty: &Counterparty, version: &Version, - ) -> Result<(ModuleExtras, Version), Error>; + ) -> Result<(ModuleExtras, Version), ChannelError>; #[allow(clippy::too_many_arguments)] fn on_chan_open_try( @@ -119,14 +119,14 @@ pub trait Module: Send + Sync + AsAnyMut { channel_id: &ChannelId, counterparty: &Counterparty, counterparty_version: &Version, - ) -> Result<(ModuleExtras, Version), Error>; + ) -> Result<(ModuleExtras, Version), ChannelError>; fn on_chan_open_ack( &mut self, _port_id: &PortId, _channel_id: &ChannelId, _counterparty_version: &Version, - ) -> Result { + ) -> Result { Ok(ModuleExtras::empty()) } @@ -134,7 +134,7 @@ pub trait Module: Send + Sync + AsAnyMut { &mut self, _port_id: &PortId, _channel_id: &ChannelId, - ) -> Result { + ) -> Result { Ok(ModuleExtras::empty()) } @@ -142,7 +142,7 @@ pub trait Module: Send + Sync + AsAnyMut { &mut self, _port_id: &PortId, _channel_id: &ChannelId, - ) -> Result { + ) -> Result { Ok(ModuleExtras::empty()) } @@ -150,7 +150,7 @@ pub trait Module: Send + Sync + AsAnyMut { &mut self, _port_id: &PortId, _channel_id: &ChannelId, - ) -> Result { + ) -> Result { Ok(ModuleExtras::empty()) } @@ -169,7 +169,7 @@ pub trait Module: Send + Sync + AsAnyMut { _packet: &Packet, _acknowledgement: &GenericAcknowledgement, _relayer: &Signer, - ) -> Result<(), Error> { + ) -> Result<(), PacketError> { Ok(()) } @@ -178,7 +178,7 @@ pub trait Module: Send + Sync + AsAnyMut { _output: &mut ModuleOutputBuilder, _packet: &Packet, _relayer: &Signer, - ) -> Result<(), Error> { + ) -> Result<(), PacketError> { Ok(()) } } diff --git a/crates/ibc/src/core/ics26_routing/error.rs b/crates/ibc/src/core/ics26_routing/error.rs index c0d3c69c6..6122b8265 100644 --- a/crates/ibc/src/core/ics26_routing/error.rs +++ b/crates/ibc/src/core/ics26_routing/error.rs @@ -1,36 +1,25 @@ +use crate::core::context::ContextError; use crate::prelude::*; -use flex_error::{define_error, TraceError}; -use crate::applications::transfer; -use crate::core::ics02_client; -use crate::core::ics03_connection; -use crate::core::ics04_channel; +use displaydoc::Display; -define_error! { - #[derive(Debug, PartialEq, Eq)] - Error { - Ics02Client - [ ics02_client::error::Error ] - | _ | { "ICS02 client error" }, - - Ics03Connection - [ ics03_connection::error::Error ] - | _ | { "ICS03 connection error" }, - - Ics04Channel - [ ics04_channel::error::Error ] - | _ | { "ICS04 channel error" }, - - Ics20FungibleTokenTransfer - [ transfer::error::Error ] - | _ | { "ICS20 fungible token transfer error" }, - - UnknownMessageTypeUrl - { url: String } - | e | { format_args!("unknown type URL {0}", e.url) }, +#[derive(Debug, Display)] +pub enum RouterError { + /// context error: `{0}` + ContextError(ContextError), + /// unknown type URL `{url}` + UnknownMessageTypeUrl { url: String }, + /// the message is malformed and cannot be decoded error: `{0}` + MalformedMessageBytes(ibc_proto::protobuf::Error), +} - MalformedMessageBytes - [ TraceError ] - | _ | { "the message is malformed and cannot be decoded" }, +#[cfg(feature = "std")] +impl std::error::Error for RouterError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::ContextError(e) => Some(e), + Self::UnknownMessageTypeUrl { .. } => None, + Self::MalformedMessageBytes(e) => Some(e), + } } } diff --git a/crates/ibc/src/core/ics26_routing/handler.rs b/crates/ibc/src/core/ics26_routing/handler.rs index bc3f32dd5..141ba46e1 100644 --- a/crates/ibc/src/core/ics26_routing/handler.rs +++ b/crates/ibc/src/core/ics26_routing/handler.rs @@ -14,7 +14,7 @@ use crate::core::ics04_channel::handler::{ }; use crate::core::ics04_channel::packet::PacketResult; use crate::core::ics26_routing::context::RouterContext; -use crate::core::ics26_routing::error::Error; +use crate::core::ics26_routing::error::RouterError; use crate::core::ics26_routing::msgs::MsgEnvelope::{ self, ChannelMsg, ClientMsg, ConnectionMsg, PacketMsg, }; @@ -30,7 +30,7 @@ pub struct MsgReceipt { /// Mimics the DeliverTx ABCI interface, but for a single message and at a slightly lower level. /// No need for authentication info or signature checks here. /// Returns a vector of all events that got generated as a byproduct of processing `message`. -pub fn deliver(ctx: &mut Ctx, message: Any) -> Result +pub fn deliver(ctx: &mut Ctx, message: Any) -> Result where Ctx: RouterContext, { @@ -44,7 +44,7 @@ where } /// Attempts to convert a message into a [MsgEnvelope] message -pub fn decode(message: Any) -> Result { +pub fn decode(message: Any) -> Result { message.try_into() } @@ -53,17 +53,18 @@ pub fn decode(message: Any) -> Result { /// and events produced after processing the input `msg`. /// If this method returns an error, the runtime is expected to rollback all state modifications to /// the `Ctx` caused by all messages from the transaction that this `msg` is a part of. -pub fn dispatch(ctx: &mut Ctx, msg: MsgEnvelope) -> Result, Error> +pub fn dispatch(ctx: &mut Ctx, msg: MsgEnvelope) -> Result, RouterError> where Ctx: RouterContext, { let output = match msg { ClientMsg(msg) => { - let handler_output = ics2_msg_dispatcher(ctx, msg).map_err(Error::ics02_client)?; + let handler_output = + ics2_msg_dispatcher(ctx, msg).map_err(|e| RouterError::ContextError(e.into()))?; // Apply the result to the context (host chain store). ctx.store_client_result(handler_output.result) - .map_err(Error::ics02_client)?; + .map_err(|e| RouterError::ContextError(e.into()))?; HandlerOutput::builder() .with_log(handler_output.log) @@ -72,11 +73,12 @@ where } ConnectionMsg(msg) => { - let handler_output = ics3_msg_dispatcher(ctx, msg).map_err(Error::ics03_connection)?; + let handler_output = + ics3_msg_dispatcher(ctx, msg).map_err(|e| RouterError::ContextError(e.into()))?; // Apply any results to the host chain store. ctx.store_connection_result(handler_output.result) - .map_err(Error::ics03_connection)?; + .map_err(|e| RouterError::ContextError(e.into()))?; HandlerOutput::builder() .with_log(handler_output.log) @@ -85,18 +87,19 @@ where } ChannelMsg(msg) => { - let module_id = channel_validate(ctx, &msg).map_err(Error::ics04_channel)?; + let module_id = + channel_validate(ctx, &msg).map_err(|e| RouterError::ContextError(e.into()))?; let dispatch_output = HandlerOutputBuilder::<()>::new(); let (dispatch_log, mut channel_result) = - channel_dispatch(ctx, &msg).map_err(Error::ics04_channel)?; + channel_dispatch(ctx, &msg).map_err(|e| RouterError::ContextError(e.into()))?; // Note: `OpenInit` and `OpenTry` modify the `version` field of the `channel_result`, // so we must pass it mutably. We intend to clean this up with the implementation of // ADR 5. // See issue [#190](https://github.com/cosmos/ibc-rs/issues/190) let callback_extras = channel_callback(ctx, &module_id, &msg, &mut channel_result) - .map_err(Error::ics04_channel)?; + .map_err(|e| RouterError::ContextError(e.into()))?; // We need to construct events here instead of directly in the // `process` functions because we need to wait for the callback to @@ -111,7 +114,7 @@ where // Apply any results to the host chain store. ctx.store_channel_result(channel_result) - .map_err(Error::ics04_channel)?; + .map_err(|e| RouterError::ContextError(e.into()))?; dispatch_output .with_events(dispatch_events) @@ -128,20 +131,21 @@ where } PacketMsg(msg) => { - let module_id = get_module_for_packet_msg(ctx, &msg).map_err(Error::ics04_channel)?; - let (mut handler_builder, packet_result) = - ics4_packet_msg_dispatcher(ctx, &msg).map_err(Error::ics04_channel)?; + let module_id = get_module_for_packet_msg(ctx, &msg) + .map_err(|e| RouterError::ContextError(e.into()))?; + let (mut handler_builder, packet_result) = ics4_packet_msg_dispatcher(ctx, &msg) + .map_err(|e| RouterError::ContextError(e.into()))?; if matches!(packet_result, PacketResult::Recv(RecvPacketResult::NoOp)) { return Ok(handler_builder.with_result(())); } let cb_result = ics4_packet_callback(ctx, &module_id, &msg, &mut handler_builder); - cb_result.map_err(Error::ics04_channel)?; + cb_result.map_err(|e| RouterError::ContextError(e.into()))?; // Apply any results to the host chain store. ctx.store_packet_result(packet_result) - .map_err(Error::ics04_channel)?; + .map_err(|e| RouterError::ContextError(e.into()))?; handler_builder.with_result(()) } @@ -203,7 +207,7 @@ mod tests { use crate::core::ics23_commitment::commitment::CommitmentPrefix; use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use crate::core::ics26_routing::context::{ModuleId, Router, RouterBuilder, RouterContext}; - use crate::core::ics26_routing::error::Error; + use crate::core::ics26_routing::error::RouterError; use crate::core::ics26_routing::handler::dispatch; use crate::core::ics26_routing::msgs::MsgEnvelope; use crate::events::IbcEvent; @@ -632,7 +636,7 @@ mod tests { msg, ) .map(|_| ()) - .map_err(Error::ics04_channel) + .map_err(|e| RouterError::ContextError(e.into())) } }; diff --git a/crates/ibc/src/core/ics26_routing/msgs.rs b/crates/ibc/src/core/ics26_routing/msgs.rs index a037a8562..7d0142dcb 100644 --- a/crates/ibc/src/core/ics26_routing/msgs.rs +++ b/crates/ibc/src/core/ics26_routing/msgs.rs @@ -10,7 +10,7 @@ use crate::core::ics04_channel::msgs::{ acknowledgement, chan_close_confirm, chan_close_init, chan_open_ack, chan_open_confirm, chan_open_init, chan_open_try, recv_packet, timeout, timeout_on_close, ChannelMsg, PacketMsg, }; -use crate::core::ics26_routing::error::Error; +use crate::core::ics26_routing::error::RouterError; use ibc_proto::protobuf::Protobuf; /// Enumeration of all messages that the local ICS26 module is capable of routing. @@ -23,7 +23,7 @@ pub enum MsgEnvelope { } impl TryFrom for MsgEnvelope { - type Error = Error; + type Error = RouterError; fn try_from(any_msg: Any) -> Result { match any_msg.type_url.as_str() { @@ -31,38 +31,38 @@ impl TryFrom for MsgEnvelope { create_client::TYPE_URL => { // Pop out the message and then wrap it in the corresponding type. let domain_msg = create_client::MsgCreateClient::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ClientMsg(ClientMsg::CreateClient(domain_msg))) } update_client::TYPE_URL => { let domain_msg = update_client::MsgUpdateClient::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ClientMsg(ClientMsg::UpdateClient(domain_msg))) } upgrade_client::TYPE_URL => { let domain_msg = upgrade_client::MsgUpgradeClient::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ClientMsg(ClientMsg::UpgradeClient(domain_msg))) } // ICS03 conn_open_init::TYPE_URL => { let domain_msg = conn_open_init::MsgConnectionOpenInit::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ConnectionMsg( ConnectionMsg::ConnectionOpenInit(domain_msg), )) } conn_open_try::TYPE_URL => { let domain_msg = conn_open_try::MsgConnectionOpenTry::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ConnectionMsg( ConnectionMsg::ConnectionOpenTry(Box::new(domain_msg)), )) } conn_open_ack::TYPE_URL => { let domain_msg = conn_open_ack::MsgConnectionOpenAck::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ConnectionMsg( ConnectionMsg::ConnectionOpenAck(Box::new(domain_msg)), )) @@ -70,7 +70,7 @@ impl TryFrom for MsgEnvelope { conn_open_confirm::TYPE_URL => { let domain_msg = conn_open_confirm::MsgConnectionOpenConfirm::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ConnectionMsg( ConnectionMsg::ConnectionOpenConfirm(domain_msg), )) @@ -79,21 +79,21 @@ impl TryFrom for MsgEnvelope { // ICS04 channel messages chan_open_init::TYPE_URL => { let domain_msg = chan_open_init::MsgChannelOpenInit::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ChannelMsg(ChannelMsg::ChannelOpenInit( domain_msg, ))) } chan_open_try::TYPE_URL => { let domain_msg = chan_open_try::MsgChannelOpenTry::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ChannelMsg(ChannelMsg::ChannelOpenTry( domain_msg, ))) } chan_open_ack::TYPE_URL => { let domain_msg = chan_open_ack::MsgChannelOpenAck::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ChannelMsg(ChannelMsg::ChannelOpenAck( domain_msg, ))) @@ -101,14 +101,14 @@ impl TryFrom for MsgEnvelope { chan_open_confirm::TYPE_URL => { let domain_msg = chan_open_confirm::MsgChannelOpenConfirm::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ChannelMsg(ChannelMsg::ChannelOpenConfirm( domain_msg, ))) } chan_close_init::TYPE_URL => { let domain_msg = chan_close_init::MsgChannelCloseInit::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ChannelMsg(ChannelMsg::ChannelCloseInit( domain_msg, ))) @@ -116,7 +116,7 @@ impl TryFrom for MsgEnvelope { chan_close_confirm::TYPE_URL => { let domain_msg = chan_close_confirm::MsgChannelCloseConfirm::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::ChannelMsg(ChannelMsg::ChannelCloseConfirm( domain_msg, ))) @@ -124,27 +124,29 @@ impl TryFrom for MsgEnvelope { // ICS04 packet messages recv_packet::TYPE_URL => { let domain_msg = recv_packet::MsgRecvPacket::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::PacketMsg(PacketMsg::RecvPacket(domain_msg))) } acknowledgement::TYPE_URL => { let domain_msg = acknowledgement::MsgAcknowledgement::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::PacketMsg(PacketMsg::AckPacket(domain_msg))) } timeout::TYPE_URL => { let domain_msg = timeout::MsgTimeout::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::PacketMsg(PacketMsg::TimeoutPacket(domain_msg))) } timeout_on_close::TYPE_URL => { let domain_msg = timeout_on_close::MsgTimeoutOnClose::decode_vec(&any_msg.value) - .map_err(Error::malformed_message_bytes)?; + .map_err(RouterError::MalformedMessageBytes)?; Ok(MsgEnvelope::PacketMsg(PacketMsg::TimeoutOnClosePacket( domain_msg, ))) } - _ => Err(Error::unknown_message_type_url(any_msg.type_url)), + _ => Err(RouterError::UnknownMessageTypeUrl { + url: any_msg.type_url, + }), } } } diff --git a/crates/ibc/src/events.rs b/crates/ibc/src/events.rs index eb6bcb1d1..ef7793832 100644 --- a/crates/ibc/src/events.rs +++ b/crates/ibc/src/events.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use core::convert::{TryFrom, TryInto}; use core::str::FromStr; -use flex_error::{define_error, TraceError}; +use displaydoc::Display; use serde_derive::{Deserialize, Serialize}; use tendermint::abci; @@ -16,57 +16,40 @@ use crate::core::ics24_host::error::ValidationError; use crate::core::ics26_routing::context::ModuleId; use crate::timestamp::ParseTimestampError; -define_error! { - Error { - Height - | _ | { "error parsing height" }, - - Parse - [ ValidationError ] - | _ | { "parse error" }, - - Client - [ client_error::Error ] - | _ | { "ICS02 client error" }, - - Connection - [ connection_error::Error ] - | _ | { "connection error" }, - - Channel - [ channel_error::Error ] - | _ | { "channel error" }, - - Timestamp - [ ParseTimestampError ] - | _ | { "error parsing timestamp" }, - - MissingKey - { key: String } - | e | { format_args!("missing event key {}", e.key) }, - - Decode - [ TraceError ] - | _ | { "error decoding protobuf" }, - - SubtleEncoding - [ TraceError ] - | _ | { "error decoding hex" }, - - MissingActionString - | _ | { "missing action string" }, - - IncorrectEventType - { event: String } - | e | { format_args!("incorrect event type: {}", e.event) }, - - MalformedModuleEvent - { event: ModuleEvent } - | e | { format_args!("module event cannot use core event types: {:?}", e.event) }, +#[derive(Debug, Display)] +pub enum Error { + /// error parsing height + Height, + /// parse error: `{0}` + Parse(ValidationError), + /// client error: `{0}` + Client(client_error::ClientError), + /// connection error: `{0}` + Connection(connection_error::ConnectionError), + /// channel error: `{0}` + Channel(channel_error::ChannelError), + /// parsing timestamp error: `{0}` + Timestamp(ParseTimestampError), + /// decoding protobuf error: `{0}` + Decode(prost::DecodeError), + /// incorrect event type: `{event}` + IncorrectEventType { event: String }, + /// module event cannot use core event types: `{event:?}` + MalformedModuleEvent { event: ModuleEvent }, +} - UnsupportedAbciEvent - {event_type: String} - |e| { format_args!("Unable to parse abci event type '{}' into IbcEvent", e.event_type)} +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Parse(e) => Some(e), + Self::Client(e) => Some(e), + Self::Connection(e) => Some(e), + Self::Channel(e) => Some(e), + Self::Timestamp(e) => Some(e), + Self::Decode(e) => Some(e), + _ => None, + } } } @@ -197,7 +180,9 @@ impl FromStr for IbcEventType { TIMEOUT_EVENT => Ok(IbcEventType::Timeout), CHANNEL_CLOSED_EVENT => Ok(IbcEventType::ChannelClosed), // from_str() for `APP_MODULE_EVENT` MUST fail because a `ModuleEvent`'s type isn't constant - _ => Err(Error::incorrect_event_type(s.to_string())), + _ => Err(Error::IncorrectEventType { + event: s.to_string(), + }), } } } @@ -251,11 +236,11 @@ impl TryFrom for abci::Event { IbcEvent::OpenConfirmChannel(event) => event.into(), IbcEvent::CloseInitChannel(event) => event.into(), IbcEvent::CloseConfirmChannel(event) => event.into(), - IbcEvent::SendPacket(event) => event.try_into().map_err(Error::channel)?, - IbcEvent::ReceivePacket(event) => event.try_into().map_err(Error::channel)?, - IbcEvent::WriteAcknowledgement(event) => event.try_into().map_err(Error::channel)?, - IbcEvent::AcknowledgePacket(event) => event.try_into().map_err(Error::channel)?, - IbcEvent::TimeoutPacket(event) => event.try_into().map_err(Error::channel)?, + IbcEvent::SendPacket(event) => event.try_into().map_err(Error::Channel)?, + IbcEvent::ReceivePacket(event) => event.try_into().map_err(Error::Channel)?, + IbcEvent::WriteAcknowledgement(event) => event.try_into().map_err(Error::Channel)?, + IbcEvent::AcknowledgePacket(event) => event.try_into().map_err(Error::Channel)?, + IbcEvent::TimeoutPacket(event) => event.try_into().map_err(Error::Channel)?, IbcEvent::ChannelClosed(event) => event.into(), IbcEvent::AppModule(event) => event.try_into()?, }) @@ -302,7 +287,7 @@ impl TryFrom for abci::Event { fn try_from(event: ModuleEvent) -> Result { if IbcEventType::from_str(event.kind.as_str()).is_ok() { - return Err(Error::malformed_module_event(event)); + return Err(Error::MalformedModuleEvent { event }); } let attributes = event.attributes.into_iter().map(Into::into).collect(); diff --git a/crates/ibc/src/mock/client_state.rs b/crates/ibc/src/mock/client_state.rs index 80088a0e8..b445c7153 100644 --- a/crates/ibc/src/mock/client_state.rs +++ b/crates/ibc/src/mock/client_state.rs @@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize}; use crate::core::ics02_client::client_state::{ClientState, UpdatedState, UpgradeOptions}; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; use crate::mock::client_state::client_type as mock_client_type; use crate::mock::consensus_state::MockConsensusState; @@ -82,7 +82,7 @@ impl MockClientState { impl Protobuf for MockClientState {} impl TryFrom for MockClientState { - type Error = Error; + type Error = ClientError; fn try_from(raw: RawMockClientState) -> Result { Ok(Self::new(raw.header.unwrap().try_into()?)) @@ -103,16 +103,16 @@ impl From for RawMockClientState { impl Protobuf for MockClientState {} impl TryFrom for MockClientState { - type Error = Error; + type Error = ClientError; - fn try_from(raw: Any) -> Result { + fn try_from(raw: Any) -> Result { use bytes::Buf; use core::ops::Deref; use prost::Message; - fn decode_client_state(buf: B) -> Result { + fn decode_client_state(buf: B) -> Result { RawMockClientState::decode(buf) - .map_err(Error::decode)? + .map_err(ClientError::Decode)? .try_into() } @@ -120,7 +120,9 @@ impl TryFrom for MockClientState { MOCK_CLIENT_STATE_TYPE_URL => { decode_client_state(raw.value.deref()).map_err(Into::into) } - _ => Err(Error::unknown_client_state_type(raw.type_url)), + _ => Err(ClientError::UnknownClientStateType { + client_state_type: raw.type_url, + }), } } } @@ -165,7 +167,7 @@ impl ClientState for MockClientState { false } - fn initialise(&self, consensus_state: Any) -> Result, Error> { + fn initialise(&self, consensus_state: Any) -> Result, ClientError> { MockConsensusState::try_from(consensus_state).map(MockConsensusState::into_box) } @@ -174,14 +176,14 @@ impl ClientState for MockClientState { _ctx: &dyn ClientReader, _client_id: ClientId, header: Any, - ) -> Result { + ) -> Result { let header = MockHeader::try_from(header)?; if self.latest_height() >= header.height() { - return Err(Error::low_header_height( - header.height(), - self.latest_height(), - )); + return Err(ClientError::LowHeaderHeight { + header_height: header.height(), + latest_height: self.latest_height(), + }); } Ok(UpdatedState { @@ -195,14 +197,14 @@ impl ClientState for MockClientState { _ctx: &dyn crate::core::ValidationContext, _client_id: ClientId, header: Any, - ) -> Result { + ) -> Result { let header = MockHeader::try_from(header)?; if self.latest_height() >= header.height() { - return Err(Error::low_header_height( - header.height(), - self.latest_height(), - )); + return Err(ClientError::LowHeaderHeight { + header_height: header.height(), + latest_height: self.latest_height(), + }); } Ok(UpdatedState { @@ -216,7 +218,7 @@ impl ClientState for MockClientState { consensus_state: Any, _proof_upgrade_client: MerkleProof, _proof_upgrade_consensus_state: MerkleProof, - ) -> Result { + ) -> Result { let consensus_state = MockConsensusState::try_from(consensus_state)?; Ok(UpdatedState { client_state: clone_box(self), @@ -233,7 +235,7 @@ impl ClientState for MockClientState { client_id: &ClientId, consensus_height: Height, _expected_consensus_state: &dyn ConsensusState, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { let client_prefixed_path = Path::ClientConsensusState(ClientConsensusStatePath { client_id: client_id.clone(), epoch: consensus_height.revision_number(), @@ -254,7 +256,7 @@ impl ClientState for MockClientState { _root: &CommitmentRoot, _connection_id: &ConnectionId, _expected_connection_end: &ConnectionEnd, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } @@ -267,7 +269,7 @@ impl ClientState for MockClientState { _port_id: &PortId, _channel_id: &ChannelId, _expected_channel_end: &ChannelEnd, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } @@ -279,7 +281,7 @@ impl ClientState for MockClientState { _root: &CommitmentRoot, _client_id: &ClientId, _expected_client_state: Any, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } @@ -294,7 +296,7 @@ impl ClientState for MockClientState { _channel_id: &ChannelId, _sequence: Sequence, _commitment: PacketCommitment, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } @@ -309,7 +311,7 @@ impl ClientState for MockClientState { _channel_id: &ChannelId, _sequence: Sequence, _ack: AcknowledgementCommitment, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } @@ -323,7 +325,7 @@ impl ClientState for MockClientState { _port_id: &PortId, _channel_id: &ChannelId, _sequence: Sequence, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } @@ -337,7 +339,7 @@ impl ClientState for MockClientState { _port_id: &PortId, _channel_id: &ChannelId, _sequence: Sequence, - ) -> Result<(), Error> { + ) -> Result<(), ClientError> { Ok(()) } } diff --git a/crates/ibc/src/mock/consensus_state.rs b/crates/ibc/src/mock/consensus_state.rs index 51ceed757..5cf3a157f 100644 --- a/crates/ibc/src/mock/consensus_state.rs +++ b/crates/ibc/src/mock/consensus_state.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics23_commitment::commitment::CommitmentRoot; use crate::mock::client_state::client_type as mock_client_type; use crate::mock::header::MockHeader; @@ -37,10 +37,10 @@ impl MockConsensusState { impl Protobuf for MockConsensusState {} impl TryFrom for MockConsensusState { - type Error = Error; + type Error = ClientError; fn try_from(raw: RawMockConsensusState) -> Result { - let raw_header = raw.header.ok_or_else(Error::missing_raw_consensus_state)?; + let raw_header = raw.header.ok_or(ClientError::MissingRawConsensusState)?; Ok(Self { header: MockHeader::try_from(raw_header)?, @@ -63,16 +63,16 @@ impl From for RawMockConsensusState { impl Protobuf for MockConsensusState {} impl TryFrom for MockConsensusState { - type Error = Error; + type Error = ClientError; - fn try_from(raw: Any) -> Result { + fn try_from(raw: Any) -> Result { use bytes::Buf; use core::ops::Deref; use prost::Message; - fn decode_consensus_state(buf: B) -> Result { + fn decode_consensus_state(buf: B) -> Result { RawMockConsensusState::decode(buf) - .map_err(Error::decode)? + .map_err(ClientError::Decode)? .try_into() } @@ -80,7 +80,9 @@ impl TryFrom for MockConsensusState { MOCK_CONSENSUS_STATE_TYPE_URL => { decode_consensus_state(raw.value.deref()).map_err(Into::into) } - _ => Err(Error::unknown_consensus_state_type(raw.type_url)), + _ => Err(ClientError::UnknownConsensusStateType { + consensus_state_type: raw.type_url, + }), } } } diff --git a/crates/ibc/src/mock/context.rs b/crates/ibc/src/mock/context.rs index c5d0376fe..410ca763e 100644 --- a/crates/ibc/src/mock/context.rs +++ b/crates/ibc/src/mock/context.rs @@ -22,19 +22,18 @@ use crate::core::ics02_client::client_state::ClientState; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; use crate::core::ics02_client::context::{ClientKeeper, ClientReader}; -use crate::core::ics02_client::error::Error as Ics02Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::header::Header; use crate::core::ics03_connection::connection::ConnectionEnd; use crate::core::ics03_connection::context::{ConnectionKeeper, ConnectionReader}; -use crate::core::ics03_connection::error::Error as Ics03Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics04_channel::channel::ChannelEnd; use crate::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCommitment}; use crate::core::ics04_channel::context::{ChannelKeeper, ChannelReader}; -use crate::core::ics04_channel::error::Error as Ics04Error; +use crate::core::ics04_channel::error::{ChannelError, PacketError}; use crate::core::ics04_channel::packet::{Receipt, Sequence}; use crate::core::ics05_port::context::PortReader; -use crate::core::ics05_port::error::Error as Ics05Error; -use crate::core::ics05_port::error::Error; +use crate::core::ics05_port::error::PortError; use crate::core::ics23_commitment::commitment::CommitmentPrefix; use crate::core::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; use crate::core::ics26_routing::context::{Module, ModuleId, Router, RouterBuilder, RouterContext}; @@ -48,7 +47,7 @@ use crate::mock::consensus_state::MockConsensusState; use crate::mock::header::MockHeader; use crate::mock::host::{HostBlock, HostType}; use crate::relayer::ics18_relayer::context::RelayerContext; -use crate::relayer::ics18_relayer::error::Error as Ics18Error; +use crate::relayer::ics18_relayer::error::RelayerError; use crate::signer::Signer; use crate::timestamp::Timestamp; use crate::Height; @@ -466,8 +465,8 @@ impl MockContext { /// A datagram passes from the relayer to the IBC module (on host chain). /// Alternative method to `Ics18Context::send` that does not exercise any serialization. /// Used in testing the Ics18 algorithms, hence this may return a Ics18Error. - pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), Ics18Error> { - dispatch(self, msg).map_err(Ics18Error::transaction_failed)?; + pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), RelayerError> { + dispatch(self, msg).map_err(RelayerError::TransactionFailed)?; // Create a new block. self.advance_host_chain_height(); Ok(()) @@ -660,10 +659,12 @@ impl RouterContext for MockContext { } impl PortReader for MockContext { - fn lookup_module_by_port(&self, port_id: &PortId) -> Result { + fn lookup_module_by_port(&self, port_id: &PortId) -> Result { match self.ibc_store.lock().unwrap().port_to_module.get(port_id) { Some(mod_id) => Ok(mod_id.clone()), - None => Err(Ics05Error::unknown_port(port_id.clone())), + None => Err(PortError::UnknownPort { + port_id: port_id.clone(), + }), } } } @@ -673,7 +674,7 @@ impl ChannelReader for MockContext { &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -683,46 +684,46 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(channel_id)) { Some(channel_end) => Ok(channel_end.clone()), - None => Err(Ics04Error::channel_not_found( - port_id.clone(), - channel_id.clone(), - )), + None => Err(ChannelError::ChannelNotFound { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), } } - fn connection_end(&self, cid: &ConnectionId) -> Result { - ConnectionReader::connection_end(self, cid).map_err(Ics04Error::ics03_connection) + fn connection_end(&self, cid: &ConnectionId) -> Result { + ConnectionReader::connection_end(self, cid).map_err(ChannelError::Connection) } fn connection_channels( &self, cid: &ConnectionId, - ) -> Result, Ics04Error> { + ) -> Result, ChannelError> { match self.ibc_store.lock().unwrap().connection_channels.get(cid) { Some(pcid) => Ok(pcid.clone()), - None => Err(Ics04Error::missing_channel()), + None => Err(ChannelError::MissingChannel), } } - fn client_state(&self, client_id: &ClientId) -> Result, Ics04Error> { + fn client_state(&self, client_id: &ClientId) -> Result, ChannelError> { ClientReader::client_state(self, client_id) - .map_err(|e| Ics04Error::ics03_connection(Ics03Error::ics02_client(e))) + .map_err(|e| ChannelError::Connection(ConnectionError::Client(e))) } fn client_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result, Ics04Error> { + ) -> Result, ChannelError> { ClientReader::consensus_state(self, client_id, height) - .map_err(|e| Ics04Error::ics03_connection(Ics03Error::ics02_client(e))) + .map_err(|e| ChannelError::Connection(ConnectionError::Client(e))) } fn get_next_sequence_send( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -732,10 +733,10 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(channel_id)) { Some(sequence) => Ok(*sequence), - None => Err(Ics04Error::missing_next_send_seq( - port_id.clone(), - channel_id.clone(), - )), + None => Err(PacketError::MissingNextSendSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), } } @@ -743,7 +744,7 @@ impl ChannelReader for MockContext { &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -753,10 +754,10 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(channel_id)) { Some(sequence) => Ok(*sequence), - None => Err(Ics04Error::missing_next_recv_seq( - port_id.clone(), - channel_id.clone(), - )), + None => Err(PacketError::MissingNextRecvSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), } } @@ -764,7 +765,7 @@ impl ChannelReader for MockContext { &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -774,10 +775,10 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(channel_id)) { Some(sequence) => Ok(*sequence), - None => Err(Ics04Error::missing_next_ack_seq( - port_id.clone(), - channel_id.clone(), - )), + None => Err(PacketError::MissingNextAckSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), } } @@ -786,7 +787,7 @@ impl ChannelReader for MockContext { port_id: &PortId, channel_id: &ChannelId, seq: Sequence, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -797,7 +798,7 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(&seq)) { Some(commitment) => Ok(commitment.clone()), - None => Err(Ics04Error::packet_commitment_not_found(seq)), + None => Err(PacketError::PacketCommitmentNotFound { sequence: seq }), } } @@ -806,7 +807,7 @@ impl ChannelReader for MockContext { port_id: &PortId, channel_id: &ChannelId, seq: Sequence, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -817,7 +818,7 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(&seq)) { Some(receipt) => Ok(receipt.clone()), - None => Err(Ics04Error::packet_receipt_not_found(seq)), + None => Err(PacketError::PacketReceiptNotFound { sequence: seq }), } } @@ -826,7 +827,7 @@ impl ChannelReader for MockContext { port_id: &PortId, channel_id: &ChannelId, seq: Sequence, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -837,7 +838,7 @@ impl ChannelReader for MockContext { .and_then(|map| map.get(&seq)) { Some(ack) => Ok(ack.clone()), - None => Err(Ics04Error::packet_acknowledgement_not_found(seq)), + None => Err(PacketError::PacketAcknowledgementNotFound { sequence: seq }), } } @@ -845,28 +846,33 @@ impl ChannelReader for MockContext { sha2::Sha256::digest(value).to_vec() } - fn host_height(&self) -> Result { + fn host_height(&self) -> Result { Ok(self.latest_height()) } - fn host_timestamp(&self) -> Result { - ClientReader::host_timestamp(self).map_err(|e| Ics04Error::other(e.to_string())) + fn host_timestamp(&self) -> Result { + ClientReader::host_timestamp(self).map_err(|e| ChannelError::Other { + description: e.to_string(), + }) } - fn host_consensus_state(&self, height: Height) -> Result, Ics04Error> { - ConnectionReader::host_consensus_state(self, height).map_err(Ics04Error::ics03_connection) + fn host_consensus_state( + &self, + height: Height, + ) -> Result, ChannelError> { + ConnectionReader::host_consensus_state(self, height).map_err(ChannelError::Connection) } - fn pending_host_consensus_state(&self) -> Result, Ics04Error> { + fn pending_host_consensus_state(&self) -> Result, ChannelError> { ClientReader::pending_host_consensus_state(self) - .map_err(|e| Ics04Error::ics03_connection(Ics03Error::ics02_client(e))) + .map_err(|e| ChannelError::Connection(ConnectionError::Client(e))) } fn client_update_time( &self, client_id: &ClientId, height: Height, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -875,10 +881,10 @@ impl ChannelReader for MockContext { .get(&(client_id.clone(), height)) { Some(time) => Ok(*time), - None => Err(Ics04Error::processed_time_not_found( - client_id.clone(), + None => Err(ChannelError::ProcessedTimeNotFound { + client_id: client_id.clone(), height, - )), + }), } } @@ -886,7 +892,7 @@ impl ChannelReader for MockContext { &self, client_id: &ClientId, height: Height, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -895,14 +901,14 @@ impl ChannelReader for MockContext { .get(&(client_id.clone(), height)) { Some(height) => Ok(*height), - None => Err(Ics04Error::processed_height_not_found( - client_id.clone(), + None => Err(ChannelError::ProcessedHeightNotFound { + client_id: client_id.clone(), height, - )), + }), } } - fn channel_counter(&self) -> Result { + fn channel_counter(&self) -> Result { Ok(self.ibc_store.lock().unwrap().channel_ids_counter) } @@ -918,7 +924,7 @@ impl ChannelKeeper for MockContext { channel_id: ChannelId, seq: Sequence, commitment: PacketCommitment, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -937,7 +943,7 @@ impl ChannelKeeper for MockContext { channel_id: ChannelId, seq: Sequence, ack_commitment: AcknowledgementCommitment, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -955,7 +961,7 @@ impl ChannelKeeper for MockContext { port_id: &PortId, channel_id: &ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -971,7 +977,7 @@ impl ChannelKeeper for MockContext { cid: ConnectionId, port_id: PortId, channel_id: ChannelId, - ) -> Result<(), Ics04Error> { + ) -> Result<(), ChannelError> { self.ibc_store .lock() .unwrap() @@ -987,7 +993,7 @@ impl ChannelKeeper for MockContext { port_id: PortId, channel_id: ChannelId, channel_end: ChannelEnd, - ) -> Result<(), Ics04Error> { + ) -> Result<(), ChannelError> { self.ibc_store .lock() .unwrap() @@ -1003,7 +1009,7 @@ impl ChannelKeeper for MockContext { port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -1019,7 +1025,7 @@ impl ChannelKeeper for MockContext { port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -1035,7 +1041,7 @@ impl ChannelKeeper for MockContext { port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -1055,7 +1061,7 @@ impl ChannelKeeper for MockContext { port_id: &PortId, channel_id: &ChannelId, seq: Sequence, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -1072,7 +1078,7 @@ impl ChannelKeeper for MockContext { channel_id: ChannelId, seq: Sequence, receipt: Receipt, - ) -> Result<(), Ics04Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -1087,27 +1093,32 @@ impl ChannelKeeper for MockContext { } impl ConnectionReader for MockContext { - fn connection_end(&self, cid: &ConnectionId) -> Result { + fn connection_end(&self, cid: &ConnectionId) -> Result { match self.ibc_store.lock().unwrap().connections.get(cid) { Some(connection_end) => Ok(connection_end.clone()), - None => Err(Ics03Error::connection_not_found(cid.clone())), + None => Err(ConnectionError::ConnectionNotFound { + connection_id: cid.clone(), + }), } } - fn client_state(&self, client_id: &ClientId) -> Result, Ics03Error> { + fn client_state(&self, client_id: &ClientId) -> Result, ConnectionError> { // Forward method call to the Ics2 Client-specific method. - ClientReader::client_state(self, client_id).map_err(Ics03Error::ics02_client) + ClientReader::client_state(self, client_id).map_err(ConnectionError::Client) } - fn decode_client_state(&self, client_state: Any) -> Result, Ics03Error> { - ClientReader::decode_client_state(self, client_state).map_err(Ics03Error::ics02_client) + fn decode_client_state( + &self, + client_state: Any, + ) -> Result, ConnectionError> { + ClientReader::decode_client_state(self, client_state).map_err(ConnectionError::Client) } - fn host_current_height(&self) -> Result { + fn host_current_height(&self) -> Result { Ok(self.latest_height()) } - fn host_oldest_height(&self) -> Result { + fn host_oldest_height(&self) -> Result { // history must be non-empty, so `self.history[0]` is valid Ok(self.history[0].height()) } @@ -1120,21 +1131,24 @@ impl ConnectionReader for MockContext { &self, client_id: &ClientId, height: Height, - ) -> Result, Ics03Error> { + ) -> Result, ConnectionError> { // Forward method call to the Ics2Client-specific method. self.consensus_state(client_id, height) - .map_err(Ics03Error::ics02_client) + .map_err(ConnectionError::Client) } - fn host_consensus_state(&self, height: Height) -> Result, Ics03Error> { - ClientReader::host_consensus_state(self, height).map_err(Ics03Error::ics02_client) + fn host_consensus_state( + &self, + height: Height, + ) -> Result, ConnectionError> { + ClientReader::host_consensus_state(self, height).map_err(ConnectionError::Client) } - fn connection_counter(&self) -> Result { + fn connection_counter(&self) -> Result { Ok(self.ibc_store.lock().unwrap().connection_ids_counter) } - fn validate_self_client(&self, _counterparty_client_state: Any) -> Result<(), Ics03Error> { + fn validate_self_client(&self, _counterparty_client_state: Any) -> Result<(), ConnectionError> { Ok(()) } } @@ -1144,7 +1158,7 @@ impl ConnectionKeeper for MockContext { &mut self, connection_id: ConnectionId, connection_end: &ConnectionEnd, - ) -> Result<(), Ics03Error> { + ) -> Result<(), ConnectionError> { self.ibc_store .lock() .unwrap() @@ -1157,7 +1171,7 @@ impl ConnectionKeeper for MockContext { &mut self, connection_id: ConnectionId, client_id: &ClientId, - ) -> Result<(), Ics03Error> { + ) -> Result<(), ConnectionError> { self.ibc_store .lock() .unwrap() @@ -1172,30 +1186,40 @@ impl ConnectionKeeper for MockContext { } impl ClientReader for MockContext { - fn client_type(&self, client_id: &ClientId) -> Result { + fn client_type(&self, client_id: &ClientId) -> Result { match self.ibc_store.lock().unwrap().clients.get(client_id) { Some(client_record) => Ok(client_record.client_type.clone()), - None => Err(Ics02Error::client_not_found(client_id.clone())), + None => Err(ClientError::ClientNotFound { + client_id: client_id.clone(), + }), } } - fn client_state(&self, client_id: &ClientId) -> Result, Ics02Error> { + fn client_state(&self, client_id: &ClientId) -> Result, ClientError> { match self.ibc_store.lock().unwrap().clients.get(client_id) { - Some(client_record) => client_record - .client_state - .clone() - .ok_or_else(|| Ics02Error::client_not_found(client_id.clone())), - None => Err(Ics02Error::client_not_found(client_id.clone())), + Some(client_record) => { + client_record + .client_state + .clone() + .ok_or_else(|| ClientError::ClientNotFound { + client_id: client_id.clone(), + }) + } + None => Err(ClientError::ClientNotFound { + client_id: client_id.clone(), + }), } } - fn decode_client_state(&self, client_state: Any) -> Result, Ics02Error> { + fn decode_client_state(&self, client_state: Any) -> Result, ClientError> { if let Ok(client_state) = TmClientState::try_from(client_state.clone()) { Ok(client_state.into_box()) } else if let Ok(client_state) = MockClientState::try_from(client_state.clone()) { Ok(client_state.into_box()) } else { - Err(Ics02Error::unknown_client_state_type(client_state.type_url)) + Err(ClientError::UnknownClientStateType { + client_state_type: client_state.type_url, + }) } } @@ -1203,19 +1227,19 @@ impl ClientReader for MockContext { &self, client_id: &ClientId, height: Height, - ) -> Result, Ics02Error> { + ) -> Result, ClientError> { match self.ibc_store.lock().unwrap().clients.get(client_id) { Some(client_record) => match client_record.consensus_states.get(&height) { Some(consensus_state) => Ok(consensus_state.clone()), - None => Err(Ics02Error::consensus_state_not_found( - client_id.clone(), + None => Err(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), height, - )), + }), }, - None => Err(Ics02Error::consensus_state_not_found( - client_id.clone(), + None => Err(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), height, - )), + }), } } @@ -1224,12 +1248,15 @@ impl ClientReader for MockContext { &self, client_id: &ClientId, height: Height, - ) -> Result>, Ics02Error> { + ) -> Result>, ClientError> { let ibc_store = self.ibc_store.lock().unwrap(); - let client_record = ibc_store - .clients - .get(client_id) - .ok_or_else(|| Ics02Error::client_not_found(client_id.clone()))?; + let client_record = + ibc_store + .clients + .get(client_id) + .ok_or_else(|| ClientError::ClientNotFound { + client_id: client_id.clone(), + })?; // Get the consensus state heights and sort them in ascending order. let mut heights: Vec = client_record.consensus_states.keys().cloned().collect(); @@ -1252,12 +1279,15 @@ impl ClientReader for MockContext { &self, client_id: &ClientId, height: Height, - ) -> Result>, Ics02Error> { + ) -> Result>, ClientError> { let ibc_store = self.ibc_store.lock().unwrap(); - let client_record = ibc_store - .clients - .get(client_id) - .ok_or_else(|| Ics02Error::client_not_found(client_id.clone()))?; + let client_record = + ibc_store + .clients + .get(client_id) + .ok_or_else(|| ClientError::ClientNotFound { + client_id: client_id.clone(), + })?; // Get the consensus state heights and sort them in descending order. let mut heights: Vec = client_record.consensus_states.keys().cloned().collect(); @@ -1275,11 +1305,11 @@ impl ClientReader for MockContext { Ok(None) } - fn host_height(&self) -> Result { + fn host_height(&self) -> Result { Ok(self.latest_height()) } - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { Ok(self .history .last() @@ -1289,18 +1319,18 @@ impl ClientReader for MockContext { .unwrap()) } - fn host_consensus_state(&self, height: Height) -> Result, Ics02Error> { + fn host_consensus_state(&self, height: Height) -> Result, ClientError> { match self.host_block(height) { Some(block_ref) => Ok(block_ref.clone().into()), - None => Err(Ics02Error::missing_local_consensus_state(height)), + None => Err(ClientError::MissingLocalConsensusState { height }), } } - fn pending_host_consensus_state(&self) -> Result, Ics02Error> { - Err(Ics02Error::implementation_specific()) + fn pending_host_consensus_state(&self) -> Result, ClientError> { + Err(ClientError::ImplementationSpecific) } - fn client_counter(&self) -> Result { + fn client_counter(&self) -> Result { Ok(self.ibc_store.lock().unwrap().client_ids_counter) } } @@ -1310,7 +1340,7 @@ impl ClientKeeper for MockContext { &mut self, client_id: ClientId, client_type: ClientType, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let mut ibc_store = self.ibc_store.lock().unwrap(); let client_record = ibc_store .clients @@ -1329,7 +1359,7 @@ impl ClientKeeper for MockContext { &mut self, client_id: ClientId, client_state: Box, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let mut ibc_store = self.ibc_store.lock().unwrap(); let client_record = ibc_store .clients @@ -1349,7 +1379,7 @@ impl ClientKeeper for MockContext { client_id: ClientId, height: Height, consensus_state: Box, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let mut ibc_store = self.ibc_store.lock().unwrap(); let client_record = ibc_store .clients @@ -1375,7 +1405,7 @@ impl ClientKeeper for MockContext { client_id: ClientId, height: Height, timestamp: Timestamp, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let _ = self .ibc_store .lock() @@ -1390,7 +1420,7 @@ impl ClientKeeper for MockContext { client_id: ClientId, height: Height, host_height: Height, - ) -> Result<(), Ics02Error> { + ) -> Result<(), ClientError> { let _ = self .ibc_store .lock() @@ -1402,8 +1432,8 @@ impl ClientKeeper for MockContext { } impl RelayerContext for MockContext { - fn query_latest_height(&self) -> Result { - self.host_current_height().map_err(Ics18Error::ics03) + fn query_latest_height(&self) -> Result { + self.host_current_height().map_err(RelayerError::Connection) } fn query_client_full_state(&self, client_id: &ClientId) -> Option> { @@ -1416,12 +1446,12 @@ impl RelayerContext for MockContext { block_ref.cloned().map(Header::into_box) } - fn send(&mut self, msgs: Vec) -> Result, Ics18Error> { + fn send(&mut self, msgs: Vec) -> Result, RelayerError> { // Forward call to Ics26 delivery method. let mut all_events = vec![]; for msg in msgs { let MsgReceipt { mut events, .. } = - deliver(self, msg).map_err(Ics18Error::transaction_failed)?; + deliver(self, msg).map_err(RelayerError::TransactionFailed)?; all_events.append(&mut events); } self.advance_host_chain_height(); // Advance chain height @@ -1440,7 +1470,7 @@ mod tests { use alloc::str::FromStr; use crate::core::ics04_channel::channel::{Counterparty, Order}; - use crate::core::ics04_channel::error::Error; + use crate::core::ics04_channel::error::ChannelError; use crate::core::ics04_channel::handler::ModuleExtras; use crate::core::ics04_channel::packet::Packet; use crate::core::ics04_channel::Version; @@ -1624,7 +1654,7 @@ mod tests { _channel_id: &ChannelId, _counterparty: &Counterparty, version: &Version, - ) -> Result<(ModuleExtras, Version), Error> { + ) -> Result<(ModuleExtras, Version), ChannelError> { Ok((ModuleExtras::empty(), version.clone())) } @@ -1636,7 +1666,7 @@ mod tests { _channel_id: &ChannelId, _counterparty: &Counterparty, counterparty_version: &Version, - ) -> Result<(ModuleExtras, Version), Error> { + ) -> Result<(ModuleExtras, Version), ChannelError> { Ok((ModuleExtras::empty(), counterparty_version.clone())) } @@ -1669,7 +1699,7 @@ mod tests { _channel_id: &ChannelId, _counterparty: &Counterparty, version: &Version, - ) -> Result<(ModuleExtras, Version), Error> { + ) -> Result<(ModuleExtras, Version), ChannelError> { Ok((ModuleExtras::empty(), version.clone())) } @@ -1681,7 +1711,7 @@ mod tests { _channel_id: &ChannelId, _counterparty: &Counterparty, counterparty_version: &Version, - ) -> Result<(ModuleExtras, Version), Error> { + ) -> Result<(ModuleExtras, Version), ChannelError> { Ok((ModuleExtras::empty(), counterparty_version.clone())) } } diff --git a/crates/ibc/src/mock/header.rs b/crates/ibc/src/mock/header.rs index df5570f4f..2856eb398 100644 --- a/crates/ibc/src/mock/header.rs +++ b/crates/ibc/src/mock/header.rs @@ -7,7 +7,7 @@ use ibc_proto::protobuf::Protobuf; use serde_derive::{Deserialize, Serialize}; use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::header::Header; use crate::mock::client_state::client_type as mock_client_type; use crate::timestamp::Timestamp; @@ -43,17 +43,17 @@ impl Display for MockHeader { impl Protobuf for MockHeader {} impl TryFrom for MockHeader { - type Error = Error; + type Error = ClientError; fn try_from(raw: RawMockHeader) -> Result { Ok(MockHeader { height: raw .height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or_else(Error::missing_raw_header)?, + .ok_or(ClientError::MissingRawHeader)?, timestamp: Timestamp::from_nanoseconds(raw.timestamp) - .map_err(Error::invalid_packet_timestamp)?, + .map_err(ClientError::InvalidPacketTimestamp)?, }) } } @@ -101,13 +101,15 @@ impl Header for MockHeader { impl Protobuf for MockHeader {} impl TryFrom for MockHeader { - type Error = Error; + type Error = ClientError; - fn try_from(raw: Any) -> Result { + fn try_from(raw: Any) -> Result { match raw.type_url.as_str() { MOCK_HEADER_TYPE_URL => Ok(Protobuf::::decode_vec(&raw.value) - .map_err(Error::invalid_raw_header)?), - _ => Err(Error::unknown_header_type(raw.type_url)), + .map_err(ClientError::InvalidRawHeader)?), + _ => Err(ClientError::UnknownHeaderType { + header_type: raw.type_url, + }), } } } diff --git a/crates/ibc/src/mock/host.rs b/crates/ibc/src/mock/host.rs index f4c78dceb..ca59c360b 100644 --- a/crates/ibc/src/mock/host.rs +++ b/crates/ibc/src/mock/host.rs @@ -13,7 +13,7 @@ use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TMConse use crate::clients::ics07_tendermint::header::TENDERMINT_HEADER_TYPE_URL; use crate::core::ics02_client::client_type::ClientType; use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics02_client::header::Header; use crate::core::ics24_host::identifier::ChainId; use crate::mock::client_state::client_type as mock_client_type; @@ -140,9 +140,9 @@ impl From for Box { impl ErasedProtobuf for HostBlock {} impl TryFrom for HostBlock { - type Error = Error; + type Error = ClientError; - fn try_from(_raw: Any) -> Result { + fn try_from(_raw: Any) -> Result { todo!() } } diff --git a/crates/ibc/src/mock/misbehaviour.rs b/crates/ibc/src/mock/misbehaviour.rs index e75cdb3e1..d04216e25 100644 --- a/crates/ibc/src/mock/misbehaviour.rs +++ b/crates/ibc/src/mock/misbehaviour.rs @@ -4,7 +4,7 @@ use ibc_proto::ibc::mock::Misbehaviour as RawMisbehaviour; use ibc_proto::protobuf::Protobuf; use serde::{Deserialize, Serialize}; -use crate::core::ics02_client::error::Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics24_host::identifier::ClientId; use crate::mock::header::MockHeader; use crate::Height; @@ -31,18 +31,18 @@ impl crate::core::ics02_client::misbehaviour::Misbehaviour for Misbehaviour { impl Protobuf for Misbehaviour {} impl TryFrom for Misbehaviour { - type Error = Error; + type Error = ClientError; fn try_from(raw: RawMisbehaviour) -> Result { Ok(Self { client_id: Default::default(), header1: raw .header1 - .ok_or_else(Error::missing_raw_misbehaviour)? + .ok_or(ClientError::MissingRawMisbehaviour)? .try_into()?, header2: raw .header2 - .ok_or_else(Error::missing_raw_misbehaviour)? + .ok_or(ClientError::MissingRawMisbehaviour)? .try_into()?, }) } diff --git a/crates/ibc/src/proofs.rs b/crates/ibc/src/proofs.rs index dead16749..0eb21cf3d 100644 --- a/crates/ibc/src/proofs.rs +++ b/crates/ibc/src/proofs.rs @@ -2,18 +2,19 @@ use serde::Serialize; use crate::core::ics23_commitment::commitment::CommitmentProofBytes; use crate::Height; -use flex_error::define_error; +use displaydoc::Display; -define_error! { - #[derive(Debug, PartialEq, Eq)] - ProofError { - ZeroHeight - | _ | { format_args!("proof height cannot be zero") }, - EmptyProof - | _ | { format_args!("proof cannot be empty") }, - } +#[derive(Debug, Display)] +pub enum ProofError { + /// proof height cannot be zero + ZeroHeight, + /// proof cannot be empty + EmptyProof, } +#[cfg(feature = "std")] +impl std::error::Error for ProofError {} + /// Structure comprising proofs in a message. Proofs are typically present in messages for /// handshake protocols, e.g., ICS3 connection (open) handshake or ICS4 channel (open and close) /// handshake, as well as for ICS4 packets, timeouts, and acknowledgements. diff --git a/crates/ibc/src/relayer/ics18_relayer/context.rs b/crates/ibc/src/relayer/ics18_relayer/context.rs index 9b4bf375d..111af1845 100644 --- a/crates/ibc/src/relayer/ics18_relayer/context.rs +++ b/crates/ibc/src/relayer/ics18_relayer/context.rs @@ -6,7 +6,7 @@ use crate::core::ics02_client::header::Header; use crate::events::IbcEvent; use crate::core::ics24_host::identifier::ClientId; -use crate::relayer::ics18_relayer::error::Error; +use crate::relayer::ics18_relayer::error::RelayerError; use crate::signer::Signer; use crate::Height; @@ -17,7 +17,7 @@ use crate::Height; /// types, light client, RPC client, etc.) pub trait RelayerContext { /// Returns the latest height of the chain. - fn query_latest_height(&self) -> Result; + fn query_latest_height(&self) -> Result; /// Returns this client state for the given `client_id` on this chain. /// Wrapper over the `/abci_query?path=..` endpoint. @@ -28,7 +28,7 @@ pub trait RelayerContext { /// Interface that the relayer uses to submit a datagram to this chain. /// One can think of this as wrapping around the `/broadcast_tx_commit` ABCI endpoint. - fn send(&mut self, msgs: Vec) -> Result, Error>; + fn send(&mut self, msgs: Vec) -> Result, RelayerError>; /// Temporary solution. Similar to `CosmosSDKChain::key_and_signer()` but simpler. fn signer(&self) -> Signer; diff --git a/crates/ibc/src/relayer/ics18_relayer/error.rs b/crates/ibc/src/relayer/ics18_relayer/error.rs index d141358ca..013cf46a7 100644 --- a/crates/ibc/src/relayer/ics18_relayer/error.rs +++ b/crates/ibc/src/relayer/ics18_relayer/error.rs @@ -1,43 +1,38 @@ use crate::core::ics03_connection; use crate::core::ics24_host::identifier::ClientId; -use crate::core::ics26_routing::error::Error as RoutingError; +use crate::core::ics26_routing::error::RouterError; use crate::Height; -use flex_error::define_error; +use displaydoc::Display; -define_error! { - Error { - ClientStateNotFound - { client_id: ClientId } - | e | { format_args!("client state on destination chain not found, (client id: {0})", e.client_id) }, - - ClientAlreadyUpToDate - { - client_id: ClientId, - source_height: Height, - destination_height: Height, - } - | e | { - format_args!("the client on destination chain is already up-to-date (client id: {0}, source height: {1}, dest height: {2})", - e.client_id, e.source_height, e.destination_height) - }, - - ClientAtHigherHeight - { - client_id: ClientId, - source_height: Height, - destination_height: Height, - } - | e | { - format_args!("the client on destination chain is at a higher height (client id: {0}, source height: {1}, dest height: {2})", - e.client_id, e.source_height, e.destination_height) - }, - - TransactionFailed - [ RoutingError ] - | _ | { "transaction processing by modules failed" }, +#[derive(Debug, Display)] +pub enum RelayerError { + /// client state on destination chain not found, (client id: `{client_id}`) + ClientStateNotFound { client_id: ClientId }, + /// the client on destination chain is already up-to-date (client id: `{client_id}`, source height: `{source_height}`, dest height: `{destination_height}`) + ClientAlreadyUpToDate { + client_id: ClientId, + source_height: Height, + destination_height: Height, + }, + /// the client on destination chain is at a higher height (client id: `{client_id}`, source height: `{source_height}`, dest height: `{destination_height}`) + ClientAtHigherHeight { + client_id: ClientId, + source_height: Height, + destination_height: Height, + }, + /// transaction processing by modules failed error: `{0}` + TransactionFailed(RouterError), + /// connection error: `{0}` + Connection(ics03_connection::error::ConnectionError), +} - Ics03 - [ ics03_connection::error::Error ] - | _ | { "ics03 connection error" } +#[cfg(feature = "std")] +impl std::error::Error for RelayerError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::TransactionFailed(e) => Some(e), + Self::Connection(e) => Some(e), + _ => None, + } } } diff --git a/crates/ibc/src/relayer/ics18_relayer/utils.rs b/crates/ibc/src/relayer/ics18_relayer/utils.rs index 349008fc8..52c47c513 100644 --- a/crates/ibc/src/relayer/ics18_relayer/utils.rs +++ b/crates/ibc/src/relayer/ics18_relayer/utils.rs @@ -3,7 +3,7 @@ use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; use crate::core::ics02_client::msgs::ClientMsg; use crate::core::ics24_host::identifier::ClientId; use crate::relayer::ics18_relayer::context::RelayerContext; -use crate::relayer::ics18_relayer::error::Error; +use crate::relayer::ics18_relayer::error::RelayerError; /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. @@ -11,32 +11,34 @@ pub fn build_client_update_datagram( dest: &Ctx, client_id: &ClientId, src_header: &dyn Header, -) -> Result +) -> Result where Ctx: RelayerContext, { // Check if client for ibc0 on ibc1 has been updated to latest height: // - query client state on destination chain - let dest_client_state = dest - .query_client_full_state(client_id) - .ok_or_else(|| Error::client_state_not_found(client_id.clone()))?; + let dest_client_state = dest.query_client_full_state(client_id).ok_or_else(|| { + RelayerError::ClientStateNotFound { + client_id: client_id.clone(), + } + })?; let dest_client_latest_height = dest_client_state.latest_height(); if src_header.height() == dest_client_latest_height { - return Err(Error::client_already_up_to_date( - client_id.clone(), - src_header.height(), - dest_client_latest_height, - )); + return Err(RelayerError::ClientAlreadyUpToDate { + client_id: client_id.clone(), + source_height: src_header.height(), + destination_height: dest_client_latest_height, + }); }; if dest_client_latest_height > src_header.height() { - return Err(Error::client_at_higher_height( - client_id.clone(), - src_header.height(), - dest_client_latest_height, - )); + return Err(RelayerError::ClientAtHigherHeight { + client_id: client_id.clone(), + source_height: src_header.height(), + destination_height: dest_client_latest_height, + }); }; // Client on destination chain can be updated. diff --git a/crates/ibc/src/signer.rs b/crates/ibc/src/signer.rs index 21c62bf11..ee273ed4d 100644 --- a/crates/ibc/src/signer.rs +++ b/crates/ibc/src/signer.rs @@ -3,17 +3,17 @@ use core::str::FromStr; use crate::prelude::*; use derive_more::Display; -use flex_error::define_error; use serde::{Deserialize, Serialize}; -define_error! { - #[derive(Debug, PartialEq, Eq)] - SignerError { - EmptySigner - | _ | { "signer cannot be empty" }, - } +#[derive(Debug, displaydoc::Display)] +pub enum SignerError { + /// signer cannot be empty + EmptySigner, } +#[cfg(feature = "std")] +impl std::error::Error for SignerError {} + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Display)] pub struct Signer(String); @@ -23,7 +23,7 @@ impl FromStr for Signer { fn from_str(s: &str) -> Result { let s = s.to_string(); if s.trim().is_empty() { - return Err(SignerError::empty_signer()); + return Err(SignerError::EmptySigner); } Ok(Self(s)) } diff --git a/crates/ibc/src/test_utils.rs b/crates/ibc/src/test_utils.rs index b2e2fce15..86ed2e336 100644 --- a/crates/ibc/src/test_utils.rs +++ b/crates/ibc/src/test_utils.rs @@ -7,21 +7,21 @@ use crate::applications::transfer::context::{ cosmos_adr028_escrow_address, BankKeeper, TokenTransferContext, TokenTransferKeeper, TokenTransferReader, }; -use crate::applications::transfer::{error::Error as Ics20Error, PrefixedCoin}; +use crate::applications::transfer::{error::TokenTransferError, PrefixedCoin}; use crate::core::ics02_client::client_state::ClientState; use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::Error as Ics02Error; +use crate::core::ics02_client::error::ClientError; use crate::core::ics03_connection::connection::ConnectionEnd; -use crate::core::ics03_connection::error::Error as Ics03Error; +use crate::core::ics03_connection::error::ConnectionError; use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order}; use crate::core::ics04_channel::commitment::PacketCommitment; use crate::core::ics04_channel::context::SendPacketReader; -use crate::core::ics04_channel::error::Error; +use crate::core::ics04_channel::error::{ChannelError, PacketError}; use crate::core::ics04_channel::handler::ModuleExtras; use crate::core::ics04_channel::packet::Sequence; use crate::core::ics04_channel::Version; use crate::core::ics05_port::context::PortReader; -use crate::core::ics05_port::error::Error as PortError; +use crate::core::ics05_port::error::PortError; use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use crate::core::ics26_routing::context::{Module, ModuleId}; use crate::mock::context::MockIbcStore; @@ -87,7 +87,7 @@ impl Module for DummyTransferModule { _channel_id: &ChannelId, _counterparty: &Counterparty, version: &Version, - ) -> Result<(ModuleExtras, Version), Error> { + ) -> Result<(ModuleExtras, Version), ChannelError> { Ok(( ModuleExtras { events: Vec::new(), @@ -105,7 +105,7 @@ impl Module for DummyTransferModule { _channel_id: &ChannelId, _counterparty: &Counterparty, counterparty_version: &Version, - ) -> Result<(ModuleExtras, Version), Error> { + ) -> Result<(ModuleExtras, Version), ChannelError> { Ok(( ModuleExtras { events: Vec::new(), @@ -123,7 +123,7 @@ impl TokenTransferKeeper for DummyTransferModule { channel_id: ChannelId, seq: Sequence, commitment: PacketCommitment, - ) -> Result<(), Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -141,7 +141,7 @@ impl TokenTransferKeeper for DummyTransferModule { port_id: PortId, channel_id: ChannelId, seq: Sequence, - ) -> Result<(), Error> { + ) -> Result<(), PacketError> { self.ibc_store .lock() .unwrap() @@ -167,7 +167,7 @@ impl BankKeeper for DummyTransferModule { _from: &Self::AccountId, _to: &Self::AccountId, _amt: &PrefixedCoin, - ) -> Result<(), Ics20Error> { + ) -> Result<(), TokenTransferError> { Ok(()) } @@ -175,7 +175,7 @@ impl BankKeeper for DummyTransferModule { &mut self, _account: &Self::AccountId, _amt: &PrefixedCoin, - ) -> Result<(), Ics20Error> { + ) -> Result<(), TokenTransferError> { Ok(()) } @@ -183,7 +183,7 @@ impl BankKeeper for DummyTransferModule { &mut self, _account: &Self::AccountId, _amt: &PrefixedCoin, - ) -> Result<(), Ics20Error> { + ) -> Result<(), TokenTransferError> { Ok(()) } } @@ -191,7 +191,7 @@ impl BankKeeper for DummyTransferModule { impl TokenTransferReader for DummyTransferModule { type AccountId = Signer; - fn get_port(&self) -> Result { + fn get_port(&self) -> Result { Ok(PortId::transfer()) } @@ -199,7 +199,7 @@ impl TokenTransferReader for DummyTransferModule { &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result<::AccountId, Ics20Error> { + ) -> Result<::AccountId, TokenTransferError> { let addr = cosmos_adr028_escrow_address(port_id, channel_id); Ok(bech32::encode("cosmos", addr).parse().unwrap()) } @@ -214,7 +214,11 @@ impl TokenTransferReader for DummyTransferModule { } impl SendPacketReader for DummyTransferModule { - fn channel_end(&self, port_id: &PortId, channel_id: &ChannelId) -> Result { + fn channel_end( + &self, + port_id: &PortId, + channel_id: &ChannelId, + ) -> Result { match self .ibc_store .lock() @@ -224,58 +228,66 @@ impl SendPacketReader for DummyTransferModule { .and_then(|map| map.get(channel_id)) { Some(channel_end) => Ok(channel_end.clone()), - None => Err(Error::channel_not_found( - port_id.clone(), - channel_id.clone(), - )), + None => Err(PacketError::ChannelNotFound { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), } } - fn connection_end(&self, cid: &ConnectionId) -> Result { + fn connection_end(&self, cid: &ConnectionId) -> Result { match self.ibc_store.lock().unwrap().connections.get(cid) { Some(connection_end) => Ok(connection_end.clone()), - None => Err(Ics03Error::connection_not_found(cid.clone())), + None => Err(ConnectionError::ConnectionNotFound { + connection_id: cid.clone(), + }), } - .map_err(Error::ics03_connection) + .map_err(PacketError::Connection) } - fn client_state(&self, client_id: &ClientId) -> Result, Error> { + fn client_state(&self, client_id: &ClientId) -> Result, PacketError> { match self.ibc_store.lock().unwrap().clients.get(client_id) { - Some(client_record) => client_record - .client_state - .clone() - .ok_or_else(|| Ics02Error::client_not_found(client_id.clone())), - None => Err(Ics02Error::client_not_found(client_id.clone())), + Some(client_record) => { + client_record + .client_state + .clone() + .ok_or_else(|| ClientError::ClientNotFound { + client_id: client_id.clone(), + }) + } + None => Err(ClientError::ClientNotFound { + client_id: client_id.clone(), + }), } - .map_err(|e| Error::ics03_connection(Ics03Error::ics02_client(e))) + .map_err(|e| PacketError::Connection(ConnectionError::Client(e))) } fn client_consensus_state( &self, client_id: &ClientId, height: Height, - ) -> Result, Error> { + ) -> Result, PacketError> { match self.ibc_store.lock().unwrap().clients.get(client_id) { Some(client_record) => match client_record.consensus_states.get(&height) { Some(consensus_state) => Ok(consensus_state.clone()), - None => Err(Ics02Error::consensus_state_not_found( - client_id.clone(), + None => Err(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), height, - )), + }), }, - None => Err(Ics02Error::consensus_state_not_found( - client_id.clone(), + None => Err(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), height, - )), + }), } - .map_err(|e| Error::ics03_connection(Ics03Error::ics02_client(e))) + .map_err(|e| PacketError::Connection(ConnectionError::Client(e))) } fn get_next_sequence_send( &self, port_id: &PortId, channel_id: &ChannelId, - ) -> Result { + ) -> Result { match self .ibc_store .lock() @@ -285,10 +297,10 @@ impl SendPacketReader for DummyTransferModule { .and_then(|map| map.get(channel_id)) { Some(sequence) => Ok(*sequence), - None => Err(Error::missing_next_send_seq( - port_id.clone(), - channel_id.clone(), - )), + None => Err(PacketError::MissingNextSendSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), } } diff --git a/crates/ibc/src/timestamp.rs b/crates/ibc/src/timestamp.rs index c3d655185..191a11d2d 100644 --- a/crates/ibc/src/timestamp.rs +++ b/crates/ibc/src/timestamp.rs @@ -7,7 +7,7 @@ use core::ops::{Add, Sub}; use core::str::FromStr; use core::time::Duration; -use flex_error::{define_error, TraceError}; +use displaydoc::Display; use serde_derive::{Deserialize, Serialize}; use tendermint::Time; use time::OffsetDateTime; @@ -176,14 +176,15 @@ impl Display for Timestamp { } } -define_error! { - #[derive(Debug, PartialEq, Eq)] - TimestampOverflowError { - TimestampOverflow - |_| { "Timestamp overflow when modifying with duration" } - } +#[derive(Debug, Display)] +pub enum TimestampOverflowError { + /// Timestamp overflow when modifying with duration + TimestampOverflow, } +#[cfg(feature = "std")] +impl std::error::Error for TimestampOverflowError {} + impl Add for Timestamp { type Output = Result; @@ -191,7 +192,7 @@ impl Add for Timestamp { match self.time { Some(time) => { let time = - (time + duration).map_err(|_| TimestampOverflowError::timestamp_overflow())?; + (time + duration).map_err(|_| TimestampOverflowError::TimestampOverflow)?; Ok(Timestamp { time: Some(time) }) } None => Ok(self), @@ -206,7 +207,7 @@ impl Sub for Timestamp { match self.time { Some(time) => { let time = - (time - duration).map_err(|_| TimestampOverflowError::timestamp_overflow())?; + (time - duration).map_err(|_| TimestampOverflowError::TimestampOverflow)?; Ok(Timestamp { time: Some(time) }) } None => Ok(self), @@ -214,12 +215,18 @@ impl Sub for Timestamp { } } -define_error! { - #[derive(Debug, PartialEq, Eq)] - ParseTimestampError { - ParseInt - [ TraceError ] - | _ | { "error parsing u64 integer from string"}, +#[derive(Debug, Display)] +pub enum ParseTimestampError { + /// parsing u64 integer from string error: `{0}` + ParseInt(ParseIntError), +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseTimestampError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + ParseTimestampError::ParseInt(e) => Some(e), + } } } @@ -227,7 +234,7 @@ impl FromStr for Timestamp { type Err = ParseTimestampError; fn from_str(s: &str) -> Result { - let nanoseconds = u64::from_str(s).map_err(ParseTimestampError::parse_int)?; + let nanoseconds = u64::from_str(s).map_err(ParseTimestampError::ParseInt)?; Timestamp::from_nanoseconds(nanoseconds) }