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 b7c0c8eff7..0000000000 --- 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/1375-bump-ibc-proto-and-tendermint-and-msrv.md b/.changelog/unreleased/breaking-changes/1375-bump-ibc-proto-and-tendermint-and-msrv.md new file mode 100644 index 0000000000..0c641fcd60 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1375-bump-ibc-proto-and-tendermint-and-msrv.md @@ -0,0 +1,4 @@ +- [ibc] Update `ibc-proto` to `v0.51.1` and `tendermint` to `v0.40.0`. + ([\#1375](https://github.com/cosmos/ibc-rs/pull/1375)) +- [ibc] Update MSRV to `1.75.0`. + ([\#1375](https://github.com/cosmos/ibc-rs/pull/1375)) diff --git a/.changelog/unreleased/improvements/1362-relax-timeout-check-in-MsgTransfer-conversions.md b/.changelog/unreleased/improvements/1362-relax-timeout-check-in-MsgTransfer-conversions.md new file mode 100644 index 0000000000..e2fa072f73 --- /dev/null +++ b/.changelog/unreleased/improvements/1362-relax-timeout-check-in-MsgTransfer-conversions.md @@ -0,0 +1,2 @@ +- [ibc-apps] Relax timeout check during `MsgTransfer` proto to domain + conversions ([#1362](https://github.com/cosmos/ibc-rs/issues/1362)). diff --git a/.changelog/unreleased/improvements/1374-optimize-vec-push-avoid-realloc-mem.md b/.changelog/unreleased/improvements/1374-optimize-vec-push-avoid-realloc-mem.md new file mode 100644 index 0000000000..65f1b64c1c --- /dev/null +++ b/.changelog/unreleased/improvements/1374-optimize-vec-push-avoid-realloc-mem.md @@ -0,0 +1,2 @@ +- [ibc-app-nft-transfer] Preallocate `Vec` to avoid reallocation of memory + ([\#1374](https://github.com/cosmos/ibc-rs/pull/1374)). 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 b68e2b01c2..f05961027a 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 661201f750..064388cf10 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 f0f87285e3..c656a7fd91 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 9c25b1ba3e..7157bd3beb 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/v0.55.0/breaking-changes/1311-migrate-cosmwasm-workspace.md b/.changelog/v0.55.0/breaking-changes/1311-migrate-cosmwasm-workspace.md new file mode 100644 index 0000000000..2f042580ca --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/breaking-changes/1319-consolidate-decoding-related-errors.md b/.changelog/v0.55.0/breaking-changes/1319-consolidate-decoding-related-errors.md new file mode 100644 index 0000000000..3a466c375c --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/breaking-changes/1320-define-host-error-type.md b/.changelog/v0.55.0/breaking-changes/1320-define-host-error-type.md new file mode 100644 index 0000000000..895cd82bc3 --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/breaking-changes/1339-merge-packet-error-into-channel-error.md b/.changelog/v0.55.0/breaking-changes/1339-merge-packet-error-into-channel-error.md new file mode 100644 index 0000000000..01647c658e --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/breaking-changes/1346-remove-generic-string-error-variants.md b/.changelog/v0.55.0/breaking-changes/1346-remove-generic-string-error-variants.md new file mode 100644 index 0000000000..23df16a16e --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/breaking-changes/1352-update-ConsensusState-timestamp-to-return-Result.md b/.changelog/v0.55.0/breaking-changes/1352-update-ConsensusState-timestamp-to-return-Result.md new file mode 100644 index 0000000000..f21ac0b1fe --- /dev/null +++ b/.changelog/v0.55.0/breaking-changes/1352-update-ConsensusState-timestamp-to-return-Result.md @@ -0,0 +1,3 @@ +- [ibc-core-client] Update ICS-02 `ConsensusState::timestamp()` to return + `Result` + ([\#1352](https://github.com/cosmos/ibc-rs/issues/1352)) diff --git a/.changelog/v0.55.0/breaking-changes/270-standardize-error-variants-to-be-less-specific.md b/.changelog/v0.55.0/breaking-changes/270-standardize-error-variants-to-be-less-specific.md new file mode 100644 index 0000000000..7df9ffd079 --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/breaking-changes/950-return-DecodingError-when-decoding-Any-to-MsgEnvelope.md b/.changelog/v0.55.0/breaking-changes/950-return-DecodingError-when-decoding-Any-to-MsgEnvelope.md new file mode 100644 index 0000000000..bc94b49d54 --- /dev/null +++ b/.changelog/v0.55.0/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/v0.55.0/bug-fixes/1336-remove-faulty-receipt-check-during-recv-packet-validate.md b/.changelog/v0.55.0/bug-fixes/1336-remove-faulty-receipt-check-during-recv-packet-validate.md new file mode 100644 index 0000000000..ea9421ae4e --- /dev/null +++ b/.changelog/v0.55.0/bug-fixes/1336-remove-faulty-receipt-check-during-recv-packet-validate.md @@ -0,0 +1,2 @@ +- [ibc-core] Remove faulty receipt check during `recv_packet_validate` + ([#1336](https://github.com/cosmos/ibc-rs/issues/1336)). diff --git a/.changelog/v0.55.0/improvements/1323-Timestamp-converstions-to-from-host-time.md b/.changelog/v0.55.0/improvements/1323-Timestamp-converstions-to-from-host-time.md new file mode 100644 index 0000000000..26ad71bf92 --- /dev/null +++ b/.changelog/v0.55.0/improvements/1323-Timestamp-converstions-to-from-host-time.md @@ -0,0 +1,3 @@ +- [ibc-primitives] Define utility traits for converting between `Timestamp` and + host-specific time types. + ([#1323](https://github.com/cosmos/ibc-rs/pull/1323)). diff --git a/.changelog/v0.55.0/improvements/1338-remove-redundant-path-constructions-in-testkit.md b/.changelog/v0.55.0/improvements/1338-remove-redundant-path-constructions-in-testkit.md new file mode 100644 index 0000000000..13c2103a99 --- /dev/null +++ b/.changelog/v0.55.0/improvements/1338-remove-redundant-path-constructions-in-testkit.md @@ -0,0 +1,3 @@ +- [ibc-testkit] Remove redundant path constructions in the implementation of + `ValidationContext` for `MockIbcStore` + ([#1338](https://github.com/cosmos/ibc-rs/pull/1338)). diff --git a/.changelog/v0.55.0/summary.md b/.changelog/v0.55.0/summary.md new file mode 100644 index 0000000000..1971ac21a9 --- /dev/null +++ b/.changelog/v0.55.0/summary.md @@ -0,0 +1,22 @@ +This release brings major improvements to error handling in `ibc-rs`, giving +hosting environments better control over errors and easier debugging for the +developers. A key enhancement is the clearer distinction between host-sourced +errors and those propagated by `ibc-rs`, effectively separating host-level +errors from protocol-level ones. Therefore, a noticeable update is the renaming +of the previous `ContextError` to `HandlerError`, which now exclusively manages +errors from IBC handlers. In parallel, a new `HostError` has been introduced to +handle errors originating from hosts, particularly those from validation and +execution contexts. Additionally, error definitions within `ibc-rs` have been +unified, reducing the granularity of error variants. For more details, please +refer to [ADR-011](./docs/architecture/adr-11-refactor-errors.md). + +In addition, it introduces various fixes and enhancements. Notably, helper +traits with default implementations have been added to simplify the conversion +between host time types and `Timestamp`. Consequently, the `ibc-primitives` +crate has been fully decoupled from the `tendermint` dependency. + +It’s also worth noting that the `cosmwasm` workspace has been relocated to its +own repository, now available under +[cosmwasm-ibc](https://github.com/informalsystems/cosmwasm-ibc). + +There are no consensus-breaking changes in this release. diff --git a/.changelog/v0.55.1/bug-fixes/1357-remove-non-existing-ibc-client-cw-from-workspace.md b/.changelog/v0.55.1/bug-fixes/1357-remove-non-existing-ibc-client-cw-from-workspace.md new file mode 100644 index 0000000000..fc3469c1e7 --- /dev/null +++ b/.changelog/v0.55.1/bug-fixes/1357-remove-non-existing-ibc-client-cw-from-workspace.md @@ -0,0 +1,2 @@ +- [ibc] Remove non-existing `ibc-client-cw` link in the `Cargo.toml` workspace. + ([#1357](https://github.com/cosmos/ibc-rs/issues/1357)). diff --git a/.changelog/v0.55.1/improvements/1356-impl-From-Infallible-for-ClientError.md b/.changelog/v0.55.1/improvements/1356-impl-From-Infallible-for-ClientError.md new file mode 100644 index 0000000000..e453d3f20a --- /dev/null +++ b/.changelog/v0.55.1/improvements/1356-impl-From-Infallible-for-ClientError.md @@ -0,0 +1,2 @@ +- [ibc-core] Implement `From` for `ClientError` + ([#1356](https://github.com/cosmos/ibc-rs/issues/1356)). diff --git a/.changelog/v0.55.1/summary.md b/.changelog/v0.55.1/summary.md new file mode 100644 index 0000000000..2ebb97411e --- /dev/null +++ b/.changelog/v0.55.1/summary.md @@ -0,0 +1,4 @@ +This patch release fixes the `Cargo.toml` workspace file and adds a helper +`From` implementation for `ClientError`. + +There are no consensus-breaking changes in this release. diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index d331361848..4ddf016007 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -110,8 +110,8 @@ jobs: name: Test MSRV for `ibc` libraries timeout-minutes: 30 env: - CARGO_MSRV_VERSION: 0.16.0-beta.23 - MSRV: 1.72.1 + CARGO_MSRV_VERSION: 0.16.2 + MSRV: 1.75.0 strategy: matrix: param: @@ -141,7 +141,7 @@ jobs: cargo msrv --version - name: Calculate MSRV - run: cargo msrv --output-format minimal --min 1.68.0 + run: cargo msrv find --output-format minimal --min 1.68.0 - name: Build with MSRV uses: actions-rs/cargo@v1 diff --git a/.gitignore b/.gitignore index 56794f065e..1cb584d2d0 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,5 @@ mc.log # caused by a dependency updated to a faulty version. /Cargo.lock -# Ignore CosmWasm artifacts -cw-contracts/ - # Ignore dotenv .env diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e1495796..ebb285c4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,90 @@ # CHANGELOG +## v0.55.1 + +*October 17, 2024* + +This patch release fixes the `Cargo.toml` workspace file and adds a helper +`From` implementation for `ClientError`. + +There are no consensus-breaking changes in this release. + +### BUG FIXES + +- [ibc] Remove non-existing `ibc-client-cw` link in the `Cargo.toml` workspace. + ([#1357](https://github.com/cosmos/ibc-rs/issues/1357)). + +### IMPROVEMENTS + +- [ibc-core] Implement `From` for `ClientError` + ([#1356](https://github.com/cosmos/ibc-rs/issues/1356)). + +## v0.55.0 + +*September 26, 2024* + +This release brings major improvements to error handling in `ibc-rs`, giving +hosting environments better control over errors and easier debugging for the +developers. A key enhancement is the clearer distinction between host-sourced +errors and those propagated by `ibc-rs`, effectively separating host-level +errors from protocol-level ones. Therefore, a noticeable update is the renaming +of the previous `ContextError` to `HandlerError`, which now exclusively manages +errors from IBC handlers. In parallel, a new `HostError` has been introduced to +handle errors originating from hosts, particularly those from validation and +execution contexts. Additionally, error definitions within `ibc-rs` have been +unified, reducing the granularity of error variants. For more details, please +refer to [ADR-011](./docs/architecture/adr-11-refactor-errors.md). + +In addition, it introduces various fixes and enhancements. Notably, helper +traits with default implementations have been added to simplify the conversion +between host time types and `Timestamp`. Consequently, the `ibc-primitives` +crate has been fully decoupled from the `tendermint` dependency. + +It’s also worth noting that the `cosmwasm` workspace has been relocated to its +own repository, now available under +[cosmwasm-ibc](https://github.com/informalsystems/cosmwasm-ibc). + +There are no consensus-breaking changes in this release. + +### BREAKING CHANGES + +- [ibc] Standardize error variants across the codebase to make them less + specific and more consistent. + ([\#270](https://github.com/cosmos/ibc-rs/issues/270)) +- [ibc-core-handler] Return `DecodingError` for `MsgEnvelope` when trying to + decode from `Any` ([\#950](https://github.com/cosmos/ibc-rs/issues/950)) +- [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)) +- [ibc] Consolidate decoding-related errors into new `DecodingError` type + ([\1319](https://github.com/cosmos/ibc-rs/issues/1319)) +- [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)) +- [ibc-core-channel] Merge `PacketError` type into `ChannelError` + ([#1339](https://github.com/cosmos/ibc-rs/issues/1339)) +- [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)) +- [ibc-core-client] Update ICS-02 `ConsensusState::timestamp()` to return + `Result` + ([\#1352](https://github.com/cosmos/ibc-rs/issues/1352)) + +### BUG FIXES + +- [ibc-core] Remove faulty receipt check during `recv_packet_validate` + ([#1336](https://github.com/cosmos/ibc-rs/issues/1336)). + +### IMPROVEMENTS + +- [ibc-primitives] Define utility traits for converting between `Timestamp` and + host-specific time types. + ([#1323](https://github.com/cosmos/ibc-rs/pull/1323)). +- [ibc-testkit] Remove redundant path constructions in the implementation of + `ValidationContext` for `MockIbcStore` + ([#1338](https://github.com/cosmos/ibc-rs/pull/1338)). + ## v0.54.0 *August 12, 2024* @@ -641,9 +726,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 +1127,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 +1184,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 8d7b2cd2fc..16843ae9eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,10 +44,10 @@ exclude = [ ] [workspace.package] -version = "0.54.0" +version = "0.55.1" license = "Apache-2.0" edition = "2021" -rust-version = "1.72.1" +rust-version = "1.75.0" readme = "README.md" repository = "https://github.com/cosmos/ibc-rs" authors = [ "Informal Systems " ] @@ -57,9 +57,9 @@ authors = [ "Informal Systems " ] base64 = { version = "0.22", default-features = false } borsh = { version = "1", default-features = false, features = [ "derive" ] } displaydoc = { version = "0.2.5", default-features = false } -prost = { version = "0.13.1", default-features = false } +prost = { version = "0.13.2", default-features = false } derive_more = { version = "1.0.0", default-features = false, features = [ "from", "into", "display", "try_into" ] } -rstest = { version = "0.22" } +rstest = { version = "0.23" } schemars = { version = "0.8.21" } sha2 = { version = "0.10.8", default-features = false } serde = { version = "1.0", default-features = false } @@ -68,52 +68,51 @@ subtle-encoding = { version = "0.5", default-features = false } hex = { version = "0.4.3", default-features = false } # ibc dependencies -ibc = { version = "0.54.0", path = "./ibc", default-features = false } -ibc-core = { version = "0.54.0", path = "./ibc-core", default-features = false } -ibc-clients = { version = "0.54.0", path = "./ibc-clients", default-features = false } -ibc-apps = { version = "0.54.0", path = "./ibc-apps", default-features = false } -ibc-primitives = { version = "0.54.0", path = "./ibc-primitives", default-features = false } -ibc-testkit = { version = "0.54.0", path = "./ibc-testkit", default-features = false } +ibc = { version = "0.55.1", path = "./ibc", default-features = false } +ibc-core = { version = "0.55.1", path = "./ibc-core", default-features = false } +ibc-clients = { version = "0.55.1", path = "./ibc-clients", default-features = false } +ibc-apps = { version = "0.55.1", path = "./ibc-apps", default-features = false } +ibc-primitives = { version = "0.55.1", path = "./ibc-primitives", default-features = false } +ibc-testkit = { version = "0.55.1", path = "./ibc-testkit", default-features = false } -ibc-derive = { version = "0.8.0", path = "./ibc-derive" } +ibc-derive = { version = "0.9.0", path = "./ibc-derive" } -ibc-core-client = { version = "0.54.0", path = "./ibc-core/ics02-client", default-features = false } -ibc-core-connection = { version = "0.54.0", path = "./ibc-core/ics03-connection", default-features = false } -ibc-core-channel = { version = "0.54.0", path = "./ibc-core/ics04-channel", default-features = false } -ibc-core-host = { version = "0.54.0", path = "./ibc-core/ics24-host", default-features = false } -ibc-core-handler = { version = "0.54.0", path = "./ibc-core/ics25-handler", default-features = false } -ibc-core-router = { version = "0.54.0", path = "./ibc-core/ics26-routing", default-features = false } -ibc-query = { version = "0.54.0", path = "./ibc-query", default-features = false } +ibc-core-client = { version = "0.55.1", path = "./ibc-core/ics02-client", default-features = false } +ibc-core-connection = { version = "0.55.1", path = "./ibc-core/ics03-connection", default-features = false } +ibc-core-channel = { version = "0.55.1", path = "./ibc-core/ics04-channel", default-features = false } +ibc-core-host = { version = "0.55.1", path = "./ibc-core/ics24-host", default-features = false } +ibc-core-handler = { version = "0.55.1", path = "./ibc-core/ics25-handler", default-features = false } +ibc-core-router = { version = "0.55.1", path = "./ibc-core/ics26-routing", default-features = false } +ibc-query = { version = "0.55.1", path = "./ibc-query", default-features = false } -ibc-client-cw = { version = "0.54.0", path = "./ibc-clients/cw-context", default-features = false } -ibc-client-tendermint = { version = "0.54.0", path = "./ibc-clients/ics07-tendermint", default-features = false } +ibc-client-tendermint = { version = "0.55.1", path = "./ibc-clients/ics07-tendermint", default-features = false } -ibc-app-transfer = { version = "0.54.0", path = "./ibc-apps/ics20-transfer", default-features = false } -ibc-app-nft-transfer = { version = "0.54.0", path = "./ibc-apps/ics721-nft-transfer", default-features = false } +ibc-app-transfer = { version = "0.55.1", path = "./ibc-apps/ics20-transfer", default-features = false } +ibc-app-nft-transfer = { version = "0.55.1", path = "./ibc-apps/ics721-nft-transfer", default-features = false } -ibc-core-client-context = { version = "0.54.0", path = "./ibc-core/ics02-client/context", default-features = false } -ibc-core-client-types = { version = "0.54.0", path = "./ibc-core/ics02-client/types", default-features = false } -ibc-core-channel-types = { version = "0.54.0", path = "./ibc-core/ics04-channel/types", default-features = false } -ibc-core-connection-types = { version = "0.54.0", path = "./ibc-core/ics03-connection/types", default-features = false } -ibc-core-commitment-types = { version = "0.54.0", path = "./ibc-core/ics23-commitment/types", default-features = false } -ibc-core-host-cosmos = { version = "0.54.0", path = "./ibc-core/ics24-host/cosmos", default-features = false } -ibc-core-host-types = { version = "0.54.0", path = "./ibc-core/ics24-host/types", default-features = false } -ibc-core-handler-types = { version = "0.54.0", path = "./ibc-core/ics25-handler/types", default-features = false } -ibc-core-router-types = { version = "0.54.0", path = "./ibc-core/ics26-routing/types", default-features = false } -ibc-client-tendermint-types = { version = "0.54.0", path = "./ibc-clients/ics07-tendermint/types", default-features = false } -ibc-client-wasm-types = { version = "0.54.0", path = "./ibc-clients/ics08-wasm/types", default-features = false } -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-core-client-context = { version = "0.55.1", path = "./ibc-core/ics02-client/context", default-features = false } +ibc-core-client-types = { version = "0.55.1", path = "./ibc-core/ics02-client/types", default-features = false } +ibc-core-channel-types = { version = "0.55.1", path = "./ibc-core/ics04-channel/types", default-features = false } +ibc-core-connection-types = { version = "0.55.1", path = "./ibc-core/ics03-connection/types", default-features = false } +ibc-core-commitment-types = { version = "0.55.1", path = "./ibc-core/ics23-commitment/types", default-features = false } +ibc-core-host-cosmos = { version = "0.55.1", path = "./ibc-core/ics24-host/cosmos", default-features = false } +ibc-core-host-types = { version = "0.55.1", path = "./ibc-core/ics24-host/types", default-features = false } +ibc-core-handler-types = { version = "0.55.1", path = "./ibc-core/ics25-handler/types", default-features = false } +ibc-core-router-types = { version = "0.55.1", path = "./ibc-core/ics26-routing/types", default-features = false } +ibc-client-tendermint-types = { version = "0.55.1", path = "./ibc-clients/ics07-tendermint/types", default-features = false } +ibc-client-wasm-types = { version = "0.55.1", path = "./ibc-clients/ics08-wasm/types", default-features = false } +ibc-app-transfer-types = { version = "0.55.1", path = "./ibc-apps/ics20-transfer/types", default-features = false } +ibc-app-nft-transfer-types = { version = "0.55.1", path = "./ibc-apps/ics721-nft-transfer/types", default-features = false } -ibc-proto = { version = "0.47.0", default-features = false } +ibc-proto = { version = "0.51.1", default-features = false } # cosmos dependencies -tendermint = { version = "0.38.0", default-features = false } -tendermint-light-client = { version = "0.38.0", default-features = false } -tendermint-light-client-verifier = { version = "0.38.0", default-features = false } -tendermint-proto = { version = "0.38.0", default-features = false } -tendermint-rpc = { version = "0.38.0", default-features = false } -tendermint-testgen = { version = "0.38.0", default-features = false } +tendermint = { version = "0.40.0", default-features = false } +tendermint-light-client = { version = "0.40.0", default-features = false } +tendermint-light-client-verifier = { version = "0.40.0", default-features = false } +tendermint-proto = { version = "0.40.0", default-features = false } +tendermint-rpc = { version = "0.40.0", default-features = false } +tendermint-testgen = { version = "0.40.0", default-features = false } # parity dependencies parity-scale-codec = { version = "3.6.12", default-features = false, features = [ "derive" ] } diff --git a/ci/cw-check/Cargo.lock b/ci/cw-check/Cargo.lock index b7b3e2a48c..7c5d3781a5 100644 --- a/ci/cw-check/Cargo.lock +++ b/ci/cw-check/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -16,15 +16,15 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "ark-bls12-381" @@ -149,21 +149,21 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base16ct" @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9ec96fe9a81b5e365f9db71fe00edc4fe4ca2cc7dcb7861f0603012a7caa210" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" dependencies = [ "arrayref", "arrayvec", @@ -246,18 +246,21 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.1.10" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -273,21 +276,33 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "cosmos-sdk-proto" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462e1f6a8e005acc8835d32d60cbd7973ed65ea2a8d8473830e675f050956427" +dependencies = [ + "informalsystems-pbjson", + "prost", + "serde", + "tendermint-proto", +] [[package]] name = "cosmwasm-core" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d905990ef3afb5753bb709dc7de88e9e370aa32bcc2f31731d4b533b63e82490" +checksum = "5f6ceb8624260d0d3a67c4e1a1d43fc7e9406720afbcb124521501dd138f90aa" [[package]] name = "cosmwasm-crypto" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b2a7bd9c1dd9a377a4dc0f4ad97d24b03c33798cd5a6d7ceb8869b41c5d2f2d" +checksum = "4125381e5fd7fefe9f614640049648088015eca2b60d861465329a5d87dfa538" dependencies = [ "ark-bls12-381", "ark-ec", @@ -308,20 +323,20 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029910b409398fdf81955d7301b906caf81f2c42b013ea074fbd89720229c424" +checksum = "1b5658b1dc64e10b56ae7a449f678f96932a96f6cfad1769d608d1d1d656480a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] name = "cosmwasm-schema" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc0d4d85e83438ab9a0fea9348446f7268bc016aacfebce37e998559f151294" +checksum = "f86b4d949b6041519c58993a73f4bbfba8083ba14f7001eae704865a09065845" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -332,20 +347,20 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edf5c8adac41bb7751c050d7c4c18675be19ee128714454454575e894424eeef" +checksum = "c8ef1b5835a65fcca3ab8b9a02b4f4dacc78e233a5c2f20b270efb9db0666d12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] name = "cosmwasm-std" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51dec99a2e478715c0a4277f0dbeadbb8466500eb7dec873d0924edd086e77f1" +checksum = "70eb7ab0c1e99dd6207496963ba2a457c4128ac9ad9c72a83f8d9808542b849b" dependencies = [ "base64 0.22.1", "bech32", @@ -366,9 +381,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -450,7 +465,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -503,7 +518,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -523,7 +538,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", "unicode-xid", ] @@ -547,7 +562,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -660,9 +675,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -674,9 +689,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -684,33 +699,33 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-sink", @@ -771,6 +786,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + [[package]] name = "hex" version = "0.4.3" @@ -788,7 +809,7 @@ dependencies = [ [[package]] name = "ibc-app-transfer" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-app-transfer-types", "ibc-core", @@ -797,7 +818,7 @@ dependencies = [ [[package]] name = "ibc-app-transfer-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -812,14 +833,14 @@ dependencies = [ [[package]] name = "ibc-apps" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-app-transfer", ] [[package]] name = "ibc-client-wasm-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "base64 0.22.1", "displaydoc", @@ -831,7 +852,7 @@ dependencies = [ [[package]] name = "ibc-core" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -846,7 +867,7 @@ dependencies = [ [[package]] name = "ibc-core-channel" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-core-channel-types", "ibc-core-client", @@ -860,7 +881,7 @@ dependencies = [ [[package]] name = "ibc-core-channel-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -881,7 +902,7 @@ dependencies = [ [[package]] name = "ibc-core-client" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-core-client-context", "ibc-core-client-types", @@ -893,7 +914,7 @@ dependencies = [ [[package]] name = "ibc-core-client-context" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -908,7 +929,7 @@ dependencies = [ [[package]] name = "ibc-core-client-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -926,7 +947,7 @@ dependencies = [ [[package]] name = "ibc-core-commitment-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -943,7 +964,7 @@ dependencies = [ [[package]] name = "ibc-core-connection" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-client-wasm-types", "ibc-core-client", @@ -956,7 +977,7 @@ dependencies = [ [[package]] name = "ibc-core-connection-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -975,7 +996,7 @@ dependencies = [ [[package]] name = "ibc-core-handler" -version = "0.54.0" +version = "0.55.1" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -989,7 +1010,7 @@ dependencies = [ [[package]] name = "ibc-core-handler-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -1011,7 +1032,7 @@ dependencies = [ [[package]] name = "ibc-core-host" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -1028,12 +1049,14 @@ dependencies = [ [[package]] name = "ibc-core-host-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ + "base64 0.22.1", "derive_more 0.99.18", "displaydoc", "ibc-primitives", "parity-scale-codec", + "prost", "scale-info", "schemars", "serde", @@ -1041,7 +1064,7 @@ dependencies = [ [[package]] name = "ibc-core-router" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -1054,7 +1077,7 @@ dependencies = [ [[package]] name = "ibc-core-router-types" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -1071,16 +1094,16 @@ dependencies = [ [[package]] name = "ibc-derive" -version = "0.8.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] name = "ibc-primitives" -version = "0.54.0" +version = "0.55.1" dependencies = [ "derive_more 0.99.18", "displaydoc", @@ -1090,24 +1113,22 @@ dependencies = [ "scale-info", "schemars", "serde", - "tendermint", "time", ] [[package]] name = "ibc-proto" -version = "0.47.0" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1678333cf68c9094ca66aaf9a271269f1f6bf5c26881161def8bd88cee831a23" +checksum = "9b70f517162e74e2d35875b8b94bf4d1e45f2c69ef3de452dc855944455d33ca" dependencies = [ "base64 0.22.1", "bytes", + "cosmos-sdk-proto", "flex-error", "ics23", "informalsystems-pbjson", - "parity-scale-codec", "prost", - "scale-info", "schemars", "serde", "subtle-encoding", @@ -1135,9 +1156,9 @@ dependencies = [ [[package]] name = "impl-serde" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" dependencies = [ "serde", ] @@ -1155,12 +1176,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.1", ] [[package]] @@ -1199,9 +1220,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -1220,9 +1241,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "memchr" @@ -1266,9 +1287,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "p256" @@ -1314,9 +1335,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1360,9 +1381,9 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.12.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" dependencies = [ "fixed-hash", "impl-serde", @@ -1371,27 +1392,27 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -1399,31 +1420,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.74", -] - -[[package]] -name = "prost-types" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" -dependencies = [ - "prost", + "syn 2.0.87", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1498,9 +1510,9 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -1513,26 +1525,26 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" dependencies = [ "cfg-if", - "derive_more 0.99.18", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] @@ -1556,7 +1568,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -1580,9 +1592,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.206" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -1607,13 +1619,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.206" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -1624,14 +1636,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -1647,7 +1659,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -1671,6 +1683,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -1725,9 +1743,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1736,9 +1754,9 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.38.1" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d9d6ffeb83b1de47c307c6e0d2dff56c6256989299010ad03cd80a8491e97" +checksum = "37d513ce7f9e41c67ab2dd3d554ef65f36fbcc61745af1e1f93eafdeefa1ce37" dependencies = [ "bytes", "digest", @@ -1748,7 +1766,6 @@ dependencies = [ "num-traits", "once_cell", "prost", - "prost-types", "serde", "serde_bytes", "serde_json", @@ -1763,14 +1780,16 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.38.1" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed14abe3b0502a3afe21ca74ca5cdd6c7e8d326d982c26f98a394445eb31d6e" +checksum = "c81ba1b023ec00763c3bc4f4376c67c0047f185cccf95c416c7a2f16272c4cbb" dependencies = [ "bytes", "flex-error", + "parity-scale-codec", "prost", - "prost-types", + "scale-info", + "schemars", "serde", "serde_bytes", "subtle-encoding", @@ -1779,22 +1798,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -1835,9 +1854,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -1852,9 +1871,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "uint" -version = "0.9.5" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" dependencies = [ "byteorder", "crunchy", @@ -1864,15 +1883,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "version_check" @@ -1888,9 +1907,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -1913,7 +1932,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] [[package]] @@ -1933,5 +1952,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.87", ] diff --git a/ci/no-std-check/Cargo.lock b/ci/no-std-check/Cargo.lock index 01ea501f8b..cbea91bf43 100644 --- a/ci/no-std-check/Cargo.lock +++ b/ci/no-std-check/Cargo.lock @@ -14,18 +14,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -61,39 +61,15 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "ark-bls12-377" @@ -232,35 +208,35 @@ checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -355,9 +331,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" dependencies = [ "arrayref", "arrayvec", @@ -386,9 +362,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +checksum = "f5327f6c99920069d1fe374aa743be1af0031dea9f250852cdf1ae6a0861ee24" dependencies = [ "borsh-derive", "cfg_aliases", @@ -396,23 +372,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +checksum = "10aedd8f1a81a8aafbfde924b0e3061cd6fedd6f6bbcfc6a76e6fd426d7bfe26" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.70", - "syn_derive", + "syn 2.0.87", ] [[package]] name = "bounded-collections" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32385ecb91a31bddaf908e8dcf4a15aef1bcd3913cc03ebfad02ff6d568abc1" +checksum = "3d077619e9c237a5d1875166f5e8033e8f6bff0c96f8caf81e1c2d7738c431bf" dependencies = [ "log", "parity-scale-codec", @@ -429,12 +404,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -449,18 +418,21 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -474,18 +446,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-targets", -] - [[package]] name = "common-path" version = "1.0.0" @@ -500,9 +460,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "constcat" @@ -511,16 +471,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "cosmos-sdk-proto" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "462e1f6a8e005acc8835d32d60cbd7973ed65ea2a8d8473830e675f050956427" +dependencies = [ + "informalsystems-pbjson", + "prost", + "serde", + "tendermint-proto", +] [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -588,7 +554,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -642,7 +608,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -653,7 +619,27 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -685,23 +671,23 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] name = "docify" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" +checksum = "a772b62b1837c8f060432ddcc10b17aae1453ef17617a99bc07789252d2a5896" dependencies = [ "docify_macros", ] [[package]] name = "docify_macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" +checksum = "60e6be249b0a462a14784a99b19bf35a667bb5e09de611738bb7362fa4c95ff7" dependencies = [ "common-path", "derive-syn-parse", @@ -709,7 +695,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.70", + "syn 2.0.87", "termcolor", "toml", "walkdir", @@ -859,7 +845,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -926,9 +912,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -941,9 +927,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -951,15 +937,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -969,38 +955,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1048,9 +1034,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "group" @@ -1097,6 +1083,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1145,32 +1137,9 @@ dependencies = [ "hmac 0.8.1", ] -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "ibc" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-apps", "ibc-clients", @@ -1182,7 +1151,7 @@ dependencies = [ [[package]] name = "ibc-app-transfer" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-app-transfer-types", "ibc-core", @@ -1191,29 +1160,29 @@ dependencies = [ [[package]] name = "ibc-app-transfer-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core", "ibc-proto", - "primitive-types", + "primitive-types 0.13.1", "serde", - "uint", + "uint 0.10.0", ] [[package]] name = "ibc-apps" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-app-transfer", ] [[package]] name = "ibc-client-tendermint" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "ibc-client-tendermint-types", "ibc-core-client", "ibc-core-commitment-types", @@ -1227,7 +1196,7 @@ dependencies = [ [[package]] name = "ibc-client-tendermint-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ "displaydoc", "ibc-core-client-types", @@ -1243,7 +1212,7 @@ dependencies = [ [[package]] name = "ibc-client-wasm-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ "base64 0.22.1", "displaydoc", @@ -1256,7 +1225,7 @@ dependencies = [ [[package]] name = "ibc-clients" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-client-tendermint", "ibc-client-wasm-types", @@ -1264,7 +1233,7 @@ dependencies = [ [[package]] name = "ibc-core" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -1279,7 +1248,7 @@ dependencies = [ [[package]] name = "ibc-core-channel" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-core-channel-types", "ibc-core-client", @@ -1293,9 +1262,9 @@ dependencies = [ [[package]] name = "ibc-core-channel-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-client-types", "ibc-core-commitment-types", @@ -1311,7 +1280,7 @@ dependencies = [ [[package]] name = "ibc-core-client" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-core-client-context", "ibc-core-client-types", @@ -1323,9 +1292,9 @@ dependencies = [ [[package]] name = "ibc-core-client-context" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-client-types", "ibc-core-commitment-types", @@ -1338,9 +1307,9 @@ dependencies = [ [[package]] name = "ibc-core-client-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-commitment-types", "ibc-core-host-types", @@ -1353,9 +1322,9 @@ dependencies = [ [[package]] name = "ibc-core-commitment-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-host-types", "ibc-primitives", @@ -1367,7 +1336,7 @@ dependencies = [ [[package]] name = "ibc-core-connection" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-core-client", "ibc-core-connection-types", @@ -1378,9 +1347,9 @@ dependencies = [ [[package]] name = "ibc-core-connection-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-client-types", "ibc-core-commitment-types", @@ -1394,7 +1363,7 @@ dependencies = [ [[package]] name = "ibc-core-handler" -version = "0.53.0" +version = "0.55.1" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -1408,9 +1377,9 @@ dependencies = [ [[package]] name = "ibc-core-handler-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-channel-types", "ibc-core-client-types", @@ -1427,9 +1396,9 @@ dependencies = [ [[package]] name = "ibc-core-host" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-channel-types", "ibc-core-client-context", @@ -1444,9 +1413,9 @@ dependencies = [ [[package]] name = "ibc-core-host-cosmos" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-app-transfer-types", "ibc-client-tendermint", @@ -1466,19 +1435,21 @@ dependencies = [ [[package]] name = "ibc-core-host-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "base64 0.22.1", + "derive_more 0.99.18", "displaydoc", "ibc-primitives", + "prost", "serde", ] [[package]] name = "ibc-core-router" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-channel-types", "ibc-core-host-types", @@ -1489,9 +1460,9 @@ dependencies = [ [[package]] name = "ibc-core-router-types" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-core-host-types", "ibc-primitives", @@ -1503,41 +1474,38 @@ dependencies = [ [[package]] name = "ibc-derive" -version = "0.7.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] name = "ibc-primitives" -version = "0.53.0" +version = "0.55.1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "displaydoc", "ibc-proto", "prost", "serde", - "tendermint", "time", ] [[package]] name = "ibc-proto" -version = "0.46.0" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb09e0b52b8a16e98ce98845e7c15b018440f3c56defa12fa44782cd66bab65" +checksum = "9b70f517162e74e2d35875b8b94bf4d1e45f2c69ef3de452dc855944455d33ca" dependencies = [ "base64 0.22.1", - "borsh", "bytes", + "cosmos-sdk-proto", "flex-error", "ics23", "informalsystems-pbjson", - "parity-scale-codec", "prost", - "scale-info", "serde", "subtle-encoding", "tendermint-proto", @@ -1545,9 +1513,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", @@ -1580,6 +1548,15 @@ dependencies = [ "serde", ] +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -1593,12 +1570,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.1", ] [[package]] @@ -1640,9 +1617,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -1653,20 +1630,11 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -1693,9 +1661,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libsecp256k1" @@ -1763,9 +1731,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" -version = "0.0.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] @@ -1799,11 +1767,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1816,7 +1784,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", - "syn 2.0.70", + "syn 2.0.87", "tendermint", "tendermint-light-client-verifier", "tendermint-proto", @@ -1828,6 +1796,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1884,18 +1862,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -1903,6 +1881,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parity-bip39" version = "2.0.1" @@ -1995,9 +1979,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2039,7 +2023,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2049,7 +2033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2060,18 +2044,21 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2082,57 +2069,45 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "impl-serde", + "impl-serde 0.4.0", "scale-info", - "uint", + "uint 0.9.5", ] [[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit 0.21.1", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "primitive-types" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", + "fixed-hash", + "impl-serde 0.5.0", + "uint 0.10.0", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-crate" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -2140,31 +2115,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.70", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost", + "syn 2.0.87", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2207,9 +2173,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -2231,19 +2197,19 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -2257,13 +2223,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2274,9 +2240,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rfc6979" @@ -2311,18 +2277,18 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -2341,13 +2307,13 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" dependencies = [ "bitvec", "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -2355,14 +2321,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] @@ -2451,9 +2417,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -2478,22 +2444,23 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2506,14 +2473,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2571,6 +2538,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -2648,7 +2621,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.4.0", "itertools 0.11.0", "k256", "libsecp256k1", @@ -2658,7 +2631,7 @@ dependencies = [ "parity-scale-codec", "parking_lot", "paste", - "primitive-types", + "primitive-types 0.12.2", "rand", "scale-info", "schnorrkel", @@ -2701,7 +2674,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2767,9 +2740,9 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "38.0.0" +version = "38.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ef409c414546b655ec1e94aaea178e4a97e21284a91b24c762aebf836d3b49" +checksum = "5273900f0b0bef48b2e1ff9c4fb5e188b8168ee5891418a427f4be2af92ee40f" dependencies = [ "docify", "either", @@ -2789,6 +2762,7 @@ dependencies = [ "sp-io", "sp-std", "sp-weights", + "tracing", ] [[package]] @@ -2801,7 +2775,7 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "polkavm-derive", - "primitive-types", + "primitive-types 0.12.2", "sp-externalities", "sp-runtime-interface-proc-macro", "sp-std", @@ -2822,7 +2796,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2858,7 +2832,7 @@ version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", @@ -2867,9 +2841,9 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "17.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b3decf116db9f1dfaf1f1597096b043d0e12c952d3bcdc018c6d6b77deec7e" +checksum = "cf641a1d17268c8fcfdb8e0fa51a79c2d4222f4cfda5f3944dbdbc384dced8d5" dependencies = [ "parity-scale-codec", "tracing", @@ -2903,10 +2877,11 @@ dependencies = [ [[package]] name = "sp-wasm-interface" -version = "21.0.0" +version = "21.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b04b919e150b4736d85089d49327eab65507deb1485eec929af69daa2278eb3" +checksum = "b066baa6d57951600b14ffe1243f54c47f9c23dd89c262e17ca00ae8dca58be9" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -2939,9 +2914,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.47.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" +checksum = "19409f13998e55816d1c728395af0b52ec066206341d939e22e7766df9b494b8" dependencies = [ "Inflector", "num-format", @@ -3005,27 +2980,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.70" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.70", -] - [[package]] name = "tap" version = "1.0.1" @@ -3034,9 +2997,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tendermint" -version = "0.37.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954496fbc9716eb4446cdd6d00c071a3e2f22578d62aa03b40c7e5b4fda3ed42" +checksum = "37d513ce7f9e41c67ab2dd3d554ef65f36fbcc61745af1e1f93eafdeefa1ce37" dependencies = [ "bytes", "digest 0.10.7", @@ -3047,7 +3010,6 @@ dependencies = [ "num-traits", "once_cell", "prost", - "prost-types", "serde", "serde_bytes", "serde_json", @@ -3063,11 +3025,11 @@ dependencies = [ [[package]] name = "tendermint-light-client-verifier" -version = "0.37.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3848090df4502a09ee27cb1a00f1835e1111c8993b22c5e1e41ffb7f6f09d57e" +checksum = "7affc5fffe9df158185e15bce3e47fc3a0c901e6708f3b7d33f0867d7aef8ce1" dependencies = [ - "derive_more", + "derive_more 0.99.18", "flex-error", "serde", "tendermint", @@ -3076,14 +3038,16 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.37.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc87024548c7f3da479885201e3da20ef29e85a3b13d04606b380ac4c7120d87" +checksum = "c81ba1b023ec00763c3bc4f4376c67c0047f185cccf95c416c7a2f16272c4cbb" dependencies = [ + "borsh", "bytes", "flex-error", + "parity-scale-codec", "prost", - "prost-types", + "scale-info", "serde", "serde_bytes", "subtle-encoding", @@ -3101,22 +3065,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3136,8 +3100,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", + "serde", "time-core", "time-macros", ] @@ -3175,47 +3141,36 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.14" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.15", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow", ] [[package]] @@ -3224,6 +3179,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3237,7 +3193,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3252,45 +3208,32 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - [[package]] name = "tracing-subscriber" -version = "0.2.25" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "ansi_term", - "chrono", - "lazy_static", "matchers", + "nu-ansi-term", + "once_cell", "regex", - "serde", - "serde_json", "sharded-slab", "smallvec", "thread_local", + "time", "tracing", "tracing-core", "tracing-log", - "tracing-serde", ] [[package]] @@ -3344,11 +3287,23 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -3361,9 +3316,9 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "valuable" @@ -3373,15 +3328,15 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "w3f-bls" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5da5fa2c6afa2c9158eaa7cd9aee249765eb32b5fb0c63ad8b9e79336a47ec" +checksum = "70a3028804c8bbae2a97a15b71ffc0e308c4b01a520994aafa77d56e94e19024" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -3417,60 +3372,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.70", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.70", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - [[package]] name = "winapi" version = "0.3.9" @@ -3489,9 +3390,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] @@ -3502,20 +3403,11 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] @@ -3586,18 +3478,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -3617,6 +3500,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -3628,7 +3512,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3648,5 +3532,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] diff --git a/ci/no-std-check/Cargo.toml b/ci/no-std-check/Cargo.toml index a1b614cb3b..c95f75cbe0 100644 --- a/ci/no-std-check/Cargo.toml +++ b/ci/no-std-check/Cargo.toml @@ -6,14 +6,14 @@ resolver = "2" [dependencies] ibc = { path = "../../ibc", default-features = false, features = [ "serde" ] } -ibc-proto = { version = "0.47", default-features = false, features = [ +ibc-proto = { version = "0.51", default-features = false, features = [ "parity-scale-codec", "borsh", "serde", ] } -tendermint = { version = "0.38", default-features = false } -tendermint-proto = { version = "0.38", default-features = false } -tendermint-light-client-verifier = { version = "0.38", default-features = false, features = [ "rust-crypto" ] } +tendermint = { version = "0.40", default-features = false } +tendermint-proto = { version = "0.40", default-features = false } +tendermint-light-client-verifier = { version = "0.40", default-features = false, features = [ "rust-crypto" ] } sp-core = { version = "34.0", default-features = false, optional = true } sp-io = { version = "37.0", default-features = false, optional = true } diff --git a/docs/architecture/adr-001-handler-implementation.md b/docs/architecture/adr-001-handler-implementation.md index 0b662ad750..e3c8e76f6f 100644 --- a/docs/architecture/adr-001-handler-implementation.md +++ b/docs/architecture/adr-001-handler-implementation.md @@ -24,7 +24,7 @@ and eventually picked up by the IBC relayer. An event has an arbitrary structure, depending on the handler that produces it. Here is the [list of all IBC-related events][events], as seen by the relayer. -Note that the consumer of these events in production would not be the relayer directly +Note that the consumer of these events in production would not be the relayer directly (instead the consumer is the node/SDK where the IBC module executes), but nevertheless handlers will reuse these event definitions. @@ -41,7 +41,7 @@ pub enum IBCEvent { OpenInitConnection(ConnectionEvents::OpenInit), OpenTryConnection(ConnectionEvents::OpenTry), // ... -} +} ``` ### Logging @@ -445,7 +445,7 @@ which are agnostic to the actual type of client being used. We could alternatively model all chain-specific datatypes as boxed trait objects (`Box`), but this approach runs into a lot of limitations of trait objects, such as the inability to easily -require such trait objects to be Clonable, or Serializable, or to define an equality relation on them. +require such trait objects to be Cloneable, or Serializable, or to define an equality relation on them. Some support for such functionality can be found in third-party libraries, but the overall experience for the developer is too subpar. diff --git a/docs/architecture/adr-004-light-client-crates-extraction.md b/docs/architecture/adr-004-light-client-crates-extraction.md index 094e5a17c5..4cfdc37695 100644 --- a/docs/architecture/adr-004-light-client-crates-extraction.md +++ b/docs/architecture/adr-004-light-client-crates-extraction.md @@ -30,7 +30,7 @@ in [ADR003 - Dealing with chain-specific datatypes](https://github.com/informals -> > We could alternatively model all chain-specific datatypes as boxed trait objects (`Box`), but this approach > runs into a lot of limitations of trait objects, such as the inability to easily require such trait objects to be -> Clonable, or Serializable, or to define an equality relation on them. Some support for such functionality can be found +> Cloneable, or Serializable, or to define an equality relation on them. Some support for such functionality can be found > in third-party libraries, but the overall experience for the developer is too subpar. > > We thus settle on a different strategy: lifting chain-specific data into an enum over all possible chain types. @@ -183,7 +183,7 @@ available). #### Light client traits cannot have constructors -This restriction comes from the fact that trait methods of object safe traits cannot return `Self`. +This restriction comes from the fact that trait methods of object safe traits cannot return `Self`. However, we would need a constructor to be able to create a `ClientState` and `ConsensusState` in the `create_client` handler. This can be done using a `where Self: Sized` clause on the trait method. @@ -348,7 +348,7 @@ define_error! { #[derive(Debug, PartialEq, Eq)] Error { /* ... */ - + Tendermint [ Ics07Error ] | _ | { "tendermint error" }, @@ -371,11 +371,11 @@ define_error! { #[derive(Debug, PartialEq, Eq)] Error { /* ... */ - + ClientSpecific { description: String } | e | { format_args!("client specific error: {0}", e.description) }, - + /* ... */ } } diff --git a/docs/architecture/adr-006-upgrade-client-implementation.md b/docs/architecture/adr-006-upgrade-client-implementation.md index d1aa8fc862..fbfd865353 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 b0caabdd51..5bf30de70c 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 24dbe11496..feb0c9695a 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/docs/architecture/adr-11-refactor-errors.md b/docs/architecture/adr-11-refactor-errors.md new file mode 100644 index 0000000000..e43561077d --- /dev/null +++ b/docs/architecture/adr-11-refactor-errors.md @@ -0,0 +1,387 @@ +# ADR 11: Refactoring `ibc-rs`'s Error Handling Architecture + +## Changelog + +- 2024-09-20: Draft Proposed + +## Status + +Proposed + +## Context + +As a library whose main use cases involve integrating with external host code +bases, ibc-rs's error handling architecture should strive to achieve the +following goals: + +1. Empower host developers to respond to ibc-rs-originating errors that affect + host logic. +2. Assist ibc-rs core developers' debugging efforts by providing detailed, + human-readable error reports and/or stack traces. + +These two aims highlight the need to clearly distinguish between *host*-level +errors and *protocol*-level errors. Developers working at the host level often +do not need to concern themselves with lower-level protocol errors; even if they +do, those errors should not be exposed to host developers at the fine-grained +level of granularity that is more appropriate for protocol-level developers. + +As it currently stands, ibc-rs exposes a top-level `ContextError` type that is +returned by all validation and execution context methods, which are those that +need to be implemented by hosts as part of the ibc-rs integration process. This +`ContextError` type encapsulates all of the protocol-level errors that ibc-rs +exposes. As a result, the onus is being placed on host developers to decide +whether these errors are relevant or not. In other words, the distinction +between host- and protocol-level errors is not reflected in ibc-rs's error +handling methodology. + +## Proposal + +### Some Changes to `ContextError` + +ibc-rs's top-level error type, `ContextError`, will be renamed to `HandlerError` +in an effort to improve the semantics of this error type. `HandlerError` will be +returned only by the top- level handler entrypoints of ibc-rs, namely the +[dispatch], [validate], and [execute] methods. + +```diff +- pub enum ContextError { ++ pub enum HandlerError { + /// ICS02 Client error: {0} + Client(ClientError), + /// ICS03 Connection error: {0} + Connection(ConnectionError), + /// ICS04 Channel error: {0} + Channel(ChannelError), +- /// ICS04 Packet error: {0} +- Packet(PacketError), + /// ICS26 Router error: {0} + Router(RouterError), +} +``` + +In addition, the error variant that contained the `PacketError` type will be +removed. This is because we're opting to move towards only having a single error +type for each ICS module, removing the discrepancy that the ICS04 module exposes +two distinct error types. The `PacketError` type will be merged into the +`ChannelError` type. + +### The New `HostError` Type + +In light of the stated rationale of making it clear when an error originates +from host logic vs ibc-rs's internal logic, we propose adding a new `HostError` +type that will live as a variant in each of the module-level error types, as +well as in the application-level error types, `TokenTransferError` and +`NftTransferError`. Initially, we had only a single `Host(HostError)` variant +that existed in the `HandlerError`type, but it became clear that this wasn't the +correct place in which to expose host-level errors. This is because host errors +can crop up within any of the core ibc-rs modules. + +We introduce the following concrete `HostError` type in the `ics24-host` crate: + +```rust +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 }, +} +``` + +This initial definition offers fairly generic error variants that nonetheless +aim to capture most of the error use-cases faced by hosts. One such notable +use-case is fetching/retrieving data from the host's storage. + +Host errors can occur within any of the core ibc-rs modules. Thus, we'll be +adding `HostError` variants to each of the module-level error types where +appropriate: `ClientError` in ICS02, `ConnectionError` in ICS03, and +`ChannelError` in ICS04. Note that as of now a `HostError` variant is not being +added to the `RouterError` type in ICS26 as it is not used by hosts, i.e., it +does not expose its own handlers. + +The main areas where `HostError`s are now being returned are mainly in +`ValidationContext` and `ExecutionContext` trait methods. These traits are the +ones that hosts need to implement as part of the process of integrating ibc-rs. + +As an example, consider the [consensus_state] method under the +`ClientValidationContext` trait. This method used to return a `ContextError`. +One place where it is called is in the `upgrade_client` handler: + +```rust +pub fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), ContextError> + ... + let old_consensus_state = client_val_ctx + .consensus_state(&old_cons_state_path) + .map_err(|_| ClientError::MissingConsensusState { + client_id, + height: old_client_state.latest_height(), + })?; +``` + +The `ContextError` of the `consensus_state` method was being mapped onto a +`ClientError`, which was then mapped *back* into a `ContextError`; certainly an +inefficient round-trip. In the case that an error did occur in the +`consensus_state` method, it would not have been clear to the user that the +error originated from a host context. + +This `validate` function will now be changed to return a `ClientError`. Coupled +with the `consensus_state` method now returning a `HostError`, this call can now +be made much more cleanly: + +```rust +pub fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), ClientError> + ... + let old_consensus_state = client_val_ctx + .consensus_state(&old_cons_state_path)?; +``` + +`HostError`s cleanly map to `ClientError`s (along with any other of the module +errors in ibc-core). In the same vein, `ClientError`s map cleanly to +`HandlerError`s: with these changes, the granularity of where and how errors are +defined in ibc-rs makes a lot more sense. Plus, the error will now carry the +relevant host context! + +### Defining a New `DecodingError` Type + +Another significant change that will be made as part of refactoring ibc-rs's +error handling is the introduction of a new `DecodingError` type. The purpose of +this type is to serve as the single error type returned by every raw `TryFrom` +conversion in ibc-rs. A major chunk of ibc-rs's logic deals with these sorts of +conversions. As it stands, depending on what type was being converted into, +different module-level errors were used as the error type. + +For example, a `TryFrom for AnyClientState` impl would return a +`ClientError`, while a `TryFrom> for ProofSpecs` impl would +return a `CommitmentError`. As a result, there were many conversion-related +error variants scattered across all the different error types, which led to a +lot of duplication and redundancy. In essence, these methods are all dealing +with decoding and/or deserialization in some form or another. + +The introduction of the `DecodingError` type seeks to consolidate these +duplications and redundancies: + +```rust +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), + /// protobuf decoding error: {0} + Protobuf(ProtoError), + /// prost decoding error: {0} + Prost(ProstError), + /// invalid hash bytes: {description} + InvalidHash { description: String }, + /// 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), +} +``` + +This type captures most of external decoding- and parsing-related errors that +crop up in ibc-rs, as well as variants for encoding internal decoding issues. + +Deploying this error type across all of ibc-rs's conversions will have a marked +effect on the number of outstanding variants held by the module-level errors, +allowing us to reduce a significant number of variants and reduce these module +error to something much more akin to its essence. + +### Positives + +The changes introduced in this ADR represent a clear net positive effect on the +usability and maintainability of ibc-rs as a whole. The overall hierarchy and +semantics of the error system make a lot more sense than its prior incarnation. +Module-level errors are much more simplified and streamlined. Additionally, host +contexts can be attached to ibc-core errors, making it much clearer where an +error originated from. + +### Negatives + +With all that said, these changes of course come with some downsides. A major +one would be the generality of the error types that were introduced: it's not +clear whether we captured the right level of granularity with them. Especially +with the `HostError` type, it's not clear whether the way this type is laid out +is sufficient for hosts, or whether they would prefer something more bespoke and +tailored to their particular needs. + +Most of the new error variants introduced also require String allocations, which +is ideal; this is a tradeoff between generality of error variants and +specificity. Introducing more specific error variants would help cut down on the +number of String allocations, but would contribute to bloating and redundancy +within ibc-rs's error types. + +Lastly, the new error types and variants do not come with guard rails to help +steer ibc-rs's contributors towards following the conventions laid out in their +usage. It is very easy to abuse String-allocating variants for errors that they +may not actually be appropriate for. The main guard against this comes in the +form of PR review, which is not ideal. + +## Notes on Error Handling Conventions + +### Error Classifications + +This section details the conventions surrounding how error types, their +variants, and the error messages contained within variants, are formatted and +structured. It serves as a guide for how to parse and read ibc-rs's error +messages. + +The naming convention of variants follows a few distinct classifications, +illustrated by the following example code snippet: + +```rust +/// The possible classes of errors that error variants can encapsulate +enum ErrorClassifications { + /// nested error: {0} + NestedError(SomeError), + /// something is missing + Missing, + /// already exists + Duplicate, + /// something is invalid + Invalid, + /// mismatched thing: expected `{expected}`, actual `{actual}` + Mismatched { expected: String, actual: String }, + /// failed to do something + FailedTo, + /// unknown resource + Unknown, + /// insufficient value + Insufficient, +} +``` + +A few exceptions to these classifications exist, but these classifications +capture almost all of the error variants that exist within ibc-rs. New error +variants defined going forward should fall into one of the above +classifications. + +### Structuring Variants and Error Messages + +We'll start with conventions that apply to all variants, regardless of their +classification. Error variants themselves should start with a capital letter, +while error messages should all start with a lower-case letter. +String-interpolated values within error messages should all be surrounded by +backticks, in order to signify that it is an interpolated value. The exception +to this is nested errors that are appended to the end of an error message: these +interpolated values are __not__ surrounded by backticks. Instead, they should +all follow a colon in the error message itself; thus, colons indicate that the +following is a nested error message. Lastly, error messages should all start +with the classification of its respective error variant, i.e., an error message +for an `Invalid` error class should start with "invalid...", while an error +message for a `Missing` error class should start with "missing...", etc. + +When it comes to the `NestedError` and `Mismatched` classifications, the +structure and formatting do not deviate. `NestedError`s are always newtype +wrappers around a contained error. The sole purpose of these variants is to +provide a means of converting the contained error to the containing error type. +Thus, every `NestedError` variant should be accompanied by a `From +for ContainingError` impl. The naming scheme for `NestedError` variants should +include the name of the contained error, minus the word "Error" itself. The +error message then should clearly delineate the type of the contained error, the +fact that the message is referring to a lower-level error, and the contents of +the lower-level error. An example looks like this: + +```rust + /// timestamp error: {0} + Timestamp(TimestampError), +``` + +Note the colon followed by the interpolation of the nested error: it should +__not__ be surrounded by backticks. + +The `Mismatched` classification is used for situations where an expected +instance of a type is known, along with the instance that was actually found. +These are both included in the error under the `expected` and `actual` fields. +The error message for this class should always include "expected ``{expected}``, +actual ``{actual}``"; this statement should be precluded by "mismatched +[TYPE]:". The type should be spelled in the singular and followed by a colon. +The `expected` and `actual` interpolated values in the error message should be +surrounded by ``{}`` backticks and then braces. + +The rest of the error classes are a bit more freeform in how they are +structured. They could take the form of unit structs that do not contain any +values, serving mainly to surface a specific error message and nothing more. + +```rust + /// missing attribute key + MissingAttributeKey, + /// missing attribute value + MissingAttributeValue, +``` + +These two variants each highlight the fact that a very particular resource is +missing. + +Single-element unit structs are used primarily to surface an invalid or +duplicate type, opting to not provide any additional context other than what the +error message itself provides. + +```rust + /// invalid status `{0}` + InvalidStatus(Status), + /// duplicate client state `{0}` + DuplicateClientState(ClientId), +``` + +The most common form of variant is a one-element struct with a `description` +field. This serves to allow injecting more context at the call site of the error +in the form of a String. This is the most general variant: care should be taken +to not allow these sorts of variants to be abused for unintended purposes. Note +that, like variants that contain nested errors, descriptions should be +interpolated in the error message without enclosing backticks. + +```rust + /// failed to verify header: {description} + FailedToVerifyHeader { description: String }, + /// invalid hash: {description} + InvalidHash { description: String }, +``` + +## Future Work + +In light of the stated downsides, a natural follow-up of this work would be to +generalize ibc-rs's error handling architecture to allow hosts to introduce +their own bespoke error types. This would allow host developers to define more +precise error types and variants that can better signal what the root cause of +an error might be. This would improve upon the rather generic error variants +that are exposed through ibc-rs's own `HostError` definition. + +The downside is that this would add additional work on top of the already +considerable amount of work that it takes to integrate ibc-rs into host chains. + +## References + +- [#1319][issue-1319]: Consolidating duplicated error variants into the + `DecodingError` type +- [#1320][issue-1320]: Defining the `HostError` type +- [#1339][issue-1339]: Merging `PacketError` into `ChannelError` +- [#1340][issue-1340]: Adding `HostError` variants to each module-level error +- [#1346][issue-1346]: Cleaning up generic `String` error variants + +[dispatch]: + https://github.com/cosmos/ibc-rs/blob/4aecaece9bda3c0f4a3b6a8379d73bd7eddc2cc4/ibc-core/ics25-handler/src/entrypoint.rs#L35 +[validate]: + https://github.com/cosmos/ibc-rs/blob/4aecaece9bda3c0f4a3b6a8379d73bd7eddc2cc4/ibc-core/ics25-handler/src/entrypoint.rs#L54 +[execute]: + https://github.com/cosmos/ibc-rs/blob/4aecaece9bda3c0f4a3b6a8379d73bd7eddc2cc4/ibc-core/ics25-handler/src/entrypoint.rs#L130 +[issue-1319]: https://github.com/cosmos/ibc-rs/issues/1319 +[issue-1320]: https://github.com/cosmos/ibc-rs/issues/1320 +[issue-1339]: https://github.com/cosmos/ibc-rs/issues/1339 +[issue-1340]: https://github.com/cosmos/ibc-rs/issues/1340 +[issue-1346]: https://github.com/cosmos/ibc-rs/issues/1346 diff --git a/ibc-apps/ics20-transfer/src/context.rs b/ibc-apps/ics20-transfer/src/context.rs index 8c22ad386b..2917c9fe95 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 39dcd373df..f1cd659677 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 193b331969..76e22b3b61 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 439e5604c6..39d40ea46e 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 392ca5939c..41d3016a4f 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/Cargo.toml b/ibc-apps/ics20-transfer/types/Cargo.toml index 5d8da8acab..30e477f28f 100644 --- a/ibc-apps/ics20-transfer/types/Cargo.toml +++ b/ibc-apps/ics20-transfer/types/Cargo.toml @@ -23,10 +23,10 @@ all-features = true borsh = { workspace = true, optional = true } derive_more = { workspace = true } displaydoc = { workspace = true } -primitive-types = { version = "0.12.2", default-features = false, features = [ "serde_no_std" ] } +primitive-types = { version = "0.13.1", default-features = false, features = [ "serde_no_std" ] } schemars = { workspace = true, optional = true } serde = { workspace = true, optional = true } -uint = { version = "0.9", default-features = false } +uint = { version = "0.10", default-features = false } # ibc dependencies ibc-core = { workspace = true } diff --git a/ibc-apps/ics20-transfer/types/src/amount.rs b/ibc-apps/ics20-transfer/types/src/amount.rs index 6952be5148..8f1b2c81f3 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 65a1472183..db95bcc9fb 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 62ef91b9e3..44cd4e210e 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::{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 a394eb77d1..3c20585982 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 cc311a2caf..4f822b5a65 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,30 +46,20 @@ 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))?; - } - Ok(MsgTransfer { port_id_on_a: raw_msg.source_port.parse()?, chan_id_on_a: raw_msg.source_channel.parse()?, 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 +88,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 a3615a0905..8d4d5192cd 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 ede6ee8981..201d342cbf 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 2adbb47f6d..6615339580 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 8782bf7a2f..59cae4ddbb 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() @@ -78,7 +79,7 @@ where }; let mut extras = ModuleExtras { - events: vec![], + events: Vec::with_capacity(data.token_ids.0.len()), log: Vec::new(), }; for (i, token_id) in data.token_ids.0.iter().enumerate() { @@ -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 4cf1a759cd..c156f75f27 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; @@ -172,9 +172,11 @@ where // overwrite even if they are set in MsgTransfer if let Some(uris) = &mut packet_data.token_uris { uris.clear(); + uris.reserve_exact(token_ids.0.len()); } if let Some(data) = &mut packet_data.token_data { data.clear(); + data.reserve_exact(token_ids.0.len()); } for token_id in token_ids.as_ref() { if is_sender_chain_source(msg.port_id_on_a.clone(), msg.chan_id_on_a.clone(), class_id) { @@ -197,14 +199,14 @@ where let nft = transfer_ctx.get_nft(class_id, token_id)?; // Set the URI and the data if both exists if let (Some(uri), Some(data)) = (nft.get_uri(), nft.get_data()) { - match &mut packet_data.token_uris { - Some(uris) => uris.push(uri.clone()), - None => packet_data.token_uris = Some(vec![uri.clone()]), - } - match &mut packet_data.token_data { - Some(token_data) => token_data.push(data.clone()), - None => packet_data.token_data = Some(vec![data.clone()]), - } + packet_data + .token_uris + .get_or_insert_with(|| Vec::with_capacity(token_ids.0.len())) + .push(uri.clone()); + packet_data + .token_data + .get_or_insert_with(|| Vec::with_capacity(token_ids.0.len())) + .push(data.clone()); } } diff --git a/ibc-apps/ics721-nft-transfer/src/module.rs b/ibc-apps/ics721-nft-transfer/src/module.rs index 074e7c8692..53b45e7c3b 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 831883331c..d554be89b5 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 a9aecc5099..5151203f35 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 94c230adde..9335f4c2f0 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 b35a5c1cc2..0ced7dbacf 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,21 +46,12 @@ 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))?; - } - let memo = if raw_msg.memo.is_empty() { None } else { @@ -118,18 +107,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 c2f9d2d2f9..afa773bfc9 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 1571eac487..b865ed1265 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 0adb68f22e..6319e309cc 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 ef5b044696..bc7725e5b8 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(()) @@ -187,7 +180,7 @@ pub fn consensus_state_status( // consensus state is in the future, then we don't consider the client // to be expired. if let Some(elapsed_since_latest_consensus_state) = - host_timestamp.duration_since(&consensus_state.timestamp()) + host_timestamp.duration_since(&consensus_state.timestamp()?) { // Note: The equality is considered as expired to stay consistent with // the check in tendermint-rs, where a header at `trusted_header_time + @@ -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 084d0441c2..636e0cc3fc 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::{IntoHostTime, TimestampError}; use super::ClientState; @@ -336,16 +337,12 @@ where let tm_consensus_state: ConsensusStateType = consensus_state.try_into().map_err(Into::into)?; - let host_timestamp = ctx.host_timestamp()?.into_tm_time(); + let host_timestamp = ctx.host_timestamp()?.into_host_time()?; 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 ea40fa7177..eefa2eaa14 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs @@ -1,13 +1,14 @@ -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::*; -use ibc_primitives::Timestamp; +use ibc_primitives::{IntoHostTime, IntoTimestamp, Timestamp}; use tendermint::crypto::Sha256; use tendermint::merkle::MerkleHash; use tendermint::{Hash, Time}; @@ -97,18 +98,15 @@ where // ensure trusted consensus state is within trusting period { - let trusted_timestamp = trusted_time.try_into().expect("time conversion failed"); + let trusted_timestamp = trusted_time.into_timestamp()?; 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,17 +117,18 @@ 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)?; - let current_timestamp = current_timestamp.into_tm_time(); + let current_timestamp = current_timestamp.into_host_time()?; verifier .verify_misbehaviour_header(untrusted_state, trusted_state, options, current_timestamp) 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 03da355ff0..695fe72d0d 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/update_client.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/update_client.rs @@ -1,11 +1,13 @@ -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::*; +use ibc_primitives::IntoHostTime; use tendermint::crypto::Sha256; use tendermint::merkle::MerkleHash; use tendermint_light_client_verifier::options::Options; @@ -52,21 +54,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, @@ -83,7 +84,7 @@ where next_validators: None, }; - let now = ctx.host_timestamp()?.into_tm_time(); + let now = ctx.host_timestamp()?.into_host_time()?; // main header verification, delegated to the tendermint-light-client crate. verifier diff --git a/ibc-clients/ics07-tendermint/src/client_state/validation.rs b/ibc-clients/ics07-tendermint/src/client_state/validation.rs index b1a028749f..ccb08b5af6 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 3f64ce0857..b85546f160 100644 --- a/ibc-clients/ics07-tendermint/src/consensus_state.rs +++ b/ibc-clients/ics07-tendermint/src/consensus_state.rs @@ -6,15 +6,15 @@ //! 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; +use ibc_primitives::{IntoTimestamp, Timestamp}; use tendermint::{Hash, Time}; /// Newtype wrapper around the `ConsensusState` type imported from the @@ -52,7 +52,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 +68,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)?)) @@ -92,10 +92,7 @@ impl ConsensusStateTrait for ConsensusState { &self.0.root } - fn timestamp(&self) -> Timestamp { - self.0 - .timestamp - .try_into() - .expect("UNIX Timestamp can't be negative") + fn timestamp(&self) -> Result { + self.0.timestamp.into_timestamp().map_err(Into::into) } } diff --git a/ibc-clients/ics07-tendermint/types/src/client_state.rs b/ibc-clients/ics07-tendermint/types/src/client_state.rs index ee30dbdb0e..a6058a97ef 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, @@ -309,9 +320,9 @@ impl From for RawTmClientState { Self { chain_id: value.chain_id.to_string(), trust_level: Some(value.trust_level.into()), - trusting_period: Some(value.trusting_period.into()), - unbonding_period: Some(value.unbonding_period.into()), - max_clock_drift: Some(value.max_clock_drift.into()), + trusting_period: value.trusting_period.try_into().ok(), + unbonding_period: value.unbonding_period.try_into().ok(), + max_clock_drift: value.max_clock_drift.try_into().ok(), // NOTE: The protobuf encoded `frozen_height` of an active client // must be set to `0` so that `ibc-go` driven chains can properly // decode the `ClientState` value. In `RawClientState`, a @@ -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 bc4e675c8b..dd64ce0fbe 100644 --- a/ibc-clients/ics07-tendermint/types/src/consensus_state.rs +++ b/ibc-clients/ics07-tendermint/types/src/consensus_state.rs @@ -1,8 +1,9 @@ //! 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_primitives::{IntoHostTime, Timestamp}; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::lightclients::tendermint::v1::ConsensusState as RawConsensusState; use ibc_proto::Protobuf; @@ -11,7 +12,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 = @@ -35,6 +35,7 @@ impl ConsensusState { } } + /// Returns the timestamp of the consensus state as a `tendermint::Time`. pub fn timestamp(&self) -> Time { self.timestamp } @@ -47,32 +48,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(), - })?; - // 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 + let timestamp: Timestamp = raw + .timestamp + .ok_or(DecodingError::missing_raw_data("consensus state timestamp"))? .try_into() - .map_err(|e| Error::InvalidRawClientState { - reason: format!("invalid timestamp: {e}"), - })?; + .map_err(DecodingError::invalid_raw_data)?; + + let timestamp = timestamp + .into_host_time() + .map_err(DecodingError::invalid_raw_data)?; 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 +101,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 264e398a0a..588e39f180 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 6ee8069f09..80a1966a16 100644 --- a/ibc-clients/ics07-tendermint/types/src/header.rs +++ b/ibc-clients/ics07-tendermint/types/src/header.rs @@ -5,9 +5,10 @@ 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; +use ibc_primitives::{IntoTimestamp, Timestamp}; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::lightclients::tendermint::v1::Header as RawHeader; use ibc_proto::Protobuf; @@ -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) + .into_timestamp() + .map_err(Into::into) } 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 c51f1892fa..e4a87c5aee 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 830bba74d0..bad0013020 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 e9570b3209..64a7786333 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 597cc4e870..e77f469c78 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 eb48a2855b..0000000000 --- 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 169eb3b302..3a88d0431b 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 40c737457b..3f7bc6a93c 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/consensus_state.rs b/ibc-core/ics02-client/context/src/consensus_state.rs index 556486c676..2149c76ff9 100644 --- a/ibc-core/ics02-client/context/src/consensus_state.rs +++ b/ibc-core/ics02-client/context/src/consensus_state.rs @@ -1,5 +1,6 @@ //! Defines the trait to be implemented by all concrete consensus state types +use ibc_core_client_types::error::ClientError; use ibc_core_commitment_types::commitment::CommitmentRoot; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; @@ -16,5 +17,5 @@ pub trait ConsensusState: Send + Sync + Convertible { fn root(&self) -> &CommitmentRoot; /// The timestamp of the consensus state - fn timestamp(&self) -> Timestamp; + fn timestamp(&self) -> Result; } diff --git a/ibc-core/ics02-client/context/src/context.rs b/ibc-core/ics02-client/context/src/context.rs index 972ec547ed..e9a47bf0d2 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 d6413596fd..511f45f2c2 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 1676c6e809..a239ed8fc2 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 81e140b8a9..51a30c3b1b 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 a072988a23..ae119faeb8 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 cda8b0f05e..49ced05dd0 100644 --- a/ibc-core/ics02-client/types/src/error.rs +++ b/ibc-core/ics02-client/types/src/error.rs @@ -4,116 +4,89 @@ 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(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 +100,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 +113,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 +140,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 a91e1b4139..744776b88a 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 96eda4c923..612351863f 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 fe6944b3d6..1891a143e9 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 0b4031fe5d..1e495393bb 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 5bc6c05943..5ef857cbaf 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 df79446ef7..16a5d250d8 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 c3d9a956bd..d581f8e1bc 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 d77a7bbe63..39e4f36f2b 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 06fb030989..b0f427b22b 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 443d32877e..a91a16c761 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 f9ef4eb61b..ba934493d0 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 8d42866169..267603a7fb 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 4b59913e7f..2b47e488d7 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 241136411c..791d1c7787 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 dbfa87b016..f518f86a50 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 e71d5426c9..fa4ad6297f 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 dc3056b2a8..9379c25e36 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 e18821d579..24a58b87e1 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 7d9affcfda..57790a3870 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 6686b806a2..ea5f22f039 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 a4446d113b..89f577cc0a 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 1fba1de8fd..92844b4d46 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 c58edc74a1..fddea0d821 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 5385d18c3d..798f628e12 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 15c38c9f5f..6a0a6e1892 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 7e5b6b2405..c6d81a9fe8 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 dfbc777c1e..2f1fc56d37 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 58a161d0bd..fe931efa0c 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 08c82deac7..c8e7357004 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 aa22d3ec9c..60bf301990 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, { @@ -50,7 +49,7 @@ where let packet = &msg.packet; let receipt_path_on_b = ReceiptPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a); - ctx_b.get_packet_receipt(&receipt_path_on_b).is_ok() + ctx_b.get_packet_receipt(&receipt_path_on_b)?.is_ok() } Order::Ordered => { let seq_recv_path_on_b = @@ -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 { @@ -248,44 +239,34 @@ where } } Order::Unordered => { - let receipt_path_on_b = ReceiptPath::new( - &msg.packet.port_id_on_a, - &msg.packet.chan_id_on_a, - msg.packet.seq_on_a, - ); - let packet_rec = ctx_b.get_packet_receipt(&receipt_path_on_b); - match packet_rec { - Ok(_receipt) => {} - Err(ContextError::PacketError(PacketError::PacketReceiptNotFound { sequence })) - if sequence == msg.packet.seq_on_a => {} - Err(e) => return Err(e), - } + // Note: We don't check for the packet receipt here because another + // relayer may have already relayed the packet. If that's the case, + // we want to avoid failing the transaction and consuming + // unnecessary fees. + // Case where the recvPacket is successful and an // acknowledgement will be written (not a no-op) 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 514ffb89d5..b393c74add 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( @@ -78,21 +76,20 @@ pub fn send_packet_validate( ); let consensus_state_of_b_on_a = client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; - let latest_timestamp = consensus_state_of_b_on_a.timestamp(); + 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 ef478ed4ed..da4e42d373 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 @@ -195,16 +193,15 @@ where ); let consensus_state_of_b_on_a = client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; - let timestamp_of_b = consensus_state_of_b_on_a.timestamp(); + 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 8366a5d26b..00d880577b 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 7364857d18..efbbe28ce3 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 846696561d..38140cb354 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 7042411d3f..911aed095e 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 798210a824..4b65a94c62 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 a15a312200..c7a2c41799 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 a93f7a6222..10839e91d1 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 c533fc59c1..f3cbf1171e 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 2663b4186c..3d852e5fa1 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 432ca92fe2..4d1151ff61 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 6070eb1a27..d28fe2a6e4 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 7ab3f15985..2299802d49 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 c6cbd918b5..042f2c07a6 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 5295e18073..c2b14ab418 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 362630ff0f..5fc2558966 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 7673d5f337..34394b8561 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 d07746b773..e5f2e87796 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. @@ -20,6 +20,10 @@ pub enum PacketMsgType { } /// Packet receipt, used over unordered channels. +/// +/// If the receipt is present in the host's state, it's marked as `Ok`, +/// indicating the packet has already been processed. If the receipt is absent, +/// it's marked as `None`, meaning the packet has not been received. #[cfg_attr( feature = "parity-scale-codec", derive( @@ -36,6 +40,17 @@ pub enum PacketMsgType { #[derive(Clone, Debug)] pub enum Receipt { Ok, + None, +} + +impl Receipt { + pub fn is_ok(&self) -> bool { + matches!(self, Receipt::Ok) + } + + pub fn is_none(&self) -> bool { + matches!(self, Receipt::None) + } } impl core::fmt::Display for PacketMsgType { @@ -164,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 @@ -189,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 { @@ -274,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 43dfe3bf3c..46d6ab49a9 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 86ef4328aa..8cd45b712b 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 2cfc1e46c4..1cf5c894af 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 2555f89ac6..af1f30153a 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 fdbdd24c74..3554767ebe 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 d597ef1c85..d2bcea088a 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 46bf79a953..5d0f0f473f 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 1328355e88..89ec14ff3a 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 3f27bad12e..419a1e46ce 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 d1a25adcc2..6541ad049a 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 a12f4fab4f..a98441ce7d 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 7064fdcd83..96b13306cd 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 da858968d6..a9a84ab53a 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,47 +81,52 @@ 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 - fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result; + /// Returns the packet receipt for the given store path. This receipt is + /// used to acknowledge the successful processing of a received packet, and + /// must not be pruned. + /// + /// If the receipt is present in the host's state, return `Receipt::Ok`, + /// 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; /// 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; @@ -137,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. @@ -151,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 7ad95dcc05..e4953c7205 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 4df4c4dd90..3eb5b75e04 100644 --- a/ibc-core/ics24-host/types/src/error.rs +++ b/ibc-core/ics24-host/types/src/error.rs @@ -1,22 +1,171 @@ +//! 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: ProstError) -> Self { + Self::Prost(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 d37ad4e1f7..c96ccc36f3 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 9e8b2ccbc9..0ab91f24cb 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 f466de4c26..1fae79ac6a 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 75e3494ef1..614cfd8a42 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 3c271a9bb7..f9a0cabc32 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 5bea4b54d0..fce14adc9c 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 aa62ea813e..77ebe79e1a 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 1272c0aff9..a47a9b7021 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 26b2967134..a0877b98d7 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 01fb3c6c64..19ad7c2adf 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-derive/Cargo.toml b/ibc-derive/Cargo.toml index 437b95b19d..51fde3d3a7 100644 --- a/ibc-derive/Cargo.toml +++ b/ibc-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ibc-derive" -version = "0.8.0" +version = "0.9.0" license = { workspace = true } repository = { workspace = true } edition = { workspace = true } diff --git a/ibc-derive/src/client_state.rs b/ibc-derive/src/client_state.rs index 4e30551990..ff7c3382dd 100644 --- a/ibc-derive/src/client_state.rs +++ b/ibc-derive/src/client_state.rs @@ -118,6 +118,7 @@ fn split_for_impl( let mut predicates = vec![]; if let syn::PathArguments::AngleBracketed(gen) = args { + generics.reserve_exact(gen.args.len()); for arg in gen.args { match arg.clone() { GenericArgument::Type(_) | GenericArgument::Lifetime(_) => { diff --git a/ibc-derive/src/consensus_state.rs b/ibc-derive/src/consensus_state.rs index 87fafcffec..b4e0067c62 100644 --- a/ibc-derive/src/consensus_state.rs +++ b/ibc-derive/src/consensus_state.rs @@ -24,6 +24,7 @@ pub fn consensus_state_derive_impl(ast: DeriveInput, imports: &Imports) -> Token let CommitmentRoot = imports.commitment_root(); let ConsensusState = imports.consensus_state(); let Timestamp = imports.timestamp(); + let ClientError = imports.client_error(); quote! { impl #ConsensusState for #enum_name { @@ -33,7 +34,7 @@ pub fn consensus_state_derive_impl(ast: DeriveInput, imports: &Imports) -> Token } } - fn timestamp(&self) -> #Timestamp { + fn timestamp(&self) -> core::result::Result<#Timestamp, #ClientError> { match self { #(#timestamp_impl),* } diff --git a/ibc-primitives/Cargo.toml b/ibc-primitives/Cargo.toml index 2c56cd6dee..7f8432e7d5 100644 --- a/ibc-primitives/Cargo.toml +++ b/ibc-primitives/Cargo.toml @@ -31,9 +31,6 @@ time = { version = ">=0.3.0, <0.3.37", default-features = false } # ibc dependencies ibc-proto = { workspace = true } -# cosmos dependencies -tendermint = { workspace = true } - # parity dependencies parity-scale-codec = { workspace = true, optional = true } scale-info = { workspace = true, optional = true } @@ -49,7 +46,6 @@ std = [ "prost/std", "serde/std", "ibc-proto/std", - "tendermint/std", "time/std", ] serde = [ diff --git a/ibc-primitives/src/lib.rs b/ibc-primitives/src/lib.rs index 5aa99f4e58..277c794cd8 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 7551db5e76..33fca85d68 100644 --- a/ibc-primitives/src/types/timestamp.rs +++ b/ibc-primitives/src/types/timestamp.rs @@ -12,8 +12,6 @@ use ibc_proto::google::protobuf::Timestamp as RawTimestamp; 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}; @@ -38,8 +36,8 @@ pub struct Timestamp { impl Timestamp { pub fn from_nanoseconds(nanoseconds: u64) -> Self { - // As the `u64` representation can only represent times up to about year - // 2554, there is no risk of overflowing `Time` or `OffsetDateTime`. + // As the `u64` can only represent times up to about year 2554, there is + // no risk of overflowing `Time` or `OffsetDateTime`. let odt = OffsetDateTime::from_unix_timestamp_nanos(nanoseconds.into()) .expect("nanoseconds as u64 is in the range"); Self::from_utc(odt).expect("nanoseconds as u64 is in the range") @@ -47,7 +45,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 +64,7 @@ impl Timestamp { 1970..=9999 => Ok(Self { time: PrimitiveDateTime::new(t.date(), t.time()), }), - _ => Err(TimestampError::DateOutOfRange), + _ => Err(TimestampError::InvalidDate), } } @@ -105,11 +103,6 @@ impl Timestamp { s.try_into() .expect("Fails UNIX timestamp is negative, but we don't allow that to be constructed") } - - pub fn into_tm_time(self) -> Time { - Time::try_from(self.time.assume_offset(offset!(UTC))) - .expect("Timestamp is in the range of 0..=9999 years") - } } impl Protobuf for Timestamp {} @@ -168,11 +161,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,21 +174,44 @@ 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()) } } -impl TryFrom