diff --git a/.changelog/unreleased/breaking-changes/1311-migrate-cosmwasm-workspace b/.changelog/unreleased/breaking-changes/1311-migrate-cosmwasm-workspace deleted file mode 100644 index b7c0c8eff..000000000 --- a/.changelog/unreleased/breaking-changes/1311-migrate-cosmwasm-workspace +++ /dev/null @@ -1,3 +0,0 @@ -- [cosmwasm] Migrated the `cosmwasm` workspace into its own separate repository - located at [https://github.com/informalsystems/cosmwasm-ibc]. - ([\#1311](https://github.com/cosmos/ibc-rs/issues/1311)) diff --git a/.changelog/unreleased/breaking-changes/1311-migrate-cosmwasm-workspace.md b/.changelog/unreleased/breaking-changes/1311-migrate-cosmwasm-workspace.md new file mode 100644 index 000000000..2f042580c --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1311-migrate-cosmwasm-workspace.md @@ -0,0 +1,3 @@ +- [cosmwasm] Migrate the `cosmwasm` workspace into its own separate repository + located at [cosmwasm-ibc](https://github.com/informalsystems/cosmwasm-ibc). + ([\#1311](https://github.com/cosmos/ibc-rs/issues/1311)) diff --git a/.changelog/unreleased/breaking-changes/1319-consolidate-decoding-related-errors.md b/.changelog/unreleased/breaking-changes/1319-consolidate-decoding-related-errors.md new file mode 100644 index 000000000..3a466c375 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1319-consolidate-decoding-related-errors.md @@ -0,0 +1,2 @@ +- [ibc] Consolidate decoding-related errors into new `DecodingError` type + ([\1319](https://github.com/cosmos/ibc-rs/issues/1319)) diff --git a/.changelog/unreleased/breaking-changes/1320-define-host-error-type.md b/.changelog/unreleased/breaking-changes/1320-define-host-error-type.md new file mode 100644 index 000000000..895cd82bc --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1320-define-host-error-type.md @@ -0,0 +1,4 @@ +- [ibc-core] Define a new `HostError` type in ICS-24 to draw distinction between + protocol errors and host errors. Additionally, rename `ContextError` to + `HandlerError` to better reflect its use case. + ([\1320](https://github.com/cosmos/ibc-rs/issues/1320)) diff --git a/.changelog/unreleased/breaking-changes/1339-merge-packet-error-into-channel-error.md b/.changelog/unreleased/breaking-changes/1339-merge-packet-error-into-channel-error.md new file mode 100644 index 000000000..01647c658 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1339-merge-packet-error-into-channel-error.md @@ -0,0 +1,2 @@ +- [ibc-core-channel] Merge `PacketError` type into `ChannelError` + ([#1339](https://github.com/cosmos/ibc-rs/issues/1339)) diff --git a/.changelog/unreleased/breaking-changes/1346-remove-generic-string-error-variants.md b/.changelog/unreleased/breaking-changes/1346-remove-generic-string-error-variants.md new file mode 100644 index 000000000..23df16a16 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1346-remove-generic-string-error-variants.md @@ -0,0 +1,3 @@ +- [ibc] Clean up multi-purpose variants like the `Other` variant and reduce + unnecessary `String` allocations in `*Error` enums. + ([\#1346](https://github.com/cosmos/ibc-rs/issues/1346)) diff --git a/.changelog/unreleased/breaking-changes/270-standardize-error-variants-to-be-less-specific.md b/.changelog/unreleased/breaking-changes/270-standardize-error-variants-to-be-less-specific.md new file mode 100644 index 000000000..7df9ffd07 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/270-standardize-error-variants-to-be-less-specific.md @@ -0,0 +1,3 @@ +- [ibc] Standardize error variants across the codebase to make them less + specific and more consistent. + ([\#270](https://github.com/cosmos/ibc-rs/issues/270)) diff --git a/.changelog/unreleased/breaking-changes/950-return-DecodingError-when-decoding-Any-to-MsgEnvelope.md b/.changelog/unreleased/breaking-changes/950-return-DecodingError-when-decoding-Any-to-MsgEnvelope.md new file mode 100644 index 000000000..bc94b49d5 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/950-return-DecodingError-when-decoding-Any-to-MsgEnvelope.md @@ -0,0 +1,2 @@ +- [ibc-core-handler] Return `DecodingError` for `MsgEnvelope` when trying to + decode from `Any` ([\#950](https://github.com/cosmos/ibc-rs/issues/950)) diff --git a/.changelog/unreleased/improvements/1336-remove-faulty-receipt-check-during-recv-packet-validate.md b/.changelog/unreleased/improvements/1338-remove-redundant-path-constructions-in-testkit.md similarity index 100% rename from .changelog/unreleased/improvements/1336-remove-faulty-receipt-check-during-recv-packet-validate.md rename to .changelog/unreleased/improvements/1338-remove-redundant-path-constructions-in-testkit.md diff --git a/.changelog/v0.31.0/breaking-changes/482-fix-validate-self-client-error-type.md b/.changelog/v0.31.0/breaking-changes/482-fix-validate-self-client-error-type.md index b68e2b01c..f05961027 100644 --- a/.changelog/v0.31.0/breaking-changes/482-fix-validate-self-client-error-type.md +++ b/.changelog/v0.31.0/breaking-changes/482-fix-validate-self-client-error-type.md @@ -1,3 +1,3 @@ -- Modify `validate_self_client` error type to return `ContextError` instead of - `ConnectionError` - ([#482](https://github.com/cosmos/ibc-rs/issues/482)) \ No newline at end of file +- Modify `validate_self_client` error type to return `HostError` instead of + `ConnectionError` + ([#482](https://github.com/cosmos/ibc-rs/issues/482)) diff --git a/.changelog/v0.33.0/improvement/547-error-strings.md b/.changelog/v0.33.0/improvement/547-error-strings.md index 661201f75..064388cf1 100644 --- a/.changelog/v0.33.0/improvement/547-error-strings.md +++ b/.changelog/v0.33.0/improvement/547-error-strings.md @@ -1,2 +1,2 @@ -- Fix ContextError Display output +- Fix HandlerError Display output ([#547](https://github.com/cosmos/ibc-rs/issues/547)) diff --git a/.changelog/v0.45.0/breaking-changes/857-use-result-for-safe-counters-increment.md b/.changelog/v0.45.0/breaking-changes/857-use-result-for-safe-counters-increment.md index f0f87285e..c656a7fd9 100644 --- a/.changelog/v0.45.0/breaking-changes/857-use-result-for-safe-counters-increment.md +++ b/.changelog/v0.45.0/breaking-changes/857-use-result-for-safe-counters-increment.md @@ -1,3 +1,3 @@ - Allow hosts to handle overflow cases in `increase_*_counter` methods by - returning `Result<(),ContextError>` type. + returning `Result<(),HostError>` type. ([#857](https://github.com/cosmos/ibc-rs/issues/857)) diff --git a/.changelog/v0.45.0/breaking-changes/859-return-result-in-logger-event-emitter-methods.md b/.changelog/v0.45.0/breaking-changes/859-return-result-in-logger-event-emitter-methods.md index 9c25b1ba3..7157bd3be 100644 --- a/.changelog/v0.45.0/breaking-changes/859-return-result-in-logger-event-emitter-methods.md +++ b/.changelog/v0.45.0/breaking-changes/859-return-result-in-logger-event-emitter-methods.md @@ -1,2 +1,2 @@ -- logger and event emitter methods return `Result<(), ContextError>` type. +- logger and event emitter methods return `Result<(), HostError>` type. ([#859](https://github.com/cosmos/ibc-rs/issues/859)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e149579..2d4949e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -641,9 +641,9 @@ There are no consensus-breaking changes. - Bump ibc-proto-rs to v0.34.0 ([#790](https://github.com/cosmos/ibc-rs/issues/790)) - Allow hosts to handle overflow cases in `increase_*_counter` methods by - returning `Result<(),ContextError>` type. + returning `Result<(),HandlerError>` type. ([#857](https://github.com/cosmos/ibc-rs/issues/857)) -- logger and event emitter methods return `Result<(), ContextError>` type. +- logger and event emitter methods return `Result<(), HandlerError>` type. ([#859](https://github.com/cosmos/ibc-rs/issues/859)) - Bump `ibc-proto-rs` to v0.35.0 along with some other minor dependency updates ([#873](https://github.com/cosmos/ibc-rs/issues/873)) @@ -1042,7 +1042,7 @@ There are no consensus-breaking changes. ### IMPROVEMENT -- Fix `ContextError` Display output +- Fix `HandlerError` Display output ([#547](https://github.com/cosmos/ibc-rs/issues/547)) ## v0.32.0 @@ -1099,7 +1099,7 @@ There are no consensus-breaking changes. ([#479](https://github.com/cosmos/ibc-rs/issues/479)) - Remove Send + Sync supertraits on the Module trait ([#480](https://github.com/cosmos/ibc-rs/issues/480)) -- Modify `validate_self_client` error type to return `ContextError` instead of +- Modify `validate_self_client` error type to return `HandlerError` instead of `ConnectionError` ([#482](https://github.com/cosmos/ibc-rs/issues/482)) diff --git a/Cargo.toml b/Cargo.toml index 2ff25e88f..283f46b7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,7 @@ ibc-client-wasm-types = { version = "0.54.0", path = "./ibc-clients/ics08- ibc-app-transfer-types = { version = "0.54.0", path = "./ibc-apps/ics20-transfer/types", default-features = false } ibc-app-nft-transfer-types = { version = "0.54.0", path = "./ibc-apps/ics721-nft-transfer/types", default-features = false } -ibc-proto = { version = "0.47.0", default-features = false } +ibc-proto = { version = "0.47.1", default-features = false } # cosmos dependencies tendermint = { version = "0.38.0", default-features = false } diff --git a/ci/cw-check/Cargo.lock b/ci/cw-check/Cargo.lock index 54e6b75f0..128dbb7e6 100644 --- a/ci/cw-check/Cargo.lock +++ b/ci/cw-check/Cargo.lock @@ -1030,10 +1030,12 @@ dependencies = [ name = "ibc-core-host-types" version = "0.54.0" dependencies = [ + "base64 0.22.1", "derive_more 0.99.18", "displaydoc", "ibc-primitives", "parity-scale-codec", + "prost", "scale-info", "schemars", "serde", @@ -1096,9 +1098,9 @@ dependencies = [ [[package]] name = "ibc-proto" -version = "0.47.0" +version = "0.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1678333cf68c9094ca66aaf9a271269f1f6bf5c26881161def8bd88cee831a23" +checksum = "c852d22b782d2d793f4a646f968de419be635e02bc8798d5d74a6e44eef27733" dependencies = [ "base64 0.22.1", "bytes", diff --git a/ci/no-std-check/Cargo.lock b/ci/no-std-check/Cargo.lock index 01ea501f8..bbc64e565 100644 --- a/ci/no-std-check/Cargo.lock +++ b/ci/no-std-check/Cargo.lock @@ -1170,7 +1170,7 @@ dependencies = [ [[package]] name = "ibc" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-apps", "ibc-clients", @@ -1182,7 +1182,7 @@ dependencies = [ [[package]] name = "ibc-app-transfer" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-app-transfer-types", "ibc-core", @@ -1191,7 +1191,7 @@ dependencies = [ [[package]] name = "ibc-app-transfer-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1204,14 +1204,14 @@ dependencies = [ [[package]] name = "ibc-apps" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-app-transfer", ] [[package]] name = "ibc-client-tendermint" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "ibc-client-tendermint-types", @@ -1227,7 +1227,7 @@ dependencies = [ [[package]] name = "ibc-client-tendermint-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "displaydoc", "ibc-core-client-types", @@ -1243,7 +1243,7 @@ dependencies = [ [[package]] name = "ibc-client-wasm-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "base64 0.22.1", "displaydoc", @@ -1256,7 +1256,7 @@ dependencies = [ [[package]] name = "ibc-clients" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-client-tendermint", "ibc-client-wasm-types", @@ -1264,7 +1264,7 @@ dependencies = [ [[package]] name = "ibc-core" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -1279,7 +1279,7 @@ dependencies = [ [[package]] name = "ibc-core-channel" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-core-channel-types", "ibc-core-client", @@ -1293,7 +1293,7 @@ dependencies = [ [[package]] name = "ibc-core-channel-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1311,7 +1311,7 @@ dependencies = [ [[package]] name = "ibc-core-client" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-core-client-context", "ibc-core-client-types", @@ -1323,7 +1323,7 @@ dependencies = [ [[package]] name = "ibc-core-client-context" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1338,7 +1338,7 @@ dependencies = [ [[package]] name = "ibc-core-client-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1353,7 +1353,7 @@ dependencies = [ [[package]] name = "ibc-core-commitment-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1367,7 +1367,7 @@ dependencies = [ [[package]] name = "ibc-core-connection" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-core-client", "ibc-core-connection-types", @@ -1378,7 +1378,7 @@ dependencies = [ [[package]] name = "ibc-core-connection-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1394,7 +1394,7 @@ dependencies = [ [[package]] name = "ibc-core-handler" -version = "0.53.0" +version = "0.54.0" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -1408,7 +1408,7 @@ dependencies = [ [[package]] name = "ibc-core-handler-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1427,7 +1427,7 @@ dependencies = [ [[package]] name = "ibc-core-host" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1444,7 +1444,7 @@ dependencies = [ [[package]] name = "ibc-core-host-cosmos" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1466,17 +1466,19 @@ dependencies = [ [[package]] name = "ibc-core-host-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ + "base64 0.22.1", "derive_more", "displaydoc", "ibc-primitives", + "prost", "serde", ] [[package]] name = "ibc-core-router" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1489,7 +1491,7 @@ dependencies = [ [[package]] name = "ibc-core-router-types" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1503,7 +1505,7 @@ dependencies = [ [[package]] name = "ibc-derive" -version = "0.7.0" +version = "0.8.0" dependencies = [ "proc-macro2", "quote", @@ -1512,7 +1514,7 @@ dependencies = [ [[package]] name = "ibc-primitives" -version = "0.53.0" +version = "0.54.0" dependencies = [ "derive_more", "displaydoc", @@ -1525,9 +1527,9 @@ dependencies = [ [[package]] name = "ibc-proto" -version = "0.46.0" +version = "0.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb09e0b52b8a16e98ce98845e7c15b018440f3c56defa12fa44782cd66bab65" +checksum = "c852d22b782d2d793f4a646f968de419be635e02bc8798d5d74a6e44eef27733" dependencies = [ "base64 0.22.1", "borsh", @@ -1545,9 +1547,9 @@ dependencies = [ [[package]] name = "ics23" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3b8be84e7285c73b88effdc3294b552277d6b0ec728ee016c861b7b9a2c19c" +checksum = "73b17f1a5bd7d12ad30a21445cfa5f52fd7651cb3243ba866f9916b1ec112f12" dependencies = [ "anyhow", "blake2", @@ -2130,9 +2132,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.6" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" dependencies = [ "bytes", "prost-derive", @@ -2140,9 +2142,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", "itertools 0.12.1", @@ -2153,9 +2155,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.6" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" dependencies = [ "prost", ] @@ -3034,9 +3036,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tendermint" -version = "0.37.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954496fbc9716eb4446cdd6d00c071a3e2f22578d62aa03b40c7e5b4fda3ed42" +checksum = "505d9d6ffeb83b1de47c307c6e0d2dff56c6256989299010ad03cd80a8491e97" dependencies = [ "bytes", "digest 0.10.7", @@ -3063,9 +3065,9 @@ dependencies = [ [[package]] name = "tendermint-light-client-verifier" -version = "0.37.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3848090df4502a09ee27cb1a00f1835e1111c8993b22c5e1e41ffb7f6f09d57e" +checksum = "7a2674adbf0dc51aa0c8eaf8462c7d6692ec79502713e50ed5432a442002be90" dependencies = [ "derive_more", "flex-error", @@ -3076,9 +3078,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.37.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc87024548c7f3da479885201e3da20ef29e85a3b13d04606b380ac4c7120d87" +checksum = "8ed14abe3b0502a3afe21ca74ca5cdd6c7e8d326d982c26f98a394445eb31d6e" dependencies = [ "bytes", "flex-error", diff --git a/docs/architecture/adr-006-upgrade-client-implementation.md b/docs/architecture/adr-006-upgrade-client-implementation.md index d1aa8fc86..fbfd86535 100644 --- a/docs/architecture/adr-006-upgrade-client-implementation.md +++ b/docs/architecture/adr-006-upgrade-client-implementation.md @@ -119,7 +119,7 @@ supported by `IBC-rs`: negotiated on connection handshake. 10. (U) Changing parameters that are customizable by relayers like `TrustLevel` and `TrustingPeriod`, `max_clock_drift` - + #### Upgrade Process Step-by-step An IBC-connected Tendermint chain will take the following steps to completely @@ -250,7 +250,7 @@ previous section as mentioned: 1. ```rust if old_client_state.is_frozen() { - return Err(ContextError::ClientError(ClientError::ClientFrozen { + return Err(HandlerError::Client(ClientError::ClientFrozen { client_id, })); } diff --git a/docs/architecture/adr-007-light-client-contexts.md b/docs/architecture/adr-007-light-client-contexts.md index b0caabdd5..5bf30de70 100644 --- a/docs/architecture/adr-007-light-client-contexts.md +++ b/docs/architecture/adr-007-light-client-contexts.md @@ -98,12 +98,12 @@ pub trait ClientExecutionContext: Sized { /// Called upon successful client creation and update fn store_client_state( ... - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Called upon successful client creation and update fn store_consensus_state( ... - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; } ``` @@ -120,7 +120,7 @@ pub trait ValidationContext: Router { type ClientValidationContext; type ClientExecutionContext; /// Enum that can contain a `ConsensusState` object of any supported light client - type AnyConsensusState: ConsensusState; + type AnyConsensusState: ConsensusState; /// Enum that can contain a `ClientState` object of any supported light client type AnyClientState: ClientState< Self::AnyConsensusState, diff --git a/docs/architecture/adr-010-enable-standalone-ics02-integration.md b/docs/architecture/adr-010-enable-standalone-ics02-integration.md index 24dbe1149..feb0c9695 100644 --- a/docs/architecture/adr-010-enable-standalone-ics02-integration.md +++ b/docs/architecture/adr-010-enable-standalone-ics02-integration.md @@ -129,26 +129,26 @@ pub trait ValidationContext { fn get_client_validation_context(&self) -> &Self::V; // This method will be removed and replaced by a `ClientStateDecoder` trait that will encapsulate the ability to decode a client state from an `Any` -- fn decode_client_state(&self, client_state: Any) -> Result; +- fn decode_client_state(&self, client_state: Any) -> Result; -- fn client_state(&self, client_id: &ClientId) -> Result; +- fn client_state(&self, client_id: &ClientId) -> Result; - fn consensus_state( - &self, - client_cons_state_path: &ClientConsensusStatePath, -- ) -> Result; +- ) -> Result; fn host_consensus_state( &self, height: &Height, -- ) -> Result; -+ ) -> Result; +- ) -> Result; ++ ) -> Result; fn validate_self_client( &self, - client_state_of_host_on_counterparty: Any, + client_state_of_host_on_counterparty: Self::HostClientState, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; ... // other methods } @@ -200,18 +200,18 @@ pub trait ClientValidationContext: Sized { + type ClientStateRef: ClientStateValidation; + type ConsensusStateRef: ConsensusState; -+ fn client_state(&self, client_id: &ClientId) -> Result; ++ fn client_state(&self, client_id: &ClientId) -> Result; + fn consensus_state( + &self, + client_cons_state_path: &ClientConsensusStatePath, -+ ) -> Result; ++ ) -> Result; fn client_update_meta( &self, client_id: &ClientId, height: &Height, - ) -> Result<(Timestamp, Height), ContextError>; + ) -> Result<(Timestamp, Height), HostError>; } pub trait ClientExecutionContext: @@ -222,7 +222,7 @@ pub trait ClientExecutionContext: - type AnyConsensusState: ConsensusState; + type ClientStateMut: ClientStateExecution; -+ fn client_state_mut(&self, client_id: &ClientId) -> Result { ++ fn client_state_mut(&self, client_id: &ClientId) -> Result { + self.client_state(client_id) + } @@ -230,18 +230,18 @@ pub trait ClientExecutionContext: &mut self, client_state_path: ClientStatePath, client_state: Self::ClientStateMut, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; fn store_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, consensus_state: Self::ConsensusStateRef, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; fn delete_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; fn store_update_meta( &mut self, @@ -249,13 +249,13 @@ pub trait ClientExecutionContext: height: Height, host_timestamp: Timestamp, host_height: Height, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; fn delete_update_meta( &mut self, client_id: ClientId, height: Height, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; } ``` @@ -306,28 +306,28 @@ pub trait ExtClientValidationContext: + type ConversionError: ToString; + type AnyConsensusState: TryInto; -+ fn host_timestamp(&self) -> Result; ++ fn host_timestamp(&self) -> Result; -+ fn host_height(&self) -> Result; ++ fn host_height(&self) -> Result; - fn consensus_state( - &self, - client_cons_state_path: &ClientConsensusStatePath, -- ) -> Result; +- ) -> Result; -+ fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError>; ++ fn consensus_state_heights(&self, client_id: &ClientId) -> Result, HostError>; fn next_consensus_state( &self, client_id: &ClientId, height: &Height, - ) -> Result, ContextError>; + ) -> Result, HostError>; fn prev_consensus_state( &self, client_id: &ClientId, height: &Height, - ) -> Result, ContextError>; + ) -> Result, HostError>; } -impl ExecutionContext for T where T: CommonContext + ClientExecutionContext {} diff --git a/ibc-apps/ics20-transfer/src/context.rs b/ibc-apps/ics20-transfer/src/context.rs index 8c22ad386..2917c9fe9 100644 --- a/ibc-apps/ics20-transfer/src/context.rs +++ b/ibc-apps/ics20-transfer/src/context.rs @@ -1,7 +1,7 @@ //! Defines the main context traits and IBC module callbacks -use ibc_app_transfer_types::error::TokenTransferError; use ibc_app_transfer_types::{Memo, PrefixedCoin, PrefixedDenom}; +use ibc_core::host::types::error::HostError; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; use ibc_core::primitives::Signer; @@ -11,13 +11,13 @@ pub trait TokenTransferValidationContext { type AccountId: TryFrom; /// get_port returns the portID for the transfer module. - fn get_port(&self) -> Result; + fn get_port(&self) -> Result; /// Returns Ok() if the host chain supports sending coins. - fn can_send_coins(&self) -> Result<(), TokenTransferError>; + fn can_send_coins(&self) -> Result<(), HostError>; /// Returns Ok() if the host chain supports receiving coins. - fn can_receive_coins(&self) -> Result<(), TokenTransferError>; + fn can_receive_coins(&self) -> Result<(), HostError>; /// Validates that the tokens can be escrowed successfully. /// @@ -30,7 +30,7 @@ pub trait TokenTransferValidationContext { channel_id: &ChannelId, coin: &PrefixedCoin, memo: &Memo, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Validates that the tokens can be unescrowed successfully. fn unescrow_coins_validate( @@ -39,14 +39,14 @@ pub trait TokenTransferValidationContext { port_id: &PortId, channel_id: &ChannelId, coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Validates the receiver account and the coin input fn mint_coins_validate( &self, account: &Self::AccountId, coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Validates the sender account and the coin input before burning. /// @@ -57,7 +57,7 @@ pub trait TokenTransferValidationContext { account: &Self::AccountId, coin: &PrefixedCoin, memo: &Memo, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Returns a hash of the prefixed denom. /// Implement only if the host chain supports hashed denominations. @@ -79,7 +79,7 @@ pub trait TokenTransferExecutionContext: TokenTransferValidationContext { channel_id: &ChannelId, coin: &PrefixedCoin, memo: &Memo, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Executes the unescrow of the tokens in a user account. fn unescrow_coins_execute( @@ -88,14 +88,14 @@ pub trait TokenTransferExecutionContext: TokenTransferValidationContext { port_id: &PortId, channel_id: &ChannelId, coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Executes minting of the tokens in a user account. fn mint_coins_execute( &mut self, account: &Self::AccountId, coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; /// Executes burning of the tokens in a user account. /// @@ -106,5 +106,5 @@ pub trait TokenTransferExecutionContext: TokenTransferValidationContext { account: &Self::AccountId, coin: &PrefixedCoin, memo: &Memo, - ) -> Result<(), TokenTransferError>; + ) -> Result<(), HostError>; } diff --git a/ibc-apps/ics20-transfer/src/handler/mod.rs b/ibc-apps/ics20-transfer/src/handler/mod.rs index 39dcd373d..f1cd65967 100644 --- a/ibc-apps/ics20-transfer/src/handler/mod.rs +++ b/ibc-apps/ics20-transfer/src/handler/mod.rs @@ -6,6 +6,7 @@ use ibc_app_transfer_types::error::TokenTransferError; use ibc_app_transfer_types::is_sender_chain_source; use ibc_app_transfer_types::packet::PacketData; use ibc_core::channel::types::packet::Packet; +use ibc_core::primitives::prelude::*; pub use on_recv_packet::*; pub use send_transfer::*; @@ -20,7 +21,7 @@ pub fn refund_packet_token_execute( .sender .clone() .try_into() - .map_err(|_| TokenTransferError::ParseAccountFailure)?; + .map_err(|_| TokenTransferError::FailedToParseAccount)?; if is_sender_chain_source( packet.port_id_on_a.clone(), @@ -32,12 +33,14 @@ pub fn refund_packet_token_execute( &packet.port_id_on_a, &packet.chan_id_on_a, &data.token, - ) + )?; } // mint vouchers back to sender else { - ctx_a.mint_coins_execute(&sender, &data.token) + ctx_a.mint_coins_execute(&sender, &data.token)?; } + + Ok(()) } pub fn refund_packet_token_validate( @@ -49,7 +52,7 @@ pub fn refund_packet_token_validate( .sender .clone() .try_into() - .map_err(|_| TokenTransferError::ParseAccountFailure)?; + .map_err(|_| TokenTransferError::FailedToParseAccount)?; if is_sender_chain_source( packet.port_id_on_a.clone(), @@ -61,8 +64,10 @@ pub fn refund_packet_token_validate( &packet.port_id_on_a, &packet.chan_id_on_a, &data.token, - ) + )?; } else { - ctx_a.mint_coins_validate(&sender, &data.token) + ctx_a.mint_coins_validate(&sender, &data.token)?; } + + Ok(()) } diff --git a/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs b/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs index 193b33196..76e22b3b6 100644 --- a/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs +++ b/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs @@ -21,12 +21,12 @@ pub fn process_recv_packet_execute( ) -> Result { ctx_b .can_receive_coins() - .map_err(|err| (ModuleExtras::empty(), err))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; let receiver_account = data.receiver.clone().try_into().map_err(|_| { ( ModuleExtras::empty(), - TokenTransferError::ParseAccountFailure, + TokenTransferError::FailedToParseAccount, ) })?; @@ -60,7 +60,7 @@ pub fn process_recv_packet_execute( &packet.chan_id_on_b, &coin, ) - .map_err(|token_err| (ModuleExtras::empty(), token_err))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; ctx_b .unescrow_coins_execute( &receiver_account, @@ -68,7 +68,7 @@ pub fn process_recv_packet_execute( &packet.chan_id_on_b, &coin, ) - .map_err(|token_err| (ModuleExtras::empty(), token_err))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; ModuleExtras::empty() } else { @@ -103,11 +103,11 @@ pub fn process_recv_packet_execute( // can be refunded. ctx_b .mint_coins_validate(&receiver_account, &coin) - .map_err(|token_err| (extras.clone(), token_err))?; + .map_err(|err| (extras.clone(), err.into()))?; ctx_b .mint_coins_execute(&receiver_account, &coin) - .map_err(|token_err| (extras.clone(), token_err))?; + .map_err(|err| (extras.clone(), err.into()))?; extras }; diff --git a/ibc-apps/ics20-transfer/src/handler/send_transfer.rs b/ibc-apps/ics20-transfer/src/handler/send_transfer.rs index 439e5604c..39d40ea46 100644 --- a/ibc-apps/ics20-transfer/src/handler/send_transfer.rs +++ b/ibc-apps/ics20-transfer/src/handler/send_transfer.rs @@ -45,7 +45,7 @@ where let chan_id_on_b = chan_end_on_a .counterparty() .channel_id() - .ok_or_else(|| TokenTransferError::DestinationChannelNotFound { + .ok_or_else(|| TokenTransferError::MissingDestinationChannel { port_id: msg.port_id_on_a.clone(), channel_id: msg.chan_id_on_a.clone(), })? @@ -61,7 +61,7 @@ where .sender .clone() .try_into() - .map_err(|_| TokenTransferError::ParseAccountFailure)?; + .map_err(|_| TokenTransferError::FailedToParseAccount)?; if is_sender_chain_source( msg.port_id_on_a.clone(), @@ -117,7 +117,7 @@ where let chan_on_b = chan_end_on_a .counterparty() .channel_id() - .ok_or_else(|| TokenTransferError::DestinationChannelNotFound { + .ok_or_else(|| TokenTransferError::MissingDestinationChannel { port_id: msg.port_id_on_a.clone(), channel_id: msg.chan_id_on_a.clone(), })? @@ -134,7 +134,7 @@ where .sender .clone() .try_into() - .map_err(|_| TokenTransferError::ParseAccountFailure)?; + .map_err(|_| TokenTransferError::FailedToParseAccount)?; if is_sender_chain_source( msg.port_id_on_a.clone(), diff --git a/ibc-apps/ics20-transfer/src/module.rs b/ibc-apps/ics20-transfer/src/module.rs index 392ca5939..41d3016a4 100644 --- a/ibc-apps/ics20-transfer/src/module.rs +++ b/ibc-apps/ics20-transfer/src/module.rs @@ -6,7 +6,6 @@ use ibc_core::channel::types::acknowledgement::{Acknowledgement, Acknowledgement use ibc_core::channel::types::channel::{Counterparty, Order}; use ibc_core::channel::types::packet::Packet; use ibc_core::channel::types::Version; -use ibc_core::handler::types::error::ContextError; use ibc_core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc_core::primitives::prelude::*; use ibc_core::primitives::Signer; @@ -27,23 +26,21 @@ pub fn on_chan_open_init_validate( version: &Version, ) -> Result<(), TokenTransferError> { if order != Order::Unordered { - return Err(TokenTransferError::ChannelNotUnordered { - expect_order: Order::Unordered, - got_order: order, + return Err(TokenTransferError::MismatchedChannelOrders { + expected: Order::Unordered, + actual: order, }); } let bound_port = ctx.get_port()?; if port_id != &bound_port { - return Err(TokenTransferError::InvalidPort { - port_id: port_id.clone(), - exp_port_id: bound_port, + return Err(TokenTransferError::MismatchedPortIds { + actual: port_id.clone(), + expected: bound_port, }); } if !version.is_empty() { - version - .verify_is_expected(Version::new(VERSION.to_string())) - .map_err(ContextError::from)?; + version.verify_is_expected(Version::new(VERSION.to_string()))?; } Ok(()) @@ -71,15 +68,13 @@ pub fn on_chan_open_try_validate( counterparty_version: &Version, ) -> Result<(), TokenTransferError> { if order != Order::Unordered { - return Err(TokenTransferError::ChannelNotUnordered { - expect_order: Order::Unordered, - got_order: order, + return Err(TokenTransferError::MismatchedChannelOrders { + expected: Order::Unordered, + actual: order, }); } - counterparty_version - .verify_is_expected(Version::new(VERSION.to_string())) - .map_err(ContextError::from)?; + counterparty_version.verify_is_expected(Version::new(VERSION.to_string()))?; Ok(()) } @@ -102,9 +97,7 @@ pub fn on_chan_open_ack_validate( _channel_id: &ChannelId, counterparty_version: &Version, ) -> Result<(), TokenTransferError> { - counterparty_version - .verify_is_expected(Version::new(VERSION.to_string())) - .map_err(ContextError::from)?; + counterparty_version.verify_is_expected(Version::new(VERSION.to_string()))?; Ok(()) } @@ -139,7 +132,7 @@ pub fn on_chan_close_init_validate( _port_id: &PortId, _channel_id: &ChannelId, ) -> Result<(), TokenTransferError> { - Err(TokenTransferError::CantCloseChannel) + Err(TokenTransferError::InvalidClosedChannel) } pub fn on_chan_close_init_execute( @@ -147,7 +140,7 @@ pub fn on_chan_close_init_execute( _port_id: &PortId, _channel_id: &ChannelId, ) -> Result { - Err(TokenTransferError::CantCloseChannel) + Err(TokenTransferError::InvalidClosedChannel) } pub fn on_chan_close_confirm_validate( @@ -172,7 +165,7 @@ pub fn on_recv_packet_execute( ) -> (ModuleExtras, Acknowledgement) { let Ok(data) = serde_json::from_slice::(&packet.data) else { let ack = - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()); + AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into()); return (ModuleExtras::empty(), ack.into()); }; @@ -204,10 +197,10 @@ where Ctx: TokenTransferValidationContext, { let data = serde_json::from_slice::(&packet.data) - .map_err(|_| TokenTransferError::PacketDataDeserialization)?; + .map_err(|_| TokenTransferError::FailedToDeserializePacketData)?; let acknowledgement = serde_json::from_slice::(acknowledgement.as_ref()) - .map_err(|_| TokenTransferError::AckDeserialization)?; + .map_err(|_| TokenTransferError::FailedToDeserializeAck)?; if !acknowledgement.is_successful() { refund_packet_token_validate(ctx, packet, &data)?; @@ -225,7 +218,7 @@ pub fn on_acknowledgement_packet_execute( let Ok(data) = serde_json::from_slice::(&packet.data) else { return ( ModuleExtras::empty(), - Err(TokenTransferError::PacketDataDeserialization), + Err(TokenTransferError::FailedToDeserializePacketData), ); }; @@ -234,7 +227,7 @@ pub fn on_acknowledgement_packet_execute( else { return ( ModuleExtras::empty(), - Err(TokenTransferError::AckDeserialization), + Err(TokenTransferError::FailedToDeserializeAck), ); }; @@ -270,7 +263,7 @@ where Ctx: TokenTransferValidationContext, { let data = serde_json::from_slice::(&packet.data) - .map_err(|_| TokenTransferError::PacketDataDeserialization)?; + .map_err(|_| TokenTransferError::FailedToDeserializePacketData)?; refund_packet_token_validate(ctx, packet, &data)?; @@ -285,7 +278,7 @@ pub fn on_timeout_packet_execute( let Ok(data) = serde_json::from_slice::(&packet.data) else { return ( ModuleExtras::empty(), - Err(TokenTransferError::PacketDataDeserialization), + Err(TokenTransferError::FailedToDeserializePacketData), ); }; @@ -324,7 +317,7 @@ mod test { r#"{"result":"AQ=="}"#, ); ser_json_assert_eq( - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()), + AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into()), r#"{"error":"failed to deserialize packet data"}"#, ); } @@ -342,7 +335,7 @@ mod test { #[test] fn test_ack_error_to_vec() { let ack_error: Vec = - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()) + AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into()) .into(); // Check that it's the same output as ibc-go @@ -367,7 +360,7 @@ mod test { ); de_json_assert_eq( r#"{"error":"failed to deserialize packet data"}"#, - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()), + AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into()), ); assert!(serde_json::from_str::(r#"{"success":"AQ=="}"#).is_err()); diff --git a/ibc-apps/ics20-transfer/types/src/amount.rs b/ibc-apps/ics20-transfer/types/src/amount.rs index 6952be514..8f1b2c81f 100644 --- a/ibc-apps/ics20-transfer/types/src/amount.rs +++ b/ibc-apps/ics20-transfer/types/src/amount.rs @@ -3,13 +3,12 @@ use core::ops::Deref; use core::str::FromStr; use derive_more::{Display, From, Into}; +use ibc_core::host::types::error::DecodingError; use ibc_core::primitives::prelude::*; #[cfg(feature = "serde")] use ibc_core::primitives::serializers; use primitive_types::U256; -use super::error::TokenTransferError; - /// A type for representing token transfer amounts. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] @@ -103,10 +102,13 @@ impl AsRef for Amount { } impl FromStr for Amount { - type Err = TokenTransferError; + type Err = DecodingError; fn from_str(s: &str) -> Result { - let amount = U256::from_dec_str(s).map_err(TokenTransferError::InvalidAmount)?; + let amount = U256::from_dec_str(s).map_err(|e| { + DecodingError::invalid_raw_data(format!("amount could not be parsed as a U256: {e}")) + })?; + Ok(Self(amount)) } } diff --git a/ibc-apps/ics20-transfer/types/src/coin.rs b/ibc-apps/ics20-transfer/types/src/coin.rs index 65a147218..db95bcc9f 100644 --- a/ibc-apps/ics20-transfer/types/src/coin.rs +++ b/ibc-apps/ics20-transfer/types/src/coin.rs @@ -2,12 +2,12 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use core::str::FromStr; +use ibc_core::host::types::error::DecodingError; use ibc_core::primitives::prelude::*; use ibc_proto::cosmos::base::v1beta1::Coin as ProtoCoin; use super::amount::Amount; use super::denom::{BaseDenom, PrefixedDenom}; -use super::error::TokenTransferError; /// A `Coin` type with fully qualified `PrefixedDenom`. pub type PrefixedCoin = Coin; @@ -41,21 +41,21 @@ pub struct Coin { impl Coin where - D::Err: Into, + D::Err: Display, { - pub fn from_string_list(coin_str: &str) -> Result, TokenTransferError> { + pub fn from_string_list(coin_str: &str) -> Result, DecodingError> { coin_str.split(',').map(FromStr::from_str).collect() } } impl FromStr for Coin where - D::Err: Into, + D::Err: Display, { - type Err = TokenTransferError; + type Err = DecodingError; #[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: @@ -76,22 +76,22 @@ where .chars() .all(|x| x.is_alphanumeric() || VALID_DENOM_CHARACTERS.contains(x)) }) - .ok_or_else(|| TokenTransferError::InvalidCoin { - coin: coin_str.to_string(), - })?; + .ok_or(DecodingError::invalid_raw_data(format!( + "coin str: {coin_str}" + )))?; Ok(Coin { amount: amount.parse()?, - denom: denom.parse().map_err(Into::into)?, + denom: denom.parse().map_err(DecodingError::invalid_raw_data)?, }) } } impl TryFrom for Coin where - D::Err: Into, + D::Err: Into, { - type Error = TokenTransferError; + type Error = DecodingError; fn try_from(proto: ProtoCoin) -> Result, Self::Error> { let denom = D::from_str(&proto.denom).map_err(Into::into)?; @@ -178,7 +178,7 @@ mod tests { fn test_parse_raw_coin_list( #[case] coins_str: &str, #[case] coins: &[(u64, &str)], - ) -> Result<(), TokenTransferError> { + ) -> Result<(), DecodingError> { assert_eq!( RawCoin::from_string_list(coins_str)?, coins diff --git a/ibc-apps/ics20-transfer/types/src/denom.rs b/ibc-apps/ics20-transfer/types/src/denom.rs index 4bd30f60c..02ffc71c9 100644 --- a/ibc-apps/ics20-transfer/types/src/denom.rs +++ b/ibc-apps/ics20-transfer/types/src/denom.rs @@ -3,14 +3,13 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use core::str::FromStr; use derive_more::{Display, From}; +use ibc_core::host::types::error::DecodingError; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; #[cfg(feature = "serde")] use ibc_core::primitives::serializers; use ibc_proto::ibc::applications::transfer::v1::DenomTrace as RawDenomTrace; -use super::error::TokenTransferError; - /// The "base" of a denomination. /// /// For example, given the token `my_port-1/my_channel-1/my_port-2/my_channel-2/base_denom`, @@ -40,11 +39,11 @@ impl BaseDenom { } impl FromStr for BaseDenom { - type Err = TokenTransferError; + type Err = DecodingError; fn from_str(s: &str) -> Result { if s.trim().is_empty() { - Err(TokenTransferError::EmptyBaseDenom) + Err(DecodingError::missing_raw_data("empty base denom")) } else { Ok(BaseDenom(s.to_owned())) } @@ -208,7 +207,7 @@ impl TracePath { } impl FromStr for TracePath { - type Err = TokenTransferError; + type Err = DecodingError; fn from_str(s: &str) -> Result { if s.is_empty() { @@ -219,7 +218,7 @@ impl FromStr for TracePath { remaining_parts .is_none() .then_some(trace_path) - .ok_or_else(|| TokenTransferError::MalformedTrace(s.to_string())) + .ok_or(DecodingError::invalid_raw_data(format!("trace path: {s}"))) } } @@ -323,7 +322,7 @@ pub fn is_receiver_chain_source( } impl FromStr for PrefixedDenom { - type Err = TokenTransferError; + type Err = DecodingError; /// Initializes a [`PrefixedDenom`] from a string that adheres to the format /// `{nth-port-id/channel-}/{(n-1)th-port-id/channel-}/.../{1st-port-id/channel-}/`. @@ -361,7 +360,7 @@ impl FromStr for PrefixedDenom { } impl TryFrom for PrefixedDenom { - type Error = TokenTransferError; + type Error = DecodingError; fn try_from(value: RawDenomTrace) -> Result { let base_denom = BaseDenom::from_str(&value.base_denom)?; @@ -483,7 +482,7 @@ mod tests { fn test_strange_but_accepted_prefixed_denom( #[case] prefix: &str, #[case] denom: &str, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), DecodingError> { let pd_s = if prefix.is_empty() { denom.to_owned() } else { @@ -505,9 +504,9 @@ mod tests { #[case("transfer/channel-1/transfer/channel-2/")] #[case("transfer/channel-21/transfer/channel-23/ ")] #[case("transfer/channel-0/")] - #[should_panic(expected = "EmptyBaseDenom")] fn test_prefixed_empty_base_denom(#[case] pd_s: &str) { - PrefixedDenom::from_str(pd_s).expect("error"); + PrefixedDenom::from_str(pd_s) + .expect_err("error: MissingRawData { description: \"empty base denom\" }"); } #[rstest] @@ -617,7 +616,7 @@ mod tests { } #[test] - fn test_trace_path() -> Result<(), TokenTransferError> { + fn test_trace_path() -> Result<(), DecodingError> { assert!(TracePath::from_str("").is_ok(), "empty trace path"); assert!( TracePath::from_str("transfer/uatom").is_err(), diff --git a/ibc-apps/ics20-transfer/types/src/error.rs b/ibc-apps/ics20-transfer/types/src/error.rs index a394eb77d..3c2058598 100644 --- a/ibc-apps/ics20-transfer/types/src/error.rs +++ b/ibc-apps/ics20-transfer/types/src/error.rs @@ -1,127 +1,53 @@ //! Defines the token transfer error type -use core::convert::Infallible; -use core::str::Utf8Error; - use displaydoc::Display; use ibc_core::channel::types::acknowledgement::StatusValue; use ibc_core::channel::types::channel::Order; -use ibc_core::handler::types::error::ContextError; -use ibc_core::host::types::error::IdentifierError; +use ibc_core::channel::types::error::ChannelError; +use ibc_core::host::types::error::{DecodingError, HostError}; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; -use uint::FromDecStrErr; -#[derive(Display, Debug)] +#[derive(Display, Debug, derive_more::From)] pub enum TokenTransferError { - /// context error: `{0}` - ContextError(ContextError), - /// invalid identifier: `{0}` - InvalidIdentifier(IdentifierError), - /// insufficient funds: tried to send `{send_attempt}`, sender only has `{available_funds}` - InsufficientFunds { - send_attempt: String, - available_funds: String, - }, - /// destination channel not found in the counterparty of port_id `{port_id}` and channel_id `{channel_id}` - DestinationChannelNotFound { + /// host error: {0} + Host(HostError), + /// decoding error: {0} + Decoding(DecodingError), + /// channel error: {0} + Channel(ChannelError), + /// missing destination channel `{channel_id}` on port `{port_id}` + MissingDestinationChannel { port_id: PortId, channel_id: ChannelId, }, - /// base denomination is empty - EmptyBaseDenom, - /// invalid prot id n trace at position: `{pos}`, validation error: `{validation_error}` - InvalidTracePortId { - pos: u64, - validation_error: IdentifierError, - }, - /// invalid channel id in trace at position: `{pos}`, validation error: `{validation_error}` - InvalidTraceChannelId { - pos: u64, - validation_error: IdentifierError, - }, - /// malformed trace: `{0}` - MalformedTrace(String), - /// trace length must be even but got: `{len}` - InvalidTraceLength { len: u64 }, - /// invalid amount: `{0}` - InvalidAmount(FromDecStrErr), - /// invalid token - InvalidToken, - /// expected `{expect_order}` channel, got `{got_order}` - ChannelNotUnordered { - expect_order: Order, - got_order: Order, - }, - /// channel cannot be closed - CantCloseChannel, + /// mismatched channel orders: expected `{expected}`, actual `{actual}` + MismatchedChannelOrders { expected: Order, actual: Order }, + /// mismatched port IDs: expected `{expected}`, actual `{actual}` + MismatchedPortIds { expected: PortId, actual: PortId }, + /// invalid channel state: cannot be closed + InvalidClosedChannel, /// failed to deserialize packet data - PacketDataDeserialization, + FailedToDeserializePacketData, /// failed to deserialize acknowledgement - AckDeserialization, - /// receive is not enabled - ReceiveDisabled { reason: String }, - /// send is not enabled - SendDisabled { reason: String }, - /// 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: `{reason}` - DecodeRawMsg { reason: String }, - /// unknown msg type: `{msg_type}` - UnknownMsgType { msg_type: String }, - /// invalid coin string: `{coin}` - InvalidCoin { coin: String }, - /// decoding raw bytes as UTF-8 string error: `{0}` - Utf8Decode(Utf8Error), - /// other error: `{0}` - Other(String), + FailedToDeserializeAck, + /// failed to parse account + FailedToParseAccount, } #[cfg(feature = "std")] impl std::error::Error for TokenTransferError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::ContextError(e) => Some(e), - Self::InvalidIdentifier(e) - | Self::InvalidTracePortId { - validation_error: e, - .. - } - | Self::InvalidTraceChannelId { - validation_error: e, - .. - } => Some(e), - Self::InvalidAmount(e) => Some(e), - Self::Utf8Decode(e) => Some(e), + Self::Host(e) => Some(e), + Self::Decoding(e) => Some(e), + Self::Channel(e) => Some(e), _ => None, } } } -impl From for TokenTransferError { - fn from(e: Infallible) -> Self { - match e {} - } -} - -impl From for TokenTransferError { - fn from(err: ContextError) -> TokenTransferError { - Self::ContextError(err) - } -} - -impl From for TokenTransferError { - fn from(err: IdentifierError) -> TokenTransferError { - Self::InvalidIdentifier(err) - } -} - impl From for StatusValue { - fn from(err: TokenTransferError) -> Self { - StatusValue::new(err.to_string()).expect("error message must not be empty") + fn from(e: TokenTransferError) -> Self { + StatusValue::new(e.to_string()).expect("error message must not be empty") } } diff --git a/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs b/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs index cc311a2ca..a337b2b51 100644 --- a/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs +++ b/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs @@ -1,15 +1,13 @@ //! Defines the token transfer message type -use ibc_core::channel::types::error::PacketError; use ibc_core::channel::types::timeout::{TimeoutHeight, TimeoutTimestamp}; -use ibc_core::handler::types::error::ContextError; +use ibc_core::host::types::error::DecodingError; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::applications::transfer::v1::MsgTransfer as RawMsgTransfer; use ibc_proto::Protobuf; -use crate::error::TokenTransferError; use crate::packet::PacketData; pub(crate) const TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; @@ -48,19 +46,17 @@ pub struct MsgTransfer { } impl TryFrom for MsgTransfer { - type Error = TokenTransferError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgTransfer) -> Result { - let timeout_height_on_b: TimeoutHeight = raw_msg - .timeout_height - .try_into() - .map_err(ContextError::from)?; - + let timeout_height_on_b: TimeoutHeight = raw_msg.timeout_height.try_into()?; let timeout_timestamp_on_b: TimeoutTimestamp = raw_msg.timeout_timestamp.into(); // Packet timeout height and packet timeout timestamp cannot both be unset. if !timeout_height_on_b.is_set() && !timeout_timestamp_on_b.is_set() { - return Err(ContextError::from(PacketError::MissingTimeout))?; + return Err(DecodingError::missing_raw_data( + "msg transfer timeout height or timeout timestamp", + )); } Ok(MsgTransfer { @@ -69,9 +65,8 @@ impl TryFrom for MsgTransfer { packet_data: PacketData { token: raw_msg .token - .ok_or(TokenTransferError::InvalidToken)? - .try_into() - .map_err(|_| TokenTransferError::InvalidToken)?, + .ok_or(DecodingError::missing_raw_data("msg transfer token"))? + .try_into()?, sender: raw_msg.sender.into(), receiver: raw_msg.receiver.into(), memo: raw_msg.memo.into(), @@ -100,18 +95,16 @@ impl From for RawMsgTransfer { impl Protobuf for MsgTransfer {} impl TryFrom for MsgTransfer { - type Error = TokenTransferError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - match raw.type_url.as_str() { - TYPE_URL => { - MsgTransfer::decode_vec(&raw.value).map_err(|e| TokenTransferError::DecodeRawMsg { - reason: e.to_string(), - }) - } - _ => Err(TokenTransferError::UnknownMsgType { - msg_type: raw.type_url, - }), + if let TYPE_URL = raw.type_url.as_str() { + MsgTransfer::decode_vec(&raw.value).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-apps/ics20-transfer/types/src/packet.rs b/ibc-apps/ics20-transfer/types/src/packet.rs index a3615a090..8d4d5192c 100644 --- a/ibc-apps/ics20-transfer/types/src/packet.rs +++ b/ibc-apps/ics20-transfer/types/src/packet.rs @@ -2,11 +2,11 @@ use core::str::FromStr; +use ibc_core::host::types::error::DecodingError; use ibc_core::primitives::prelude::*; use ibc_core::primitives::Signer; use ibc_proto::ibc::applications::transfer::v2::FungibleTokenPacketData as RawPacketData; -use super::error::TokenTransferError; use super::{Amount, Memo, PrefixedCoin, PrefixedDenom}; /// Defines the structure of token transfers' packet bytes @@ -33,7 +33,7 @@ pub struct PacketData { } impl TryFrom for PacketData { - type Error = TokenTransferError; + type Error = DecodingError; fn try_from(raw_pkt_data: RawPacketData) -> Result { // This denom may be prefixed or unprefixed. diff --git a/ibc-apps/ics721-nft-transfer/src/context.rs b/ibc-apps/ics721-nft-transfer/src/context.rs index ede6ee898..201d342cb 100644 --- a/ibc-apps/ics721-nft-transfer/src/context.rs +++ b/ibc-apps/ics721-nft-transfer/src/context.rs @@ -1,10 +1,10 @@ //! Defines the required context traits for ICS-721 to interact with host //! machine. +use ibc_core::host::types::error::HostError; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; use ibc_core::primitives::Signer; -use crate::types::error::NftTransferError; use crate::types::{ ClassData, ClassId, ClassUri, Memo, PrefixedClassId, TokenData, TokenId, TokenUri, }; @@ -41,13 +41,13 @@ pub trait NftTransferValidationContext { type NftClass: NftClassContext; /// get_port returns the portID for the transfer module. - fn get_port(&self) -> Result; + fn get_port(&self) -> Result; /// Returns Ok() if the host chain supports sending NFTs. - fn can_send_nft(&self) -> Result<(), NftTransferError>; + fn can_send_nft(&self) -> Result<(), HostError>; /// Returns Ok() if the host chain supports receiving NFTs. - fn can_receive_nft(&self) -> Result<(), NftTransferError>; + fn can_receive_nft(&self) -> Result<(), HostError>; /// Validates that the NFT can be created or updated successfully. /// @@ -62,7 +62,7 @@ pub trait NftTransferValidationContext { class_id: &PrefixedClassId, class_uri: Option<&ClassUri>, class_data: Option<&ClassData>, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Validates that the tokens can be escrowed successfully. /// @@ -77,7 +77,7 @@ pub trait NftTransferValidationContext { class_id: &PrefixedClassId, token_id: &TokenId, memo: &Memo, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Validates that the NFT can be unescrowed successfully. fn unescrow_nft_validate( @@ -87,7 +87,7 @@ pub trait NftTransferValidationContext { channel_id: &ChannelId, class_id: &PrefixedClassId, token_id: &TokenId, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Validates the receiver account and the NFT input /// @@ -104,7 +104,7 @@ pub trait NftTransferValidationContext { token_id: &TokenId, token_uri: Option<&TokenUri>, token_data: Option<&TokenData>, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Validates the sender account and the coin input before burning. /// @@ -117,7 +117,7 @@ pub trait NftTransferValidationContext { class_id: &PrefixedClassId, token_id: &TokenId, memo: &Memo, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Returns a hash of the prefixed class ID and the token ID. /// Implement only if the host chain supports hashed class ID and token ID. @@ -134,11 +134,10 @@ pub trait NftTransferValidationContext { &self, class_id: &PrefixedClassId, token_id: &TokenId, - ) -> Result; + ) -> Result; /// Returns the NFT class - fn get_nft_class(&self, class_id: &PrefixedClassId) - -> Result; + fn get_nft_class(&self, class_id: &PrefixedClassId) -> Result; } /// Read-write methods required in NFT transfer execution context. @@ -149,7 +148,7 @@ pub trait NftTransferExecutionContext: NftTransferValidationContext { class_id: &PrefixedClassId, class_uri: Option<&ClassUri>, class_data: Option<&ClassData>, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Executes the escrow of the NFT in a user account. /// @@ -163,7 +162,7 @@ pub trait NftTransferExecutionContext: NftTransferValidationContext { class_id: &PrefixedClassId, token_id: &TokenId, memo: &Memo, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Executes the unescrow of the NFT in a user account. fn unescrow_nft_execute( @@ -173,7 +172,7 @@ pub trait NftTransferExecutionContext: NftTransferValidationContext { channel_id: &ChannelId, class_id: &PrefixedClassId, token_id: &TokenId, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Executes minting of the NFT in a user account. fn mint_nft_execute( @@ -183,7 +182,7 @@ pub trait NftTransferExecutionContext: NftTransferValidationContext { token_id: &TokenId, token_uri: Option<&TokenUri>, token_data: Option<&TokenData>, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; /// Executes burning of the NFT in a user account. /// @@ -195,5 +194,5 @@ pub trait NftTransferExecutionContext: NftTransferValidationContext { class_id: &PrefixedClassId, token_id: &TokenId, memo: &Memo, - ) -> Result<(), NftTransferError>; + ) -> Result<(), HostError>; } diff --git a/ibc-apps/ics721-nft-transfer/src/handler/mod.rs b/ibc-apps/ics721-nft-transfer/src/handler/mod.rs index 2adbb47f6..661533958 100644 --- a/ibc-apps/ics721-nft-transfer/src/handler/mod.rs +++ b/ibc-apps/ics721-nft-transfer/src/handler/mod.rs @@ -21,7 +21,7 @@ pub fn refund_packet_nft_execute( .sender .clone() .try_into() - .map_err(|_| NftTransferError::ParseAccountFailure)?; + .map_err(|_| NftTransferError::FailedToParseAccount)?; if is_sender_chain_source( packet.port_id_on_a.clone(), @@ -29,13 +29,15 @@ pub fn refund_packet_nft_execute( &data.class_id, ) { data.token_ids.as_ref().iter().try_for_each(|token_id| { - ctx_a.unescrow_nft_execute( - &sender, - &packet.port_id_on_a, - &packet.chan_id_on_a, - &data.class_id, - token_id, - ) + ctx_a + .unescrow_nft_execute( + &sender, + &packet.port_id_on_a, + &packet.chan_id_on_a, + &data.class_id, + token_id, + ) + .map_err(NftTransferError::from) }) } // mint vouchers back to sender @@ -43,8 +45,10 @@ pub fn refund_packet_nft_execute( for (i, token_id) in data.token_ids.0.iter().enumerate() { let token_uri = data.token_uris.as_ref().and_then(|uris| uris.get(i)); let token_data = data.token_data.as_ref().and_then(|data| data.get(i)); + ctx_a.mint_nft_execute(&sender, &data.class_id, token_id, token_uri, token_data)?; } + Ok(()) } } @@ -58,7 +62,7 @@ pub fn refund_packet_nft_validate( .sender .clone() .try_into() - .map_err(|_| NftTransferError::ParseAccountFailure)?; + .map_err(|_| NftTransferError::FailedToParseAccount)?; if is_sender_chain_source( packet.port_id_on_a.clone(), @@ -66,20 +70,24 @@ pub fn refund_packet_nft_validate( &data.class_id, ) { data.token_ids.0.iter().try_for_each(|token_id| { - ctx_a.unescrow_nft_validate( - &sender, - &packet.port_id_on_a, - &packet.chan_id_on_a, - &data.class_id, - token_id, - ) + ctx_a + .unescrow_nft_validate( + &sender, + &packet.port_id_on_a, + &packet.chan_id_on_a, + &data.class_id, + token_id, + ) + .map_err(NftTransferError::from) }) } else { for (i, token_id) in data.token_ids.0.iter().enumerate() { let token_uri = data.token_uris.as_ref().and_then(|uris| uris.get(i)); let token_data = data.token_data.as_ref().and_then(|data| data.get(i)); + ctx_a.mint_nft_validate(&sender, &data.class_id, token_id, token_uri, token_data)?; } + Ok(()) } } diff --git a/ibc-apps/ics721-nft-transfer/src/handler/on_recv_packet.rs b/ibc-apps/ics721-nft-transfer/src/handler/on_recv_packet.rs index 8782bf7a2..6d8eeb194 100644 --- a/ibc-apps/ics721-nft-transfer/src/handler/on_recv_packet.rs +++ b/ibc-apps/ics721-nft-transfer/src/handler/on_recv_packet.rs @@ -23,13 +23,14 @@ where { ctx_b .can_receive_nft() - .map_err(|err| (ModuleExtras::empty(), err))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; - let receiver_account = data - .receiver - .clone() - .try_into() - .map_err(|_| (ModuleExtras::empty(), NftTransferError::ParseAccountFailure))?; + let receiver_account = data.receiver.clone().try_into().map_err(|_| { + ( + ModuleExtras::empty(), + NftTransferError::FailedToParseAccount, + ) + })?; let extras = if is_receiver_chain_source( packet.port_id_on_a.clone(), @@ -55,7 +56,7 @@ where &class_id, token_id, ) - .map_err(|nft_error| (ModuleExtras::empty(), nft_error))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; ctx_b .unescrow_nft_execute( &receiver_account, @@ -64,7 +65,7 @@ where &class_id, token_id, ) - .map_err(|nft_error| (ModuleExtras::empty(), nft_error))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; } ModuleExtras::empty() @@ -101,14 +102,14 @@ where data.class_uri.as_ref(), data.class_data.as_ref(), ) - .map_err(|nft_error| (ModuleExtras::empty(), nft_error))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; ctx_b .create_or_update_class_execute( &class_id, data.class_uri.as_ref(), data.class_data.as_ref(), ) - .map_err(|nft_error| (ModuleExtras::empty(), nft_error))?; + .map_err(|err| (ModuleExtras::empty(), err.into()))?; ctx_b .mint_nft_validate( @@ -118,7 +119,7 @@ where token_uri, token_data, ) - .map_err(|nft_error| (extras.clone(), nft_error))?; + .map_err(|err| (extras.clone(), err.into()))?; ctx_b .mint_nft_execute( &receiver_account, @@ -127,7 +128,7 @@ where token_uri, token_data, ) - .map_err(|nft_error| (extras.clone(), nft_error))?; + .map_err(|err| (extras.clone(), err.into()))?; } extras diff --git a/ibc-apps/ics721-nft-transfer/src/handler/send_transfer.rs b/ibc-apps/ics721-nft-transfer/src/handler/send_transfer.rs index 4cf1a759c..8a33fef56 100644 --- a/ibc-apps/ics721-nft-transfer/src/handler/send_transfer.rs +++ b/ibc-apps/ics721-nft-transfer/src/handler/send_transfer.rs @@ -47,7 +47,7 @@ where let chan_id_on_b = chan_end_on_a .counterparty() .channel_id() - .ok_or_else(|| NftTransferError::DestinationChannelNotFound { + .ok_or_else(|| NftTransferError::MissingDestinationChannel { port_id: msg.port_id_on_a.clone(), channel_id: msg.chan_id_on_a.clone(), })? @@ -61,7 +61,7 @@ where .sender .clone() .try_into() - .map_err(|_| NftTransferError::ParseAccountFailure)?; + .map_err(|_| NftTransferError::FailedToParseAccount)?; let mut packet_data = msg.packet_data; let class_id = &packet_data.class_id; @@ -149,7 +149,7 @@ where let chan_on_b = chan_end_on_a .counterparty() .channel_id() - .ok_or_else(|| NftTransferError::DestinationChannelNotFound { + .ok_or_else(|| NftTransferError::MissingDestinationChannel { port_id: msg.port_id_on_a.clone(), channel_id: msg.chan_id_on_a.clone(), })? @@ -164,7 +164,7 @@ where .sender .clone() .try_into() - .map_err(|_| NftTransferError::ParseAccountFailure)?; + .map_err(|_| NftTransferError::FailedToParseAccount)?; let mut packet_data = msg.packet_data; let class_id = &packet_data.class_id; diff --git a/ibc-apps/ics721-nft-transfer/src/module.rs b/ibc-apps/ics721-nft-transfer/src/module.rs index 074e7c869..53b45e7c3 100644 --- a/ibc-apps/ics721-nft-transfer/src/module.rs +++ b/ibc-apps/ics721-nft-transfer/src/module.rs @@ -3,7 +3,6 @@ use ibc_core::channel::types::acknowledgement::{Acknowledgement, Acknowledgement use ibc_core::channel::types::channel::{Counterparty, Order}; use ibc_core::channel::types::packet::Packet; use ibc_core::channel::types::Version; -use ibc_core::handler::types::error::ContextError; use ibc_core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc_core::primitives::prelude::*; use ibc_core::primitives::Signer; @@ -28,23 +27,21 @@ pub fn on_chan_open_init_validate( version: &Version, ) -> Result<(), NftTransferError> { if order != Order::Unordered { - return Err(NftTransferError::ChannelNotUnordered { - expect_order: Order::Unordered, - got_order: order, + return Err(NftTransferError::MismatchedChannelOrders { + expected: Order::Unordered, + actual: order, }); } let bound_port = ctx.get_port()?; if port_id != &bound_port { - return Err(NftTransferError::InvalidPort { - port_id: port_id.clone(), - exp_port_id: bound_port, + return Err(NftTransferError::MismatchedPortIds { + actual: port_id.clone(), + expected: bound_port, }); } if !version.is_empty() { - version - .verify_is_expected(Version::new(VERSION.to_string())) - .map_err(ContextError::from)?; + version.verify_is_expected(Version::new(VERSION.to_string()))?; } Ok(()) @@ -72,15 +69,13 @@ pub fn on_chan_open_try_validate( counterparty_version: &Version, ) -> Result<(), NftTransferError> { if order != Order::Unordered { - return Err(NftTransferError::ChannelNotUnordered { - expect_order: Order::Unordered, - got_order: order, + return Err(NftTransferError::MismatchedChannelOrders { + expected: Order::Unordered, + actual: order, }); } - counterparty_version - .verify_is_expected(Version::new(VERSION.to_string())) - .map_err(ContextError::from)?; + counterparty_version.verify_is_expected(Version::new(VERSION.to_string()))?; Ok(()) } @@ -103,10 +98,7 @@ pub fn on_chan_open_ack_validate( _channel_id: &ChannelId, counterparty_version: &Version, ) -> Result<(), NftTransferError> { - counterparty_version - .verify_is_expected(Version::new(VERSION.to_string())) - .map_err(ContextError::from)?; - + counterparty_version.verify_is_expected(Version::new(VERSION.to_string()))?; Ok(()) } @@ -140,7 +132,7 @@ pub fn on_chan_close_init_validate( _port_id: &PortId, _channel_id: &ChannelId, ) -> Result<(), NftTransferError> { - Err(NftTransferError::CantCloseChannel) + Err(NftTransferError::InvalidClosedChannel) } pub fn on_chan_close_init_execute( @@ -148,7 +140,7 @@ pub fn on_chan_close_init_execute( _port_id: &PortId, _channel_id: &ChannelId, ) -> Result { - Err(NftTransferError::CantCloseChannel) + Err(NftTransferError::InvalidClosedChannel) } pub fn on_chan_close_confirm_validate( @@ -172,7 +164,8 @@ pub fn on_recv_packet_execute( packet: &Packet, ) -> (ModuleExtras, Acknowledgement) { let Ok(data) = serde_json::from_slice::(&packet.data) else { - let ack = AcknowledgementStatus::error(NftTransferError::PacketDataDeserialization.into()); + let ack = + AcknowledgementStatus::error(NftTransferError::FailedToDeserializePacketData.into()); return (ModuleExtras::empty(), ack.into()); }; @@ -204,10 +197,10 @@ pub fn on_acknowledgement_packet_validate( _relayer: &Signer, ) -> Result<(), NftTransferError> { let data = serde_json::from_slice::(&packet.data) - .map_err(|_| NftTransferError::PacketDataDeserialization)?; + .map_err(|_| NftTransferError::FailedToDeserializePacketData)?; let acknowledgement = serde_json::from_slice::(acknowledgement.as_ref()) - .map_err(|_| NftTransferError::AckDeserialization)?; + .map_err(|_| NftTransferError::FailedToDeserializeAck)?; if !acknowledgement.is_successful() { refund_packet_nft_validate(ctx, packet, &data)?; @@ -225,7 +218,7 @@ pub fn on_acknowledgement_packet_execute( let Ok(data) = serde_json::from_slice::(&packet.data) else { return ( ModuleExtras::empty(), - Err(NftTransferError::PacketDataDeserialization), + Err(NftTransferError::FailedToDeserializePacketData), ); }; @@ -234,7 +227,7 @@ pub fn on_acknowledgement_packet_execute( else { return ( ModuleExtras::empty(), - Err(NftTransferError::AckDeserialization), + Err(NftTransferError::FailedToDeserializeAck), ); }; @@ -267,7 +260,7 @@ pub fn on_timeout_packet_validate( _relayer: &Signer, ) -> Result<(), NftTransferError> { let data = serde_json::from_slice::(&packet.data) - .map_err(|_| NftTransferError::PacketDataDeserialization)?; + .map_err(|_| NftTransferError::FailedToDeserializePacketData)?; refund_packet_nft_validate(ctx, packet, &data)?; @@ -282,7 +275,7 @@ pub fn on_timeout_packet_execute( let Ok(data) = serde_json::from_slice::(&packet.data) else { return ( ModuleExtras::empty(), - Err(NftTransferError::PacketDataDeserialization), + Err(NftTransferError::FailedToDeserializePacketData), ); }; @@ -321,7 +314,7 @@ mod test { r#"{"result":"AQ=="}"#, ); ser_json_assert_eq( - AcknowledgementStatus::error(NftTransferError::PacketDataDeserialization.into()), + AcknowledgementStatus::error(NftTransferError::FailedToDeserializePacketData.into()), r#"{"error":"failed to deserialize packet data"}"#, ); } @@ -339,7 +332,8 @@ mod test { #[test] fn test_ack_error_to_vec() { let ack_error: Vec = - AcknowledgementStatus::error(NftTransferError::PacketDataDeserialization.into()).into(); + AcknowledgementStatus::error(NftTransferError::FailedToDeserializePacketData.into()) + .into(); // Check that it's the same output as ibc-go // Note: this also implicitly checks that the ack bytes are non-empty, @@ -363,7 +357,7 @@ mod test { ); de_json_assert_eq( r#"{"error":"failed to deserialize packet data"}"#, - AcknowledgementStatus::error(NftTransferError::PacketDataDeserialization.into()), + AcknowledgementStatus::error(NftTransferError::FailedToDeserializePacketData.into()), ); assert!(serde_json::from_str::(r#"{"success":"AQ=="}"#).is_err()); diff --git a/ibc-apps/ics721-nft-transfer/types/src/class.rs b/ibc-apps/ics721-nft-transfer/types/src/class.rs index 831883331..d554be89b 100644 --- a/ibc-apps/ics721-nft-transfer/types/src/class.rs +++ b/ibc-apps/ics721-nft-transfer/types/src/class.rs @@ -4,6 +4,7 @@ use core::str::FromStr; use http::Uri; pub use ibc_app_transfer_types::{TracePath, TracePrefix}; +use ibc_core::host::types::error::DecodingError; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; #[cfg(feature = "serde")] @@ -11,7 +12,6 @@ use ibc_core::primitives::serializers; use ibc_proto::ibc::applications::nft_transfer::v1::ClassTrace as RawClassTrace; use crate::data::Data; -use crate::error::NftTransferError; /// Class ID for an NFT #[cfg_attr( @@ -44,11 +44,11 @@ impl Display for ClassId { } impl FromStr for ClassId { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(class_id: &str) -> Result { if class_id.trim().is_empty() { - Err(NftTransferError::EmptyBaseClassId) + Err(DecodingError::missing_raw_data("empty base class ID")) } else { Ok(Self(class_id.to_string())) } @@ -119,7 +119,7 @@ pub fn is_receiver_chain_source( } impl FromStr for PrefixedClassId { - type Err = NftTransferError; + type Err = DecodingError; /// The parsing logic is same as [`FromStr`] impl of /// [`PrefixedDenom`](ibc_app_transfer_types::PrefixedDenom) from ICS-20. @@ -138,13 +138,12 @@ impl FromStr for PrefixedClassId { } impl TryFrom for PrefixedClassId { - type Error = NftTransferError; + type Error = DecodingError; fn try_from(value: RawClassTrace) -> Result { let base_class_id = ClassId::from_str(&value.base_class_id)?; - // FIXME: separate `TracePath` error. - let trace_path = TracePath::from_str(&value.path) - .map_err(|err| NftTransferError::Other(err.to_string()))?; + let trace_path = TracePath::from_str(&value.path)?; + Ok(Self { trace_path, base_class_id, @@ -243,15 +242,12 @@ impl Display for ClassUri { } impl FromStr for ClassUri { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(class_uri: &str) -> Result { match Uri::from_str(class_uri) { Ok(uri) => Ok(Self(uri)), - Err(err) => Err(NftTransferError::InvalidUri { - uri: class_uri.to_string(), - validation_error: err, - }), + Err(err) => Err(DecodingError::invalid_raw_data(format!("class URI: {err}"))), } } } @@ -281,7 +277,7 @@ impl Display for ClassData { } impl FromStr for ClassData { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(class_data: &str) -> Result { // validate the data @@ -339,7 +335,7 @@ mod tests { } #[test] - fn test_class_id_trace() -> Result<(), NftTransferError> { + fn test_class_id_trace() -> Result<(), DecodingError> { assert_eq!( PrefixedClassId::from_str("transfer/channel-0/myclass")?, PrefixedClassId { @@ -363,7 +359,7 @@ mod tests { } #[test] - fn test_class_id_serde() -> Result<(), NftTransferError> { + fn test_class_id_serde() -> Result<(), DecodingError> { let dt_str = "transfer/channel-0/myclass"; let dt = PrefixedClassId::from_str(dt_str)?; assert_eq!(dt.to_string(), dt_str, "valid single trace info"); @@ -376,7 +372,7 @@ mod tests { } #[test] - fn test_trace_path() -> Result<(), NftTransferError> { + fn test_trace_path() -> Result<(), DecodingError> { assert!(TracePath::from_str("").is_ok(), "empty trace path"); assert!( TracePath::from_str("transfer/myclass").is_err(), diff --git a/ibc-apps/ics721-nft-transfer/types/src/data.rs b/ibc-apps/ics721-nft-transfer/types/src/data.rs index a9aecc509..5151203f3 100644 --- a/ibc-apps/ics721-nft-transfer/types/src/data.rs +++ b/ibc-apps/ics721-nft-transfer/types/src/data.rs @@ -6,11 +6,10 @@ use core::str::FromStr; use base64::prelude::BASE64_STANDARD; #[cfg(feature = "serde")] use base64::Engine; +use ibc_core::host::types::error::DecodingError; use ibc_core::primitives::prelude::*; use mime::Mime; -use crate::error::NftTransferError; - #[cfg_attr( feature = "parity-scale-codec", derive( @@ -30,7 +29,7 @@ pub struct Data(String); #[cfg(feature = "serde")] impl Data { /// Parses the data in the format specified by ICS-721. - pub fn parse_as_ics721_data(&self) -> Result { + pub fn parse_as_ics721_data(&self) -> Result { self.0.parse::() } } @@ -42,7 +41,7 @@ impl Display for Data { } impl FromStr for Data { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(s: &str) -> Result { Ok(Self(s.to_string())) @@ -94,10 +93,12 @@ pub struct Ics721Data(BTreeMap); #[cfg(feature = "serde")] impl FromStr for Ics721Data { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(s: &str) -> Result { - serde_json::from_str(s).map_err(|_| NftTransferError::InvalidIcs721Data) + serde_json::from_str(s).map_err(|e| DecodingError::InvalidJson { + description: e.to_string(), + }) } } diff --git a/ibc-apps/ics721-nft-transfer/types/src/error.rs b/ibc-apps/ics721-nft-transfer/types/src/error.rs index 94c230add..9335f4c2f 100644 --- a/ibc-apps/ics721-nft-transfer/types/src/error.rs +++ b/ibc-apps/ics721-nft-transfer/types/src/error.rs @@ -1,138 +1,56 @@ //! Defines the Non-Fungible Token Transfer (ICS-721) error types. -use core::convert::Infallible; -use core::str::Utf8Error; - +use derive_more::From; use displaydoc::Display; use ibc_core::channel::types::acknowledgement::StatusValue; use ibc_core::channel::types::channel::Order; -use ibc_core::handler::types::error::ContextError; -use ibc_core::host::types::error::IdentifierError; +use ibc_core::channel::types::error::ChannelError; +use ibc_core::host::types::error::{DecodingError, HostError}; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; -#[derive(Display, Debug)] +#[derive(Display, Debug, From)] pub enum NftTransferError { - /// context error: `{0}` - ContextError(ContextError), - /// invalid identifier: `{0}` - InvalidIdentifier(IdentifierError), - /// invalid URI: `{uri}`, validation error: `{validation_error}`` - InvalidUri { - uri: String, - validation_error: http::uri::InvalidUri, - }, - /// destination channel not found in the counterparty of port_id `{port_id}` and channel_id `{channel_id}` - DestinationChannelNotFound { + /// host error: {0} + Host(HostError), + /// channel error: {0} + Channel(ChannelError), + /// decoding error: {0} + Decoding(DecodingError), + /// missing destination channel `{channel_id}` on port `{port_id}` + MissingDestinationChannel { port_id: PortId, channel_id: ChannelId, }, - /// base class ID is empty - EmptyBaseClassId, - /// invalid prot id n trace at position: `{pos}`, validation error: `{validation_error}` - InvalidTracePortId { - pos: u64, - validation_error: IdentifierError, - }, - /// invalid channel id in trace at position: `{pos}`, validation error: `{validation_error}` - InvalidTraceChannelId { - pos: u64, - validation_error: IdentifierError, - }, - /// trace length must be even but got: `{len}` - InvalidTraceLength { len: u64 }, - /// no token ID - NoTokenId, - /// invalid token ID - InvalidTokenId, - /// duplicated token IDs - DuplicatedTokenIds, - /// The length of token IDs mismatched that of token URIs or token data - TokenMismatched, - /// invalid json data - InvalidJsonData, - /// the data is not in the JSON format specified by ICS-721 - InvalidIcs721Data, - /// expected `{expect_order}` channel, got `{got_order}` - ChannelNotUnordered { - expect_order: Order, - got_order: Order, - }, - /// channel cannot be closed - CantCloseChannel, - /// `{sender}` doesn't own the NFT - InvalidOwner { sender: String }, - /// owner is not found - OwnerNotFound, - /// nft is not found - NftNotFound, - /// nft class is not found - NftClassNotFound, + /// missing token ID + MissingTokenId, + /// mismatched number of token IDs: expected `{expected}`, actual `{actual}` + MismatchedNumberOfTokenIds { expected: usize, actual: usize }, + /// mismatched channel orders: expected `{expected}`, actual `{actual}` + MismatchedChannelOrders { expected: Order, actual: Order }, + /// mismatched port IDs: expected `{expected}`, actual `{actual}` + MismatchedPortIds { expected: PortId, actual: PortId }, /// failed to deserialize packet data - PacketDataDeserialization, + FailedToDeserializePacketData, /// failed to deserialize acknowledgement - AckDeserialization, - /// receive is not enabled - ReceiveDisabled { reason: String }, - /// send is not enabled - SendDisabled { reason: String }, - /// 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: `{reason}` - DecodeRawMsg { reason: String }, - /// unknown msg type: `{msg_type}` - UnknownMsgType { msg_type: String }, - /// decoding raw bytes as UTF-8 string error: `{0}` - Utf8Decode(Utf8Error), - /// other error: `{0}` - Other(String), + FailedToDeserializeAck, + /// failed to parse account ID + FailedToParseAccount, + /// invalid channel state: cannot be closed + InvalidClosedChannel, } #[cfg(feature = "std")] impl std::error::Error for NftTransferError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::ContextError(e) => Some(e), - Self::InvalidUri { - validation_error: e, - .. - } => Some(e), - Self::InvalidIdentifier(e) - | Self::InvalidTracePortId { - validation_error: e, - .. - } - | Self::InvalidTraceChannelId { - validation_error: e, - .. - } => Some(e), + Self::Channel(e) => Some(e), + Self::Host(e) => Some(e), + Self::Decoding(e) => Some(e), _ => None, } } } -impl From for NftTransferError { - fn from(e: Infallible) -> Self { - match e {} - } -} - -impl From for NftTransferError { - fn from(err: ContextError) -> NftTransferError { - Self::ContextError(err) - } -} - -impl From for NftTransferError { - fn from(err: IdentifierError) -> NftTransferError { - Self::InvalidIdentifier(err) - } -} - impl From for StatusValue { fn from(err: NftTransferError) -> Self { StatusValue::new(err.to_string()).expect("error message must not be empty") diff --git a/ibc-apps/ics721-nft-transfer/types/src/msgs/transfer.rs b/ibc-apps/ics721-nft-transfer/types/src/msgs/transfer.rs index b35a5c1cc..97fa5b415 100644 --- a/ibc-apps/ics721-nft-transfer/types/src/msgs/transfer.rs +++ b/ibc-apps/ics721-nft-transfer/types/src/msgs/transfer.rs @@ -1,15 +1,13 @@ //! Defines the Non-Fungible Token Transfer message type -use ibc_core::channel::types::error::PacketError; use ibc_core::channel::types::timeout::{TimeoutHeight, TimeoutTimestamp}; -use ibc_core::handler::types::error::ContextError; +use ibc_core::host::types::error::DecodingError; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::applications::nft_transfer::v1::MsgTransfer as RawMsgTransfer; use ibc_proto::Protobuf; -use crate::error::NftTransferError; use crate::packet::PacketData; pub(crate) const TYPE_URL: &str = "/ibc.applications.nft_transfer.v1.MsgTransfer"; @@ -48,19 +46,17 @@ pub struct MsgTransfer { } impl TryFrom for MsgTransfer { - type Error = NftTransferError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgTransfer) -> Result { - let timeout_height_on_b: TimeoutHeight = raw_msg - .timeout_height - .try_into() - .map_err(ContextError::from)?; - + let timeout_height_on_b: TimeoutHeight = raw_msg.timeout_height.try_into()?; let timeout_timestamp_on_b: TimeoutTimestamp = raw_msg.timeout_timestamp.into(); // Packet timeout height and packet timeout timestamp cannot both be unset. if !timeout_height_on_b.is_set() && !timeout_timestamp_on_b.is_set() { - return Err(ContextError::from(PacketError::MissingTimeout))?; + return Err(DecodingError::missing_raw_data( + "missing timeout height or timeout timestamp", + )); } let memo = if raw_msg.memo.is_empty() { @@ -118,18 +114,16 @@ impl From for RawMsgTransfer { impl Protobuf for MsgTransfer {} impl TryFrom for MsgTransfer { - type Error = NftTransferError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - match raw.type_url.as_str() { - TYPE_URL => { - MsgTransfer::decode_vec(&raw.value).map_err(|e| NftTransferError::DecodeRawMsg { - reason: e.to_string(), - }) - } - _ => Err(NftTransferError::UnknownMsgType { - msg_type: raw.type_url, - }), + if let TYPE_URL = raw.type_url.as_str() { + MsgTransfer::decode_vec(&raw.value).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-apps/ics721-nft-transfer/types/src/packet.rs b/ibc-apps/ics721-nft-transfer/types/src/packet.rs index c2f9d2d2f..afa773bfc 100644 --- a/ibc-apps/ics721-nft-transfer/types/src/packet.rs +++ b/ibc-apps/ics721-nft-transfer/types/src/packet.rs @@ -2,6 +2,7 @@ use base64::prelude::BASE64_STANDARD; use base64::Engine; +use ibc_core::host::types::error::DecodingError; use ibc_core::primitives::prelude::*; #[cfg(feature = "serde")] use ibc_core::primitives::serializers; @@ -91,7 +92,7 @@ impl PacketData { /// Performs the basic validation of the packet data fields. pub fn validate_basic(&self) -> Result<(), NftTransferError> { if self.token_ids.0.is_empty() { - return Err(NftTransferError::NoTokenId); + return Err(NftTransferError::MissingTokenId); } let num = self.token_ids.0.len(); let num_uri = self @@ -105,14 +106,17 @@ impl PacketData { .map(|t| t.len()) .unwrap_or_default(); if (num_uri != 0 && num_uri != num) || (num_data != 0 && num_data != num) { - return Err(NftTransferError::TokenMismatched); + return Err(NftTransferError::MismatchedNumberOfTokenIds { + actual: num, + expected: num_uri, + }); } Ok(()) } } impl TryFrom for PacketData { - type Error = NftTransferError; + type Error = DecodingError; fn try_from(raw_pkt_data: RawPacketData) -> Result { let class_uri = if raw_pkt_data.class_uri.is_empty() { @@ -123,11 +127,8 @@ impl TryFrom for PacketData { let class_data = if raw_pkt_data.class_data.is_empty() { None } else { - let decoded = BASE64_STANDARD - .decode(raw_pkt_data.class_data) - .map_err(|_| NftTransferError::InvalidJsonData)?; - let data_str = - String::from_utf8(decoded).map_err(|_| NftTransferError::InvalidJsonData)?; + let decoded = BASE64_STANDARD.decode(raw_pkt_data.class_data)?; + let data_str = String::from_utf8(decoded)?; Some(data_str.parse()?) }; @@ -138,11 +139,8 @@ impl TryFrom for PacketData { .token_data .iter() .map(|data| { - let decoded = BASE64_STANDARD - .decode(data) - .map_err(|_| NftTransferError::InvalidJsonData)?; - let data_str = - String::from_utf8(decoded).map_err(|_| NftTransferError::InvalidJsonData)?; + let decoded = BASE64_STANDARD.decode(data)?; + let data_str = String::from_utf8(decoded)?; data_str.parse() }) .collect(); @@ -157,6 +155,7 @@ impl TryFrom for PacketData { raw_pkt_data.receiver.into(), raw_pkt_data.memo.into(), ) + .map_err(DecodingError::invalid_raw_data) } } diff --git a/ibc-apps/ics721-nft-transfer/types/src/token.rs b/ibc-apps/ics721-nft-transfer/types/src/token.rs index 1571eac48..b865ed126 100644 --- a/ibc-apps/ics721-nft-transfer/types/src/token.rs +++ b/ibc-apps/ics721-nft-transfer/types/src/token.rs @@ -3,12 +3,12 @@ use core::fmt::{self, Display}; use core::str::FromStr; use http::Uri; +use ibc_core::host::types::error::DecodingError; use ibc_core::primitives::prelude::*; #[cfg(feature = "serde")] use ibc_core::primitives::serializers; use crate::data::Data; -use crate::error::NftTransferError; /// Token ID for an NFT #[cfg_attr( @@ -41,11 +41,11 @@ impl Display for TokenId { } impl FromStr for TokenId { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(token_id: &str) -> Result { if token_id.trim().is_empty() { - Err(NftTransferError::InvalidTokenId) + Err(DecodingError::missing_raw_data("empty token ID")) } else { Ok(Self(token_id.to_string())) } @@ -90,19 +90,27 @@ impl Display for TokenIds { } impl TryFrom> for TokenIds { - type Error = NftTransferError; + type Error = DecodingError; fn try_from(token_ids: Vec) -> Result { if token_ids.is_empty() { - return Err(NftTransferError::NoTokenId); + return Err(DecodingError::missing_raw_data("empty token IDs")); } + let ids: Result, _> = token_ids.iter().map(|t| t.parse()).collect(); let mut ids = ids?; + ids.sort(); ids.dedup(); + if ids.len() != token_ids.len() { - return Err(NftTransferError::DuplicatedTokenIds); + return Err(DecodingError::invalid_raw_data(format!( + "mismatched number of token IDs: expected {}, actual {}", + token_ids.len(), + ids.len() + ))); } + Ok(Self(ids)) } } @@ -170,16 +178,11 @@ impl Display for TokenUri { } impl FromStr for TokenUri { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(token_uri: &str) -> Result { - match Uri::from_str(token_uri) { - Ok(uri) => Ok(Self(uri)), - Err(err) => Err(NftTransferError::InvalidUri { - uri: token_uri.to_string(), - validation_error: err, - }), - } + let token_uri = Uri::from_str(token_uri).map_err(DecodingError::invalid_raw_data)?; + Ok(Self(token_uri)) } } @@ -208,7 +211,7 @@ impl Display for TokenData { } impl FromStr for TokenData { - type Err = NftTransferError; + type Err = DecodingError; fn from_str(token_data: &str) -> Result { let data = Data::from_str(token_data)?; diff --git a/ibc-clients/ics07-tendermint/src/client_state.rs b/ibc-clients/ics07-tendermint/src/client_state.rs index 0adb68f22..6319e309c 100644 --- a/ibc-clients/ics07-tendermint/src/client_state.rs +++ b/ibc-clients/ics07-tendermint/src/client_state.rs @@ -8,10 +8,9 @@ //! Rust). As such, this module also includes some trait implementations that //! serve to pass through traits implemented on the wrapped `ClientState` type. -use ibc_client_tendermint_types::error::Error; use ibc_client_tendermint_types::proto::v1::ClientState as RawTmClientState; use ibc_client_tendermint_types::ClientState as ClientStateType; -use ibc_core_client::types::error::ClientError; +use ibc_core_host::types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; @@ -44,7 +43,7 @@ impl ClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = Error; + type Error = DecodingError; fn try_from(raw: RawTmClientState) -> Result { Ok(Self(ClientStateType::try_from(raw)?)) @@ -60,7 +59,7 @@ impl From for RawTmClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { Ok(Self(ClientStateType::try_from(raw)?)) diff --git a/ibc-clients/ics07-tendermint/src/client_state/common.rs b/ibc-clients/ics07-tendermint/src/client_state/common.rs index ef5b04469..4623df3ac 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/common.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/common.rs @@ -66,10 +66,7 @@ impl ClientStateCommon for ClientState { let upgrade_path = &self.inner().upgrade_path; let (upgrade_path_prefix, upgrade_path) = match upgrade_path.len() { 0 => { - return Err(UpgradeClientError::InvalidUpgradePath { - reason: "no upgrade path has been set".to_string(), - } - .into()); + return Err(UpgradeClientError::MissingUpgradePath.into()); } 1 => (CommitmentPrefix::empty(), upgrade_path[0].clone()), 2 => ( @@ -78,7 +75,7 @@ impl ClientStateCommon for ClientState { ), _ => { return Err(UpgradeClientError::InvalidUpgradePath { - reason: "upgrade path is too long".to_string(), + description: "upgrade path is too long".to_string(), } .into()); } @@ -162,15 +159,11 @@ pub fn verify_consensus_state( let tm_consensus_state = TmConsensusState::try_from(consensus_state)?; if tm_consensus_state.root().is_empty() { - return Err(ClientError::Other { - description: "empty commitment root".into(), - }); + Err(CommitmentError::MissingCommitmentRoot)?; }; if consensus_state_status(&tm_consensus_state, host_timestamp, trusting_period)?.is_expired() { - return Err(ClientError::ClientNotActive { - status: Status::Expired, - }); + return Err(ClientError::InvalidStatus(Status::Expired)); } Ok(()) @@ -213,9 +206,9 @@ pub fn validate_proof_height( let latest_height = client_state.latest_height; if latest_height < proof_height { - return Err(ClientError::InvalidProofHeight { - latest_height, - proof_height, + return Err(ClientError::InsufficientProofHeight { + actual: latest_height, + expected: proof_height, }); } @@ -258,7 +251,7 @@ pub fn verify_upgrade_client( // the upgrade height This condition checks both the revision number and // the height if latest_height >= upgraded_tm_client_state_height { - Err(UpgradeClientError::LowUpgradeHeight { + Err(UpgradeClientError::InsufficientUpgradeHeight { upgraded_height: upgraded_tm_client_state_height, client_height: latest_height, })? @@ -301,17 +294,16 @@ pub fn verify_membership( value: Vec, ) -> Result<(), ClientError> { if prefix.is_empty() { - return Err(ClientError::Ics23Verification( - CommitmentError::EmptyCommitmentPrefix, - )); + Err(CommitmentError::MissingCommitmentPrefix)?; } let merkle_path = MerklePath::new(vec![prefix.as_bytes().to_vec().into(), path]); - let merkle_proof = MerkleProof::try_from(proof).map_err(ClientError::InvalidCommitmentProof)?; - merkle_proof - .verify_membership::(proof_specs, root.clone().into(), merkle_path, value, 0) - .map_err(ClientError::Ics23Verification) + let merkle_proof = MerkleProof::try_from(proof)?; + + merkle_proof.verify_membership::(proof_specs, root.clone().into(), merkle_path, value, 0)?; + + Ok(()) } /// Verify that the given value does not belong in the client's merkle proof. @@ -327,9 +319,10 @@ pub fn verify_non_membership( path: PathBytes, ) -> Result<(), ClientError> { let merkle_path = MerklePath::new(vec![prefix.as_bytes().to_vec().into(), path]); - let merkle_proof = MerkleProof::try_from(proof).map_err(ClientError::InvalidCommitmentProof)?; - merkle_proof - .verify_non_membership::(proof_specs, root.clone().into(), merkle_path) - .map_err(ClientError::Ics23Verification) + let merkle_proof = MerkleProof::try_from(proof)?; + + merkle_proof.verify_non_membership::(proof_specs, root.clone().into(), merkle_path)?; + + Ok(()) } diff --git a/ibc-clients/ics07-tendermint/src/client_state/execution.rs b/ibc-clients/ics07-tendermint/src/client_state/execution.rs index 084d0441c..d4aafbf1b 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/execution.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/execution.rs @@ -8,6 +8,7 @@ use ibc_core_host::types::identifiers::ClientId; use ibc_core_host::types::path::{ClientConsensusStatePath, ClientStatePath}; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; +use ibc_primitives::TimestampError; use super::ClientState; @@ -341,11 +342,7 @@ where let tm_consensus_state_timestamp = tm_consensus_state.timestamp(); let tm_consensus_state_expiry = (tm_consensus_state_timestamp + client_state.trusting_period) - .map_err(|_| ClientError::Other { - description: String::from( - "Timestamp overflow error occurred while attempting to parse TmConsensusState", - ), - })?; + .map_err(|_| TimestampError::OverflowedTimestamp)?; if tm_consensus_state_expiry > host_timestamp { break; diff --git a/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs b/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs index ea40fa717..0d8ce51ce 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs @@ -1,9 +1,10 @@ -use ibc_client_tendermint_types::error::{Error, IntoResult}; +use ibc_client_tendermint_types::error::{IntoResult, TendermintClientError}; use ibc_client_tendermint_types::{ ConsensusState as ConsensusStateType, Header as TmHeader, Misbehaviour as TmMisbehaviour, }; use ibc_core_client::context::{Convertible, ExtClientValidationContext}; use ibc_core_client::types::error::ClientError; +use ibc_core_host::types::error::IdentifierError; use ibc_core_host::types::identifiers::{ChainId, ClientId}; use ibc_core_host::types::path::ClientConsensusStatePath; use ibc_primitives::prelude::*; @@ -101,14 +102,11 @@ where let duration_since_consensus_state = current_timestamp.duration_since(&trusted_timestamp).ok_or( - ClientError::InvalidConsensusStateTimestamp { - time1: trusted_timestamp, - time2: current_timestamp, - }, + ClientError::InvalidConsensusStateTimestamp(trusted_timestamp), )?; if duration_since_consensus_state >= options.trusting_period { - return Err(Error::ConsensusStateTimestampGteTrustingPeriod { + return Err(TendermintClientError::InsufficientTrustingPeriod { duration_since_consensus_state, trusting_period: options.trusting_period, } @@ -119,12 +117,13 @@ where // main header verification, delegated to the tendermint-light-client crate. let untrusted_state = header.as_untrusted_block_state(); - let tm_chain_id = &chain_id - .as_str() - .try_into() - .map_err(|e| ClientError::Other { - description: format!("failed to parse chain id: {e}"), - })?; + let tm_chain_id = + &chain_id + .as_str() + .try_into() + .map_err(|e| IdentifierError::FailedToParse { + description: format!("chain ID `{chain_id}`: {e:?}"), + })?; let trusted_state = header.as_trusted_block_state(tm_chain_id, trusted_time, trusted_next_validator_hash)?; diff --git a/ibc-clients/ics07-tendermint/src/client_state/update_client.rs b/ibc-clients/ics07-tendermint/src/client_state/update_client.rs index 03da355ff..d0f706b30 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/update_client.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/update_client.rs @@ -1,8 +1,9 @@ -use ibc_client_tendermint_types::error::{Error, IntoResult}; +use ibc_client_tendermint_types::error::{IntoResult, TendermintClientError}; use ibc_client_tendermint_types::{ConsensusState as ConsensusStateType, Header as TmHeader}; use ibc_core_client::context::{Convertible, ExtClientValidationContext}; use ibc_core_client::types::error::ClientError; use ibc_core_client::types::Height; +use ibc_core_host::types::error::IdentifierError; use ibc_core_host::types::identifiers::{ChainId, ClientId}; use ibc_core_host::types::path::ClientConsensusStatePath; use ibc_primitives::prelude::*; @@ -52,21 +53,20 @@ where )?; TrustedBlockState { - chain_id: &chain_id - .as_str() - .try_into() - .map_err(|e| ClientError::Other { - description: format!("failed to parse chain id: {}", e), - })?, + chain_id: &chain_id.as_str().try_into().map_err(|e| { + IdentifierError::FailedToParse { + description: format!("chain ID `{chain_id}`: {e:?}"), + } + })?, header_time: trusted_consensus_state.timestamp(), height: header .trusted_height .revision_height() .try_into() - .map_err(|_| ClientError::ClientSpecific { - description: Error::InvalidHeaderHeight { - height: header.trusted_height.revision_height(), - } + .map_err(|_| ClientError::FailedToVerifyHeader { + description: TendermintClientError::InvalidHeaderHeight( + header.trusted_height.revision_height(), + ) .to_string(), })?, next_validators: &header.trusted_next_validator_set, diff --git a/ibc-clients/ics07-tendermint/src/client_state/validation.rs b/ibc-clients/ics07-tendermint/src/client_state/validation.rs index b1a028749..ccb08b5af 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/validation.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/validation.rs @@ -278,5 +278,5 @@ where && subject_proof_specs == &substitute_proof_specs && subject_upgrade_path == &substitute_upgrade_path) .then_some(()) - .ok_or(ClientError::ClientRecoveryStateMismatch) + .ok_or(ClientError::FailedToVerifyClientRecoveryStates) } diff --git a/ibc-clients/ics07-tendermint/src/consensus_state.rs b/ibc-clients/ics07-tendermint/src/consensus_state.rs index 3f64ce085..52f0173ce 100644 --- a/ibc-clients/ics07-tendermint/src/consensus_state.rs +++ b/ibc-clients/ics07-tendermint/src/consensus_state.rs @@ -6,12 +6,11 @@ //! implementations that serve to pass through traits implemented on the wrapped //! `ConsensusState` type. -use ibc_client_tendermint_types::error::Error; use ibc_client_tendermint_types::proto::v1::ConsensusState as RawTmConsensusState; use ibc_client_tendermint_types::ConsensusState as ConsensusStateType; use ibc_core_client::context::consensus_state::ConsensusState as ConsensusStateTrait; -use ibc_core_client::types::error::ClientError; use ibc_core_commitment_types::commitment::CommitmentRoot; +use ibc_core_host::types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; use ibc_primitives::Timestamp; @@ -52,7 +51,7 @@ impl From for ConsensusStateType { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = Error; + type Error = DecodingError; fn try_from(raw: RawTmConsensusState) -> Result { Ok(Self(ConsensusStateType::try_from(raw)?)) @@ -68,7 +67,7 @@ impl From for RawTmConsensusState { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { Ok(Self(ConsensusStateType::try_from(raw)?)) diff --git a/ibc-clients/ics07-tendermint/types/src/client_state.rs b/ibc-clients/ics07-tendermint/types/src/client_state.rs index ee30dbdb0..2ad610d9d 100644 --- a/ibc-clients/ics07-tendermint/types/src/client_state.rs +++ b/ibc-clients/ics07-tendermint/types/src/client_state.rs @@ -4,10 +4,10 @@ use core::cmp::max; use core::str::FromStr; use core::time::Duration; -use ibc_core_client_types::error::ClientError; use ibc_core_client_types::proto::v1::Height as RawHeight; use ibc_core_client_types::Height; use ibc_core_commitment_types::specs::ProofSpecs; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ChainId; use ibc_primitives::prelude::*; use ibc_primitives::ZERO_DURATION; @@ -18,7 +18,7 @@ use tendermint::chain::id::MAX_LENGTH as MaxChainIdLen; use tendermint::trust_threshold::TrustThresholdFraction as TendermintTrustThresholdFraction; use tendermint_light_client_verifier::options::Options; -use crate::error::Error; +use crate::error::TendermintClientError; use crate::header::Header as TmHeader; use crate::trust_threshold::TrustThreshold; @@ -88,7 +88,7 @@ impl ClientState { proof_specs: ProofSpecs, upgrade_path: Vec, allow_update: AllowUpdate, - ) -> Result { + ) -> Result { let client_state = Self::new_without_validation( chain_id, trust_level, @@ -105,7 +105,7 @@ impl ClientState { Ok(client_state) } - pub fn with_header(self, header: TmHeader) -> Result { + pub fn with_header(self, header: TmHeader) -> Result { Ok(Self { latest_height: max(header.height(), self.latest_height), ..self @@ -119,14 +119,14 @@ impl ClientState { } } - pub fn validate(&self) -> Result<(), Error> { + pub fn validate(&self) -> Result<(), TendermintClientError> { self.chain_id.validate_length(3, MaxChainIdLen as u64)?; // `TrustThreshold` is guaranteed to be in the range `[0, 1)`, but a `TrustThreshold::ZERO` // value is invalid in this context if self.trust_level == TrustThreshold::ZERO { - return Err(Error::InvalidTrustThreshold { - reason: "ClientState trust-level cannot be zero".to_string(), + return Err(TendermintClientError::InvalidTrustThreshold { + description: "ClientState trust-level cannot be zero".to_string(), }); } @@ -134,12 +134,18 @@ impl ClientState { self.trust_level.numerator(), self.trust_level.denominator(), ) - .map_err(Error::InvalidTendermintTrustThreshold)?; + .map_err(|_| TendermintClientError::InvalidTrustThreshold { + description: format!( + "invalid Tendermint trust threshold: {:?}/{:?}", + self.trust_level.numerator(), + self.trust_level.denominator() + ), + })?; // Basic validation of trusting period and unbonding period: each should be non-zero. if self.trusting_period <= Duration::new(0, 0) { - return Err(Error::InvalidTrustThreshold { - reason: format!( + return Err(TendermintClientError::InvalidTrustThreshold { + description: format!( "ClientState trusting period ({:?}) must be greater than zero", self.trusting_period ), @@ -147,8 +153,8 @@ impl ClientState { } if self.unbonding_period <= Duration::new(0, 0) { - return Err(Error::InvalidTrustThreshold { - reason: format!( + return Err(TendermintClientError::InvalidTrustThreshold { + description: format!( "ClientState unbonding period ({:?}) must be greater than zero", self.unbonding_period ), @@ -156,23 +162,21 @@ impl ClientState { } if self.trusting_period >= self.unbonding_period { - return Err(Error::InvalidTrustThreshold { - reason: format!( + return Err(TendermintClientError::InvalidTrustThreshold { + description: format!( "ClientState trusting period ({:?}) must be smaller than unbonding period ({:?})", self.trusting_period, self.unbonding_period ), }); } if self.max_clock_drift <= Duration::new(0, 0) { - return Err(Error::InvalidMaxClockDrift { - reason: "ClientState max-clock-drift must be greater than zero".to_string(), - }); + return Err(TendermintClientError::InvalidMaxClockDrift); } if self.latest_height.revision_number() != self.chain_id.revision_number() { - return Err(Error::InvalidLatestHeight { - reason: "ClientState latest-height revision number must match chain-id version" - .to_string(), + return Err(TendermintClientError::MismatchedRevisionHeights { + expected: self.chain_id.revision_number(), + actual: self.latest_height.revision_number(), }); } @@ -180,13 +184,9 @@ impl ClientState { self.proof_specs.validate()?; // `upgrade_path` itself may be empty, but if not then each key must be non-empty - for (idx, key) in self.upgrade_path.iter().enumerate() { + for key in self.upgrade_path.iter() { if key.trim().is_empty() { - return Err(Error::Validation { - reason: format!( - "ClientState upgrade-path key at index {idx:?} cannot be empty" - ), - }); + return Err(TendermintClientError::MissingUpgradePathKey); } } @@ -200,13 +200,9 @@ impl ClientState { /// Helper method to produce a [`Options`] struct for use in /// Tendermint-specific light client verification. - pub fn as_light_client_options(&self) -> Result { + pub fn as_light_client_options(&self) -> Result { Ok(Options { - trust_threshold: self.trust_level.try_into().map_err(|e: ClientError| { - Error::InvalidTrustThreshold { - reason: e.to_string(), - } - })?, + trust_threshold: self.trust_level.try_into()?, trusting_period: self.trusting_period, clock_drift: self.max_clock_drift, }) @@ -234,49 +230,64 @@ impl ClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = Error; + type Error = DecodingError; fn try_from(raw: RawTmClientState) -> Result { let chain_id = ChainId::from_str(raw.chain_id.as_str())?; let trust_level = { - let trust_level = raw.trust_level.ok_or(Error::MissingTrustingPeriod)?; - trust_level - .try_into() - .map_err(|e| Error::InvalidTrustThreshold { - reason: format!("{e}"), - })? + let trust_level = raw.trust_level.ok_or(DecodingError::missing_raw_data( + "tm client state trust level", + ))?; + trust_level.try_into()? }; let trusting_period = raw .trusting_period - .ok_or(Error::MissingTrustingPeriod)? + .ok_or(DecodingError::missing_raw_data( + "tm client state trusting period", + ))? .try_into() - .map_err(|_| Error::MissingTrustingPeriod)?; + .map_err(|d| { + DecodingError::invalid_raw_data(format!("tm client state trusting period: {d:?}")) + })?; let unbonding_period = raw .unbonding_period - .ok_or(Error::MissingUnbondingPeriod)? + .ok_or(DecodingError::missing_raw_data( + "tm client state unbonding period", + ))? .try_into() - .map_err(|_| Error::MissingUnbondingPeriod)?; + .map_err(|d| { + DecodingError::invalid_raw_data(format!("tm client state unbonding period: {d:?}")) + })?; let max_clock_drift = raw .max_clock_drift - .ok_or(Error::NegativeMaxClockDrift)? + .ok_or(DecodingError::missing_raw_data( + "tm client state max clock drift", + ))? .try_into() - .map_err(|_| Error::NegativeMaxClockDrift)?; + .map_err(|d| { + DecodingError::invalid_raw_data(format!("tm client state max clock drift: {d:?}")) + })?; let latest_height = raw .latest_height - .ok_or(Error::MissingLatestHeight)? - .try_into() - .map_err(|_| Error::MissingLatestHeight)?; + .ok_or(DecodingError::missing_raw_data( + "tm client state latest height", + ))? + .try_into()?; + + let proof_specs = raw.proof_specs.try_into()?; // NOTE: In `RawClientState`, a `frozen_height` of `0` means "not // frozen". See: // https://github.com/cosmos/ibc-go/blob/8422d0c4c35ef970539466c5bdec1cd27369bab3/modules/light-clients/07-tendermint/types/client_state.go#L74 - let frozen_height = - Height::try_from(raw.frozen_height.ok_or(Error::MissingFrozenHeight)?).ok(); + let frozen_height = Height::try_from(raw.frozen_height.ok_or( + DecodingError::missing_raw_data("tm client state frozen height"), + )?) + .ok(); // We use set this deprecated field just so that we can properly convert // it back in its raw form @@ -293,7 +304,7 @@ impl TryFrom for ClientState { unbonding_period, max_clock_drift, latest_height, - raw.proof_specs.try_into()?, + proof_specs, raw.upgrade_path, frozen_height, allow_update, @@ -333,22 +344,16 @@ impl From for RawTmClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - fn decode_client_state(value: &[u8]) -> Result { - let client_state = - Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(client_state) - } - - match raw.type_url.as_str() { - TENDERMINT_CLIENT_STATE_TYPE_URL => decode_client_state(&raw.value), - _ => Err(ClientError::UnknownClientStateType { - client_state_type: raw.type_url, - }), + if let TENDERMINT_CLIENT_STATE_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TENDERMINT_CLIENT_STATE_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } @@ -514,7 +519,7 @@ mod tests { for test in tests { let p = test.params.clone(); - let cs_result: Result = ClientState::new( + let cs_result: Result = ClientState::new( p.id, p.trust_level, p.trusting_period, diff --git a/ibc-clients/ics07-tendermint/types/src/consensus_state.rs b/ibc-clients/ics07-tendermint/types/src/consensus_state.rs index bc4e675c8..d38fda228 100644 --- a/ibc-clients/ics07-tendermint/types/src/consensus_state.rs +++ b/ibc-clients/ics07-tendermint/types/src/consensus_state.rs @@ -1,7 +1,7 @@ //! Defines Tendermint's `ConsensusState` type -use ibc_core_client_types::error::ClientError; use ibc_core_commitment_types::commitment::CommitmentRoot; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::lightclients::tendermint::v1::ConsensusState as RawConsensusState; @@ -11,7 +11,6 @@ use tendermint::time::Time; use tendermint::Hash; use tendermint_proto::google::protobuf as tpb; -use crate::error::Error; use crate::header::Header; pub const TENDERMINT_CONSENSUS_STATE_TYPE_URL: &str = @@ -47,32 +46,29 @@ impl ConsensusState { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = Error; + type Error = DecodingError; fn try_from(raw: RawConsensusState) -> Result { let proto_root = raw .root - .ok_or(Error::InvalidRawClientState { - reason: "missing commitment root".into(), - })? + .ok_or(DecodingError::missing_raw_data( + "consensus state commitment root", + ))? .hash; - let ibc_proto::google::protobuf::Timestamp { seconds, nanos } = - raw.timestamp.ok_or(Error::InvalidRawClientState { - reason: "missing timestamp".into(), - })?; + let ibc_proto::google::protobuf::Timestamp { seconds, nanos } = raw + .timestamp + .ok_or(DecodingError::missing_raw_data("consensus state timestamp"))?; // 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::InvalidRawClientState { - reason: format!("invalid timestamp: {e}"), - })?; + .map_err(|e| DecodingError::invalid_raw_data(format!("timestamp: {e}")))?; let next_validators_hash = Hash::from_bytes(Algorithm::Sha256, &raw.next_validators_hash) - .map_err(|e| Error::InvalidRawClientState { - reason: e.to_string(), + .map_err(|e| { + DecodingError::invalid_raw_data(format!("next validators hash: {e}")) })?; Ok(Self { @@ -103,22 +99,16 @@ impl From for RawConsensusState { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - fn decode_consensus_state(value: &[u8]) -> Result { - let client_state = - Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(client_state) - } - - match raw.type_url.as_str() { - TENDERMINT_CONSENSUS_STATE_TYPE_URL => decode_consensus_state(&raw.value), - _ => Err(ClientError::UnknownConsensusStateType { - consensus_state_type: raw.type_url, - }), + if let TENDERMINT_CONSENSUS_STATE_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TENDERMINT_CONSENSUS_STATE_TYPE_URL.to_string(), + actual: raw.type_url, + })? } } } diff --git a/ibc-clients/ics07-tendermint/types/src/error.rs b/ibc-clients/ics07-tendermint/types/src/error.rs index 264e398a0..588e39f18 100644 --- a/ibc-clients/ics07-tendermint/types/src/error.rs +++ b/ibc-clients/ics07-tendermint/types/src/error.rs @@ -6,139 +6,108 @@ use displaydoc::Display; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::Height; use ibc_core_commitment_types::error::CommitmentError; -use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::ClientId; +use ibc_core_host_types::error::{DecodingError, IdentifierError}; use ibc_primitives::prelude::*; use ibc_primitives::TimestampError; -use tendermint::{Error as TendermintError, Hash}; +use tendermint::Hash; use tendermint_light_client_verifier::errors::VerificationErrorDetail as LightClientErrorDetail; use tendermint_light_client_verifier::operations::VotingPowerTally; use tendermint_light_client_verifier::Verdict; -/// The main error type +/// The main error type for the Tendermint light client #[derive(Debug, Display)] -pub enum Error { - /// invalid identifier: `{0}` - InvalidIdentifier(IdentifierError), - /// 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 client proof specs: `{0}` +pub enum TendermintClientError { + /// decoding error: {0} + Decoding(DecodingError), + /// invalid client state trust threshold: {description} + InvalidTrustThreshold { description: String }, + /// invalid max clock drift; must be greater than 0 + InvalidMaxClockDrift, + /// invalid client proof specs `{0}` InvalidProofSpec(CommitmentError), - /// invalid raw client state: `{reason}` - InvalidRawClientState { reason: String }, - /// missing validator set - MissingValidatorSet, - /// missing trusted next validator set - MissingTrustedNextValidatorSet, - /// missing trusted height - MissingTrustedHeight, - /// missing trusting period - MissingTrustingPeriod, - /// missing unbonding period - MissingUnbondingPeriod, - /// negative max clock drift - NegativeMaxClockDrift, - /// missing the latest height - MissingLatestHeight, - /// invalid raw header error: `{0}` - InvalidRawHeader(TendermintError), - /// invalid raw misbehaviour: `{reason}` - InvalidRawMisbehaviour { reason: String }, - /// invalid header timestamp: `{0}` - InvalidHeaderTimestamp(TimestampError), - /// header revision height = `{height}` is invalid - InvalidHeaderHeight { height: u64 }, - /// frozen height is missing - MissingFrozenHeight, - /// the header's trusted revision number (`{trusted_revision}`) and the update's revision number (`{header_revision}`) should be the same - MismatchHeightRevisions { - trusted_revision: u64, - header_revision: u64, - }, - /// the given chain-id (`{given}`) does not match the chain-id of the client (`{expected}`) - MismatchHeaderChainId { given: String, expected: String }, - /// not enough trust because insufficient validators overlap: `{reason}` - NotEnoughTrustedValsSigned { reason: VotingPowerTally }, - /// verification failed: `{detail}` - VerificationError { detail: Box }, - /// Processed time or height for the client `{client_id}` at height `{height}` not found - UpdateMetaDataNotFound { client_id: ClientId, height: Height }, - /// The given hash of the validators does not match the given hash in the signed header. Expected: `{signed_header_validators_hash}`, got: `{validators_hash}` - MismatchValidatorsHashes { - validators_hash: Hash, - signed_header_validators_hash: Hash, - }, - /// current timestamp minus the latest consensus state timestamp is greater than or equal to the trusting period (`{duration_since_consensus_state:?}` >= `{trusting_period:?}`) - ConsensusStateTimestampGteTrustingPeriod { + /// invalid timestamp `{0}` + InvalidTimestamp(TimestampError), + /// invalid header height `{0}` + InvalidHeaderHeight(u64), + /// mismatched revision heights: expected `{expected}`, actual `{actual}` + MismatchedRevisionHeights { expected: u64, actual: u64 }, + /// mismatched header chain ids: expected `{expected}`, actual `{actual}` + MismatchedHeaderChainIds { expected: String, actual: String }, + /// mismatched validator hashes: expected `{expected}`, actual `{actual}` + MismatchedValidatorHashes { expected: Hash, actual: Hash }, + /// missing client state upgrade-path key + MissingUpgradePathKey, + /// failed to verify header: {0} + FailedToVerifyHeader(Box), + /// insufficient validator overlap `{0}` + InsufficientValidatorOverlap(VotingPowerTally), + /// insufficient trusting period `{trusting_period:?}`; should be > consensus state timestamp `{duration_since_consensus_state:?}` + InsufficientTrustingPeriod { duration_since_consensus_state: Duration, trusting_period: Duration, }, - /// headers block hashes are equal - MisbehaviourHeadersBlockHashesEqual, - /// headers are not at the same height and are monotonically increasing - MisbehaviourHeadersNotAtSameHeight, + /// insufficient misbehaviour header height: header1 height `{height_1}` should be >= header2 height `{height_2}` + InsufficientMisbehaviourHeaderHeight { height_1: Height, height_2: Height }, } #[cfg(feature = "std")] -impl std::error::Error for Error { +impl std::error::Error for TendermintClientError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::InvalidIdentifier(e) => Some(e), - Self::InvalidHeader { error: e, .. } - | Self::InvalidTendermintTrustThreshold(e) - | Self::InvalidRawHeader(e) => Some(e), + Self::Decoding(e) => Some(e), + Self::InvalidTimestamp(e) => Some(e), + Self::InvalidProofSpec(e) => Some(e), _ => None, } } } -impl From for ClientError { - fn from(e: Error) -> Self { +impl From for ClientError { + fn from(e: TendermintClientError) -> Self { Self::ClientSpecific { description: e.to_string(), } } } -impl From for Error { +impl From for TendermintClientError { fn from(e: IdentifierError) -> Self { - Self::InvalidIdentifier(e) + Self::Decoding(DecodingError::Identifier(e)) } } -impl From for Error { +impl From for TendermintClientError { fn from(e: CommitmentError) -> Self { Self::InvalidProofSpec(e) } } +impl From for TendermintClientError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) + } +} + +impl From for TendermintClientError { + fn from(e: TimestampError) -> Self { + Self::InvalidTimestamp(e) + } +} + pub trait IntoResult { fn into_result(self) -> Result; } -impl IntoResult<(), Error> for Verdict { - fn into_result(self) -> Result<(), Error> { +impl IntoResult<(), TendermintClientError> for Verdict { + fn into_result(self) -> Result<(), TendermintClientError> { match self { Verdict::Success => Ok(()), - Verdict::NotEnoughTrust(reason) => Err(Error::NotEnoughTrustedValsSigned { reason }), - Verdict::Invalid(detail) => Err(Error::VerificationError { - detail: Box::new(detail), - }), + Verdict::NotEnoughTrust(tally) => { + Err(TendermintClientError::InsufficientValidatorOverlap(tally)) + } + Verdict::Invalid(detail) => Err(TendermintClientError::FailedToVerifyHeader(Box::new( + detail, + ))), } } } diff --git a/ibc-clients/ics07-tendermint/types/src/header.rs b/ibc-clients/ics07-tendermint/types/src/header.rs index 6ee8069f0..376e16c78 100644 --- a/ibc-clients/ics07-tendermint/types/src/header.rs +++ b/ibc-clients/ics07-tendermint/types/src/header.rs @@ -5,6 +5,7 @@ use core::str::FromStr; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::Height; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ChainId; use ibc_primitives::prelude::*; use ibc_primitives::Timestamp; @@ -20,7 +21,7 @@ use tendermint::validator::Set as ValidatorSet; use tendermint::{Hash, Time}; use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState}; -use crate::error::Error; +use crate::error::TendermintClientError; pub const TENDERMINT_HEADER_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Header"; @@ -47,12 +48,12 @@ impl Display for Header { } impl Header { - pub fn timestamp(&self) -> Result { + pub fn timestamp(&self) -> Result { self.signed_header .header .time .try_into() - .map_err(Error::InvalidHeaderTimestamp) + .map_err(TendermintClientError::InvalidTimestamp) } pub fn height(&self) -> Height { @@ -78,7 +79,7 @@ impl Header { chain_id: &'a TmChainId, header_time: Time, next_validators_hash: Hash, - ) -> Result, Error> { + ) -> Result, TendermintClientError> { Ok(TrustedBlockState { chain_id, header_time, @@ -86,18 +87,23 @@ impl Header { .trusted_height .revision_height() .try_into() - .map_err(|_| Error::InvalidHeaderHeight { - height: self.trusted_height.revision_height(), + .map_err(|_| { + TendermintClientError::InvalidHeaderHeight( + self.trusted_height.revision_height(), + ) })?, next_validators: &self.trusted_next_validator_set, next_validators_hash, }) } - pub fn verify_chain_id_version_matches_height(&self, chain_id: &ChainId) -> Result<(), Error> { + pub fn verify_chain_id_version_matches_height( + &self, + chain_id: &ChainId, + ) -> Result<(), TendermintClientError> { if self.height().revision_number() != chain_id.revision_number() { - return Err(Error::MismatchHeaderChainId { - given: self.signed_header.header.chain_id.to_string(), + return Err(TendermintClientError::MismatchedHeaderChainIds { + actual: self.signed_header.header.chain_id.to_string(), expected: chain_id.to_string(), }); } @@ -114,20 +120,21 @@ impl Header { if &self.trusted_next_validator_set.hash_with::() == trusted_next_validator_hash { Ok(()) } else { - Err(ClientError::HeaderVerificationFailure { - reason: - "header trusted next validator set hash does not match hash stored on chain" - .to_string(), + Err(ClientError::FailedToVerifyHeader { + description: "trusted next validator set hash does not match hash stored on chain" + .to_string(), }) } } /// Checks if the fields of a given header are consistent with the trusted fields of this header. - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic( + &self, + ) -> Result<(), TendermintClientError> { if self.height().revision_number() != self.trusted_height.revision_number() { - return Err(Error::MismatchHeightRevisions { - trusted_revision: self.trusted_height.revision_number(), - header_revision: self.height().revision_number(), + return Err(TendermintClientError::MismatchedRevisionHeights { + expected: self.trusted_height.revision_number(), + actual: self.height().revision_number(), }); } @@ -136,17 +143,17 @@ impl Header { // based on) must be smaller than height of the new header that we're // installing. if self.trusted_height >= self.height() { - return Err(Error::InvalidHeaderHeight { - height: self.height().revision_height(), - }); + return Err(TendermintClientError::InvalidHeaderHeight( + self.height().revision_height(), + )); } let validators_hash = self.validator_set.hash_with::(); if validators_hash != self.signed_header.header.validators_hash { - return Err(Error::MismatchValidatorsHashes { - signed_header_validators_hash: self.signed_header.header.validators_hash, - validators_hash, + return Err(TendermintClientError::MismatchedValidatorHashes { + expected: self.signed_header.header.validators_hash, + actual: validators_hash, }); } @@ -157,32 +164,33 @@ impl Header { impl Protobuf for Header {} impl TryFrom for Header { - type Error = Error; + type Error = DecodingError; fn try_from(raw: RawHeader) -> Result { let header = Self { signed_header: raw .signed_header - .ok_or(Error::MissingSignedHeader)? + .ok_or(DecodingError::missing_raw_data("signed header"))? .try_into() - .map_err(|e| Error::InvalidHeader { - reason: "signed header conversion".to_string(), - error: e, - })?, + .map_err(|e| DecodingError::invalid_raw_data(format!("signed header: {e:?}")))?, validator_set: raw .validator_set - .ok_or(Error::MissingValidatorSet)? + .ok_or(DecodingError::missing_raw_data("validator set"))? .try_into() - .map_err(Error::InvalidRawHeader)?, + .map_err(|e| DecodingError::invalid_raw_data(format!("validator set: {e:?}")))?, trusted_height: raw .trusted_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(Error::MissingTrustedHeight)?, + .ok_or(DecodingError::missing_raw_data("trusted height"))?, trusted_next_validator_set: raw .trusted_validators - .ok_or(Error::MissingTrustedNextValidatorSet)? + .ok_or(DecodingError::missing_raw_data( + "trusted next validator set", + ))? .try_into() - .map_err(Error::InvalidRawHeader)?, + .map_err(|e| { + DecodingError::invalid_raw_data(format!("trusted next validator set: {e:?}")) + })?, }; Ok(header) @@ -192,20 +200,16 @@ impl TryFrom for Header { impl Protobuf for Header {} impl TryFrom for Header { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - fn decode_header(value: &[u8]) -> Result { - let header = Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(header) - } - match raw.type_url.as_str() { - TENDERMINT_HEADER_TYPE_URL => decode_header(&raw.value), - _ => Err(ClientError::UnknownHeaderType { - header_type: raw.type_url, - }), + if let TENDERMINT_HEADER_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TENDERMINT_HEADER_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs b/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs index c51f1892f..e4a87c5ae 100644 --- a/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs +++ b/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs @@ -1,6 +1,6 @@ //! Defines the misbehaviour type for the tendermint light client -use ibc_core_client_types::error::ClientError; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_proto::google::protobuf::Any; @@ -9,7 +9,7 @@ use ibc_proto::Protobuf; use tendermint::crypto::Sha256; use tendermint::merkle::MerkleHash; -use crate::error::Error; +use crate::error::TendermintClientError; use crate::header::Header; pub const TENDERMINT_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Misbehaviour"; @@ -44,25 +44,27 @@ impl Misbehaviour { &self.header2 } - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic( + &self, + ) -> Result<(), TendermintClientError> { self.header1.validate_basic::()?; self.header2.validate_basic::()?; if self.header1.signed_header.header.chain_id != self.header2.signed_header.header.chain_id { - return Err(Error::InvalidRawMisbehaviour { - reason: "headers must have identical chain_ids".to_owned(), + return Err(TendermintClientError::MismatchedHeaderChainIds { + expected: self.header1.signed_header.header.chain_id.to_string(), + actual: self.header2.signed_header.header.chain_id.to_string(), }); } if self.header1.height() < self.header2.height() { - return Err(Error::InvalidRawMisbehaviour { - reason: format!( - "header1 height is less than header2 height ({} < {})", - self.header1.height(), - self.header2.height() - ), - }); + return Err( + TendermintClientError::InsufficientMisbehaviourHeaderHeight { + height_1: self.header1.height(), + height_2: self.header2.height(), + }, + ); } Ok(()) @@ -72,23 +74,20 @@ impl Misbehaviour { impl Protobuf for Misbehaviour {} impl TryFrom for Misbehaviour { - type Error = Error; + type Error = DecodingError; + #[allow(deprecated)] fn try_from(raw: RawMisbehaviour) -> Result { let client_id = raw.client_id.parse()?; let header1: Header = raw .header_1 - .ok_or_else(|| Error::InvalidRawMisbehaviour { - reason: "missing header1".into(), - })? + .ok_or_else(|| DecodingError::missing_raw_data("misbehaviour header1"))? .try_into()?; let header2: Header = raw .header_2 - .ok_or_else(|| Error::InvalidRawMisbehaviour { - reason: "missing header2".into(), - })? + .ok_or_else(|| DecodingError::missing_raw_data("misbehaviour header2"))? .try_into()?; Ok(Self::new(client_id, header1, header2)) @@ -109,21 +108,16 @@ impl From for RawMisbehaviour { impl Protobuf for Misbehaviour {} impl TryFrom for Misbehaviour { - type Error = ClientError; - - fn try_from(raw: Any) -> Result { - fn decode_misbehaviour(value: &[u8]) -> Result { - let misbehaviour = - Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(misbehaviour) - } - match raw.type_url.as_str() { - TENDERMINT_MISBEHAVIOUR_TYPE_URL => decode_misbehaviour(&raw.value), - _ => Err(ClientError::UnknownMisbehaviourType { - misbehaviour_type: raw.type_url, - }), + type Error = DecodingError; + + fn try_from(raw: Any) -> Result { + if let TENDERMINT_MISBEHAVIOUR_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TENDERMINT_MISBEHAVIOUR_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs b/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs index 830bba74d..bad001302 100644 --- a/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs +++ b/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs @@ -5,6 +5,7 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use ibc_core_client_types::error::ClientError; +use ibc_core_host_types::error::DecodingError; use ibc_proto::ibc::lightclients::tendermint::v1::Fraction; use ibc_proto::Protobuf; use tendermint::trust_threshold::TrustThresholdFraction; @@ -103,15 +104,11 @@ impl From for TrustThreshold { /// Conversion from IBC domain type into /// Tendermint domain type. impl TryFrom for TrustThresholdFraction { - type Error = ClientError; + type Error = DecodingError; fn try_from(t: TrustThreshold) -> Result { - Self::new(t.numerator, t.denominator).map_err(|_| { - ClientError::FailedTrustThresholdConversion { - numerator: t.numerator, - denominator: t.denominator, - } - }) + Self::new(t.numerator, t.denominator) + .map_err(|_| DecodingError::invalid_raw_data("trust threshold")) } } @@ -127,10 +124,11 @@ impl From for Fraction { } impl TryFrom for TrustThreshold { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: Fraction) -> Result { Self::new(value.numerator, value.denominator) + .map_err(|_| DecodingError::invalid_raw_data("trust threshold")) } } diff --git a/ibc-clients/ics08-wasm/types/src/client_state.rs b/ibc-clients/ics08-wasm/types/src/client_state.rs index e9570b320..64a778633 100644 --- a/ibc-clients/ics08-wasm/types/src/client_state.rs +++ b/ibc-clients/ics08-wasm/types/src/client_state.rs @@ -1,11 +1,11 @@ //! Defines the client state type for the ICS-08 Wasm light client. use ibc_core_client::types::Height; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; use ibc_proto::ibc::lightclients::wasm::v1::ClientState as RawClientState; -use crate::error::Error; #[cfg(feature = "serde")] use crate::serializer::Base64; use crate::Bytes; @@ -38,18 +38,15 @@ impl From for RawClientState { } impl TryFrom for ClientState { - type Error = Error; + type Error = DecodingError; fn try_from(raw: RawClientState) -> Result { let latest_height = raw .latest_height - .ok_or(Error::InvalidLatestHeight { - reason: "missing latest height".to_string(), - })? - .try_into() - .map_err(|_| Error::InvalidLatestHeight { - reason: "invalid protobuf latest height".to_string(), - })?; + .ok_or(DecodingError::missing_raw_data( + "client state latest height", + ))? + .try_into()?; Ok(Self { data: raw.data, checksum: raw.checksum, @@ -70,23 +67,16 @@ impl From for Any { } impl TryFrom for ClientState { - type Error = Error; + type Error = DecodingError; fn try_from(any: Any) -> Result { - fn decode_client_state(value: &[u8]) -> Result { - let client_state = - Protobuf::::decode(value).map_err(|e| Error::DecodeError { - reason: e.to_string(), - })?; - - Ok(client_state) - } - - match any.type_url.as_str() { - WASM_CLIENT_STATE_TYPE_URL => decode_client_state(&any.value), - _ => Err(Error::DecodeError { - reason: "type_url does not match".into(), - }), + if let WASM_CLIENT_STATE_TYPE_URL = any.type_url.as_str() { + Protobuf::::decode(any.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: WASM_CLIENT_STATE_TYPE_URL.to_string(), + actual: any.type_url, + }) } } } diff --git a/ibc-clients/ics08-wasm/types/src/consensus_state.rs b/ibc-clients/ics08-wasm/types/src/consensus_state.rs index 597cc4e87..e77f469c7 100644 --- a/ibc-clients/ics08-wasm/types/src/consensus_state.rs +++ b/ibc-clients/ics08-wasm/types/src/consensus_state.rs @@ -1,6 +1,6 @@ //! Defines the consensus state type for the ICS-08 Wasm light client. -use ibc_core_client::types::error::ClientError; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; use ibc_proto::ibc::lightclients::wasm::v1::ConsensusState as RawConsensusState; @@ -35,7 +35,7 @@ impl From for RawConsensusState { } impl TryFrom for ConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: RawConsensusState) -> Result { Ok(Self { data: value.data }) @@ -54,21 +54,16 @@ impl From for Any { } impl TryFrom for ConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(any: Any) -> Result { - fn decode_consensus_state(value: &[u8]) -> Result { - let consensus_state = - Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(consensus_state) - } - match any.type_url.as_str() { - WASM_CONSENSUS_STATE_TYPE_URL => decode_consensus_state(&any.value), - _ => Err(ClientError::Other { - description: "type_url does not match".into(), - }), + if let WASM_CONSENSUS_STATE_TYPE_URL = any.type_url.as_str() { + Protobuf::::decode(any.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: WASM_CONSENSUS_STATE_TYPE_URL.to_string(), + actual: any.type_url, + }) } } } diff --git a/ibc-clients/ics08-wasm/types/src/error.rs b/ibc-clients/ics08-wasm/types/src/error.rs deleted file mode 100644 index eb48a2855..000000000 --- a/ibc-clients/ics08-wasm/types/src/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Defines the error type for the ICS-08 Wasm light client. - -use displaydoc::Display; -use ibc_core_host_types::error::IdentifierError; -use ibc_primitives::prelude::*; - -/// The main error type -#[derive(Debug, Display)] -pub enum Error { - /// invalid identifier: `{0}` - InvalidIdentifier(IdentifierError), - /// decoding error: `{reason}` - DecodeError { reason: String }, - /// invalid client state latest height: `{reason}` - InvalidLatestHeight { reason: String }, -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::InvalidIdentifier(err) => Some(err), - _ => None, - } - } -} - -impl From for Error { - fn from(e: IdentifierError) -> Self { - Self::InvalidIdentifier(e) - } -} diff --git a/ibc-clients/ics08-wasm/types/src/lib.rs b/ibc-clients/ics08-wasm/types/src/lib.rs index 169eb3b30..3a88d0431 100644 --- a/ibc-clients/ics08-wasm/types/src/lib.rs +++ b/ibc-clients/ics08-wasm/types/src/lib.rs @@ -15,7 +15,6 @@ pub mod client_message; pub mod client_state; pub mod consensus_state; -pub mod error; pub mod msgs; #[cfg(feature = "serde")] diff --git a/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs b/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs index 40c737457..3f7bc6a93 100644 --- a/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs +++ b/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs @@ -1,12 +1,12 @@ use core::str::FromStr; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::lightclients::wasm::v1::MsgMigrateContract as RawMsgMigrateContract; use ibc_proto::Protobuf; -use crate::error::Error; use crate::Bytes; pub const MIGRATE_CONTRACT_TYPE_URL: &str = "/ibc.lightclients.wasm.v1.MsgMigrateContract"; @@ -34,7 +34,7 @@ impl From for RawMsgMigrateContract { } impl TryFrom for MsgMigrateContract { - type Error = Error; + type Error = DecodingError; fn try_from(value: RawMsgMigrateContract) -> Result { Ok(Self { diff --git a/ibc-core/ics02-client/context/src/context.rs b/ibc-core/ics02-client/context/src/context.rs index 972ec547e..e9a47bf0d 100644 --- a/ibc-core/ics02-client/context/src/context.rs +++ b/ibc-core/ics02-client/context/src/context.rs @@ -1,5 +1,5 @@ use ibc_core_client_types::Height; -use ibc_core_handler_types::error::ContextError; +use ibc_core_host_types::error::HostError; use ibc_core_host_types::identifiers::ClientId; use ibc_core_host_types::path::{ClientConsensusStatePath, ClientStatePath}; use ibc_primitives::prelude::*; @@ -19,7 +19,7 @@ pub trait ClientValidationContext: Sized { /// Returns the ClientState for the given identifier `client_id`. /// /// Note: Clients have the responsibility to store client states on client creation and update. - fn client_state(&self, client_id: &ClientId) -> Result; + fn client_state(&self, client_id: &ClientId) -> Result; /// Retrieve the consensus state for the given client ID at the specified /// height. @@ -30,7 +30,7 @@ pub trait ClientValidationContext: Sized { fn consensus_state( &self, client_cons_state_path: &ClientConsensusStatePath, - ) -> Result; + ) -> Result; /// Returns the timestamp and height of the host when it processed a client /// update request at the specified height. @@ -38,7 +38,7 @@ pub trait ClientValidationContext: Sized { &self, client_id: &ClientId, height: &Height, - ) -> Result<(Timestamp, Height), ContextError>; + ) -> Result<(Timestamp, Height), HostError>; } /// Defines the methods that all client `ExecutionContext`s (precisely the @@ -54,7 +54,7 @@ pub trait ClientExecutionContext: { type ClientStateMut: ClientStateExecution; - fn client_state_mut(&self, client_id: &ClientId) -> Result { + fn client_state_mut(&self, client_id: &ClientId) -> Result { self.client_state(client_id) } @@ -63,20 +63,20 @@ pub trait ClientExecutionContext: &mut self, client_state_path: ClientStatePath, client_state: Self::ClientStateRef, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Called upon successful client creation and update fn store_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, consensus_state: Self::ConsensusStateRef, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Delete the consensus state from the store located at the given `ClientConsensusStatePath` fn delete_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Called upon successful client update. /// @@ -88,7 +88,7 @@ pub trait ClientExecutionContext: height: Height, host_timestamp: Timestamp, host_height: Height, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Delete the update time and height associated with the client at the /// specified height. @@ -97,11 +97,7 @@ pub trait ClientExecutionContext: /// specified height. /// /// Note that this timestamp is determined by the host. - fn delete_update_meta( - &mut self, - client_id: ClientId, - height: Height, - ) -> Result<(), ContextError>; + fn delete_update_meta(&mut self, client_id: ClientId, height: Height) -> Result<(), HostError>; } /// An optional trait that extends the client validation context capabilities by @@ -115,27 +111,27 @@ pub trait ClientExecutionContext: /// specific light client requirements. pub trait ExtClientValidationContext: ClientValidationContext { /// Returns the current timestamp of the local chain. - fn host_timestamp(&self) -> Result; + fn host_timestamp(&self) -> Result; /// Returns the current height of the local chain. - fn host_height(&self) -> Result; + fn host_height(&self) -> Result; /// Returns all the heights at which a consensus state is stored. - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError>; + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, HostError>; /// Search for the lowest consensus state higher than `height`. fn next_consensus_state( &self, client_id: &ClientId, height: &Height, - ) -> Result, ContextError>; + ) -> Result, HostError>; /// Search for the highest consensus state lower than `height`. fn prev_consensus_state( &self, client_id: &ClientId, height: &Height, - ) -> Result, ContextError>; + ) -> Result, HostError>; } /// An optional trait that extends the client context required during execution. diff --git a/ibc-core/ics02-client/src/handler/create_client.rs b/ibc-core/ics02-client/src/handler/create_client.rs index d6413596f..511f45f2c 100644 --- a/ibc-core/ics02-client/src/handler/create_client.rs +++ b/ibc-core/ics02-client/src/handler/create_client.rs @@ -4,13 +4,13 @@ use ibc_core_client_context::prelude::*; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::events::CreateClient; use ibc_core_client_types::msgs::MsgCreateClient; -use ibc_core_handler_types::error::ContextError; +use ibc_core_client_types::Status; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::{ClientStateMut, ClientStateRef, ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; -pub fn validate(ctx: &Ctx, msg: MsgCreateClient) -> Result<(), ContextError> +pub fn validate(ctx: &Ctx, msg: MsgCreateClient) -> Result<(), ClientError> where Ctx: ValidationContext, as TryFrom>::Error: Into, @@ -35,10 +35,7 @@ where let status = client_state.status(client_val_ctx, &client_id)?; if status.is_frozen() { - return Err(ClientError::ClientFrozen { - description: "the client is frozen".to_string(), - } - .into()); + return Err(ClientError::InvalidStatus(Status::Frozen)); }; let host_timestamp = ctx.host_timestamp()?; @@ -46,13 +43,13 @@ where client_state.verify_consensus_state(consensus_state, &host_timestamp)?; if client_val_ctx.client_state(&client_id).is_ok() { - return Err(ClientError::ClientStateAlreadyExists { client_id }.into()); + return Err(ClientError::DuplicateClientState(client_id)); }; Ok(()) } -pub fn execute(ctx: &mut Ctx, msg: MsgCreateClient) -> Result<(), ContextError> +pub fn execute(ctx: &mut Ctx, msg: MsgCreateClient) -> Result<(), ClientError> where Ctx: ExecutionContext, as TryFrom>::Error: Into, diff --git a/ibc-core/ics02-client/src/handler/recover_client.rs b/ibc-core/ics02-client/src/handler/recover_client.rs index 1676c6e80..a239ed8fc 100644 --- a/ibc-core/ics02-client/src/handler/recover_client.rs +++ b/ibc-core/ics02-client/src/handler/recover_client.rs @@ -3,7 +3,6 @@ use ibc_core_client_context::prelude::*; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::msgs::MsgRecoverClient; -use ibc_core_handler_types::error::ContextError; use ibc_core_host::types::path::ClientConsensusStatePath; use ibc_core_host::{ExecutionContext, ValidationContext}; @@ -11,7 +10,7 @@ use ibc_core_host::{ExecutionContext, ValidationContext}; /// includes validating that the parameters of the subject and substitute clients match, /// as well as validating that the substitute client *is* active and that the subject /// client is *not* active. -pub fn validate(ctx: &Ctx, msg: MsgRecoverClient) -> Result<(), ContextError> +pub fn validate(ctx: &Ctx, msg: MsgRecoverClient) -> Result<(), ClientError> where Ctx: ValidationContext, { @@ -30,11 +29,10 @@ where let substitute_height = substitute_client_state.latest_height(); if subject_height >= substitute_height { - return Err(ClientError::ClientRecoveryHeightMismatch { + return Err(ClientError::InvalidClientRecoveryHeights { subject_height, substitute_height, - } - .into()); + }); } substitute_client_state @@ -62,7 +60,7 @@ where /// - copying the substitute client's consensus state as the subject's consensus state /// - setting the subject client's processed height and processed time values to match the substitute client's /// - setting the subject client's latest height, trusting period, and chain ID values to match the substitute client's -pub fn execute(ctx: &mut Ctx, msg: MsgRecoverClient) -> Result<(), ContextError> +pub fn execute(ctx: &mut Ctx, msg: MsgRecoverClient) -> Result<(), ClientError> where Ctx: ExecutionContext, { diff --git a/ibc-core/ics02-client/src/handler/update_client.rs b/ibc-core/ics02-client/src/handler/update_client.rs index 81e140b8a..51a30c3b1 100644 --- a/ibc-core/ics02-client/src/handler/update_client.rs +++ b/ibc-core/ics02-client/src/handler/update_client.rs @@ -5,13 +5,13 @@ use ibc_core_client_types::error::ClientError; use ibc_core_client_types::events::{ClientMisbehaviour, UpdateClient}; use ibc_core_client_types::msgs::MsgUpdateOrMisbehaviour; use ibc_core_client_types::UpdateKind; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::error::HostError; use ibc_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::ToVec; -pub fn validate(ctx: &Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ContextError> +pub fn validate(ctx: &Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ClientError> where Ctx: ValidationContext, { @@ -35,7 +35,7 @@ where Ok(()) } -pub fn execute(ctx: &mut Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ContextError> +pub fn execute(ctx: &mut Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ClientError> where Ctx: ExecutionContext, { @@ -64,10 +64,9 @@ where ctx.emit_ibc_event(event)?; } else { if !matches!(update_kind, UpdateKind::UpdateClient) { - return Err(ClientError::MisbehaviourHandlingFailure { - reason: "misbehaviour submitted, but none found".to_string(), - } - .into()); + return Err(ClientError::FailedToHandleMisbehaviour { + description: "misbehaviour submitted, but none found".to_string(), + }); } let header = client_message; @@ -77,9 +76,9 @@ where { let event = { - let consensus_height = consensus_heights.first().ok_or(ClientError::Other { - description: "client update state returned no updated height".to_string(), - })?; + let consensus_height = consensus_heights.first().ok_or( + HostError::missing_state("updated height in client update state"), + )?; IbcEvent::UpdateClient(UpdateClient::new( client_id, diff --git a/ibc-core/ics02-client/src/handler/upgrade_client.rs b/ibc-core/ics02-client/src/handler/upgrade_client.rs index a072988a2..ae119faeb 100644 --- a/ibc-core/ics02-client/src/handler/upgrade_client.rs +++ b/ibc-core/ics02-client/src/handler/upgrade_client.rs @@ -4,13 +4,12 @@ use ibc_core_client_context::prelude::*; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::events::UpgradeClient; use ibc_core_client_types::msgs::MsgUpgradeClient; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::ClientConsensusStatePath; use ibc_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; -pub fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), ContextError> +pub fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), ClientError> where Ctx: ValidationContext, { @@ -36,12 +35,7 @@ where old_client_state.latest_height().revision_number(), old_client_state.latest_height().revision_height(), ); - let old_consensus_state = client_val_ctx - .consensus_state(&old_client_cons_state_path) - .map_err(|_| ClientError::ConsensusStateNotFound { - client_id, - height: old_client_state.latest_height(), - })?; + let old_consensus_state = client_val_ctx.consensus_state(&old_client_cons_state_path)?; // Validate the upgraded client state and consensus state and verify proofs against the root old_client_state.verify_upgrade_client( @@ -55,7 +49,7 @@ where Ok(()) } -pub fn execute(ctx: &mut Ctx, msg: MsgUpgradeClient) -> Result<(), ContextError> +pub fn execute(ctx: &mut Ctx, msg: MsgUpgradeClient) -> Result<(), ClientError> where Ctx: ExecutionContext, { diff --git a/ibc-core/ics02-client/types/src/error.rs b/ibc-core/ics02-client/types/src/error.rs index cda8b0f05..bdd4b509f 100644 --- a/ibc-core/ics02-client/types/src/error.rs +++ b/ibc-core/ics02-client/types/src/error.rs @@ -1,125 +1,90 @@ //! Defines the client error type -use core::convert::Infallible; - use displaydoc::Display; use ibc_core_commitment_types::error::CommitmentError; -use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::{ClientId, ClientType}; +use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; -use ibc_primitives::Timestamp; +use ibc_primitives::{Timestamp, TimestampError}; -use super::status::Status; use crate::height::Height; +use crate::Status; /// Encodes all the possible client errors #[derive(Debug, Display)] pub enum ClientError { - /// upgrade client error: `{0}` + /// host error : {0} + Host(HostError), + /// upgrade client error: {0} Upgrade(UpgradeClientError), - /// client is frozen with description: `{description}` - ClientFrozen { description: String }, - /// client is not active. Status=`{status}` - ClientNotActive { status: Status }, - /// client is not frozen or expired. Status=`{status}` - ClientNotInactive { status: Status }, - /// client state not found: `{client_id}` - ClientStateNotFound { client_id: ClientId }, - /// client state already exists: `{client_id}` - ClientStateAlreadyExists { client_id: ClientId }, - /// Substitute client height `{substitute_height}` is not greater than subject client height `{subject_height}` during client recovery - ClientRecoveryHeightMismatch { - subject_height: Height, - substitute_height: Height, - }, - /// Subject and substitute client state mismatch during client recovery - ClientRecoveryStateMismatch, - /// consensus state not found at: `{client_id}` at height `{height}` - ConsensusStateNotFound { client_id: ClientId, height: Height }, - /// Processed time or height for the client `{client_id}` at height `{height}` not found - UpdateMetaDataNotFound { client_id: ClientId, height: Height }, - /// header verification failed with reason: `{reason}` - HeaderVerificationFailure { reason: String }, - /// failed to build trust threshold from fraction: `{numerator}`/`{denominator}` + /// decoding error: {0} + Decoding(DecodingError), + /// timestamp error: {0} + Timestamp(TimestampError), + /// invalid trust threshold `{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 }, - /// unknown client consensus state type: `{consensus_state_type}` - UnknownConsensusStateType { consensus_state_type: String }, - /// unknown header type: `{header_type}` - UnknownHeaderType { header_type: String }, - /// unknown misbehaviour type: `{misbehaviour_type}` - UnknownMisbehaviourType { misbehaviour_type: String }, - /// missing raw client state - MissingRawClientState, - /// missing raw client consensus state - MissingRawConsensusState, - /// invalid client id in the update client message: `{0}` - InvalidMsgUpdateClientId(IdentifierError), - /// invalid client id in recover client message: `{0}` - InvalidMsgRecoverClientId(IdentifierError), - /// invalid client identifier error: `{0}` - InvalidClientIdentifier(IdentifierError), - /// invalid raw header error: `{reason}` - InvalidRawHeader { reason: String }, - /// missing raw client message - MissingClientMessage, - /// invalid raw misbehaviour error: `{0}` - InvalidRawMisbehaviour(IdentifierError), - /// missing raw misbehaviour - MissingRawMisbehaviour, - /// revision height cannot be zero + /// invalid client state type `{0}` + InvalidClientStateType(String), + /// invalid update client message + InvalidUpdateClientMessage, + /// invalid height; cannot be zero or negative InvalidHeight, - /// height cannot end up zero or negative - InvalidHeightResult, - /// the proof height is insufficient: latest_height=`{latest_height}` proof_height=`{proof_height}` - InvalidProofHeight { - latest_height: Height, - proof_height: Height, + /// invalid status `{0}` + InvalidStatus(Status), + /// invalid consensus state timestamp `{0}` + InvalidConsensusStateTimestamp(Timestamp), + /// invalid header type `{0}` + InvalidHeaderType(String), + /// invalid client recovery heights: expected substitute client height `{substitute_height}` > subject client height `{subject_height}` + InvalidClientRecoveryHeights { + subject_height: Height, + substitute_height: Height, }, - /// invalid commitment proof bytes error: `{0}` - InvalidCommitmentProof(CommitmentError), - /// mismatch between client and arguments types - ClientArgsTypeMismatch { client_type: ClientType }, - /// timestamp is invalid or missing, timestamp=`{time1}`, now=`{time2}` - InvalidConsensusStateTimestamp { time1: Timestamp, time2: Timestamp }, - /// the local consensus state could not be retrieved for height `{height}` - MissingLocalConsensusState { height: Height }, - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// ics23 verification failure error: `{0}` - Ics23Verification(CommitmentError), - /// misbehaviour handling failed with reason: `{reason}` - MisbehaviourHandlingFailure { reason: String }, - /// client-specific error: `{description}` + /// insufficient proof height; expected `{actual}` >= `{expected}` + InsufficientProofHeight { expected: Height, actual: Height }, + /// missing local consensus state at `{0}` + MissingLocalConsensusState(Height), + /// duplicate client state `{0}` + DuplicateClientState(ClientId), + /// failed to verify client recovery states + FailedToVerifyClientRecoveryStates, + /// failed ICS23 verification: {0} + FailedICS23Verification(CommitmentError), + /// failed to verify header: {description} + FailedToVerifyHeader { description: String }, + /// failed to handle misbehaviour: {description} + FailedToHandleMisbehaviour { description: String }, + /// client-specific error: {description} ClientSpecific { description: String }, - /// client counter overflow error - CounterOverflow, - /// update client message did not contain valid header or misbehaviour - InvalidUpdateClientMessage, - /// other error: `{description}` - Other { description: String }, - /// invalid attribute key: `{attribute_key}` - InvalidAttributeKey { attribute_key: String }, - /// invalid attribute value: `{attribute_value}` - InvalidAttributeValue { attribute_value: String }, - /// Missing attribute key: `{attribute_key}` - MissingAttributeKey { attribute_key: String }, } -impl From<&'static str> for ClientError { - fn from(s: &'static str) -> Self { - Self::Other { - description: s.to_string(), - } +impl From for ClientError { + fn from(e: CommitmentError) -> Self { + Self::FailedICS23Verification(e) + } +} + +impl From for ClientError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) } } -impl From for ClientError { - fn from(value: Infallible) -> Self { - match value {} +impl From for ClientError { + fn from(e: HostError) -> Self { + Self::Host(e) + } +} + +impl From for ClientError { + fn from(e: IdentifierError) -> Self { + Self::Decoding(DecodingError::Identifier(e)) + } +} + +impl From for ClientError { + fn from(e: TimestampError) -> Self { + Self::Timestamp(e) } } @@ -127,10 +92,11 @@ impl From for ClientError { impl std::error::Error for ClientError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::InvalidMsgUpdateClientId(e) - | Self::InvalidClientIdentifier(e) - | Self::InvalidRawMisbehaviour(e) => Some(e), - Self::InvalidCommitmentProof(e) | Self::Ics23Verification(e) => Some(e), + Self::FailedICS23Verification(e) => Some(e), + Self::Decoding(e) => Some(e), + Self::Upgrade(e) => Some(e), + Self::Host(e) => Some(e), + Self::Timestamp(e) => Some(e), _ => None, } } @@ -139,23 +105,25 @@ impl std::error::Error for ClientError { /// Encodes all the possible upgrade client errors #[derive(Debug, Display)] pub enum UpgradeClientError { - /// invalid proof for the upgraded client state error: `{0}` - InvalidUpgradeClientProof(CommitmentError), - /// invalid proof for the upgraded consensus state error: `{0}` + /// decoding error: `{0}` + Decoding(DecodingError), + /// host chain error: `{0}` + Host(HostError), + /// invalid upgrade proposal: `{description}` + InvalidUpgradeProposal { description: String }, + /// invalid proof for the upgraded client state: `{0}` + InvalidUpgradeClientStateProof(CommitmentError), + /// invalid proof for the upgraded consensus state: `{0}` InvalidUpgradeConsensusStateProof(CommitmentError), - /// upgraded client height `{upgraded_height}` must be at greater than current client height `{client_height}` - LowUpgradeHeight { + /// invalid upgrade path: `{description}` + InvalidUpgradePath { description: String }, + /// missing upgrade path + MissingUpgradePath, + /// insufficient upgrade client height `{upgraded_height}`; must be greater than current client height `{client_height}` + InsufficientUpgradeHeight { upgraded_height: Height, client_height: Height, }, - /// Invalid upgrade path: `{reason}` - InvalidUpgradePath { reason: String }, - /// invalid upgrade proposal: `{reason}` - InvalidUpgradeProposal { reason: String }, - /// invalid upgrade plan: `{reason}` - InvalidUpgradePlan { reason: String }, - /// other upgrade client error: `{reason}` - Other { reason: String }, } impl From for ClientError { @@ -164,13 +132,26 @@ impl From for ClientError { } } +impl From for UpgradeClientError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) + } +} + +impl From for UpgradeClientError { + fn from(e: HostError) -> Self { + Self::Host(e) + } +} + #[cfg(feature = "std")] impl std::error::Error for UpgradeClientError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::InvalidUpgradeClientProof(e) | Self::InvalidUpgradeConsensusStateProof(e) => { - Some(e) - } + Self::Decoding(e) => Some(e), + Self::Host(e) => Some(e), + Self::InvalidUpgradeClientStateProof(e) + | Self::InvalidUpgradeConsensusStateProof(e) => Some(e), _ => None, } } diff --git a/ibc-core/ics02-client/types/src/events.rs b/ibc-core/ics02-client/types/src/events.rs index a91e1b413..744776b88 100644 --- a/ibc-core/ics02-client/types/src/events.rs +++ b/ibc-core/ics02-client/types/src/events.rs @@ -1,13 +1,15 @@ //! Types for the IBC events emitted from Tendermint Websocket by the client module. + use derive_more::From; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ClientId, ClientType}; use ibc_primitives::prelude::*; use subtle_encoding::hex; use tendermint::abci; use self::str::FromStr; -use crate::error::ClientError; use crate::height::Height; + /// Client event types pub const CREATE_CLIENT_EVENT: &str = "create_client"; pub const UPDATE_CLIENT_EVENT: &str = "update_client"; @@ -53,34 +55,27 @@ impl From for abci::EventAttribute { } } impl TryFrom for ClientIdAttribute { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CLIENT_ID_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), + return Err(DecodingError::MismatchedResourceName { + expected: CLIENT_ID_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), }); } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(DecodingError::missing_raw_data("attribute key")); } value .value_str() .map(|value| { - let client_id = - ClientId::from_str(value).map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - })?; - + let client_id = ClientId::from_str(value)?; Ok(ClientIdAttribute { client_id }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), - })? + .map_err(|e| DecodingError::missing_raw_data(format!("attribute value: {e}")))? } } @@ -109,34 +104,28 @@ impl From for abci::EventAttribute { } impl TryFrom for ClientTypeAttribute { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CLIENT_TYPE_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(DecodingError::MismatchedResourceName { + expected: CLIENT_TYPE_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(DecodingError::missing_raw_data("client type attribute key")); } value .value_str() .map(|value| { - let client_type = ClientType::from_str(value).map_err(|_| { - ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - } - })?; - + let client_type = ClientType::from_str(value)?; Ok(ClientTypeAttribute { client_type }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), + .map_err(|e| { + DecodingError::missing_raw_data(format!("client type attribute value: {e}")) })? } } @@ -166,35 +155,34 @@ impl From for abci::EventAttribute { } impl TryFrom for ConsensusHeightAttribute { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CONSENSUS_HEIGHT_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(DecodingError::MismatchedResourceName { + expected: CONSENSUS_HEIGHT_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(DecodingError::missing_raw_data( + "consensus height attribute key", + )); } value .value_str() .map(|value| { - let consensus_height = - Height::from_str(value).map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - })?; + let consensus_height = Height::from_str(value)?; Ok(ConsensusHeightAttribute { consensus_height }) }) - .map_err(|_| ClientError::InvalidAttributeKey { - attribute_key: String::new(), + .map_err(|e| { + DecodingError::missing_raw_data(format!("consensus height attribute value: {e}")) })? } } + #[cfg_attr( feature = "parity-scale-codec", derive( @@ -225,19 +213,20 @@ impl From for abci::EventAttribute { } impl TryFrom for ConsensusHeightsAttribute { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CONSENSUS_HEIGHTS_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(DecodingError::MismatchedResourceName { + expected: CONSENSUS_HEIGHTS_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(DecodingError::missing_raw_data( + "consensus heights attribute key", + )); } value @@ -245,19 +234,14 @@ impl TryFrom for ConsensusHeightsAttribute { .map(|value| { let consensus_heights: Vec = value .split(',') - .map(|height_str| { - Height::from_str(height_str).map_err(|_| { - ClientError::InvalidAttributeValue { - attribute_value: height_str.to_string(), - } - }) - }) - .collect::, ClientError>>()?; + .map(Height::from_str) + .collect::, DecodingError>>( + )?; Ok(ConsensusHeightsAttribute { consensus_heights }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), + .map_err(|e| { + DecodingError::invalid_raw_data(format!("consensus heights attribute value: {e}")) })? } } @@ -287,40 +271,36 @@ impl From for abci::EventAttribute { ( HEADER_ATTRIBUTE_KEY, str::from_utf8(&hex::encode(attr.header)) - .expect("Never fails because hexadecimal is valid UTF-8"), + .expect("never fails because hexadecimal is valid UTF-8"), ) .into() } } impl TryFrom for HeaderAttribute { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != HEADER_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(DecodingError::MismatchedResourceName { + expected: HEADER_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(DecodingError::missing_raw_data("header attribute key")); } value .value_str() .map(|value| { - let header = - hex::decode(value).map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - })?; + let header = hex::decode(value).map_err(|e| { + DecodingError::invalid_raw_data(format!("header attribute value: {e}")) + })?; Ok(HeaderAttribute { header }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), - })? + .map_err(|e| DecodingError::invalid_raw_data(format!("header attribute value: {e}")))? } } @@ -385,13 +365,14 @@ impl From for abci::Event { } impl TryFrom for CreateClient { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::Event) -> Result { if value.kind != CREATE_CLIENT_EVENT { - return Err(ClientError::Other { - description: "Error in parsing CreateClient event".to_string(), - }); + return Err(DecodingError::MismatchedResourceName { + expected: CREATE_CLIENT_EVENT.to_string(), + actual: value.kind, + })?; } value @@ -405,12 +386,9 @@ impl TryFrom for CreateClient { Option, ), attribute| { - let key = - attribute - .key_str() - .map_err(|_| ClientError::InvalidAttributeKey { - attribute_key: String::new(), - })?; + let key = attribute.key_str().map_err(|e| { + DecodingError::missing_raw_data(format!("create client attribute key: {e}")) + })?; match key { CLIENT_ID_ATTRIBUTE_KEY => Ok(( @@ -436,17 +414,13 @@ impl TryFrom for CreateClient { Option, Option, )| { - let client_id = client_id.ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - })?; - let client_type = - client_type.ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_TYPE_ATTRIBUTE_KEY.to_string(), - })?; - let consensus_height = - consensus_height.ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CONSENSUS_HEIGHT_ATTRIBUTE_KEY.to_string(), - })?; + let client_id = + client_id.ok_or(DecodingError::missing_raw_data("client ID attribute"))?; + let client_type = client_type + .ok_or(DecodingError::missing_raw_data("client type attribute"))?; + let consensus_height = consensus_height.ok_or( + DecodingError::missing_raw_data("consensus height attribute"), + )?; Ok(CreateClient::new( client_id.client_id, @@ -543,13 +517,14 @@ impl From for abci::Event { } } impl TryFrom for UpdateClient { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: abci::Event) -> Result { if value.kind != UPDATE_CLIENT_EVENT { - return Err(ClientError::Other { - description: "Error in parsing UpdateClient event".to_string(), - }); + return Err(DecodingError::MismatchedResourceName { + expected: UPDATE_CLIENT_EVENT.to_string(), + actual: value.kind.to_string(), + })?; } type UpdateClientAttributes = ( @@ -566,12 +541,9 @@ impl TryFrom for UpdateClient { .try_fold( (None, None, None, None, None), |acc: UpdateClientAttributes, attribute| { - let key = - attribute - .key_str() - .map_err(|_| ClientError::InvalidAttributeKey { - attribute_key: String::new(), - })?; + let key = attribute.key_str().map_err(|e| { + DecodingError::invalid_raw_data(format!("attribute key: {e}")) + })?; match key { CLIENT_ID_ATTRIBUTE_KEY => Ok(( @@ -616,29 +588,23 @@ impl TryFrom for UpdateClient { .and_then( |(client_id, client_type, consensus_height, consensus_heights, header)| { let client_id = client_id - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(DecodingError::missing_raw_data("client ID"))? .client_id; let client_type = client_type - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_TYPE_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(DecodingError::missing_raw_data("client type attribute"))? .client_type; let consensus_height = consensus_height - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CONSENSUS_HEIGHT_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(DecodingError::missing_raw_data( + "consensus height attribute", + ))? .consensus_height; let consensus_heights = consensus_heights - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CONSENSUS_HEIGHTS_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(DecodingError::missing_raw_data( + "consensus heights attribute", + ))? .consensus_heights; let header = header - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: HEADER_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(DecodingError::missing_raw_data("header attribute"))? .header; Ok(UpdateClient::new( @@ -796,9 +762,10 @@ mod tests { abci::EventAttribute::from(("consensus_height", "1-10")), ], }, - Err(ClientError::Other { - description: "Error in parsing CreateClient event".to_string(), - }), + Err(DecodingError::MismatchedResourceName { + expected: "CreateClient".to_string(), + actual: "some_other_event".to_string(), + }) )] #[case( abci::Event { @@ -808,13 +775,11 @@ mod tests { abci::EventAttribute::from(("consensus_height", "1-10")), ], }, - Err(ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - }), + Err(DecodingError::missing_raw_data("attribute key")), )] fn test_create_client_try_from( #[case] event: abci::Event, - #[case] expected: Result, + #[case] expected: Result, ) { let result = CreateClient::try_from(event); if expected.is_err() { @@ -858,8 +823,9 @@ mod tests { abci::EventAttribute::from(("header", "1234")), ], }, - Err(ClientError::Other { - description: "Error in parsing UpdateClient event".to_string(), + Err(DecodingError::MismatchedResourceName { + expected: UPDATE_CLIENT_EVENT.to_string(), + actual: "some_other_event".to_owned(), }), )] #[case( @@ -872,13 +838,11 @@ mod tests { abci::EventAttribute::from(("header", "1234")), ], }, - Err(ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - }), + Err(DecodingError::missing_raw_data("attribute key")), )] fn test_update_client_try_from( #[case] event: abci::Event, - #[case] expected: Result, + #[case] expected: Result, ) { let result = UpdateClient::try_from(event); if expected.is_err() { diff --git a/ibc-core/ics02-client/types/src/height.rs b/ibc-core/ics02-client/types/src/height.rs index 96eda4c92..612351863 100644 --- a/ibc-core/ics02-client/types/src/height.rs +++ b/ibc-core/ics02-client/types/src/height.rs @@ -1,10 +1,9 @@ //! Defines the core `Height` type used throughout the library use core::cmp::Ordering; -use core::num::ParseIntError; use core::str::FromStr; -use displaydoc::Display; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::ibc::core::client::v1::Height as RawHeight; use ibc_proto::Protobuf; @@ -77,7 +76,7 @@ impl Height { pub fn sub(&self, delta: u64) -> Result { if self.revision_height <= delta { - return Err(ClientError::InvalidHeightResult); + return Err(ClientError::InvalidHeight); } Ok(Height { @@ -116,10 +115,11 @@ impl Ord for Height { impl Protobuf for Height {} impl TryFrom for Height { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw_height: RawHeight) -> Result { Height::new(raw_height.revision_number, raw_height.revision_height) + .map_err(|_| DecodingError::invalid_raw_data("height of 0 not allowed")) } } @@ -148,58 +148,19 @@ impl core::fmt::Display for Height { } } -/// Encodes all errors related to chain heights -#[derive(Debug, Display, PartialEq, Eq)] -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, - /// the height(`{raw_height}`) is not a valid format, this format must be used: \[revision_number\]-\[revision_height\] - InvalidFormat { raw_height: String }, -} - -#[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 | HeightError::InvalidFormat { .. } => None, - } - } -} - impl TryFrom<&str> for Height { - type Error = HeightError; + type Error = DecodingError; fn try_from(value: &str) -> Result { - let (rev_number_str, rev_height_str) = - value - .split_once('-') - .ok_or_else(|| HeightError::InvalidFormat { - raw_height: value.to_owned(), - })?; - - let revision_number = - rev_number_str - .parse::() - .map_err(|e| HeightError::HeightConversion { - height: value.to_owned(), - error: e, - })?; - - let revision_height = - rev_height_str - .parse::() - .map_err(|e| HeightError::HeightConversion { - height: value.to_owned(), - error: e, - })?; - - Height::new(revision_number, revision_height).map_err(|_| HeightError::ZeroHeight) + let (rev_number_str, rev_height_str) = value.split_once('-').ok_or_else(|| { + DecodingError::invalid_raw_data(format!("height `{value}` not properly formatted")) + })?; + + let revision_number = rev_number_str.parse::()?; + let revision_height = rev_height_str.parse::()?; + + Height::new(revision_number, revision_height) + .map_err(|_| DecodingError::invalid_raw_data("height of 0 not allowed")) } } @@ -210,7 +171,7 @@ impl From for String { } impl FromStr for Height { - type Err = HeightError; + type Err = DecodingError; fn from_str(s: &str) -> Result { Height::try_from(s) @@ -220,41 +181,34 @@ impl FromStr for Height { #[test] fn test_valid_height() { assert_eq!( - "1-1".parse::(), - Ok(Height { + "1-1".parse::().unwrap(), + Height { revision_number: 1, revision_height: 1 - }) + } ); assert_eq!( - "1-10".parse::(), - Ok(Height { + "1-10".parse::().unwrap(), + Height { revision_number: 1, revision_height: 10 - }) + } ); } #[test] fn test_invalid_height() { - assert_eq!( - HeightError::ZeroHeight, - "0-0".parse::().unwrap_err() - ); + assert!("0-0".parse::().is_err()); assert!("0-".parse::().is_err()); assert!("-0".parse::().is_err()); assert!("-".parse::().is_err()); assert!("1-1-1".parse::().is_err()); - assert_eq!( - "1".parse::(), - Err(HeightError::InvalidFormat { - raw_height: "1".to_owned() - }) - ); - assert_eq!( - "".parse::(), - Err(HeightError::InvalidFormat { - raw_height: "".to_owned() - }) - ); + + let decoding_err = "1".parse::().unwrap_err(); + let decoding_err = decoding_err.to_string(); + assert!(decoding_err.contains("height `1` not properly formatted")); + + let decoding_err = "".parse::().unwrap_err(); + let decoding_err = decoding_err.to_string(); + assert!(decoding_err.contains("height `` not properly formatted")); } diff --git a/ibc-core/ics02-client/types/src/msgs/create_client.rs b/ibc-core/ics02-client/types/src/msgs/create_client.rs index fe6944b3d..1891a143e 100644 --- a/ibc-core/ics02-client/types/src/msgs/create_client.rs +++ b/ibc-core/ics02-client/types/src/msgs/create_client.rs @@ -1,13 +1,12 @@ //! Definition of domain type message `MsgCreateClient`. +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient; use ibc_proto::Protobuf; -use crate::error::ClientError; - pub const CREATE_CLIENT_TYPE_URL: &str = "/ibc.core.client.v1.MsgCreateClient"; /// A type of message that triggers the creation of a new on-chain (IBC) client. @@ -36,14 +35,16 @@ impl MsgCreateClient { impl Protobuf for MsgCreateClient {} impl TryFrom for MsgCreateClient { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMsgCreateClient) -> Result { - let raw_client_state = raw.client_state.ok_or(ClientError::MissingRawClientState)?; + let raw_client_state = raw + .client_state + .ok_or(DecodingError::missing_raw_data("client state"))?; let raw_consensus_state = raw .consensus_state - .ok_or(ClientError::MissingRawConsensusState)?; + .ok_or(DecodingError::missing_raw_data("consensus state"))?; Ok(MsgCreateClient::new( raw_client_state, diff --git a/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs b/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs index 0b4031fe5..1e495393b 100644 --- a/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs +++ b/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs @@ -1,5 +1,6 @@ //! Definition of domain type message `MsgSubmitMisbehaviour`. +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -7,8 +8,6 @@ use ibc_proto::google::protobuf::Any as ProtoAny; use ibc_proto::ibc::core::client::v1::MsgSubmitMisbehaviour as RawMsgSubmitMisbehaviour; use ibc_proto::Protobuf; -use crate::error::ClientError; - pub const SUBMIT_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.core.client.v1.MsgSubmitMisbehaviour"; /// A type of message that submits client misbehaviour proof. @@ -37,18 +36,15 @@ pub struct MsgSubmitMisbehaviour { impl Protobuf for MsgSubmitMisbehaviour {} impl TryFrom for MsgSubmitMisbehaviour { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMsgSubmitMisbehaviour) -> Result { let raw_misbehaviour = raw .misbehaviour - .ok_or(ClientError::MissingRawMisbehaviour)?; + .ok_or(DecodingError::missing_raw_data("msg submit misbehaviour"))?; Ok(MsgSubmitMisbehaviour { - client_id: raw - .client_id - .parse() - .map_err(ClientError::InvalidRawMisbehaviour)?, + client_id: raw.client_id.parse()?, misbehaviour: raw_misbehaviour, signer: raw.signer.into(), }) diff --git a/ibc-core/ics02-client/types/src/msgs/recover_client.rs b/ibc-core/ics02-client/types/src/msgs/recover_client.rs index 5bc6c0594..5ef857cba 100644 --- a/ibc-core/ics02-client/types/src/msgs/recover_client.rs +++ b/ibc-core/ics02-client/types/src/msgs/recover_client.rs @@ -1,13 +1,12 @@ //! Definition of domain type message `MsgRecoverClient`. +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::client::v1::MsgRecoverClient as RawMsgRecoverClient; use ibc_proto::Protobuf; -use crate::error::ClientError; - pub const RECOVER_CLIENT_TYPE_URL: &str = "/ibc.core.client.v1.MsgRecoverClient"; /// Defines the message used to recover a frozen or expired client. @@ -38,18 +37,12 @@ pub struct MsgRecoverClient { impl Protobuf for MsgRecoverClient {} impl TryFrom for MsgRecoverClient { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMsgRecoverClient) -> Result { Ok(MsgRecoverClient { - subject_client_id: raw - .subject_client_id - .parse() - .map_err(ClientError::InvalidMsgRecoverClientId)?, - substitute_client_id: raw - .substitute_client_id - .parse() - .map_err(ClientError::InvalidMsgRecoverClientId)?, + subject_client_id: raw.subject_client_id.parse()?, + substitute_client_id: raw.substitute_client_id.parse()?, signer: raw.signer.into(), }) } diff --git a/ibc-core/ics02-client/types/src/msgs/update_client.rs b/ibc-core/ics02-client/types/src/msgs/update_client.rs index df79446ef..16a5d250d 100644 --- a/ibc-core/ics02-client/types/src/msgs/update_client.rs +++ b/ibc-core/ics02-client/types/src/msgs/update_client.rs @@ -1,5 +1,6 @@ //! Definition of domain type message `MsgUpdateClient`. +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -7,8 +8,6 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; use ibc_proto::Protobuf; -use crate::error::ClientError; - pub const UPDATE_CLIENT_TYPE_URL: &str = "/ibc.core.client.v1.MsgUpdateClient"; /// Represents the message that triggers the update of an on-chain (IBC) client @@ -30,17 +29,14 @@ pub struct MsgUpdateClient { impl Protobuf for MsgUpdateClient {} impl TryFrom for MsgUpdateClient { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMsgUpdateClient) -> Result { Ok(MsgUpdateClient { - client_id: raw - .client_id - .parse() - .map_err(ClientError::InvalidMsgUpdateClientId)?, - client_message: raw - .client_message - .ok_or(ClientError::MissingClientMessage)?, + client_id: raw.client_id.parse()?, + client_message: raw.client_message.ok_or(DecodingError::MissingRawData { + description: "client message not set".to_string(), + })?, signer: raw.signer.into(), }) } diff --git a/ibc-core/ics02-client/types/src/msgs/upgrade_client.rs b/ibc-core/ics02-client/types/src/msgs/upgrade_client.rs index c3d9a956b..d581f8e1b 100644 --- a/ibc-core/ics02-client/types/src/msgs/upgrade_client.rs +++ b/ibc-core/ics02-client/types/src/msgs/upgrade_client.rs @@ -3,7 +3,7 @@ use core::str::FromStr; use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_commitment_types::error::CommitmentError; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -11,8 +11,6 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; use ibc_proto::Protobuf; -use crate::error::{ClientError, UpgradeClientError}; - pub const UPGRADE_CLIENT_TYPE_URL: &str = "/ibc.core.client.v1.MsgUpgradeClient"; /// A type of message that triggers the upgrade of an on-chain (IBC) client. @@ -54,31 +52,25 @@ impl From for RawMsgUpgradeClient { } impl TryFrom for MsgUpgradeClient { - type Error = ClientError; + type Error = DecodingError; fn try_from(proto_msg: RawMsgUpgradeClient) -> Result { let raw_client_state = proto_msg .client_state - .ok_or(ClientError::MissingRawClientState)?; + .ok_or(DecodingError::missing_raw_data("msg upgrade client state"))?; - let raw_consensus_state = proto_msg - .consensus_state - .ok_or(ClientError::MissingRawConsensusState)?; + let raw_consensus_state = + proto_msg + .consensus_state + .ok_or(DecodingError::missing_raw_data( + "msg upgrade client consensus state", + ))?; - let c_bytes = - CommitmentProofBytes::try_from(proto_msg.proof_upgrade_client).map_err(|_| { - UpgradeClientError::InvalidUpgradeClientProof(CommitmentError::EmptyMerkleProof) - })?; - let cs_bytes = CommitmentProofBytes::try_from(proto_msg.proof_upgrade_consensus_state) - .map_err(|_| { - UpgradeClientError::InvalidUpgradeConsensusStateProof( - CommitmentError::EmptyMerkleProof, - ) - })?; + let c_bytes = CommitmentProofBytes::try_from(proto_msg.proof_upgrade_client)?; + let cs_bytes = CommitmentProofBytes::try_from(proto_msg.proof_upgrade_consensus_state)?; Ok(MsgUpgradeClient { - client_id: ClientId::from_str(&proto_msg.client_id) - .map_err(ClientError::InvalidClientIdentifier)?, + client_id: ClientId::from_str(&proto_msg.client_id)?, upgraded_client_state: raw_client_state, upgraded_consensus_state: raw_consensus_state, proof_upgrade_client: c_bytes, diff --git a/ibc-core/ics02-client/types/src/status.rs b/ibc-core/ics02-client/types/src/status.rs index d77a7bbe6..39e4f36f2 100644 --- a/ibc-core/ics02-client/types/src/status.rs +++ b/ibc-core/ics02-client/types/src/status.rs @@ -1,6 +1,7 @@ use core::fmt::{Debug, Display, Formatter}; use core::str::FromStr; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use crate::error::ClientError; @@ -50,7 +51,7 @@ impl Status { pub fn verify_is_active(&self) -> Result<(), ClientError> { match self { Self::Active => Ok(()), - &status => Err(ClientError::ClientNotActive { status }), + &status => Err(ClientError::InvalidStatus(status)), } } @@ -58,7 +59,7 @@ impl Status { pub fn verify_is_inactive(&self) -> Result<(), ClientError> { match self { Self::Frozen | Self::Expired => Ok(()), - &status => Err(ClientError::ClientNotInactive { status }), + &status => Err(ClientError::InvalidStatus(status)), } } } @@ -70,7 +71,7 @@ impl Display for Status { } impl FromStr for Status { - type Err = ClientError; + type Err = DecodingError; fn from_str(s: &str) -> Result { match s { @@ -78,9 +79,9 @@ impl FromStr for Status { "FROZEN" => Ok(Status::Frozen), "EXPIRED" => Ok(Status::Expired), "UNAUTHORIZED" => Ok(Status::Unauthorized), - _ => Err(ClientError::Other { - description: format!("invalid status string: {s}"), - }), + _ => Err(DecodingError::invalid_raw_data(format!( + "invalid status {s}", + ))), } } } diff --git a/ibc-core/ics03-connection/src/delay.rs b/ibc-core/ics03-connection/src/delay.rs index 06fb03098..b0f427b22 100644 --- a/ibc-core/ics03-connection/src/delay.rs +++ b/ibc-core/ics03-connection/src/delay.rs @@ -2,14 +2,13 @@ use ibc_core_client::context::ClientValidationContext; use ibc_core_client::types::Height; use ibc_core_connection_types::error::ConnectionError; use ibc_core_connection_types::ConnectionEnd; -use ibc_core_handler_types::error::ContextError; use ibc_core_host::ValidationContext; pub fn verify_conn_delay_passed( ctx: &Ctx, packet_proof_height: Height, connection_end: &ConnectionEnd, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ValidationContext, { @@ -28,26 +27,21 @@ where let conn_delay_height_period = ctx.block_delay(&conn_delay_time_period); // Verify that the current host chain time is later than the last client update time - let earliest_valid_time = (last_client_update.0 + conn_delay_time_period) - .map_err(ConnectionError::TimestampOverflow)?; + let earliest_valid_time = (last_client_update.0 + conn_delay_time_period)?; if current_host_time < earliest_valid_time { - return Err(ContextError::ConnectionError( - ConnectionError::NotEnoughTimeElapsed { - current_host_time, - earliest_valid_time, - }, - )); + return Err(ConnectionError::InsufficientTimeElapsed { + current_host_time, + earliest_valid_time, + }); } // Verify that the current host chain height is later than the last client update height let earliest_valid_height = last_client_update.1.add(conn_delay_height_period); if current_host_height < earliest_valid_height { - return Err(ContextError::ConnectionError( - ConnectionError::NotEnoughBlocksElapsed { - current_host_height, - earliest_valid_height, - }, - )); + return Err(ConnectionError::InsufficientBlocksElapsed { + current_host_height, + earliest_valid_height, + }); }; Ok(()) diff --git a/ibc-core/ics03-connection/src/handler/conn_open_ack.rs b/ibc-core/ics03-connection/src/handler/conn_open_ack.rs index 443d32877..a91a16c76 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_ack.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_ack.rs @@ -6,7 +6,6 @@ use ibc_core_connection_types::error::ConnectionError; use ibc_core_connection_types::events::OpenAck; use ibc_core_connection_types::msgs::MsgConnectionOpenAck; use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::ClientId; use ibc_core_host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path}; @@ -17,7 +16,7 @@ use ibc_primitives::ToVec; use crate::handler::{pack_host_consensus_state, unpack_host_client_state}; -pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenAck) -> Result<(), ContextError> +pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenAck) -> Result<(), ConnectionError> where Ctx: ValidationContext, >::Error: Into, @@ -30,22 +29,20 @@ fn validate_impl( ctx_a: &Ctx, msg: &MsgConnectionOpenAck, vars: &LocalVars, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ValidationContext, >::Error: Into, { ctx_a.validate_message_signer(&msg.signer)?; - let host_height = ctx_a.host_height().map_err(|_| ConnectionError::Other { - description: "failed to get host height".to_string(), - })?; + let host_height = ctx_a.host_height()?; + if msg.consensus_height_of_a_on_b > host_height { - return Err(ConnectionError::InvalidConsensusHeight { + return Err(ConnectionError::InsufficientConsensusHeight { target_height: msg.consensus_height_of_a_on_b, current_height: host_height, - } - .into()); + }); } let client_val_ctx_a = ctx_a.get_client_validation_context(); @@ -69,6 +66,7 @@ where client_state_of_b_on_a .status(client_val_ctx_a, vars.client_id_on_a())? .verify_is_active()?; + client_state_of_b_on_a.validate_proof_height(msg.proofs_height_on_b)?; let client_cons_state_path_on_a = ClientConsensusStatePath::new( @@ -96,29 +94,22 @@ where vars.conn_end_on_a.delay_period(), )?; - client_state_of_b_on_a - .verify_membership( - prefix_on_b, - &msg.proof_conn_end_on_b, - consensus_state_of_b_on_a.root(), - Path::Connection(ConnectionPath::new(&msg.conn_id_on_b)), - expected_conn_end_on_b.encode_vec(), - ) - .map_err(ConnectionError::VerifyConnectionState)?; - } - - client_state_of_b_on_a - .verify_membership( + client_state_of_b_on_a.verify_membership( prefix_on_b, - &msg.proof_client_state_of_a_on_b, + &msg.proof_conn_end_on_b, consensus_state_of_b_on_a.root(), - Path::ClientState(ClientStatePath::new(vars.client_id_on_b().clone())), - msg.client_state_of_a_on_b.to_vec(), - ) - .map_err(|e| ConnectionError::ClientStateVerificationFailure { - client_id: vars.client_id_on_b().clone(), - client_error: e, - })?; + Path::Connection(ConnectionPath::new(&msg.conn_id_on_b)), + expected_conn_end_on_b.encode_vec(), + )?; + } + + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_client_state_of_a_on_b, + consensus_state_of_b_on_a.root(), + Path::ClientState(ClientStatePath::new(vars.client_id_on_b().clone())), + msg.client_state_of_a_on_b.to_vec(), + )?; let expected_consensus_state_of_a_on_b = ctx_a.host_consensus_state(&msg.consensus_height_of_a_on_b)?; @@ -132,24 +123,19 @@ where msg.consensus_height_of_a_on_b.revision_height(), ); - client_state_of_b_on_a - .verify_membership( - prefix_on_b, - &msg.proof_consensus_state_of_a_on_b, - consensus_state_of_b_on_a.root(), - Path::ClientConsensusState(client_cons_state_path_on_b), - stored_consensus_state_of_a_on_b.to_vec(), - ) - .map_err(|e| ConnectionError::ConsensusStateVerificationFailure { - height: msg.proofs_height_on_b, - client_error: e, - })?; + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_consensus_state_of_a_on_b, + consensus_state_of_b_on_a.root(), + Path::ClientConsensusState(client_cons_state_path_on_b), + stored_consensus_state_of_a_on_b.to_vec(), + )?; } Ok(()) } -pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenAck) -> Result<(), ContextError> +pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenAck) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { @@ -161,7 +147,7 @@ fn execute_impl( ctx_a: &mut Ctx, msg: MsgConnectionOpenAck, vars: LocalVars, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { @@ -199,7 +185,7 @@ struct LocalVars { } impl LocalVars { - fn new(ctx_a: &Ctx, msg: &MsgConnectionOpenAck) -> Result + fn new(ctx_a: &Ctx, msg: &MsgConnectionOpenAck) -> Result where Ctx: ValidationContext, { diff --git a/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs b/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs index f9ef4eb61..ba934493d 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs @@ -5,7 +5,6 @@ use ibc_core_connection_types::error::ConnectionError; use ibc_core_connection_types::events::OpenConfirm; use ibc_core_connection_types::msgs::MsgConnectionOpenConfirm; use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; use ibc_core_host::types::path::{ClientConsensusStatePath, ConnectionPath, Path}; @@ -13,7 +12,7 @@ use ibc_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; -pub fn validate(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ContextError> +pub fn validate(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ConnectionError> where Ctx: ValidationContext, { @@ -25,7 +24,7 @@ fn validate_impl( ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm, vars: &LocalVars, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ValidationContext, { @@ -48,6 +47,7 @@ where client_state_of_a_on_b .status(client_val_ctx_b, client_id_on_b)? .verify_is_active()?; + client_state_of_a_on_b.validate_proof_height(msg.proof_height_on_a)?; let client_cons_state_path_on_b = ClientConsensusStatePath::new( @@ -73,21 +73,19 @@ where conn_end_on_b.delay_period(), )?; - client_state_of_a_on_b - .verify_membership( - prefix_on_a, - &msg.proof_conn_end_on_a, - consensus_state_of_a_on_b.root(), - Path::Connection(ConnectionPath::new(conn_id_on_a)), - expected_conn_end_on_a.encode_vec(), - ) - .map_err(ConnectionError::VerifyConnectionState)?; + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_conn_end_on_a, + consensus_state_of_a_on_b.root(), + Path::Connection(ConnectionPath::new(conn_id_on_a)), + expected_conn_end_on_a.encode_vec(), + )?; } Ok(()) } -pub fn execute(ctx_b: &mut Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ContextError> +pub fn execute(ctx_b: &mut Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { @@ -99,7 +97,7 @@ fn execute_impl( ctx_b: &mut Ctx, msg: &MsgConnectionOpenConfirm, vars: LocalVars, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { @@ -136,7 +134,7 @@ struct LocalVars { } impl LocalVars { - fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result + fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result where Ctx: ValidationContext, { diff --git a/ibc-core/ics03-connection/src/handler/conn_open_init.rs b/ibc-core/ics03-connection/src/handler/conn_open_init.rs index 8d4286616..267603a7f 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_init.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_init.rs @@ -1,16 +1,16 @@ //! Protocol logic specific to ICS3 messages of type `MsgConnectionOpenInit`. use ibc_core_client::context::prelude::*; +use ibc_core_connection_types::error::ConnectionError; use ibc_core_connection_types::events::OpenInit; use ibc_core_connection_types::msgs::MsgConnectionOpenInit; use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::ConnectionId; use ibc_core_host::types::path::{ClientConnectionPath, ConnectionPath}; use ibc_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; -pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenInit) -> Result<(), ContextError> +pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> where Ctx: ValidationContext, { @@ -32,7 +32,7 @@ where Ok(()) } -pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenInit) -> Result<(), ContextError> +pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { diff --git a/ibc-core/ics03-connection/src/handler/conn_open_try.rs b/ibc-core/ics03-connection/src/handler/conn_open_try.rs index 4b59913e7..2b47e488d 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_try.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_try.rs @@ -5,7 +5,6 @@ use ibc_core_connection_types::error::ConnectionError; use ibc_core_connection_types::events::OpenTry; use ibc_core_connection_types::msgs::MsgConnectionOpenTry; use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; use ibc_core_host::types::path::{ @@ -18,7 +17,7 @@ use ibc_primitives::ToVec; use crate::handler::{pack_host_consensus_state, unpack_host_client_state}; -pub fn validate(ctx_b: &Ctx, msg: MsgConnectionOpenTry) -> Result<(), ContextError> +pub fn validate(ctx_b: &Ctx, msg: MsgConnectionOpenTry) -> Result<(), ConnectionError> where Ctx: ValidationContext, >::Error: Into, @@ -31,7 +30,7 @@ fn validate_impl( ctx_b: &Ctx, msg: &MsgConnectionOpenTry, vars: &LocalVars, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ValidationContext, >::Error: Into, @@ -47,16 +46,14 @@ where ctx_b.validate_self_client(client_state_of_b_on_a)?; - let host_height = ctx_b.host_height().map_err(|_| ConnectionError::Other { - description: "failed to get host height".to_string(), - })?; + let host_height = ctx_b.host_height()?; + if msg.consensus_height_of_b_on_a > host_height { // Fail if the consensus height is too advanced. - return Err(ConnectionError::InvalidConsensusHeight { + return Err(ConnectionError::InsufficientConsensusHeight { target_height: msg.consensus_height_of_b_on_a, current_height: host_height, - } - .into()); + }); } let client_id_on_a = msg.counterparty.client_id(); @@ -69,6 +66,7 @@ where client_state_of_a_on_b .status(client_val_ctx_b, &msg.client_id_on_b)? .verify_is_active()?; + client_state_of_a_on_b.validate_proof_height(msg.proofs_height_on_a)?; let client_cons_state_path_on_b = ClientConsensusStatePath::new( @@ -92,29 +90,22 @@ where msg.delay_period, )?; - client_state_of_a_on_b - .verify_membership( - prefix_on_a, - &msg.proof_conn_end_on_a, - consensus_state_of_a_on_b.root(), - Path::Connection(ConnectionPath::new(&vars.conn_id_on_a)), - expected_conn_end_on_a.encode_vec(), - ) - .map_err(ConnectionError::VerifyConnectionState)?; - } - - client_state_of_a_on_b - .verify_membership( + client_state_of_a_on_b.verify_membership( prefix_on_a, - &msg.proof_client_state_of_b_on_a, + &msg.proof_conn_end_on_a, consensus_state_of_a_on_b.root(), - Path::ClientState(ClientStatePath::new(client_id_on_a.clone())), - msg.client_state_of_b_on_a.to_vec(), - ) - .map_err(|e| ConnectionError::ClientStateVerificationFailure { - client_id: msg.client_id_on_b.clone(), - client_error: e, - })?; + Path::Connection(ConnectionPath::new(&vars.conn_id_on_a)), + expected_conn_end_on_a.encode_vec(), + )?; + } + + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_client_state_of_b_on_a, + consensus_state_of_a_on_b.root(), + Path::ClientState(ClientStatePath::new(client_id_on_a.clone())), + msg.client_state_of_b_on_a.to_vec(), + )?; let expected_consensus_state_of_b_on_a = ctx_b.host_consensus_state(&msg.consensus_height_of_b_on_a)?; @@ -128,24 +119,19 @@ where msg.consensus_height_of_b_on_a.revision_height(), ); - client_state_of_a_on_b - .verify_membership( - prefix_on_a, - &msg.proof_consensus_state_of_b_on_a, - consensus_state_of_a_on_b.root(), - Path::ClientConsensusState(client_cons_state_path_on_a), - stored_consensus_state_of_b_on_a.to_vec(), - ) - .map_err(|e| ConnectionError::ConsensusStateVerificationFailure { - height: msg.proofs_height_on_a, - client_error: e, - })?; + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_consensus_state_of_b_on_a, + consensus_state_of_a_on_b.root(), + Path::ClientConsensusState(client_cons_state_path_on_a), + stored_consensus_state_of_b_on_a.to_vec(), + )?; } Ok(()) } -pub fn execute(ctx_b: &mut Ctx, msg: MsgConnectionOpenTry) -> Result<(), ContextError> +pub fn execute(ctx_b: &mut Ctx, msg: MsgConnectionOpenTry) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { @@ -157,7 +143,7 @@ fn execute_impl( ctx_b: &mut Ctx, msg: MsgConnectionOpenTry, vars: LocalVars, -) -> Result<(), ContextError> +) -> Result<(), ConnectionError> where Ctx: ExecutionContext, { @@ -194,7 +180,7 @@ struct LocalVars { } impl LocalVars { - fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenTry) -> Result + fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenTry) -> Result where Ctx: ValidationContext, { diff --git a/ibc-core/ics03-connection/src/handler/mod.rs b/ibc-core/ics03-connection/src/handler/mod.rs index 241136411..791d1c778 100644 --- a/ibc-core/ics03-connection/src/handler/mod.rs +++ b/ibc-core/ics03-connection/src/handler/mod.rs @@ -1,5 +1,7 @@ use ibc_core_client::types::error::ClientError; -use ibc_core_handler_types::error::ContextError; +use ibc_core_connection_types::error::ConnectionError; +#[cfg(feature = "wasm-client")] +use ibc_core_host::types::error::DecodingError; use ibc_core_host::types::identifiers::ClientId; use ibc_primitives::proto::Any; @@ -16,7 +18,7 @@ pub mod conn_open_try; pub(crate) fn unpack_host_client_state( value: Any, host_client_id_at_counterparty: &ClientId, -) -> Result +) -> Result where CS: TryFrom, >::Error: Into, @@ -24,22 +26,12 @@ where #[cfg(feature = "wasm-client")] if host_client_id_at_counterparty.is_wasm_client_id() { use ibc_client_wasm_types::client_state::ClientState as WasmClientState; - use ibc_core_connection_types::error::ConnectionError; - use ibc_primitives::prelude::ToString; use prost::Message; - let wasm_client_state = WasmClientState::try_from(value).map_err(|e| { - ContextError::ConnectionError(ConnectionError::InvalidClientState { - reason: e.to_string(), - }) - })?; + let wasm_client_state = WasmClientState::try_from(value)?; let any_client_state = ::decode(wasm_client_state.data.as_slice()) - .map_err(|e| { - ContextError::ConnectionError(ConnectionError::InvalidClientState { - reason: e.to_string(), - }) - })?; + .map_err(|e| ConnectionError::Decoding(DecodingError::Prost(e)))?; Ok(CS::try_from(any_client_state).map_err(Into::::into)?) } else { diff --git a/ibc-core/ics03-connection/types/src/connection.rs b/ibc-core/ics03-connection/types/src/connection.rs index dbfa87b01..f518f86a5 100644 --- a/ibc-core/ics03-connection/types/src/connection.rs +++ b/ibc-core/ics03-connection/types/src/connection.rs @@ -4,6 +4,7 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use core::time::Duration; use ibc_core_commitment_types::commitment::CommitmentPrefix; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; use ibc_primitives::prelude::*; use ibc_proto::ibc::core::connection::v1::{ @@ -55,7 +56,7 @@ impl IdentifiedConnectionEnd { impl Protobuf for IdentifiedConnectionEnd {} impl TryFrom for IdentifiedConnectionEnd { - type Error = ConnectionError; + type Error = DecodingError; fn try_from(value: RawIdentifiedConnection) -> Result { let raw_connection_end = RawConnectionEnd { @@ -67,10 +68,7 @@ impl TryFrom for IdentifiedConnectionEnd { }; Ok(IdentifiedConnectionEnd { - connection_id: value - .id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, + connection_id: value.id.parse()?, connection_end: raw_connection_end.try_into()?, }) } @@ -197,27 +195,25 @@ mod sealed { impl Protobuf for ConnectionEnd {} impl TryFrom for ConnectionEnd { - type Error = ConnectionError; + type Error = DecodingError; + fn try_from(value: RawConnectionEnd) -> Result { let state = value.state.try_into()?; if value.client_id.is_empty() { - return Err(ConnectionError::EmptyProtoConnectionEnd); + return Err(DecodingError::missing_raw_data("connection end client ID"))?; } if value.versions.is_empty() { - return Err(ConnectionError::EmptyVersions); + return Err(DecodingError::missing_raw_data("connection end versions"))?; } Self::new( state, - value - .client_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, + value.client_id.parse()?, value .counterparty - .ok_or(ConnectionError::MissingCounterparty)? + .ok_or(DecodingError::missing_raw_data("counterparty"))? .try_into()?, value .versions @@ -226,6 +222,7 @@ impl TryFrom for ConnectionEnd { .collect::, _>>()?, Duration::from_nanos(value.delay_period), ) + .map_err(|_| DecodingError::invalid_raw_data("connection end")) } } @@ -257,7 +254,7 @@ impl ConnectionEnd { // + Init: contains the set of compatible versions, // + TryOpen/Open: contains the single version chosen by the handshake protocol. if state != State::Init && versions.len() != 1 { - return Err(ConnectionError::InvalidVersionLength); + return Err(ConnectionError::InvalidState { description: "failed to initialize new ConnectionEnd; expected `Init` connection state and a single version".to_string() }); } Ok(Self { @@ -312,7 +309,7 @@ impl ConnectionEnd { /// Checks if the state of this connection end matches with an expected state. pub fn verify_state_matches(&self, expected: &State) -> Result<(), ConnectionError> { if !self.state.eq(expected) { - return Err(ConnectionError::InvalidState { + return Err(ConnectionError::MismatchedConnectionStates { expected: expected.to_string(), actual: self.state.to_string(), }); @@ -368,28 +365,20 @@ 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 = ConnectionError; + type Error = DecodingError; fn try_from(raw_counterparty: RawCounterparty) -> Result { let connection_id: Option = if raw_counterparty.connection_id.is_empty() { None } else { - Some( - raw_counterparty - .connection_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, - ) + Some(raw_counterparty.connection_id.parse()?) }; Ok(Counterparty::new( - raw_counterparty - .client_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, + raw_counterparty.client_id.parse()?, connection_id, raw_counterparty .prefix - .ok_or(ConnectionError::MissingCounterparty)? + .ok_or(DecodingError::missing_raw_data("counterparty prefix"))? .key_prefix .into(), )) @@ -436,15 +425,6 @@ impl Counterparty { pub fn prefix(&self) -> &CommitmentPrefix { &self.prefix } - - /// Called upon initiating a connection handshake on the host chain to verify - /// that the counterparty connection id has not been set. - pub(crate) fn verify_empty_connection_id(&self) -> Result<(), ConnectionError> { - if self.connection_id().is_some() { - return Err(ConnectionError::InvalidCounterparty); - } - Ok(()) - } } #[cfg_attr( @@ -488,8 +468,8 @@ impl State { 1 => Ok(Self::Init), 2 => Ok(Self::TryOpen), 3 => Ok(Self::Open), - _ => Err(ConnectionError::InvalidState { - expected: "Must be one of: 0, 1, 2, 3".to_string(), + _ => Err(ConnectionError::MismatchedConnectionStates { + expected: "0, 1, 2, or 3".to_string(), actual: s.to_string(), }), } @@ -521,17 +501,17 @@ impl Display for State { } impl TryFrom for State { - type Error = ConnectionError; + type Error = DecodingError; + fn try_from(value: i32) -> Result { match value { 0 => Ok(Self::Uninitialized), 1 => Ok(Self::Init), 2 => Ok(Self::TryOpen), 3 => Ok(Self::Open), - _ => Err(ConnectionError::InvalidState { - expected: "Must be one of: 0, 1, 2, 3".to_string(), - actual: value.to_string(), - }), + _ => Err(DecodingError::invalid_raw_data(format!( + "connection state expected to be 0, 1, 2, or 3, actual {value}", + ))), } } } diff --git a/ibc-core/ics03-connection/types/src/error.rs b/ibc-core/ics03-connection/types/src/error.rs index e71d5426c..fa4ad6297 100644 --- a/ibc-core/ics03-connection/types/src/error.rs +++ b/ibc-core/ics03-connection/types/src/error.rs @@ -1,106 +1,89 @@ //! Defines the connection error type use displaydoc::Display; -use ibc_core_client_types::{error as client_error, Height}; -use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; +use ibc_core_client_types::error::ClientError; +use ibc_core_client_types::Height; +use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; -use crate::version::Version; - #[derive(Debug, Display)] pub enum ConnectionError { - /// client error: `{0}` - Client(client_error::ClientError), - /// invalid connection state: expected `{expected}`, actual `{actual}` - InvalidState { expected: String, actual: String }, - /// 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(IdentifierError), - /// ConnectionEnd domain object could not be constructed out of empty proto object - EmptyProtoConnectionEnd, - /// empty supported versions - EmptyVersions, - /// single version must be negotiated on connection before opening channel - InvalidVersionLength, - /// version \"`{version}`\" not supported - VersionNotSupported { version: Version }, - /// no common version - NoCommonVersion, - /// empty supported features - EmptyFeatures, - /// feature \"`{feature}`\" not supported - FeatureNotSupported { feature: String }, - /// no common features - NoCommonFeatures, - /// missing proof height - MissingProofHeight, - /// missing consensus height - MissingConsensusHeight, - /// invalid connection proof error - InvalidProof, - /// verifying connection state error: `{0}` - VerifyConnectionState(client_error::ClientError), - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// no connection was found for the previous connection id provided `{connection_id}` - ConnectionNotFound { connection_id: ConnectionId }, + /// client error: {0} + Client(ClientError), + /// decoding error: {0} + Decoding(DecodingError), + /// host error: {0} + Host(HostError), + /// timestamp error: {0} + Timestamp(TimestampError), /// invalid counterparty InvalidCounterparty, + /// invalid connection state: {description} + InvalidState { description: String }, + /// mismatched connection states: expected `{expected}`, actual `{actual}` + MismatchedConnectionStates { expected: String, actual: String }, + /// missing supported features + MissingFeatures, + /// missing common version + MissingCommonVersion, /// missing counterparty MissingCounterparty, - /// 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 - client_id: ClientId, - client_error: client_error::ClientError, + /// insufficient consensus height `{current_height}` for host chain; needs to meet counterparty's height `{target_height}` + InsufficientConsensusHeight { + target_height: Height, + current_height: Height, }, - /// invalid client state: `{reason}` - InvalidClientState { reason: String }, - /// not enough blocks elapsed, current height `{current_host_height}` is still less than the earliest acceptable height `{earliest_valid_height}` - NotEnoughBlocksElapsed { + /// insufficient blocks elapsed: current height `{current_host_height}` needs to meet `{earliest_valid_height}` + InsufficientBlocksElapsed { current_host_height: Height, earliest_valid_height: Height, }, - /// not enough time elapsed, current timestamp `{current_host_time}` is still less than the earliest acceptable timestamp `{earliest_valid_time}` - NotEnoughTimeElapsed { + /// insufficient time elapsed: current timestamp `{current_host_time}` needs to meet `{earliest_valid_time}` + InsufficientTimeElapsed { current_host_time: Timestamp, earliest_valid_time: Timestamp, }, - /// timestamp overflowed error: `{0}` - TimestampOverflow(TimestampError), - /// connection counter overflow error - CounterOverflow, - /// other error: `{description}` - Other { description: String }, +} + +impl From for ConnectionError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) + } +} + +impl From for ConnectionError { + fn from(e: IdentifierError) -> Self { + Self::Decoding(DecodingError::Identifier(e)) + } +} + +impl From for ConnectionError { + fn from(e: TimestampError) -> Self { + Self::Timestamp(e) + } +} + +impl From for ConnectionError { + fn from(e: ClientError) -> Self { + Self::Client(e) + } +} + +impl From for ConnectionError { + fn from(e: HostError) -> Self { + Self::Host(e) + } } #[cfg(feature = "std")] impl std::error::Error for ConnectionError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::Client(e) - | Self::VerifyConnectionState(e) - | Self::ConsensusStateVerificationFailure { - client_error: e, .. - } - | Self::ClientStateVerificationFailure { - client_error: e, .. - } => Some(e), - Self::InvalidIdentifier(e) => Some(e), - Self::TimestampOverflow(e) => Some(e), + Self::Host(e) => Some(e), + Self::Client(e) => Some(e), + Self::Decoding(e) => Some(e), + Self::Timestamp(e) => Some(e), _ => None, } } diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs index dc3056b2a..9379c25e3 100644 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs +++ b/ibc-core/ics03-connection/types/src/msgs/conn_open_ack.rs @@ -1,5 +1,6 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ConnectionId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -7,7 +8,6 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; use ibc_proto::Protobuf; -use crate::error::ConnectionError; use crate::version::Version; pub const CONN_OPEN_ACK_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenAck"; @@ -47,54 +47,35 @@ pub struct MsgConnectionOpenAck { impl Protobuf for MsgConnectionOpenAck {} impl TryFrom for MsgConnectionOpenAck { - type Error = ConnectionError; + type Error = DecodingError; fn try_from(msg: RawMsgConnectionOpenAck) -> Result { Ok(Self { - conn_id_on_a: msg - .connection_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, - conn_id_on_b: msg - .counterparty_connection_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, + conn_id_on_a: msg.connection_id.parse()?, + conn_id_on_b: msg.counterparty_connection_id.parse()?, client_state_of_a_on_b: msg .client_state - .ok_or(ConnectionError::MissingClientState)?, + .ok_or(DecodingError::missing_raw_data("client state"))?, version: msg .version - .ok_or(ConnectionError::EmptyVersions)? + .ok_or(DecodingError::missing_raw_data("connection version"))? .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(|_| ConnectionError::InvalidProof)?, - proof_consensus_state_of_a_on_b: msg - .proof_consensus - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, + proof_conn_end_on_b: msg.proof_try.try_into()?, + proof_client_state_of_a_on_b: msg.proof_client.try_into()?, + proof_consensus_state_of_a_on_b: msg.proof_consensus.try_into()?, proofs_height_on_b: msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ConnectionError::MissingProofHeight)?, + .ok_or(DecodingError::missing_raw_data("proof height"))?, consensus_height_of_a_on_b: msg .consensus_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ConnectionError::MissingConsensusHeight)?, + .ok_or(DecodingError::missing_raw_data("consensus height"))?, signer: msg.signer.into(), proof_consensus_state_of_a: if msg.host_consensus_state_proof.is_empty() { None } else { - Some( - msg.host_consensus_state_proof - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, - ) + Some(msg.host_consensus_state_proof.try_into()?) }, }) } diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs index e18821d57..24a58b87e 100644 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs +++ b/ibc-core/ics03-connection/types/src/msgs/conn_open_confirm.rs @@ -1,13 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ConnectionId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; use ibc_proto::Protobuf; -use crate::error::ConnectionError; - pub const CONN_OPEN_CONFIRM_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenConfirm"; /// Per our convention, this message is sent to chain B. @@ -31,22 +30,18 @@ pub struct MsgConnectionOpenConfirm { impl Protobuf for MsgConnectionOpenConfirm {} impl TryFrom for MsgConnectionOpenConfirm { - type Error = ConnectionError; + type Error = DecodingError; fn try_from(msg: RawMsgConnectionOpenConfirm) -> Result { Ok(Self { - conn_id_on_b: msg - .connection_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, - proof_conn_end_on_a: msg - .proof_ack - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, + conn_id_on_b: msg.connection_id.parse()?, + proof_conn_end_on_a: msg.proof_ack.try_into()?, proof_height_on_a: msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ConnectionError::MissingProofHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg conn open confirm proof height", + ))?, signer: msg.signer.into(), }) } diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs index 7d9affcfd..57790a387 100644 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs +++ b/ibc-core/ics03-connection/types/src/msgs/conn_open_init.rs @@ -1,5 +1,6 @@ use core::time::Duration; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -7,7 +8,6 @@ use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnect use ibc_proto::Protobuf; use crate::connection::Counterparty; -use crate::error::ConnectionError; use crate::version::Version; pub const CONN_OPEN_INIT_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenInit"; @@ -50,7 +50,10 @@ mod borsh_impls { self.delay_period.as_nanos().try_into().map_err(|_| { io::Error::new( io::ErrorKind::Other, - format!("Duration too long: {} nanos", self.delay_period.as_nanos()), + format!( + "Duration too long: `{}` nanos", + self.delay_period.as_nanos() + ), ) })?; @@ -84,21 +87,24 @@ mod borsh_impls { impl Protobuf for MsgConnectionOpenInit {} impl TryFrom for MsgConnectionOpenInit { - type Error = ConnectionError; + type Error = DecodingError; fn try_from(msg: RawMsgConnectionOpenInit) -> Result { let counterparty: Counterparty = msg .counterparty - .ok_or(ConnectionError::MissingCounterparty)? + .ok_or(DecodingError::missing_raw_data( + "msg conn open init counterparty", + ))? .try_into()?; - counterparty.verify_empty_connection_id()?; + if let Some(cid) = counterparty.connection_id() { + return Err(DecodingError::invalid_raw_data(format!( + "expected msg conn open init connection ID to be empty, actual `{cid}`", + ))); + } Ok(Self { - client_id_on_a: msg - .client_id - .parse() - .map_err(ConnectionError::InvalidIdentifier)?, + client_id_on_a: msg.client_id.parse()?, counterparty, version: msg.version.map(TryInto::try_into).transpose()?, delay_period: Duration::from_nanos(msg.delay_period), diff --git a/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs b/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs index 6686b806a..ea5f22f03 100644 --- a/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs +++ b/ibc-core/ics03-connection/types/src/msgs/conn_open_try.rs @@ -2,6 +2,7 @@ use core::time::Duration; use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -10,7 +11,6 @@ use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenTry as RawMsgConnecti use ibc_proto::Protobuf; use crate::connection::Counterparty; -use crate::error::ConnectionError; use crate::version::Version; pub const CONN_OPEN_TRY_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenTry"; @@ -144,7 +144,7 @@ mod borsh_impls { impl Protobuf for MsgConnectionOpenTry {} impl TryFrom for MsgConnectionOpenTry { - type Error = ConnectionError; + type Error = DecodingError; fn try_from(msg: RawMsgConnectionOpenTry) -> Result { let counterparty_versions = msg @@ -154,7 +154,9 @@ impl TryFrom for MsgConnectionOpenTry { .collect::, _>>()?; if counterparty_versions.is_empty() { - return Err(ConnectionError::EmptyVersions); + return Err(DecodingError::missing_raw_data( + "msg conn open try connection versions", + )); } // We set the deprecated `previous_connection_id` field so that we can @@ -162,48 +164,38 @@ 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(ConnectionError::InvalidIdentifier)?, - client_state_of_b_on_a: msg - .client_state - .ok_or(ConnectionError::MissingClientState)?, + client_id_on_b: msg.client_id.parse()?, + client_state_of_b_on_a: msg.client_state.ok_or(DecodingError::missing_raw_data( + "msg conn open try client state", + ))?, counterparty: msg .counterparty - .ok_or(ConnectionError::MissingCounterparty)? + .ok_or(DecodingError::missing_raw_data( + "msg conn open try counterparty", + ))? .try_into()?, versions_on_a: counterparty_versions, - proof_conn_end_on_a: msg - .proof_init - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, - proof_client_state_of_b_on_a: msg - .proof_client - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, - proof_consensus_state_of_b_on_a: msg - .proof_consensus - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, + proof_conn_end_on_a: msg.proof_init.try_into()?, + proof_client_state_of_b_on_a: msg.proof_client.try_into()?, + proof_consensus_state_of_b_on_a: msg.proof_consensus.try_into()?, proofs_height_on_a: msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ConnectionError::MissingProofHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg conn open try proof height", + ))?, consensus_height_of_b_on_a: msg .consensus_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ConnectionError::MissingConsensusHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg conn open try consensus height", + ))?, delay_period: Duration::from_nanos(msg.delay_period), signer: msg.signer.into(), proof_consensus_state_of_b: if msg.host_consensus_state_proof.is_empty() { None } else { - Some( - msg.host_consensus_state_proof - .try_into() - .map_err(|_| ConnectionError::InvalidProof)?, - ) + Some(msg.host_consensus_state_proof.try_into()?) }, }) } diff --git a/ibc-core/ics03-connection/types/src/version.rs b/ibc-core/ics03-connection/types/src/version.rs index a4446d113..89f577cc0 100644 --- a/ibc-core/ics03-connection/types/src/version.rs +++ b/ibc-core/ics03-connection/types/src/version.rs @@ -2,6 +2,7 @@ use core::fmt::Display; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::utils::PrettySlice; use ibc_proto::ibc::core::connection::v1::Version as RawVersion; @@ -42,7 +43,7 @@ impl Version { let maybe_supported_version = find_supported_version(self, supported_versions)?; if self.features.is_empty() { - return Err(ConnectionError::EmptyFeatures); + return Err(ConnectionError::MissingFeatures); } for feature in self.features.iter() { @@ -54,7 +55,7 @@ impl Version { /// Checks whether the given feature is supported in this version pub fn verify_feature_supported(&self, feature: String) -> Result<(), ConnectionError> { if !self.features.contains(&feature) { - return Err(ConnectionError::FeatureNotSupported { feature }); + return Err(ConnectionError::MissingFeatures); } Ok(()) } @@ -71,14 +72,15 @@ impl Version { impl Protobuf for Version {} impl TryFrom for Version { - type Error = ConnectionError; + type Error = DecodingError; + fn try_from(value: RawVersion) -> Result { if value.identifier.trim().is_empty() { - return Err(ConnectionError::EmptyVersions); + return Err(DecodingError::missing_raw_data("version identifier")); } for feature in value.features.iter() { if feature.trim().is_empty() { - return Err(ConnectionError::EmptyFeatures); + return Err(DecodingError::missing_raw_data("version features")); } } Ok(Version { @@ -116,12 +118,13 @@ impl Display for Version { /// compatible version continues. This function is called in the `conn_open_try` /// handshake procedure. /// -/// NOTE: Empty feature set is not currently allowed for a chosen version. +/// NOTE: Empty feature sets are not currently allowed for a chosen version. pub fn pick_version( supported_versions: &[Version], counterparty_versions: &[Version], ) -> Result { let mut intersection: Vec = Vec::new(); + for sv in supported_versions.iter() { if let Ok(cv) = find_supported_version(sv, counterparty_versions) { if let Ok(feature_set) = get_feature_set_intersection(&sv.features, &cv.features) { @@ -134,10 +137,11 @@ pub fn pick_version( } if intersection.is_empty() { - return Err(ConnectionError::NoCommonVersion); + return Err(ConnectionError::MissingCommonVersion); } intersection.sort_by(|a, b| a.identifier.cmp(&b.identifier)); + Ok(intersection[0].clone()) } @@ -150,9 +154,7 @@ fn find_supported_version( supported_versions .iter() .find(|sv| sv.identifier == version.identifier) - .ok_or(ConnectionError::VersionNotSupported { - version: version.clone(), - }) + .ok_or(ConnectionError::MissingCommonVersion) .cloned() } @@ -171,7 +173,7 @@ fn get_feature_set_intersection( .collect(); if feature_set_intersection.is_empty() { - return Err(ConnectionError::NoCommonFeatures); + return Err(ConnectionError::MissingFeatures); } Ok(feature_set_intersection) @@ -368,7 +370,7 @@ mod tests { name: "Disjoint versions".to_string(), supported: disjoint().0, counterparty: disjoint().1, - picked: Err(ConnectionError::NoCommonVersion), + picked: Err(ConnectionError::MissingCommonVersion), want_pass: false, }, ]; diff --git a/ibc-core/ics04-channel/src/context.rs b/ibc-core/ics04-channel/src/context.rs index 1fba1de8f..92844b4d4 100644 --- a/ibc-core/ics04-channel/src/context.rs +++ b/ibc-core/ics04-channel/src/context.rs @@ -4,8 +4,8 @@ use ibc_core_channel_types::channel::ChannelEnd; use ibc_core_channel_types::commitment::PacketCommitment; use ibc_core_client::context::prelude::*; use ibc_core_connection::types::ConnectionEnd; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::IbcEvent; +use ibc_core_host::types::error::HostError; use ibc_core_host::types::identifiers::{ConnectionId, Sequence}; use ibc_core_host::types::path::{ChannelEndPath, CommitmentPath, SeqSendPath}; use ibc_core_host::{ExecutionContext, ValidationContext}; @@ -19,13 +19,12 @@ pub trait SendPacketValidationContext { fn get_client_validation_context(&self) -> &Self::V; /// Returns the ChannelEnd for the given `port_id` and `chan_id`. - fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result; + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> 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 get_next_sequence_send(&self, seq_send_path: &SeqSendPath) - -> Result; + fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) -> Result; } impl SendPacketValidationContext for T @@ -38,18 +37,15 @@ where self.get_client_validation_context() } - fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { self.channel_end(channel_end_path) } - fn connection_end(&self, connection_id: &ConnectionId) -> Result { + fn connection_end(&self, connection_id: &ConnectionId) -> Result { self.connection_end(connection_id) } - fn get_next_sequence_send( - &self, - seq_send_path: &SeqSendPath, - ) -> Result { + fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) -> Result { self.get_next_sequence_send(seq_send_path) } } @@ -60,19 +56,19 @@ pub trait SendPacketExecutionContext: SendPacketValidationContext { &mut self, seq_send_path: &SeqSendPath, seq: Sequence, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; fn store_packet_commitment( &mut self, commitment_path: &CommitmentPath, commitment: PacketCommitment, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Ibc events - fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError>; + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError>; /// Logging facility - fn log_message(&mut self, message: String) -> Result<(), ContextError>; + fn log_message(&mut self, message: String) -> Result<(), HostError>; } impl SendPacketExecutionContext for T @@ -83,7 +79,7 @@ where &mut self, seq_send_path: &SeqSendPath, seq: Sequence, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.store_next_sequence_send(seq_send_path, seq) } @@ -91,15 +87,15 @@ where &mut self, commitment_path: &CommitmentPath, commitment: PacketCommitment, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.store_packet_commitment(commitment_path, commitment) } - fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError> { self.emit_ibc_event(event) } - fn log_message(&mut self, message: String) -> Result<(), ContextError> { + fn log_message(&mut self, message: String) -> Result<(), HostError> { self.log_message(message) } } diff --git a/ibc-core/ics04-channel/src/handler/acknowledgement.rs b/ibc-core/ics04-channel/src/handler/acknowledgement.rs index c58edc74a..fddea0d82 100644 --- a/ibc-core/ics04-channel/src/handler/acknowledgement.rs +++ b/ibc-core/ics04-channel/src/handler/acknowledgement.rs @@ -1,12 +1,11 @@ use ibc_core_channel_types::channel::{Counterparty, Order, State as ChannelState}; use ibc_core_channel_types::commitment::{compute_ack_commitment, compute_packet_commitment}; -use ibc_core_channel_types::error::{ChannelError, PacketError}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::AcknowledgePacket; use ibc_core_channel_types::msgs::MsgAcknowledgement; use ibc_core_client::context::prelude::*; use ibc_core_connection::delay::verify_conn_delay_passed; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, SeqAckPath, @@ -19,22 +18,20 @@ pub fn acknowledgement_packet_validate( ctx_a: &ValCtx, module: &dyn Module, msg: MsgAcknowledgement, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { validate(ctx_a, &msg)?; - module - .on_acknowledgement_packet_validate(&msg.packet, &msg.acknowledgement, &msg.signer) - .map_err(ContextError::PacketError) + module.on_acknowledgement_packet_validate(&msg.packet, &msg.acknowledgement, &msg.signer) } pub fn acknowledgement_packet_execute( ctx_a: &mut ExecCtx, module: &mut dyn Module, msg: MsgAcknowledgement, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -103,7 +100,7 @@ where Ok(()) } -fn validate(ctx_a: &Ctx, msg: &MsgAcknowledgement) -> Result<(), ContextError> +fn validate(ctx_a: &Ctx, msg: &MsgAcknowledgement) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -139,28 +136,27 @@ where return Ok(()); }; - if commitment_on_a - != compute_packet_commitment( - &packet.data, - &packet.timeout_height_on_b, - &packet.timeout_timestamp_on_b, - ) - { - return Err(PacketError::IncorrectPacketCommitment { - sequence: packet.seq_on_a, - } - .into()); + let expected_commitment_on_a = compute_packet_commitment( + &packet.data, + &packet.timeout_height_on_b, + &packet.timeout_timestamp_on_b, + ); + + if commitment_on_a != expected_commitment_on_a { + return Err(ChannelError::MismatchedPacketCommitment { + actual: commitment_on_a, + expected: expected_commitment_on_a, + }); } if let Order::Ordered = chan_end_on_a.ordering { let seq_ack_path_on_a = SeqAckPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); let next_seq_ack = ctx_a.get_next_sequence_ack(&seq_ack_path_on_a)?; if packet.seq_on_a != next_seq_ack { - return Err(PacketError::InvalidPacketSequence { - given_sequence: packet.seq_on_a, - next_sequence: next_seq_ack, - } - .into()); + return Err(ChannelError::MismatchedPacketSequence { + actual: packet.seq_on_a, + expected: next_seq_ack, + }); } } @@ -175,6 +171,7 @@ where client_state_of_b_on_a .status(ctx_a.get_client_validation_context(), client_id_on_a)? .verify_is_active()?; + client_state_of_b_on_a.validate_proof_height(msg.proof_height_on_b)?; let client_cons_state_path_on_a = ClientConsensusStatePath::new( @@ -191,19 +188,13 @@ where verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?; // Verify the proof for the packet against the chain store. - client_state_of_b_on_a - .verify_membership( - conn_end_on_a.counterparty().prefix(), - &msg.proof_acked_on_b, - consensus_state_of_b_on_a.root(), - Path::Ack(ack_path_on_b), - ack_commitment.into_vec(), - ) - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: packet.seq_on_a, - client_error: e, - }) - .map_err(PacketError::Channel)?; + client_state_of_b_on_a.verify_membership( + conn_end_on_a.counterparty().prefix(), + &msg.proof_acked_on_b, + consensus_state_of_b_on_a.root(), + Path::Ack(ack_path_on_b), + ack_commitment.into_vec(), + )?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs b/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs index 5385d18c3..798f628e1 100644 --- a/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs +++ b/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs @@ -5,8 +5,8 @@ use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::CloseConfirm; use ibc_core_channel_types::msgs::MsgChannelCloseConfirm; use ibc_core_client::context::prelude::*; +use ibc_core_connection::types::error::ConnectionError; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; use ibc_core_host::{ExecutionContext, ValidationContext}; @@ -18,7 +18,7 @@ pub fn chan_close_confirm_validate( ctx_b: &ValCtx, module: &dyn Module, msg: MsgChannelCloseConfirm, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -33,7 +33,7 @@ pub fn chan_close_confirm_execute( ctx_b: &mut ExecCtx, module: &mut dyn Module, msg: MsgChannelCloseConfirm, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -61,11 +61,7 @@ where .counterparty() .channel_id .clone() - .ok_or(ContextError::ChannelError(ChannelError::Other { - description: - "internal error: ChannelEnd doesn't have a counterparty channel id in CloseInit" - .to_string(), - }))?; + .ok_or(ChannelError::MissingCounterparty)?; let conn_id_on_b = chan_end_on_b.connection_hops[0].clone(); IbcEvent::CloseConfirmChannel(CloseConfirm::new( @@ -91,7 +87,7 @@ where Ok(()) } -fn validate(ctx_b: &Ctx, msg: &MsgChannelCloseConfirm) -> Result<(), ContextError> +fn validate(ctx_b: &Ctx, msg: &MsgChannelCloseConfirm) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -119,6 +115,7 @@ where client_state_of_a_on_b .status(ctx_b.get_client_validation_context(), client_id_on_b)? .verify_is_active()?; + client_state_of_a_on_b.validate_proof_height(msg.proof_height_on_a)?; let client_cons_state_path_on_b = ClientConsensusStatePath::new( @@ -134,11 +131,10 @@ where .counterparty() .channel_id() .ok_or(ChannelError::MissingCounterparty)?; - 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(), - }, - )?; + let conn_id_on_a = conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_a = ChannelEnd::new( ChannelState::Closed, @@ -151,15 +147,13 @@ where // Verify the proof for the channel state against the expected channel end. // A counterparty channel id of None in not possible, and is checked by validate_basic in msg. - client_state_of_a_on_b - .verify_membership( - prefix_on_a, - &msg.proof_chan_end_on_a, - consensus_state_of_a_on_b.root(), - Path::ChannelEnd(chan_end_path_on_a), - expected_chan_end_on_a.encode_vec(), - ) - .map_err(ChannelError::VerifyChannelFailed)?; + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_chan_end_on_a, + consensus_state_of_a_on_b.root(), + Path::ChannelEnd(chan_end_path_on_a), + expected_chan_end_on_a.encode_vec(), + )?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_close_init.rs b/ibc-core/ics04-channel/src/handler/chan_close_init.rs index 15c38c9f5..6a0a6e189 100644 --- a/ibc-core/ics04-channel/src/handler/chan_close_init.rs +++ b/ibc-core/ics04-channel/src/handler/chan_close_init.rs @@ -5,7 +5,6 @@ use ibc_core_channel_types::events::CloseInit; use ibc_core_channel_types::msgs::MsgChannelCloseInit; use ibc_core_client::context::prelude::*; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::ChannelEndPath; use ibc_core_host::{ExecutionContext, ValidationContext}; @@ -16,7 +15,7 @@ pub fn chan_close_init_validate( ctx_a: &ValCtx, module: &dyn Module, msg: MsgChannelCloseInit, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -31,7 +30,7 @@ pub fn chan_close_init_execute( ctx_a: &mut ExecCtx, module: &mut dyn Module, msg: MsgChannelCloseInit, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -60,11 +59,7 @@ where .counterparty() .channel_id .clone() - .ok_or(ContextError::ChannelError(ChannelError::Other { - description: - "internal error: ChannelEnd doesn't have a counterparty channel id in CloseInit" - .to_string(), - }))?; + .ok_or(ChannelError::MissingCounterparty)?; let conn_id_on_a = chan_end_on_a.connection_hops[0].clone(); IbcEvent::CloseInitChannel(CloseInit::new( @@ -90,7 +85,7 @@ where Ok(()) } -fn validate(ctx_a: &Ctx, msg: &MsgChannelCloseInit) -> Result<(), ContextError> +fn validate(ctx_a: &Ctx, msg: &MsgChannelCloseInit) -> Result<(), ChannelError> where Ctx: ValidationContext, { diff --git a/ibc-core/ics04-channel/src/handler/chan_open_ack.rs b/ibc-core/ics04-channel/src/handler/chan_open_ack.rs index 7e5b6b240..c6d81a9fe 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_ack.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_ack.rs @@ -4,8 +4,8 @@ use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::OpenAck; use ibc_core_channel_types::msgs::MsgChannelOpenAck; use ibc_core_client::context::prelude::*; +use ibc_core_connection::types::error::ConnectionError; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; use ibc_core_host::{ExecutionContext, ValidationContext}; @@ -17,7 +17,7 @@ pub fn chan_open_ack_validate( ctx_a: &ValCtx, module: &dyn Module, msg: MsgChannelOpenAck, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -32,7 +32,7 @@ pub fn chan_open_ack_execute( ctx_a: &mut ExecCtx, module: &mut dyn Module, msg: MsgChannelOpenAck, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -86,7 +86,7 @@ where Ok(()) } -fn validate(ctx_a: &Ctx, msg: &MsgChannelOpenAck) -> Result<(), ContextError> +fn validate(ctx_a: &Ctx, msg: &MsgChannelOpenAck) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -114,6 +114,7 @@ where client_state_of_b_on_a .status(ctx_a.get_client_validation_context(), client_id_on_a)? .verify_is_active()?; + client_state_of_b_on_a.validate_proof_height(msg.proof_height_on_b)?; let client_cons_state_path_on_a = ClientConsensusStatePath::new( @@ -125,11 +126,10 @@ where client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; 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( - ChannelError::UndefinedConnectionCounterparty { - connection_id: chan_end_on_a.connection_hops()[0].clone(), - }, - )?; + let conn_id_on_b = conn_end_on_a + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_b = ChannelEnd::new( ChannelState::TryOpen, @@ -144,15 +144,13 @@ where // Verify the proof for the channel state against the expected channel end. // A counterparty channel id of None in not possible, and is checked by validate_basic in msg. - client_state_of_b_on_a - .verify_membership( - prefix_on_b, - &msg.proof_chan_end_on_b, - consensus_state_of_b_on_a.root(), - Path::ChannelEnd(chan_end_path_on_b), - expected_chan_end_on_b.encode_vec(), - ) - .map_err(ChannelError::VerifyChannelFailed)?; + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_chan_end_on_b, + consensus_state_of_b_on_a.root(), + Path::ChannelEnd(chan_end_path_on_b), + expected_chan_end_on_b.encode_vec(), + )?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs b/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs index dfbc777c1..2f1fc56d3 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs @@ -5,8 +5,8 @@ use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::OpenConfirm; use ibc_core_channel_types::msgs::MsgChannelOpenConfirm; use ibc_core_client::context::prelude::*; +use ibc_core_connection::types::error::ConnectionError; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; use ibc_core_host::{ExecutionContext, ValidationContext}; @@ -18,7 +18,7 @@ pub fn chan_open_confirm_validate( ctx_b: &ValCtx, module: &dyn Module, msg: MsgChannelOpenConfirm, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -33,7 +33,7 @@ pub fn chan_open_confirm_execute( ctx_b: &mut ExecCtx, module: &mut dyn Module, msg: MsgChannelOpenConfirm, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -62,11 +62,7 @@ where .counterparty() .channel_id .clone() - .ok_or(ContextError::ChannelError(ChannelError::Other { - description: - "internal error: ChannelEnd doesn't have a counterparty channel id in OpenConfirm" - .to_string(), - }))?; + .ok_or(ChannelError::MissingCounterparty)?; let core_event = IbcEvent::OpenConfirmChannel(OpenConfirm::new( msg.port_id_on_b.clone(), @@ -90,7 +86,7 @@ where Ok(()) } -fn validate(ctx_b: &Ctx, msg: &MsgChannelOpenConfirm) -> Result<(), ContextError> +fn validate(ctx_b: &Ctx, msg: &MsgChannelOpenConfirm) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -119,6 +115,7 @@ where client_state_of_a_on_b .status(ctx_b.get_client_validation_context(), client_id_on_b)? .verify_is_active()?; + client_state_of_a_on_b.validate_proof_height(msg.proof_height_on_a)?; let client_cons_state_path_on_b = ClientConsensusStatePath::new( @@ -134,11 +131,10 @@ where .counterparty() .channel_id() .ok_or(ChannelError::MissingCounterparty)?; - 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(), - }, - )?; + let conn_id_on_a = conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_a = ChannelEnd::new( ChannelState::Open, @@ -151,15 +147,13 @@ where // Verify the proof for the channel state against the expected channel end. // A counterparty channel id of None in not possible, and is checked in msg. - client_state_of_a_on_b - .verify_membership( - prefix_on_a, - &msg.proof_chan_end_on_a, - consensus_state_of_a_on_b.root(), - Path::ChannelEnd(chan_end_path_on_a), - expected_chan_end_on_a.encode_vec(), - ) - .map_err(ChannelError::VerifyChannelFailed)?; + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_chan_end_on_a, + consensus_state_of_a_on_b.root(), + Path::ChannelEnd(chan_end_path_on_a), + expected_chan_end_on_a.encode_vec(), + )?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_open_init.rs b/ibc-core/ics04-channel/src/handler/chan_open_init.rs index 58a161d0b..fe931efa0 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_init.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_init.rs @@ -1,10 +1,10 @@ //! Protocol logic specific to ICS4 messages of type `MsgChannelOpenInit`. use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::OpenInit; use ibc_core_channel_types::msgs::MsgChannelOpenInit; use ibc_core_client::context::prelude::*; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::ChannelId; use ibc_core_host::types::path::{ChannelEndPath, SeqAckPath, SeqRecvPath, SeqSendPath}; @@ -16,7 +16,7 @@ pub fn chan_open_init_validate( ctx_a: &ValCtx, module: &dyn Module, msg: MsgChannelOpenInit, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -39,7 +39,7 @@ pub fn chan_open_init_execute( ctx_a: &mut ExecCtx, module: &mut dyn Module, msg: MsgChannelOpenInit, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -107,7 +107,7 @@ where Ok(()) } -fn validate(ctx_a: &Ctx, msg: &MsgChannelOpenInit) -> Result<(), ContextError> +fn validate(ctx_a: &Ctx, msg: &MsgChannelOpenInit) -> Result<(), ChannelError> where Ctx: ValidationContext, { diff --git a/ibc-core/ics04-channel/src/handler/chan_open_try.rs b/ibc-core/ics04-channel/src/handler/chan_open_try.rs index 08c82deac..c8e735700 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_try.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_try.rs @@ -5,8 +5,8 @@ use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::OpenTry; use ibc_core_channel_types::msgs::MsgChannelOpenTry; use ibc_core_client::context::prelude::*; +use ibc_core_connection::types::error::ConnectionError; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::ChannelId; use ibc_core_host::types::path::{ @@ -21,7 +21,7 @@ pub fn chan_open_try_validate( ctx_b: &ValCtx, module: &dyn Module, msg: MsgChannelOpenTry, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -45,7 +45,7 @@ pub fn chan_open_try_execute( ctx_b: &mut ExecCtx, module: &mut dyn Module, msg: MsgChannelOpenTry, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -115,7 +115,7 @@ where Ok(()) } -fn validate(ctx_b: &Ctx, msg: &MsgChannelOpenTry) -> Result<(), ContextError> +fn validate(ctx_b: &Ctx, msg: &MsgChannelOpenTry) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -153,11 +153,10 @@ where let prefix_on_a = conn_end_on_b.counterparty().prefix(); let port_id_on_a = msg.port_id_on_a.clone(); let chan_id_on_a = msg.chan_id_on_a.clone(); - let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( - ChannelError::UndefinedConnectionCounterparty { - connection_id: msg.connection_hops_on_b[0].clone(), - }, - )?; + let conn_id_on_a = conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_a = ChannelEnd::new( ChannelState::Init, @@ -170,15 +169,13 @@ where // Verify the proof for the channel state against the expected channel end. // A counterparty channel id of None in not possible, and is checked by validate_basic in msg. - client_state_of_a_on_b - .verify_membership( - prefix_on_a, - &msg.proof_chan_end_on_a, - consensus_state_of_a_on_b.root(), - Path::ChannelEnd(chan_end_path_on_a), - expected_chan_end_on_a.encode_vec(), - ) - .map_err(ChannelError::VerifyChannelFailed)?; + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_chan_end_on_a, + consensus_state_of_a_on_b.root(), + Path::ChannelEnd(chan_end_path_on_a), + expected_chan_end_on_a.encode_vec(), + )?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/recv_packet.rs b/ibc-core/ics04-channel/src/handler/recv_packet.rs index 781b142f0..60bf30199 100644 --- a/ibc-core/ics04-channel/src/handler/recv_packet.rs +++ b/ibc-core/ics04-channel/src/handler/recv_packet.rs @@ -1,13 +1,12 @@ use ibc_core_channel_types::channel::{Counterparty, Order, State as ChannelState}; use ibc_core_channel_types::commitment::{compute_ack_commitment, compute_packet_commitment}; -use ibc_core_channel_types::error::{ChannelError, PacketError}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::{ReceivePacket, WriteAcknowledgement}; use ibc_core_channel_types::msgs::MsgRecvPacket; use ibc_core_channel_types::packet::Receipt; use ibc_core_client::context::prelude::*; use ibc_core_connection::delay::verify_conn_delay_passed; use ibc_core_connection::types::State as ConnectionState; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, @@ -17,7 +16,7 @@ use ibc_core_host::{ExecutionContext, ValidationContext}; use ibc_core_router::module::Module; use ibc_primitives::prelude::*; -pub fn recv_packet_validate(ctx_b: &ValCtx, msg: MsgRecvPacket) -> Result<(), ContextError> +pub fn recv_packet_validate(ctx_b: &ValCtx, msg: MsgRecvPacket) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -32,7 +31,7 @@ pub fn recv_packet_execute( ctx_b: &mut ExecCtx, module: &mut dyn Module, msg: MsgRecvPacket, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -136,7 +135,7 @@ where Ok(()) } -fn validate(ctx_b: &Ctx, msg: &MsgRecvPacket) -> Result<(), ContextError> +fn validate(ctx_b: &Ctx, msg: &MsgRecvPacket) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -162,11 +161,10 @@ where let latest_height = ctx_b.host_height()?; if msg.packet.timeout_height_on_b.has_expired(latest_height) { - return Err(PacketError::LowPacketHeight { + return Err(ChannelError::InsufficientPacketHeight { chain_height: latest_height, timeout_height: msg.packet.timeout_height_on_b, - } - .into()); + }); } let latest_timestamp = ctx_b.host_timestamp()?; @@ -175,7 +173,7 @@ where .timeout_timestamp_on_b .has_expired(&latest_timestamp) { - return Err(PacketError::LowPacketTimestamp.into()); + return Err(ChannelError::ExpiredPacketTimestamp); } // Verify proofs @@ -213,19 +211,13 @@ where verify_conn_delay_passed(ctx_b, msg.proof_height_on_a, &conn_end_on_b)?; // Verify the proof for the packet against the chain store. - client_state_of_a_on_b - .verify_membership( - conn_end_on_b.counterparty().prefix(), - &msg.proof_commitment_on_a, - consensus_state_of_a_on_b.root(), - Path::Commitment(commitment_path_on_a), - expected_commitment_on_a.into_vec(), - ) - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: msg.packet.seq_on_a, - client_error: e, - }) - .map_err(PacketError::Channel)?; + client_state_of_a_on_b.verify_membership( + conn_end_on_b.counterparty().prefix(), + &msg.proof_commitment_on_a, + consensus_state_of_a_on_b.root(), + Path::Commitment(commitment_path_on_a), + expected_commitment_on_a.into_vec(), + )?; } match chan_end_on_b.ordering { @@ -234,11 +226,10 @@ where SeqRecvPath::new(&msg.packet.port_id_on_b, &msg.packet.chan_id_on_b); let next_seq_recv = ctx_b.get_next_sequence_recv(&seq_recv_path_on_b)?; if msg.packet.seq_on_a > next_seq_recv { - return Err(PacketError::InvalidPacketSequence { - given_sequence: msg.packet.seq_on_a, - next_sequence: next_seq_recv, - } - .into()); + return Err(ChannelError::MismatchedPacketSequence { + actual: msg.packet.seq_on_a, + expected: next_seq_recv, + }); } if msg.packet.seq_on_a == next_seq_recv { @@ -258,27 +249,24 @@ where validate_write_acknowledgement(ctx_b, msg)?; } Order::None => { - return Err(ContextError::ChannelError(ChannelError::InvalidOrderType { - expected: "Channel ordering cannot be None".to_string(), + return Err(ChannelError::InvalidState { + expected: "Channel ordering to not be None".to_string(), actual: chan_end_on_b.ordering.to_string(), - })) + }) } } Ok(()) } -fn validate_write_acknowledgement(ctx_b: &Ctx, msg: &MsgRecvPacket) -> Result<(), ContextError> +fn validate_write_acknowledgement(ctx_b: &Ctx, msg: &MsgRecvPacket) -> Result<(), ChannelError> where Ctx: ValidationContext, { let packet = msg.packet.clone(); let ack_path_on_b = AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a); if ctx_b.get_packet_acknowledgement(&ack_path_on_b).is_ok() { - return Err(PacketError::AcknowledgementExists { - sequence: msg.packet.seq_on_a, - } - .into()); + return Err(ChannelError::DuplicateAcknowledgment(msg.packet.seq_on_a)); } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/send_packet.rs b/ibc-core/ics04-channel/src/handler/send_packet.rs index 514ffb89d..1206d0460 100644 --- a/ibc-core/ics04-channel/src/handler/send_packet.rs +++ b/ibc-core/ics04-channel/src/handler/send_packet.rs @@ -1,10 +1,9 @@ use ibc_core_channel_types::channel::Counterparty; use ibc_core_channel_types::commitment::compute_packet_commitment; -use ibc_core_channel_types::error::PacketError; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::SendPacket; use ibc_core_channel_types::packet::Packet; use ibc_core_client::context::prelude::*; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, SeqSendPath, @@ -19,7 +18,7 @@ use crate::context::{SendPacketExecutionContext, SendPacketValidationContext}; pub fn send_packet( ctx_a: &mut impl SendPacketExecutionContext, packet: Packet, -) -> Result<(), ContextError> { +) -> Result<(), ChannelError> { send_packet_validate(ctx_a, &packet)?; send_packet_execute(ctx_a, packet) } @@ -28,9 +27,9 @@ pub fn send_packet( pub fn send_packet_validate( ctx_a: &impl SendPacketValidationContext, packet: &Packet, -) -> Result<(), ContextError> { +) -> Result<(), ChannelError> { if !packet.timeout_height_on_b.is_set() && !packet.timeout_timestamp_on_b.is_set() { - return Err(ContextError::PacketError(PacketError::MissingTimeout)); + return Err(ChannelError::MissingTimeout); } let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); @@ -64,11 +63,10 @@ pub fn send_packet_validate( let latest_height_on_a = client_state_of_b_on_a.latest_height(); if packet.timeout_height_on_b.has_expired(latest_height_on_a) { - return Err(PacketError::LowPacketHeight { + return Err(ChannelError::InsufficientPacketHeight { chain_height: latest_height_on_a, timeout_height: packet.timeout_height_on_b, - } - .into()); + }); } let client_cons_state_path_on_a = ClientConsensusStatePath::new( @@ -81,18 +79,17 @@ pub fn send_packet_validate( let latest_timestamp = consensus_state_of_b_on_a.timestamp(); let packet_timestamp = packet.timeout_timestamp_on_b; if packet_timestamp.has_expired(&latest_timestamp) { - return Err(PacketError::LowPacketTimestamp.into()); + return Err(ChannelError::ExpiredPacketTimestamp); } let seq_send_path_on_a = SeqSendPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); let next_seq_send_on_a = ctx_a.get_next_sequence_send(&seq_send_path_on_a)?; if packet.seq_on_a != next_seq_send_on_a { - return Err(PacketError::InvalidPacketSequence { - given_sequence: packet.seq_on_a, - next_sequence: next_seq_send_on_a, - } - .into()); + return Err(ChannelError::MismatchedPacketSequence { + actual: packet.seq_on_a, + expected: next_seq_send_on_a, + }); } Ok(()) @@ -104,7 +101,7 @@ pub fn send_packet_validate( pub fn send_packet_execute( ctx_a: &mut impl SendPacketExecutionContext, packet: Packet, -) -> Result<(), ContextError> { +) -> Result<(), ChannelError> { { let seq_send_path_on_a = SeqSendPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); let next_seq_send_on_a = ctx_a.get_next_sequence_send(&seq_send_path_on_a)?; diff --git a/ibc-core/ics04-channel/src/handler/timeout.rs b/ibc-core/ics04-channel/src/handler/timeout.rs index ef478ed4e..33604b958 100644 --- a/ibc-core/ics04-channel/src/handler/timeout.rs +++ b/ibc-core/ics04-channel/src/handler/timeout.rs @@ -1,11 +1,10 @@ use ibc_core_channel_types::channel::{Counterparty, Order, State}; use ibc_core_channel_types::commitment::compute_packet_commitment; -use ibc_core_channel_types::error::{ChannelError, PacketError}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::events::{ChannelClosed, TimeoutPacket}; use ibc_core_channel_types::msgs::{MsgTimeout, MsgTimeoutOnClose}; use ibc_core_client::context::prelude::*; use ibc_core_connection::delay::verify_conn_delay_passed; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, @@ -25,7 +24,7 @@ pub fn timeout_packet_validate( ctx_a: &ValCtx, module: &dyn Module, timeout_msg_type: TimeoutMsgType, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ValCtx: ValidationContext, { @@ -39,16 +38,14 @@ where TimeoutMsgType::TimeoutOnClose(msg) => (msg.packet, msg.signer), }; - module - .on_timeout_packet_validate(&packet, &signer) - .map_err(ContextError::PacketError) + module.on_timeout_packet_validate(&packet, &signer) } pub fn timeout_packet_execute( ctx_a: &mut ExecCtx, module: &mut dyn Module, timeout_msg_type: TimeoutMsgType, -) -> Result<(), ContextError> +) -> Result<(), ChannelError> where ExecCtx: ExecutionContext, { @@ -126,7 +123,7 @@ where Ok(()) } -fn validate(ctx_a: &Ctx, msg: &MsgTimeout) -> Result<(), ContextError> +fn validate(ctx_a: &Ctx, msg: &MsgTimeout) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -168,11 +165,12 @@ where &msg.packet.timeout_height_on_b, &msg.packet.timeout_timestamp_on_b, ); + if commitment_on_a != expected_commitment_on_a { - return Err(PacketError::IncorrectPacketCommitment { - sequence: msg.packet.seq_on_a, - } - .into()); + return Err(ChannelError::MismatchedPacketCommitment { + expected: expected_commitment_on_a, + actual: commitment_on_a, + }); } // Verify proofs @@ -198,13 +196,12 @@ where let timestamp_of_b = consensus_state_of_b_on_a.timestamp(); if !msg.packet.timed_out(×tamp_of_b, msg.proof_height_on_b) { - return Err(PacketError::PacketTimeoutNotReached { + return Err(ChannelError::InsufficientPacketTimeout { timeout_height: msg.packet.timeout_height_on_b, chain_height: msg.proof_height_on_b, timeout_timestamp: msg.packet.timeout_timestamp_on_b, chain_timestamp: timestamp_of_b, - } - .into()); + }); } verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?; @@ -212,11 +209,10 @@ where let next_seq_recv_verification_result = match chan_end_on_a.ordering { Order::Ordered => { if msg.packet.seq_on_a < msg.next_seq_recv_on_b { - return Err(PacketError::InvalidPacketSequence { - given_sequence: msg.packet.seq_on_a, - next_sequence: msg.next_seq_recv_on_b, - } - .into()); + return Err(ChannelError::MismatchedPacketSequence { + actual: msg.packet.seq_on_a, + expected: msg.next_seq_recv_on_b, + }); } let seq_recv_path_on_b = SeqRecvPath::new(&msg.packet.port_id_on_b, &msg.packet.chan_id_on_b); @@ -244,19 +240,14 @@ where ) } Order::None => { - return Err(ContextError::ChannelError(ChannelError::InvalidOrderType { - expected: "Channel ordering cannot be None".to_string(), + return Err(ChannelError::InvalidState { + expected: "Channel ordering to not be None".to_string(), actual: chan_end_on_a.ordering.to_string(), - })) + }) } }; - next_seq_recv_verification_result - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: msg.next_seq_recv_on_b, - client_error: e, - }) - .map_err(PacketError::Channel)?; + next_seq_recv_verification_result?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/timeout_on_close.rs b/ibc-core/ics04-channel/src/handler/timeout_on_close.rs index 8366a5d26..00d880577 100644 --- a/ibc-core/ics04-channel/src/handler/timeout_on_close.rs +++ b/ibc-core/ics04-channel/src/handler/timeout_on_close.rs @@ -1,10 +1,10 @@ use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, Order, State}; use ibc_core_channel_types::commitment::compute_packet_commitment; -use ibc_core_channel_types::error::{ChannelError, PacketError}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::msgs::MsgTimeoutOnClose; use ibc_core_client::context::prelude::*; use ibc_core_connection::delay::verify_conn_delay_passed; -use ibc_core_handler_types::error::ContextError; +use ibc_core_connection::types::error::ConnectionError; use ibc_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, }; @@ -12,7 +12,7 @@ use ibc_core_host::ValidationContext; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; -pub fn validate(ctx_a: &Ctx, msg: &MsgTimeoutOnClose) -> Result<(), ContextError> +pub fn validate(ctx_a: &Ctx, msg: &MsgTimeoutOnClose) -> Result<(), ChannelError> where Ctx: ValidationContext, { @@ -50,10 +50,10 @@ where &packet.timeout_timestamp_on_b, ); if commitment_on_a != expected_commitment_on_a { - return Err(PacketError::IncorrectPacketCommitment { - sequence: packet.seq_on_a, - } - .into()); + return Err(ChannelError::MismatchedPacketCommitment { + expected: expected_commitment_on_a, + actual: commitment_on_a, + }); } let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone(); @@ -83,12 +83,11 @@ where let chan_id_on_b = chan_end_on_a .counterparty() .channel_id() - .ok_or(PacketError::Channel(ChannelError::MissingCounterparty))?; - let conn_id_on_b = conn_end_on_a.counterparty().connection_id().ok_or( - PacketError::UndefinedConnectionCounterparty { - connection_id: chan_end_on_a.connection_hops()[0].clone(), - }, - )?; + .ok_or(ChannelError::MissingCounterparty)?; + let conn_id_on_b = conn_end_on_a + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_conn_hops_on_b = vec![conn_id_on_b.clone()]; let expected_counterparty = Counterparty::new( packet.port_id_on_a.clone(), @@ -106,27 +105,23 @@ where // Verify the proof for the channel state against the expected channel end. // A counterparty channel id of None in not possible, and is checked by validate_basic in msg. - client_state_of_b_on_a - .verify_membership( - prefix_on_b, - &msg.proof_close_on_b, - consensus_state_of_b_on_a.root(), - Path::ChannelEnd(chan_end_path_on_b), - expected_chan_end_on_b.encode_vec(), - ) - .map_err(ChannelError::VerifyChannelFailed) - .map_err(PacketError::Channel)?; + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_close_on_b, + consensus_state_of_b_on_a.root(), + Path::ChannelEnd(chan_end_path_on_b), + expected_chan_end_on_b.encode_vec(), + )?; verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?; let next_seq_recv_verification_result = match chan_end_on_a.ordering { Order::Ordered => { if packet.seq_on_a < msg.next_seq_recv_on_b { - return Err(PacketError::InvalidPacketSequence { - given_sequence: packet.seq_on_a, - next_sequence: msg.next_seq_recv_on_b, - } - .into()); + return Err(ChannelError::MismatchedPacketSequence { + actual: packet.seq_on_a, + expected: msg.next_seq_recv_on_b, + }); } let seq_recv_path_on_b = SeqRecvPath::new(&packet.port_id_on_b, &packet.chan_id_on_b); @@ -154,19 +149,14 @@ where ) } Order::None => { - return Err(ContextError::ChannelError(ChannelError::InvalidOrderType { - expected: "Channel ordering cannot be None".to_string(), + return Err(ChannelError::InvalidState { + expected: "Channel ordering to not be None".to_string(), actual: chan_end_on_a.ordering.to_string(), - })) + }) } }; - next_seq_recv_verification_result - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: msg.next_seq_recv_on_b, - client_error: e, - }) - .map_err(PacketError::Channel)?; + next_seq_recv_verification_result?; }; Ok(()) diff --git a/ibc-core/ics04-channel/types/src/acknowledgement.rs b/ibc-core/ics04-channel/types/src/acknowledgement.rs index 7364857d1..efbbe28ce 100644 --- a/ibc-core/ics04-channel/types/src/acknowledgement.rs +++ b/ibc-core/ics04-channel/types/src/acknowledgement.rs @@ -3,9 +3,10 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use derive_more::Into; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; -use super::error::PacketError; +use crate::error::ChannelError; /// A generic Acknowledgement type that modules may interpret as they like. /// @@ -41,11 +42,11 @@ impl AsRef<[u8]> for Acknowledgement { } impl TryFrom> for Acknowledgement { - type Error = PacketError; + type Error = DecodingError; fn try_from(bytes: Vec) -> Result { if bytes.is_empty() { - Err(PacketError::InvalidAcknowledgement) + Err(DecodingError::missing_raw_data("acknowledgment")) } else { Ok(Self(bytes)) } @@ -77,11 +78,11 @@ pub struct StatusValue(String); impl StatusValue { /// Constructs a new instance of `StatusValue` if the given value is not empty. - pub fn new(value: impl ToString) -> Result { + pub fn new(value: impl ToString) -> Result { let value = value.to_string(); if value.is_empty() { - return Err(PacketError::EmptyAcknowledgementStatus); + return Err(ChannelError::MissingAcknowledgmentStatus); } Ok(Self(value)) diff --git a/ibc-core/ics04-channel/types/src/channel.rs b/ibc-core/ics04-channel/types/src/channel.rs index 846696561..38140cb35 100644 --- a/ibc-core/ics04-channel/types/src/channel.rs +++ b/ibc-core/ics04-channel/types/src/channel.rs @@ -3,6 +3,7 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use core::str::FromStr; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::utils::PrettySlice; @@ -50,11 +51,13 @@ impl IdentifiedChannelEnd { impl Protobuf for IdentifiedChannelEnd {} impl TryFrom for IdentifiedChannelEnd { - type Error = ChannelError; + type Error = DecodingError; fn try_from(value: RawIdentifiedChannel) -> Result { if value.upgrade_sequence != 0 { - return Err(ChannelError::UnsupportedChannelUpgradeSequence); + return Err(DecodingError::invalid_raw_data( + "channel upgrade sequence expected to be 0", + )); } let raw_channel_end = RawChannel { @@ -131,17 +134,18 @@ impl Display for ChannelEnd { impl Protobuf for ChannelEnd {} impl TryFrom for ChannelEnd { - type Error = ChannelError; + type Error = DecodingError; fn try_from(value: RawChannel) -> Result { - let chan_state: State = State::from_i32(value.state)?; - - let chan_ordering = Order::from_i32(value.ordering)?; + let chan_state: State = State::from_i32(value.state) + .map_err(|e| DecodingError::invalid_raw_data(format!("channel state: {e}")))?; + let chan_ordering = Order::from_i32(value.ordering) + .map_err(|e| DecodingError::invalid_raw_data(format!("channel ordering: {e}")))?; // Assemble the 'remote' attribute of the Channel, which represents the Counterparty. let remote = value .counterparty - .ok_or(ChannelError::MissingCounterparty)? + .ok_or(DecodingError::missing_raw_data("channel counterparty"))? .try_into()?; // Parse each item in connection_hops into a ConnectionId. @@ -153,7 +157,10 @@ impl TryFrom for ChannelEnd { let version = value.version.into(); - ChannelEnd::new(chan_state, chan_ordering, remote, connection_hops, version) + let channel = ChannelEnd::new(chan_state, chan_ordering, remote, connection_hops, version) + .map_err(|e| DecodingError::invalid_raw_data(format!("channel end: {e}")))?; + + Ok(channel) } } @@ -251,14 +258,14 @@ impl ChannelEnd { pub fn validate_basic(&self) -> Result<(), ChannelError> { if self.state == State::Uninitialized { return Err(ChannelError::InvalidState { - expected: "Channel state cannot be Uninitialized".to_string(), + expected: "Channel state to not be Uninitialized".to_string(), actual: self.state.to_string(), }); } if self.ordering == Order::None { - return Err(ChannelError::InvalidOrderType { - expected: "Channel ordering cannot be None".to_string(), + return Err(ChannelError::InvalidState { + expected: "Channel ordering to not be None".to_string(), actual: self.ordering.to_string(), }); } @@ -281,7 +288,7 @@ impl ChannelEnd { pub fn verify_not_closed(&self) -> Result<(), ChannelError> { if self.state.eq(&State::Closed) { return Err(ChannelError::InvalidState { - expected: "Channel state cannot be Closed".to_string(), + expected: "Channel state to not be Closed".to_string(), actual: self.state.to_string(), }); } @@ -304,7 +311,7 @@ impl ChannelEnd { /// Checks if the counterparty of this channel end matches with an expected counterparty. pub fn verify_counterparty_matches(&self, expected: &Counterparty) -> Result<(), ChannelError> { if !self.counterparty().eq(expected) { - return Err(ChannelError::InvalidCounterparty { + return Err(ChannelError::MismatchedCounterparty { expected: expected.clone(), actual: self.counterparty().clone(), }); @@ -373,18 +380,6 @@ impl Counterparty { pub fn channel_id(&self) -> Option<&ChannelId> { self.channel_id.as_ref() } - - /// Called upon initiating a channel handshake on the host chain to verify - /// that the counterparty channel id has not been set. - pub(crate) fn verify_empty_channel_id(&self) -> Result<(), ChannelError> { - if self.channel_id().is_some() { - return Err(ChannelError::InvalidChannelId { - expected: "Counterparty channel id must be empty".to_string(), - actual: format!("{:?}", self.channel_id), - }); - } - Ok(()) - } } impl Display for Counterparty { @@ -407,7 +402,7 @@ impl Display for Counterparty { impl Protobuf for Counterparty {} impl TryFrom for Counterparty { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_counterparty: RawCounterparty) -> Result { let channel_id: Option = if raw_counterparty.channel_id.is_empty() { @@ -480,8 +475,8 @@ impl Order { 0 => Ok(Self::None), 1 => Ok(Self::Unordered), 2 => Ok(Self::Ordered), - _ => Err(ChannelError::InvalidOrderType { - expected: "Must be one of 0, 1, 2".to_string(), + _ => Err(ChannelError::InvalidState { + expected: "to be one of 0, 1, 2".to_string(), actual: nr.to_string(), }), } @@ -496,8 +491,8 @@ impl FromStr for Order { "uninitialized" => Ok(Self::None), "unordered" => Ok(Self::Unordered), "ordered" => Ok(Self::Ordered), - _ => Err(ChannelError::InvalidOrderType { - expected: "Must be one of 'uninitialized', 'unordered', 'ordered'".to_string(), + _ => Err(ChannelError::InvalidState { + expected: "to be one of 'uninitialized', 'unordered', 'ordered'".to_string(), actual: s.to_string(), }), } @@ -550,7 +545,7 @@ impl State { 3 => Ok(Self::Open), 4 => Ok(Self::Closed), _ => Err(ChannelError::InvalidState { - expected: "Must be one of: 0, 1, 2, 3, 4".to_string(), + expected: "to be one of: 0, 1, 2, 3, 4".to_string(), actual: s.to_string(), }), } diff --git a/ibc-core/ics04-channel/types/src/error.rs b/ibc-core/ics04-channel/types/src/error.rs index 7042411d3..911aed095 100644 --- a/ibc-core/ics04-channel/types/src/error.rs +++ b/ibc-core/ics04-channel/types/src/error.rs @@ -3,202 +3,81 @@ use displaydoc::Display; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::Height; -use ibc_core_connection_types::error as connection_error; -use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; +use ibc_core_connection_types::error::ConnectionError; +use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_core_host_types::identifiers::Sequence; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; use super::channel::Counterparty; use super::timeout::TimeoutHeight; -use crate::channel::State; +use crate::commitment::PacketCommitment; use crate::timeout::TimeoutTimestamp; use crate::Version; -#[derive(Debug, Display)] +/// Errors that arise from the ICS04 Channel module +#[derive(Debug, Display, derive_more::From)] pub enum ChannelError { - /// invalid channel end: `{channel_end}` - InvalidChannelEnd { channel_end: String }, - /// invalid channel id: expected `{expected}`, actual `{actual}` - InvalidChannelId { expected: String, actual: String }, - /// invalid channel state: expected `{expected}`, actual `{actual}` - InvalidState { expected: String, actual: String }, - /// invalid channel order type: expected `{expected}`, actual `{actual}` - InvalidOrderType { expected: String, actual: String }, - /// invalid connection hops length: expected `{expected}`; actual `{actual}` - InvalidConnectionHopsLength { expected: u64, actual: u64 }, - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// 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, - /// unsupported channel upgrade sequence - UnsupportedChannelUpgradeSequence, - /// version not supported: expected `{expected}`, actual `{actual}` - VersionNotSupported { expected: Version, actual: Version }, - /// missing channel end - MissingChannel, - /// 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: ClientError, - }, - /// Error verifying channel state error: `{0}` - VerifyChannelFailed(ClientError), - /// String `{value}` cannot be converted to packet sequence, error: `{error}` - InvalidStringAsSequence { - value: String, - error: core::num::ParseIntError, - }, - /// invalid channel counterparty: expected `{expected}`, actual `{actual}` - InvalidCounterparty { - expected: Counterparty, - actual: Counterparty, - }, - /// application module error: `{description}` - AppModule { description: String }, - /// Undefined counterparty connection for `{connection_id}` - UndefinedConnectionCounterparty { connection_id: ConnectionId }, - /// invalid proof: empty proof - InvalidProof, - /// identifier error: `{0}` - InvalidIdentifier(IdentifierError), - /// channel counter overflow error - CounterOverflow, - /// other error: `{description}` - Other { description: String }, -} - -#[derive(Debug, Display)] -pub enum PacketError { - /// connection error: `{0}` - Connection(connection_error::ConnectionError), - /// channel error: `{0}` - Channel(ChannelError), - /// Receiving chain block height `{chain_height}` >= packet timeout height `{timeout_height}` - LowPacketHeight { + /// decoding error: {0} + Decoding(DecodingError), + /// host error: {0} + Host(HostError), + /// client error: {0} + Client(ClientError), + /// connection error: {0} + Connection(ConnectionError), + /// timestamp error: {0} + Timestamp(TimestampError), + /// packet acknowledgment for sequence `{0}` already exists + DuplicateAcknowledgment(Sequence), + /// insufficient packet timeout height: should have `{timeout_height}` > `{chain_height}` + InsufficientPacketHeight { 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: empty proof - InvalidProof, - /// Packet timeout height `{timeout_height}` > chain height `{chain_height} and timeout timestamp `{timeout_timestamp}` > chain timestamp `{chain_timestamp}` - PacketTimeoutNotReached { + /// expired packet timestamp: should be greater than chain block timestamp + ExpiredPacketTimestamp, + /// packet timeout height `{timeout_height}` > chain height `{chain_height} and timeout timestamp `{timeout_timestamp}` > chain timestamp `{chain_timestamp}` + InsufficientPacketTimeout { timeout_height: TimeoutHeight, chain_height: Height, timeout_timestamp: TimeoutTimestamp, chain_timestamp: Timestamp, }, - /// Packet acknowledgement exists for the packet with the sequence `{sequence}` - AcknowledgementExists { sequence: Sequence }, - /// Acknowledgment cannot be empty - InvalidAcknowledgement, - /// Acknowledgment status cannot be empty - EmptyAcknowledgementStatus, - /// 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 error: `{reason}` - InvalidSigner { reason: String }, - /// application module error: `{description}` - AppModule { description: String }, - /// route not found - RouteNotFound, - /// packet sequence cannot be 0 - ZeroPacketSequence, - /// packet data bytes cannot be empty - ZeroPacketData, - /// invalid timeout height with error: `{0}` - InvalidTimeoutHeight(ClientError), - /// Invalid timeout timestamp with error: `{0}` - InvalidTimeoutTimestamp(TimestampError), + /// invalid channel state: expected `{expected}`, actual `{actual}` + InvalidState { expected: String, actual: String }, + /// invalid connection hops length: expected `{expected}`, actual `{actual}` + InvalidConnectionHopsLength { expected: u64, actual: u64 }, + /// missing acknowledgment status + MissingAcknowledgmentStatus, + /// missing counterparty + MissingCounterparty, /// missing timeout MissingTimeout, - /// invalid identifier error: `{0}` - InvalidIdentifier(IdentifierError), - /// 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, + /// mismatched counterparty: expected `{expected}`, actual `{actual}` + MismatchedCounterparty { + expected: Counterparty, + actual: Counterparty, }, - /// 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, + /// mismatched packet sequence: expected `{expected}`, actual `{actual}` + MismatchedPacketSequence { + expected: Sequence, + actual: Sequence, }, - /// Missing sequence number for ack packets on port `{port_id}` and channel `{channel_id}` - MissingNextAckSeq { - port_id: PortId, - channel_id: ChannelId, + /// mismatched packet commitments: expected `{expected:?}`, actual `{actual:?}` + MismatchedPacketCommitment { + expected: PacketCommitment, + actual: PacketCommitment, }, - /// other error: `{description}` - Other { description: String }, + /// unsupported version: expected `{expected}`, actual `{actual}` + UnsupportedVersion { expected: Version, actual: Version }, + /// application specific error: `{description}` + AppSpecific { description: String }, } impl From for ChannelError { - fn from(err: IdentifierError) -> Self { - Self::InvalidIdentifier(err) - } -} - -impl From for PacketError { - fn from(err: IdentifierError) -> Self { - Self::InvalidIdentifier(err) - } -} - -impl From for PacketError { - fn from(err: TimestampError) -> Self { - Self::InvalidTimeoutTimestamp(err) - } -} - -#[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::InvalidIdentifier(e) => Some(e), - _ => None, - } + fn from(e: IdentifierError) -> Self { + Self::Decoding(DecodingError::Identifier(e)) } } @@ -206,11 +85,11 @@ impl std::error::Error for PacketError { impl std::error::Error for ChannelError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::InvalidIdentifier(e) => Some(e), - Self::PacketVerificationFailed { - client_error: e, .. - } => Some(e), - Self::InvalidStringAsSequence { error: e, .. } => Some(e), + Self::Decoding(e) => Some(e), + Self::Client(e) => Some(e), + Self::Connection(e) => Some(e), + Self::Host(e) => Some(e), + Self::Timestamp(e) => Some(e), _ => None, } } diff --git a/ibc-core/ics04-channel/types/src/events/mod.rs b/ibc-core/ics04-channel/types/src/events/mod.rs index 798210a82..4b65a94c6 100644 --- a/ibc-core/ics04-channel/types/src/events/mod.rs +++ b/ibc-core/ics04-channel/types/src/events/mod.rs @@ -3,6 +3,7 @@ mod channel_attributes; mod packet_attributes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; use ibc_primitives::prelude::*; use tendermint::abci; @@ -21,7 +22,6 @@ use super::acknowledgement::Acknowledgement; use super::channel::Order; use super::timeout::TimeoutHeight; use super::Version; -use crate::error::ChannelError; use crate::packet::Packet; use crate::timeout::TimeoutTimestamp; @@ -671,7 +671,7 @@ impl SendPacket { } impl TryFrom for abci::Event { - type Error = ChannelError; + type Error = DecodingError; fn try_from(v: SendPacket) -> Result { let mut attributes = Vec::with_capacity(11); @@ -782,7 +782,7 @@ impl ReceivePacket { } impl TryFrom for abci::Event { - type Error = ChannelError; + type Error = DecodingError; fn try_from(v: ReceivePacket) -> Result { let mut attributes = Vec::with_capacity(11); @@ -897,7 +897,7 @@ impl WriteAcknowledgement { } impl TryFrom for abci::Event { - type Error = ChannelError; + type Error = DecodingError; fn try_from(v: WriteAcknowledgement) -> Result { let mut attributes = Vec::with_capacity(11); @@ -1002,7 +1002,7 @@ impl AcknowledgePacket { } impl TryFrom for abci::Event { - type Error = ChannelError; + type Error = DecodingError; fn try_from(v: AcknowledgePacket) -> Result { Ok(abci::Event { @@ -1099,7 +1099,7 @@ impl TimeoutPacket { } impl TryFrom for abci::Event { - type Error = ChannelError; + type Error = DecodingError; fn try_from(v: TimeoutPacket) -> Result { Ok(abci::Event { diff --git a/ibc-core/ics04-channel/types/src/events/packet_attributes.rs b/ibc-core/ics04-channel/types/src/events/packet_attributes.rs index a15a31220..c7a2c4179 100644 --- a/ibc-core/ics04-channel/types/src/events/packet_attributes.rs +++ b/ibc-core/ics04-channel/types/src/events/packet_attributes.rs @@ -4,6 +4,7 @@ use core::str; use derive_more::From; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; use ibc_primitives::prelude::*; use subtle_encoding::hex; @@ -11,7 +12,6 @@ use tendermint::abci; use crate::acknowledgement::Acknowledgement; use crate::channel::Order; -use crate::error::ChannelError; use crate::timeout::{TimeoutHeight, TimeoutTimestamp}; const PKT_SEQ_ATTRIBUTE_KEY: &str = "packet_sequence"; @@ -47,15 +47,11 @@ pub struct PacketDataAttribute { } impl TryFrom for Vec { - type Error = ChannelError; + type Error = DecodingError; fn try_from(attr: PacketDataAttribute) -> Result { let tags = vec![ - ( - PKT_DATA_ATTRIBUTE_KEY, - str::from_utf8(&attr.packet_data).map_err(|_| ChannelError::NonUtf8PacketData)?, - ) - .into(), + (PKT_DATA_ATTRIBUTE_KEY, str::from_utf8(&attr.packet_data)?).into(), ( PKT_DATA_HEX_ATTRIBUTE_KEY, str::from_utf8(&hex::encode(attr.packet_data)) @@ -312,7 +308,7 @@ pub struct AcknowledgementAttribute { } impl TryFrom for Vec { - type Error = ChannelError; + type Error = DecodingError; fn try_from(attr: AcknowledgementAttribute) -> Result { let tags = vec![ @@ -322,8 +318,7 @@ impl TryFrom for Vec { // is valid UTF-8, even though the standard doesn't require // it. It has been deprecated in ibc-go. It will be removed // in the future. - str::from_utf8(attr.acknowledgement.as_bytes()) - .map_err(|_| ChannelError::NonUtf8PacketData)?, + str::from_utf8(attr.acknowledgement.as_bytes())?, ) .into(), ( diff --git a/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs b/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs index a93f7a622..10839e91d 100644 --- a/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs +++ b/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs @@ -1,12 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgAcknowledgement as RawMsgAcknowledgement; use ibc_proto::Protobuf; use crate::acknowledgement::Acknowledgement; -use crate::error::PacketError; use crate::packet::Packet; pub const ACKNOWLEDGEMENT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgAcknowledgement"; @@ -33,23 +33,24 @@ pub struct MsgAcknowledgement { impl Protobuf for MsgAcknowledgement {} impl TryFrom for MsgAcknowledgement { - type Error = PacketError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgAcknowledgement) -> Result { Ok(MsgAcknowledgement { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(DecodingError::missing_raw_data( + "msg acknowledgement packet data", + ))? .try_into()?, acknowledgement: raw_msg.acknowledgement.try_into()?, - proof_acked_on_b: raw_msg - .proof_acked - .try_into() - .map_err(|_| PacketError::InvalidProof)?, + proof_acked_on_b: raw_msg.proof_acked.try_into()?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg acknowledgement proof height", + ))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs b/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs index c533fc59c..f3cbf1171 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs @@ -1,13 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; use ibc_proto::Protobuf; -use crate::error::ChannelError; - pub const CHAN_CLOSE_CONFIRM_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelCloseConfirm"; /// @@ -32,24 +31,25 @@ pub struct MsgChannelCloseConfirm { impl Protobuf for MsgChannelCloseConfirm {} impl TryFrom for MsgChannelCloseConfirm { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgChannelCloseConfirm) -> Result { if raw_msg.counterparty_upgrade_sequence != 0 { - return Err(ChannelError::UnsupportedChannelUpgradeSequence); + return Err(DecodingError::invalid_raw_data( + "counterparty upgrade sequence must be 0", + )); } Ok(MsgChannelCloseConfirm { port_id_on_b: raw_msg.port_id.parse()?, chan_id_on_b: raw_msg.channel_id.parse()?, - proof_chan_end_on_a: raw_msg - .proof_init - .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + proof_chan_end_on_a: raw_msg.proof_init.try_into()?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg channel close confirm proof height", + ))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_close_init.rs b/ibc-core/ics04-channel/types/src/msgs/chan_close_init.rs index 2663b4186..3d852e5fa 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_close_init.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_close_init.rs @@ -1,11 +1,10 @@ +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; use ibc_proto::Protobuf; -use crate::error::ChannelError; - pub const CHAN_CLOSE_INIT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelCloseInit"; /// @@ -27,7 +26,7 @@ pub struct MsgChannelCloseInit { impl Protobuf for MsgChannelCloseInit {} impl TryFrom for MsgChannelCloseInit { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgChannelCloseInit) -> Result { Ok(MsgChannelCloseInit { diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs index 432ca92fe..4d1151ff6 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs @@ -1,12 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; use ibc_proto::Protobuf; -use crate::error::ChannelError; use crate::Version; pub const CHAN_OPEN_ACK_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenAck"; @@ -33,7 +33,7 @@ pub struct MsgChannelOpenAck { impl Protobuf for MsgChannelOpenAck {} impl TryFrom for MsgChannelOpenAck { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgChannelOpenAck) -> Result { Ok(MsgChannelOpenAck { @@ -41,14 +41,11 @@ impl TryFrom for MsgChannelOpenAck { chan_id_on_a: raw_msg.channel_id.parse()?, chan_id_on_b: raw_msg.counterparty_channel_id.parse()?, version_on_b: raw_msg.counterparty_version.into(), - proof_chan_end_on_b: raw_msg - .proof_try - .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + proof_chan_end_on_b: raw_msg.proof_try.try_into()?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(DecodingError::missing_raw_data("proof height"))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs index 6070eb1a2..d28fe2a6e 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs @@ -1,13 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; use ibc_proto::Protobuf; -use crate::error::ChannelError; - pub const CHAN_OPEN_CONFIRM_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenConfirm"; /// @@ -32,20 +31,19 @@ pub struct MsgChannelOpenConfirm { impl Protobuf for MsgChannelOpenConfirm {} impl TryFrom for MsgChannelOpenConfirm { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgChannelOpenConfirm) -> Result { Ok(MsgChannelOpenConfirm { port_id_on_b: raw_msg.port_id.parse()?, chan_id_on_b: raw_msg.channel_id.parse()?, - proof_chan_end_on_a: raw_msg - .proof_ack - .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + proof_chan_end_on_a: raw_msg.proof_ack.try_into()?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg channel open confirm proof height", + ))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs index 7ab3f1598..2299802d4 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs @@ -1,3 +1,4 @@ +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ConnectionId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -42,15 +43,28 @@ impl MsgChannelOpenInit { impl Protobuf for MsgChannelOpenInit {} impl TryFrom for MsgChannelOpenInit { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgChannelOpenInit) -> Result { let chan_end_on_a: ChannelEnd = raw_msg .channel - .ok_or(ChannelError::MissingChannel)? + .ok_or(DecodingError::missing_raw_data("channel end"))? .try_into()?; - chan_end_on_a.verify_state_matches(&State::Init)?; - chan_end_on_a.counterparty().verify_empty_channel_id()?; + + chan_end_on_a + .verify_state_matches(&State::Init) + .map_err(|_| { + DecodingError::invalid_raw_data(format!( + "expected channel end to be in `Init` state but it is in `{}` instead", + chan_end_on_a.state + )) + })?; + + if let Some(cid) = chan_end_on_a.counterparty().channel_id() { + return Err(DecodingError::invalid_raw_data(format!( + "expected counterparty channel ID to be empty, actual `{cid}`", + ))); + } Ok(MsgChannelOpenInit { port_id_on_a: raw_msg.port_id.parse()?, diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs index c6cbd918b..042f2c07a 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs @@ -1,5 +1,6 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; @@ -50,22 +51,26 @@ impl MsgChannelOpenTry { impl Protobuf for MsgChannelOpenTry {} impl TryFrom for MsgChannelOpenTry { - type Error = ChannelError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgChannelOpenTry) -> Result { let chan_end_on_b: ChannelEnd = raw_msg .channel - .ok_or(ChannelError::MissingChannel)? + .ok_or(DecodingError::missing_raw_data("channel end not set"))? .try_into()?; - chan_end_on_b.verify_state_matches(&State::TryOpen)?; + chan_end_on_b + .verify_state_matches(&State::TryOpen) + .map_err(|_| { + DecodingError::invalid_raw_data(format!( + "channel state expected to be in `TryOpen` state, actual `{}`", + chan_end_on_b.state() + )) + })?; #[allow(deprecated)] if !raw_msg.previous_channel_id.is_empty() { - return Err(ChannelError::InvalidChannelId { - expected: "previous channel id must be empty. It has been deprecated as crossing hellos are no longer supported".to_string(), - actual: raw_msg.previous_channel_id, - }); + return Err(DecodingError::invalid_raw_data("previous channel id must be empty; it has been deprecated as crossing hellos are no longer supported"))?; } #[allow(deprecated)] @@ -74,19 +79,17 @@ impl TryFrom for MsgChannelOpenTry { ordering: chan_end_on_b.ordering, connection_hops_on_b: chan_end_on_b.connection_hops, port_id_on_a: chan_end_on_b.remote.port_id, - chan_id_on_a: chan_end_on_b - .remote - .channel_id - .ok_or(ChannelError::MissingCounterparty)?, + chan_id_on_a: chan_end_on_b.remote.channel_id.ok_or( + DecodingError::missing_raw_data("msg channel open try counterparty channel ID"), + )?, version_supported_on_a: raw_msg.counterparty_version.into(), - proof_chan_end_on_a: raw_msg - .proof_init - .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + proof_chan_end_on_a: raw_msg.proof_init.try_into()?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(DecodingError::invalid_raw_data( + "msg channel open try proof height", + ))?, signer: raw_msg.signer.into(), version_proposal: chan_end_on_b.version, }; diff --git a/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs b/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs index 5295e1807..c2b14ab41 100644 --- a/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs +++ b/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs @@ -1,11 +1,11 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; use ibc_proto::Protobuf; -use crate::error::PacketError; use crate::packet::Packet; pub const RECV_PACKET_TYPE_URL: &str = "/ibc.core.channel.v1.MsgRecvPacket"; @@ -33,22 +33,19 @@ pub struct MsgRecvPacket { impl Protobuf for MsgRecvPacket {} impl TryFrom for MsgRecvPacket { - type Error = PacketError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgRecvPacket) -> Result { Ok(MsgRecvPacket { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(DecodingError::missing_raw_data("msg recv packet data"))? .try_into()?, - proof_commitment_on_a: raw_msg - .proof_commitment - .try_into() - .map_err(|_| PacketError::InvalidProof)?, + proof_commitment_on_a: raw_msg.proof_commitment.try_into()?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(DecodingError::invalid_raw_data("msg recv proof height"))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/timeout.rs b/ibc-core/ics04-channel/types/src/msgs/timeout.rs index 362630ff0..5fc255896 100644 --- a/ibc-core/ics04-channel/types/src/msgs/timeout.rs +++ b/ibc-core/ics04-channel/types/src/msgs/timeout.rs @@ -1,12 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::Sequence; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; use ibc_proto::Protobuf; -use crate::error::PacketError; use crate::packet::Packet; pub const TIMEOUT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgTimeout"; @@ -32,26 +32,25 @@ pub struct MsgTimeout { impl Protobuf for MsgTimeout {} impl TryFrom for MsgTimeout { - type Error = PacketError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgTimeout) -> Result { if raw_msg.next_sequence_recv == 0 { - return Err(PacketError::ZeroPacketSequence); + return Err(DecodingError::invalid_raw_data( + "msg timeout packet sequence cannot be 0", + )); } Ok(MsgTimeout { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(DecodingError::missing_raw_data("msg timeout packet data"))? .try_into()?, next_seq_recv_on_b: Sequence::from(raw_msg.next_sequence_recv), - proof_unreceived_on_b: raw_msg - .proof_unreceived - .try_into() - .map_err(|_| PacketError::InvalidProof)?, + proof_unreceived_on_b: raw_msg.proof_unreceived.try_into()?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(DecodingError::missing_raw_data("msg timeout proof height"))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs b/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs index 7673d5f33..34394b856 100644 --- a/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs +++ b/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs @@ -1,12 +1,12 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::Sequence; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgTimeoutOnClose as RawMsgTimeoutOnClose; use ibc_proto::Protobuf; -use crate::error::{ChannelError, PacketError}; use crate::packet::Packet; pub const TIMEOUT_ON_CLOSE_TYPE_URL: &str = "/ibc.core.channel.v1.MsgTimeoutOnClose"; @@ -32,37 +32,31 @@ pub struct MsgTimeoutOnClose { impl Protobuf for MsgTimeoutOnClose {} impl TryFrom for MsgTimeoutOnClose { - type Error = PacketError; + type Error = DecodingError; fn try_from(raw_msg: RawMsgTimeoutOnClose) -> Result { if raw_msg.next_sequence_recv == 0 { - return Err(PacketError::ZeroPacketSequence); + return Err(DecodingError::invalid_raw_data( + "packet sequence cannot be 0", + )); } if raw_msg.counterparty_upgrade_sequence != 0 { - return Err(PacketError::Channel( - ChannelError::UnsupportedChannelUpgradeSequence, - )); + return Err(DecodingError::invalid_raw_data("channel upgrade sequence")); } Ok(MsgTimeoutOnClose { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(DecodingError::missing_raw_data("msg timeout packet data"))? .try_into()?, next_seq_recv_on_b: Sequence::from(raw_msg.next_sequence_recv), - proof_unreceived_on_b: raw_msg - .proof_unreceived - .try_into() - .map_err(|_| PacketError::InvalidProof)?, - proof_close_on_b: raw_msg - .proof_close - .try_into() - .map_err(|_| PacketError::InvalidProof)?, + proof_unreceived_on_b: raw_msg.proof_unreceived.try_into()?, + proof_close_on_b: raw_msg.proof_close.try_into()?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(DecodingError::invalid_raw_data("msg timeout proof height"))?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/packet.rs b/ibc-core/ics04-channel/types/src/packet.rs index 53feb671d..e5f2e8779 100644 --- a/ibc-core/ics04-channel/types/src/packet.rs +++ b/ibc-core/ics04-channel/types/src/packet.rs @@ -1,12 +1,12 @@ //! Defines the packet type use ibc_core_client_types::Height; +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::identifiers::{ChannelId, PortId, Sequence}; use ibc_primitives::prelude::*; use ibc_primitives::Timestamp; use ibc_proto::ibc::core::channel::v1::{Packet as RawPacket, PacketState as RawPacketState}; use super::timeout::TimeoutHeight; -use crate::error::PacketError; use crate::timeout::TimeoutTimestamp; /// Enumeration of proof carrying ICS4 message, helper for relayer. @@ -179,15 +179,17 @@ impl core::fmt::Display for Packet { } impl TryFrom for Packet { - type Error = PacketError; + type Error = DecodingError; fn try_from(raw_pkt: RawPacket) -> Result { if Sequence::from(raw_pkt.sequence).is_zero() { - return Err(PacketError::ZeroPacketSequence); + return Err(DecodingError::invalid_raw_data( + "packet sequence cannot be 0", + )); } if raw_pkt.data.is_empty() { - return Err(PacketError::ZeroPacketData); + return Err(DecodingError::missing_raw_data("packet data is not set"))?; } // Note: ibc-go currently (July 2022) incorrectly treats the timeout @@ -204,7 +206,9 @@ impl TryFrom for Packet { // Packet timeout height and packet timeout timestamp cannot both be unset. if !packet_timeout_height.is_set() && !timeout_timestamp_on_b.is_set() { - return Err(PacketError::MissingTimeout); + return Err(DecodingError::missing_raw_data( + "missing one of packet timeout height or timeout timestamp", + )); } Ok(Packet { @@ -289,15 +293,17 @@ impl core::fmt::Display for PacketState { } impl TryFrom for PacketState { - type Error = PacketError; + type Error = DecodingError; fn try_from(raw_pkt: RawPacketState) -> Result { if Sequence::from(raw_pkt.sequence).is_zero() { - return Err(PacketError::ZeroPacketSequence); + return Err(DecodingError::invalid_raw_data( + "packet sequence cannot be 0", + )); } if raw_pkt.data.is_empty() { - return Err(PacketError::ZeroPacketData); + return Err(DecodingError::missing_raw_data("packet data not set"))?; } Ok(PacketState { diff --git a/ibc-core/ics04-channel/types/src/timeout/height.rs b/ibc-core/ics04-channel/types/src/timeout/height.rs index 43dfe3bf3..46d6ab49a 100644 --- a/ibc-core/ics04-channel/types/src/timeout/height.rs +++ b/ibc-core/ics04-channel/types/src/timeout/height.rs @@ -3,11 +3,10 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use ibc_core_client_types::Height; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::ibc::core::client::v1::Height as RawHeight; -use crate::error::PacketError; - /// Indicates a consensus height on the destination chain after which the packet /// will no longer be processed, and will instead count as having timed-out. /// @@ -86,7 +85,7 @@ impl TimeoutHeight { } impl TryFrom for TimeoutHeight { - type Error = PacketError; + type Error = DecodingError; // Note: it is important for `revision_number` to also be `0`, otherwise // packet commitment proofs will be incorrect (proof construction in @@ -96,16 +95,14 @@ impl TryFrom for TimeoutHeight { if raw_height.revision_number == 0 && raw_height.revision_height == 0 { Ok(TimeoutHeight::Never) } else { - let height = raw_height - .try_into() - .map_err(PacketError::InvalidTimeoutHeight)?; + let height = raw_height.try_into()?; Ok(TimeoutHeight::At(height)) } } } impl TryFrom> for TimeoutHeight { - type Error = PacketError; + type Error = DecodingError; fn try_from(maybe_raw_height: Option) -> Result { match maybe_raw_height { diff --git a/ibc-core/ics04-channel/types/src/timeout/timestamp.rs b/ibc-core/ics04-channel/types/src/timeout/timestamp.rs index 86ef4328a..8cd45b712 100644 --- a/ibc-core/ics04-channel/types/src/timeout/timestamp.rs +++ b/ibc-core/ics04-channel/types/src/timeout/timestamp.rs @@ -5,7 +5,7 @@ use core::time::Duration; use ibc_primitives::prelude::*; use ibc_primitives::Timestamp; -use crate::error::PacketError; +use crate::error::ChannelError; /// Indicates a timestamp on the destination chain after which the packet will /// no longer be processed, and will instead count as having timed-out. @@ -101,7 +101,7 @@ impl Display for TimeoutTimestamp { } impl Add for TimeoutTimestamp { - type Output = Result; + type Output = Result; fn add(self, rhs: Duration) -> Self::Output { match self { @@ -109,13 +109,13 @@ impl Add for TimeoutTimestamp { let new_timestamp = timestamp.add(rhs)?; Ok(TimeoutTimestamp::At(new_timestamp)) } - TimeoutTimestamp::Never => Err(PacketError::MissingTimeout), + TimeoutTimestamp::Never => Err(ChannelError::MissingTimeout), } } } impl Sub for TimeoutTimestamp { - type Output = Result; + type Output = Result; fn sub(self, rhs: Duration) -> Self::Output { match self { @@ -123,7 +123,7 @@ impl Sub for TimeoutTimestamp { let new_timestamp = timestamp.sub(rhs)?; Ok(TimeoutTimestamp::At(new_timestamp)) } - TimeoutTimestamp::Never => Err(PacketError::MissingTimeout), + TimeoutTimestamp::Never => Err(ChannelError::MissingTimeout), } } } diff --git a/ibc-core/ics04-channel/types/src/version.rs b/ibc-core/ics04-channel/types/src/version.rs index 2cfc1e46c..1cf5c894a 100644 --- a/ibc-core/ics04-channel/types/src/version.rs +++ b/ibc-core/ics04-channel/types/src/version.rs @@ -51,7 +51,7 @@ impl Version { pub fn verify_is_expected(&self, expected: Version) -> Result<(), ChannelError> { if self != &expected { - return Err(ChannelError::VersionNotSupported { + return Err(ChannelError::UnsupportedVersion { expected, actual: self.clone(), }); diff --git a/ibc-core/ics23-commitment/types/src/commitment.rs b/ibc-core/ics23-commitment/types/src/commitment.rs index 2555f89ac..af1f30153 100644 --- a/ibc-core/ics23-commitment/types/src/commitment.rs +++ b/ibc-core/ics23-commitment/types/src/commitment.rs @@ -2,6 +2,7 @@ use core::fmt; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::ToVec; use ibc_proto::ibc::core::commitment::v1::MerkleProof as RawMerkleProof; @@ -9,7 +10,6 @@ use ibc_proto::Protobuf; use subtle_encoding::{Encoding, Hex}; use super::merkle::MerkleProof; -use crate::error::CommitmentError; /// Encodes a commitment root; most often a Merkle tree root hash. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -90,11 +90,11 @@ impl fmt::Debug for CommitmentProofBytes { } impl TryFrom> for CommitmentProofBytes { - type Error = CommitmentError; + type Error = DecodingError; fn try_from(bytes: Vec) -> Result { if bytes.is_empty() { - Err(Self::Error::EmptyMerkleProof) + Err(DecodingError::missing_raw_data("commitment proof bytes"))? } else { Ok(Self { bytes }) } @@ -102,7 +102,7 @@ impl TryFrom> for CommitmentProofBytes { } impl TryFrom for CommitmentProofBytes { - type Error = CommitmentError; + type Error = DecodingError; fn try_from(proof: RawMerkleProof) -> Result { proof.to_vec().try_into() @@ -110,7 +110,7 @@ impl TryFrom for CommitmentProofBytes { } impl TryFrom for CommitmentProofBytes { - type Error = CommitmentError; + type Error = DecodingError; fn try_from(value: MerkleProof) -> Result { Self::try_from(RawMerkleProof::from(value)) @@ -118,11 +118,10 @@ impl TryFrom for CommitmentProofBytes { } impl<'a> TryFrom<&'a CommitmentProofBytes> for MerkleProof { - type Error = CommitmentError; + type Error = DecodingError; fn try_from(value: &'a CommitmentProofBytes) -> Result { - Protobuf::::decode(value.as_ref()) - .map_err(|e| CommitmentError::DecodingFailure(e.to_string())) + Ok(Protobuf::::decode(value.as_ref())?) } } diff --git a/ibc-core/ics23-commitment/types/src/error.rs b/ibc-core/ics23-commitment/types/src/error.rs index fdbdd24c7..3554767eb 100644 --- a/ibc-core/ics23-commitment/types/src/error.rs +++ b/ibc-core/ics23-commitment/types/src/error.rs @@ -1,43 +1,47 @@ //! Defines the commitment error type use displaydoc::Display; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; #[derive(Debug, Display)] pub enum CommitmentError { - /// empty commitment prefix - EmptyCommitmentPrefix, - /// empty merkle proof - EmptyMerkleProof, - /// empty merkle root - EmptyMerkleRoot, - /// empty verified value - EmptyVerifiedValue, - /// empty proof specs - EmptyProofSpecs, - /// invalid depth range: [{0}, {1}] - InvalidDepthRange(i32, i32), - /// mismatch between the number of proofs with that of specs - NumberOfSpecsMismatch, - /// mismatch between the number of proofs with that of keys - NumberOfKeysMismatch, + /// decoding error: {0} + Decoding(DecodingError), + /// missing commitment root + MissingCommitmentRoot, + /// missing commitment prefix + MissingCommitmentPrefix, + /// missing merkle proof + MissingMerkleProof, + /// missing merkle root + MissingMerkleRoot, + /// missing verified value + MissingVerifiedValue, + /// missing proof specs + MissingProofSpecs, + /// mismatched number of proofs: expected `{expected}`, actual `{actual}` + MismatchedNumberOfProofs { expected: usize, actual: usize }, + /// invalid range [`{min}`, `{max}`] + InvalidRange { min: i32, max: i32 }, /// invalid merkle proof InvalidMerkleProof, - /// proof verification failed - VerificationFailure, - /// encoded commitment prefix is not a valid hex string: `{0}` - EncodingFailure(String), - /// decoding commitment proof bytes failed: `{0}` - DecodingFailure(String), - /// invalid prefix length range: `[{0}, {1}]` - InvalidPrefixLengthRange(i32, i32), - /// invalid child size: `{0}` - InvalidChildSize(i32), - /// invalid hash operation: `{0}` - InvalidHashOp(i32), - /// invalid length operation: `{0}` - InvalidLengthOp(i32), + /// failed to verify membership + FailedToVerifyMembership, +} + +impl From for CommitmentError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) + } } #[cfg(feature = "std")] -impl std::error::Error for CommitmentError {} +impl std::error::Error for CommitmentError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Decoding(e) => Some(e), + _ => None, + } + } +} diff --git a/ibc-core/ics23-commitment/types/src/merkle.rs b/ibc-core/ics23-commitment/types/src/merkle.rs index d597ef1c8..d2bcea088 100644 --- a/ibc-core/ics23-commitment/types/src/merkle.rs +++ b/ibc-core/ics23-commitment/types/src/merkle.rs @@ -1,5 +1,6 @@ //! Merkle proof utilities +use ibc_core_host_types::error::DecodingError; use ibc_core_host_types::path::PathBytes; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; @@ -70,7 +71,7 @@ pub struct MerkleProof { impl Protobuf for MerkleProof {} impl TryFrom for MerkleProof { - type Error = CommitmentError; + type Error = DecodingError; fn try_from(proof: RawMerkleProof) -> Result { Ok(Self { @@ -98,21 +99,27 @@ impl MerkleProof { ) -> Result<(), CommitmentError> { // validate arguments if self.proofs.is_empty() { - return Err(CommitmentError::EmptyMerkleProof); + return Err(CommitmentError::MissingMerkleProof); } if root.hash.is_empty() { - return Err(CommitmentError::EmptyMerkleRoot); + return Err(CommitmentError::MissingMerkleRoot); } let num = self.proofs.len(); let ics23_specs = Vec::::from(specs.clone()); if ics23_specs.len() != num { - return Err(CommitmentError::NumberOfSpecsMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + expected: ics23_specs.len(), + actual: num, + }); } if keys.key_path.len() != num { - return Err(CommitmentError::NumberOfKeysMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + expected: keys.key_path.len(), + actual: num, + }); } if value.is_empty() { - return Err(CommitmentError::EmptyVerifiedValue); + return Err(CommitmentError::MissingVerifiedValue); } let mut subroot = value.clone(); @@ -135,7 +142,7 @@ impl MerkleProof { .map_err(|_| CommitmentError::InvalidMerkleProof)?; if !verify_membership::(proof, spec, &subroot, key.as_ref(), &value) { - return Err(CommitmentError::VerificationFailure); + return Err(CommitmentError::FailedToVerifyMembership); } value.clone_from(&subroot); } @@ -144,7 +151,7 @@ impl MerkleProof { } if root.hash != subroot { - return Err(CommitmentError::VerificationFailure); + return Err(CommitmentError::FailedToVerifyMembership); } Ok(()) @@ -158,18 +165,24 @@ impl MerkleProof { ) -> Result<(), CommitmentError> { // validate arguments if self.proofs.is_empty() { - return Err(CommitmentError::EmptyMerkleProof); + return Err(CommitmentError::MissingMerkleProof); } if root.hash.is_empty() { - return Err(CommitmentError::EmptyMerkleRoot); + return Err(CommitmentError::MissingMerkleRoot); } let num = self.proofs.len(); let ics23_specs = Vec::::from(specs.clone()); if ics23_specs.len() != num { - return Err(CommitmentError::NumberOfSpecsMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + actual: num, + expected: ics23_specs.len(), + }); } if keys.key_path.len() != num { - return Err(CommitmentError::NumberOfKeysMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + actual: num, + expected: keys.key_path.len(), + }); } // verify the absence of key in lowest subtree @@ -190,7 +203,7 @@ impl MerkleProof { let subroot = calculate_non_existence_root::(non_existence_proof)?; if !verify_non_membership::(proof, spec, &subroot, key.as_ref()) { - return Err(CommitmentError::VerificationFailure); + return Err(CommitmentError::FailedToVerifyMembership); } // verify membership proofs starting from index 1 with value = subroot diff --git a/ibc-core/ics23-commitment/types/src/specs.rs b/ibc-core/ics23-commitment/types/src/specs.rs index 46bf79a95..5d0f0f473 100644 --- a/ibc-core/ics23-commitment/types/src/specs.rs +++ b/ibc-core/ics23-commitment/types/src/specs.rs @@ -1,5 +1,6 @@ //! Defines proof specs, which encode the structure of proofs +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::ics23::{InnerSpec as RawInnerSpec, LeafOp as RawLeafOp, ProofSpec as RawProofSpec}; use ics23::{HashOp, LengthOp}; @@ -30,7 +31,7 @@ impl ProofSpecs { pub fn validate(&self) -> Result<(), CommitmentError> { if self.is_empty() { - return Err(CommitmentError::EmptyProofSpecs); + return Err(CommitmentError::MissingProofSpecs); } for proof_spec in &self.0 { // A non-positive `min_depth` or `max_depth` indicates no limit on the respective bound. @@ -43,10 +44,10 @@ impl ProofSpecs { && 0 < proof_spec.0.max_depth && proof_spec.0.max_depth < proof_spec.0.min_depth) { - return Err(CommitmentError::InvalidDepthRange( - proof_spec.0.min_depth, - proof_spec.0.max_depth, - )); + return Err(CommitmentError::InvalidRange { + min: proof_spec.0.min_depth, + max: proof_spec.0.max_depth, + }); } } Ok(()) @@ -54,11 +55,12 @@ impl ProofSpecs { } impl TryFrom> for ProofSpecs { - type Error = CommitmentError; - fn try_from(ics23_specs: Vec) -> Result { + type Error = DecodingError; + + fn try_from(ics23_specs: Vec) -> Result { // no proof specs provided if ics23_specs.is_empty() { - return Err(CommitmentError::EmptyProofSpecs); + return Err(DecodingError::missing_raw_data("proof specs")); } ics23_specs @@ -80,8 +82,9 @@ impl From for Vec { struct ProofSpec(RawProofSpec); impl TryFrom for ProofSpec { - type Error = CommitmentError; - fn try_from(spec: RawProofSpec) -> Result { + type Error = DecodingError; + + fn try_from(spec: RawProofSpec) -> Result { // A non-positive `min_depth` or `max_depth` indicates no limit on the respective bound. // For simplicity, negative values for `min_depth` and `max_depth` are not allowed // and only `0` is used to indicate no limit. When `min_depth` and `max_depth` are both positive, @@ -90,10 +93,10 @@ impl TryFrom for ProofSpec { || spec.min_depth < 0 || (0 < spec.min_depth && 0 < spec.max_depth && spec.max_depth < spec.min_depth) { - return Err(CommitmentError::InvalidDepthRange( - spec.min_depth, - spec.max_depth, - )); + return Err(DecodingError::invalid_raw_data(format!( + "proof spec range [`{}`, `{}`]", + spec.min_depth, spec.max_depth + ))); } let leaf_spec = spec @@ -128,16 +131,17 @@ impl From for RawProofSpec { struct LeafOp(RawLeafOp); impl TryFrom for LeafOp { - type Error = CommitmentError; + type Error = DecodingError; + fn try_from(leaf_op: RawLeafOp) -> Result { let _ = HashOp::try_from(leaf_op.hash) - .map_err(|_| CommitmentError::InvalidHashOp(leaf_op.hash))?; + .map_err(|_| DecodingError::invalid_raw_data("leaf op hash"))?; let _ = HashOp::try_from(leaf_op.prehash_key) - .map_err(|_| CommitmentError::InvalidHashOp(leaf_op.prehash_key))?; + .map_err(|_| DecodingError::invalid_raw_data("leaf op prehash key"))?; let _ = HashOp::try_from(leaf_op.prehash_value) - .map_err(|_| CommitmentError::InvalidHashOp(leaf_op.prehash_value))?; + .map_err(|_| DecodingError::invalid_raw_data("leaf op prehash value"))?; let _ = LengthOp::try_from(leaf_op.length) - .map_err(|_| CommitmentError::InvalidLengthOp(leaf_op.length))?; + .map_err(|_| DecodingError::invalid_raw_data("leaf op length"))?; Ok(Self(leaf_op)) } @@ -154,10 +158,11 @@ impl From for RawLeafOp { struct InnerSpec(RawInnerSpec); impl TryFrom for InnerSpec { - type Error = CommitmentError; - fn try_from(inner_spec: RawInnerSpec) -> Result { + type Error = DecodingError; + + fn try_from(inner_spec: RawInnerSpec) -> Result { if inner_spec.child_size <= 0 { - return Err(CommitmentError::InvalidChildSize(inner_spec.child_size)); + return Err(DecodingError::invalid_raw_data("inner spec child size")); } // Negative prefix lengths are not allowed and the maximum prefix length must @@ -166,10 +171,10 @@ impl TryFrom for InnerSpec { || inner_spec.max_prefix_length < 0 || inner_spec.max_prefix_length < inner_spec.min_prefix_length { - return Err(CommitmentError::InvalidPrefixLengthRange( - inner_spec.min_prefix_length, - inner_spec.max_prefix_length, - )); + return Err(DecodingError::invalid_raw_data(format!( + "inner spec range: [`{}`, `{}`]", + inner_spec.min_prefix_length, inner_spec.max_prefix_length + ))); } Ok(Self(RawInnerSpec { @@ -200,15 +205,15 @@ mod tests { #[case(0, 0)] #[case(2, 2)] #[case(5, 6)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(-3,3)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(2,-6)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(-2,-6)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(-6,-2)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(5, 3)] fn test_proof_specs_try_from(#[case] min_depth: i32, #[case] max_depth: i32) { let raw_proof_spec = RawProofSpec { @@ -225,15 +230,15 @@ mod tests { #[case(0, 0)] #[case(1, 2)] #[case(2, 2)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(2, 1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(-2,1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(2,-1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(-2,-1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRawData")] #[case(-1,-2)] fn test_inner_specs_try_from(#[case] min_prefix_length: i32, #[case] max_prefix_length: i32) { let raw_inner_spec = RawInnerSpec { @@ -250,21 +255,21 @@ mod tests { #[rstest] #[case(0, 0, 0, 0)] #[case(9, 9, 9, 8)] - #[should_panic(expected = "InvalidHashOp")] + #[should_panic(expected = "leaf op hash")] #[case(-1, 4, 4, 4)] - #[should_panic(expected = "InvalidHashOp")] + #[should_panic(expected = "leaf op hash")] #[case(10, 4, 4, 4)] - #[should_panic(expected = "InvalidHashOp")] + #[should_panic(expected = "leaf op prehash key")] #[case(4, -1, 4, 4)] - #[should_panic(expected = "InvalidHashOp")] + #[should_panic(expected = "leaf op prehash key")] #[case(4, 10, 4, 4)] - #[should_panic(expected = "InvalidHashOp")] + #[should_panic(expected = "leaf op prehash value")] #[case(4, 4, -1, 4)] - #[should_panic(expected = "InvalidHashOp")] + #[should_panic(expected = "leaf op prehash value")] #[case(4, 4, 10, 4)] - #[should_panic(expected = "InvalidLengthOp")] + #[should_panic(expected = "leaf op length")] #[case(4, 4, 4, -1)] - #[should_panic(expected = "InvalidLengthOp")] + #[should_panic(expected = "leaf op length")] #[case(4, 4, 4, 9)] fn test_leaf_op_try_from( #[case] hash: i32, diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/context.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/context.rs index 1328355e8..89ec14ff3 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/context.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/context.rs @@ -6,7 +6,7 @@ //! If it proves to be generic enough, we may move it to the ICS02 section. use ibc_core_client_context::ClientValidationContext; -use ibc_core_client_types::error::UpgradeClientError; +use ibc_core_host_types::error::HostError; use ibc_core_host_types::path::{UpgradeClientStatePath, UpgradeConsensusStatePath}; use super::Plan; @@ -23,41 +23,41 @@ pub trait UpgradeValidationContext { type V: ClientValidationContext; /// Returns the upgrade plan that is scheduled and has not been executed yet. - fn upgrade_plan(&self) -> Result; + fn upgrade_plan(&self) -> Result; /// Returns the upgraded client state at the specified upgrade path. fn upgraded_client_state( &self, upgrade_path: &UpgradeClientStatePath, - ) -> Result, UpgradeClientError>; + ) -> Result, HostError>; /// Returns the upgraded consensus state at the specified upgrade path. fn upgraded_consensus_state( &self, upgrade_path: &UpgradeConsensusStatePath, - ) -> Result, UpgradeClientError>; + ) -> Result, HostError>; } /// Helper context to execute client upgrades, providing methods to schedule /// an upgrade and store related upgraded client and consensus states. pub trait UpgradeExecutionContext: UpgradeValidationContext { /// Schedules an upgrade based on the specified plan. If there is another `Plan` it should be overwritten. - fn schedule_upgrade(&mut self, plan: Plan) -> Result<(), UpgradeClientError>; + fn schedule_upgrade(&mut self, plan: Plan) -> Result<(), HostError>; /// Clears the upgrade plan at the specified height. - fn clear_upgrade_plan(&mut self, plan_height: u64) -> Result<(), UpgradeClientError>; + fn clear_upgrade_plan(&mut self, plan_height: u64) -> Result<(), HostError>; /// Stores the upgraded client state at the specified upgrade path. fn store_upgraded_client_state( &mut self, upgrade_path: UpgradeClientStatePath, client_state: UpgradedClientStateRef, - ) -> Result<(), UpgradeClientError>; + ) -> Result<(), HostError>; /// Stores the upgraded consensus state at the specified upgrade path. fn store_upgraded_consensus_state( &mut self, upgrade_path: UpgradeConsensusStatePath, consensus_state: UpgradedConsensusStateRef, - ) -> Result<(), UpgradeClientError>; + ) -> Result<(), HostError>; } diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs index 3f27bad12..419a1e46c 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs @@ -29,7 +29,7 @@ where let mut client_state = TmClientState::try_from(proposal.upgraded_client_state).map_err(|e| { UpgradeClientError::InvalidUpgradeProposal { - reason: e.to_string(), + description: e.to_string(), } })?; diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs index d1a25adcc..6541ad049 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs @@ -1,6 +1,6 @@ //! Definition of domain `Plan` type. -use ibc_core_client_types::error::UpgradeClientError; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::cosmos::upgrade::v1beta1::Plan as RawPlan; use ibc_proto::google::protobuf::Any; @@ -28,36 +28,30 @@ pub struct Plan { impl Protobuf for Plan {} impl TryFrom for Plan { - type Error = UpgradeClientError; + type Error = DecodingError; fn try_from(raw: RawPlan) -> Result { if raw.name.is_empty() { - return Err(UpgradeClientError::InvalidUpgradePlan { - reason: "name field cannot be empty".to_string(), - }); + return Err(DecodingError::missing_raw_data("upgrade plan name")); } #[allow(deprecated)] if raw.time.is_some() { - return Err(UpgradeClientError::InvalidUpgradePlan { - reason: "time field must be empty".to_string(), - }); + return Err(DecodingError::invalid_raw_data( + "upgrade plan time must be empty", + )); } #[allow(deprecated)] if raw.upgraded_client_state.is_some() { - return Err(UpgradeClientError::InvalidUpgradePlan { - reason: "upgraded_client_state field must be empty".to_string(), - }); + return Err(DecodingError::invalid_raw_data( + "upgrade plan `upgraded_client_state` field must be empty", + )); } Ok(Self { name: raw.name, - height: u64::try_from(raw.height).map_err(|_| { - UpgradeClientError::InvalidUpgradePlan { - reason: "height plan overflow".to_string(), - } - })?, + height: u64::try_from(raw.height)?, info: raw.info, }) } @@ -79,25 +73,17 @@ impl From for RawPlan { impl Protobuf for Plan {} impl TryFrom for Plan { - type Error = UpgradeClientError; + type Error = DecodingError; fn try_from(any: Any) -> Result { - if any.type_url != TYPE_URL { - return Err(UpgradeClientError::InvalidUpgradePlan { - reason: format!( - "type_url do not match: expected {}, got {}", - TYPE_URL, any.type_url - ), - }); + if let TYPE_URL = any.type_url.as_str() { + Protobuf::::decode_vec(&any.value).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: TYPE_URL.to_string(), + actual: any.type_url, + }) } - - let plan = Protobuf::::decode_vec(&any.value).map_err(|e| { - UpgradeClientError::InvalidUpgradePlan { - reason: format!("raw plan decode error: {}", e), - } - })?; - - Ok(plan) } } diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs index a12f4fab4..a98441ce7 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs @@ -1,6 +1,6 @@ //! Definition of domain `UpgradeProposal` type for handling upgrade client proposal -use ibc_core_client_types::error::UpgradeClientError; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::UpgradeProposal as RawUpgradeProposal; @@ -28,33 +28,27 @@ pub struct UpgradeProposal { impl Protobuf for UpgradeProposal {} impl TryFrom for UpgradeProposal { - type Error = UpgradeClientError; + type Error = DecodingError; fn try_from(raw: RawUpgradeProposal) -> Result { if raw.title.is_empty() { - return Err(UpgradeClientError::InvalidUpgradeProposal { - reason: "title field cannot be empty".to_string(), - }); + return Err(DecodingError::missing_raw_data("upgrade proposal title")); } if raw.description.is_empty() { - return Err(UpgradeClientError::InvalidUpgradeProposal { - reason: "description field cannot be empty".to_string(), - }); + return Err(DecodingError::missing_raw_data( + "upgrade proposal description", + )); } let plan = if let Some(plan) = raw.plan { plan.try_into()? } else { - return Err(UpgradeClientError::InvalidUpgradeProposal { - reason: "plan field cannot be empty".to_string(), - }); + return Err(DecodingError::missing_raw_data("upgrade proposal plan")); }; let upgraded_client_state = raw.upgraded_client_state.ok_or_else(|| { - UpgradeClientError::InvalidUpgradeProposal { - reason: "upgraded client state cannot be empty".to_string(), - } + DecodingError::missing_raw_data("upgrade proposal upgraded client state") })?; Ok(Self { diff --git a/ibc-core/ics24-host/cosmos/src/validate_self_client.rs b/ibc-core/ics24-host/cosmos/src/validate_self_client.rs index 7064fdcd8..96b13306c 100644 --- a/ibc-core/ics24-host/cosmos/src/validate_self_client.rs +++ b/ibc-core/ics24-host/cosmos/src/validate_self_client.rs @@ -1,11 +1,9 @@ use core::time::Duration; use ibc_client_tendermint::types::ClientState as TmClientState; -use ibc_core_client_types::error::ClientError; use ibc_core_client_types::Height; use ibc_core_commitment_types::specs::ProofSpecs; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_handler_types::error::ContextError; +use ibc_core_host_types::error::HostError; use ibc_core_host_types::identifiers::ChainId; use ibc_primitives::prelude::*; use tendermint::trust_threshold::TrustThresholdFraction as TendermintTrustThresholdFraction; @@ -19,66 +17,53 @@ pub trait ValidateSelfClientContext { fn validate_self_tendermint_client( &self, client_state_of_host_on_counterparty: TmClientState, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { client_state_of_host_on_counterparty .validate() - .map_err(ClientError::from)?; + .map_err(|e| { + HostError::invalid_state(format!( + "counterparty client state could not be validated: {e}" + )) + })?; if client_state_of_host_on_counterparty.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); + return Err(HostError::invalid_state("client unexpectedly frozen")); } let self_chain_id = self.chain_id(); + if self_chain_id != &client_state_of_host_on_counterparty.chain_id { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "invalid chain-id. expected: {}, got: {}", - self_chain_id, client_state_of_host_on_counterparty.chain_id - ), - }, - )); + return Err(HostError::invalid_state(format!( + "chain ID: expected `{}`, actual `{}`", + self_chain_id, client_state_of_host_on_counterparty.chain_id + ))); } let latest_height = client_state_of_host_on_counterparty.latest_height; let self_revision_number = self_chain_id.revision_number(); + if self_revision_number != latest_height.revision_number() { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client is not in the same revision as the chain. expected: {}, got: {}", - self_revision_number, - latest_height.revision_number() - ), - }, - )); + return Err(HostError::invalid_state(format!( + "mismatched client revision numbers; expected `{}`, actual `{}`", + self_revision_number, + latest_height.revision_number() + ))); } if latest_height >= self.host_current_height() { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client has latest height {} greater than or equal to chain height {}", - latest_height, - self.host_current_height() - ), - }, - )); + return Err(HostError::invalid_state(format!( + "client latest height `{}` should be less than chain height `{}`", + latest_height, + self.host_current_height() + ))); } if self.proof_specs() != &client_state_of_host_on_counterparty.proof_specs { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client has invalid proof specs. expected: {:?}, got: {:?}", - self.proof_specs(), - client_state_of_host_on_counterparty.proof_specs - ), - }, - )); + return Err(HostError::invalid_state(format!( + "client proof specs; expected `{:?}`, actual `{:?}`", + self.proof_specs(), + client_state_of_host_on_counterparty.proof_specs + ))); } let _ = { @@ -88,45 +73,35 @@ pub trait ValidateSelfClientContext { trust_level.numerator(), trust_level.denominator(), ) - .map_err(|_| ConnectionError::InvalidClientState { - reason: "invalid trust level".to_string(), - })? + .map_err(HostError::invalid_state)? }; if self.unbonding_period() != client_state_of_host_on_counterparty.unbonding_period { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "invalid unbonding period. expected: {:?}, got: {:?}", - self.unbonding_period(), - client_state_of_host_on_counterparty.unbonding_period, - ), - }, - )); + return Err(HostError::invalid_state(format!( + "unbonding period; expected `{:?}`, actual `{:?}`", + self.unbonding_period(), + client_state_of_host_on_counterparty.unbonding_period, + ))); } if client_state_of_host_on_counterparty.unbonding_period < client_state_of_host_on_counterparty.trusting_period { - return Err(ContextError::ConnectionError(ConnectionError::InvalidClientState{ reason: format!( - "unbonding period must be greater than trusting period. unbonding period ({:?}) < trusting period ({:?})", + return Err(HostError::invalid_state(format!( + "counterparty client state: unbonding period must be greater than trusting period; unbonding period ({:?}) < trusting period ({:?})", client_state_of_host_on_counterparty.unbonding_period, client_state_of_host_on_counterparty.trusting_period - )})); + ))); } if !client_state_of_host_on_counterparty.upgrade_path.is_empty() && self.upgrade_path() != client_state_of_host_on_counterparty.upgrade_path { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "invalid upgrade path. expected: {:?}, got: {:?}", - self.upgrade_path(), - client_state_of_host_on_counterparty.upgrade_path - ), - }, - )); + return Err(HostError::invalid_state(format!( + "upgrade path; expected `{:?}`, actual `{:?}`", + self.upgrade_path(), + client_state_of_host_on_counterparty.upgrade_path + ))); } Ok(()) diff --git a/ibc-core/ics24-host/src/context.rs b/ibc-core/ics24-host/src/context.rs index 44451c4f5..a9a84ab53 100644 --- a/ibc-core/ics24-host/src/context.rs +++ b/ibc-core/ics24-host/src/context.rs @@ -8,8 +8,8 @@ use ibc_core_client_types::Height; use ibc_core_commitment_types::commitment::CommitmentPrefix; use ibc_core_connection_types::version::{pick_version, Version as ConnectionVersion}; use ibc_core_connection_types::ConnectionEnd; -use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::IbcEvent; +use ibc_core_host_types::error::HostError; use ibc_core_host_types::identifiers::{ConnectionId, Sequence}; use ibc_core_host_types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, CommitmentPath, ConnectionPath, ReceiptPath, @@ -34,24 +34,21 @@ pub trait ValidationContext { fn get_client_validation_context(&self) -> &Self::V; /// 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; /// Returns the `ConsensusState` of the host (local) chain at a specific height. - fn host_consensus_state( - &self, - height: &Height, - ) -> Result; + fn host_consensus_state(&self, height: &Height) -> Result; /// Returns a natural number, counting how many clients have been created /// thus far. The value of this counter should increase only via method /// `ExecutionContext::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; /// Validates the `ClientState` of the host chain stored on the counterparty /// chain against the host's internal state. @@ -65,13 +62,13 @@ pub trait ValidationContext { fn validate_self_client( &self, client_state_of_host_on_counterparty: Self::HostClientState, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Returns the prefix that the local chain uses in the KV store. 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. @@ -84,33 +81,31 @@ pub trait ValidationContext { fn pick_version( &self, counterparty_candidate_versions: &[ConnectionVersion], - ) -> Result { - let version = pick_version( + ) -> Result { + pick_version( &self.get_compatible_versions(), counterparty_candidate_versions, - )?; - Ok(version) + ) + .map_err(HostError::missing_state) } /// Returns the `ChannelEnd` for the given `port_id` and `chan_id`. - fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result; + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result; /// Returns the sequence number for the next packet to be sent for the given store path - fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) - -> Result; + fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) -> Result; /// Returns the sequence number for the next packet to be received for the given store path - fn get_next_sequence_recv(&self, seq_recv_path: &SeqRecvPath) - -> Result; + fn get_next_sequence_recv(&self, seq_recv_path: &SeqRecvPath) -> Result; /// Returns the sequence number for the next packet to be acknowledged for the given store path - fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result; + fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result; /// Returns the packet commitment for the given store path fn get_packet_commitment( &self, commitment_path: &CommitmentPath, - ) -> Result; + ) -> Result; /// Returns the packet receipt for the given store path. This receipt is /// used to acknowledge the successful processing of a received packet, and @@ -120,18 +115,18 @@ pub trait ValidationContext { /// indicating the packet has already been processed. If the receipt is /// absent, return `Receipt::None`, indicating the packet has not been /// received. - fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result; + fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result; /// Returns the packet acknowledgement for the given store path fn get_packet_acknowledgement( &self, ack_path: &AckPath, - ) -> 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 /// `ExecutionContext::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; @@ -144,7 +139,7 @@ pub trait ValidationContext { /// Validates the `signer` field of IBC messages, which represents the address /// of the user/relayer that signed the given message. - fn validate_message_signer(&self, signer: &Signer) -> Result<(), ContextError>; + fn validate_message_signer(&self, signer: &Signer) -> Result<(), HostError>; } /// Context to be implemented by the host that provides all "write-only" methods. @@ -158,93 +153,93 @@ pub trait ExecutionContext: ValidationContext { /// Called upon client creation. /// Increases the counter, that keeps track of how many clients have been created. - fn increase_client_counter(&mut self) -> Result<(), ContextError>; + fn increase_client_counter(&mut self) -> Result<(), HostError>; /// Stores the given connection_end at path fn store_connection( &mut self, connection_path: &ConnectionPath, connection_end: ConnectionEnd, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Stores the given connection_id at a path associated with the client_id. fn store_connection_to_client( &mut self, client_connection_path: &ClientConnectionPath, conn_id: ConnectionId, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Called upon connection identifier creation (Init or Try process). /// Increases the counter which keeps track of how many connections have been created. - fn increase_connection_counter(&mut self) -> Result<(), ContextError>; + fn increase_connection_counter(&mut self) -> Result<(), HostError>; /// Stores the given packet commitment at the given store path fn store_packet_commitment( &mut self, commitment_path: &CommitmentPath, commitment: PacketCommitment, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Deletes the packet commitment at the given store path fn delete_packet_commitment( &mut self, commitment_path: &CommitmentPath, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Stores the given packet receipt at the given store path fn store_packet_receipt( &mut self, receipt_path: &ReceiptPath, receipt: Receipt, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Stores the given packet acknowledgement at the given store path fn store_packet_acknowledgement( &mut self, ack_path: &AckPath, ack_commitment: AcknowledgementCommitment, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Deletes the packet acknowledgement at the given store path - fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError>; + fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), HostError>; /// Stores the given channel_end at a path associated with the port_id and channel_id. fn store_channel( &mut self, channel_end_path: &ChannelEndPath, channel_end: ChannelEnd, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Stores the given `nextSequenceSend` number at the given store path fn store_next_sequence_send( &mut self, seq_send_path: &SeqSendPath, seq: Sequence, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Stores the given `nextSequenceRecv` number at the given store path fn store_next_sequence_recv( &mut self, seq_recv_path: &SeqRecvPath, seq: Sequence, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Stores the given `nextSequenceAck` number at the given store path fn store_next_sequence_ack( &mut self, seq_ack_path: &SeqAckPath, seq: Sequence, - ) -> Result<(), ContextError>; + ) -> Result<(), HostError>; /// Called upon channel identifier creation (Init or Try message processing). /// Increases the counter, that keeps track of how many channels have been created. - fn increase_channel_counter(&mut self) -> Result<(), ContextError>; + fn increase_channel_counter(&mut self) -> Result<(), HostError>; /// Emit the given IBC event - fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError>; + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError>; /// Log the given message. - fn log_message(&mut self, message: String) -> Result<(), ContextError>; + fn log_message(&mut self, message: String) -> Result<(), HostError>; } /// Convenient type alias for `ClientStateRef`, providing access to client diff --git a/ibc-core/ics24-host/types/Cargo.toml b/ibc-core/ics24-host/types/Cargo.toml index 7ad95dcc0..e4953c720 100644 --- a/ibc-core/ics24-host/types/Cargo.toml +++ b/ibc-core/ics24-host/types/Cargo.toml @@ -20,9 +20,11 @@ all-features = true [dependencies] # external dependencies +base64 = { workspace = true } borsh = { workspace = true, optional = true } derive_more = { workspace = true } displaydoc = { workspace = true } +prost = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, optional = true } diff --git a/ibc-core/ics24-host/types/src/error.rs b/ibc-core/ics24-host/types/src/error.rs index 4df4c4dd9..580fcc90b 100644 --- a/ibc-core/ics24-host/types/src/error.rs +++ b/ibc-core/ics24-host/types/src/error.rs @@ -1,22 +1,165 @@ +//! Foundational error types that are applicable across multiple ibc-rs workspaces. + +use alloc::string::{FromUtf8Error, String}; +use core::num::{ParseIntError, TryFromIntError}; +use core::str::Utf8Error; + +use base64::DecodeError as Base64Error; use displaydoc::Display; use ibc_primitives::prelude::*; +use ibc_primitives::proto::Error as ProtoError; +use prost::DecodeError as ProstError; + +/// Errors that originate from host implementations. +#[derive(Debug, Display)] +pub enum HostError { + /// invalid state: `{description}` + InvalidState { description: String }, + /// missing state: `{description}` + MissingState { description: String }, + /// failed to update store: `{description}` + FailedToStore { description: String }, + /// failed to retrieve from store: `{description}` + FailedToRetrieve { description: String }, + /// other error: `{description}` + Other { description: String }, +} +impl HostError { + pub fn invalid_state(description: T) -> Self { + Self::InvalidState { + description: description.to_string(), + } + } + + pub fn missing_state(description: T) -> Self { + Self::MissingState { + description: description.to_string(), + } + } + + pub fn failed_to_retrieve(description: T) -> Self { + Self::FailedToRetrieve { + description: description.to_string(), + } + } + + pub fn failed_to_store(description: T) -> Self { + Self::FailedToStore { + description: description.to_string(), + } + } +} + +/// Errors that arise when parsing identifiers. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Display)] pub enum IdentifierError { - /// identifier `{id}` has invalid length; must be between `{min}` and `{max}` characters - InvalidLength { id: String, min: u64, max: u64 }, - /// identifier `{id}` must only contain alphanumeric characters or `.`, `_`, `+`, `-`, `#`, - `[`, `]`, `<`, `>` - InvalidCharacter { id: String }, - /// identifier prefix `{prefix}` is invalid - InvalidPrefix { prefix: String }, - /// chain identifier is not formatted with revision number - UnformattedRevisionNumber { chain_id: String }, - /// revision number overflowed - RevisionNumberOverflow, - /// String `{value}` cannot be converted to packet sequence, error: `{reason}` - InvalidStringAsSequence { value: String, reason: String }, + /// id `{actual}` has invalid length; must be between [`{min}`,`{max}`) + InvalidLength { actual: String, min: u64, max: u64 }, + /// id `{0}` can only contain alphanumeric characters or `.`, `_`, `+`, `-`, `#`, - `[`, `]`, `<`, `>` + InvalidCharacter(String), + /// invalid prefix `{0}` + InvalidPrefix(String), + /// failed to parse: `{description}` + FailedToParse { description: String }, + /// overflowed revision number + OverflowedRevisionNumber, +} + +/// Errors that occur during the process of decoding, deserializing, +/// and/or converting raw types into domain types. +#[derive(Debug, Display)] +pub enum DecodingError { + /// identifier error: {0} + Identifier(IdentifierError), + /// base64 decoding error: {0} + Base64(Base64Error), + /// utf-8 String decoding error: {0} + StringUtf8(FromUtf8Error), + /// utf-8 str decoding error: {0} + StrUtf8(Utf8Error), + /// integer parsing error: {0} + ParseInt(ParseIntError), + /// integer TryFrom error: {0} + TryFromInt(TryFromIntError), + /// protobuf decoding error: {0} + Protobuf(ProtoError), + /// prost decoding error: {0} + Prost(ProstError), + /// invalid JSON data: `{description}` + InvalidJson { description: String }, + /// invalid raw data: `{description}` + InvalidRawData { description: String }, + /// missing raw data: `{description}` + MissingRawData { description: String }, + /// mismatched resource name: expected `{expected}`, actual `{actual}` + MismatchedResourceName { expected: String, actual: String }, + /// unknown type URL `{0}` + UnknownTypeUrl(String), +} + +impl DecodingError { + pub fn invalid_raw_data(description: T) -> Self { + Self::InvalidRawData { + description: description.to_string(), + } + } + + pub fn missing_raw_data(description: T) -> Self { + Self::MissingRawData { + description: description.to_string(), + } + } +} + +impl From for DecodingError { + fn from(e: IdentifierError) -> Self { + Self::Identifier(e) + } +} + +impl From for DecodingError { + fn from(e: ProtoError) -> Self { + Self::Protobuf(e) + } +} + +impl From for DecodingError { + fn from(e: Base64Error) -> Self { + Self::Base64(e) + } +} + +impl From for DecodingError { + fn from(e: FromUtf8Error) -> Self { + Self::StringUtf8(e) + } +} + +impl From for DecodingError { + fn from(e: Utf8Error) -> Self { + Self::StrUtf8(e) + } +} + +impl From for DecodingError { + fn from(e: ParseIntError) -> Self { + Self::ParseInt(e) + } +} + +impl From for DecodingError { + fn from(e: TryFromIntError) -> Self { + Self::TryFromInt(e) + } } #[cfg(feature = "std")] impl std::error::Error for IdentifierError {} + +#[cfg(feature = "std")] +impl std::error::Error for DecodingError {} + +#[cfg(feature = "std")] +impl std::error::Error for HostError {} diff --git a/ibc-core/ics24-host/types/src/identifiers/chain_id.rs b/ibc-core/ics24-host/types/src/identifiers/chain_id.rs index d37ad4e1f..c96ccc36f 100644 --- a/ibc-core/ics24-host/types/src/identifiers/chain_id.rs +++ b/ibc-core/ics24-host/types/src/identifiers/chain_id.rs @@ -98,7 +98,7 @@ impl ChainId { let inc_revision_number = self .revision_number .checked_add(1) - .ok_or(IdentifierError::RevisionNumberOverflow)?; + .ok_or(IdentifierError::OverflowedRevisionNumber)?; self.id = format!("{}-{}", chain_name, inc_revision_number); self.revision_number = inc_revision_number; Ok(()) @@ -312,8 +312,8 @@ fn parse_chain_id_string(chain_id_str: &str) -> Result<(&str, u64), IdentifierEr .ok() .map(|revision_number| (chain_name, revision_number)) }) - .ok_or(IdentifierError::UnformattedRevisionNumber { - chain_id: chain_id_str.to_string(), + .ok_or(IdentifierError::FailedToParse { + description: format!("revision number for chain ID `{chain_id_str}`"), }) } diff --git a/ibc-core/ics24-host/types/src/identifiers/sequence.rs b/ibc-core/ics24-host/types/src/identifiers/sequence.rs index 9e8b2ccbc..0ab91f24c 100644 --- a/ibc-core/ics24-host/types/src/identifiers/sequence.rs +++ b/ibc-core/ics24-host/types/src/identifiers/sequence.rs @@ -25,9 +25,8 @@ impl core::str::FromStr for Sequence { fn from_str(s: &str) -> Result { Ok(Self::from(s.parse::().map_err(|e| { - IdentifierError::InvalidStringAsSequence { - value: s.to_string(), - reason: e.to_string(), + IdentifierError::FailedToParse { + description: format!("sequence `{s}`: {e}"), } })?)) } diff --git a/ibc-core/ics24-host/types/src/lib.rs b/ibc-core/ics24-host/types/src/lib.rs index f466de4c2..1fae79ac6 100644 --- a/ibc-core/ics24-host/types/src/lib.rs +++ b/ibc-core/ics24-host/types/src/lib.rs @@ -12,6 +12,8 @@ rust_2018_idioms )] +extern crate alloc; + #[cfg(feature = "std")] extern crate std; diff --git a/ibc-core/ics24-host/types/src/validate.rs b/ibc-core/ics24-host/types/src/validate.rs index 75e3494ef..614cfd8a4 100644 --- a/ibc-core/ics24-host/types/src/validate.rs +++ b/ibc-core/ics24-host/types/src/validate.rs @@ -17,7 +17,7 @@ pub fn validate_identifier_chars(id: &str) -> Result<(), Error> { .chars() .all(|c| c.is_alphanumeric() || VALID_SPECIAL_CHARS.contains(c)) { - return Err(Error::InvalidCharacter { id: id.into() }); + return Err(Error::InvalidCharacter(id.into())); } // All good! @@ -35,7 +35,7 @@ pub fn validate_identifier_length(id: &str, min: u64, max: u64) -> Result<(), Er Ok(()) } else { Err(Error::InvalidLength { - id: id.into(), + actual: id.into(), min, max, }) @@ -67,17 +67,17 @@ pub fn validate_prefix_length( pub fn validate_named_u64_index(id: &str, name: &str) -> Result<(), Error> { let number_s = id .strip_prefix(name) - .ok_or_else(|| Error::InvalidPrefix { prefix: id.into() })? + .ok_or_else(|| Error::InvalidPrefix(id.into()))? .strip_prefix('-') - .ok_or_else(|| Error::InvalidPrefix { prefix: id.into() })?; + .ok_or_else(|| Error::InvalidPrefix(id.into()))?; if number_s.starts_with('0') && number_s.len() > 1 { - return Err(Error::InvalidPrefix { prefix: id.into() }); + return Err(Error::InvalidPrefix(id.into())); } - _ = number_s - .parse::() - .map_err(|_| Error::InvalidPrefix { prefix: id.into() })?; + _ = number_s.parse::().map_err(|e| Error::FailedToParse { + description: format!("named index `{id}`: {e}"), + })?; Ok(()) } diff --git a/ibc-core/ics25-handler/src/entrypoint.rs b/ibc-core/ics25-handler/src/entrypoint.rs index 3c271a9bb..f9a0cabc3 100644 --- a/ibc-core/ics25-handler/src/entrypoint.rs +++ b/ibc-core/ics25-handler/src/entrypoint.rs @@ -17,11 +17,13 @@ use ibc_core_connection::handler::{ conn_open_ack, conn_open_confirm, conn_open_init, conn_open_try, }; use ibc_core_connection::types::msgs::ConnectionMsg; -use ibc_core_handler_types::error::ContextError; +use ibc_core_handler_types::error::HandlerError; use ibc_core_handler_types::msgs::MsgEnvelope; +use ibc_core_host::types::error::HostError; use ibc_core_host::{ExecutionContext, ValidationContext}; use ibc_core_router::router::Router; use ibc_core_router::types::error::RouterError; +use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; /// Entrypoint which performs both validation and message execution @@ -29,7 +31,7 @@ pub fn dispatch( ctx: &mut Ctx, router: &mut impl Router, msg: MsgEnvelope, -) -> Result<(), ContextError> +) -> Result<(), HandlerError> where Ctx: ExecutionContext, <::ClientStateRef as TryFrom>::Error: Into, @@ -48,7 +50,7 @@ where /// That is, the state transition of message `i` must be applied before /// message `i+1` is validated. This is equivalent to calling /// `dispatch()` on each successively. -pub fn validate(ctx: &Ctx, router: &impl Router, msg: MsgEnvelope) -> Result<(), ContextError> +pub fn validate(ctx: &Ctx, router: &impl Router, msg: MsgEnvelope) -> Result<(), HandlerError> where Ctx: ValidationContext, <::ClientStateRef as TryFrom>::Error: Into, @@ -56,69 +58,66 @@ where { match msg { MsgEnvelope::Client(msg) => match msg { - ClientMsg::CreateClient(msg) => create_client::validate(ctx, msg), + ClientMsg::CreateClient(msg) => create_client::validate(ctx, msg)?, ClientMsg::UpdateClient(msg) => { - update_client::validate(ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)) + update_client::validate(ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg))? } ClientMsg::Misbehaviour(msg) => { - update_client::validate(ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg)) + update_client::validate(ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg))? } - ClientMsg::UpgradeClient(msg) => upgrade_client::validate(ctx, msg), + ClientMsg::UpgradeClient(msg) => upgrade_client::validate(ctx, msg)?, ClientMsg::RecoverClient(_msg) => { // Recover client messages are not dispatched by ibc-rs as they can only be // authorized via a passing governance proposal - Ok(()) } }, MsgEnvelope::Connection(msg) => match msg { - ConnectionMsg::OpenInit(msg) => conn_open_init::validate(ctx, msg), - ConnectionMsg::OpenTry(msg) => conn_open_try::validate(ctx, msg), - ConnectionMsg::OpenAck(msg) => conn_open_ack::validate(ctx, msg), - ConnectionMsg::OpenConfirm(msg) => conn_open_confirm::validate(ctx, &msg), + ConnectionMsg::OpenInit(msg) => conn_open_init::validate(ctx, msg)?, + ConnectionMsg::OpenTry(msg) => conn_open_try::validate(ctx, msg)?, + ConnectionMsg::OpenAck(msg) => conn_open_ack::validate(ctx, msg)?, + ConnectionMsg::OpenConfirm(msg) => conn_open_confirm::validate(ctx, &msg)?, }, MsgEnvelope::Channel(msg) => { let port_id = channel_msg_to_port_id(&msg); - let module_id = router - .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + let module_id = router.lookup_module(port_id).ok_or(RouterError::Host( + HostError::missing_state(format!("missing module ID for port {}", port_id.clone())), + ))?; let module = router .get_route(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { - ChannelMsg::OpenInit(msg) => chan_open_init_validate(ctx, module, msg), - ChannelMsg::OpenTry(msg) => chan_open_try_validate(ctx, module, msg), - ChannelMsg::OpenAck(msg) => chan_open_ack_validate(ctx, module, msg), - ChannelMsg::OpenConfirm(msg) => chan_open_confirm_validate(ctx, module, msg), - ChannelMsg::CloseInit(msg) => chan_close_init_validate(ctx, module, msg), - ChannelMsg::CloseConfirm(msg) => chan_close_confirm_validate(ctx, module, msg), + ChannelMsg::OpenInit(msg) => chan_open_init_validate(ctx, module, msg)?, + ChannelMsg::OpenTry(msg) => chan_open_try_validate(ctx, module, msg)?, + ChannelMsg::OpenAck(msg) => chan_open_ack_validate(ctx, module, msg)?, + ChannelMsg::OpenConfirm(msg) => chan_open_confirm_validate(ctx, module, msg)?, + ChannelMsg::CloseInit(msg) => chan_close_init_validate(ctx, module, msg)?, + ChannelMsg::CloseConfirm(msg) => chan_close_confirm_validate(ctx, module, msg)?, } } MsgEnvelope::Packet(msg) => { let port_id = packet_msg_to_port_id(&msg); - let module_id = router - .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + let module_id = router.lookup_module(port_id).ok_or(RouterError::Host( + HostError::missing_state(format!("missing module ID for port {}", port_id.clone())), + ))?; let module = router .get_route(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { - PacketMsg::Recv(msg) => recv_packet_validate(ctx, msg), - PacketMsg::Ack(msg) => acknowledgement_packet_validate(ctx, module, msg), + PacketMsg::Recv(msg) => recv_packet_validate(ctx, msg)?, + PacketMsg::Ack(msg) => acknowledgement_packet_validate(ctx, module, msg)?, PacketMsg::Timeout(msg) => { - timeout_packet_validate(ctx, module, TimeoutMsgType::Timeout(msg)) + timeout_packet_validate(ctx, module, TimeoutMsgType::Timeout(msg))? } PacketMsg::TimeoutOnClose(msg) => { - timeout_packet_validate(ctx, module, TimeoutMsgType::TimeoutOnClose(msg)) + timeout_packet_validate(ctx, module, TimeoutMsgType::TimeoutOnClose(msg))? } } } - } + }; + + Ok(()) } /// Entrypoint which only performs message execution @@ -126,74 +125,71 @@ pub fn execute( ctx: &mut Ctx, router: &mut impl Router, msg: MsgEnvelope, -) -> Result<(), ContextError> +) -> Result<(), HandlerError> where Ctx: ExecutionContext, <::ClientStateMut as TryFrom>::Error: Into, { match msg { MsgEnvelope::Client(msg) => match msg { - ClientMsg::CreateClient(msg) => create_client::execute(ctx, msg), + ClientMsg::CreateClient(msg) => create_client::execute(ctx, msg)?, ClientMsg::UpdateClient(msg) => { - update_client::execute(ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)) + update_client::execute(ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg))? } ClientMsg::Misbehaviour(msg) => { - update_client::execute(ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg)) + update_client::execute(ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg))? } - ClientMsg::UpgradeClient(msg) => upgrade_client::execute(ctx, msg), + ClientMsg::UpgradeClient(msg) => upgrade_client::execute(ctx, msg)?, ClientMsg::RecoverClient(_msg) => { // Recover client messages are not dispatched by ibc-rs as they can only be // authorized via a passing governance proposal - Ok(()) } }, MsgEnvelope::Connection(msg) => match msg { - ConnectionMsg::OpenInit(msg) => conn_open_init::execute(ctx, msg), - ConnectionMsg::OpenTry(msg) => conn_open_try::execute(ctx, msg), - ConnectionMsg::OpenAck(msg) => conn_open_ack::execute(ctx, msg), - ConnectionMsg::OpenConfirm(msg) => conn_open_confirm::execute(ctx, &msg), + ConnectionMsg::OpenInit(msg) => conn_open_init::execute(ctx, msg)?, + ConnectionMsg::OpenTry(msg) => conn_open_try::execute(ctx, msg)?, + ConnectionMsg::OpenAck(msg) => conn_open_ack::execute(ctx, msg)?, + ConnectionMsg::OpenConfirm(msg) => conn_open_confirm::execute(ctx, &msg)?, }, MsgEnvelope::Channel(msg) => { let port_id = channel_msg_to_port_id(&msg); - let module_id = router - .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + let module_id = router.lookup_module(port_id).ok_or(RouterError::Host( + HostError::missing_state(format!("missing module ID for port {}", port_id.clone())), + ))?; let module = router .get_route_mut(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { - ChannelMsg::OpenInit(msg) => chan_open_init_execute(ctx, module, msg), - ChannelMsg::OpenTry(msg) => chan_open_try_execute(ctx, module, msg), - ChannelMsg::OpenAck(msg) => chan_open_ack_execute(ctx, module, msg), - ChannelMsg::OpenConfirm(msg) => chan_open_confirm_execute(ctx, module, msg), - ChannelMsg::CloseInit(msg) => chan_close_init_execute(ctx, module, msg), - ChannelMsg::CloseConfirm(msg) => chan_close_confirm_execute(ctx, module, msg), + ChannelMsg::OpenInit(msg) => chan_open_init_execute(ctx, module, msg)?, + ChannelMsg::OpenTry(msg) => chan_open_try_execute(ctx, module, msg)?, + ChannelMsg::OpenAck(msg) => chan_open_ack_execute(ctx, module, msg)?, + ChannelMsg::OpenConfirm(msg) => chan_open_confirm_execute(ctx, module, msg)?, + ChannelMsg::CloseInit(msg) => chan_close_init_execute(ctx, module, msg)?, + ChannelMsg::CloseConfirm(msg) => chan_close_confirm_execute(ctx, module, msg)?, } } MsgEnvelope::Packet(msg) => { let port_id = packet_msg_to_port_id(&msg); - let module_id = router - .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + let module_id = router.lookup_module(port_id).ok_or(RouterError::Host( + HostError::missing_state(format!("missing module ID for port {}", port_id.clone())), + ))?; let module = router .get_route_mut(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { - PacketMsg::Recv(msg) => recv_packet_execute(ctx, module, msg), - PacketMsg::Ack(msg) => acknowledgement_packet_execute(ctx, module, msg), + PacketMsg::Recv(msg) => recv_packet_execute(ctx, module, msg)?, + PacketMsg::Ack(msg) => acknowledgement_packet_execute(ctx, module, msg)?, PacketMsg::Timeout(msg) => { - timeout_packet_execute(ctx, module, TimeoutMsgType::Timeout(msg)) + timeout_packet_execute(ctx, module, TimeoutMsgType::Timeout(msg))? } PacketMsg::TimeoutOnClose(msg) => { - timeout_packet_execute(ctx, module, TimeoutMsgType::TimeoutOnClose(msg)) + timeout_packet_execute(ctx, module, TimeoutMsgType::TimeoutOnClose(msg))? } } } } + + Ok(()) } diff --git a/ibc-core/ics25-handler/types/src/error.rs b/ibc-core/ics25-handler/types/src/error.rs index 5bea4b54d..fce14adc9 100644 --- a/ibc-core/ics25-handler/types/src/error.rs +++ b/ibc-core/ics25-handler/types/src/error.rs @@ -1,48 +1,34 @@ -//! Defines the context error type +//! Defines the handler error type use derive_more::From; use displaydoc::Display; -use ibc_core_channel_types::error::{ChannelError, PacketError}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_client_types::error::ClientError; use ibc_core_connection_types::error::ConnectionError; use ibc_core_router_types::error::RouterError; use ibc_primitives::prelude::*; -/// Top-level error +/// Top-level type that surfaces errors from the core ibc-rs crates. #[derive(Debug, Display, From)] -pub enum ContextError { +pub enum HandlerError { /// ICS02 Client error: {0} - ClientError(ClientError), + Client(ClientError), /// ICS03 Connection error: {0} - ConnectionError(ConnectionError), + Connection(ConnectionError), /// ICS04 Channel error: {0} - ChannelError(ChannelError), - /// ICS04 Packet error: {0} - PacketError(PacketError), + Channel(ChannelError), /// ICS26 Routing error: {0} - RouterError(RouterError), -} - -impl From for ClientError { - fn from(context_error: ContextError) -> Self { - match context_error { - ContextError::ClientError(e) => e, - _ => ClientError::Other { - description: context_error.to_string(), - }, - } - } + Router(RouterError), } #[cfg(feature = "std")] -impl std::error::Error for ContextError { +impl std::error::Error for HandlerError { 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), - Self::RouterError(e) => Some(e), + Self::Client(e) => Some(e), + Self::Connection(e) => Some(e), + Self::Channel(e) => Some(e), + Self::Router(e) => Some(e), } } } diff --git a/ibc-core/ics25-handler/types/src/events.rs b/ibc-core/ics25-handler/types/src/events.rs index aa62ea813..77ebe79e1 100644 --- a/ibc-core/ics25-handler/types/src/events.rs +++ b/ibc-core/ics25-handler/types/src/events.rs @@ -1,51 +1,13 @@ //! Defines events emitted during handling of IBC messages -use displaydoc::Display; -use ibc_core_channel_types::{error as channel_error, events as ChannelEvents}; -use ibc_core_client_types::error as client_error; +use ibc_core_channel_types::events as ChannelEvents; use ibc_core_client_types::events::{self as ClientEvents}; -use ibc_core_connection_types::{error as connection_error, events as ConnectionEvents}; -use ibc_core_host_types::error::IdentifierError; +use ibc_core_connection_types::events as ConnectionEvents; +use ibc_core_host_types::error::DecodingError; use ibc_core_router_types::event::ModuleEvent; use ibc_primitives::prelude::*; -use ibc_primitives::TimestampError; use tendermint::abci; -/// All error variants related to IBC events -#[derive(Debug, Display)] -pub enum Error { - /// error parsing height - Height, - /// parse error: `{0}` - Parse(IdentifierError), - /// 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(TimestampError), - /// incorrect event type: `{event}` - IncorrectEventType { event: String }, - /// module event cannot use core event types: `{event:?}` - MalformedModuleEvent { event: ModuleEvent }, -} - -#[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), - _ => None, - } - } -} - const MESSAGE_EVENT: &str = "message"; /// Events created by the IBC component of a chain, destined for a relayer. @@ -93,7 +55,7 @@ pub enum IbcEvent { } impl TryFrom for abci::Event { - type Error = Error; + type Error = DecodingError; fn try_from(event: IbcEvent) -> Result { Ok(match event { @@ -111,11 +73,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()?, + IbcEvent::ReceivePacket(event) => event.try_into()?, + IbcEvent::WriteAcknowledgement(event) => event.try_into()?, + IbcEvent::AcknowledgePacket(event) => event.try_into()?, + IbcEvent::TimeoutPacket(event) => event.try_into()?, IbcEvent::ChannelClosed(event) => event.into(), IbcEvent::Module(event) => event.into(), IbcEvent::Message(event) => abci::Event { diff --git a/ibc-core/ics25-handler/types/src/msgs.rs b/ibc-core/ics25-handler/types/src/msgs.rs index 1272c0aff..a47a9b702 100644 --- a/ibc-core/ics25-handler/types/src/msgs.rs +++ b/ibc-core/ics25-handler/types/src/msgs.rs @@ -17,7 +17,7 @@ use ibc_core_connection_types::msgs::{ MsgConnectionOpenTry, CONN_OPEN_ACK_TYPE_URL, CONN_OPEN_CONFIRM_TYPE_URL, CONN_OPEN_INIT_TYPE_URL, CONN_OPEN_TRY_TYPE_URL, }; -use ibc_core_router_types::error::RouterError; +use ibc_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::Protobuf; @@ -38,79 +38,44 @@ pub enum MsgEnvelope { #[allow(deprecated)] impl TryFrom for MsgEnvelope { - type Error = RouterError; + type Error = DecodingError; fn try_from(any_msg: Any) -> Result { match any_msg.type_url.as_str() { // ICS2 messages CREATE_CLIENT_TYPE_URL => { // Pop out the message and then wrap it in the corresponding type. - let domain_msg = MsgCreateClient::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgCreateClient::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Client(ClientMsg::CreateClient(domain_msg))) } UPDATE_CLIENT_TYPE_URL => { - let domain_msg = MsgUpdateClient::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgUpdateClient::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Client(ClientMsg::UpdateClient(domain_msg))) } UPGRADE_CLIENT_TYPE_URL => { - let domain_msg = MsgUpgradeClient::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgUpgradeClient::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Client(ClientMsg::UpgradeClient(domain_msg))) } SUBMIT_MISBEHAVIOUR_TYPE_URL => { - let domain_msg = - MsgSubmitMisbehaviour::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgSubmitMisbehaviour::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Client(ClientMsg::Misbehaviour(domain_msg))) } // ICS03 CONN_OPEN_INIT_TYPE_URL => { - let domain_msg = - MsgConnectionOpenInit::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgConnectionOpenInit::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenInit(domain_msg))) } CONN_OPEN_TRY_TYPE_URL => { - let domain_msg = MsgConnectionOpenTry::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgConnectionOpenTry::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenTry(domain_msg))) } CONN_OPEN_ACK_TYPE_URL => { - let domain_msg = MsgConnectionOpenAck::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgConnectionOpenAck::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenAck(domain_msg))) } CONN_OPEN_CONFIRM_TYPE_URL => { - let domain_msg = - MsgConnectionOpenConfirm::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgConnectionOpenConfirm::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenConfirm( domain_msg, ))) @@ -118,91 +83,48 @@ impl TryFrom for MsgEnvelope { // ICS04 channel messages CHAN_OPEN_INIT_TYPE_URL => { - let domain_msg = MsgChannelOpenInit::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgChannelOpenInit::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenInit(domain_msg))) } CHAN_OPEN_TRY_TYPE_URL => { - let domain_msg = MsgChannelOpenTry::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgChannelOpenTry::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenTry(domain_msg))) } CHAN_OPEN_ACK_TYPE_URL => { - let domain_msg = MsgChannelOpenAck::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgChannelOpenAck::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenAck(domain_msg))) } CHAN_OPEN_CONFIRM_TYPE_URL => { - let domain_msg = - MsgChannelOpenConfirm::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgChannelOpenConfirm::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenConfirm(domain_msg))) } CHAN_CLOSE_INIT_TYPE_URL => { - let domain_msg = MsgChannelCloseInit::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgChannelCloseInit::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Channel(ChannelMsg::CloseInit(domain_msg))) } CHAN_CLOSE_CONFIRM_TYPE_URL => { - let domain_msg = - MsgChannelCloseConfirm::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgChannelCloseConfirm::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Channel(ChannelMsg::CloseConfirm(domain_msg))) } // ICS04 packet messages RECV_PACKET_TYPE_URL => { - let domain_msg = MsgRecvPacket::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgRecvPacket::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Packet(PacketMsg::Recv(domain_msg))) } ACKNOWLEDGEMENT_TYPE_URL => { - let domain_msg = MsgAcknowledgement::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgAcknowledgement::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Packet(PacketMsg::Ack(domain_msg))) } TIMEOUT_TYPE_URL => { - let domain_msg = MsgTimeout::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgTimeout::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Packet(PacketMsg::Timeout(domain_msg))) } TIMEOUT_ON_CLOSE_TYPE_URL => { - let domain_msg = MsgTimeoutOnClose::decode_vec(&any_msg.value).map_err(|e| { - RouterError::MalformedMessageBytes { - reason: e.to_string(), - } - })?; + let domain_msg = MsgTimeoutOnClose::decode_vec(&any_msg.value)?; Ok(MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(domain_msg))) } - _ => Err(RouterError::UnknownMessageTypeUrl { - url: any_msg.type_url, - }), + + _ => Err(DecodingError::UnknownTypeUrl(any_msg.type_url))?, } } } diff --git a/ibc-core/ics26-routing/src/module.rs b/ibc-core/ics26-routing/src/module.rs index 26b296713..a0877b98d 100644 --- a/ibc-core/ics26-routing/src/module.rs +++ b/ibc-core/ics26-routing/src/module.rs @@ -3,7 +3,7 @@ use core::fmt::Debug; use ibc_core_channel_types::acknowledgement::Acknowledgement; use ibc_core_channel_types::channel::{Counterparty, Order}; -use ibc_core_channel_types::error::{ChannelError, PacketError}; +use ibc_core_channel_types::error::ChannelError; use ibc_core_channel_types::packet::Packet; use ibc_core_channel_types::Version; use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; @@ -134,14 +134,14 @@ pub trait Module: Debug { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> Result<(), PacketError>; + ) -> Result<(), ChannelError>; fn on_acknowledgement_packet_execute( &mut self, _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>); + ) -> (ModuleExtras, Result<(), ChannelError>); /// Note: `MsgTimeout` and `MsgTimeoutOnClose` use the same callback @@ -149,7 +149,7 @@ pub trait Module: Debug { &self, packet: &Packet, relayer: &Signer, - ) -> Result<(), PacketError>; + ) -> Result<(), ChannelError>; /// Note: `MsgTimeout` and `MsgTimeoutOnClose` use the same callback @@ -157,5 +157,5 @@ pub trait Module: Debug { &mut self, packet: &Packet, relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>); + ) -> (ModuleExtras, Result<(), ChannelError>); } diff --git a/ibc-core/ics26-routing/types/src/error.rs b/ibc-core/ics26-routing/types/src/error.rs index 01fb3c6c6..19ad7c2ad 100644 --- a/ibc-core/ics26-routing/types/src/error.rs +++ b/ibc-core/ics26-routing/types/src/error.rs @@ -1,18 +1,14 @@ use displaydoc::Display; -use ibc_core_host_types::identifiers::PortId; +use ibc_core_host_types::error::HostError; use ibc_primitives::prelude::*; /// Error type for the router module. -#[derive(Debug, Display)] +#[derive(Debug, Display, derive_more::From)] pub enum RouterError { - /// unknown type URL `{url}` - UnknownMessageTypeUrl { url: String }, - /// the message is malformed and cannot be decoded error: `{reason}` - MalformedMessageBytes { reason: String }, - /// port `{port_id}` is unknown - UnknownPort { port_id: PortId }, - /// module not found - ModuleNotFound, + /// host error: {0} + Host(HostError), + /// missing module + MissingModule, } #[cfg(feature = "std")] diff --git a/ibc-primitives/src/lib.rs b/ibc-primitives/src/lib.rs index 5aa99f4e5..277c794cd 100644 --- a/ibc-primitives/src/lib.rs +++ b/ibc-primitives/src/lib.rs @@ -32,5 +32,5 @@ pub mod serializers; pub mod proto { pub use ibc_proto::google::protobuf::{Any, Duration, Timestamp}; - pub use ibc_proto::Protobuf; + pub use ibc_proto::{Error, Protobuf}; } diff --git a/ibc-primitives/src/types/timestamp.rs b/ibc-primitives/src/types/timestamp.rs index 7551db5e7..fe2b043dd 100644 --- a/ibc-primitives/src/types/timestamp.rs +++ b/ibc-primitives/src/types/timestamp.rs @@ -13,7 +13,6 @@ use ibc_proto::Protobuf; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use tendermint::Time; -use time::error::ComponentRange; use time::macros::offset; use time::{OffsetDateTime, PrimitiveDateTime}; @@ -47,7 +46,7 @@ impl Timestamp { pub fn from_unix_timestamp(secs: u64, nanos: u32) -> Result { if nanos > 999_999_999 { - return Err(TimestampError::DateOutOfRange); + return Err(TimestampError::InvalidDate); } let total_nanos = secs as i128 * 1_000_000_000 + nanos as i128; @@ -66,7 +65,7 @@ impl Timestamp { 1970..=9999 => Ok(Self { time: PrimitiveDateTime::new(t.date(), t.time()), }), - _ => Err(TimestampError::DateOutOfRange), + _ => Err(TimestampError::InvalidDate), } } @@ -168,11 +167,11 @@ impl Add for Timestamp { type Output = Result; fn add(self, rhs: Duration) -> Self::Output { - let duration = rhs.try_into().map_err(|_| TimestampError::DateOutOfRange)?; + let duration = rhs.try_into().map_err(|_| TimestampError::InvalidDate)?; let t = self .time .checked_add(duration) - .ok_or(TimestampError::DateOutOfRange)?; + .ok_or(TimestampError::InvalidDate)?; Self::from_utc(t.assume_utc()) } } @@ -181,11 +180,11 @@ impl Sub for Timestamp { type Output = Result; fn sub(self, rhs: Duration) -> Self::Output { - let duration = rhs.try_into().map_err(|_| TimestampError::DateOutOfRange)?; + let duration = rhs.try_into().map_err(|_| TimestampError::InvalidDate)?; let t = self .time .checked_sub(duration) - .ok_or(TimestampError::DateOutOfRange)?; + .ok_or(TimestampError::InvalidDate)?; Self::from_utc(t.assume_utc()) } } @@ -269,16 +268,16 @@ impl scale_info::TypeInfo for Timestamp { #[derive(Debug, Display, derive_more::From)] pub enum TimestampError { - /// parsing u64 integer from string error: `{0}` + /// parse int error: {0} ParseInt(ParseIntError), - /// error converting integer to `Timestamp`: `{0}` + /// try from int error: {0} TryFromInt(TryFromIntError), - /// date out of range - DateOutOfRange, - /// Timestamp overflow when modifying with duration - TimestampOverflow, - /// Timestamp is not set - Conversion(ComponentRange), + /// failed to convert timestamp: {0} + Conversion(time::error::ComponentRange), + /// invalid date: out of range + InvalidDate, + /// overflowed timestamp + OverflowedTimestamp, } #[cfg(feature = "std")] diff --git a/ibc-query/src/core/channel/query.rs b/ibc-query/src/core/channel/query.rs index 475aaefac..2b853330b 100644 --- a/ibc-query/src/core/channel/query.rs +++ b/ibc-query/src/core/channel/query.rs @@ -47,7 +47,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::ChannelEnd(channel_end_path.clone())) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for channel end path {channel_end_path:?}" )) })?; @@ -119,7 +119,7 @@ where .first() .map(|connection_id| ibc_ctx.connection_end(connection_id)) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Channel {} does not have a connection", request.channel_id )) @@ -140,7 +140,7 @@ where &Path::ClientState(ClientStatePath::new(connection_end.client_id().clone())), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for client state path: {:?}", connection_end.client_id() )) @@ -172,7 +172,7 @@ where .first() .map(|connection_id| ibc_ctx.connection_end(connection_id)) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Channel {} does not have a connection", request.channel_id )) @@ -198,7 +198,7 @@ where &Path::ClientConsensusState(consensus_path.clone()), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for client consensus state path: {consensus_path:?}" )) })?; @@ -233,7 +233,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::Commitment(commitment_path.clone())) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for packet commitment path: {commitment_path:?}" )) })?; @@ -290,7 +290,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::Receipt(receipt_path.clone())) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for packet receipt path: {receipt_path:?}" )) })?; @@ -324,7 +324,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::Ack(acknowledgement_path.clone())) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for packet acknowledgement path: {acknowledgement_path:?}" )) })?; @@ -430,7 +430,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::SeqSend(next_seq_send_path)) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Next sequence send proof not found for channel {}", request.channel_id )) @@ -463,7 +463,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::SeqRecv(next_seq_recv_path)) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Next sequence receive proof not found for channel {}", request.channel_id )) diff --git a/ibc-query/src/core/client/query.rs b/ibc-query/src/core/client/query.rs index 27e64a33f..728e2ee26 100644 --- a/ibc-query/src/core/client/query.rs +++ b/ibc-query/src/core/client/query.rs @@ -50,7 +50,7 @@ where &Path::ClientState(ClientStatePath::new(client_id.clone())), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for client state path: {client_id:?}" )) })?; @@ -109,7 +109,7 @@ where .into_iter() .max_by_key(|&(h, _)| h) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "No consensus state found for client: {client_id:?}" )) })? @@ -130,7 +130,7 @@ where )), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for consensus state path: {client_id:?}" )) })?; @@ -242,7 +242,7 @@ where &Path::UpgradeClientState(upgraded_client_state_path), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for upgraded client state at: {proof_height:?}" )) })?; @@ -301,7 +301,7 @@ where &Path::UpgradeConsensusState(upgraded_consensus_state_path), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for upgraded consensus state at: {proof_height:?}" )) })?; diff --git a/ibc-query/src/core/client/types/request.rs b/ibc-query/src/core/client/types/request.rs index 8648c69bc..5c3a537e2 100644 --- a/ibc-query/src/core/client/types/request.rs +++ b/ibc-query/src/core/client/types/request.rs @@ -2,6 +2,7 @@ //! and from the corresponding gRPC proto types for the client module. use ibc::core::client::types::Height; +use ibc::core::host::types::error::DecodingError; use ibc::core::host::types::identifiers::ClientId; use ibc::primitives::prelude::*; use ibc_proto::ibc::core::client::v1::{ @@ -82,7 +83,12 @@ impl TryFrom for QueryConsensusStateRequest { client_id: request.client_id.parse()?, consensus_height: (!request.latest_height) .then(|| Height::new(request.revision_number, request.revision_height)) - .transpose()?, + .transpose() + .map_err(|e| { + DecodingError::invalid_raw_data(format!( + "consensus state request consensus height: {e}" + )) + })?, query_height: None, }) } diff --git a/ibc-query/src/core/connection/query.rs b/ibc-query/src/core/connection/query.rs index d156c4c1c..ed1657869 100644 --- a/ibc-query/src/core/connection/query.rs +++ b/ibc-query/src/core/connection/query.rs @@ -41,7 +41,7 @@ where &Path::Connection(ConnectionPath::new(&request.connection_id)), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for connection path: {:?}", request.connection_id )) @@ -92,7 +92,7 @@ where &Path::ClientConnection(ClientConnectionPath::new(request.client_id.clone())), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for client connection path: {:?}", request.client_id )) @@ -130,7 +130,7 @@ where &Path::ClientState(ClientStatePath::new(connection_end.client_id().clone())), ) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for client state path: {:?}", connection_end.client_id() )) @@ -172,7 +172,7 @@ where let proof = ibc_ctx .get_proof(proof_height, &Path::ClientConsensusState(consensus_path)) .ok_or_else(|| { - QueryError::proof_not_found(format!( + QueryError::missing_proof(format!( "Proof not found for consensus state path: {:?}", connection_end.client_id() )) diff --git a/ibc-query/src/core/context.rs b/ibc-query/src/core/context.rs index 693dafec9..7f29306c6 100644 --- a/ibc-query/src/core/context.rs +++ b/ibc-query/src/core/context.rs @@ -4,7 +4,7 @@ use ibc::core::channel::types::channel::IdentifiedChannelEnd; use ibc::core::channel::types::packet::PacketState; use ibc::core::client::types::Height; use ibc::core::connection::types::IdentifiedConnectionEnd; -use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::error::HostError; use ibc::core::host::types::identifiers::{ClientId, ConnectionId, Sequence}; use ibc::core::host::types::path::{ChannelEndPath, Path}; use ibc::core::host::{ClientStateRef, ConsensusStateRef, ValidationContext}; @@ -22,32 +22,29 @@ pub trait QueryContext: ProvableContext + ValidationContext { // Client queries /// Returns the list of all clients. - fn client_states(&self) -> Result)>, ContextError>; + fn client_states(&self) -> Result)>, HostError>; /// Returns the list of all consensus states for the given client. fn consensus_states( &self, client_id: &ClientId, - ) -> Result)>, ContextError>; + ) -> Result)>, HostError>; /// Returns the list of all heights at which consensus states for the given client are. - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError>; + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, HostError>; // Connection queries /// Returns the list of all connection ends. - fn connection_ends(&self) -> Result, ContextError>; + fn connection_ends(&self) -> Result, HostError>; /// Returns the list of all connection ids of the given client. - fn client_connection_ends( - &self, - client_id: &ClientId, - ) -> Result, ContextError>; + fn client_connection_ends(&self, client_id: &ClientId) -> Result, HostError>; // Channel queries /// Returns the list of all channel ends. - fn channel_ends(&self) -> Result, ContextError>; + fn channel_ends(&self) -> Result, HostError>; // Packet queries @@ -55,7 +52,7 @@ pub trait QueryContext: ProvableContext + ValidationContext { fn packet_commitments( &self, channel_end_path: &ChannelEndPath, - ) -> Result, ContextError>; + ) -> Result, HostError>; /// Filters the list of packet sequences for the given channel end that are acknowledged. /// Returns all the packet acknowledgements if `sequences` is empty. @@ -63,14 +60,14 @@ pub trait QueryContext: ProvableContext + ValidationContext { &self, channel_end_path: &ChannelEndPath, sequences: impl ExactSizeIterator, - ) -> Result, ContextError>; + ) -> Result, HostError>; /// Filters the packet sequences for the given channel end that are not received. fn unreceived_packets( &self, channel_end_path: &ChannelEndPath, sequences: impl ExactSizeIterator, - ) -> Result, ContextError>; + ) -> Result, HostError>; /// Filters the list of packet sequences for the given channel end whose acknowledgement is not received. /// Returns all the unreceived acknowledgements if `sequences` is empty. @@ -78,5 +75,5 @@ pub trait QueryContext: ProvableContext + ValidationContext { &self, channel_end_path: &ChannelEndPath, sequences: impl ExactSizeIterator, - ) -> Result, ContextError>; + ) -> Result, HostError>; } diff --git a/ibc-query/src/error.rs b/ibc-query/src/error.rs index 6d11fd2d8..2db8bce20 100644 --- a/ibc-query/src/error.rs +++ b/ibc-query/src/error.rs @@ -1,28 +1,33 @@ use alloc::string::{String, ToString}; use displaydoc::Display; -use ibc::core::channel::types::error::{ChannelError, PacketError}; +use ibc::core::channel::types::error::ChannelError; use ibc::core::client::types::error::ClientError; use ibc::core::connection::types::error::ConnectionError; -use ibc::core::handler::types::error::ContextError; -use ibc::core::host::types::error::IdentifierError; +use ibc::core::handler::types::error::HandlerError; +use ibc::core::host::types::error::{DecodingError, HostError, IdentifierError}; use tonic::Status; +/// The main error type of the ibc-query crate. This type mainly +/// serves to surface lower-level errors that occur when executing +/// ibc-query's codepaths. #[derive(Debug, Display)] pub enum QueryError { - /// Context error: {0} - ContextError(ContextError), - /// Identifier error: {0} - IdentifierError(IdentifierError), - /// Proof not found: {0} - ProofNotFound(String), - /// Missing field: {0} + /// handler error: `{0}` + Handler(HandlerError), + /// host error: `{0}` + Host(HostError), + /// decoding error: `{0}` + Decoding(DecodingError), + /// missing proof: `{0}` + MissingProof(String), + /// missing field: `{0}` MissingField(String), } impl QueryError { - pub fn proof_not_found(description: T) -> Self { - Self::ProofNotFound(description.to_string()) + pub fn missing_proof(description: T) -> Self { + Self::MissingProof(description.to_string()) } pub fn missing_field(description: T) -> Self { @@ -33,46 +38,47 @@ impl QueryError { impl From for Status { fn from(e: QueryError) -> Self { match e { - QueryError::ContextError(ctx_err) => Self::internal(ctx_err.to_string()), - QueryError::IdentifierError(id_err) => Self::internal(id_err.to_string()), - QueryError::ProofNotFound(description) => Self::not_found(description), + QueryError::Handler(ctx_err) => Self::internal(ctx_err.to_string()), + QueryError::Host(host_err) => Self::internal(host_err.to_string()), + QueryError::Decoding(de) => Self::internal(de.to_string()), + QueryError::MissingProof(description) => Self::not_found(description), QueryError::MissingField(description) => Self::invalid_argument(description), } } } -impl From for QueryError { - fn from(e: ContextError) -> Self { - Self::ContextError(e) - } -} - impl From for QueryError { fn from(e: ClientError) -> Self { - Self::ContextError(ContextError::ClientError(e)) + Self::Handler(HandlerError::Client(e)) } } impl From for QueryError { fn from(e: ConnectionError) -> Self { - Self::ContextError(ContextError::ConnectionError(e)) + Self::Handler(HandlerError::Connection(e)) } } impl From for QueryError { fn from(e: ChannelError) -> Self { - Self::ContextError(ContextError::ChannelError(e)) + Self::Handler(HandlerError::Channel(e)) } } -impl From for QueryError { - fn from(e: PacketError) -> Self { - Self::ContextError(ContextError::PacketError(e)) +impl From for QueryError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) } } impl From for QueryError { fn from(e: IdentifierError) -> Self { - Self::IdentifierError(e) + Self::Decoding(DecodingError::Identifier(e)) + } +} + +impl From for QueryError { + fn from(e: HostError) -> Self { + Self::Host(e) } } diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 4380de474..92986d14f 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -10,7 +10,7 @@ use ibc::core::client::context::{ClientExecutionContext, ClientValidationContext use ibc::core::client::types::Height; use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::{dispatch, execute, validate}; -use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::error::HandlerError; use ibc::core::handler::types::events::IbcEvent; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; @@ -25,7 +25,6 @@ use ibc::primitives::Timestamp; use super::testapp::ibc::core::types::{LightClientState, MockIbcStore}; use crate::fixtures::core::context::TestContextConfig; use crate::hosts::{HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; -use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::DEFAULT_BLOCK_TIME_SECS; @@ -463,26 +462,26 @@ where } /// Calls [`validate`] function on [`MsgEnvelope`] using the context's IBC store and router. - pub fn validate(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { + pub fn validate(&mut self, msg: MsgEnvelope) -> Result<(), HandlerError> { validate(&self.ibc_store, &self.ibc_router, msg) } /// Calls [`execute`] function on [`MsgEnvelope`] using the context's IBC store and router. - pub fn execute(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { + pub fn execute(&mut self, msg: MsgEnvelope) -> Result<(), HandlerError> { execute(&mut self.ibc_store, &mut self.ibc_router, msg) } /// Calls [`dispatch`] function on [`MsgEnvelope`] using the context's IBC store and router. - pub fn dispatch(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { + pub fn dispatch(&mut self, msg: MsgEnvelope) -> Result<(), HandlerError> { dispatch(&mut self.ibc_store, &mut self.ibc_router, msg) } /// 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 an Ics18Error. - pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), RelayerError> { - self.dispatch(msg) - .map_err(RelayerError::TransactionFailed)?; + pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), HandlerError> { + self.dispatch(msg)?; + // Create a new block. self.advance_block_height(); Ok(()) diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index 89d870f72..3653c39ef 100644 --- a/ibc-testkit/src/fixtures/clients/tendermint.rs +++ b/ibc-testkit/src/fixtures/clients/tendermint.rs @@ -3,7 +3,7 @@ use core::time::Duration; use basecoin_store::avl::get_proof_spec as basecoin_proof_spec; use ibc::clients::tendermint::client_state::ClientState as TmClientState; -use ibc::clients::tendermint::types::error::{Error as ClientError, Error}; +use ibc::clients::tendermint::types::error::TendermintClientError; use ibc::clients::tendermint::types::proto::v1::{ClientState as RawTmClientState, Fraction}; #[cfg(feature = "serde")] use ibc::clients::tendermint::types::Header; @@ -13,13 +13,16 @@ use ibc::clients::tendermint::types::{ use ibc::core::client::types::proto::v1::Height as RawHeight; use ibc::core::client::types::Height; use ibc::core::commitment_types::specs::ProofSpecs; +use ibc::core::host::types::error::DecodingError; use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use tendermint::block::Header as TmHeader; use typed_builder::TypedBuilder; /// Returns a dummy tendermint `ClientState` by given `frozen_height`, for testing purposes only! -pub fn dummy_tm_client_state_from_raw(frozen_height: RawHeight) -> Result { +pub fn dummy_tm_client_state_from_raw( + frozen_height: RawHeight, +) -> Result { ClientStateType::try_from(dummy_raw_tm_client_state(frozen_height)).map(TmClientState::from) } @@ -95,7 +98,7 @@ impl ClientStateConfig { self, chain_id: ChainId, latest_height: Height, - ) -> Result { + ) -> Result { Ok(ClientStateType::new( chain_id, self.trust_level, diff --git a/ibc-testkit/src/fixtures/core/channel/acknowledgement.rs b/ibc-testkit/src/fixtures/core/channel/acknowledgement.rs index ac09a89e0..8d22324c2 100644 --- a/ibc-testkit/src/fixtures/core/channel/acknowledgement.rs +++ b/ibc-testkit/src/fixtures/core/channel/acknowledgement.rs @@ -27,8 +27,8 @@ pub fn dummy_raw_msg_ack_with_packet(packet: RawPacket, height: u64) -> RawMsgAc #[cfg(test)] mod test { - use ibc::core::channel::types::error::PacketError; use ibc::core::channel::types::msgs::MsgAcknowledgement; + use ibc::core::host::types::error::DecodingError; use ibc::primitives::prelude::*; use super::*; @@ -85,7 +85,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/ibc-testkit/src/fixtures/core/channel/recv_packet.rs b/ibc-testkit/src/fixtures/core/channel/recv_packet.rs index 57403a69e..2a34925b5 100644 --- a/ibc-testkit/src/fixtures/core/channel/recv_packet.rs +++ b/ibc-testkit/src/fixtures/core/channel/recv_packet.rs @@ -46,7 +46,7 @@ pub fn dummy_raw_msg_recv_packet(height: u64) -> RawMsgRecvPacket { #[cfg(test)] mod test { - use ibc::core::channel::types::error::PacketError; + use ibc::core::host::types::error::DecodingError; use ibc::primitives::prelude::*; use super::*; @@ -94,7 +94,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/ibc-testkit/src/fixtures/core/channel/timeout.rs b/ibc-testkit/src/fixtures/core/channel/timeout.rs index 97719aafd..175c6605e 100644 --- a/ibc-testkit/src/fixtures/core/channel/timeout.rs +++ b/ibc-testkit/src/fixtures/core/channel/timeout.rs @@ -25,8 +25,8 @@ pub fn dummy_raw_msg_timeout( #[cfg(test)] mod test { - use ibc::core::channel::types::error::PacketError; use ibc::core::channel::types::msgs::MsgTimeout; + use ibc::core::host::types::error::DecodingError; use ibc::primitives::prelude::*; use super::*; @@ -86,7 +86,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/ibc-testkit/src/fixtures/mod.rs b/ibc-testkit/src/fixtures/mod.rs index f6b51ef46..c5e14f29a 100644 --- a/ibc-testkit/src/fixtures/mod.rs +++ b/ibc-testkit/src/fixtures/mod.rs @@ -3,13 +3,13 @@ pub mod clients; pub mod core; use alloc::fmt::Debug; -use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::error::HandlerError; use ibc::core::primitives::prelude::*; use crate::testapp::ibc::core::types::DefaultIbcStore; pub enum Expect { Success, - Failure(Option), + Failure(Option), } #[derive(Debug)] @@ -23,7 +23,7 @@ impl Fixture { &self, expect: &Expect, process: &str, - res: &Result<(), ContextError>, + res: &Result<(), HandlerError>, ) -> String { let base_error = match expect { Expect::Success => "step failed!", diff --git a/ibc-testkit/src/relayer/error.rs b/ibc-testkit/src/relayer/error.rs deleted file mode 100644 index 20a995461..000000000 --- a/ibc-testkit/src/relayer/error.rs +++ /dev/null @@ -1,38 +0,0 @@ -use displaydoc::Display; -use ibc::core::client::types::Height; -use ibc::core::connection::types::error::ConnectionError; -use ibc::core::handler::types::error::ContextError; -use ibc::core::host::types::identifiers::ClientId; - -#[derive(Debug, Display)] -pub enum RelayerError { - /// client state on destination chain not found, (client id: `{client_id}`) - ClientStateNotFound { client_id: ClientId }, - /// the client on the 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 the 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(ContextError), - /// connection error: `{0}` - Connection(ConnectionError), -} - -#[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/ibc-testkit/src/relayer/mod.rs b/ibc-testkit/src/relayer/mod.rs index 961de7885..309d094da 100644 --- a/ibc-testkit/src/relayer/mod.rs +++ b/ibc-testkit/src/relayer/mod.rs @@ -1,4 +1,3 @@ pub mod context; -pub mod error; pub mod integration; pub mod utils; diff --git a/ibc-testkit/src/testapp/ibc/applications/nft_transfer/context.rs b/ibc-testkit/src/testapp/ibc/applications/nft_transfer/context.rs index a526dcf45..84838d846 100644 --- a/ibc-testkit/src/testapp/ibc/applications/nft_transfer/context.rs +++ b/ibc-testkit/src/testapp/ibc/applications/nft_transfer/context.rs @@ -1,10 +1,10 @@ use ibc::apps::nft_transfer::context::{ NftClassContext, NftContext, NftTransferExecutionContext, NftTransferValidationContext, }; -use ibc::apps::nft_transfer::types::error::NftTransferError; use ibc::apps::nft_transfer::types::{ ClassData, ClassId, ClassUri, Memo, PrefixedClassId, TokenData, TokenId, TokenUri, }; +use ibc::core::host::types::error::HostError; use ibc::core::host::types::identifiers::{ChannelId, PortId}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Signer; @@ -48,15 +48,15 @@ impl NftTransferValidationContext for DummyNftTransferModule { type Nft = DummyNft; type NftClass = DummyNftClass; - fn get_port(&self) -> Result { + fn get_port(&self) -> Result { Ok(PortId::transfer()) } - fn can_send_nft(&self) -> Result<(), NftTransferError> { + fn can_send_nft(&self) -> Result<(), HostError> { Ok(()) } - fn can_receive_nft(&self) -> Result<(), NftTransferError> { + fn can_receive_nft(&self) -> Result<(), HostError> { Ok(()) } @@ -65,7 +65,7 @@ impl NftTransferValidationContext for DummyNftTransferModule { _class_id: &PrefixedClassId, _class_uri: Option<&ClassUri>, _class_data: Option<&ClassData>, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -77,7 +77,7 @@ impl NftTransferValidationContext for DummyNftTransferModule { _class_id: &PrefixedClassId, _token_id: &TokenId, _memo: &Memo, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -88,7 +88,7 @@ impl NftTransferValidationContext for DummyNftTransferModule { _channel_id: &ChannelId, _class_id: &PrefixedClassId, _token_id: &TokenId, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -99,7 +99,7 @@ impl NftTransferValidationContext for DummyNftTransferModule { _token_id: &TokenId, _token_uri: Option<&TokenUri>, _token_data: Option<&TokenData>, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -109,7 +109,7 @@ impl NftTransferValidationContext for DummyNftTransferModule { _class_id: &PrefixedClassId, _token_id: &TokenId, _memo: &Memo, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -117,14 +117,11 @@ impl NftTransferValidationContext for DummyNftTransferModule { &self, _class_id: &PrefixedClassId, _token_id: &TokenId, - ) -> Result { + ) -> Result { Ok(DummyNft::default()) } - fn get_nft_class( - &self, - _class_id: &PrefixedClassId, - ) -> Result { + fn get_nft_class(&self, _class_id: &PrefixedClassId) -> Result { Ok(DummyNftClass::default()) } } @@ -135,7 +132,7 @@ impl NftTransferExecutionContext for DummyNftTransferModule { _class_id: &PrefixedClassId, _class_uri: Option<&ClassUri>, _class_data: Option<&ClassData>, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -147,7 +144,7 @@ impl NftTransferExecutionContext for DummyNftTransferModule { _class_id: &PrefixedClassId, _token_id: &TokenId, _memo: &Memo, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -158,7 +155,7 @@ impl NftTransferExecutionContext for DummyNftTransferModule { _channel_id: &ChannelId, _class_id: &PrefixedClassId, _token_id: &TokenId, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -169,7 +166,7 @@ impl NftTransferExecutionContext for DummyNftTransferModule { _token_id: &TokenId, _token_uri: Option<&TokenUri>, _token_data: Option<&TokenData>, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -179,7 +176,7 @@ impl NftTransferExecutionContext for DummyNftTransferModule { _class_id: &PrefixedClassId, _token_id: &TokenId, _memo: &Memo, - ) -> Result<(), NftTransferError> { + ) -> Result<(), HostError> { Ok(()) } } diff --git a/ibc-testkit/src/testapp/ibc/applications/nft_transfer/module.rs b/ibc-testkit/src/testapp/ibc/applications/nft_transfer/module.rs index 06652a1dd..df6a158a6 100644 --- a/ibc-testkit/src/testapp/ibc/applications/nft_transfer/module.rs +++ b/ibc-testkit/src/testapp/ibc/applications/nft_transfer/module.rs @@ -1,6 +1,6 @@ use ibc::core::channel::types::acknowledgement::Acknowledgement; use ibc::core::channel::types::channel::{Counterparty, Order}; -use ibc::core::channel::types::error::{ChannelError, PacketError}; +use ibc::core::channel::types::error::ChannelError; use ibc::core::channel::types::packet::Packet; use ibc::core::channel::types::Version; use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; @@ -75,7 +75,7 @@ impl Module for DummyNftTransferModule { &self, _packet: &Packet, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -83,7 +83,7 @@ impl Module for DummyNftTransferModule { &mut self, _packet: &Packet, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } @@ -92,7 +92,7 @@ impl Module for DummyNftTransferModule { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -101,7 +101,7 @@ impl Module for DummyNftTransferModule { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } } diff --git a/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs b/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs index d8876b315..d4a9677ef 100644 --- a/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs +++ b/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs @@ -1,6 +1,6 @@ use ibc::apps::transfer::context::{TokenTransferExecutionContext, TokenTransferValidationContext}; -use ibc::apps::transfer::types::error::TokenTransferError; use ibc::apps::transfer::types::{Memo, PrefixedCoin}; +use ibc::core::host::types::error::HostError; use ibc::core::host::types::identifiers::{ChannelId, PortId}; use ibc::core::primitives::Signer; @@ -9,15 +9,15 @@ use super::types::DummyTransferModule; impl TokenTransferValidationContext for DummyTransferModule { type AccountId = Signer; - fn get_port(&self) -> Result { + fn get_port(&self) -> Result { Ok(PortId::transfer()) } - fn can_send_coins(&self) -> Result<(), TokenTransferError> { + fn can_send_coins(&self) -> Result<(), HostError> { Ok(()) } - fn can_receive_coins(&self) -> Result<(), TokenTransferError> { + fn can_receive_coins(&self) -> Result<(), HostError> { Ok(()) } fn escrow_coins_validate( @@ -27,7 +27,7 @@ impl TokenTransferValidationContext for DummyTransferModule { _channel_id: &ChannelId, _coin: &PrefixedCoin, _memo: &Memo, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -37,7 +37,7 @@ impl TokenTransferValidationContext for DummyTransferModule { _port_id: &PortId, _channel_id: &ChannelId, _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -45,7 +45,7 @@ impl TokenTransferValidationContext for DummyTransferModule { &self, _account: &Self::AccountId, _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -54,7 +54,7 @@ impl TokenTransferValidationContext for DummyTransferModule { _account: &Self::AccountId, _coin: &PrefixedCoin, _memo: &Memo, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } } @@ -67,7 +67,7 @@ impl TokenTransferExecutionContext for DummyTransferModule { _channel_id: &ChannelId, _coin: &PrefixedCoin, _memo: &Memo, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -77,7 +77,7 @@ impl TokenTransferExecutionContext for DummyTransferModule { _port_id: &PortId, _channel_id: &ChannelId, _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -85,7 +85,7 @@ impl TokenTransferExecutionContext for DummyTransferModule { &mut self, _account: &Self::AccountId, _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } @@ -94,7 +94,7 @@ impl TokenTransferExecutionContext for DummyTransferModule { _account: &Self::AccountId, _coin: &PrefixedCoin, _memo: &Memo, - ) -> Result<(), TokenTransferError> { + ) -> Result<(), HostError> { Ok(()) } } diff --git a/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs b/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs index 2246c893b..289feb654 100644 --- a/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs +++ b/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs @@ -1,6 +1,6 @@ use ibc::core::channel::types::acknowledgement::Acknowledgement; use ibc::core::channel::types::channel::{Counterparty, Order}; -use ibc::core::channel::types::error::{ChannelError, PacketError}; +use ibc::core::channel::types::error::ChannelError; use ibc::core::channel::types::packet::Packet; use ibc::core::channel::types::Version; use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; @@ -75,7 +75,7 @@ impl Module for DummyTransferModule { &self, _packet: &Packet, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -83,7 +83,7 @@ impl Module for DummyTransferModule { &mut self, _packet: &Packet, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } @@ -92,7 +92,7 @@ impl Module for DummyTransferModule { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -101,7 +101,7 @@ impl Module for DummyTransferModule { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index c10a14210..a789743e1 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -8,7 +8,7 @@ use ibc::core::client::types::{Height, Status}; use ibc::core::commitment_types::commitment::{ CommitmentPrefix, CommitmentProofBytes, CommitmentRoot, }; -use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::error::{DecodingError, HostError}; use ibc::core::host::types::identifiers::{ClientId, ClientType}; use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, Path, PathBytes}; use ibc::core::primitives::prelude::*; @@ -91,15 +91,13 @@ impl MockClientState { impl Protobuf for MockClientState {} impl TryFrom for MockClientState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMockClientState) -> Result { Ok(Self { header: raw .header - .ok_or(ClientError::Other { - description: "header is not present".into(), - })? + .ok_or(DecodingError::missing_raw_data("mock client state header"))? .try_into()?, trusting_period: Duration::from_nanos(raw.trusting_period), frozen: raw.frozen, @@ -124,21 +122,16 @@ impl From for RawMockClientState { impl Protobuf for MockClientState {} impl TryFrom for MockClientState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - fn decode_client_state(value: &[u8]) -> Result { - let client_state = - Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(client_state) - } - match raw.type_url.as_str() { - MOCK_CLIENT_STATE_TYPE_URL => decode_client_state(&raw.value), - _ => Err(ClientError::UnknownClientStateType { - client_state_type: raw.type_url, - }), + if let MOCK_CLIENT_STATE_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: MOCK_CLIENT_STATE_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } @@ -154,10 +147,10 @@ impl From for Any { pub trait MockClientContext { /// Returns the current timestamp of the local chain. - fn host_timestamp(&self) -> Result; + fn host_timestamp(&self) -> Result; /// Returns the current height of the local chain. - fn host_height(&self) -> Result; + fn host_height(&self) -> Result; } impl ClientStateCommon for MockClientState { @@ -171,9 +164,7 @@ impl ClientStateCommon for MockClientState { if consensus_state_status(&mock_consensus_state, host_timestamp, self.trusting_period)? .is_expired() { - return Err(ClientError::ClientNotActive { - status: Status::Expired, - }); + return Err(ClientError::InvalidStatus(Status::Expired)); } Ok(()) @@ -189,9 +180,9 @@ impl ClientStateCommon for MockClientState { fn validate_proof_height(&self, proof_height: Height) -> Result<(), ClientError> { if self.latest_height() < proof_height { - return Err(ClientError::InvalidProofHeight { - latest_height: self.latest_height(), - proof_height, + return Err(ClientError::InsufficientProofHeight { + actual: self.latest_height(), + expected: proof_height, }); } Ok(()) @@ -212,7 +203,7 @@ impl ClientStateCommon for MockClientState { let upgraded_mock_client_state = Self::try_from(upgraded_client_state)?; MockConsensusState::try_from(upgraded_consensus_state)?; if self.latest_height() >= upgraded_mock_client_state.latest_height() { - return Err(UpgradeClientError::LowUpgradeHeight { + return Err(UpgradeClientError::InsufficientUpgradeHeight { upgraded_height: self.latest_height(), client_height: upgraded_mock_client_state.latest_height(), })?; @@ -285,9 +276,7 @@ where Ok(header_heights_equal && headers_are_in_future) } - header_type => Err(ClientError::UnknownHeaderType { - header_type: header_type.to_owned(), - }), + header_type => Err(ClientError::InvalidHeaderType(header_type.to_owned())), } } @@ -312,9 +301,9 @@ where let now = ctx.host_timestamp()?; let elapsed_since_latest_consensus_state = now .duration_since(&latest_consensus_state.timestamp()) - .ok_or(ClientError::Other { - description: format!("latest consensus state is in the future. now: {now}, latest consensus state: {}", latest_consensus_state.timestamp()), - })?; + .ok_or(ClientError::InvalidConsensusStateTimestamp( + latest_consensus_state.timestamp(), + ))?; if self.expired(elapsed_since_latest_consensus_state) { return Ok(Status::Expired); diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index 82536d2d6..8149a5d62 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -1,6 +1,6 @@ use ibc::core::client::context::consensus_state::ConsensusState; -use ibc::core::client::types::error::ClientError; use ibc::core::commitment_types::commitment::CommitmentRoot; +use ibc::core::host::types::error::DecodingError; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::{Any, Protobuf}; @@ -38,10 +38,12 @@ impl MockConsensusState { impl Protobuf for MockConsensusState {} impl TryFrom for MockConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMockConsensusState) -> Result { - let raw_header = raw.header.ok_or(ClientError::MissingRawConsensusState)?; + let raw_header = raw.header.ok_or(DecodingError::missing_raw_data( + "mock consensus state header", + ))?; Ok(Self { header: raw_header.try_into()?, @@ -61,23 +63,16 @@ impl From for RawMockConsensusState { impl Protobuf for MockConsensusState {} impl TryFrom for MockConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - fn decode_consensus_state(value: &[u8]) -> Result { - let mock_consensus_state = - Protobuf::::decode(value).map_err(|e| { - ClientError::Other { - description: e.to_string(), - } - })?; - Ok(mock_consensus_state) - } - match raw.type_url.as_str() { - MOCK_CONSENSUS_STATE_TYPE_URL => decode_consensus_state(&raw.value), - _ => Err(ClientError::UnknownConsensusStateType { - consensus_state_type: raw.type_url, - }), + if let MOCK_CONSENSUS_STATE_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: MOCK_CONSENSUS_STATE_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index d45e11827..42830d58b 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -1,8 +1,8 @@ use alloc::string::ToString; use core::fmt::{Display, Error as FmtError, Formatter}; -use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; +use ibc::core::host::types::error::DecodingError; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::{Any, Protobuf}; @@ -40,15 +40,13 @@ impl Display for MockHeader { impl Protobuf for MockHeader {} impl TryFrom for MockHeader { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMockHeader) -> Result { Ok(Self { height: raw .height - .ok_or(ClientError::Other { - description: "missing height".into(), - })? + .ok_or(DecodingError::missing_raw_data("mock header height"))? .try_into()?, timestamp: Timestamp::from_nanoseconds(raw.timestamp), }) @@ -91,18 +89,16 @@ impl MockHeader { impl Protobuf for MockHeader {} impl TryFrom for MockHeader { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { - match raw.type_url.as_str() { - MOCK_HEADER_TYPE_URL => Ok(Protobuf::::decode_vec(&raw.value).map_err( - |e| ClientError::InvalidRawHeader { - reason: e.to_string(), - }, - )?), - _ => Err(ClientError::UnknownHeaderType { - header_type: raw.type_url, - }), + if let MOCK_HEADER_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode_vec(&raw.value).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: MOCK_HEADER_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs b/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs index ff4664da4..a3fde7a6d 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs @@ -1,4 +1,4 @@ -use ibc::core::client::types::error::ClientError; +use ibc::core::host::types::error::DecodingError; use ibc::core::host::types::identifiers::ClientId; use ibc::core::primitives::prelude::*; use ibc::primitives::proto::{Any, Protobuf}; @@ -19,18 +19,18 @@ pub struct Misbehaviour { impl Protobuf for Misbehaviour {} impl TryFrom for Misbehaviour { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: RawMisbehaviour) -> Result { Ok(Self { client_id: ClientId::new("07-tendermint", 0).expect("no error"), header1: raw .header1 - .ok_or(ClientError::MissingRawMisbehaviour)? + .ok_or(DecodingError::missing_raw_data("misbehaviour header1"))? .try_into()?, header2: raw .header2 - .ok_or(ClientError::MissingRawMisbehaviour)? + .ok_or(DecodingError::missing_raw_data("misbehaviour header2"))? .try_into()?, }) } @@ -49,21 +49,16 @@ impl From for RawMisbehaviour { impl Protobuf for Misbehaviour {} impl TryFrom for Misbehaviour { - type Error = ClientError; + type Error = DecodingError; - fn try_from(raw: Any) -> Result { - fn decode_misbehaviour(value: &[u8]) -> Result { - let raw_misbehaviour = - Protobuf::::decode(value).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - Ok(raw_misbehaviour) - } - match raw.type_url.as_str() { - MOCK_MISBEHAVIOUR_TYPE_URL => decode_misbehaviour(&raw.value), - _ => Err(ClientError::UnknownMisbehaviourType { - misbehaviour_type: raw.type_url, - }), + fn try_from(raw: Any) -> Result { + if let MOCK_MISBEHAVIOUR_TYPE_URL = raw.type_url.as_str() { + Protobuf::::decode(raw.value.as_ref()).map_err(Into::into) + } else { + Err(DecodingError::MismatchedResourceName { + expected: MOCK_MISBEHAVIOUR_TYPE_URL.to_string(), + actual: raw.type_url, + }) } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mod.rs index 05fe539da..3a0b8389d 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mod.rs @@ -10,8 +10,8 @@ use ibc::clients::tendermint::types::{ ClientState as ClientStateType, ConsensusState as ConsensusStateType, TENDERMINT_CLIENT_STATE_TYPE_URL, TENDERMINT_CONSENSUS_STATE_TYPE_URL, }; -use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; +use ibc::core::host::types::error::DecodingError; use ibc::core::primitives::prelude::*; use ibc::derive::{ClientState, ConsensusState}; use ibc::primitives::proto::{Any, Protobuf}; @@ -51,7 +51,7 @@ impl AnyClientState { impl Protobuf for AnyClientState {} impl TryFrom for AnyClientState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { if raw.type_url == TENDERMINT_CLIENT_STATE_TYPE_URL { @@ -59,9 +59,7 @@ impl TryFrom for AnyClientState { } else if raw.type_url == MOCK_CLIENT_STATE_TYPE_URL { MockClientState::try_from(raw).map(Into::into) } else { - Err(ClientError::Other { - description: "failed to deserialize message".to_string(), - }) + Err(DecodingError::UnknownTypeUrl(raw.type_url)) } } } @@ -94,7 +92,7 @@ pub enum AnyConsensusState { } impl TryFrom for AnyConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(raw: Any) -> Result { if raw.type_url == TENDERMINT_CONSENSUS_STATE_TYPE_URL { @@ -102,9 +100,7 @@ impl TryFrom for AnyConsensusState { } else if raw.type_url == MOCK_CONSENSUS_STATE_TYPE_URL { MockConsensusState::try_from(raw).map(Into::into) } else { - Err(ClientError::Other { - description: "failed to deserialize message".to_string(), - }) + Err(DecodingError::UnknownTypeUrl(raw.type_url)) } } } @@ -119,28 +115,27 @@ impl From for Any { } impl TryFrom for ConsensusStateType { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: AnyConsensusState) -> Result { match value { AnyConsensusState::Tendermint(cs) => Ok(cs.inner().clone()), - _ => Err(ClientError::Other { - description: "failed to convert AnyConsensusState to TmConsensusState".to_string(), - }), + _ => Err(DecodingError::invalid_raw_data( + "AnyConsensusState could not be converted to TmConsensusState", + )), } } } impl TryFrom for MockConsensusState { - type Error = ClientError; + type Error = DecodingError; fn try_from(value: AnyConsensusState) -> Result { match value { AnyConsensusState::Mock(cs) => Ok(cs), - _ => Err(ClientError::Other { - description: "failed to convert AnyConsensusState to MockConsensusState" - .to_string(), - }), + _ => Err(DecodingError::invalid_raw_data( + "AnyConsensusState could not be converted to MockConsensusState", + )), } } } diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 8c7bd8dd2..4640a6c23 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -5,9 +5,8 @@ use basecoin_store::types::Height as StoreHeight; use ibc::core::client::context::{ ClientExecutionContext, ClientValidationContext, ExtClientValidationContext, }; -use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; -use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::error::HostError; use ibc::core::host::types::identifiers::{ChannelId, ClientId, PortId}; use ibc::core::host::types::path::{ ClientConsensusStatePath, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, Path, @@ -37,11 +36,11 @@ impl MockClientContext for MockIbcStore where S: ProvableStore + Debug, { - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { ValidationContext::host_timestamp(self) } - fn host_height(&self) -> Result { + fn host_height(&self) -> Result { ValidationContext::host_height(self) } } @@ -50,21 +49,17 @@ impl ExtClientValidationContext for MockIbcStore where S: ProvableStore + Debug, { - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { ValidationContext::host_timestamp(self) } - fn host_height(&self) -> Result { + fn host_height(&self) -> Result { ValidationContext::host_height(self) } /// Returns the list of heights at which the consensus state of the given client was updated. - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { - let path = format!("clients/{}/consensusStates", client_id) - .try_into() - .map_err(|_| ClientError::Other { - description: "Invalid consensus state path".into(), - })?; + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, HostError> { + let path = format!("clients/{}/consensusStates", client_id).into(); self.consensus_state_store .get_keys(&path) @@ -77,10 +72,11 @@ where } }) .map(|consensus_path| { - Ok(Height::new( + Height::new( consensus_path.revision_number, consensus_path.revision_height, - )?) + ) + .map_err(HostError::invalid_state) }) .collect::, _>>() } @@ -89,7 +85,7 @@ where &self, client_id: &ClientId, height: &Height, - ) -> Result, ContextError> { + ) -> Result, HostError> { let path = format!("clients/{client_id}/consensusStates").into(); let keys = self.store.get_keys(&path); @@ -108,12 +104,16 @@ where .map(|path| { self.consensus_state_store .get(StoreHeight::Pending, &path) - .ok_or_else(|| ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height: *height, + .ok_or_else(|| { + HostError::failed_to_retrieve(format!( + "consensus state for client `{}` at height `{}`", + client_id.clone(), + *height + )) }) }) - .transpose()?; + .transpose() + .map_err(HostError::missing_state)?; Ok(consensus_state) } @@ -122,7 +122,7 @@ where &self, client_id: &ClientId, height: &Height, - ) -> Result, ContextError> { + ) -> Result, HostError> { let path = format!("clients/{client_id}/consensusStates").into(); let keys = self.store.get_keys(&path); @@ -141,12 +141,16 @@ where .map(|path| { self.consensus_state_store .get(StoreHeight::Pending, &path) - .ok_or_else(|| ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height: *height, + .ok_or_else(|| { + HostError::failed_to_retrieve(format!( + "consensus state for client `{}` at height `{}`", + client_id.clone(), + *height + )) }) }) - .transpose()?; + .transpose() + .map_err(HostError::missing_state)?; Ok(consensus_state) } @@ -159,31 +163,32 @@ where type ClientStateRef = AnyClientState; type ConsensusStateRef = AnyConsensusState; - fn client_state(&self, client_id: &ClientId) -> Result { - Ok(self - .client_state_store + fn client_state(&self, client_id: &ClientId) -> Result { + self.client_state_store .get(StoreHeight::Pending, &ClientStatePath(client_id.clone())) - .ok_or(ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?) + .ok_or(HostError::failed_to_retrieve(format!( + "missing client state for client {}", + client_id.clone() + ))) } fn consensus_state( &self, client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { + ) -> Result { let height = Height::new( client_cons_state_path.revision_number, client_cons_state_path.revision_height, ) - .map_err(|_| ClientError::InvalidHeight)?; + .map_err(HostError::invalid_state)?; let consensus_state = self .consensus_state_store .get(StoreHeight::Pending, client_cons_state_path) - .ok_or(ClientError::ConsensusStateNotFound { - client_id: client_cons_state_path.client_id.clone(), - height, - })?; + .ok_or(HostError::failed_to_retrieve(format!( + "consensus state for client `{}` at height `{}`", + client_cons_state_path.client_id.clone(), + height + )))?; Ok(consensus_state) } @@ -194,7 +199,7 @@ where &self, client_id: &ClientId, height: &Height, - ) -> Result<(Timestamp, Height), ContextError> { + ) -> Result<(Timestamp, Height), HostError> { let client_update_time_path = ClientUpdateTimePath::new( client_id.clone(), height.revision_number(), @@ -203,10 +208,11 @@ where let processed_timestamp = self .client_processed_times .get(StoreHeight::Pending, &client_update_time_path) - .ok_or(ClientError::UpdateMetaDataNotFound { - client_id: client_id.clone(), - height: *height, - })?; + .ok_or(HostError::failed_to_retrieve(format!( + "missing client update metadata for client {} at height {}", + client_id.clone(), + *height, + )))?; let client_update_height_path = ClientUpdateHeightPath::new( client_id.clone(), height.revision_number(), @@ -215,10 +221,11 @@ where let processed_height = self .client_processed_heights .get(StoreHeight::Pending, &client_update_height_path) - .ok_or(ClientError::UpdateMetaDataNotFound { - client_id: client_id.clone(), - height: *height, - })?; + .ok_or(HostError::failed_to_retrieve(format!( + "missing client update metadata for client {} at height {}", + client_id.clone(), + *height, + )))?; Ok((processed_timestamp, processed_height)) } @@ -235,12 +242,10 @@ where &mut self, client_state_path: ClientStatePath, client_state: Self::ClientStateRef, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.client_state_store .set(client_state_path, client_state) - .map_err(|_| ClientError::Other { - description: "Client state store error".to_string(), - })?; + .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?; Ok(()) } @@ -250,30 +255,24 @@ where &mut self, consensus_state_path: ClientConsensusStatePath, consensus_state: Self::ConsensusStateRef, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.consensus_state_store .set(consensus_state_path, consensus_state) - .map_err(|_| ClientError::Other { - description: "Consensus state store error".to_string(), - })?; + .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?; Ok(()) } fn delete_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.consensus_state_store.delete(consensus_state_path); Ok(()) } /// Delete the update metadata associated with the client at the specified /// height. - fn delete_update_meta( - &mut self, - client_id: ClientId, - height: Height, - ) -> Result<(), ContextError> { + fn delete_update_meta(&mut self, client_id: ClientId, height: Height) -> Result<(), HostError> { let client_update_time_path = ClientUpdateTimePath::new( client_id.clone(), height.revision_number(), @@ -299,7 +298,7 @@ where height: Height, host_timestamp: Timestamp, host_height: Height, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { let client_update_time_path = ClientUpdateTimePath::new( client_id.clone(), height.revision_number(), @@ -307,9 +306,7 @@ where ); self.client_processed_times .set(client_update_time_path, host_timestamp) - .map_err(|_| ClientError::Other { - description: "store update error".into(), - })?; + .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?; let client_update_height_path = ClientUpdateHeightPath::new( client_id, height.revision_number(), @@ -317,9 +314,7 @@ where ); self.client_processed_heights .set(client_update_height_path, host_height) - .map_err(|_| ClientError::Other { - description: "store update error".into(), - })?; + .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?; Ok(()) } } diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 9c5b2fb16..9e1837308 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -7,17 +7,15 @@ use basecoin_store::context::{ProvableStore, Store}; use basecoin_store::types::Height as StoreHeight; use ibc::core::channel::types::channel::{ChannelEnd, IdentifiedChannelEnd}; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; -use ibc::core::channel::types::error::{ChannelError, PacketError}; use ibc::core::channel::types::packet::{PacketState, Receipt}; use ibc::core::client::context::consensus_state::ConsensusState; use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::commitment_types::merkle::MerkleProof; -use ibc::core::connection::types::error::ConnectionError; use ibc::core::connection::types::{ConnectionEnd, IdentifiedConnectionEnd}; -use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::IbcEvent; +use ibc::core::host::types::error::HostError; use ibc::core::host::types::identifiers::{ClientId, ConnectionId, Sequence}; use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, CommitmentPath, ConnectionPath, @@ -42,48 +40,40 @@ where type HostClientState = AnyClientState; type HostConsensusState = AnyConsensusState; - fn host_height(&self) -> Result { - Ok(Height::new( - *self.revision_number.lock(), - self.store.current_height(), - )?) + fn host_height(&self) -> Result { + Height::new(*self.revision_number.lock(), self.store.current_height()) + .map_err(HostError::invalid_state) } - fn host_timestamp(&self) -> Result { + fn host_timestamp(&self) -> Result { let host_height = self.host_height()?; let host_cons_state = self.host_consensus_state(&host_height)?; Ok(host_cons_state.timestamp()) } - fn client_counter(&self) -> Result { - Ok(self - .client_counter + fn client_counter(&self) -> Result { + self.client_counter .get(StoreHeight::Pending, &NextClientSequencePath) - .ok_or(ClientError::Other { - description: "client counter not found".into(), - })?) + .ok_or(HostError::missing_state("client counter")) } - fn host_consensus_state( - &self, - height: &Height, - ) -> Result { + fn host_consensus_state(&self, height: &Height) -> Result { let consensus_states_binding = self.host_consensus_states.lock(); - Ok(consensus_states_binding + + consensus_states_binding .get(&height.revision_height()) .cloned() - .ok_or(ClientError::MissingLocalConsensusState { height: *height })?) + .ok_or(HostError::missing_state( + ClientError::MissingLocalConsensusState(*height), + )) } fn validate_self_client( &self, client_state_of_host_on_counterparty: Self::HostClientState, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { if client_state_of_host_on_counterparty.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); + return Err(HostError::invalid_state("client unexpectedly frozen")); } let latest_height = self.host_height()?; @@ -94,42 +84,36 @@ where .latest_height() .revision_number() { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client is not in the same revision as the chain. expected: {}, got: {}", - self_revision_number, - client_state_of_host_on_counterparty - .latest_height() - .revision_number() - ), - }, - )); + return Err(HostError::invalid_state(format!( + "client is not in the same revision as the chain; expected `{}`, actual `{}`", + self_revision_number, + client_state_of_host_on_counterparty + .latest_height() + .revision_number() + ))); } let host_current_height = latest_height.increment(); if client_state_of_host_on_counterparty.latest_height() >= host_current_height { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client has latest height {} greater than or equal to chain height {}", - client_state_of_host_on_counterparty.latest_height(), - host_current_height - ), - }, + return Err(HostError::invalid_state( + format!( + "counterparty client state: client latest height `{}` should be less than chain height `{}`", + client_state_of_host_on_counterparty.latest_height(), + host_current_height + ), )); } Ok(()) } - fn connection_end(&self, conn_id: &ConnectionId) -> Result { - Ok(self - .connection_end_store + fn connection_end(&self, conn_id: &ConnectionId) -> Result { + self.connection_end_store .get(StoreHeight::Pending, &ConnectionPath::new(conn_id)) - .ok_or(ConnectionError::ConnectionNotFound { - connection_id: conn_id.clone(), - })?) + .ok_or(HostError::missing_state(format!( + "connection end for connection `{}`", + conn_id.clone() + ))) } fn commitment_prefix(&self) -> CommitmentPrefix { @@ -138,60 +122,50 @@ where CommitmentPrefix::from(b"mock".to_vec()) } - fn connection_counter(&self) -> Result { - Ok(self - .conn_counter + fn connection_counter(&self) -> Result { + self.conn_counter .get(StoreHeight::Pending, &NextConnectionSequencePath) - .ok_or(ConnectionError::Other { - description: "connection counter not found".into(), - })?) + .ok_or(HostError::missing_state("connection counter")) } - fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { - Ok(self - .channel_end_store + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { + self.channel_end_store .get(StoreHeight::Pending, channel_end_path) - .ok_or(ChannelError::MissingChannel)?) + .ok_or(HostError::missing_state(format!( + "channel `{}` in port `{}`", + channel_end_path.1.clone(), + channel_end_path.0.clone() + ))) } - fn get_next_sequence_send( - &self, - seq_send_path: &SeqSendPath, - ) -> Result { - Ok(self - .send_sequence_store + fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) -> Result { + self.send_sequence_store .get(StoreHeight::Pending, seq_send_path) - .ok_or(PacketError::ImplementationSpecific)?) + .ok_or(HostError::failed_to_retrieve("send packet sequence")) } - fn get_next_sequence_recv( - &self, - seq_recv_path: &SeqRecvPath, - ) -> Result { - Ok(self - .recv_sequence_store + fn get_next_sequence_recv(&self, seq_recv_path: &SeqRecvPath) -> Result { + self.recv_sequence_store .get(StoreHeight::Pending, seq_recv_path) - .ok_or(PacketError::ImplementationSpecific)?) + .ok_or(HostError::failed_to_retrieve("recv packet sequence")) } - fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { - Ok(self - .ack_sequence_store + fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { + self.ack_sequence_store .get(StoreHeight::Pending, seq_ack_path) - .ok_or(PacketError::ImplementationSpecific)?) + .ok_or(HostError::failed_to_retrieve("ack packet sequence")) } fn get_packet_commitment( &self, commitment_path: &CommitmentPath, - ) -> Result { - Ok(self - .packet_commitment_store + ) -> Result { + self.packet_commitment_store .get(StoreHeight::Pending, commitment_path) - .ok_or(PacketError::ImplementationSpecific)?) + .ok_or(HostError::failed_to_retrieve("packet commitment")) } - fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { + fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { if self .packet_receipt_store .is_path_set(StoreHeight::Pending, receipt_path) @@ -205,25 +179,22 @@ where fn get_packet_acknowledgement( &self, ack_path: &AckPath, - ) -> Result { - Ok(self - .packet_ack_store + ) -> Result { + self.packet_ack_store .get(StoreHeight::Pending, ack_path) - .ok_or(PacketError::PacketAcknowledgementNotFound { - sequence: ack_path.sequence, - })?) + .ok_or(HostError::failed_to_retrieve(format!( + "packet acknowledgment `{}`", + ack_path.sequence + ))) } /// Returns a counter of the number of channel ids that have been created thus far. /// The value of this counter should increase only via the /// `ChannelKeeper::increase_channel_counter` method. - fn channel_counter(&self) -> Result { - Ok(self - .channel_counter + fn channel_counter(&self) -> Result { + self.channel_counter .get(StoreHeight::Pending, &NextChannelSequencePath) - .ok_or(ChannelError::Other { - description: "channel counter not found".into(), - })?) + .ok_or(HostError::failed_to_retrieve("channel counter")) } /// Returns the maximum expected time per block @@ -231,7 +202,7 @@ where Duration::from_secs(DEFAULT_BLOCK_TIME_SECS) } - fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { + fn validate_message_signer(&self, _signer: &Signer) -> Result<(), HostError> { Ok(()) } @@ -271,7 +242,7 @@ where S: ProvableStore + Debug, { /// Returns the list of all client states. - fn client_states(&self) -> Result)>, ContextError> { + fn client_states(&self) -> Result)>, HostError> { let path = "clients".to_owned().into(); self.client_state_store @@ -288,8 +259,11 @@ where let client_state = self .client_state_store .get(StoreHeight::Pending, &client_state_path) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_state_path.0.clone(), + .ok_or_else(|| { + HostError::failed_to_retrieve(format!( + "client state from path `{}`", + client_state_path.0.clone() + )) })?; Ok((client_state_path.0, client_state)) }) @@ -300,12 +274,8 @@ where fn consensus_states( &self, client_id: &ClientId, - ) -> Result)>, ContextError> { - let path = format!("clients/{}/consensusStates", client_id) - .try_into() - .map_err(|_| ClientError::Other { - description: "Invalid consensus state path".into(), - })?; + ) -> Result)>, HostError> { + let path = format!("clients/{}/consensusStates", client_id).into(); self.consensus_state_store .get_keys(&path) @@ -321,28 +291,23 @@ where let height = Height::new( consensus_path.revision_number, consensus_path.revision_height, - )?; + ) + .map_err(HostError::invalid_state)?; let client_state = self .consensus_state_store .get(StoreHeight::Pending, &consensus_path) - .ok_or({ - ClientError::ConsensusStateNotFound { - client_id: consensus_path.client_id, - height, - } - })?; + .ok_or(HostError::failed_to_retrieve(format!( + "consensus state for client `{}` at height `{}`", + consensus_path.client_id, height, + )))?; Ok((height, client_state)) }) .collect() } /// Returns the list of heights at which the consensus state of the given client was updated. - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { - let path = format!("clients/{}/consensusStates", client_id) - .try_into() - .map_err(|_| ClientError::Other { - description: "Invalid consensus state path".into(), - })?; + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, HostError> { + let path = format!("clients/{}/consensusStates", client_id).into(); self.consensus_state_store .get_keys(&path) @@ -355,16 +320,17 @@ where } }) .map(|consensus_path| { - Ok(Height::new( + Height::new( consensus_path.revision_number, consensus_path.revision_height, - )?) + ) + .map_err(HostError::invalid_state) }) .collect::, _>>() } /// Returns all the IBC connection ends of a chain. - fn connection_ends(&self) -> Result, ContextError> { + fn connection_ends(&self) -> Result, HostError> { let path = "connections".to_owned().into(); self.connection_end_store @@ -381,8 +347,11 @@ where let connection_end = self .connection_end_store .get(StoreHeight::Pending, &connection_path) - .ok_or_else(|| ConnectionError::ConnectionNotFound { - connection_id: connection_path.0.clone(), + .ok_or_else(|| { + HostError::failed_to_retrieve(format!( + "connection end `{}`", + connection_path.0.clone() + )) })?; Ok(IdentifiedConnectionEnd { connection_id: connection_path.0, @@ -393,10 +362,7 @@ where } /// Returns all the IBC connection ends associated with a client. - fn client_connection_ends( - &self, - client_id: &ClientId, - ) -> Result, ContextError> { + fn client_connection_ends(&self, client_id: &ClientId) -> Result, HostError> { let client_connection_path = ClientConnectionPath::new(client_id.clone()); Ok(self @@ -406,7 +372,7 @@ where } /// Returns all the IBC channel ends of a chain. - fn channel_ends(&self) -> Result, ContextError> { + fn channel_ends(&self) -> Result, HostError> { let path = "channelEnds".to_owned().into(); self.channel_end_store @@ -423,9 +389,12 @@ where let channel_end = self .channel_end_store .get(StoreHeight::Pending, &channel_path) - .ok_or_else(|| ChannelError::ChannelNotFound { - port_id: channel_path.0.clone(), - channel_id: channel_path.1.clone(), + .ok_or_else(|| { + HostError::failed_to_retrieve(format!( + "channel `{}` with port `{}`", + channel_path.1.clone(), + channel_path.0.clone() + )) })?; Ok(IdentifiedChannelEnd { port_id: channel_path.0, @@ -440,15 +409,12 @@ where fn packet_commitments( &self, channel_end_path: &ChannelEndPath, - ) -> Result, ContextError> { + ) -> Result, HostError> { let path = format!( "commitments/ports/{}/channels/{}/sequences", channel_end_path.0, channel_end_path.1 ) - .try_into() - .map_err(|_| PacketError::Other { - description: "Invalid commitment path".into(), - })?; + .into(); self.packet_commitment_store .get_keys(&path) @@ -485,17 +451,14 @@ where &self, channel_end_path: &ChannelEndPath, sequences: impl ExactSizeIterator, - ) -> Result, ContextError> { + ) -> Result, HostError> { let collected_paths: Vec<_> = if sequences.len() == 0 { // if sequences is empty, return all the acks let ack_path_prefix = format!( "acks/ports/{}/channels/{}/sequences", channel_end_path.0, channel_end_path.1 ) - .try_into() - .map_err(|_| PacketError::Other { - description: "Invalid ack path".into(), - })?; + .into(); self.packet_ack_store .get_keys(&ack_path_prefix) @@ -541,7 +504,7 @@ where &self, channel_end_path: &ChannelEndPath, sequences: impl ExactSizeIterator, - ) -> Result, ContextError> { + ) -> Result, HostError> { // QUESTION. Currently only works for unordered channels; ordered channels // don't use receipts. However, ibc-go does it this way. Investigate if // this query only ever makes sense on unordered channels. @@ -566,17 +529,14 @@ where &self, channel_end_path: &ChannelEndPath, sequences: impl ExactSizeIterator, - ) -> Result, ContextError> { + ) -> Result, HostError> { let collected_paths: Vec<_> = if sequences.len() == 0 { // if sequences is empty, return all the acks let commitment_path_prefix = format!( "commitments/ports/{}/channels/{}/sequences", channel_end_path.0, channel_end_path.1 ) - .try_into() - .map_err(|_| PacketError::Other { - description: "Invalid commitment path".into(), - })?; + .into(); self.packet_commitment_store .get_keys(&commitment_path_prefix) @@ -620,19 +580,15 @@ where /// Called upon client creation. /// Increases the counter, that keeps track of how many clients have been created. - fn increase_client_counter(&mut self) -> Result<(), ContextError> { + fn increase_client_counter(&mut self) -> Result<(), HostError> { let current_sequence = self .client_counter .get(StoreHeight::Pending, &NextClientSequencePath) - .ok_or(ClientError::Other { - description: "client counter not found".into(), - })?; + .ok_or(HostError::failed_to_retrieve("client counter"))?; self.client_counter .set(NextClientSequencePath, current_sequence + 1) - .map_err(|e| ClientError::Other { - description: format!("client counter update failed: {e:?}"), - })?; + .map_err(|e| HostError::failed_to_store(format!("client counter: {e:?}")))?; Ok(()) } @@ -642,12 +598,10 @@ where &mut self, connection_path: &ConnectionPath, connection_end: ConnectionEnd, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.connection_end_store .set(connection_path.clone(), connection_end) - .map_err(|_| ConnectionError::Other { - description: "Connection end store error".to_string(), - })?; + .map_err(|e| HostError::failed_to_store(format!("connection end: {e:?}")))?; Ok(()) } @@ -656,7 +610,7 @@ where &mut self, client_connection_path: &ClientConnectionPath, conn_id: ConnectionId, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { let mut conn_ids: Vec = self .connection_ids_store .get(StoreHeight::Pending, client_connection_path) @@ -664,27 +618,21 @@ where conn_ids.push(conn_id); self.connection_ids_store .set(client_connection_path.clone(), conn_ids) - .map_err(|_| ConnectionError::Other { - description: "Connection ids store error".to_string(), - })?; + .map_err(|e| HostError::failed_to_store(format!("connection IDs: {e:?}")))?; Ok(()) } /// Called upon connection identifier creation (Init or Try process). /// Increases the counter, that keeps track of how many connections have been created. - fn increase_connection_counter(&mut self) -> Result<(), ContextError> { + fn increase_connection_counter(&mut self) -> Result<(), HostError> { let current_sequence = self .conn_counter .get(StoreHeight::Pending, &NextConnectionSequencePath) - .ok_or(ConnectionError::Other { - description: "connection counter not found".into(), - })?; + .ok_or(HostError::failed_to_retrieve("connection counter"))?; self.conn_counter .set(NextConnectionSequencePath, current_sequence + 1) - .map_err(|e| ConnectionError::Other { - description: format!("connection counter update failed: {e:?}"), - })?; + .map_err(|e| HostError::failed_to_store(format!("connection counter: {e:?}")))?; Ok(()) } @@ -693,17 +641,17 @@ where &mut self, commitment_path: &CommitmentPath, commitment: PacketCommitment, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.packet_commitment_store .set(commitment_path.clone(), commitment) - .map_err(|_| PacketError::ImplementationSpecific)?; + .map_err(|e| HostError::failed_to_store(format!("packet commitment: {e:?}")))?; Ok(()) } fn delete_packet_commitment( &mut self, commitment_path: &CommitmentPath, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.packet_commitment_store.delete(commitment_path.clone()); Ok(()) } @@ -712,10 +660,10 @@ where &mut self, receipt_path: &ReceiptPath, _receipt: Receipt, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.packet_receipt_store .set_path(receipt_path.clone()) - .map_err(|_| PacketError::ImplementationSpecific)?; + .map_err(|e| HostError::failed_to_store(format!("packet receipt: {e:?}")))?; Ok(()) } @@ -723,14 +671,14 @@ where &mut self, ack_path: &AckPath, ack_commitment: AcknowledgementCommitment, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.packet_ack_store .set(ack_path.clone(), ack_commitment) - .map_err(|_| PacketError::ImplementationSpecific)?; + .map_err(|e| HostError::failed_to_store(format!("packet acknowledgment: {e:?}")))?; Ok(()) } - fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError> { + fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), HostError> { self.packet_ack_store.delete(ack_path.clone()); Ok(()) } @@ -739,12 +687,10 @@ where &mut self, channel_end_path: &ChannelEndPath, channel_end: ChannelEnd, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.channel_end_store .set(channel_end_path.clone(), channel_end) - .map_err(|_| ChannelError::Other { - description: "Channel end store error".to_string(), - })?; + .map_err(|e| HostError::failed_to_store(format!("channel: {e:?}")))?; Ok(()) } @@ -752,10 +698,10 @@ where &mut self, seq_send_path: &SeqSendPath, seq: Sequence, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.send_sequence_store .set(seq_send_path.clone(), seq) - .map_err(|_| PacketError::ImplementationSpecific)?; + .map_err(|e| HostError::failed_to_store(format!("next send sequence: {e:?}")))?; Ok(()) } @@ -763,10 +709,10 @@ where &mut self, seq_recv_path: &SeqRecvPath, seq: Sequence, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.recv_sequence_store .set(seq_recv_path.clone(), seq) - .map_err(|_| PacketError::ImplementationSpecific)?; + .map_err(|e| HostError::failed_to_store(format!("next recv sequence: {e:?}")))?; Ok(()) } @@ -774,36 +720,33 @@ where &mut self, seq_ack_path: &SeqAckPath, seq: Sequence, - ) -> Result<(), ContextError> { + ) -> Result<(), HostError> { self.ack_sequence_store .set(seq_ack_path.clone(), seq) - .map_err(|_| PacketError::ImplementationSpecific)?; + .map_err(|e| { + HostError::failed_to_store(format!("failed to store ack sequence: {e:?}")) + })?; Ok(()) } - fn increase_channel_counter(&mut self) -> Result<(), ContextError> { + fn increase_channel_counter(&mut self) -> Result<(), HostError> { let current_sequence = self .channel_counter .get(StoreHeight::Pending, &NextChannelSequencePath) - .ok_or(ChannelError::Other { - description: "channel counter not found".into(), - })?; + .ok_or(HostError::failed_to_retrieve("channel counter"))?; self.channel_counter .set(NextChannelSequencePath, current_sequence + 1) - .map_err(|e| ChannelError::Other { - description: format!("channel counter update failed: {e:?}"), - })?; - + .map_err(|e| HostError::failed_to_store(format!("channel counter: {e:?}")))?; Ok(()) } - fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError> { self.events.lock().push(event); Ok(()) } - fn log_message(&mut self, message: String) -> Result<(), ContextError> { + fn log_message(&mut self, message: String) -> Result<(), HostError> { self.logs.lock().push(message); Ok(()) } diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 4bd314c60..9389d642c 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -205,7 +205,7 @@ where mod tests { use ibc::core::channel::types::acknowledgement::Acknowledgement; use ibc::core::channel::types::channel::{Counterparty, Order}; - use ibc::core::channel::types::error::{ChannelError, PacketError}; + use ibc::core::channel::types::error::ChannelError; use ibc::core::channel::types::packet::Packet; use ibc::core::channel::types::Version; use ibc::core::host::types::identifiers::{ChannelId, PortId}; @@ -292,7 +292,7 @@ mod tests { &self, _packet: &Packet, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -300,7 +300,7 @@ mod tests { &mut self, _packet: &Packet, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } @@ -309,7 +309,7 @@ mod tests { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -318,7 +318,7 @@ mod tests { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } } @@ -390,7 +390,7 @@ mod tests { &self, _packet: &Packet, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -398,7 +398,7 @@ mod tests { &mut self, _packet: &Packet, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } @@ -407,7 +407,7 @@ mod tests { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> Result<(), PacketError> { + ) -> Result<(), ChannelError> { Ok(()) } @@ -416,7 +416,7 @@ mod tests { _packet: &Packet, _acknowledgement: &Acknowledgement, _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { + ) -> (ModuleExtras, Result<(), ChannelError>) { (ModuleExtras::empty(), Ok(())) } } diff --git a/tests-integration/tests/core/ics02_client/create_client.rs b/tests-integration/tests/core/ics02_client/create_client.rs index 71b40718c..852cdd273 100644 --- a/tests-integration/tests/core/ics02_client/create_client.rs +++ b/tests-integration/tests/core/ics02_client/create_client.rs @@ -8,7 +8,7 @@ use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient}; use ibc::core::client::types::Height; use ibc::core::commitment_types::error::CommitmentError; use ibc::core::entrypoint::{execute, validate}; -use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::error::HandlerError; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::types::path::{ClientConsensusStatePath, NextClientSequencePath}; @@ -180,11 +180,9 @@ fn test_create_expired_mock_client() { let fxt = create_client_fixture(Ctx::Default, Msg::ExpiredMockHeader); create_client_validate( &fxt, - Expect::Failure(Some(ContextError::ClientError( - ClientError::ClientNotActive { - status: Status::Expired, - }, - ))), + Expect::Failure(Some(HandlerError::Client(ClientError::InvalidStatus( + Status::Expired, + )))), ); } @@ -208,11 +206,9 @@ fn test_create_expired_tm_client() { let fxt = create_client_fixture(Ctx::Default, Msg::ExpiredTendermintHeader); create_client_validate( &fxt, - Expect::Failure(Some(ContextError::ClientError( - ClientError::ClientNotActive { - status: Status::Expired, - }, - ))), + Expect::Failure(Some(HandlerError::Client(ClientError::InvalidStatus( + Status::Expired, + )))), ); } @@ -222,9 +218,9 @@ fn test_create_frozen_tm_client() { let fxt = create_client_fixture(Ctx::Default, Msg::FrozenTendermintHeader); create_client_validate( &fxt, - Expect::Failure(Some(ContextError::ClientError(ClientError::ClientFrozen { - description: "the client is frozen".to_string(), - }))), + Expect::Failure(Some(HandlerError::Client(ClientError::InvalidStatus( + Status::Frozen, + )))), ); } @@ -306,6 +302,6 @@ fn test_tm_create_client_proof_verification_ok() { serde_json::to_vec(&(next_client_seq_value + 1)).expect("valid json serialization"), ) .expect_err("proof verification fails"), - ClientError::Ics23Verification(CommitmentError::VerificationFailure) + ClientError::FailedICS23Verification(CommitmentError::FailedToVerifyMembership) )); } diff --git a/tests-integration/tests/core/ics02_client/update_client.rs b/tests-integration/tests/core/ics02_client/update_client.rs index 2a4d64864..6afa73cb9 100644 --- a/tests-integration/tests/core/ics02_client/update_client.rs +++ b/tests-integration/tests/core/ics02_client/update_client.rs @@ -32,7 +32,6 @@ use ibc_testkit::hosts::tendermint::BlockParams; use ibc_testkit::hosts::{ HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost, }; -use ibc_testkit::relayer::error::RelayerError; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; @@ -1527,7 +1526,7 @@ pub(crate) fn build_client_update_datagram( dest: &TestContext, client_id: &ClientId, src_header: &H, -) -> Result +) -> Option where HostClientState: ClientStateValidation, { @@ -1536,23 +1535,15 @@ where let dest_client_latest_height = dest.light_client_latest_height(client_id); if 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, - }); + return None; }; if dest_client_latest_height > src_header.height() { - return Err(RelayerError::ClientAtHigherHeight { - client_id: client_id.clone(), - source_height: src_header.height(), - destination_height: dest_client_latest_height, - }); + return None; }; // Client on destination chain can be updated. - Ok(ClientMsg::UpdateClient(MsgUpdateClient { + Some(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), client_message: src_header.clone().into(), signer: dummy_account_id(), @@ -1615,9 +1606,9 @@ fn client_update_ping_pong() { ); assert!( - client_msg_b_res.is_ok(), - "create_client_update failed for context destination {ctx_b:?}, error: {client_msg_b_res:?}", - ); + client_msg_b_res.is_some(), + "create_client_update failed for context destination {ctx_b:?}", + ); let client_msg_b = client_msg_b_res.unwrap(); @@ -1653,9 +1644,9 @@ fn client_update_ping_pong() { build_client_update_datagram(&ctx_a, &client_on_a_for_b, &b_latest_header); assert!( - client_msg_a_res.is_ok(), - "create_client_update failed for context destination {ctx_a:?}, error: {client_msg_a_res:?}", - ); + client_msg_a_res.is_some(), + "create_client_update failed for context destination {ctx_a:?}", + ); let client_msg_a = client_msg_a_res.unwrap(); diff --git a/tests-integration/tests/core/ics02_client/upgrade_client.rs b/tests-integration/tests/core/ics02_client/upgrade_client.rs index c59a7686a..e2713f4d6 100644 --- a/tests-integration/tests/core/ics02_client/upgrade_client.rs +++ b/tests-integration/tests/core/ics02_client/upgrade_client.rs @@ -4,10 +4,11 @@ use ibc::core::client::types::error::{ClientError, UpgradeClientError}; use ibc::core::client::types::msgs::{ClientMsg, MsgUpgradeClient}; use ibc::core::client::types::Height; use ibc::core::entrypoint::{execute, validate}; -use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::error::HandlerError; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::path::ClientConsensusStatePath; +use ibc_core_host_types::error::HostError; use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tm_client_state_from_header, dummy_valid_tendermint_header, @@ -142,9 +143,11 @@ fn msg_upgrade_client_healthy() { #[test] fn upgrade_client_fail_nonexisting_client() { let fxt = msg_upgrade_client_fixture(Ctx::Default, Msg::Default); - let expected_err = ContextError::ClientError(ClientError::ClientStateNotFound { - client_id: fxt.msg.client_id.clone(), - }); + let expected_err: HandlerError = ClientError::Host(HostError::missing_state(format!( + "missing client state for client {}", + fxt.msg.client_id.clone() + ))) + .into(); upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err))); } @@ -152,22 +155,22 @@ fn upgrade_client_fail_nonexisting_client() { fn upgrade_client_fail_low_upgrade_height() { let fxt: Fixture = msg_upgrade_client_fixture(Ctx::WithClient, Msg::LowUpgradeHeight); - let expected_err: ClientError = UpgradeClientError::LowUpgradeHeight { + let expected_err: ClientError = UpgradeClientError::InsufficientUpgradeHeight { upgraded_height: Height::new(0, 26).unwrap(), client_height: fxt.ctx.host_height().unwrap(), } .into(); upgrade_client_validate( &fxt, - Expect::Failure(Some(ContextError::from(expected_err))), + Expect::Failure(Some(HandlerError::from(expected_err))), ); } #[test] fn upgrade_client_fail_unknown_upgraded_client_state() { let fxt = msg_upgrade_client_fixture(Ctx::WithClient, Msg::UnknownUpgradedClientStateType); - let expected_err = ContextError::ClientError(ClientError::UnknownClientStateType { - client_state_type: client_type().to_string(), - }); + let expected_err = HandlerError::Client(ClientError::InvalidClientStateType( + client_type().to_string(), + )); upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err))); } diff --git a/tests-integration/tests/core/ics03_connection/conn_open_ack.rs b/tests-integration/tests/core/ics03_connection/conn_open_ack.rs index 651ab378f..7bab98ef4 100644 --- a/tests-integration/tests/core/ics03_connection/conn_open_ack.rs +++ b/tests-integration/tests/core/ics03_connection/conn_open_ack.rs @@ -6,13 +6,14 @@ use ibc::core::connection::types::error::ConnectionError; use ibc::core::connection::types::msgs::{ConnectionMsg, MsgConnectionOpenAck}; use ibc::core::connection::types::{ConnectionEnd, Counterparty, State}; use ibc::core::entrypoint::{execute, validate}; -use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::error::HandlerError; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChainId, ClientId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; +use ibc_core_host_types::error::HostError; use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_ack; use ibc_testkit::fixtures::core::context::TestContextConfig; @@ -126,16 +127,18 @@ fn conn_open_ack_validate(fxt: &Fixture, expect: Expect) { let cons_state_height = fxt.msg.consensus_height_of_a_on_b; match res.unwrap_err() { - ContextError::ConnectionError(ConnectionError::ConnectionNotFound { connection_id }) => { - assert_eq!(connection_id, right_connection_id) + HandlerError::Connection(ConnectionError::Host(HostError::MissingState { + ref description, + })) => { + assert!(description.contains(right_connection_id.to_string().as_str())) } - ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { + HandlerError::Connection(ConnectionError::InsufficientConsensusHeight { target_height, current_height: _, }) => { assert_eq!(cons_state_height, target_height); } - ContextError::ConnectionError(ConnectionError::InvalidState { + HandlerError::Connection(ConnectionError::MismatchedConnectionStates { expected: _, actual: _, }) => {} @@ -187,16 +190,18 @@ fn conn_open_ack_healthy() { #[test] fn conn_open_ack_no_connection() { let fxt = conn_open_ack_fixture(Ctx::New); - let expected_err = ContextError::ConnectionError(ConnectionError::ConnectionNotFound { - connection_id: fxt.msg.conn_id_on_a.clone(), - }); + let expected_err: HandlerError = ConnectionError::Host(HostError::missing_state(format!( + "missing connection end for connection {}", + fxt.msg.conn_id_on_a.clone() + ))) + .into(); conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err))); } #[test] fn conn_open_ack_invalid_consensus_height() { let fxt = conn_open_ack_fixture(Ctx::DefaultWithConnection); - let expected_err = ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { + let expected_err = HandlerError::Connection(ConnectionError::InsufficientConsensusHeight { target_height: fxt.msg.consensus_height_of_a_on_b, current_height: Height::new(0, 10).unwrap(), }); @@ -206,7 +211,7 @@ fn conn_open_ack_invalid_consensus_height() { #[test] fn conn_open_ack_connection_mismatch() { let fxt = conn_open_ack_fixture(Ctx::NewWithConnectionEndOpen); - let expected_err = ContextError::ConnectionError(ConnectionError::InvalidState { + let expected_err = HandlerError::Connection(ConnectionError::MismatchedConnectionStates { expected: State::Init.to_string(), actual: State::Open.to_string(), }); diff --git a/tests-integration/tests/core/router.rs b/tests-integration/tests/core/router.rs index 303f5f469..5db8045ae 100644 --- a/tests-integration/tests/core/router.rs +++ b/tests-integration/tests/core/router.rs @@ -1,7 +1,6 @@ use core::ops::Add; use ibc::apps::transfer::handler::send_transfer; -use ibc::apps::transfer::types::error::TokenTransferError; use ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use ibc::apps::transfer::types::{BaseCoin, U256}; use ibc::core::channel::types::error::ChannelError; @@ -14,7 +13,7 @@ use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient, MsgUpdateClient use ibc::core::client::types::Height; use ibc::core::connection::types::msgs::ConnectionMsg; use ibc::core::entrypoint::dispatch; -use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::error::HandlerError; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; @@ -407,10 +406,10 @@ fn routing_module_and_keepers() { let res = match test.msg.clone() { TestMsg::Ics26(msg) => dispatch(&mut ctx.ibc_store, &mut router, msg), TestMsg::Ics20(msg) => send_transfer(&mut ctx.ibc_store, &mut DummyTransferModule, msg) - .map_err(|e: TokenTransferError| ChannelError::AppModule { - description: e.to_string(), + .map_err(|e| ChannelError::AppSpecific { + description: format!("token transfer application error: {e}"), }) - .map_err(ContextError::from), + .map_err(HandlerError::from), }; assert_eq!(