From c8b4c88efc7fdce3c5bd40884a258638582074ac Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 21 Oct 2024 11:45:28 +0200 Subject: [PATCH 1/5] copy ibc-core to ibc-eureka-core --- ibc-eureka-core/Cargo.toml | 84 + ibc-eureka-core/README.md | 128 ++ ibc-eureka-core/ics02-client/Cargo.toml | 71 + .../ics02-client/context/Cargo.toml | 77 + .../ics02-client/context/src/client_state.rs | 260 +++ .../context/src/consensus_state.rs | 21 + .../ics02-client/context/src/context.rs | 159 ++ .../ics02-client/context/src/lib.rs | 37 + .../ics02-client/src/handler/create_client.rs | 90 + .../ics02-client/src/handler/mod.rs | 6 + .../src/handler/recover_client.rs | 89 + .../ics02-client/src/handler/update_client.rs | 97 ++ .../src/handler/upgrade_client.rs | 78 + ibc-eureka-core/ics02-client/src/lib.rs | 31 + ibc-eureka-core/ics02-client/types/Cargo.toml | 88 + .../ics02-client/types/src/error.rs | 166 ++ .../ics02-client/types/src/events.rs | 857 ++++++++++ .../ics02-client/types/src/height.rs | 214 +++ ibc-eureka-core/ics02-client/types/src/lib.rs | 29 + .../types/src/msgs/create_client.rs | 65 + .../types/src/msgs/misbehaviour.rs | 62 + .../ics02-client/types/src/msgs/mod.rs | 64 + .../types/src/msgs/recover_client.rs | 59 + .../types/src/msgs/update_client.rs | 53 + .../types/src/msgs/upgrade_client.rs | 81 + .../ics02-client/types/src/status.rs | 87 + ibc-eureka-core/ics03-connection/Cargo.toml | 73 + ibc-eureka-core/ics03-connection/src/delay.rs | 48 + .../src/handler/conn_open_ack.rs | 204 +++ .../src/handler/conn_open_confirm.rs | 164 ++ .../src/handler/conn_open_init.rs | 85 + .../src/handler/conn_open_try.rs | 206 +++ .../ics03-connection/src/handler/mod.rs | 81 + ibc-eureka-core/ics03-connection/src/lib.rs | 26 + .../ics03-connection/types/Cargo.toml | 91 + .../ics03-connection/types/src/connection.rs | 523 ++++++ .../ics03-connection/types/src/error.rs | 90 + .../ics03-connection/types/src/events.rs | 413 +++++ .../ics03-connection/types/src/lib.rs | 29 + .../types/src/msgs/conn_open_ack.rs | 103 ++ .../types/src/msgs/conn_open_confirm.rs | 59 + .../types/src/msgs/conn_open_init.rs | 126 ++ .../types/src/msgs/conn_open_try.rs | 226 +++ .../ics03-connection/types/src/msgs/mod.rs | 39 + .../ics03-connection/types/src/version.rs | 403 +++++ ibc-eureka-core/ics04-channel/Cargo.toml | 83 + ibc-eureka-core/ics04-channel/src/context.rs | 101 ++ .../src/handler/acknowledgement.rs | 201 +++ .../src/handler/chan_close_confirm.rs | 160 ++ .../src/handler/chan_close_init.rs | 117 ++ .../src/handler/chan_open_ack.rs | 157 ++ .../src/handler/chan_open_confirm.rs | 160 ++ .../src/handler/chan_open_init.rs | 135 ++ .../src/handler/chan_open_try.rs | 182 ++ .../ics04-channel/src/handler/mod.rs | 24 + .../ics04-channel/src/handler/recv_packet.rs | 273 +++ .../ics04-channel/src/handler/send_packet.rs | 138 ++ .../ics04-channel/src/handler/timeout.rs | 254 +++ .../src/handler/timeout_on_close.rs | 163 ++ ibc-eureka-core/ics04-channel/src/lib.rs | 26 + .../ics04-channel/types/Cargo.toml | 103 ++ .../types/src/acknowledgement.rs | 142 ++ .../ics04-channel/types/src/channel.rs | 578 +++++++ .../ics04-channel/types/src/commitment.rs | 146 ++ .../ics04-channel/types/src/error.rs | 96 ++ .../types/src/events/channel_attributes.rs | 172 ++ .../ics04-channel/types/src/events/mod.rs | 1267 ++++++++++++++ .../types/src/events/packet_attributes.rs | 334 ++++ .../ics04-channel/types/src/lib.rs | 35 + .../types/src/msgs/acknowledgement.rs | 69 + .../types/src/msgs/chan_close_confirm.rs | 69 + .../types/src/msgs/chan_close_init.rs | 48 + .../types/src/msgs/chan_open_ack.rs | 66 + .../types/src/msgs/chan_open_confirm.rs | 62 + .../types/src/msgs/chan_open_init.rs | 95 ++ .../types/src/msgs/chan_open_try.rs | 121 ++ .../ics04-channel/types/src/msgs/mod.rs | 79 + .../types/src/msgs/recv_packet.rs | 63 + .../ics04-channel/types/src/msgs/timeout.rs | 69 + .../types/src/msgs/timeout_on_close.rs | 77 + .../ics04-channel/types/src/packet.rs | 327 ++++ .../ics04-channel/types/src/timeout/height.rs | 205 +++ .../ics04-channel/types/src/timeout/mod.rs | 5 + .../types/src/timeout/timestamp.rs | 211 +++ .../ics04-channel/types/src/version.rs | 81 + .../ics23-commitment/types/Cargo.toml | 81 + .../ics23-commitment/types/src/commitment.rs | 182 ++ .../ics23-commitment/types/src/error.rs | 47 + .../ics23-commitment/types/src/lib.rs | 31 + .../ics23-commitment/types/src/merkle.rs | 228 +++ .../ics23-commitment/types/src/serializer.rs | 14 + .../ics23-commitment/types/src/specs.rs | 289 ++++ ibc-eureka-core/ics24-host/Cargo.toml | 94 + ibc-eureka-core/ics24-host/cosmos/Cargo.toml | 112 ++ ibc-eureka-core/ics24-host/cosmos/src/lib.rs | 30 + .../cosmos/src/upgrade_proposal/context.rs | 63 + .../cosmos/src/upgrade_proposal/events.rs | 174 ++ .../cosmos/src/upgrade_proposal/handler.rs | 47 + .../cosmos/src/upgrade_proposal/mod.rs | 13 + .../cosmos/src/upgrade_proposal/plan.rs | 97 ++ .../cosmos/src/upgrade_proposal/proposal.rs | 72 + .../ics24-host/cosmos/src/utils.rs | 57 + .../cosmos/src/validate_self_client.rs | 124 ++ ibc-eureka-core/ics24-host/src/context.rs | 258 +++ ibc-eureka-core/ics24-host/src/lib.rs | 29 + ibc-eureka-core/ics24-host/src/utils.rs | 47 + ibc-eureka-core/ics24-host/types/Cargo.toml | 66 + ibc-eureka-core/ics24-host/types/src/error.rs | 171 ++ .../types/src/identifiers/chain_id.rs | 460 +++++ .../types/src/identifiers/channel_id.rs | 99 ++ .../types/src/identifiers/client_id.rs | 126 ++ .../types/src/identifiers/client_type.rs | 101 ++ .../types/src/identifiers/connection_id.rs | 93 + .../ics24-host/types/src/identifiers/mod.rs | 17 + .../types/src/identifiers/port_id.rs | 73 + .../types/src/identifiers/sequence.rs | 73 + ibc-eureka-core/ics24-host/types/src/lib.rs | 23 + ibc-eureka-core/ics24-host/types/src/path.rs | 1518 +++++++++++++++++ .../ics24-host/types/src/validate.rs | 319 ++++ ibc-eureka-core/ics25-handler/Cargo.toml | 83 + .../ics25-handler/src/entrypoint.rs | 195 +++ ibc-eureka-core/ics25-handler/src/lib.rs | 33 + .../ics25-handler/types/Cargo.toml | 109 ++ .../ics25-handler/types/src/error.rs | 34 + .../ics25-handler/types/src/events.rs | 170 ++ .../ics25-handler/types/src/lib.rs | 21 + .../ics25-handler/types/src/msgs.rs | 130 ++ ibc-eureka-core/ics26-routing/Cargo.toml | 67 + ibc-eureka-core/ics26-routing/src/lib.rs | 24 + ibc-eureka-core/ics26-routing/src/module.rs | 161 ++ ibc-eureka-core/ics26-routing/src/router.rs | 18 + .../ics26-routing/types/Cargo.toml | 80 + .../ics26-routing/types/src/error.rs | 15 + .../ics26-routing/types/src/event.rs | 67 + .../ics26-routing/types/src/lib.rs | 20 + .../ics26-routing/types/src/module.rs | 74 + ibc-eureka-core/src/lib.rs | 91 + 137 files changed, 19356 insertions(+) create mode 100644 ibc-eureka-core/Cargo.toml create mode 100644 ibc-eureka-core/README.md create mode 100644 ibc-eureka-core/ics02-client/Cargo.toml create mode 100644 ibc-eureka-core/ics02-client/context/Cargo.toml create mode 100644 ibc-eureka-core/ics02-client/context/src/client_state.rs create mode 100644 ibc-eureka-core/ics02-client/context/src/consensus_state.rs create mode 100644 ibc-eureka-core/ics02-client/context/src/context.rs create mode 100644 ibc-eureka-core/ics02-client/context/src/lib.rs create mode 100644 ibc-eureka-core/ics02-client/src/handler/create_client.rs create mode 100644 ibc-eureka-core/ics02-client/src/handler/mod.rs create mode 100644 ibc-eureka-core/ics02-client/src/handler/recover_client.rs create mode 100644 ibc-eureka-core/ics02-client/src/handler/update_client.rs create mode 100644 ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs create mode 100644 ibc-eureka-core/ics02-client/src/lib.rs create mode 100644 ibc-eureka-core/ics02-client/types/Cargo.toml create mode 100644 ibc-eureka-core/ics02-client/types/src/error.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/events.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/height.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/lib.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/msgs/mod.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs create mode 100644 ibc-eureka-core/ics02-client/types/src/status.rs create mode 100644 ibc-eureka-core/ics03-connection/Cargo.toml create mode 100644 ibc-eureka-core/ics03-connection/src/delay.rs create mode 100644 ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs create mode 100644 ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs create mode 100644 ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs create mode 100644 ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs create mode 100644 ibc-eureka-core/ics03-connection/src/handler/mod.rs create mode 100644 ibc-eureka-core/ics03-connection/src/lib.rs create mode 100644 ibc-eureka-core/ics03-connection/types/Cargo.toml create mode 100644 ibc-eureka-core/ics03-connection/types/src/connection.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/error.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/events.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/lib.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/msgs/mod.rs create mode 100644 ibc-eureka-core/ics03-connection/types/src/version.rs create mode 100644 ibc-eureka-core/ics04-channel/Cargo.toml create mode 100644 ibc-eureka-core/ics04-channel/src/context.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/mod.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/send_packet.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/timeout.rs create mode 100644 ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs create mode 100644 ibc-eureka-core/ics04-channel/src/lib.rs create mode 100644 ibc-eureka-core/ics04-channel/types/Cargo.toml create mode 100644 ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/channel.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/commitment.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/error.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/events/mod.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/lib.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/packet.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/timeout/height.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/timeout/mod.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/timeout/timestamp.rs create mode 100644 ibc-eureka-core/ics04-channel/types/src/version.rs create mode 100644 ibc-eureka-core/ics23-commitment/types/Cargo.toml create mode 100644 ibc-eureka-core/ics23-commitment/types/src/commitment.rs create mode 100644 ibc-eureka-core/ics23-commitment/types/src/error.rs create mode 100644 ibc-eureka-core/ics23-commitment/types/src/lib.rs create mode 100644 ibc-eureka-core/ics23-commitment/types/src/merkle.rs create mode 100644 ibc-eureka-core/ics23-commitment/types/src/serializer.rs create mode 100644 ibc-eureka-core/ics23-commitment/types/src/specs.rs create mode 100644 ibc-eureka-core/ics24-host/Cargo.toml create mode 100644 ibc-eureka-core/ics24-host/cosmos/Cargo.toml create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/lib.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/events.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/mod.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/utils.rs create mode 100644 ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs create mode 100644 ibc-eureka-core/ics24-host/src/context.rs create mode 100644 ibc-eureka-core/ics24-host/src/lib.rs create mode 100644 ibc-eureka-core/ics24-host/src/utils.rs create mode 100644 ibc-eureka-core/ics24-host/types/Cargo.toml create mode 100644 ibc-eureka-core/ics24-host/types/src/error.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/mod.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/port_id.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/identifiers/sequence.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/lib.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/path.rs create mode 100644 ibc-eureka-core/ics24-host/types/src/validate.rs create mode 100644 ibc-eureka-core/ics25-handler/Cargo.toml create mode 100644 ibc-eureka-core/ics25-handler/src/entrypoint.rs create mode 100644 ibc-eureka-core/ics25-handler/src/lib.rs create mode 100644 ibc-eureka-core/ics25-handler/types/Cargo.toml create mode 100644 ibc-eureka-core/ics25-handler/types/src/error.rs create mode 100644 ibc-eureka-core/ics25-handler/types/src/events.rs create mode 100644 ibc-eureka-core/ics25-handler/types/src/lib.rs create mode 100644 ibc-eureka-core/ics25-handler/types/src/msgs.rs create mode 100644 ibc-eureka-core/ics26-routing/Cargo.toml create mode 100644 ibc-eureka-core/ics26-routing/src/lib.rs create mode 100644 ibc-eureka-core/ics26-routing/src/module.rs create mode 100644 ibc-eureka-core/ics26-routing/src/router.rs create mode 100644 ibc-eureka-core/ics26-routing/types/Cargo.toml create mode 100644 ibc-eureka-core/ics26-routing/types/src/error.rs create mode 100644 ibc-eureka-core/ics26-routing/types/src/event.rs create mode 100644 ibc-eureka-core/ics26-routing/types/src/lib.rs create mode 100644 ibc-eureka-core/ics26-routing/types/src/module.rs create mode 100644 ibc-eureka-core/src/lib.rs diff --git a/ibc-eureka-core/Cargo.toml b/ibc-eureka-core/Cargo.toml new file mode 100644 index 0000000000..eebb123180 --- /dev/null +++ b/ibc-eureka-core/Cargo.toml @@ -0,0 +1,84 @@ +[package] +name = "ibc-core" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "core" ] +readme = "README.md" + +description = """ + Maintained by `ibc-rs`, re-exports a comprehensive set of libraries that implement IBC core (TAO) + modules, facilitating seamless integration of IBC core business logic into any blockchain system. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ibc-core-client = { workspace = true } +ibc-core-connection = { workspace = true } +ibc-core-channel = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host = { workspace = true } +ibc-core-router = { workspace = true } +ibc-core-handler = { workspace = true } +ibc-derive = { workspace = true } +ibc-primitives = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "ibc-core-client/std", + "ibc-core-connection/std", + "ibc-core-channel/std", + "ibc-core-commitment-types/std", + "ibc-core-host/std", + "ibc-core-router/std", + "ibc-core-handler/std", + "ibc-primitives/std", +] +serde = [ + "ibc-core-client/serde", + "ibc-core-connection/serde", + "ibc-core-channel/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host/serde", + "ibc-core-router/serde", + "ibc-core-handler/serde", + "ibc-primitives/serde", +] +borsh = [ + "ibc-core-client/borsh", + "ibc-core-connection/borsh", + "ibc-core-channel/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host/borsh", + "ibc-core-router/borsh", + "ibc-core-handler/borsh", + "ibc-primitives/borsh", +] +schema = [ + "ibc-core-client/schema", + "ibc-core-connection/schema", + "ibc-core-channel/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host/schema", + "ibc-core-router/schema", + "ibc-core-handler/schema", + "ibc-primitives/schema", + "serde", + "std", +] +parity-scale-codec = [ + "ibc-core-client/parity-scale-codec", + "ibc-core-connection/parity-scale-codec", + "ibc-core-channel/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host/parity-scale-codec", + "ibc-core-router/parity-scale-codec", + "ibc-core-handler/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/README.md b/ibc-eureka-core/README.md new file mode 100644 index 0000000000..d8460a5b5a --- /dev/null +++ b/ibc-eureka-core/README.md @@ -0,0 +1,128 @@ +# IBC Core + +This is the top-level library that re-exports the Inter-Blockchain Communication +(IBC) core modules as a meta-crate. It serves to simplify the process of +importing and integrating various IBC core modules into your blockchain. + +IBC is a distributed protocol that enables communication between distinct +sovereign blockchains and IBC core is the part of the protocol that handles the +transport, authentication, and ordering (TAO) of data packets. + +The structure within the `ibc-core` crate is designed to provide flexibility for +external users. You can choose to utilize the entire `ibc-core` crate, or +selectively import specific libraries. From there, you also have the flexibility +of bringing in an entire sub-module (e.g. the `ibc-core-client` crate), or only +a module's associated data structures (e.g. `ibc-core-client-types`). + +This versatility empowers hosts, including chain integrators, relayers, or any +IBC tooling projects, to build their solution on top of the layers that best +suit their particular requirements. + +## Sub-Crates + +Currently, the `ibc-core` crate contains the implementation of the following IBC +core specifications: + +### ICS-02: Client Semantics + +- [ibc-core-client](./../ibc-core/ics02-client) +- [ibc-core-client-context](./../ibc-core/ics02-client/context) +- [ibc-core-client-types](./../ibc-core/ics02-client/types) + +### ICS-03: Connection Semantics + +- [ibc-core-connection](./../ibc-core/ics03-connection) +- [ibc-core-connection-types](./../ibc-core/ics03-connection/types) + +### ICS-04: Channel and Packet Semantics + +- [ibc-core-channel](./../ibc-core/ics04-channel) +- [ibc-core-channel-types](./../ibc-core/ics04-channel/types) + +### ICS-24: Host Requirements + +- [ibc-core-host](./../ibc-core/ics24-host) +- [ibc-core-host-cosmos](./../ibc-core/ics24-host/cosmos) +- [ibc-core-host-types](./../ibc-core/ics24-host/types) + +### ICS-25: Handler Interface + +- [ibc-core-handler](./../ibc-core/ics25-handler) +- [ibc-core-handler-types](./../ibc-core/ics25-handler/types) + +### ICS-26: Routing Module + +- [ibc-core-routing](./../ibc-core/ics26-routing) +- [ibc-core-routing-types](./../ibc-core/ics26-routing/types) + +## Divergence from the Interchain Standards (ICS) + +This crate diverges from the [ICS specification](https://github.com/cosmos/ibc) +in a number of ways. See below for more details. + +### Module system: no support for untrusted modules + +ICS-24 (Host Requirements) gives the [following +requirement](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#module-system) +about the module system that the host state machine must support: + +> The host state machine must support a module system, whereby self-contained, +> potentially mutually distrusted packages of code can safely execute on the +> same ledger [...]. + +**This crate currently does not support mutually distrusted packages**. That is, +modules on the host state machine are assumed to be fully trusted. In practice, +this means that every module has either been written by the host state machine +developers, or fully vetted by them. + +### Port system: No object capability system + +ICS-05 (Port Allocation) requires the host system to support either +object-capability reference or source authentication for modules. + +> In the former object-capability case, the IBC handler must have the ability to +> generate object-capabilities, unique, opaque references which can be passed to +> a module and will not be duplicable by other modules. [...] In the latter +> source authentication case, the IBC handler must have the ability to securely +> read the source identifier of the calling module, a unique string for each +> module in the host state machine, which cannot be altered by the module or +> faked by another module. + +**This crate currently requires neither of the host system**. Since modules are +assumed to be trusted, there is no need for this object capability system that +protects resources for potentially malicious modules. + +For more background on this, see [this issue](https://github.com/informalsystems/ibc-rs/issues/2159). + +### Port system: transferring and releasing a port + +ICS-05 (Port Allocation) requires the IBC handler to permit [transferring +ownership of a +port](https://github.com/cosmos/ibc/tree/master/spec/core/ics-005-port-allocation#transferring-ownership-of-a-port) +and [releasing a +port](https://github.com/cosmos/ibc/tree/master/spec/core/ics-005-port-allocation#releasing-a-port). + +We currently support neither. + +### Asynchronous acknowledgements + +The standard gives the ability for modules to [acknowledge packets +asynchronously](https://github.com/cosmos/ibc/tree/main/spec/core/ics-004-channel-and-packet-semantics#writing-acknowledgements). +This allows modules to receive the packet, but only applying the changes at a +later time (after which they would write the acknowledgement). + +We currently force applications to process the packets as part of +`onRecvPacket()`. If you need asynchronous acknowledgements for your +application, please open an issue. + +Note that this still makes us 100% compatible with `ibc-go`. + +## Contributing + +IBC is specified in English in the [cosmos/ibc +repo](https://github.com/cosmos/ibc). Any protocol changes or clarifications +should be contributed there. + +If you're interested in contributing, please take a look at the +[CONTRIBUTING](./../CONTRIBUTING.md) guidelines. We welcome and appreciate +community contributions! diff --git a/ibc-eureka-core/ics02-client/Cargo.toml b/ibc-eureka-core/ics02-client/Cargo.toml new file mode 100644 index 0000000000..7502b6a259 --- /dev/null +++ b/ibc-eureka-core/ics02-client/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "ibc-core-client" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "light-client" ] +readme = "./../README.md" + +description = """ + Maintained by `ibc-rs`, contains the implementation of ICS-02 Client Semantics and + re-exports essential data structures and domain types from `ibc-core-client-types` crate. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ibc-core-client-types = { workspace = true } +ibc-core-client-context = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "ibc-core-client-types/std", + "ibc-core-client-context/std", + "ibc-core-commitment-types/std", + "ibc-core-host/std", + "ibc-core-handler-types/std", + "ibc-primitives/std", +] +serde = [ + "ibc-core-client-types/serde", + "ibc-core-client-context/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host/serde", + "ibc-core-handler-types/serde", + "ibc-primitives/serde", +] +borsh = [ + "ibc-core-client-types/borsh", + "ibc-core-client-context/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host/borsh", + "ibc-core-handler-types/borsh", + "ibc-primitives/borsh", +] +schema = [ + "ibc-core-client-types/schema", + "ibc-core-client-context/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host/schema", + "ibc-core-handler-types/schema", + "ibc-primitives/schema", + "serde", + "std", +] +parity-scale-codec = [ + "ibc-core-client-types/parity-scale-codec", + "ibc-core-client-context/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics02-client/context/Cargo.toml b/ibc-eureka-core/ics02-client/context/Cargo.toml new file mode 100644 index 0000000000..9085da29e7 --- /dev/null +++ b/ibc-eureka-core/ics02-client/context/Cargo.toml @@ -0,0 +1,77 @@ +[package] +name = "ibc-core-client-context" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc" ] +readme = "./../../README.md" + +description = """ + Maintaind by `ibc-rs`, contains essential APIs to interface with the host chain's store, + enabling smooth client state transitions. Additionally, provides necessary traits for + implementing custom IBC light clients. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +derive_more = { workspace = true } +displaydoc = { workspace = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-client-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "subtle-encoding/std", + "ibc-core-client-types/std", + "ibc-core-commitment-types/std", + "ibc-core-host-types/std", + "ibc-core-handler-types/std", + "ibc-primitives/std", + "tendermint/std", +] +serde = [ + "ibc-core-client-types/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host-types/serde", + "ibc-core-handler-types/serde", + "ibc-primitives/serde", +] +borsh = [ + "ibc-core-client-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host-types/borsh", + "ibc-core-handler-types/borsh", + "ibc-primitives/borsh", +] +schema = [ + "ibc-core-client-types/schema", + "ibc-core-host-types/schema", + "ibc-core-handler-types/schema", + "ibc-primitives/schema", + "serde", + "std", +] +parity-scale-codec = [ + "ibc-core-client-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics02-client/context/src/client_state.rs b/ibc-eureka-core/ics02-client/context/src/client_state.rs new file mode 100644 index 0000000000..bbcc24aa7a --- /dev/null +++ b/ibc-eureka-core/ics02-client/context/src/client_state.rs @@ -0,0 +1,260 @@ +//! Defines `ClientState`, the core type to be implemented by light clients + +use ibc_core_client_types::error::ClientError; +use ibc_core_client_types::{Height, Status}; +use ibc_core_commitment_types::commitment::{ + CommitmentPrefix, CommitmentProofBytes, CommitmentRoot, +}; +use ibc_core_host_types::identifiers::{ClientId, ClientType}; +use ibc_core_host_types::path::{Path, PathBytes}; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Any; +use ibc_primitives::Timestamp; + +use crate::context::{ClientExecutionContext, ClientValidationContext}; +use crate::Convertible; + +/// `ClientState` methods needed in both validation and execution. +/// +/// They do not require access to a client `ValidationContext` nor +/// `ExecutionContext`. +pub trait ClientStateCommon: Convertible { + /// Performs basic validation on the `consensus_state`. + /// + /// Notably, an implementation should verify that it can properly + /// deserialize the object into the expected format. + fn verify_consensus_state( + &self, + consensus_state: Any, + host_timestamp: &Timestamp, + ) -> Result<(), ClientError>; + + /// Type of client associated with this state (eg. Tendermint) + fn client_type(&self) -> ClientType; + + /// The latest height the client was updated to + fn latest_height(&self) -> Height; + + /// Validate that the client is at a sufficient height + fn validate_proof_height(&self, proof_height: Height) -> Result<(), ClientError>; + + /// Verify the upgraded client and consensus states and validate proofs + /// against the given root. + /// + /// NOTE: proof heights are not included, as upgrade to a new revision is + /// expected to pass only on the last height committed by the current + /// revision. Clients are responsible for ensuring that the planned last + /// height of the current revision is somehow encoded in the proof + /// verification process. This is to ensure that no premature upgrades + /// occur, since upgrade plans committed to by the counterparty may be + /// cancelled or modified before the last planned height. + fn verify_upgrade_client( + &self, + upgraded_client_state: Any, + upgraded_consensus_state: Any, + proof_upgrade_client: CommitmentProofBytes, + proof_upgrade_consensus_state: CommitmentProofBytes, + root: &CommitmentRoot, + ) -> Result<(), ClientError>; + + /// Serializes a given path object into a raw path bytes. + /// + /// This method provides essential information for IBC modules, enabling + /// them to understand how path serialization is performed on the chain this + /// light client represents it before passing the path bytes to either + /// `verify_membership_raw()` or `verify_non_membership_raw()`. + fn serialize_path(&self, path: Path) -> Result; + + /// Verifies a proof of the existence of a value at a given raw path bytes. + fn verify_membership_raw( + &self, + prefix: &CommitmentPrefix, + proof: &CommitmentProofBytes, + root: &CommitmentRoot, + path: PathBytes, + value: Vec, + ) -> Result<(), ClientError>; + + /// Verifies a proof of the existence of a value at a given path object. + fn verify_membership( + &self, + prefix: &CommitmentPrefix, + proof: &CommitmentProofBytes, + root: &CommitmentRoot, + path: Path, + value: Vec, + ) -> Result<(), ClientError> { + let path_bytes = self.serialize_path(path)?; + self.verify_membership_raw(prefix, proof, root, path_bytes, value) + } + + /// Verifies the absence of a given proof at a given raw path bytes. + fn verify_non_membership_raw( + &self, + prefix: &CommitmentPrefix, + proof: &CommitmentProofBytes, + root: &CommitmentRoot, + path: PathBytes, + ) -> Result<(), ClientError>; + + /// Verifies the absence of a given proof at a given path object. + fn verify_non_membership( + &self, + prefix: &CommitmentPrefix, + proof: &CommitmentProofBytes, + root: &CommitmentRoot, + path: Path, + ) -> Result<(), ClientError> { + let path_bytes = self.serialize_path(path)?; + self.verify_non_membership_raw(prefix, proof, root, path_bytes) + } +} + +/// `ClientState` methods which require access to the client's validation +/// context +/// +/// The generic type `V` enables light client developers to expand the set of +/// methods available under the [`ClientValidationContext`] trait and use them in +/// their implementation for validating a client state transition. +/// +/// ```ignore +/// impl ClientStateValidation for MyClientState +/// where +/// V: ClientValidationContext + MyValidationContext, +/// { +/// // `MyValidationContext` methods available +/// } +/// +/// trait MyValidationContext { +/// // My Context methods +/// } +/// ``` +pub trait ClientStateValidation: ClientStateCommon +where + V: ClientValidationContext, +{ + /// verify_client_message must verify a client_message. A client_message + /// could be a Header, Misbehaviour. It must handle each type of + /// client_message appropriately. Calls to check_for_misbehaviour, + /// update_state, and update_state_on_misbehaviour will assume that the + /// content of the client_message has been verified and can be trusted. An + /// error should be returned if the client_message fails to verify. + fn verify_client_message( + &self, + ctx: &V, + client_id: &ClientId, + client_message: Any, + ) -> Result<(), ClientError>; + + /// Checks for evidence of a misbehaviour in Header or Misbehaviour type. It + /// assumes the client_message has already been verified. + fn check_for_misbehaviour( + &self, + ctx: &V, + client_id: &ClientId, + client_message: Any, + ) -> Result; + + /// Returns the status of the client. Only Active clients are allowed to process packets. + fn status(&self, ctx: &V, client_id: &ClientId) -> Result; + + /// Verifies whether the calling (subject) client state matches the substitute + /// client state for the purposes of client recovery. + /// + /// Note that this validation function does not need to perform *all* of the + /// validation steps necessary to confirm client recovery. Some checks, such + /// as checking that the subject client state's latest height < the substitute + /// client's latest height, as well as checking that the subject client is + /// inactive and that the substitute client is active, are performed by the + /// `validate` function in the `recover_client` module at the ics02-client + /// level. + /// + /// Returns `Ok` if the subject and substitute client states match, `Err` otherwise. + fn check_substitute(&self, ctx: &V, substitute_client_state: Any) -> Result<(), ClientError>; +} + +/// `ClientState` methods which require access to the client's +/// `ExecutionContext`. +/// +/// The generic type `E` enables light client developers to expand the set of +/// methods available under the [`ClientExecutionContext`] trait and use them in +/// their implementation for executing a client state transition. +pub trait ClientStateExecution: ClientStateValidation +where + E: ClientExecutionContext, +{ + /// Initialises the client with the initial client and consensus states. + /// + /// Most clients will want to call `E::store_client_state` and + /// `E::store_consensus_state`. + fn initialise( + &self, + ctx: &mut E, + client_id: &ClientId, + consensus_state: Any, + ) -> Result<(), ClientError>; + + /// Updates and stores as necessary any associated information for an IBC + /// client, such as the ClientState and corresponding ConsensusState. Upon + /// successful update, a list of consensus heights is returned. It assumes + /// the client_message has already been verified. + /// + /// Note that `header` is the field associated with `UpdateKind::UpdateClient`. + /// + /// Post-condition: on success, the return value MUST contain at least one + /// height. + fn update_state( + &self, + ctx: &mut E, + client_id: &ClientId, + header: Any, + ) -> Result, ClientError>; + + /// update_state_on_misbehaviour should perform appropriate state changes on + /// a client state given that misbehaviour has been detected and verified + fn update_state_on_misbehaviour( + &self, + ctx: &mut E, + client_id: &ClientId, + client_message: Any, + ) -> Result<(), ClientError>; + + /// Update the client state and consensus state in the store with the upgraded ones. + fn update_state_on_upgrade( + &self, + ctx: &mut E, + client_id: &ClientId, + upgraded_client_state: Any, + upgraded_consensus_state: Any, + ) -> Result; + + /// Update the subject client using the `substitute_client_state` in response + /// to a successful client recovery. + fn update_on_recovery( + &self, + ctx: &mut E, + subject_client_id: &ClientId, + substitute_client_state: Any, + substitute_consensus_state: Any, + ) -> Result<(), ClientError>; +} + +/// Primary client trait. Defines all the methods that clients must implement. +/// +/// `ClientState` is broken up into 3 separate traits to avoid needing to use +/// fully qualified syntax for every method call (see ADR 7 for more details). +/// One only needs to implement [`ClientStateCommon`], [`ClientStateValidation`] +/// and [`ClientStateExecution`]; a blanket implementation will automatically +/// implement `ClientState`. +/// +/// Refer to [`ClientStateValidation`] and [`ClientStateExecution`] to learn +/// more about what both generic parameters represent. +pub trait ClientState: + Send + Sync + ClientStateCommon + ClientStateValidation + ClientStateExecution +{ +} + +impl ClientState for T where + T: Send + Sync + ClientStateCommon + ClientStateValidation + ClientStateExecution +{ +} diff --git a/ibc-eureka-core/ics02-client/context/src/consensus_state.rs b/ibc-eureka-core/ics02-client/context/src/consensus_state.rs new file mode 100644 index 0000000000..2149c76ff9 --- /dev/null +++ b/ibc-eureka-core/ics02-client/context/src/consensus_state.rs @@ -0,0 +1,21 @@ +//! 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; +use ibc_primitives::Timestamp; + +use crate::Convertible; + +/// Defines methods that all `ConsensusState`s should provide. +/// +/// One can think of a "consensus state" as a pruned header, to be stored on chain. In other words, +/// a consensus state only contains the header's information needed by IBC message handlers. +pub trait ConsensusState: Send + Sync + Convertible { + /// Commitment root of the consensus state, which is used for key-value pair verification. + fn root(&self) -> &CommitmentRoot; + + /// The timestamp of the consensus state + fn timestamp(&self) -> Result; +} diff --git a/ibc-eureka-core/ics02-client/context/src/context.rs b/ibc-eureka-core/ics02-client/context/src/context.rs new file mode 100644 index 0000000000..e9a47bf0d2 --- /dev/null +++ b/ibc-eureka-core/ics02-client/context/src/context.rs @@ -0,0 +1,159 @@ +use ibc_core_client_types::Height; +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::*; +use ibc_primitives::Timestamp; + +use crate::client_state::{ClientStateExecution, ClientStateValidation}; +use crate::consensus_state::ConsensusState; + +/// Defines the methods available to clients for validating client state +/// transitions. The generic `V` parameter in +/// [crate::client_state::ClientStateValidation] must +/// inherit from this trait. +pub trait ClientValidationContext: Sized { + type ClientStateRef: ClientStateValidation; + type ConsensusStateRef: ConsensusState; + + /// 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; + + /// Retrieve the consensus state for the given client ID at the specified + /// height. + /// + /// Returns an error if no such state exists. + /// + /// Note: Clients have the responsibility to store consensus states on client creation and update. + fn consensus_state( + &self, + client_cons_state_path: &ClientConsensusStatePath, + ) -> Result; + + /// Returns the timestamp and height of the host when it processed a client + /// update request at the specified height. + fn client_update_meta( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result<(Timestamp, Height), HostError>; +} + +/// Defines the methods that all client `ExecutionContext`s (precisely the +/// generic parameter of +/// [`crate::client_state::ClientStateExecution`] ) must +/// implement. +/// +/// Specifically, clients have the responsibility to store their client state +/// and consensus states. This trait defines a uniform interface to do that for +/// all clients. +pub trait ClientExecutionContext: + ClientValidationContext +{ + type ClientStateMut: ClientStateExecution; + + fn client_state_mut(&self, client_id: &ClientId) -> Result { + self.client_state(client_id) + } + + /// Called upon successful client creation and update + fn store_client_state( + &mut self, + client_state_path: ClientStatePath, + client_state: Self::ClientStateRef, + ) -> Result<(), HostError>; + + /// Called upon successful client creation and update + fn store_consensus_state( + &mut self, + consensus_state_path: ClientConsensusStatePath, + consensus_state: Self::ConsensusStateRef, + ) -> 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<(), HostError>; + + /// Called upon successful client update. + /// + /// Implementations are expected to use this to record the specified time + /// and height as the time at which this update (or header) was processed. + fn store_update_meta( + &mut self, + client_id: ClientId, + height: Height, + host_timestamp: Timestamp, + host_height: Height, + ) -> Result<(), HostError>; + + /// Delete the update time and height associated with the client at the + /// specified height. + /// + /// This update time should be associated with a consensus state through the + /// specified height. + /// + /// Note that this timestamp is determined by the host. + fn delete_update_meta(&mut self, client_id: ClientId, height: Height) -> Result<(), HostError>; +} + +/// An optional trait that extends the client validation context capabilities by +/// providing additional methods for validating a client state. Mainly +/// benefiting ICS-07 Tendermint clients by granting access to essential +/// information from hosts. +/// +/// Categorized under ICS-02, as it may also be utilized by other types of light +/// clients. Developers may view this trait as an example of a custom context +/// definition that expands client validation capabilities, according to their +/// specific light client requirements. +pub trait ExtClientValidationContext: ClientValidationContext { + /// Returns the current timestamp of the local chain. + fn host_timestamp(&self) -> Result; + + /// Returns the current height of the local chain. + 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, HostError>; + + /// Search for the lowest consensus state higher than `height`. + fn next_consensus_state( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result, HostError>; + + /// Search for the highest consensus state lower than `height`. + fn prev_consensus_state( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result, HostError>; +} + +/// An optional trait that extends the client context required during execution. +/// +/// This trait, as it stands right now, serves as a trait alias for types that +/// implement both [`ExtClientValidationContext`] and +/// [`ClientExecutionContext`], and it is auto-implemented for such types. +/// +/// Light client developers who wish to define and utilize their own custom +/// client contexts may choose to introduce execution methods within a similar +/// trait, which would allow them to store additional client-specific data and +/// conduct execution operations more efficiently, tailored to their light +/// client implementation. +pub trait ExtClientExecutionContext: ExtClientValidationContext + ClientExecutionContext {} + +impl ExtClientExecutionContext for T where T: ExtClientValidationContext + ClientExecutionContext {} + +/// General-purpose helper converter enabling `TryFrom` and `Into` conversions +/// primarily intended between an enum and its variants. This usually used by +/// standalone functions as a trait bound allowing them to obtain the concrete +/// local type from the enum containing that concrete type as its variant, like +/// when enum `AnyConsensusState` contains the Tendermint `ConsensusState`. +pub trait Convertible: TryFrom + Into {} + +impl Convertible for T where T: TryFrom + Into {} diff --git a/ibc-eureka-core/ics02-client/context/src/lib.rs b/ibc-eureka-core/ics02-client/context/src/lib.rs new file mode 100644 index 0000000000..7b873f38f8 --- /dev/null +++ b/ibc-eureka-core/ics02-client/context/src/lib.rs @@ -0,0 +1,37 @@ +//! This crate functions as an intermediary layer between the storage of host +//! chains and an IBC client implementation, providing developers with necessary +//! traits to craft their custom light clients. It streamlines the process of +//! integrating light clients with the host, enabling interaction with the store +//! for pertinent client state transitions. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod client_state; +pub mod consensus_state; + +mod context; +pub use context::*; + +/// Trait preludes for the ICS-02 client implementation. +pub mod prelude { + pub use crate::client_state::*; + pub use crate::consensus_state::*; + pub use crate::context::*; +} + +pub mod types { + #[doc(inline)] + pub use ibc_core_client_types::*; +} diff --git a/ibc-eureka-core/ics02-client/src/handler/create_client.rs b/ibc-eureka-core/ics02-client/src/handler/create_client.rs new file mode 100644 index 0000000000..511f45f2c2 --- /dev/null +++ b/ibc-eureka-core/ics02-client/src/handler/create_client.rs @@ -0,0 +1,90 @@ +//! Protocol logic specific to processing ICS2 messages of type `MsgCreateClient`. + +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_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<(), ClientError> +where + Ctx: ValidationContext, + as TryFrom>::Error: Into, +{ + let MsgCreateClient { + client_state, + consensus_state, + signer, + } = msg; + + ctx.validate_message_signer(&signer)?; + + // Construct this client's identifier + let id_counter = ctx.client_counter()?; + + let client_val_ctx = ctx.get_client_validation_context(); + + let client_state = ClientStateRef::::try_from(client_state).map_err(Into::into)?; + + let client_id = client_state.client_type().build_client_id(id_counter); + + let status = client_state.status(client_val_ctx, &client_id)?; + + if status.is_frozen() { + return Err(ClientError::InvalidStatus(Status::Frozen)); + }; + + let host_timestamp = ctx.host_timestamp()?; + + client_state.verify_consensus_state(consensus_state, &host_timestamp)?; + + if client_val_ctx.client_state(&client_id).is_ok() { + return Err(ClientError::DuplicateClientState(client_id)); + }; + + Ok(()) +} + +pub fn execute(ctx: &mut Ctx, msg: MsgCreateClient) -> Result<(), ClientError> +where + Ctx: ExecutionContext, + as TryFrom>::Error: Into, +{ + let MsgCreateClient { + client_state, + consensus_state, + signer: _, + } = msg; + + // Construct this client's identifier + let id_counter = ctx.client_counter()?; + + let client_exec_ctx = ctx.get_client_execution_context(); + + let client_state = ClientStateMut::::try_from(client_state).map_err(Into::into)?; + + let client_type = client_state.client_type(); + let client_id = client_type.build_client_id(id_counter); + + client_state.initialise(client_exec_ctx, &client_id, consensus_state)?; + + ctx.increase_client_counter()?; + + let event = IbcEvent::CreateClient(CreateClient::new( + client_id.clone(), + client_type, + client_state.latest_height(), + )); + ctx.emit_ibc_event(IbcEvent::Message(MessageEvent::Client))?; + ctx.emit_ibc_event(event)?; + + ctx.log_message(format!( + "success: generated new client identifier: {client_id}" + ))?; + + Ok(()) +} diff --git a/ibc-eureka-core/ics02-client/src/handler/mod.rs b/ibc-eureka-core/ics02-client/src/handler/mod.rs new file mode 100644 index 0000000000..791b5ce398 --- /dev/null +++ b/ibc-eureka-core/ics02-client/src/handler/mod.rs @@ -0,0 +1,6 @@ +//! This module implements the processing logic for ICS2 (client abstractions and functions) msgs. + +pub mod create_client; +pub mod recover_client; +pub mod update_client; +pub mod upgrade_client; diff --git a/ibc-eureka-core/ics02-client/src/handler/recover_client.rs b/ibc-eureka-core/ics02-client/src/handler/recover_client.rs new file mode 100644 index 0000000000..a239ed8fc2 --- /dev/null +++ b/ibc-eureka-core/ics02-client/src/handler/recover_client.rs @@ -0,0 +1,89 @@ +//! Protocol logic for processing ICS02 messages of type `MsgRecoverClient`. + +use ibc_core_client_context::prelude::*; +use ibc_core_client_types::error::ClientError; +use ibc_core_client_types::msgs::MsgRecoverClient; +use ibc_core_host::types::path::ClientConsensusStatePath; +use ibc_core_host::{ExecutionContext, ValidationContext}; + +/// Performs the validation steps associated with the client recovery process. This +/// 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<(), ClientError> +where + Ctx: ValidationContext, +{ + let signer = msg.signer; + let subject_client_id = msg.subject_client_id.clone(); + let substitute_client_id = msg.substitute_client_id.clone(); + + ctx.validate_message_signer(&signer)?; + + let client_val_ctx = ctx.get_client_validation_context(); + + let subject_client_state = client_val_ctx.client_state(&subject_client_id)?; + let substitute_client_state = client_val_ctx.client_state(&substitute_client_id)?; + + let subject_height = subject_client_state.latest_height(); + let substitute_height = substitute_client_state.latest_height(); + + if subject_height >= substitute_height { + return Err(ClientError::InvalidClientRecoveryHeights { + subject_height, + substitute_height, + }); + } + + substitute_client_state + .status(ctx.get_client_validation_context(), &substitute_client_id)? + .verify_is_active()?; + + // Verify that the subject client is inactive, i.e., that it is either frozen or expired + subject_client_state + .status(ctx.get_client_validation_context(), &subject_client_id)? + .verify_is_inactive()?; + + // Check that the subject client state and substitute client states match, i.e., that + // all their respective client state parameters match except for frozen height, latest + // height, trusting period, and chain ID + subject_client_state.check_substitute( + ctx.get_client_validation_context(), + substitute_client_state.into(), + )?; + + Ok(()) +} + +/// Executes the steps needed to recover the subject client, namely: +/// - setting the subject's status from either `frozen` or `expired` to `active` +/// - 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<(), ClientError> +where + Ctx: ExecutionContext, +{ + let subject_client_id = msg.subject_client_id.clone(); + let substitute_client_id = msg.substitute_client_id.clone(); + + let client_exec_ctx = ctx.get_client_execution_context(); + + let subject_client_state = client_exec_ctx.client_state(&subject_client_id)?; + let substitute_client_state = client_exec_ctx.client_state(&substitute_client_id)?; + let substitute_consensus_state = + client_exec_ctx.consensus_state(&ClientConsensusStatePath::new( + substitute_client_id.clone(), + substitute_client_state.latest_height().revision_number(), + substitute_client_state.latest_height().revision_height(), + ))?; + + subject_client_state.update_on_recovery( + ctx.get_client_execution_context(), + &subject_client_id, + substitute_client_state.into(), + substitute_consensus_state.into(), + )?; + + Ok(()) +} diff --git a/ibc-eureka-core/ics02-client/src/handler/update_client.rs b/ibc-eureka-core/ics02-client/src/handler/update_client.rs new file mode 100644 index 0000000000..51a30c3b1b --- /dev/null +++ b/ibc-eureka-core/ics02-client/src/handler/update_client.rs @@ -0,0 +1,97 @@ +//! Protocol logic specific to processing ICS2 messages of type `MsgUpdateAnyClient`. + +use ibc_core_client_context::prelude::*; +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::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<(), ClientError> +where + Ctx: ValidationContext, +{ + ctx.validate_message_signer(msg.signer())?; + + let client_id = msg.client_id().clone(); + + let client_val_ctx = ctx.get_client_validation_context(); + + // Read client state from the host chain store. The client should already exist. + let client_state = client_val_ctx.client_state(&client_id)?; + + client_state + .status(client_val_ctx, &client_id)? + .verify_is_active()?; + + let client_message = msg.client_message(); + + client_state.verify_client_message(client_val_ctx, &client_id, client_message)?; + + Ok(()) +} + +pub fn execute(ctx: &mut Ctx, msg: MsgUpdateOrMisbehaviour) -> Result<(), ClientError> +where + Ctx: ExecutionContext, +{ + let client_id = msg.client_id().clone(); + let update_kind = match msg { + MsgUpdateOrMisbehaviour::UpdateClient(_) => UpdateKind::UpdateClient, + MsgUpdateOrMisbehaviour::Misbehaviour(_) => UpdateKind::SubmitMisbehaviour, + }; + let client_message = msg.client_message(); + + let client_exec_ctx = ctx.get_client_execution_context(); + + let client_state = client_exec_ctx.client_state(&client_id)?; + + let found_misbehaviour = + client_state.check_for_misbehaviour(client_exec_ctx, &client_id, client_message.clone())?; + + if found_misbehaviour { + client_state.update_state_on_misbehaviour(client_exec_ctx, &client_id, client_message)?; + + let event = IbcEvent::ClientMisbehaviour(ClientMisbehaviour::new( + client_id, + client_state.client_type(), + )); + ctx.emit_ibc_event(IbcEvent::Message(MessageEvent::Client))?; + ctx.emit_ibc_event(event)?; + } else { + if !matches!(update_kind, UpdateKind::UpdateClient) { + return Err(ClientError::FailedToHandleMisbehaviour { + description: "misbehaviour submitted, but none found".to_string(), + }); + } + + let header = client_message; + + let consensus_heights = + client_state.update_state(client_exec_ctx, &client_id, header.clone())?; + + { + let event = { + let consensus_height = consensus_heights.first().ok_or( + HostError::missing_state("updated height in client update state"), + )?; + + IbcEvent::UpdateClient(UpdateClient::new( + client_id, + client_state.client_type(), + *consensus_height, + consensus_heights, + header.to_vec(), + )) + }; + ctx.emit_ibc_event(IbcEvent::Message(MessageEvent::Client))?; + ctx.emit_ibc_event(event)?; + } + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs b/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs new file mode 100644 index 0000000000..ae119faeb8 --- /dev/null +++ b/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs @@ -0,0 +1,78 @@ +//! Protocol logic specific to processing ICS2 messages of type `MsgUpgradeAnyClient`. +//! +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::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<(), ClientError> +where + Ctx: ValidationContext, +{ + let MsgUpgradeClient { + client_id, signer, .. + } = msg; + + ctx.validate_message_signer(&signer)?; + + let client_val_ctx = ctx.get_client_validation_context(); + + // Read the current latest client state from the host chain store. + let old_client_state = client_val_ctx.client_state(&client_id)?; + + // Check if the client is active. + old_client_state + .status(client_val_ctx, &client_id)? + .verify_is_active()?; + + // Read the latest consensus state from the host chain store. + let old_client_cons_state_path = ClientConsensusStatePath::new( + client_id.clone(), + 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)?; + + // Validate the upgraded client state and consensus state and verify proofs against the root + old_client_state.verify_upgrade_client( + msg.upgraded_client_state.clone(), + msg.upgraded_consensus_state, + msg.proof_upgrade_client, + msg.proof_upgrade_consensus_state, + old_consensus_state.root(), + )?; + + Ok(()) +} + +pub fn execute(ctx: &mut Ctx, msg: MsgUpgradeClient) -> Result<(), ClientError> +where + Ctx: ExecutionContext, +{ + let MsgUpgradeClient { client_id, .. } = msg; + + let client_exec_ctx = ctx.get_client_execution_context(); + + let old_client_state = client_exec_ctx.client_state(&client_id)?; + + let latest_height = old_client_state.update_state_on_upgrade( + client_exec_ctx, + &client_id, + msg.upgraded_client_state.clone(), + msg.upgraded_consensus_state, + )?; + + let event = IbcEvent::UpgradeClient(UpgradeClient::new( + client_id, + old_client_state.client_type(), + latest_height, + )); + ctx.emit_ibc_event(IbcEvent::Message(MessageEvent::Client))?; + ctx.emit_ibc_event(event)?; + + Ok(()) +} diff --git a/ibc-eureka-core/ics02-client/src/lib.rs b/ibc-eureka-core/ics02-client/src/lib.rs new file mode 100644 index 0000000000..b692624c84 --- /dev/null +++ b/ibc-eureka-core/ics02-client/src/lib.rs @@ -0,0 +1,31 @@ +//! ICS-02: Client Semantics implementation for verifying remote IBC-enabled chains, +//! along with re-exporting data structures from `ibc-core-client-types` crate. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod handler; + +/// Re-exports ICS-02 traits from `ibc-core-client-context` for custom IBC +/// client implementation. +pub mod context { + #[doc(inline)] + pub use ibc_core_client_context::*; +} + +/// Re-exports ICS-02 data structures from the `ibc-core-client-types` crate. +pub mod types { + #[doc(inline)] + pub use ibc_core_client_types::*; +} diff --git a/ibc-eureka-core/ics02-client/types/Cargo.toml b/ibc-eureka-core/ics02-client/types/Cargo.toml new file mode 100644 index 0000000000..abc812cd43 --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/Cargo.toml @@ -0,0 +1,88 @@ +[package] +name = "ibc-core-client-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "client", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-02 Client Semantics data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[dev-dependencies] +rstest = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "serde/std", + "subtle-encoding/std", + "ibc-core-commitment-types/std", + "ibc-core-host-types/std", + "ibc-primitives/std", + "ibc-proto/std", + "tendermint/std", +] +serde = [ + "ibc-core-host-types/serde", + "ibc-core-commitment-types/serde", + "ibc-primitives/serde", + "ibc-proto/serde", + "dep:serde", +] +borsh = [ + "dep:borsh", + "ibc-core-host-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-primitives/borsh", + "ibc-proto/borsh", +] +schema = [ + "dep:schemars", + "ibc-core-host-types/schema", + "ibc-core-commitment-types/schema", + "ibc-primitives/schema", + "ibc-proto/json-schema", + "serde", + "std", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-core-host-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics02-client/types/src/error.rs b/ibc-eureka-core/ics02-client/types/src/error.rs new file mode 100644 index 0000000000..49ced05dd0 --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/error.rs @@ -0,0 +1,166 @@ +//! Defines the client error type + +use core::convert::Infallible; + +use displaydoc::Display; +use ibc_core_commitment_types::error::CommitmentError; +use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_core_host_types::identifiers::ClientId; +use ibc_primitives::prelude::*; +use ibc_primitives::{Timestamp, TimestampError}; + +use crate::height::Height; +use crate::Status; + +/// Encodes all the possible client errors +#[derive(Debug, Display)] +pub enum ClientError { + /// host error : {0} + Host(HostError), + /// upgrade client error: {0} + Upgrade(UpgradeClientError), + /// decoding error: {0} + Decoding(DecodingError), + /// timestamp error: {0} + Timestamp(TimestampError), + /// invalid trust threshold `{numerator}`/`{denominator}` + InvalidTrustThreshold { numerator: u64, denominator: u64 }, + /// invalid client state type `{0}` + InvalidClientStateType(String), + /// invalid update client message + InvalidUpdateClientMessage, + /// invalid height; cannot be zero or negative + InvalidHeight, + /// 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, + }, + /// 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 }, +} + +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) + } +} + +impl From for ClientError { + fn from(value: Infallible) -> Self { + match value {} + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ClientError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + 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, + } + } +} + +/// Encodes all the possible upgrade client errors +#[derive(Debug, Display)] +pub enum UpgradeClientError { + /// 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), + /// 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, + }, +} + +impl From for ClientError { + fn from(e: UpgradeClientError) -> Self { + ClientError::Upgrade(e) + } +} + +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::Decoding(e) => Some(e), + Self::Host(e) => Some(e), + Self::InvalidUpgradeClientStateProof(e) + | Self::InvalidUpgradeConsensusStateProof(e) => Some(e), + _ => None, + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/events.rs b/ibc-eureka-core/ics02-client/types/src/events.rs new file mode 100644 index 0000000000..744776b88a --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/events.rs @@ -0,0 +1,857 @@ +//! 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::height::Height; + +/// Client event types +pub const CREATE_CLIENT_EVENT: &str = "create_client"; +pub const UPDATE_CLIENT_EVENT: &str = "update_client"; +pub const CLIENT_MISBEHAVIOUR_EVENT: &str = "client_misbehaviour"; +pub const UPGRADE_CLIENT_EVENT: &str = "upgrade_client"; + +/// The content of the `key` field for the attribute containing the client identifier. +pub const CLIENT_ID_ATTRIBUTE_KEY: &str = "client_id"; + +/// The content of the `key` field for the attribute containing the client type. +pub const CLIENT_TYPE_ATTRIBUTE_KEY: &str = "client_type"; + +/// The content of the `key` field for the attribute containing the height. +pub const CONSENSUS_HEIGHT_ATTRIBUTE_KEY: &str = "consensus_height"; + +/// The content of the `key` field for the attribute containing the heights of consensus states that were processed. +pub const CONSENSUS_HEIGHTS_ATTRIBUTE_KEY: &str = "consensus_heights"; + +/// The content of the `key` field for the header in update client event. +pub const HEADER_ATTRIBUTE_KEY: &str = "header"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct ClientIdAttribute { + client_id: ClientId, +} + +impl From for abci::EventAttribute { + fn from(attr: ClientIdAttribute) -> Self { + (CLIENT_ID_ATTRIBUTE_KEY, attr.client_id.as_str()).into() + } +} +impl TryFrom for ClientIdAttribute { + 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(DecodingError::MismatchedResourceName { + expected: CLIENT_ID_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + }); + } + } else { + return Err(DecodingError::missing_raw_data("attribute key")); + } + + value + .value_str() + .map(|value| { + let client_id = ClientId::from_str(value)?; + Ok(ClientIdAttribute { client_id }) + }) + .map_err(|e| DecodingError::missing_raw_data(format!("attribute value: {e}")))? + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct ClientTypeAttribute { + client_type: ClientType, +} + +impl From for abci::EventAttribute { + fn from(attr: ClientTypeAttribute) -> Self { + (CLIENT_TYPE_ATTRIBUTE_KEY, attr.client_type.as_str()).into() + } +} + +impl TryFrom for ClientTypeAttribute { + 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(DecodingError::MismatchedResourceName { + expected: CLIENT_TYPE_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; + } + } else { + return Err(DecodingError::missing_raw_data("client type attribute key")); + } + + value + .value_str() + .map(|value| { + let client_type = ClientType::from_str(value)?; + Ok(ClientTypeAttribute { client_type }) + }) + .map_err(|e| { + DecodingError::missing_raw_data(format!("client type attribute value: {e}")) + })? + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct ConsensusHeightAttribute { + consensus_height: Height, +} + +impl From for abci::EventAttribute { + fn from(attr: ConsensusHeightAttribute) -> Self { + (CONSENSUS_HEIGHT_ATTRIBUTE_KEY, attr.consensus_height).into() + } +} + +impl TryFrom for ConsensusHeightAttribute { + 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(DecodingError::MismatchedResourceName { + expected: CONSENSUS_HEIGHT_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; + } + } else { + return Err(DecodingError::missing_raw_data( + "consensus height attribute key", + )); + } + + value + .value_str() + .map(|value| { + let consensus_height = Height::from_str(value)?; + Ok(ConsensusHeightAttribute { consensus_height }) + }) + .map_err(|e| { + DecodingError::missing_raw_data(format!("consensus height attribute value: {e}")) + })? + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct ConsensusHeightsAttribute { + consensus_heights: Vec, +} + +impl From for abci::EventAttribute { + fn from(attr: ConsensusHeightsAttribute) -> Self { + let consensus_heights: Vec = attr + .consensus_heights + .into_iter() + .map(|consensus_height| consensus_height.to_string()) + .collect(); + (CONSENSUS_HEIGHTS_ATTRIBUTE_KEY, consensus_heights.join(",")).into() + } +} + +impl TryFrom for ConsensusHeightsAttribute { + 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(DecodingError::MismatchedResourceName { + expected: CONSENSUS_HEIGHTS_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; + } + } else { + return Err(DecodingError::missing_raw_data( + "consensus heights attribute key", + )); + } + + value + .value_str() + .map(|value| { + let consensus_heights: Vec = value + .split(',') + .map(Height::from_str) + .collect::, DecodingError>>( + )?; + + Ok(ConsensusHeightsAttribute { consensus_heights }) + }) + .map_err(|e| { + DecodingError::invalid_raw_data(format!("consensus heights attribute value: {e}")) + })? + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct HeaderAttribute { + /// NOTE: The header is encoded as bytes of the + /// [`Any`](ibc_proto::google::protobuf::Any) type. + header: Vec, +} + +impl From for abci::EventAttribute { + fn from(attr: HeaderAttribute) -> Self { + ( + HEADER_ATTRIBUTE_KEY, + str::from_utf8(&hex::encode(attr.header)) + .expect("never fails because hexadecimal is valid UTF-8"), + ) + .into() + } +} +impl TryFrom for HeaderAttribute { + 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(DecodingError::MismatchedResourceName { + expected: HEADER_ATTRIBUTE_KEY.to_string(), + actual: key_str.to_string(), + })?; + } + } else { + return Err(DecodingError::missing_raw_data("header attribute key")); + } + + value + .value_str() + .map(|value| { + let header = hex::decode(value).map_err(|e| { + DecodingError::invalid_raw_data(format!("header attribute value: {e}")) + })?; + + Ok(HeaderAttribute { header }) + }) + .map_err(|e| DecodingError::invalid_raw_data(format!("header attribute value: {e}")))? + } +} + +/// CreateClient event signals the creation of a new on-chain client (IBC client). +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateClient { + client_id: ClientIdAttribute, + client_type: ClientTypeAttribute, + consensus_height: ConsensusHeightAttribute, +} + +impl CreateClient { + pub fn new(client_id: ClientId, client_type: ClientType, consensus_height: Height) -> Self { + Self { + client_id: ClientIdAttribute::from(client_id), + client_type: ClientTypeAttribute::from(client_type), + consensus_height: ConsensusHeightAttribute::from(consensus_height), + } + } + + pub fn client_id(&self) -> &ClientId { + &self.client_id.client_id + } + + pub fn client_type(&self) -> &ClientType { + &self.client_type.client_type + } + + pub fn consensus_height(&self) -> &Height { + &self.consensus_height.consensus_height + } + + pub fn event_type(&self) -> &str { + CREATE_CLIENT_EVENT + } +} + +impl From for abci::Event { + fn from(c: CreateClient) -> Self { + Self { + kind: CREATE_CLIENT_EVENT.to_owned(), + attributes: vec![ + c.client_id.into(), + c.client_type.into(), + c.consensus_height.into(), + ], + } + } +} + +impl TryFrom for CreateClient { + type Error = DecodingError; + + fn try_from(value: abci::Event) -> Result { + if value.kind != CREATE_CLIENT_EVENT { + return Err(DecodingError::MismatchedResourceName { + expected: CREATE_CLIENT_EVENT.to_string(), + actual: value.kind, + })?; + } + + value + .attributes + .iter() + .try_fold( + (None, None, None), + |(client_id, client_type, consensus_height): ( + Option, + Option, + Option, + ), + attribute| { + 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(( + Some(attribute.clone().try_into()?), + client_type, + consensus_height, + )), + CLIENT_TYPE_ATTRIBUTE_KEY => Ok(( + client_id, + Some(attribute.clone().try_into()?), + consensus_height, + )), + CONSENSUS_HEIGHT_ATTRIBUTE_KEY => { + Ok((client_id, client_type, Some(attribute.clone().try_into()?))) + } + _ => Ok((client_id, client_type, consensus_height)), + } + }, + ) + .and_then( + |(client_id, client_type, consensus_height): ( + Option, + Option, + Option, + )| { + 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, + client_type.client_type, + consensus_height.consensus_height, + )) + }, + ) + } +} +/// UpdateClient event signals a recent update of an on-chain client (IBC Client). +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct UpdateClient { + client_id: ClientIdAttribute, + client_type: ClientTypeAttribute, + // Deprecated: consensus_height is deprecated and will be removed in a future release. + // Please use consensus_heights instead. + consensus_height: ConsensusHeightAttribute, + consensus_heights: ConsensusHeightsAttribute, + header: HeaderAttribute, +} + +impl UpdateClient { + /// Constructs a new UpdateClient event. + /// + /// NOTE: the `header` is the encoded bytes of the + /// [`Any`](ibc_proto::google::protobuf::Any) type. + pub fn new( + client_id: ClientId, + client_type: ClientType, + consensus_height: Height, + consensus_heights: Vec, + header: Vec, + ) -> Self { + Self { + client_id: ClientIdAttribute::from(client_id), + client_type: ClientTypeAttribute::from(client_type), + consensus_height: ConsensusHeightAttribute::from(consensus_height), + consensus_heights: ConsensusHeightsAttribute::from(consensus_heights), + header: HeaderAttribute::from(header), + } + } + + pub fn client_id(&self) -> &ClientId { + &self.client_id.client_id + } + + pub fn client_type(&self) -> &ClientType { + &self.client_type.client_type + } + + pub fn consensus_height(&self) -> &Height { + &self.consensus_height.consensus_height + } + + pub fn consensus_heights(&self) -> &[Height] { + self.consensus_heights.consensus_heights.as_ref() + } + + pub fn header(&self) -> &Vec { + &self.header.header + } + + pub fn event_type(&self) -> &str { + UPDATE_CLIENT_EVENT + } +} + +impl From for abci::Event { + fn from(u: UpdateClient) -> Self { + Self { + kind: UPDATE_CLIENT_EVENT.to_owned(), + attributes: vec![ + u.client_id.into(), + u.client_type.into(), + u.consensus_height.into(), + u.consensus_heights.into(), + u.header.into(), + ], + } + } +} +impl TryFrom for UpdateClient { + type Error = DecodingError; + + fn try_from(value: abci::Event) -> Result { + if value.kind != UPDATE_CLIENT_EVENT { + return Err(DecodingError::MismatchedResourceName { + expected: UPDATE_CLIENT_EVENT.to_string(), + actual: value.kind.to_string(), + })?; + } + + type UpdateClientAttributes = ( + Option, + Option, + Option, + Option, + Option, + ); + + value + .attributes + .iter() + .try_fold( + (None, None, None, None, None), + |acc: UpdateClientAttributes, attribute| { + let key = attribute.key_str().map_err(|e| { + DecodingError::invalid_raw_data(format!("attribute key: {e}")) + })?; + + match key { + CLIENT_ID_ATTRIBUTE_KEY => Ok(( + Some(attribute.clone().try_into()?), + acc.1, + acc.2, + acc.3, + acc.4, + )), + CLIENT_TYPE_ATTRIBUTE_KEY => Ok(( + acc.0, + Some(attribute.clone().try_into()?), + acc.2, + acc.3, + acc.4, + )), + CONSENSUS_HEIGHT_ATTRIBUTE_KEY => Ok(( + acc.0, + acc.1, + Some(attribute.clone().try_into()?), + acc.3, + acc.4, + )), + CONSENSUS_HEIGHTS_ATTRIBUTE_KEY => Ok(( + acc.0, + acc.1, + acc.2, + Some(attribute.clone().try_into()?), + acc.4, + )), + HEADER_ATTRIBUTE_KEY => Ok(( + acc.0, + acc.1, + acc.2, + acc.3, + Some(attribute.clone().try_into()?), + )), + _ => Ok(acc), + } + }, + ) + .and_then( + |(client_id, client_type, consensus_height, consensus_heights, header)| { + let client_id = client_id + .ok_or(DecodingError::missing_raw_data("client ID"))? + .client_id; + let client_type = client_type + .ok_or(DecodingError::missing_raw_data("client type attribute"))? + .client_type; + let consensus_height = consensus_height + .ok_or(DecodingError::missing_raw_data( + "consensus height attribute", + ))? + .consensus_height; + let consensus_heights = consensus_heights + .ok_or(DecodingError::missing_raw_data( + "consensus heights attribute", + ))? + .consensus_heights; + let header = header + .ok_or(DecodingError::missing_raw_data("header attribute"))? + .header; + + Ok(UpdateClient::new( + client_id, + client_type, + consensus_height, + consensus_heights, + header, + )) + }, + ) + } +} +/// ClientMisbehaviour event signals the update of an on-chain client (IBC Client) with evidence of +/// misbehaviour. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ClientMisbehaviour { + client_id: ClientIdAttribute, + client_type: ClientTypeAttribute, +} + +impl ClientMisbehaviour { + pub fn new(client_id: ClientId, client_type: ClientType) -> Self { + Self { + client_id: ClientIdAttribute::from(client_id), + client_type: ClientTypeAttribute::from(client_type), + } + } + + pub fn client_id(&self) -> &ClientId { + &self.client_id.client_id + } + + pub fn client_type(&self) -> &ClientType { + &self.client_type.client_type + } + + pub fn event_type(&self) -> &str { + CLIENT_MISBEHAVIOUR_EVENT + } +} + +impl From for abci::Event { + fn from(c: ClientMisbehaviour) -> Self { + Self { + kind: CLIENT_MISBEHAVIOUR_EVENT.to_owned(), + attributes: vec![c.client_id.into(), c.client_type.into()], + } + } +} + +/// Signals a recent upgrade of an on-chain client (IBC Client). +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct UpgradeClient { + client_id: ClientIdAttribute, + client_type: ClientTypeAttribute, + consensus_height: ConsensusHeightAttribute, +} + +impl UpgradeClient { + pub fn new(client_id: ClientId, client_type: ClientType, consensus_height: Height) -> Self { + Self { + client_id: ClientIdAttribute::from(client_id), + client_type: ClientTypeAttribute::from(client_type), + consensus_height: ConsensusHeightAttribute::from(consensus_height), + } + } + + pub fn client_id(&self) -> &ClientId { + &self.client_id.client_id + } + + pub fn client_type(&self) -> &ClientType { + &self.client_type.client_type + } + + pub fn consensus_height(&self) -> &Height { + &self.consensus_height.consensus_height + } + + pub fn event_type(&self) -> &str { + UPGRADE_CLIENT_EVENT + } +} + +impl From for abci::Event { + fn from(u: UpgradeClient) -> Self { + Self { + kind: UPGRADE_CLIENT_EVENT.to_owned(), + attributes: vec![ + u.client_id.into(), + u.client_type.into(), + u.consensus_height.into(), + ], + } + } +} + +#[cfg(test)] +mod tests { + use core::any::Any; + + use rstest::*; + + use super::*; + + #[rstest] + #[case( + abci::Event { + kind: CREATE_CLIENT_EVENT.to_owned(), + attributes: vec![ + abci::EventAttribute::from(("client_id", "07-tendermint-0")), + abci::EventAttribute::from(("client_type", "07-tendermint")), + abci::EventAttribute::from(("consensus_height", "1-10")), + ], + }, + Ok(CreateClient::new( + ClientId::from_str("07-tendermint-0").expect("should parse"), + ClientType::from_str("07-tendermint").expect("should parse"), + Height::new(1, 10).unwrap(), + )), + )] + #[case( + abci::Event { + kind: "some_other_event".to_owned(), + attributes: vec![ + abci::EventAttribute::from(("client_id", "07-tendermint-0")), + abci::EventAttribute::from(("client_type", "07-tendermint")), + abci::EventAttribute::from(("consensus_height", "1-10")), + ], + }, + Err(DecodingError::MismatchedResourceName { + expected: "CreateClient".to_string(), + actual: "some_other_event".to_string(), + }) + )] + #[case( + abci::Event { + kind: CREATE_CLIENT_EVENT.to_owned(), + attributes: vec![ + abci::EventAttribute::from(("client_type", "07-tendermint")), + abci::EventAttribute::from(("consensus_height", "1-10")), + ], + }, + Err(DecodingError::missing_raw_data("attribute key")), + )] + fn test_create_client_try_from( + #[case] event: abci::Event, + #[case] expected: Result, + ) { + let result = CreateClient::try_from(event); + if expected.is_err() { + assert_eq!( + result.unwrap_err().type_id(), + expected.unwrap_err().type_id() + ); + } else { + assert_eq!(result.unwrap(), expected.unwrap()); + } + } + + #[rstest] + #[case( + abci::Event { + kind: UPDATE_CLIENT_EVENT.to_owned(), + attributes: vec![ + abci::EventAttribute::from(("client_id", "07-tendermint-0")), + abci::EventAttribute::from(("client_type", "07-tendermint")), + abci::EventAttribute::from(("consensus_height", "1-10")), + abci::EventAttribute::from(("consensus_heights", "1-10,1-11")), + abci::EventAttribute::from(("header", "1234")), + ], + }, + Ok(UpdateClient::new( + ClientId::from_str("07-tendermint-0").expect("should parse"), + ClientType::from_str("07-tendermint").expect("should parse"), + Height::new(1, 10).unwrap(), + vec![Height::new(1, 10).unwrap(), Height::new(1, 11).unwrap()], + vec![0x12, 0x34], + )), + )] + #[case( + abci::Event { + kind: "some_other_event".to_owned(), + attributes: vec![ + abci::EventAttribute::from(("client_id", "07-tendermint-0")), + abci::EventAttribute::from(("client_type", "07-tendermint")), + abci::EventAttribute::from(("consensus_height", "1-10")), + abci::EventAttribute::from(("consensus_heights", "1-10,1-11")), + abci::EventAttribute::from(("header", "1234")), + ], + }, + Err(DecodingError::MismatchedResourceName { + expected: UPDATE_CLIENT_EVENT.to_string(), + actual: "some_other_event".to_owned(), + }), + )] + #[case( + abci::Event { + kind: UPDATE_CLIENT_EVENT.to_owned(), + attributes: vec![ + abci::EventAttribute::from(("client_type", "07-tendermint")), + abci::EventAttribute::from(("consensus_height", "1-10")), + abci::EventAttribute::from(("consensus_heights", "1-10,1-11")), + abci::EventAttribute::from(("header", "1234")), + ], + }, + Err(DecodingError::missing_raw_data("attribute key")), + )] + fn test_update_client_try_from( + #[case] event: abci::Event, + #[case] expected: Result, + ) { + let result = UpdateClient::try_from(event); + if expected.is_err() { + assert_eq!( + result.unwrap_err().type_id(), + expected.unwrap_err().type_id() + ); + } else { + assert_eq!(result.unwrap(), expected.unwrap()); + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/height.rs b/ibc-eureka-core/ics02-client/types/src/height.rs new file mode 100644 index 0000000000..612351863f --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/height.rs @@ -0,0 +1,214 @@ +//! Defines the core `Height` type used throughout the library + +use core::cmp::Ordering; +use core::str::FromStr; + +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; + +use crate::error::ClientError; + +/// The core IBC height type, which represents the height of a chain, +/// which typically is the number of blocks since genesis +/// (or more generally, since the last revision/hard upgrade). +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Height { + /// Previously known as "epoch" + revision_number: u64, + + /// The height of a block + revision_height: u64, +} + +impl Height { + pub fn new(revision_number: u64, revision_height: u64) -> Result { + if revision_height == 0 { + return Err(ClientError::InvalidHeight); + } + + Ok(Self { + revision_number, + revision_height, + }) + } + + pub fn min(revision_number: u64) -> Self { + Self { + revision_number, + revision_height: 1, + } + } + + pub fn revision_number(&self) -> u64 { + self.revision_number + } + + pub fn revision_height(&self) -> u64 { + self.revision_height + } + + pub fn add(&self, delta: u64) -> Height { + Height { + revision_number: self.revision_number, + revision_height: self.revision_height + delta, + } + } + + pub fn increment(&self) -> Height { + self.add(1) + } + + pub fn sub(&self, delta: u64) -> Result { + if self.revision_height <= delta { + return Err(ClientError::InvalidHeight); + } + + Ok(Height { + revision_number: self.revision_number, + revision_height: self.revision_height - delta, + }) + } + + pub fn decrement(&self) -> Result { + self.sub(1) + } +} + +impl PartialOrd for Height { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Height { + fn cmp(&self, other: &Self) -> Ordering { + if self.revision_number < other.revision_number { + Ordering::Less + } else if self.revision_number > other.revision_number { + Ordering::Greater + } else if self.revision_height < other.revision_height { + Ordering::Less + } else if self.revision_height > other.revision_height { + Ordering::Greater + } else { + Ordering::Equal + } + } +} + +impl Protobuf for Height {} + +impl TryFrom for Height { + 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")) + } +} + +impl From for RawHeight { + fn from(ics_height: Height) -> Self { + RawHeight { + revision_number: ics_height.revision_number, + revision_height: ics_height.revision_height, + } + } +} + +impl core::fmt::Debug for Height { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + f.debug_struct("Height") + .field("revision", &self.revision_number) + .field("height", &self.revision_height) + .finish() + } +} + +/// Custom debug output to omit the packet data +impl core::fmt::Display for Height { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!(f, "{}-{}", self.revision_number, self.revision_height) + } +} + +impl TryFrom<&str> for Height { + type Error = DecodingError; + + fn try_from(value: &str) -> Result { + 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")) + } +} + +impl From for String { + fn from(height: Height) -> Self { + format!("{}-{}", height.revision_number, height.revision_height) + } +} + +impl FromStr for Height { + type Err = DecodingError; + + fn from_str(s: &str) -> Result { + Height::try_from(s) + } +} + +#[test] +fn test_valid_height() { + assert_eq!( + "1-1".parse::().unwrap(), + Height { + revision_number: 1, + revision_height: 1 + } + ); + assert_eq!( + "1-10".parse::().unwrap(), + Height { + revision_number: 1, + revision_height: 10 + } + ); +} + +#[test] +fn test_invalid_height() { + 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()); + + 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-eureka-core/ics02-client/types/src/lib.rs b/ibc-eureka-core/ics02-client/types/src/lib.rs new file mode 100644 index 0000000000..f7c1f95933 --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/lib.rs @@ -0,0 +1,29 @@ +//! Implementation of the Client Semantics (ICS-02) data structures. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod error; +pub mod events; +mod height; +pub mod msgs; +mod status; + +pub use height::*; +pub use status::*; + +/// Re-exports ICS-02 proto types from the `ibc-proto` crate for added convenience. +pub mod proto { + pub use ibc_proto::ibc::core::client::*; +} diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs new file mode 100644 index 0000000000..1891a143e9 --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs @@ -0,0 +1,65 @@ +//! 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; + +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. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgCreateClient { + pub client_state: Any, + pub consensus_state: Any, + pub signer: Signer, +} + +impl MsgCreateClient { + pub fn new(client_state: Any, consensus_state: Any, signer: Signer) -> Self { + MsgCreateClient { + client_state, + consensus_state, + signer, + } + } +} + +impl Protobuf for MsgCreateClient {} + +impl TryFrom for MsgCreateClient { + type Error = DecodingError; + + fn try_from(raw: RawMsgCreateClient) -> Result { + let raw_client_state = raw + .client_state + .ok_or(DecodingError::missing_raw_data("client state"))?; + + let raw_consensus_state = raw + .consensus_state + .ok_or(DecodingError::missing_raw_data("consensus state"))?; + + Ok(MsgCreateClient::new( + raw_client_state, + raw_consensus_state, + raw.signer.into(), + )) + } +} + +impl From for RawMsgCreateClient { + fn from(ics_msg: MsgCreateClient) -> Self { + RawMsgCreateClient { + client_state: Some(ics_msg.client_state), + consensus_state: Some(ics_msg.consensus_state), + signer: ics_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs b/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs new file mode 100644 index 0000000000..1e495393bb --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs @@ -0,0 +1,62 @@ +//! 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; +use ibc_proto::google::protobuf::Any as ProtoAny; +use ibc_proto::ibc::core::client::v1::MsgSubmitMisbehaviour as RawMsgSubmitMisbehaviour; +use ibc_proto::Protobuf; + +pub const SUBMIT_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.core.client.v1.MsgSubmitMisbehaviour"; + +/// A type of message that submits client misbehaviour proof. +/// +/// Deprecated since v0.51.0. Misbehaviour reports should be submitted via the `MsgUpdateClient` +/// type through its `client_message` field. +#[deprecated( + since = "0.51.0", + note = "Misbehaviour reports should be submitted via `MsgUpdateClient` through its `client_message` field" +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgSubmitMisbehaviour { + /// client unique identifier + pub client_id: ClientId, + /// misbehaviour, used for freezing the light client + pub misbehaviour: ProtoAny, + /// signer address + pub signer: Signer, +} + +impl Protobuf for MsgSubmitMisbehaviour {} + +impl TryFrom for MsgSubmitMisbehaviour { + type Error = DecodingError; + + fn try_from(raw: RawMsgSubmitMisbehaviour) -> Result { + let raw_misbehaviour = raw + .misbehaviour + .ok_or(DecodingError::missing_raw_data("msg submit misbehaviour"))?; + + Ok(MsgSubmitMisbehaviour { + client_id: raw.client_id.parse()?, + misbehaviour: raw_misbehaviour, + signer: raw.signer.into(), + }) + } +} + +impl From for RawMsgSubmitMisbehaviour { + fn from(ics_msg: MsgSubmitMisbehaviour) -> Self { + RawMsgSubmitMisbehaviour { + client_id: ics_msg.client_id.to_string(), + misbehaviour: Some(ics_msg.misbehaviour), + signer: ics_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs b/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs new file mode 100644 index 0000000000..fbb76a8211 --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs @@ -0,0 +1,64 @@ +#![allow(deprecated)] + +//! Defines the client message types that are sent to the chain by the relayer. + +use ibc_core_host_types::identifiers::ClientId; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::google::protobuf::Any; + +mod create_client; +mod misbehaviour; +mod recover_client; +mod update_client; +mod upgrade_client; + +pub use create_client::*; +pub use misbehaviour::*; +pub use recover_client::*; +pub use update_client::*; +pub use upgrade_client::*; + +/// Encodes all the different client messages +#[allow(dead_code)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, derive_more::From)] +pub enum ClientMsg { + CreateClient(MsgCreateClient), + UpdateClient(MsgUpdateClient), + Misbehaviour(MsgSubmitMisbehaviour), + UpgradeClient(MsgUpgradeClient), + RecoverClient(MsgRecoverClient), +} + +pub enum MsgUpdateOrMisbehaviour { + UpdateClient(MsgUpdateClient), + Misbehaviour(MsgSubmitMisbehaviour), +} + +impl MsgUpdateOrMisbehaviour { + pub fn client_id(&self) -> &ClientId { + match self { + MsgUpdateOrMisbehaviour::UpdateClient(msg) => &msg.client_id, + MsgUpdateOrMisbehaviour::Misbehaviour(msg) => &msg.client_id, + } + } + + pub fn client_message(self) -> Any { + match self { + MsgUpdateOrMisbehaviour::UpdateClient(msg) => msg.client_message, + MsgUpdateOrMisbehaviour::Misbehaviour(msg) => msg.misbehaviour, + } + } + + pub fn signer(&self) -> &Signer { + match self { + MsgUpdateOrMisbehaviour::UpdateClient(msg) => &msg.signer, + MsgUpdateOrMisbehaviour::Misbehaviour(msg) => &msg.signer, + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs new file mode 100644 index 0000000000..5ef857cbaf --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs @@ -0,0 +1,59 @@ +//! 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; + +pub const RECOVER_CLIENT_TYPE_URL: &str = "/ibc.core.client.v1.MsgRecoverClient"; + +/// Defines the message used to recover a frozen or expired client. +/// +/// Note that a frozen or expired client can only be recovered by passing +/// a governance proposal. For this reason, ibc-rs does not export dispatching +/// a `MsgRecoverClient` via the `dispatch` function. In other words, the +/// client recovery functionality is not part of ibc-rs's public API. The +/// intended usage of this message type is to be integrated with hosts' +/// governance modules, not to be called directly via `dispatch`. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgRecoverClient { + /// Client identifier of the client to be updated if the proposal passes. + pub subject_client_id: ClientId, + /// Client identifier of the client that will replace the subject client + /// if the proposal passes. + pub substitute_client_id: ClientId, + /// The address of the signer who serves as the authority for the IBC + /// module. + pub signer: Signer, +} + +impl Protobuf for MsgRecoverClient {} + +impl TryFrom for MsgRecoverClient { + type Error = DecodingError; + + fn try_from(raw: RawMsgRecoverClient) -> Result { + Ok(MsgRecoverClient { + subject_client_id: raw.subject_client_id.parse()?, + substitute_client_id: raw.substitute_client_id.parse()?, + signer: raw.signer.into(), + }) + } +} + +impl From for RawMsgRecoverClient { + fn from(ics_msg: MsgRecoverClient) -> Self { + RawMsgRecoverClient { + subject_client_id: ics_msg.subject_client_id.to_string(), + substitute_client_id: ics_msg.substitute_client_id.to_string(), + signer: ics_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs new file mode 100644 index 0000000000..16a5d250d8 --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs @@ -0,0 +1,53 @@ +//! 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; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; +use ibc_proto::Protobuf; + +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 +/// either with new headers, or evidence of misbehaviour. +/// Note that some types of misbehaviour can be detected when the headers +/// are updated (`UpdateKind::UpdateClient`). +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgUpdateClient { + pub client_id: ClientId, + pub client_message: Any, + pub signer: Signer, +} + +impl Protobuf for MsgUpdateClient {} + +impl TryFrom for MsgUpdateClient { + type Error = DecodingError; + + fn try_from(raw: RawMsgUpdateClient) -> Result { + Ok(MsgUpdateClient { + 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(), + }) + } +} + +impl From for RawMsgUpdateClient { + fn from(ics_msg: MsgUpdateClient) -> Self { + RawMsgUpdateClient { + client_id: ics_msg.client_id.to_string(), + client_message: Some(ics_msg.client_message), + signer: ics_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs new file mode 100644 index 0000000000..d581f8e1bc --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs @@ -0,0 +1,81 @@ +//! Definition of domain type msg `MsgUpgradeClient`. + +use core::str::FromStr; + +use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::ClientId; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; +use ibc_proto::Protobuf; + +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. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgUpgradeClient { + // client unique identifier + pub client_id: ClientId, + // Upgraded client state + pub upgraded_client_state: Any, + // Upgraded consensus state, only contains enough information + // to serve as a basis of trust in update logic + pub upgraded_consensus_state: Any, + // proof that old chain committed to new client + pub proof_upgrade_client: CommitmentProofBytes, + // proof that old chain committed to new consensus state + pub proof_upgrade_consensus_state: CommitmentProofBytes, + // signer address + pub signer: Signer, +} + +impl Protobuf for MsgUpgradeClient {} + +impl From for RawMsgUpgradeClient { + fn from(dm_msg: MsgUpgradeClient) -> RawMsgUpgradeClient { + RawMsgUpgradeClient { + client_id: dm_msg.client_id.to_string(), + client_state: Some(dm_msg.upgraded_client_state), + consensus_state: Some(dm_msg.upgraded_consensus_state), + proof_upgrade_client: dm_msg.proof_upgrade_client.into(), + proof_upgrade_consensus_state: dm_msg.proof_upgrade_consensus_state.into(), + signer: dm_msg.signer.to_string(), + } + } +} + +impl TryFrom for MsgUpgradeClient { + type Error = DecodingError; + + fn try_from(proto_msg: RawMsgUpgradeClient) -> Result { + let raw_client_state = proto_msg + .client_state + .ok_or(DecodingError::missing_raw_data("msg upgrade client state"))?; + + 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)?; + let cs_bytes = CommitmentProofBytes::try_from(proto_msg.proof_upgrade_consensus_state)?; + + Ok(MsgUpgradeClient { + 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, + proof_upgrade_consensus_state: cs_bytes, + signer: proto_msg.signer.into(), + }) + } +} diff --git a/ibc-eureka-core/ics02-client/types/src/status.rs b/ibc-eureka-core/ics02-client/types/src/status.rs new file mode 100644 index 0000000000..39e4f36f2b --- /dev/null +++ b/ibc-eureka-core/ics02-client/types/src/status.rs @@ -0,0 +1,87 @@ +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; + +/// `UpdateKind` represents the 2 ways that a client can be updated +/// in IBC: either through a `MsgUpdateClient`, or a `MsgSubmitMisbehaviour`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UpdateKind { + /// this is the typical scenario where a new header is submitted to the client + /// to update the client. Note that light clients are free to define the type + /// of the object used to update them (e.g. could be a list of headers). + UpdateClient, + /// this is the scenario where misbehaviour is submitted to the client + /// (e.g 2 headers with the same height in Tendermint) + SubmitMisbehaviour, +} + +/// Represents the status of a client +#[derive(Clone, Debug, PartialEq, Eq, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub enum Status { + /// The client is active and allowed to be used + Active, + /// The client is frozen and not allowed to be used + Frozen, + /// The client is expired and not allowed to be used + Expired, + /// Unauthorized indicates that the client type is not registered as an allowed client type. + Unauthorized, +} + +impl Status { + pub fn is_active(&self) -> bool { + *self == Status::Active + } + + pub fn is_frozen(&self) -> bool { + *self == Status::Frozen + } + + pub fn is_expired(&self) -> bool { + *self == Status::Expired + } + + /// Checks whether the status is active; returns `Err` if not. + pub fn verify_is_active(&self) -> Result<(), ClientError> { + match self { + Self::Active => Ok(()), + &status => Err(ClientError::InvalidStatus(status)), + } + } + + /// Checks whether the client is either frozen or expired; returns `Err` if not. + pub fn verify_is_inactive(&self) -> Result<(), ClientError> { + match self { + Self::Frozen | Self::Expired => Ok(()), + &status => Err(ClientError::InvalidStatus(status)), + } + } +} + +impl Display for Status { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{self:?}") + } +} + +impl FromStr for Status { + type Err = DecodingError; + + fn from_str(s: &str) -> Result { + match s { + "ACTIVE" => Ok(Status::Active), + "FROZEN" => Ok(Status::Frozen), + "EXPIRED" => Ok(Status::Expired), + "UNAUTHORIZED" => Ok(Status::Unauthorized), + _ => Err(DecodingError::invalid_raw_data(format!( + "invalid status {s}", + ))), + } + } +} diff --git a/ibc-eureka-core/ics03-connection/Cargo.toml b/ibc-eureka-core/ics03-connection/Cargo.toml new file mode 100644 index 0000000000..a6b19b7ab0 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "ibc-core-connection" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "connection" ] +readme = "./../README.md" + +description = """ + Maintained by `ibc-rs`, contains the implementation of the ICS-03 Connection Semantics and + re-exports essential data structures and domain types from `ibc-core-connection-types` crate. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ibc-core-client = { workspace = true } +ibc-core-connection-types = { workspace = true } +ibc-core-host = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-client-wasm-types = { workspace = true, optional = true } + +prost = { workspace = true, optional = true } + +[features] +default = [ "std" ] +std = [ + "ibc-core-client/std", + "ibc-core-connection-types/std", + "ibc-core-host/std", + "ibc-core-handler-types/std", + "ibc-primitives/std", + "wasm-client", +] +serde = [ + "ibc-core-client/serde", + "ibc-core-connection-types/serde", + "ibc-core-host/serde", + "ibc-core-handler-types/serde", + "ibc-primitives/serde", +] +schema = [ + "ibc-core-client/schema", + "ibc-core-connection-types/schema", + "ibc-core-host/schema", + "ibc-core-handler-types/schema", + "ibc-primitives/schema", + "serde", + "std", +] +borsh = [ + "ibc-core-client/borsh", + "ibc-core-connection-types/borsh", + "ibc-core-host/borsh", + "ibc-core-handler-types/borsh", + "ibc-primitives/borsh", +] +parity-scale-codec = [ + "ibc-core-client/parity-scale-codec", + "ibc-core-connection-types/parity-scale-codec", + "ibc-core-host/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] +wasm-client = [ + "dep:ibc-client-wasm-types", + "dep:prost", +] diff --git a/ibc-eureka-core/ics03-connection/src/delay.rs b/ibc-eureka-core/ics03-connection/src/delay.rs new file mode 100644 index 0000000000..b0f427b22b --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/delay.rs @@ -0,0 +1,48 @@ +use ibc_core_client::context::ClientValidationContext; +use ibc_core_client::types::Height; +use ibc_core_connection_types::error::ConnectionError; +use ibc_core_connection_types::ConnectionEnd; +use ibc_core_host::ValidationContext; + +pub fn verify_conn_delay_passed( + ctx: &Ctx, + packet_proof_height: Height, + connection_end: &ConnectionEnd, +) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, +{ + // Fetch the current host chain time and height. + let current_host_time = ctx.host_timestamp()?; + let current_host_height = ctx.host_height()?; + + // Fetch the latest time and height that the counterparty client was updated on the host chain. + let client_id = connection_end.client_id(); + let last_client_update = ctx + .get_client_validation_context() + .client_update_meta(client_id, &packet_proof_height)?; + + // Fetch the connection delay time and height periods. + let conn_delay_time_period = connection_end.delay_period(); + let conn_delay_height_period = ctx.block_delay(&conn_delay_time_period); + + // Verify that the current host chain time is later than the last client update time + let earliest_valid_time = (last_client_update.0 + conn_delay_time_period)?; + if current_host_time < earliest_valid_time { + return Err(ConnectionError::InsufficientTimeElapsed { + current_host_time, + earliest_valid_time, + }); + } + + // Verify that the current host chain height is later than the last client update height + let earliest_valid_height = last_client_update.1.add(conn_delay_height_period); + if current_host_height < earliest_valid_height { + return Err(ConnectionError::InsufficientBlocksElapsed { + current_host_height, + earliest_valid_height, + }); + }; + + Ok(()) +} diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs new file mode 100644 index 0000000000..a91a16c761 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs @@ -0,0 +1,204 @@ +//! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenAck`. + +use ibc_core_client::context::prelude::*; +use ibc_core_client::types::error::ClientError; +use ibc_core_connection_types::error::ConnectionError; +use ibc_core_connection_types::events::OpenAck; +use ibc_core_connection_types::msgs::MsgConnectionOpenAck; +use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::identifiers::ClientId; +use ibc_core_host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::{Any, Protobuf}; +use ibc_primitives::ToVec; + +use crate::handler::{pack_host_consensus_state, unpack_host_client_state}; + +pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenAck) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, + >::Error: Into, +{ + let vars = LocalVars::new(ctx_a, &msg)?; + validate_impl(ctx_a, &msg, &vars) +} + +fn validate_impl( + ctx_a: &Ctx, + msg: &MsgConnectionOpenAck, + vars: &LocalVars, +) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, + >::Error: Into, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let host_height = ctx_a.host_height()?; + + if msg.consensus_height_of_a_on_b > host_height { + return Err(ConnectionError::InsufficientConsensusHeight { + target_height: msg.consensus_height_of_a_on_b, + current_height: host_height, + }); + } + + let client_val_ctx_a = ctx_a.get_client_validation_context(); + + let client_state_of_a_on_b = unpack_host_client_state::( + msg.client_state_of_a_on_b.clone(), + vars.client_id_on_b(), + )?; + + ctx_a.validate_self_client(client_state_of_a_on_b)?; + + msg.version + .verify_is_supported(vars.conn_end_on_a.versions())?; + + vars.conn_end_on_a.verify_state_matches(&State::Init)?; + + // Proof verification. + { + let client_state_of_b_on_a = client_val_ctx_a.client_state(vars.client_id_on_a())?; + + client_state_of_b_on_a + .status(client_val_ctx_a, vars.client_id_on_a())? + .verify_is_active()?; + + client_state_of_b_on_a.validate_proof_height(msg.proofs_height_on_b)?; + + let client_cons_state_path_on_a = ClientConsensusStatePath::new( + vars.client_id_on_a().clone(), + msg.proofs_height_on_b.revision_number(), + msg.proofs_height_on_b.revision_height(), + ); + + let consensus_state_of_b_on_a = + client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; + + let prefix_on_a = ctx_a.commitment_prefix(); + let prefix_on_b = vars.conn_end_on_a.counterparty().prefix(); + + { + let expected_conn_end_on_b = ConnectionEnd::new( + State::TryOpen, + vars.client_id_on_b().clone(), + Counterparty::new( + vars.client_id_on_a().clone(), + Some(msg.conn_id_on_a.clone()), + prefix_on_a, + ), + vec![msg.version.clone()], + vars.conn_end_on_a.delay_period(), + )?; + + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_conn_end_on_b, + consensus_state_of_b_on_a.root(), + Path::Connection(ConnectionPath::new(&msg.conn_id_on_b)), + expected_conn_end_on_b.encode_vec(), + )?; + } + + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_client_state_of_a_on_b, + consensus_state_of_b_on_a.root(), + Path::ClientState(ClientStatePath::new(vars.client_id_on_b().clone())), + msg.client_state_of_a_on_b.to_vec(), + )?; + + let expected_consensus_state_of_a_on_b = + ctx_a.host_consensus_state(&msg.consensus_height_of_a_on_b)?; + + let stored_consensus_state_of_a_on_b = + pack_host_consensus_state(expected_consensus_state_of_a_on_b, vars.client_id_on_b()); + + let client_cons_state_path_on_b = ClientConsensusStatePath::new( + vars.client_id_on_b().clone(), + msg.consensus_height_of_a_on_b.revision_number(), + msg.consensus_height_of_a_on_b.revision_height(), + ); + + client_state_of_b_on_a.verify_membership( + prefix_on_b, + &msg.proof_consensus_state_of_a_on_b, + consensus_state_of_b_on_a.root(), + Path::ClientConsensusState(client_cons_state_path_on_b), + stored_consensus_state_of_a_on_b.to_vec(), + )?; + } + + Ok(()) +} + +pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenAck) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let vars = LocalVars::new(ctx_a, &msg)?; + execute_impl(ctx_a, msg, vars) +} + +fn execute_impl( + ctx_a: &mut Ctx, + msg: MsgConnectionOpenAck, + vars: LocalVars, +) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let event = IbcEvent::OpenAckConnection(OpenAck::new( + msg.conn_id_on_a.clone(), + vars.client_id_on_a().clone(), + msg.conn_id_on_b.clone(), + vars.client_id_on_b().clone(), + )); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; + ctx_a.emit_ibc_event(event)?; + + ctx_a.log_message("success: conn_open_ack verification passed".to_string())?; + + { + let new_conn_end_on_a = { + let mut counterparty = vars.conn_end_on_a.counterparty().clone(); + counterparty.connection_id = Some(msg.conn_id_on_b.clone()); + + let mut new_conn_end_on_a = vars.conn_end_on_a; + new_conn_end_on_a.set_state(State::Open); + new_conn_end_on_a.set_version(msg.version.clone()); + new_conn_end_on_a.set_counterparty(counterparty); + new_conn_end_on_a + }; + + ctx_a.store_connection(&ConnectionPath::new(&msg.conn_id_on_a), new_conn_end_on_a)?; + } + + Ok(()) +} + +struct LocalVars { + conn_end_on_a: ConnectionEnd, +} + +impl LocalVars { + fn new(ctx_a: &Ctx, msg: &MsgConnectionOpenAck) -> Result + where + Ctx: ValidationContext, + { + Ok(LocalVars { + conn_end_on_a: ctx_a.connection_end(&msg.conn_id_on_a)?, + }) + } + + fn client_id_on_a(&self) -> &ClientId { + self.conn_end_on_a.client_id() + } + + fn client_id_on_b(&self) -> &ClientId { + self.conn_end_on_a.counterparty().client_id() + } +} diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs new file mode 100644 index 0000000000..ba934493d0 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs @@ -0,0 +1,164 @@ +//! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenConfirm`. + +use ibc_core_client::context::prelude::*; +use ibc_core_connection_types::error::ConnectionError; +use ibc_core_connection_types::events::OpenConfirm; +use ibc_core_connection_types::msgs::MsgConnectionOpenConfirm; +use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; +use ibc_core_host::types::path::{ClientConsensusStatePath, ConnectionPath, Path}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Protobuf; + +pub fn validate(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, +{ + let vars = LocalVars::new(ctx_b, msg)?; + validate_impl(ctx_b, msg, &vars) +} + +fn validate_impl( + ctx_b: &Ctx, + msg: &MsgConnectionOpenConfirm, + vars: &LocalVars, +) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, +{ + ctx_b.validate_message_signer(&msg.signer)?; + + let conn_end_on_b = vars.conn_end_on_b(); + + conn_end_on_b.verify_state_matches(&State::TryOpen)?; + + let client_id_on_a = vars.client_id_on_a(); + let client_id_on_b = vars.client_id_on_b(); + let conn_id_on_a = vars.conn_id_on_a()?; + + // Verify proofs + { + let client_val_ctx_b = ctx_b.get_client_validation_context(); + + let client_state_of_a_on_b = client_val_ctx_b.client_state(client_id_on_b)?; + + client_state_of_a_on_b + .status(client_val_ctx_b, client_id_on_b)? + .verify_is_active()?; + + client_state_of_a_on_b.validate_proof_height(msg.proof_height_on_a)?; + + let client_cons_state_path_on_b = ClientConsensusStatePath::new( + client_id_on_b.clone(), + msg.proof_height_on_a.revision_number(), + msg.proof_height_on_a.revision_height(), + ); + let consensus_state_of_a_on_b = + client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; + + let prefix_on_a = conn_end_on_b.counterparty().prefix(); + let prefix_on_b = ctx_b.commitment_prefix(); + + let expected_conn_end_on_a = ConnectionEnd::new( + State::Open, + client_id_on_a.clone(), + Counterparty::new( + client_id_on_b.clone(), + Some(msg.conn_id_on_b.clone()), + prefix_on_b, + ), + conn_end_on_b.versions().to_vec(), + conn_end_on_b.delay_period(), + )?; + + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_conn_end_on_a, + consensus_state_of_a_on_b.root(), + Path::Connection(ConnectionPath::new(conn_id_on_a)), + expected_conn_end_on_a.encode_vec(), + )?; + } + + Ok(()) +} + +pub fn execute(ctx_b: &mut Ctx, msg: &MsgConnectionOpenConfirm) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let vars = LocalVars::new(ctx_b, msg)?; + execute_impl(ctx_b, msg, vars) +} + +fn execute_impl( + ctx_b: &mut Ctx, + msg: &MsgConnectionOpenConfirm, + vars: LocalVars, +) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let client_id_on_a = vars.client_id_on_a(); + let client_id_on_b = vars.client_id_on_b(); + let conn_id_on_a = vars.conn_id_on_a()?; + + let event = IbcEvent::OpenConfirmConnection(OpenConfirm::new( + msg.conn_id_on_b.clone(), + client_id_on_b.clone(), + conn_id_on_a.clone(), + client_id_on_a.clone(), + )); + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; + ctx_b.emit_ibc_event(event)?; + ctx_b.log_message("success: conn_open_confirm verification passed".to_string())?; + + { + let new_conn_end_on_b = { + let mut new_conn_end_on_b = vars.conn_end_on_b; + + new_conn_end_on_b.set_state(State::Open); + new_conn_end_on_b + }; + + ctx_b.store_connection(&ConnectionPath(msg.conn_id_on_b.clone()), new_conn_end_on_b)?; + } + + Ok(()) +} + +struct LocalVars { + conn_end_on_b: ConnectionEnd, +} + +impl LocalVars { + fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenConfirm) -> Result + where + Ctx: ValidationContext, + { + Ok(Self { + conn_end_on_b: ctx_b.connection_end(&msg.conn_id_on_b)?, + }) + } + + fn conn_end_on_b(&self) -> &ConnectionEnd { + &self.conn_end_on_b + } + + fn client_id_on_a(&self) -> &ClientId { + self.conn_end_on_b.counterparty().client_id() + } + + fn client_id_on_b(&self) -> &ClientId { + self.conn_end_on_b.client_id() + } + + fn conn_id_on_a(&self) -> Result<&ConnectionId, ConnectionError> { + self.conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::InvalidCounterparty) + } +} diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs new file mode 100644 index 0000000000..267603a7fb --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs @@ -0,0 +1,85 @@ +//! Protocol logic specific to ICS3 messages of type `MsgConnectionOpenInit`. +use ibc_core_client::context::prelude::*; +use ibc_core_connection_types::error::ConnectionError; +use ibc_core_connection_types::events::OpenInit; +use ibc_core_connection_types::msgs::MsgConnectionOpenInit; +use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::identifiers::ConnectionId; +use ibc_core_host::types::path::{ClientConnectionPath, ConnectionPath}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_primitives::prelude::*; + +pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let client_val_ctx_a = ctx_a.get_client_validation_context(); + + // An IBC client running on the local (host) chain should exist. + let client_state_of_b_on_a = client_val_ctx_a.client_state(&msg.client_id_on_a)?; + + client_state_of_b_on_a + .status(client_val_ctx_a, &msg.client_id_on_a)? + .verify_is_active()?; + + if let Some(version) = msg.version { + version.verify_is_supported(&ctx_a.get_compatible_versions())?; + } + + Ok(()) +} + +pub fn execute(ctx_a: &mut Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let versions = if let Some(version) = msg.version { + version.verify_is_supported(&ctx_a.get_compatible_versions())?; + vec![version] + } else { + ctx_a.get_compatible_versions() + }; + + let conn_end_on_a = ConnectionEnd::new( + State::Init, + msg.client_id_on_a.clone(), + Counterparty::new( + msg.counterparty.client_id().clone(), + None, + msg.counterparty.prefix().clone(), + ), + versions, + msg.delay_period, + )?; + + // Construct the identifier for the new connection. + let conn_id_on_a = ConnectionId::new(ctx_a.connection_counter()?); + + ctx_a.log_message(format!( + "success: conn_open_init: generated new connection identifier: {conn_id_on_a}" + ))?; + + { + let client_id_on_b = msg.counterparty.client_id().clone(); + + let event = IbcEvent::OpenInitConnection(OpenInit::new( + conn_id_on_a.clone(), + msg.client_id_on_a.clone(), + client_id_on_b, + )); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; + ctx_a.emit_ibc_event(event)?; + } + + ctx_a.increase_connection_counter()?; + ctx_a.store_connection_to_client( + &ClientConnectionPath::new(msg.client_id_on_a), + conn_id_on_a.clone(), + )?; + ctx_a.store_connection(&ConnectionPath::new(&conn_id_on_a), conn_end_on_a)?; + + Ok(()) +} diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs new file mode 100644 index 0000000000..2b47e488d7 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs @@ -0,0 +1,206 @@ +//! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenTry`.; +use ibc_core_client::context::prelude::*; +use ibc_core_client::types::error::ClientError; +use ibc_core_connection_types::error::ConnectionError; +use ibc_core_connection_types::events::OpenTry; +use ibc_core_connection_types::msgs::MsgConnectionOpenTry; +use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; +use ibc_core_host::types::path::{ + ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path, +}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::{Any, Protobuf}; +use ibc_primitives::ToVec; + +use crate::handler::{pack_host_consensus_state, unpack_host_client_state}; + +pub fn validate(ctx_b: &Ctx, msg: MsgConnectionOpenTry) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, + >::Error: Into, +{ + let vars = LocalVars::new(ctx_b, &msg)?; + validate_impl(ctx_b, &msg, &vars) +} + +fn validate_impl( + ctx_b: &Ctx, + msg: &MsgConnectionOpenTry, + vars: &LocalVars, +) -> Result<(), ConnectionError> +where + Ctx: ValidationContext, + >::Error: Into, +{ + ctx_b.validate_message_signer(&msg.signer)?; + + let client_val_ctx_b = ctx_b.get_client_validation_context(); + + let client_state_of_b_on_a = unpack_host_client_state::( + msg.client_state_of_b_on_a.clone(), + &vars.client_id_on_a, + )?; + + ctx_b.validate_self_client(client_state_of_b_on_a)?; + + let host_height = ctx_b.host_height()?; + + if msg.consensus_height_of_b_on_a > host_height { + // Fail if the consensus height is too advanced. + return Err(ConnectionError::InsufficientConsensusHeight { + target_height: msg.consensus_height_of_b_on_a, + current_height: host_height, + }); + } + + let client_id_on_a = msg.counterparty.client_id(); + + // Verify proofs + { + let client_state_of_a_on_b = + client_val_ctx_b.client_state(vars.conn_end_on_b.client_id())?; + + client_state_of_a_on_b + .status(client_val_ctx_b, &msg.client_id_on_b)? + .verify_is_active()?; + + client_state_of_a_on_b.validate_proof_height(msg.proofs_height_on_a)?; + + let client_cons_state_path_on_b = ClientConsensusStatePath::new( + msg.client_id_on_b.clone(), + msg.proofs_height_on_a.revision_number(), + msg.proofs_height_on_a.revision_height(), + ); + + let consensus_state_of_a_on_b = + client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; + + let prefix_on_a = vars.conn_end_on_b.counterparty().prefix(); + let prefix_on_b = ctx_b.commitment_prefix(); + + { + let expected_conn_end_on_a = ConnectionEnd::new( + State::Init, + client_id_on_a.clone(), + Counterparty::new(msg.client_id_on_b.clone(), None, prefix_on_b), + msg.versions_on_a.clone(), + msg.delay_period, + )?; + + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_conn_end_on_a, + consensus_state_of_a_on_b.root(), + Path::Connection(ConnectionPath::new(&vars.conn_id_on_a)), + expected_conn_end_on_a.encode_vec(), + )?; + } + + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_client_state_of_b_on_a, + consensus_state_of_a_on_b.root(), + Path::ClientState(ClientStatePath::new(client_id_on_a.clone())), + msg.client_state_of_b_on_a.to_vec(), + )?; + + let expected_consensus_state_of_b_on_a = + ctx_b.host_consensus_state(&msg.consensus_height_of_b_on_a)?; + + let stored_consensus_state_of_b_on_a = + pack_host_consensus_state(expected_consensus_state_of_b_on_a, &vars.client_id_on_a); + + let client_cons_state_path_on_a = ClientConsensusStatePath::new( + client_id_on_a.clone(), + msg.consensus_height_of_b_on_a.revision_number(), + msg.consensus_height_of_b_on_a.revision_height(), + ); + + client_state_of_a_on_b.verify_membership( + prefix_on_a, + &msg.proof_consensus_state_of_b_on_a, + consensus_state_of_a_on_b.root(), + Path::ClientConsensusState(client_cons_state_path_on_a), + stored_consensus_state_of_b_on_a.to_vec(), + )?; + } + + Ok(()) +} + +pub fn execute(ctx_b: &mut Ctx, msg: MsgConnectionOpenTry) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let vars = LocalVars::new(ctx_b, &msg)?; + execute_impl(ctx_b, msg, vars) +} + +fn execute_impl( + ctx_b: &mut Ctx, + msg: MsgConnectionOpenTry, + vars: LocalVars, +) -> Result<(), ConnectionError> +where + Ctx: ExecutionContext, +{ + let conn_id_on_a = vars + .conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::InvalidCounterparty)?; + let event = IbcEvent::OpenTryConnection(OpenTry::new( + vars.conn_id_on_b.clone(), + msg.client_id_on_b.clone(), + conn_id_on_a.clone(), + vars.client_id_on_a.clone(), + )); + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Connection))?; + ctx_b.emit_ibc_event(event)?; + ctx_b.log_message("success: conn_open_try verification passed".to_string())?; + + ctx_b.increase_connection_counter()?; + ctx_b.store_connection_to_client( + &ClientConnectionPath::new(msg.client_id_on_b), + vars.conn_id_on_b.clone(), + )?; + ctx_b.store_connection(&ConnectionPath::new(&vars.conn_id_on_b), vars.conn_end_on_b)?; + + Ok(()) +} + +struct LocalVars { + conn_id_on_b: ConnectionId, + conn_end_on_b: ConnectionEnd, + client_id_on_a: ClientId, + conn_id_on_a: ConnectionId, +} + +impl LocalVars { + fn new(ctx_b: &Ctx, msg: &MsgConnectionOpenTry) -> Result + where + Ctx: ValidationContext, + { + let version_on_b = ctx_b.pick_version(&msg.versions_on_a)?; + + Ok(Self { + conn_id_on_b: ConnectionId::new(ctx_b.connection_counter()?), + conn_end_on_b: ConnectionEnd::new( + State::TryOpen, + msg.client_id_on_b.clone(), + msg.counterparty.clone(), + vec![version_on_b], + msg.delay_period, + )?, + client_id_on_a: msg.counterparty.client_id().clone(), + conn_id_on_a: msg + .counterparty + .connection_id() + .ok_or(ConnectionError::InvalidCounterparty)? + .clone(), + }) + } +} diff --git a/ibc-eureka-core/ics03-connection/src/handler/mod.rs b/ibc-eureka-core/ics03-connection/src/handler/mod.rs new file mode 100644 index 0000000000..791d1c7787 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/handler/mod.rs @@ -0,0 +1,81 @@ +use ibc_core_client::types::error::ClientError; +use ibc_core_connection_types::error::ConnectionError; +#[cfg(feature = "wasm-client")] +use ibc_core_host::types::error::DecodingError; +use ibc_core_host::types::identifiers::ClientId; +use ibc_primitives::proto::Any; + +pub mod conn_open_ack; +pub mod conn_open_confirm; +pub mod conn_open_init; +pub mod conn_open_try; + +/// Unpacks the client state from the format that is stored at the counterparty chain. +/// +/// Currently, the IBC-go enabled chains stores Wasm LightClient states in a WasmClientState +/// wrapper. This function unpacks the client state from the WasmClientState wrapper +/// if the client identifier at counterparty is of Wasm client type. +pub(crate) fn unpack_host_client_state( + value: Any, + host_client_id_at_counterparty: &ClientId, +) -> Result +where + CS: TryFrom, + >::Error: Into, +{ + #[cfg(feature = "wasm-client")] + if host_client_id_at_counterparty.is_wasm_client_id() { + use ibc_client_wasm_types::client_state::ClientState as WasmClientState; + use prost::Message; + + let wasm_client_state = WasmClientState::try_from(value)?; + + let any_client_state = ::decode(wasm_client_state.data.as_slice()) + .map_err(|e| ConnectionError::Decoding(DecodingError::Prost(e)))?; + + Ok(CS::try_from(any_client_state).map_err(Into::::into)?) + } else { + Ok(CS::try_from(value).map_err(Into::::into)?) + } + + #[cfg(not(feature = "wasm-client"))] + { + // this avoids lint warning for unused variable. + let _ = host_client_id_at_counterparty; + Ok(CS::try_from(value).map_err(Into::::into)?) + } +} + +/// Pack the host consensus state in the expected format stored at the counterparty chain. +/// +/// Currently, the IBC-go enabled chains stores Wasm LightClient states in a WasmConsensusState +/// wrapper. This function packs the consensus state in the WasmConsensusState wrapper +/// if the client identifier at counterparty is of Wasm client type. +pub(crate) fn pack_host_consensus_state( + value: CS, + host_client_id_at_counterparty: &ClientId, +) -> Any +where + CS: Into, +{ + let any_value = value.into(); + + #[cfg(feature = "wasm-client")] + if host_client_id_at_counterparty.is_wasm_client_id() { + use ibc_client_wasm_types::consensus_state::ConsensusState as WasmConsensusState; + use prost::Message; + + let wasm_consensus_state = WasmConsensusState::new(any_value.encode_to_vec()); + + wasm_consensus_state.into() + } else { + any_value + } + + #[cfg(not(feature = "wasm-client"))] + { + // this avoids lint warning for unused variable. + let _ = host_client_id_at_counterparty; + any_value + } +} diff --git a/ibc-eureka-core/ics03-connection/src/lib.rs b/ibc-eureka-core/ics03-connection/src/lib.rs new file mode 100644 index 0000000000..0892189d11 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/src/lib.rs @@ -0,0 +1,26 @@ +//! ICS-03: Connection Semantics implementation to process connection open +//! handshake. Exports data structures and implementations of IBC core +//! connection module. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod delay; +pub mod handler; + +/// Re-exports ICS-03 data structures from the `ibc-core-connection-types` crate +pub mod types { + #[doc(inline)] + pub use ibc_core_connection_types::*; +} diff --git a/ibc-eureka-core/ics03-connection/types/Cargo.toml b/ibc-eureka-core/ics03-connection/types/Cargo.toml new file mode 100644 index 0000000000..39ecc8485a --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/Cargo.toml @@ -0,0 +1,91 @@ +[package] +name = "ibc-core-connection-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "connection", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-03 Connection Semantics data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-client-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "subtle-encoding/std", + "serde/std", + "ibc-core-client-types/std", + "ibc-core-commitment-types/std", + "ibc-core-host-types/std", + "ibc-primitives/std", + "ibc-proto/std", + "tendermint/std", +] +serde = [ + "dep:serde", + "ibc-core-client-types/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host-types/serde", + "ibc-primitives/serde", + "ibc-proto/serde", +] +schema = [ + "dep:schemars", + "ibc-core-client-types/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host-types/schema", + "ibc-primitives/schema", + "ibc-proto/json-schema", + "serde", + "std", +] +borsh = [ + "dep:borsh", + "ibc-core-client-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host-types/borsh", + "ibc-primitives/borsh", + "ibc-proto/borsh", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-core-client-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics03-connection/types/src/connection.rs b/ibc-eureka-core/ics03-connection/types/src/connection.rs new file mode 100644 index 0000000000..f518f86a50 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/connection.rs @@ -0,0 +1,523 @@ +//! Defines the types that define a connection + +use core::fmt::{Display, Error as FmtError, Formatter}; +use core::time::Duration; + +use ibc_core_commitment_types::commitment::CommitmentPrefix; +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; +use ibc_primitives::prelude::*; +use ibc_proto::ibc::core::connection::v1::{ + ConnectionEnd as RawConnectionEnd, Counterparty as RawCounterparty, + IdentifiedConnection as RawIdentifiedConnection, +}; +use ibc_proto::Protobuf; + +use crate::error::ConnectionError; +use crate::version::Version; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct IdentifiedConnectionEnd { + pub connection_id: ConnectionId, + pub connection_end: ConnectionEnd, +} + +impl IdentifiedConnectionEnd { + pub fn new(connection_id: ConnectionId, connection_end: ConnectionEnd) -> Self { + IdentifiedConnectionEnd { + connection_id, + connection_end, + } + } + + pub fn id(&self) -> &ConnectionId { + &self.connection_id + } + + pub fn end(&self) -> &ConnectionEnd { + &self.connection_end + } +} + +impl Protobuf for IdentifiedConnectionEnd {} + +impl TryFrom for IdentifiedConnectionEnd { + type Error = DecodingError; + + fn try_from(value: RawIdentifiedConnection) -> Result { + let raw_connection_end = RawConnectionEnd { + client_id: value.client_id.to_string(), + versions: value.versions, + state: value.state, + counterparty: value.counterparty, + delay_period: value.delay_period, + }; + + Ok(IdentifiedConnectionEnd { + connection_id: value.id.parse()?, + connection_end: raw_connection_end.try_into()?, + }) + } +} + +impl From for RawIdentifiedConnection { + fn from(value: IdentifiedConnectionEnd) -> Self { + RawIdentifiedConnection { + id: value.connection_id.to_string(), + client_id: value.connection_end.client_id.to_string(), + versions: value + .connection_end + .versions + .iter() + .map(|v| From::from(v.clone())) + .collect(), + state: value.connection_end.state as i32, + delay_period: value.connection_end.delay_period.as_nanos() as u64, + counterparty: Some(value.connection_end.counterparty().clone().into()), + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive(parity_scale_codec::Encode, parity_scale_codec::Decode,) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConnectionEnd { + pub state: State, + client_id: ClientId, + counterparty: Counterparty, + versions: Vec, + delay_period: Duration, +} + +mod sealed { + use super::*; + + #[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) + )] + struct InnerConnectionEnd { + pub state: State, + client_id: ClientId, + counterparty: Counterparty, + versions: Vec, + delay_period_secs: u64, + delay_period_nanos: u32, + } + + impl From for ConnectionEnd { + fn from(value: InnerConnectionEnd) -> Self { + Self { + state: value.state, + client_id: value.client_id, + counterparty: value.counterparty, + versions: value.versions, + delay_period: Duration::new(value.delay_period_secs, value.delay_period_nanos), + } + } + } + + impl From for InnerConnectionEnd { + fn from(value: ConnectionEnd) -> Self { + Self { + state: value.state, + client_id: value.client_id, + counterparty: value.counterparty, + versions: value.versions, + delay_period_secs: value.delay_period.as_secs(), + delay_period_nanos: value.delay_period.subsec_nanos(), + } + } + } + + #[cfg(feature = "borsh")] + impl borsh::BorshSerialize for ConnectionEnd { + fn serialize(&self, writer: &mut W) -> borsh::io::Result<()> { + let value = InnerConnectionEnd::from(self.clone()); + borsh::BorshSerialize::serialize(&value, writer) + } + } + + #[cfg(feature = "borsh")] + impl borsh::BorshDeserialize for ConnectionEnd { + fn deserialize_reader(reader: &mut R) -> borsh::io::Result { + let inner_conn_end = InnerConnectionEnd::deserialize_reader(reader)?; + Ok(ConnectionEnd::from(inner_conn_end)) + } + } + + #[cfg(feature = "parity-scale-codec")] + impl scale_info::TypeInfo for ConnectionEnd { + type Identity = Self; + + fn type_info() -> scale_info::Type { + scale_info::Type::builder() + .path(scale_info::Path::new("ConnectionEnd", module_path!())) + .composite( + scale_info::build::Fields::named() + .field(|f| f.ty::().name("state").type_name("State")) + .field(|f| f.ty::().name("client_id").type_name("ClientId")) + .field(|f| { + f.ty::() + .name("counterparty") + .type_name("Counterparty") + }) + .field(|f| { + f.ty::>() + .name("versions") + .type_name("Vec") + }) + .field(|f| f.ty::().name("delay_period_secs").type_name("u64")) + .field(|f| f.ty::().name("delay_period_nanos").type_name("u32")), + ) + } + } +} + +impl Protobuf for ConnectionEnd {} + +impl TryFrom for ConnectionEnd { + type Error = DecodingError; + + fn try_from(value: RawConnectionEnd) -> Result { + let state = value.state.try_into()?; + + if value.client_id.is_empty() { + return Err(DecodingError::missing_raw_data("connection end client ID"))?; + } + + if value.versions.is_empty() { + return Err(DecodingError::missing_raw_data("connection end versions"))?; + } + + Self::new( + state, + value.client_id.parse()?, + value + .counterparty + .ok_or(DecodingError::missing_raw_data("counterparty"))? + .try_into()?, + value + .versions + .into_iter() + .map(Version::try_from) + .collect::, _>>()?, + Duration::from_nanos(value.delay_period), + ) + .map_err(|_| DecodingError::invalid_raw_data("connection end")) + } +} + +impl From for RawConnectionEnd { + fn from(value: ConnectionEnd) -> Self { + RawConnectionEnd { + client_id: value.client_id.to_string(), + versions: value + .versions + .iter() + .map(|v| From::from(v.clone())) + .collect(), + state: value.state as i32, + counterparty: Some(value.counterparty.into()), + delay_period: value.delay_period.as_nanos() as u64, + } + } +} + +impl ConnectionEnd { + pub fn new( + state: State, + client_id: ClientId, + counterparty: Counterparty, + versions: Vec, + delay_period: Duration, + ) -> Result { + // Note: `versions`'s semantics vary based on the `State` of the connection: + // + Init: contains the set of compatible versions, + // + TryOpen/Open: contains the single version chosen by the handshake protocol. + if state != State::Init && versions.len() != 1 { + return Err(ConnectionError::InvalidState { description: "failed to initialize new ConnectionEnd; expected `Init` connection state and a single version".to_string() }); + } + + Ok(Self { + state, + client_id, + counterparty, + versions, + delay_period, + }) + } + + /// Getter for the state of this connection end. + pub fn state(&self) -> &State { + &self.state + } + + /// Setter for the `state` field. + pub fn set_state(&mut self, new_state: State) { + self.state = new_state; + } + + /// Setter for the `counterparty` field. + pub fn set_counterparty(&mut self, new_cparty: Counterparty) { + self.counterparty = new_cparty; + } + + /// Setter for the `version` field. + pub fn set_version(&mut self, new_version: Version) { + self.versions = vec![new_version]; + } + + /// Helper function to compare the counterparty of this end with another counterparty. + pub fn counterparty_matches(&self, other: &Counterparty) -> bool { + self.counterparty.eq(other) + } + + /// Helper function to compare the client id of this end with another client identifier. + pub fn client_id_matches(&self, other: &ClientId) -> bool { + self.client_id.eq(other) + } + + /// Helper function to determine whether the connection is open. + pub fn is_open(&self) -> bool { + self.state == State::Open + } + + /// Helper function to determine whether the connection is uninitialized. + pub fn is_uninitialized(&self) -> bool { + self.state == State::Uninitialized + } + + /// Checks if the state of this connection end matches with an expected state. + pub fn verify_state_matches(&self, expected: &State) -> Result<(), ConnectionError> { + if !self.state.eq(expected) { + return Err(ConnectionError::MismatchedConnectionStates { + expected: expected.to_string(), + actual: self.state.to_string(), + }); + } + Ok(()) + } + + /// Getter for the client id on the local party of this connection end. + pub fn client_id(&self) -> &ClientId { + &self.client_id + } + + /// Getter for the list of versions in this connection end. + pub fn versions(&self) -> &[Version] { + &self.versions + } + + /// Getter for the counterparty. + pub fn counterparty(&self) -> &Counterparty { + &self.counterparty + } + + /// Getter for the delay_period field. This represents the duration, at minimum, + /// to delay the sending of a packet after the client update for that packet has been submitted. + pub fn delay_period(&self) -> Duration { + self.delay_period + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Counterparty { + pub client_id: ClientId, + pub connection_id: Option, + pub prefix: CommitmentPrefix, +} + +impl Protobuf for Counterparty {} + +// Converts from the wire format RawCounterparty. Typically used from the relayer side +// during queries for response validation and to extract the Counterparty structure. +impl TryFrom for Counterparty { + type Error = DecodingError; + + fn try_from(raw_counterparty: RawCounterparty) -> Result { + let connection_id: Option = if raw_counterparty.connection_id.is_empty() { + None + } else { + Some(raw_counterparty.connection_id.parse()?) + }; + Ok(Counterparty::new( + raw_counterparty.client_id.parse()?, + connection_id, + raw_counterparty + .prefix + .ok_or(DecodingError::missing_raw_data("counterparty prefix"))? + .key_prefix + .into(), + )) + } +} + +impl From for RawCounterparty { + fn from(value: Counterparty) -> Self { + RawCounterparty { + client_id: value.client_id.as_str().to_string(), + connection_id: value + .connection_id + .map_or_else(|| "".to_string(), |v| v.as_str().to_string()), + prefix: Some(ibc_proto::ibc::core::commitment::v1::MerklePrefix { + key_prefix: value.prefix.into_vec(), + }), + } + } +} + +impl Counterparty { + pub fn new( + client_id: ClientId, + connection_id: Option, + prefix: CommitmentPrefix, + ) -> Self { + Self { + client_id, + connection_id, + prefix, + } + } + + /// Getter for the client id. + pub fn client_id(&self) -> &ClientId { + &self.client_id + } + + /// Getter for connection id. + pub fn connection_id(&self) -> Option<&ConnectionId> { + self.connection_id.as_ref() + } + + pub fn prefix(&self) -> &CommitmentPrefix { + &self.prefix + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize), + borsh(use_discriminant = false) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum State { + Uninitialized = 0isize, + Init = 1isize, + TryOpen = 2isize, + Open = 3isize, +} + +impl State { + /// Yields the State as a string. + pub fn as_str(&self) -> &'static str { + match self { + Self::Uninitialized => "UNINITIALIZED", + Self::Init => "INIT", + Self::TryOpen => "TRYOPEN", + Self::Open => "OPEN", + } + } + + /// Parses the State out from an i32. + pub fn from_i32(s: i32) -> Result { + match s { + 0 => Ok(Self::Uninitialized), + 1 => Ok(Self::Init), + 2 => Ok(Self::TryOpen), + 3 => Ok(Self::Open), + _ => Err(ConnectionError::MismatchedConnectionStates { + expected: "0, 1, 2, or 3".to_string(), + actual: s.to_string(), + }), + } + } + + /// Returns if this connection state is `Open`. + pub fn is_open(self) -> bool { + self == State::Open + } + + /// Returns if this connection with this state + /// has progressed less than or the same as the argument. + /// + /// # Example + /// ```rust,ignore + /// assert!(State::Init.less_or_equal_progress(State::Open)); + /// assert!(State::TryOpen.less_or_equal_progress(State::TryOpen)); + /// assert!(!State::Open.less_or_equal_progress(State::Uninitialized)); + /// ``` + pub fn less_or_equal_progress(self, other: Self) -> bool { + self as u32 <= other as u32 + } +} + +impl Display for State { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.as_str()) + } +} + +impl TryFrom for State { + type Error = DecodingError; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Uninitialized), + 1 => Ok(Self::Init), + 2 => Ok(Self::TryOpen), + 3 => Ok(Self::Open), + _ => Err(DecodingError::invalid_raw_data(format!( + "connection state expected to be 0, 1, 2, or 3, actual {value}", + ))), + } + } +} + +impl From for i32 { + fn from(value: State) -> Self { + value as i32 + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/error.rs b/ibc-eureka-core/ics03-connection/types/src/error.rs new file mode 100644 index 0000000000..fa4ad6297f --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/error.rs @@ -0,0 +1,90 @@ +//! Defines the connection error type + +use displaydoc::Display; +use ibc_core_client_types::error::ClientError; +use ibc_core_client_types::Height; +use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_primitives::prelude::*; +use ibc_primitives::{Timestamp, TimestampError}; + +#[derive(Debug, Display)] +pub enum ConnectionError { + /// client error: {0} + Client(ClientError), + /// decoding error: {0} + Decoding(DecodingError), + /// host error: {0} + Host(HostError), + /// timestamp error: {0} + Timestamp(TimestampError), + /// invalid counterparty + InvalidCounterparty, + /// invalid connection state: {description} + InvalidState { description: String }, + /// mismatched connection states: expected `{expected}`, actual `{actual}` + MismatchedConnectionStates { expected: String, actual: String }, + /// missing supported features + MissingFeatures, + /// missing common version + MissingCommonVersion, + /// missing counterparty + MissingCounterparty, + /// insufficient consensus height `{current_height}` for host chain; needs to meet counterparty's height `{target_height}` + InsufficientConsensusHeight { + target_height: Height, + current_height: Height, + }, + /// insufficient blocks elapsed: current height `{current_host_height}` needs to meet `{earliest_valid_height}` + InsufficientBlocksElapsed { + current_host_height: Height, + earliest_valid_height: Height, + }, + /// insufficient time elapsed: current timestamp `{current_host_time}` needs to meet `{earliest_valid_time}` + InsufficientTimeElapsed { + current_host_time: Timestamp, + earliest_valid_time: Timestamp, + }, +} + +impl From for ConnectionError { + fn from(e: DecodingError) -> Self { + Self::Decoding(e) + } +} + +impl From for ConnectionError { + fn from(e: IdentifierError) -> Self { + Self::Decoding(DecodingError::Identifier(e)) + } +} + +impl From for ConnectionError { + fn from(e: TimestampError) -> Self { + Self::Timestamp(e) + } +} + +impl From for ConnectionError { + fn from(e: ClientError) -> Self { + Self::Client(e) + } +} + +impl From for ConnectionError { + fn from(e: HostError) -> Self { + Self::Host(e) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ConnectionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Host(e) => Some(e), + Self::Client(e) => Some(e), + Self::Decoding(e) => Some(e), + Self::Timestamp(e) => Some(e), + _ => None, + } + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/events.rs b/ibc-eureka-core/ics03-connection/types/src/events.rs new file mode 100644 index 0000000000..08f64d9e14 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/events.rs @@ -0,0 +1,413 @@ +//! Types for the IBC events emitted from Tendermint Websocket by the connection module. + +use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; +use ibc_primitives::prelude::*; +use tendermint::abci; + +/// Connection event types +const CONNECTION_OPEN_INIT_EVENT: &str = "connection_open_init"; +const CONNECTION_OPEN_TRY_EVENT: &str = "connection_open_try"; +const CONNECTION_OPEN_ACK_EVENT: &str = "connection_open_ack"; +const CONNECTION_OPEN_CONFIRM_EVENT: &str = "connection_open_confirm"; + +/// The content of the `key` field for the attribute containing the connection identifier. +pub const CONN_ID_ATTRIBUTE_KEY: &str = "connection_id"; +pub const CLIENT_ID_ATTRIBUTE_KEY: &str = "client_id"; +pub const COUNTERPARTY_CONN_ID_ATTRIBUTE_KEY: &str = "counterparty_connection_id"; +pub const COUNTERPARTY_CLIENT_ID_ATTRIBUTE_KEY: &str = "counterparty_client_id"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Attributes { + pub connection_id: ConnectionId, + pub client_id: ClientId, + pub counterparty_connection_id: Option, + pub counterparty_client_id: ClientId, +} + +/// Convert attributes to Tendermint ABCI tags +impl From for Vec { + fn from(a: Attributes) -> Self { + let conn_id = (CONN_ID_ATTRIBUTE_KEY, a.connection_id.as_str()).into(); + let client_id = (CLIENT_ID_ATTRIBUTE_KEY, a.client_id.as_str()).into(); + + let counterparty_conn_id = ( + COUNTERPARTY_CONN_ID_ATTRIBUTE_KEY, + a.counterparty_connection_id + .as_ref() + .map(|id| id.as_str()) + .unwrap_or(""), + ) + .into(); + + let counterparty_client_id = ( + COUNTERPARTY_CLIENT_ID_ATTRIBUTE_KEY, + a.counterparty_client_id.as_str(), + ) + .into(); + + vec![ + conn_id, + client_id, + counterparty_client_id, + counterparty_conn_id, + ] + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenInit(Attributes); + +impl OpenInit { + /// Per our convention, this event is generated on chain A. + pub fn new( + conn_id_on_a: ConnectionId, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + ) -> Self { + Self(Attributes { + connection_id: conn_id_on_a, + client_id: client_id_on_a, + counterparty_connection_id: None, + counterparty_client_id: client_id_on_b, + }) + } + + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.0.connection_id + } + pub fn client_id_on_a(&self) -> &ClientId { + &self.0.client_id + } + pub fn conn_id_on_b(&self) -> Option<&ConnectionId> { + self.0.counterparty_connection_id.as_ref() + } + pub fn client_id_on_b(&self) -> &ClientId { + &self.0.counterparty_client_id + } + + pub fn event_type(&self) -> &str { + CONNECTION_OPEN_INIT_EVENT + } +} + +impl From for abci::Event { + fn from(v: OpenInit) -> Self { + abci::Event { + kind: CONNECTION_OPEN_INIT_EVENT.to_string(), + attributes: v.0.into(), + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenTry(Attributes); + +impl OpenTry { + /// Per our convention, this event is generated on chain B. + pub fn new( + conn_id_on_b: ConnectionId, + client_id_on_b: ClientId, + conn_id_on_a: ConnectionId, + client_id_on_a: ClientId, + ) -> Self { + Self(Attributes { + connection_id: conn_id_on_b, + client_id: client_id_on_b, + counterparty_connection_id: Some(conn_id_on_a), + counterparty_client_id: client_id_on_a, + }) + } + + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.0.connection_id + } + pub fn client_id_on_b(&self) -> &ClientId { + &self.0.client_id + } + pub fn conn_id_on_a(&self) -> Option<&ConnectionId> { + self.0.counterparty_connection_id.as_ref() + } + pub fn client_id_on_a(&self) -> &ClientId { + &self.0.counterparty_client_id + } + + pub fn event_type(&self) -> &str { + CONNECTION_OPEN_TRY_EVENT + } +} + +impl From for abci::Event { + fn from(v: OpenTry) -> Self { + abci::Event { + kind: CONNECTION_OPEN_TRY_EVENT.to_string(), + attributes: v.0.into(), + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenAck(Attributes); + +impl OpenAck { + /// Per our convention, this event is generated on chain A. + pub fn new( + conn_id_on_a: ConnectionId, + client_id_on_a: ClientId, + conn_id_on_b: ConnectionId, + client_id_on_b: ClientId, + ) -> Self { + Self(Attributes { + connection_id: conn_id_on_a, + client_id: client_id_on_a, + counterparty_connection_id: Some(conn_id_on_b), + counterparty_client_id: client_id_on_b, + }) + } + + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.0.connection_id + } + pub fn client_id_on_a(&self) -> &ClientId { + &self.0.client_id + } + pub fn conn_id_on_b(&self) -> Option<&ConnectionId> { + self.0.counterparty_connection_id.as_ref() + } + pub fn client_id_on_b(&self) -> &ClientId { + &self.0.counterparty_client_id + } + + pub fn event_type(&self) -> &str { + CONNECTION_OPEN_ACK_EVENT + } +} + +impl From for abci::Event { + fn from(v: OpenAck) -> Self { + abci::Event { + kind: CONNECTION_OPEN_ACK_EVENT.to_string(), + attributes: v.0.into(), + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenConfirm(Attributes); + +impl OpenConfirm { + /// Per our convention, this event is generated on chain B. + pub fn new( + conn_id_on_b: ConnectionId, + client_id_on_b: ClientId, + conn_id_on_a: ConnectionId, + client_id_on_a: ClientId, + ) -> Self { + Self(Attributes { + connection_id: conn_id_on_b, + client_id: client_id_on_b, + counterparty_connection_id: Some(conn_id_on_a), + counterparty_client_id: client_id_on_a, + }) + } + + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.0.connection_id + } + pub fn client_id_on_b(&self) -> &ClientId { + &self.0.client_id + } + pub fn conn_id_on_a(&self) -> Option<&ConnectionId> { + self.0.counterparty_connection_id.as_ref() + } + pub fn client_id_on_a(&self) -> &ClientId { + &self.0.counterparty_client_id + } + + pub fn event_type(&self) -> &str { + CONNECTION_OPEN_CONFIRM_EVENT + } +} + +impl From for abci::Event { + fn from(v: OpenConfirm) -> Self { + abci::Event { + kind: CONNECTION_OPEN_CONFIRM_EVENT.to_string(), + attributes: v.0.into(), + } + } +} + +#[cfg(test)] +mod tests { + + use core::str::FromStr; + + use ibc_core_host_types::identifiers::ClientType; + use tendermint::abci::Event as AbciEvent; + + use super::*; + + #[test] + fn ibc_to_abci_connection_events() { + struct Test { + kind: &'static str, + event: AbciEvent, + expected_keys: Vec<&'static str>, + expected_values: Vec<&'static str>, + } + + let client_type = ClientType::from_str("07-tendermint") + .expect("never fails because it's a valid client type"); + let conn_id_on_a = ConnectionId::zero(); + let client_id_on_a = client_type.build_client_id(0); + let conn_id_on_b = ConnectionId::new(1); + let client_id_on_b = client_type.build_client_id(1); + let expected_keys = vec![ + "connection_id", + "client_id", + "counterparty_client_id", + "counterparty_connection_id", + ]; + let expected_values = vec![ + "connection-0", + "07-tendermint-0", + "07-tendermint-1", + "connection-1", + ]; + + let tests: Vec = vec![ + Test { + kind: CONNECTION_OPEN_INIT_EVENT, + event: OpenInit::new( + conn_id_on_a.clone(), + client_id_on_a.clone(), + client_id_on_b.clone(), + ) + .into(), + expected_keys: expected_keys.clone(), + expected_values: expected_values + .iter() + .enumerate() + .map(|(i, v)| if i == 3 { "" } else { v }) + .collect(), + }, + Test { + kind: CONNECTION_OPEN_TRY_EVENT, + event: OpenTry::new( + conn_id_on_b.clone(), + client_id_on_b.clone(), + conn_id_on_a.clone(), + client_id_on_a.clone(), + ) + .into(), + expected_keys: expected_keys.clone(), + expected_values: expected_values.iter().rev().copied().collect(), + }, + Test { + kind: CONNECTION_OPEN_ACK_EVENT, + event: OpenAck::new( + conn_id_on_a.clone(), + client_id_on_a.clone(), + conn_id_on_b.clone(), + client_id_on_b.clone(), + ) + .into(), + expected_keys: expected_keys.clone(), + expected_values: expected_values.clone(), + }, + Test { + kind: CONNECTION_OPEN_CONFIRM_EVENT, + event: OpenConfirm::new(conn_id_on_b, client_id_on_b, conn_id_on_a, client_id_on_a) + .into(), + expected_keys: expected_keys.clone(), + expected_values: expected_values.iter().rev().copied().collect(), + }, + ]; + + for t in tests { + assert_eq!(t.kind, t.event.kind); + assert_eq!(t.expected_keys.len(), t.event.attributes.len()); + for (i, e) in t.event.attributes.iter().enumerate() { + assert_eq!( + e.key_str().unwrap(), + t.expected_keys[i], + "key mismatch for {:?}", + t.kind + ); + } + for (i, e) in t.event.attributes.iter().enumerate() { + assert_eq!( + e.value_str().unwrap(), + t.expected_values[i], + "value mismatch for {:?}", + t.kind + ); + } + } + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/lib.rs b/ibc-eureka-core/ics03-connection/types/src/lib.rs new file mode 100644 index 0000000000..345e7eee16 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/lib.rs @@ -0,0 +1,29 @@ +//! Implementation of the Connection Semantics (ICS-03) data structures. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +mod connection; +pub use connection::*; + +pub mod error; +pub mod events; +pub mod msgs; +pub mod version; + +/// Re-exports ICS-03 proto types from the `ibc-proto` crate for added +/// convenience +pub mod proto { + pub use ibc_proto::ibc::core::connection::*; +} diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs new file mode 100644 index 0000000000..9379c25e36 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs @@ -0,0 +1,103 @@ +use ibc_core_client_types::Height; +use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::ConnectionId; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; +use ibc_proto::Protobuf; + +use crate::version::Version; + +pub const CONN_OPEN_ACK_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenAck"; + +/// Per our convention, this message is sent to chain A. +/// The handler will check proofs of chain B. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgConnectionOpenAck { + /// ConnectionId that chain A has chosen for its ConnectionEnd + pub conn_id_on_a: ConnectionId, + /// ConnectionId that chain B has chosen for its ConnectionEnd + pub conn_id_on_b: ConnectionId, + /// ClientState of client tracking chain A on chain B + pub client_state_of_a_on_b: Any, + /// proof of ConnectionEnd stored on Chain B during ConnOpenTry + pub proof_conn_end_on_b: CommitmentProofBytes, + /// proof of ClientState tracking chain A on chain B + pub proof_client_state_of_a_on_b: CommitmentProofBytes, + /// proof that chain B has stored ConsensusState of chain A on its client + pub proof_consensus_state_of_a_on_b: CommitmentProofBytes, + /// Height at which all proofs in this message were taken + pub proofs_height_on_b: Height, + /// height of latest header of chain A that updated the client on chain B + pub consensus_height_of_a_on_b: Height, + pub version: Version, + pub signer: Signer, + /// optional proof of host state machines (chain A) that are unable to + /// introspect their own consensus state + pub proof_consensus_state_of_a: Option, +} + +impl Protobuf for MsgConnectionOpenAck {} + +impl TryFrom for MsgConnectionOpenAck { + type Error = DecodingError; + + fn try_from(msg: RawMsgConnectionOpenAck) -> Result { + Ok(Self { + conn_id_on_a: msg.connection_id.parse()?, + conn_id_on_b: msg.counterparty_connection_id.parse()?, + client_state_of_a_on_b: msg + .client_state + .ok_or(DecodingError::missing_raw_data("client state"))?, + version: msg + .version + .ok_or(DecodingError::missing_raw_data("connection version"))? + .try_into()?, + proof_conn_end_on_b: msg.proof_try.try_into()?, + proof_client_state_of_a_on_b: msg.proof_client.try_into()?, + proof_consensus_state_of_a_on_b: msg.proof_consensus.try_into()?, + proofs_height_on_b: msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::missing_raw_data("proof height"))?, + consensus_height_of_a_on_b: msg + .consensus_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::missing_raw_data("consensus height"))?, + signer: msg.signer.into(), + proof_consensus_state_of_a: if msg.host_consensus_state_proof.is_empty() { + None + } else { + Some(msg.host_consensus_state_proof.try_into()?) + }, + }) + } +} + +impl From for RawMsgConnectionOpenAck { + fn from(msg: MsgConnectionOpenAck) -> Self { + RawMsgConnectionOpenAck { + connection_id: msg.conn_id_on_a.as_str().to_string(), + counterparty_connection_id: msg.conn_id_on_b.as_str().to_string(), + client_state: Some(msg.client_state_of_a_on_b), + proof_height: Some(msg.proofs_height_on_b.into()), + proof_try: msg.proof_conn_end_on_b.into(), + proof_client: msg.proof_client_state_of_a_on_b.into(), + proof_consensus: msg.proof_consensus_state_of_a_on_b.into(), + consensus_height: Some(msg.consensus_height_of_a_on_b.into()), + version: Some(msg.version.into()), + signer: msg.signer.to_string(), + host_consensus_state_proof: match msg.proof_consensus_state_of_a { + Some(proof) => proof.into(), + None => vec![], + }, + } + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs new file mode 100644 index 0000000000..24a58b87e1 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs @@ -0,0 +1,59 @@ +use ibc_core_client_types::Height; +use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::ConnectionId; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; +use ibc_proto::Protobuf; + +pub const CONN_OPEN_CONFIRM_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenConfirm"; + +/// Per our convention, this message is sent to chain B. +/// The handler will check proofs of chain A. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgConnectionOpenConfirm { + /// ConnectionId that chain B has chosen for its ConnectionEnd + pub conn_id_on_b: ConnectionId, + /// proof of ConnectionEnd stored on Chain A during ConnOpenInit + pub proof_conn_end_on_a: CommitmentProofBytes, + /// Height at which `proof_conn_end_on_a` in this message was taken + pub proof_height_on_a: Height, + pub signer: Signer, +} + +impl Protobuf for MsgConnectionOpenConfirm {} + +impl TryFrom for MsgConnectionOpenConfirm { + type Error = DecodingError; + + fn try_from(msg: RawMsgConnectionOpenConfirm) -> Result { + Ok(Self { + conn_id_on_b: msg.connection_id.parse()?, + proof_conn_end_on_a: msg.proof_ack.try_into()?, + proof_height_on_a: msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg conn open confirm proof height", + ))?, + signer: msg.signer.into(), + }) + } +} + +impl From for RawMsgConnectionOpenConfirm { + fn from(msg: MsgConnectionOpenConfirm) -> Self { + RawMsgConnectionOpenConfirm { + connection_id: msg.conn_id_on_b.as_str().to_string(), + proof_ack: msg.proof_conn_end_on_a.into(), + proof_height: Some(msg.proof_height_on_a.into()), + signer: msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs new file mode 100644 index 0000000000..57790a3870 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs @@ -0,0 +1,126 @@ +use core::time::Duration; + +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::ClientId; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnectionOpenInit; +use ibc_proto::Protobuf; + +use crate::connection::Counterparty; +use crate::version::Version; + +pub const CONN_OPEN_INIT_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenInit"; + +/// Per our convention, this message is sent to chain A. +/// The handler will check proofs of chain B. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct MsgConnectionOpenInit { + /// ClientId on chain A that the connection is being opened for + pub client_id_on_a: ClientId, + pub counterparty: Counterparty, + pub version: Option, + pub delay_period: Duration, + pub signer: Signer, +} + +/// This module encapsulates the workarounds we need to do to implement +/// `BorshSerialize` and `BorshDeserialize` on `MsgConnectionOpenInit` +#[cfg(feature = "borsh")] +mod borsh_impls { + use borsh::io::{self, Read}; + use borsh::{BorshDeserialize, BorshSerialize}; + + use super::*; + + #[derive(BorshSerialize, BorshDeserialize)] + pub struct InnerMsgConnectionOpenInit { + /// ClientId on chain A that the connection is being opened for + pub client_id_on_a: ClientId, + pub counterparty: Counterparty, + pub version: Option, + pub delay_period_nanos: u64, + pub signer: Signer, + } + + impl BorshSerialize for MsgConnectionOpenInit { + fn serialize(&self, writer: &mut W) -> io::Result<()> { + let delay_period_nanos: u64 = + self.delay_period.as_nanos().try_into().map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!( + "Duration too long: `{}` nanos", + self.delay_period.as_nanos() + ), + ) + })?; + + let inner = InnerMsgConnectionOpenInit { + client_id_on_a: self.client_id_on_a.clone(), + counterparty: self.counterparty.clone(), + version: self.version.clone(), + delay_period_nanos, + signer: self.signer.clone(), + }; + + inner.serialize(writer) + } + } + + impl BorshDeserialize for MsgConnectionOpenInit { + fn deserialize_reader(reader: &mut R) -> io::Result { + let inner = InnerMsgConnectionOpenInit::deserialize_reader(reader)?; + + Ok(MsgConnectionOpenInit { + client_id_on_a: inner.client_id_on_a, + counterparty: inner.counterparty, + version: inner.version, + delay_period: Duration::from_nanos(inner.delay_period_nanos), + signer: inner.signer, + }) + } + } +} + +impl Protobuf for MsgConnectionOpenInit {} + +impl TryFrom for MsgConnectionOpenInit { + type Error = DecodingError; + + fn try_from(msg: RawMsgConnectionOpenInit) -> Result { + let counterparty: Counterparty = msg + .counterparty + .ok_or(DecodingError::missing_raw_data( + "msg conn open init counterparty", + ))? + .try_into()?; + + if let Some(cid) = counterparty.connection_id() { + return Err(DecodingError::invalid_raw_data(format!( + "expected msg conn open init connection ID to be empty, actual `{cid}`", + ))); + } + + Ok(Self { + client_id_on_a: msg.client_id.parse()?, + counterparty, + version: msg.version.map(TryInto::try_into).transpose()?, + delay_period: Duration::from_nanos(msg.delay_period), + signer: msg.signer.into(), + }) + } +} + +impl From for RawMsgConnectionOpenInit { + fn from(ics_msg: MsgConnectionOpenInit) -> Self { + RawMsgConnectionOpenInit { + client_id: ics_msg.client_id_on_a.as_str().to_string(), + counterparty: Some(ics_msg.counterparty.into()), + version: ics_msg.version.map(Into::into), + delay_period: ics_msg.delay_period.as_nanos() as u64, + signer: ics_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs new file mode 100644 index 0000000000..ea5f22f039 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs @@ -0,0 +1,226 @@ +use core::time::Duration; + +use ibc_core_client_types::Height; +use ibc_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::ClientId; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenTry as RawMsgConnectionOpenTry; +use ibc_proto::Protobuf; + +use crate::connection::Counterparty; +use crate::version::Version; + +pub const CONN_OPEN_TRY_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenTry"; + +/// Per our convention, this message is sent to chain B. +/// The handler will check proofs of chain A. +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgConnectionOpenTry { + /// ClientId on B that the connection is being opened for + pub client_id_on_b: ClientId, + /// ClientState of client tracking chain B on chain A + pub client_state_of_b_on_a: Any, + /// ClientId, ConnectionId and prefix of chain A + pub counterparty: Counterparty, + /// Versions supported by chain A + pub versions_on_a: Vec, + /// proof of ConnectionEnd stored on Chain A during ConnOpenInit + pub proof_conn_end_on_a: CommitmentProofBytes, + /// proof that chain A has stored ClientState of chain B on its client + pub proof_client_state_of_b_on_a: CommitmentProofBytes, + /// proof that chain A has stored ConsensusState of chain B on its client + pub proof_consensus_state_of_b_on_a: CommitmentProofBytes, + /// Height at which all proofs in this message were taken + pub proofs_height_on_a: Height, + /// height of latest header of chain A that updated the client on chain B + pub consensus_height_of_b_on_a: Height, + pub delay_period: Duration, + pub signer: Signer, + /// optional proof of host state machines (chain B) that are unable to + /// introspect their own consensus state + pub proof_consensus_state_of_b: Option, + + #[deprecated(since = "0.22.0")] + /// Only kept here for proper conversion to/from the raw type + pub previous_connection_id: String, +} + +#[allow(deprecated)] +#[cfg(feature = "borsh")] +mod borsh_impls { + use borsh::io::{self, Read}; + use borsh::{BorshDeserialize, BorshSerialize}; + + use super::*; + + #[derive(BorshSerialize, BorshDeserialize)] + pub struct InnerMsgConnectionOpenTry { + /// ClientId on B that the connection is being opened for + pub client_id_on_b: ClientId, + /// ClientState of client tracking chain B on chain A + pub client_state_of_b_on_a: Any, + /// ClientId, ConnectionId and prefix of chain A + pub counterparty: Counterparty, + /// Versions supported by chain A + pub versions_on_a: Vec, + /// proof of ConnectionEnd stored on Chain A during ConnOpenInit + pub proof_conn_end_on_a: CommitmentProofBytes, + /// proof that chain A has stored ClientState of chain B on its client + pub proof_client_state_of_b_on_a: CommitmentProofBytes, + /// proof that chain A has stored ConsensusState of chain B on its client + pub proof_consensus_state_of_b_on_a: CommitmentProofBytes, + /// Height at which all proofs in this message were taken + pub proofs_height_on_a: Height, + /// height of latest header of chain A that updated the client on chain B + pub consensus_height_of_b_on_a: Height, + pub delay_period_nanos: u64, + pub signer: Signer, + /// optional proof of host state machines (chain B) that are unable to + /// introspect their own consensus state + pub proof_consensus_state_of_b: Option, + + #[deprecated(since = "0.22.0")] + /// Only kept here for proper conversion to/from the raw type + previous_connection_id: String, + } + + impl BorshSerialize for MsgConnectionOpenTry { + fn serialize(&self, writer: &mut W) -> io::Result<()> { + let delay_period_nanos: u64 = + self.delay_period.as_nanos().try_into().map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!("Duration too long: {} nanos", self.delay_period.as_nanos()), + ) + })?; + + let inner = InnerMsgConnectionOpenTry { + client_id_on_b: self.client_id_on_b.clone(), + client_state_of_b_on_a: self.client_state_of_b_on_a.clone(), + counterparty: self.counterparty.clone(), + versions_on_a: self.versions_on_a.clone(), + proof_conn_end_on_a: self.proof_conn_end_on_a.clone(), + proof_client_state_of_b_on_a: self.proof_client_state_of_b_on_a.clone(), + proof_consensus_state_of_b_on_a: self.proof_consensus_state_of_b_on_a.clone(), + proofs_height_on_a: self.proofs_height_on_a, + consensus_height_of_b_on_a: self.consensus_height_of_b_on_a, + delay_period_nanos, + signer: self.signer.clone(), + proof_consensus_state_of_b: self.proof_consensus_state_of_b.clone(), + previous_connection_id: self.previous_connection_id.clone(), + }; + + inner.serialize(writer) + } + } + + impl BorshDeserialize for MsgConnectionOpenTry { + fn deserialize_reader(reader: &mut R) -> io::Result { + let inner = InnerMsgConnectionOpenTry::deserialize_reader(reader)?; + + Ok(MsgConnectionOpenTry { + client_id_on_b: inner.client_id_on_b, + client_state_of_b_on_a: inner.client_state_of_b_on_a, + counterparty: inner.counterparty, + versions_on_a: inner.versions_on_a, + proof_conn_end_on_a: inner.proof_conn_end_on_a, + proof_client_state_of_b_on_a: inner.proof_client_state_of_b_on_a, + proof_consensus_state_of_b_on_a: inner.proof_consensus_state_of_b_on_a, + proofs_height_on_a: inner.proofs_height_on_a, + consensus_height_of_b_on_a: inner.consensus_height_of_b_on_a, + delay_period: Duration::from_nanos(inner.delay_period_nanos), + signer: inner.signer, + proof_consensus_state_of_b: inner.proof_consensus_state_of_b, + previous_connection_id: inner.previous_connection_id, + }) + } + } +} + +impl Protobuf for MsgConnectionOpenTry {} + +impl TryFrom for MsgConnectionOpenTry { + type Error = DecodingError; + + fn try_from(msg: RawMsgConnectionOpenTry) -> Result { + let counterparty_versions = msg + .counterparty_versions + .into_iter() + .map(Version::try_from) + .collect::, _>>()?; + + if counterparty_versions.is_empty() { + return Err(DecodingError::missing_raw_data( + "msg conn open try connection versions", + )); + } + + // We set the deprecated `previous_connection_id` field so that we can + // properly convert `MsgConnectionOpenTry` into its raw form + #[allow(deprecated)] + Ok(Self { + previous_connection_id: msg.previous_connection_id, + client_id_on_b: msg.client_id.parse()?, + client_state_of_b_on_a: msg.client_state.ok_or(DecodingError::missing_raw_data( + "msg conn open try client state", + ))?, + counterparty: msg + .counterparty + .ok_or(DecodingError::missing_raw_data( + "msg conn open try counterparty", + ))? + .try_into()?, + versions_on_a: counterparty_versions, + proof_conn_end_on_a: msg.proof_init.try_into()?, + proof_client_state_of_b_on_a: msg.proof_client.try_into()?, + proof_consensus_state_of_b_on_a: msg.proof_consensus.try_into()?, + proofs_height_on_a: msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg conn open try proof height", + ))?, + consensus_height_of_b_on_a: msg + .consensus_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg conn open try consensus height", + ))?, + delay_period: Duration::from_nanos(msg.delay_period), + signer: msg.signer.into(), + proof_consensus_state_of_b: if msg.host_consensus_state_proof.is_empty() { + None + } else { + Some(msg.host_consensus_state_proof.try_into()?) + }, + }) + } +} + +impl From for RawMsgConnectionOpenTry { + fn from(msg: MsgConnectionOpenTry) -> Self { + #[allow(deprecated)] + RawMsgConnectionOpenTry { + client_id: msg.client_id_on_b.as_str().to_string(), + previous_connection_id: msg.previous_connection_id, + client_state: Some(msg.client_state_of_b_on_a), + counterparty: Some(msg.counterparty.into()), + delay_period: msg.delay_period.as_nanos() as u64, + counterparty_versions: msg.versions_on_a.iter().map(|v| v.clone().into()).collect(), + proof_height: Some(msg.proofs_height_on_a.into()), + proof_init: msg.proof_conn_end_on_a.into(), + proof_client: msg.proof_client_state_of_b_on_a.into(), + proof_consensus: msg.proof_consensus_state_of_b_on_a.into(), + consensus_height: Some(msg.consensus_height_of_b_on_a.into()), + signer: msg.signer.to_string(), + host_consensus_state_proof: match msg.proof_consensus_state_of_b { + Some(proof) => proof.into(), + None => vec![], + }, + } + } +} diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/mod.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/mod.rs new file mode 100644 index 0000000000..d3be3dcf61 --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/mod.rs @@ -0,0 +1,39 @@ +//! Message definitions for the connection handshake datagrams. +//! +//! We define each of the four messages in the connection handshake protocol as a `struct`. +//! Each such message comprises the same fields as the datagrams defined in ICS3 English spec: +//! . +//! +//! One departure from ICS3 is that we abstract the three counterparty fields (connection id, +//! prefix, and client id) into a single field of type `Counterparty`; this applies to messages +//! `MsgConnectionOpenInit` and `MsgConnectionOpenTry`. One other difference with regard to +//! abstraction is that all proof-related attributes in a message are encapsulated in `Proofs` type. +//! +//! Another difference to ICS3 specs is that each message comprises an additional field called +//! `signer` which is specific to Cosmos-SDK. + +use ibc_primitives::prelude::*; + +mod conn_open_ack; +mod conn_open_confirm; +mod conn_open_init; +mod conn_open_try; + +pub use conn_open_ack::*; +pub use conn_open_confirm::*; +pub use conn_open_init::*; +pub use conn_open_try::*; + +/// Enumeration of all possible messages that the ICS3 protocol processes. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, derive_more::From)] +pub enum ConnectionMsg { + OpenInit(MsgConnectionOpenInit), + OpenTry(MsgConnectionOpenTry), + OpenAck(MsgConnectionOpenAck), + OpenConfirm(MsgConnectionOpenConfirm), +} diff --git a/ibc-eureka-core/ics03-connection/types/src/version.rs b/ibc-eureka-core/ics03-connection/types/src/version.rs new file mode 100644 index 0000000000..89f577cc0a --- /dev/null +++ b/ibc-eureka-core/ics03-connection/types/src/version.rs @@ -0,0 +1,403 @@ +//! Defines connection versioning type and functions + +use core::fmt::Display; + +use ibc_core_host_types::error::DecodingError; +use ibc_primitives::prelude::*; +use ibc_primitives::utils::PrettySlice; +use ibc_proto::ibc::core::connection::v1::Version as RawVersion; +use ibc_proto::Protobuf; + +use crate::error::ConnectionError; + +/// Stores the identifier and the features supported by a version +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Version { + /// unique version identifier + identifier: String, + /// list of features compatible with the specified identifier + features: Vec, +} + +impl Version { + /// Checks whether the version has a matching version identifier and its + /// feature set is a subset of the supported features + pub fn verify_is_supported( + &self, + supported_versions: &[Version], + ) -> Result<(), ConnectionError> { + let maybe_supported_version = find_supported_version(self, supported_versions)?; + + if self.features.is_empty() { + return Err(ConnectionError::MissingFeatures); + } + + for feature in self.features.iter() { + maybe_supported_version.verify_feature_supported(feature.to_string())?; + } + Ok(()) + } + + /// Checks whether the given feature is supported in this version + pub fn verify_feature_supported(&self, feature: String) -> Result<(), ConnectionError> { + if !self.features.contains(&feature) { + return Err(ConnectionError::MissingFeatures); + } + Ok(()) + } + + /// Returns the lists of supported versions + pub fn compatibles() -> Vec { + vec![Self { + identifier: "1".to_string(), + features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], + }] + } +} + +impl Protobuf for Version {} + +impl TryFrom for Version { + type Error = DecodingError; + + fn try_from(value: RawVersion) -> Result { + if value.identifier.trim().is_empty() { + return Err(DecodingError::missing_raw_data("version identifier")); + } + for feature in value.features.iter() { + if feature.trim().is_empty() { + return Err(DecodingError::missing_raw_data("version features")); + } + } + Ok(Version { + identifier: value.identifier, + features: value.features, + }) + } +} + +impl From for RawVersion { + fn from(value: Version) -> Self { + Self { + identifier: value.identifier, + features: value.features, + } + } +} + +impl Display for Version { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Version {{ identifier: {}, features: {} }}", + self.identifier, + PrettySlice(&self.features) + ) + } +} + +/// Iterates over the descending ordered set of compatible IBC versions and +/// selects the first version with a version identifier that is supported by the +/// counterparty. The returned version contains a feature set with the +/// intersection of the features supported by the source and counterparty +/// chains. If the feature set intersection is nil, the search for a +/// compatible version continues. This function is called in the `conn_open_try` +/// handshake procedure. +/// +/// NOTE: Empty feature sets are not currently allowed for a chosen version. +pub fn pick_version( + supported_versions: &[Version], + counterparty_versions: &[Version], +) -> Result { + let mut intersection: Vec = Vec::new(); + + for sv in supported_versions.iter() { + if let Ok(cv) = find_supported_version(sv, counterparty_versions) { + if let Ok(feature_set) = get_feature_set_intersection(&sv.features, &cv.features) { + intersection.push(Version { + identifier: cv.identifier, + features: feature_set, + }) + } + } + } + + if intersection.is_empty() { + return Err(ConnectionError::MissingCommonVersion); + } + + intersection.sort_by(|a, b| a.identifier.cmp(&b.identifier)); + + Ok(intersection[0].clone()) +} + +/// Returns the version from the list of supported versions that matches the +/// given reference version. +fn find_supported_version( + version: &Version, + supported_versions: &[Version], +) -> Result { + supported_versions + .iter() + .find(|sv| sv.identifier == version.identifier) + .ok_or(ConnectionError::MissingCommonVersion) + .cloned() +} + +/// Returns the intersections of supported features by a host and the +/// counterparty features. This is done by iterating over all the features in +/// the host supported version and seeing if they exist in the feature set for +/// the counterparty version. +fn get_feature_set_intersection( + supported_features: &[String], + counterparty_features: &[String], +) -> Result, ConnectionError> { + let feature_set_intersection: Vec = supported_features + .iter() + .filter(|f| counterparty_features.contains(f)) + .cloned() + .collect(); + + if feature_set_intersection.is_empty() { + return Err(ConnectionError::MissingFeatures); + } + + Ok(feature_set_intersection) +} + +#[cfg(test)] +mod tests { + use ibc_primitives::prelude::*; + use ibc_proto::ibc::core::connection::v1::Version as RawVersion; + + use crate::error::ConnectionError; + use crate::version::{pick_version, Version}; + + fn get_dummy_features() -> Vec { + vec!["ORDER_RANDOM".to_string(), "ORDER_UNORDERED".to_string()] + } + + fn good_versions() -> Vec { + vec![ + Version { + identifier: "1".to_string(), + features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], + } + .into(), + RawVersion { + identifier: "2".to_string(), + features: get_dummy_features(), + }, + ] + .into_iter() + .collect() + } + + fn bad_versions_identifier() -> Vec { + vec![RawVersion { + identifier: "".to_string(), + features: get_dummy_features(), + }] + .into_iter() + .collect() + } + + fn bad_versions_features() -> Vec { + vec![RawVersion { + identifier: "2".to_string(), + features: vec!["".to_string()], + }] + .into_iter() + .collect() + } + + fn overlapping() -> (Vec, Vec, Version) { + ( + vec![ + Version { + identifier: "1".to_string(), + features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], + }, + Version { + identifier: "3".to_string(), + features: get_dummy_features(), + }, + Version { + identifier: "4".to_string(), + features: get_dummy_features(), + }, + ] + .into_iter() + .collect(), + vec![ + Version { + identifier: "2".to_string(), + features: get_dummy_features(), + }, + Version { + identifier: "4".to_string(), + features: get_dummy_features(), + }, + Version { + identifier: "3".to_string(), + features: get_dummy_features(), + }, + ] + .into_iter() + .collect(), + // Should pick version 3 as it's the lowest of the intersection {3, 4} + Version { + identifier: "3".to_string(), + features: get_dummy_features(), + }, + ) + } + + fn disjoint() -> (Vec, Vec) { + ( + vec![Version { + identifier: "1".to_string(), + features: Vec::new(), + }] + .into_iter() + .collect(), + vec![Version { + identifier: "2".to_string(), + features: Vec::new(), + }] + .into_iter() + .collect(), + ) + } + + #[test] + fn verify() { + struct Test { + name: String, + versions: Vec, + want_pass: bool, + } + let tests: Vec = vec![ + Test { + name: "Compatible versions".to_string(), + versions: vec![Version { + identifier: "1".to_string(), + features: get_dummy_features(), + } + .into()], + want_pass: true, + }, + Test { + name: "Multiple versions".to_string(), + versions: good_versions(), + want_pass: true, + }, + Test { + name: "Bad version identifier".to_string(), + versions: bad_versions_identifier(), + want_pass: false, + }, + Test { + name: "Bad version feature".to_string(), + versions: bad_versions_features(), + want_pass: false, + }, + Test { + name: "Bad versions empty".to_string(), + versions: Vec::new(), + want_pass: true, + }, + ]; + + for test in tests { + let versions = test + .versions + .into_iter() + .map(Version::try_from) + .collect::, _>>(); + + assert_eq!( + test.want_pass, + versions.is_ok(), + "Validate versions failed for test {} with error {:?}", + test.name, + versions.err(), + ); + } + } + #[test] + fn pick() { + struct Test { + name: String, + supported: Vec, + counterparty: Vec, + picked: Result, + want_pass: bool, + } + let tests: Vec = vec![ + Test { + name: "Compatible versions".to_string(), + supported: Version::compatibles(), + counterparty: Version::compatibles(), + picked: Ok(Version { + identifier: "1".to_string(), + features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], + }), + want_pass: true, + }, + Test { + name: "Overlapping versions".to_string(), + supported: overlapping().0, + counterparty: overlapping().1, + picked: Ok(overlapping().2), + want_pass: true, + }, + Test { + name: "Disjoint versions".to_string(), + supported: disjoint().0, + counterparty: disjoint().1, + picked: Err(ConnectionError::MissingCommonVersion), + want_pass: false, + }, + ]; + + for test in tests { + let version = pick_version(&test.supported, &test.counterparty); + + assert_eq!( + test.want_pass, + version.is_ok(), + "Validate versions failed for test {}", + test.name, + ); + + if test.want_pass { + assert_eq!(version.unwrap(), test.picked.unwrap()); + } + } + } + #[test] + fn serialize() { + let def = Version { + identifier: "1".to_string(), + features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()], + }; + let def_raw: RawVersion = def.clone().into(); + let def_back = def_raw.try_into().unwrap(); + assert_eq!(def, def_back); + } +} diff --git a/ibc-eureka-core/ics04-channel/Cargo.toml b/ibc-eureka-core/ics04-channel/Cargo.toml new file mode 100644 index 0000000000..1ad973cc18 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/Cargo.toml @@ -0,0 +1,83 @@ +[package] +name = "ibc-core-channel" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "channel" ] +readme = "./../README.md" + +description = """ + Maintained by `ibc-rs`, contains the implementation of the ICS-04 Channel & Packet Semantics and + re-exports essential data structures and domain types from `ibc-core-channel-types` crate. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ibc-core-client = { workspace = true } +ibc-core-connection = { workspace = true } +ibc-core-channel-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-core-router = { workspace = true } +ibc-primitives = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "ibc-core-client/std", + "ibc-core-connection/std", + "ibc-core-channel-types/std", + "ibc-core-commitment-types/std", + "ibc-core-host/std", + "ibc-core-handler-types/std", + "ibc-core-router/std", + "ibc-primitives/std", +] +serde = [ + "ibc-core-client/serde", + "ibc-core-connection/serde", + "ibc-core-channel-types/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host/serde", + "ibc-core-handler-types/serde", + "ibc-core-router/serde", + "ibc-primitives/serde", +] +schema = [ + "ibc-core-client/schema", + "ibc-core-connection/schema", + "ibc-core-channel-types/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host/schema", + "ibc-core-handler-types/schema", + "ibc-core-router/schema", + "ibc-primitives/schema", + "serde", + "std", +] +borsh = [ + "ibc-core-client/borsh", + "ibc-core-connection/borsh", + "ibc-core-channel-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host/borsh", + "ibc-core-handler-types/borsh", + "ibc-core-router/borsh", + "ibc-primitives/borsh", +] +parity-scale-codec = [ + "ibc-core-client/parity-scale-codec", + "ibc-core-connection/parity-scale-codec", + "ibc-core-channel-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-core-router/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics04-channel/src/context.rs b/ibc-eureka-core/ics04-channel/src/context.rs new file mode 100644 index 0000000000..92844b4d46 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/context.rs @@ -0,0 +1,101 @@ +//! ICS4 (channel) context. + +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::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}; +use ibc_primitives::prelude::*; + +/// Methods required in send packet validation, to be implemented by the host +pub trait SendPacketValidationContext { + type V: ClientValidationContext; + + /// Retrieve the context that implements all clients' `ValidationContext`. + 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; + + /// Returns the ConnectionState for the given identifier `connection_id`. + fn connection_end(&self, connection_id: &ConnectionId) -> Result; + + fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) -> Result; +} + +impl SendPacketValidationContext for T +where + T: ValidationContext, +{ + type V = T::V; + + fn get_client_validation_context(&self) -> &Self::V { + self.get_client_validation_context() + } + + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { + self.channel_end(channel_end_path) + } + + fn connection_end(&self, connection_id: &ConnectionId) -> Result { + self.connection_end(connection_id) + } + + fn get_next_sequence_send(&self, seq_send_path: &SeqSendPath) -> Result { + self.get_next_sequence_send(seq_send_path) + } +} + +/// Methods required in send packet execution, to be implemented by the host +pub trait SendPacketExecutionContext: SendPacketValidationContext { + fn store_next_sequence_send( + &mut self, + seq_send_path: &SeqSendPath, + seq: Sequence, + ) -> Result<(), HostError>; + + fn store_packet_commitment( + &mut self, + commitment_path: &CommitmentPath, + commitment: PacketCommitment, + ) -> Result<(), HostError>; + + /// Ibc events + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError>; + + /// Logging facility + fn log_message(&mut self, message: String) -> Result<(), HostError>; +} + +impl SendPacketExecutionContext for T +where + T: ExecutionContext, +{ + fn store_next_sequence_send( + &mut self, + seq_send_path: &SeqSendPath, + seq: Sequence, + ) -> Result<(), HostError> { + self.store_next_sequence_send(seq_send_path, seq) + } + + fn store_packet_commitment( + &mut self, + commitment_path: &CommitmentPath, + commitment: PacketCommitment, + ) -> Result<(), HostError> { + self.store_packet_commitment(commitment_path, commitment) + } + + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError> { + self.emit_ibc_event(event) + } + + fn log_message(&mut self, message: String) -> Result<(), HostError> { + self.log_message(message) + } +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs b/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs new file mode 100644 index 0000000000..fddea0d821 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs @@ -0,0 +1,201 @@ +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; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ + AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, SeqAckPath, +}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; + +pub fn acknowledgement_packet_validate( + ctx_a: &ValCtx, + module: &dyn Module, + msg: MsgAcknowledgement, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_a, &msg)?; + + 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<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let chan_end_path_on_a = + ChannelEndPath::new(&msg.packet.port_id_on_a, &msg.packet.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + let conn_id_on_a = &chan_end_on_a.connection_hops()[0]; + + // In all cases, this event is emitted + let event = IbcEvent::AcknowledgePacket(AcknowledgePacket::new( + msg.packet.clone(), + chan_end_on_a.ordering, + conn_id_on_a.clone(), + )); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(event)?; + + let commitment_path_on_a = CommitmentPath::new( + &msg.packet.port_id_on_a, + &msg.packet.chan_id_on_a, + msg.packet.seq_on_a, + ); + + // check if we're in the NO-OP case + if ctx_a.get_packet_commitment(&commitment_path_on_a).is_err() { + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return Ok(()); + }; + + let (extras, cb_result) = + module.on_acknowledgement_packet_execute(&msg.packet, &msg.acknowledgement, &msg.signer); + + cb_result?; + + // apply state changes + { + ctx_a.delete_packet_commitment(&commitment_path_on_a)?; + + if let Order::Ordered = chan_end_on_a.ordering { + // Note: in validation, we verified that `msg.packet.sequence == nextSeqRecv` + // (where `nextSeqRecv` is the value in the store) + let seq_ack_path_on_a = + SeqAckPath::new(&msg.packet.port_id_on_a, &msg.packet.chan_id_on_a); + ctx_a.store_next_sequence_ack(&seq_ack_path_on_a, msg.packet.seq_on_a.increment())?; + } + } + + // emit events and logs + { + ctx_a.log_message("success: packet acknowledgement".to_string())?; + + // Note: Acknowledgement event was emitted at the beginning + + for module_event in extras.events { + ctx_a.emit_ibc_event(IbcEvent::Module(module_event))? + } + + for log_message in extras.log { + ctx_a.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_a: &Ctx, msg: &MsgAcknowledgement) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let packet = &msg.packet; + let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + chan_end_on_a.verify_state_matches(&ChannelState::Open)?; + + let counterparty = Counterparty::new( + packet.port_id_on_b.clone(), + Some(packet.chan_id_on_b.clone()), + ); + + chan_end_on_a.verify_counterparty_matches(&counterparty)?; + + let conn_id_on_a = &chan_end_on_a.connection_hops()[0]; + let conn_end_on_a = ctx_a.connection_end(conn_id_on_a)?; + + conn_end_on_a.verify_state_matches(&ConnectionState::Open)?; + + let commitment_path_on_a = + CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a); + + // Verify packet commitment + let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else { + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return Ok(()); + }; + + 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(ChannelError::MismatchedPacketSequence { + actual: packet.seq_on_a, + expected: next_seq_ack, + }); + } + } + + // Verify proofs + { + let client_id_on_a = conn_end_on_a.client_id(); + + let client_val_ctx_a = ctx_a.get_client_validation_context(); + + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + + 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( + client_id_on_a.clone(), + msg.proof_height_on_b.revision_number(), + msg.proof_height_on_b.revision_height(), + ); + let consensus_state_of_b_on_a = + client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; + let ack_commitment = compute_ack_commitment(&msg.acknowledgement); + let ack_path_on_b = + AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a); + + 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(), + )?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs new file mode 100644 index 0000000000..798f628e12 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs @@ -0,0 +1,160 @@ +//! Protocol logic specific to ICS4 messages of type `MsgChannelCloseConfirm`. + +use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Protobuf; + +pub fn chan_close_confirm_validate( + ctx_b: &ValCtx, + module: &dyn Module, + msg: MsgChannelCloseConfirm, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_b, &msg)?; + + module.on_chan_close_confirm_validate(&msg.port_id_on_b, &msg.chan_id_on_b)?; + + Ok(()) +} + +pub fn chan_close_confirm_execute( + ctx_b: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgChannelCloseConfirm, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let extras = module.on_chan_close_confirm_execute(&msg.port_id_on_b, &msg.chan_id_on_b)?; + let chan_end_path_on_b = ChannelEndPath::new(&msg.port_id_on_b, &msg.chan_id_on_b); + let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?; + + // state changes + { + let chan_end_on_b = { + let mut chan_end_on_b = chan_end_on_b.clone(); + chan_end_on_b.set_state(State::Closed); + chan_end_on_b + }; + ctx_b.store_channel(&chan_end_path_on_b, chan_end_on_b)?; + } + + // emit events and logs + { + ctx_b.log_message("success: channel close confirm".to_string())?; + + let core_event = { + let port_id_on_a = chan_end_on_b.counterparty().port_id.clone(); + let chan_id_on_a = chan_end_on_b + .counterparty() + .channel_id + .clone() + .ok_or(ChannelError::MissingCounterparty)?; + let conn_id_on_b = chan_end_on_b.connection_hops[0].clone(); + + IbcEvent::CloseConfirmChannel(CloseConfirm::new( + msg.port_id_on_b.clone(), + msg.chan_id_on_b.clone(), + port_id_on_a, + chan_id_on_a, + conn_id_on_b, + )) + }; + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_b.emit_ibc_event(core_event)?; + + for module_event in extras.events { + ctx_b.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_b.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_b: &Ctx, msg: &MsgChannelCloseConfirm) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_b.validate_message_signer(&msg.signer)?; + + // Retrieve the old channel end and validate it against the message. + let chan_end_path_on_b = ChannelEndPath::new(&msg.port_id_on_b, &msg.chan_id_on_b); + let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?; + + // Validate that the channel end is in a state where it can be closed. + chan_end_on_b.verify_not_closed()?; + + let conn_end_on_b = ctx_b.connection_end(&chan_end_on_b.connection_hops()[0])?; + + conn_end_on_b.verify_state_matches(&ConnectionState::Open)?; + + // Verify proofs + { + let client_id_on_b = conn_end_on_b.client_id(); + + let client_val_ctx_b = ctx_b.get_client_validation_context(); + + let client_state_of_a_on_b = client_val_ctx_b.client_state(client_id_on_b)?; + + client_state_of_a_on_b + .status(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( + client_id_on_b.clone(), + msg.proof_height_on_a.revision_number(), + msg.proof_height_on_a.revision_height(), + ); + let consensus_state_of_a_on_b = + client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; + let prefix_on_a = conn_end_on_b.counterparty().prefix(); + let port_id_on_a = &chan_end_on_b.counterparty().port_id; + let chan_id_on_a = chan_end_on_b + .counterparty() + .channel_id() + .ok_or(ChannelError::MissingCounterparty)?; + 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, + *chan_end_on_b.ordering(), + Counterparty::new(msg.port_id_on_b.clone(), Some(msg.chan_id_on_b.clone())), + vec![conn_id_on_a.clone()], + chan_end_on_b.version().clone(), + )?; + let chan_end_path_on_a = ChannelEndPath::new(port_id_on_a, chan_id_on_a); + + // 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(), + )?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs new file mode 100644 index 0000000000..6a0a6e1892 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs @@ -0,0 +1,117 @@ +//! Protocol logic specific to ICS4 messages of type `MsgChannelCloseInit`. +use ibc_core_channel_types::channel::State; +use ibc_core_channel_types::error::ChannelError; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::ChannelEndPath; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; + +pub fn chan_close_init_validate( + ctx_a: &ValCtx, + module: &dyn Module, + msg: MsgChannelCloseInit, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_a, &msg)?; + + module.on_chan_close_init_validate(&msg.port_id_on_a, &msg.chan_id_on_a)?; + + Ok(()) +} + +pub fn chan_close_init_execute( + ctx_a: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgChannelCloseInit, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let extras = module.on_chan_close_init_execute(&msg.port_id_on_a, &msg.chan_id_on_a)?; + let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + // state changes + { + let chan_end_on_a = { + let mut chan_end_on_a = chan_end_on_a.clone(); + chan_end_on_a.set_state(State::Closed); + chan_end_on_a + }; + + ctx_a.store_channel(&chan_end_path_on_a, chan_end_on_a)?; + } + + // emit events and logs + { + ctx_a.log_message("success: channel close init".to_string())?; + + let core_event = { + let port_id_on_b = chan_end_on_a.counterparty().port_id.clone(); + let chan_id_on_b = chan_end_on_a + .counterparty() + .channel_id + .clone() + .ok_or(ChannelError::MissingCounterparty)?; + let conn_id_on_a = chan_end_on_a.connection_hops[0].clone(); + + IbcEvent::CloseInitChannel(CloseInit::new( + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + port_id_on_b, + chan_id_on_b, + conn_id_on_a, + )) + }; + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(core_event)?; + + for module_event in extras.events { + ctx_a.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_a.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_a: &Ctx, msg: &MsgChannelCloseInit) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + // Validate that the channel end is in a state where it can be closed. + chan_end_on_a.verify_not_closed()?; + + // An OPEN IBC connection running on the local (host) chain should exist. + chan_end_on_a.verify_connection_hops_length()?; + + let conn_end_on_a = ctx_a.connection_end(&chan_end_on_a.connection_hops()[0])?; + + conn_end_on_a.verify_state_matches(&ConnectionState::Open)?; + + let client_id_on_a = conn_end_on_a.client_id(); + + let client_val_ctx_a = ctx_a.get_client_validation_context(); + + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + client_state_of_b_on_a + .status(ctx_a.get_client_validation_context(), client_id_on_a)? + .verify_is_active()?; + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs new file mode 100644 index 0000000000..c6d81a9fe8 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs @@ -0,0 +1,157 @@ +//! Protocol logic specific to ICS4 messages of type `MsgChannelOpenAck`. +use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Protobuf; + +pub fn chan_open_ack_validate( + ctx_a: &ValCtx, + module: &dyn Module, + msg: MsgChannelOpenAck, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_a, &msg)?; + + module.on_chan_open_ack_validate(&msg.port_id_on_a, &msg.chan_id_on_a, &msg.version_on_b)?; + + Ok(()) +} + +pub fn chan_open_ack_execute( + ctx_a: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgChannelOpenAck, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let extras = + module.on_chan_open_ack_execute(&msg.port_id_on_a, &msg.chan_id_on_a, &msg.version_on_b)?; + let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + // state changes + { + let chan_end_on_a = { + let mut chan_end_on_a = chan_end_on_a.clone(); + + chan_end_on_a.set_state(State::Open); + chan_end_on_a.set_version(msg.version_on_b.clone()); + chan_end_on_a.set_counterparty_channel_id(msg.chan_id_on_b.clone()); + + chan_end_on_a + }; + ctx_a.store_channel(&chan_end_path_on_a, chan_end_on_a)?; + } + + // emit events and logs + { + ctx_a.log_message("success: channel open ack".to_string())?; + + let core_event = { + let port_id_on_b = chan_end_on_a.counterparty().port_id.clone(); + let conn_id_on_a = chan_end_on_a.connection_hops[0].clone(); + + IbcEvent::OpenAckChannel(OpenAck::new( + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + port_id_on_b, + msg.chan_id_on_b, + conn_id_on_a, + )) + }; + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(core_event)?; + + for module_event in extras.events { + ctx_a.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_a.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_a: &Ctx, msg: &MsgChannelOpenAck) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + // Validate that the channel end is in a state where it can be ack. + chan_end_on_a.verify_state_matches(&ChannelState::Init)?; + + // An OPEN IBC connection running on the local (host) chain should exist. + chan_end_on_a.verify_connection_hops_length()?; + + let conn_end_on_a = ctx_a.connection_end(&chan_end_on_a.connection_hops()[0])?; + + conn_end_on_a.verify_state_matches(&ConnectionState::Open)?; + + // Verify proofs + { + let client_id_on_a = conn_end_on_a.client_id(); + let client_val_ctx_a = ctx_a.get_client_validation_context(); + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + + 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( + client_id_on_a.clone(), + msg.proof_height_on_b.revision_number(), + msg.proof_height_on_b.revision_height(), + ); + let consensus_state_of_b_on_a = + client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; + let prefix_on_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(ConnectionError::MissingCounterparty)?; + + let expected_chan_end_on_b = ChannelEnd::new( + ChannelState::TryOpen, + // Note: Both ends of a channel must have the same ordering, so it's + // fine to use A's ordering here + *chan_end_on_a.ordering(), + Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_a.clone())), + vec![conn_id_on_b.clone()], + msg.version_on_b.clone(), + )?; + let chan_end_path_on_b = ChannelEndPath::new(port_id_on_b, &msg.chan_id_on_b); + + // 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(), + )?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs new file mode 100644 index 0000000000..2f1fc56d37 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs @@ -0,0 +1,160 @@ +//! Protocol logic specific to ICS4 messages of type `MsgChannelOpenConfirm`. + +use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Protobuf; + +pub fn chan_open_confirm_validate( + ctx_b: &ValCtx, + module: &dyn Module, + msg: MsgChannelOpenConfirm, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_b, &msg)?; + + module.on_chan_open_confirm_validate(&msg.port_id_on_b, &msg.chan_id_on_b)?; + + Ok(()) +} + +pub fn chan_open_confirm_execute( + ctx_b: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgChannelOpenConfirm, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let extras = module.on_chan_open_confirm_execute(&msg.port_id_on_b, &msg.chan_id_on_b)?; + let chan_end_path_on_b = ChannelEndPath::new(&msg.port_id_on_b, &msg.chan_id_on_b); + let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?; + + // state changes + { + let chan_end_on_b = { + let mut chan_end_on_b = chan_end_on_b.clone(); + chan_end_on_b.set_state(State::Open); + + chan_end_on_b + }; + ctx_b.store_channel(&chan_end_path_on_b, chan_end_on_b)?; + } + + // emit events and logs + { + ctx_b.log_message("success: channel open confirm".to_string())?; + + let conn_id_on_b = chan_end_on_b.connection_hops[0].clone(); + let port_id_on_a = chan_end_on_b.counterparty().port_id.clone(); + let chan_id_on_a = chan_end_on_b + .counterparty() + .channel_id + .clone() + .ok_or(ChannelError::MissingCounterparty)?; + + let core_event = IbcEvent::OpenConfirmChannel(OpenConfirm::new( + msg.port_id_on_b.clone(), + msg.chan_id_on_b.clone(), + port_id_on_a, + chan_id_on_a, + conn_id_on_b, + )); + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_b.emit_ibc_event(core_event)?; + + for module_event in extras.events { + ctx_b.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_b.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_b: &Ctx, msg: &MsgChannelOpenConfirm) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_b.validate_message_signer(&msg.signer)?; + + // Unwrap the old channel end and validate it against the message. + let chan_end_path_on_b = ChannelEndPath::new(&msg.port_id_on_b, &msg.chan_id_on_b); + let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?; + + // Validate that the channel end is in a state where it can be confirmed. + chan_end_on_b.verify_state_matches(&ChannelState::TryOpen)?; + + // An OPEN IBC connection running on the local (host) chain should exist. + chan_end_on_b.verify_connection_hops_length()?; + + let conn_end_on_b = ctx_b.connection_end(&chan_end_on_b.connection_hops()[0])?; + + conn_end_on_b.verify_state_matches(&ConnectionState::Open)?; + + // Verify proofs + { + let client_id_on_b = conn_end_on_b.client_id(); + let client_val_ctx_b = ctx_b.get_client_validation_context(); + let client_state_of_a_on_b = client_val_ctx_b.client_state(client_id_on_b)?; + + client_state_of_a_on_b + .status(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( + client_id_on_b.clone(), + msg.proof_height_on_a.revision_number(), + msg.proof_height_on_a.revision_height(), + ); + let consensus_state_of_a_on_b = + client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; + let prefix_on_a = conn_end_on_b.counterparty().prefix(); + let port_id_on_a = &chan_end_on_b.counterparty().port_id; + let chan_id_on_a = chan_end_on_b + .counterparty() + .channel_id() + .ok_or(ChannelError::MissingCounterparty)?; + 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, + *chan_end_on_b.ordering(), + Counterparty::new(msg.port_id_on_b.clone(), Some(msg.chan_id_on_b.clone())), + vec![conn_id_on_a.clone()], + chan_end_on_b.version.clone(), + )?; + let chan_end_path_on_a = ChannelEndPath::new(port_id_on_a, chan_id_on_a); + + // 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(), + )?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs new file mode 100644 index 0000000000..fe931efa0c --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs @@ -0,0 +1,135 @@ +//! 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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::identifiers::ChannelId; +use ibc_core_host::types::path::{ChannelEndPath, SeqAckPath, SeqRecvPath, SeqSendPath}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; + +pub fn chan_open_init_validate( + ctx_a: &ValCtx, + module: &dyn Module, + msg: MsgChannelOpenInit, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_a, &msg)?; + let chan_id_on_a = ChannelId::new(ctx_a.channel_counter()?); + + module.on_chan_open_init_validate( + msg.ordering, + &msg.connection_hops_on_a, + &msg.port_id_on_a, + &chan_id_on_a, + &Counterparty::new(msg.port_id_on_b.clone(), None), + &msg.version_proposal, + )?; + + Ok(()) +} + +pub fn chan_open_init_execute( + ctx_a: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgChannelOpenInit, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let chan_id_on_a = ChannelId::new(ctx_a.channel_counter()?); + let (extras, version) = module.on_chan_open_init_execute( + msg.ordering, + &msg.connection_hops_on_a, + &msg.port_id_on_a, + &chan_id_on_a, + &Counterparty::new(msg.port_id_on_b.clone(), None), + &msg.version_proposal, + )?; + + let conn_id_on_a = msg.connection_hops_on_a[0].clone(); + + // state changes + { + let chan_end_on_a = ChannelEnd::new( + State::Init, + msg.ordering, + Counterparty::new(msg.port_id_on_b.clone(), None), + msg.connection_hops_on_a.clone(), + msg.version_proposal.clone(), + )?; + let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &chan_id_on_a); + ctx_a.store_channel(&chan_end_path_on_a, chan_end_on_a)?; + + ctx_a.increase_channel_counter()?; + + // Initialize send, recv, and ack sequence numbers. + let seq_send_path = SeqSendPath::new(&msg.port_id_on_a, &chan_id_on_a); + ctx_a.store_next_sequence_send(&seq_send_path, 1.into())?; + + let seq_recv_path = SeqRecvPath::new(&msg.port_id_on_a, &chan_id_on_a); + ctx_a.store_next_sequence_recv(&seq_recv_path, 1.into())?; + + let seq_ack_path = SeqAckPath::new(&msg.port_id_on_a, &chan_id_on_a); + ctx_a.store_next_sequence_ack(&seq_ack_path, 1.into())?; + } + + // emit events and logs + { + ctx_a.log_message(format!( + "success: channel open init with channel identifier: {chan_id_on_a}" + ))?; + let core_event = IbcEvent::OpenInitChannel(OpenInit::new( + msg.port_id_on_a.clone(), + chan_id_on_a.clone(), + msg.port_id_on_b, + conn_id_on_a, + version, + )); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(core_event)?; + + for module_event in extras.events { + ctx_a.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_a.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_a: &Ctx, msg: &MsgChannelOpenInit) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + msg.verify_connection_hops_length()?; + // An IBC connection running on the local (host) chain should exist. + let conn_end_on_a = ctx_a.connection_end(&msg.connection_hops_on_a[0])?; + + // Note: Not needed check if the connection end is OPEN. Optimistic channel handshake is allowed. + + let client_id_on_a = conn_end_on_a.client_id(); + let client_val_ctx_a = ctx_a.get_client_validation_context(); + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + + client_state_of_b_on_a + .status(ctx_a.get_client_validation_context(), client_id_on_a)? + .verify_is_active()?; + + let conn_version = conn_end_on_a.versions(); + + conn_version[0].verify_feature_supported(msg.ordering.to_string())?; + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs new file mode 100644 index 0000000000..c8e7357004 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs @@ -0,0 +1,182 @@ +//! Protocol logic specific to ICS4 messages of type `MsgChannelOpenTry`. + +use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State as ChannelState}; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::identifiers::ChannelId; +use ibc_core_host::types::path::{ + ChannelEndPath, ClientConsensusStatePath, Path, SeqAckPath, SeqRecvPath, SeqSendPath, +}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Protobuf; + +pub fn chan_open_try_validate( + ctx_b: &ValCtx, + module: &dyn Module, + msg: MsgChannelOpenTry, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + validate(ctx_b, &msg)?; + + let chan_id_on_b = ChannelId::new(ctx_b.channel_counter()?); + + module.on_chan_open_try_validate( + msg.ordering, + &msg.connection_hops_on_b, + &msg.port_id_on_b, + &chan_id_on_b, + &Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_a.clone())), + &msg.version_supported_on_a, + )?; + + Ok(()) +} + +pub fn chan_open_try_execute( + ctx_b: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgChannelOpenTry, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let chan_id_on_b = ChannelId::new(ctx_b.channel_counter()?); + let (extras, version) = module.on_chan_open_try_execute( + msg.ordering, + &msg.connection_hops_on_b, + &msg.port_id_on_b, + &chan_id_on_b, + &Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_a.clone())), + &msg.version_supported_on_a, + )?; + + let conn_id_on_b = msg.connection_hops_on_b[0].clone(); + + // state changes + { + let chan_end_on_b = ChannelEnd::new( + ChannelState::TryOpen, + msg.ordering, + Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_a.clone())), + msg.connection_hops_on_b.clone(), + version.clone(), + )?; + + let chan_end_path_on_b = ChannelEndPath::new(&msg.port_id_on_b, &chan_id_on_b); + ctx_b.store_channel(&chan_end_path_on_b, chan_end_on_b)?; + ctx_b.increase_channel_counter()?; + + // Initialize send, recv, and ack sequence numbers. + let seq_send_path = SeqSendPath::new(&msg.port_id_on_b, &chan_id_on_b); + ctx_b.store_next_sequence_send(&seq_send_path, 1.into())?; + + let seq_recv_path = SeqRecvPath::new(&msg.port_id_on_b, &chan_id_on_b); + ctx_b.store_next_sequence_recv(&seq_recv_path, 1.into())?; + + let seq_ack_path = SeqAckPath::new(&msg.port_id_on_b, &chan_id_on_b); + ctx_b.store_next_sequence_ack(&seq_ack_path, 1.into())?; + } + + // emit events and logs + { + ctx_b.log_message(format!( + "success: channel open try with channel identifier: {chan_id_on_b}" + ))?; + + let core_event = IbcEvent::OpenTryChannel(OpenTry::new( + msg.port_id_on_b.clone(), + chan_id_on_b.clone(), + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + conn_id_on_b, + version, + )); + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_b.emit_ibc_event(core_event)?; + + for module_event in extras.events { + ctx_b.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_b.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_b: &Ctx, msg: &MsgChannelOpenTry) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_b.validate_message_signer(&msg.signer)?; + + msg.verify_connection_hops_length()?; + + let conn_end_on_b = ctx_b.connection_end(&msg.connection_hops_on_b[0])?; + + conn_end_on_b.verify_state_matches(&ConnectionState::Open)?; + + let conn_version = conn_end_on_b.versions(); + + conn_version[0].verify_feature_supported(msg.ordering.to_string())?; + + // Verify proofs + { + let client_id_on_b = conn_end_on_b.client_id(); + let client_val_ctx_b = ctx_b.get_client_validation_context(); + let client_state_of_a_on_b = client_val_ctx_b.client_state(client_id_on_b)?; + + client_state_of_a_on_b + .status(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( + client_id_on_b.clone(), + msg.proof_height_on_a.revision_number(), + msg.proof_height_on_a.revision_height(), + ); + let consensus_state_of_a_on_b = + client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; + let prefix_on_a = conn_end_on_b.counterparty().prefix(); + let 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(ConnectionError::MissingCounterparty)?; + + let expected_chan_end_on_a = ChannelEnd::new( + ChannelState::Init, + msg.ordering, + Counterparty::new(msg.port_id_on_b.clone(), None), + vec![conn_id_on_a.clone()], + msg.version_supported_on_a.clone(), + )?; + let chan_end_path_on_a = ChannelEndPath::new(&port_id_on_a, &chan_id_on_a); + + // 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(), + )?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/mod.rs b/ibc-eureka-core/ics04-channel/src/handler/mod.rs new file mode 100644 index 0000000000..b1ea237a10 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/mod.rs @@ -0,0 +1,24 @@ +//! This module implements the processing logic for ICS4 (channel) messages. +mod acknowledgement; +mod chan_close_confirm; +mod chan_close_init; +mod chan_open_ack; +mod chan_open_confirm; +mod chan_open_init; +mod chan_open_try; +mod recv_packet; +mod send_packet; +mod timeout; +mod timeout_on_close; + +pub use acknowledgement::*; +pub use chan_close_confirm::*; +pub use chan_close_init::*; +pub use chan_open_ack::*; +pub use chan_open_confirm::*; +pub use chan_open_init::*; +pub use chan_open_try::*; +pub use recv_packet::*; +pub use send_packet::*; +pub use timeout::*; +pub use timeout_on_close::*; diff --git a/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs b/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs new file mode 100644 index 0000000000..60bf301990 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs @@ -0,0 +1,273 @@ +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; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ + AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, + SeqRecvPath, +}; +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<(), ChannelError> +where + ValCtx: ValidationContext, +{ + // Note: this contains the validation for `write_acknowledgement` as well. + validate(ctx_b, &msg) + + // nothing to validate with the module, since `onRecvPacket` cannot fail. + // If any error occurs, then an "error acknowledgement" must be returned. +} + +pub fn recv_packet_execute( + ctx_b: &mut ExecCtx, + module: &mut dyn Module, + msg: MsgRecvPacket, +) -> Result<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let chan_end_path_on_b = + ChannelEndPath::new(&msg.packet.port_id_on_b, &msg.packet.chan_id_on_b); + let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?; + + // Check if another relayer already relayed the packet. + // We don't want to fail the transaction in this case. + { + let packet_already_received = match chan_end_on_b.ordering { + // Note: ibc-go doesn't make the check for `Order::None` channels + Order::None => false, + Order::Unordered => { + 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() + } + Order::Ordered => { + let seq_recv_path_on_b = + 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)?; + + // the sequence number has already been incremented, so + // another relayer already relayed the packet + msg.packet.seq_on_a < next_seq_recv + } + }; + + if packet_already_received { + return Ok(()); + } + } + + let (extras, acknowledgement) = module.on_recv_packet_execute(&msg.packet, &msg.signer); + + // state changes + { + // `recvPacket` core handler state changes + match chan_end_on_b.ordering { + Order::Unordered => { + let receipt_path_on_b = ReceiptPath { + port_id: msg.packet.port_id_on_b.clone(), + channel_id: msg.packet.chan_id_on_b.clone(), + sequence: msg.packet.seq_on_a, + }; + + ctx_b.store_packet_receipt(&receipt_path_on_b, Receipt::Ok)?; + } + Order::Ordered => { + let seq_recv_path_on_b = + 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)?; + ctx_b.store_next_sequence_recv(&seq_recv_path_on_b, next_seq_recv.increment())?; + } + _ => {} + } + let ack_path_on_b = AckPath::new( + &msg.packet.port_id_on_b, + &msg.packet.chan_id_on_b, + msg.packet.seq_on_a, + ); + // `writeAcknowledgement` handler state changes + ctx_b.store_packet_acknowledgement( + &ack_path_on_b, + compute_ack_commitment(&acknowledgement), + )?; + } + + // emit events and logs + { + ctx_b.log_message("success: packet receive".to_string())?; + ctx_b.log_message("success: packet write acknowledgement".to_string())?; + + let conn_id_on_b = &chan_end_on_b.connection_hops()[0]; + let event = IbcEvent::ReceivePacket(ReceivePacket::new( + msg.packet.clone(), + chan_end_on_b.ordering, + conn_id_on_b.clone(), + )); + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_b.emit_ibc_event(event)?; + let event = IbcEvent::WriteAcknowledgement(WriteAcknowledgement::new( + msg.packet, + acknowledgement, + conn_id_on_b.clone(), + )); + ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_b.emit_ibc_event(event)?; + + for module_event in extras.events { + ctx_b.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_b.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_b: &Ctx, msg: &MsgRecvPacket) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_b.validate_message_signer(&msg.signer)?; + + let chan_end_path_on_b = + ChannelEndPath::new(&msg.packet.port_id_on_b, &msg.packet.chan_id_on_b); + let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?; + + chan_end_on_b.verify_state_matches(&ChannelState::Open)?; + + let counterparty = Counterparty::new( + msg.packet.port_id_on_a.clone(), + Some(msg.packet.chan_id_on_a.clone()), + ); + + chan_end_on_b.verify_counterparty_matches(&counterparty)?; + + let conn_id_on_b = &chan_end_on_b.connection_hops()[0]; + let conn_end_on_b = ctx_b.connection_end(conn_id_on_b)?; + + conn_end_on_b.verify_state_matches(&ConnectionState::Open)?; + + let latest_height = ctx_b.host_height()?; + if msg.packet.timeout_height_on_b.has_expired(latest_height) { + return Err(ChannelError::InsufficientPacketHeight { + chain_height: latest_height, + timeout_height: msg.packet.timeout_height_on_b, + }); + } + + let latest_timestamp = ctx_b.host_timestamp()?; + if msg + .packet + .timeout_timestamp_on_b + .has_expired(&latest_timestamp) + { + return Err(ChannelError::ExpiredPacketTimestamp); + } + + // Verify proofs + { + let client_id_on_b = conn_end_on_b.client_id(); + let client_val_ctx_b = ctx_b.get_client_validation_context(); + let client_state_of_a_on_b = client_val_ctx_b.client_state(client_id_on_b)?; + + client_state_of_a_on_b + .status(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( + client_id_on_b.clone(), + msg.proof_height_on_a.revision_number(), + msg.proof_height_on_a.revision_height(), + ); + + let consensus_state_of_a_on_b = + client_val_ctx_b.consensus_state(&client_cons_state_path_on_b)?; + + let expected_commitment_on_a = compute_packet_commitment( + &msg.packet.data, + &msg.packet.timeout_height_on_b, + &msg.packet.timeout_timestamp_on_b, + ); + let commitment_path_on_a = CommitmentPath::new( + &msg.packet.port_id_on_a, + &msg.packet.chan_id_on_a, + msg.packet.seq_on_a, + ); + + 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(), + )?; + } + + match chan_end_on_b.ordering { + Order::Ordered => { + let seq_recv_path_on_b = + 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(ChannelError::MismatchedPacketSequence { + actual: msg.packet.seq_on_a, + expected: next_seq_recv, + }); + } + + if msg.packet.seq_on_a == next_seq_recv { + // Case where the recvPacket is successful and an + // acknowledgement will be written (not a no-op) + validate_write_acknowledgement(ctx_b, msg)?; + } + } + Order::Unordered => { + // 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(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<(), 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(ChannelError::DuplicateAcknowledgment(msg.packet.seq_on_a)); + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs b/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs new file mode 100644 index 0000000000..b393c74add --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs @@ -0,0 +1,138 @@ +use ibc_core_channel_types::channel::Counterparty; +use ibc_core_channel_types::commitment::compute_packet_commitment; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ + ChannelEndPath, ClientConsensusStatePath, CommitmentPath, SeqSendPath, +}; +use ibc_primitives::prelude::*; + +use crate::context::{SendPacketExecutionContext, SendPacketValidationContext}; + +/// Send the given packet, including all necessary validation. +/// +/// Equivalent to calling [`send_packet_validate`], followed by [`send_packet_execute`] +pub fn send_packet( + ctx_a: &mut impl SendPacketExecutionContext, + packet: Packet, +) -> Result<(), ChannelError> { + send_packet_validate(ctx_a, &packet)?; + send_packet_execute(ctx_a, packet) +} + +/// Validate that sending the given packet would succeed. +pub fn send_packet_validate( + ctx_a: &impl SendPacketValidationContext, + packet: &Packet, +) -> Result<(), ChannelError> { + if !packet.timeout_height_on_b.is_set() && !packet.timeout_timestamp_on_b.is_set() { + return Err(ChannelError::MissingTimeout); + } + + let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + // Checks the channel end not be `Closed`. + // This allows for optimistic packet processing before a channel opens + chan_end_on_a.verify_not_closed()?; + + let counterparty = Counterparty::new( + packet.port_id_on_b.clone(), + Some(packet.chan_id_on_b.clone()), + ); + + chan_end_on_a.verify_counterparty_matches(&counterparty)?; + + let conn_id_on_a = &chan_end_on_a.connection_hops()[0]; + + let conn_end_on_a = ctx_a.connection_end(conn_id_on_a)?; + + let client_id_on_a = conn_end_on_a.client_id(); + + let client_val_ctx_a = ctx_a.get_client_validation_context(); + + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + + client_state_of_b_on_a + .status(ctx_a.get_client_validation_context(), client_id_on_a)? + .verify_is_active()?; + + 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(ChannelError::InsufficientPacketHeight { + chain_height: latest_height_on_a, + timeout_height: packet.timeout_height_on_b, + }); + } + + let client_cons_state_path_on_a = ClientConsensusStatePath::new( + client_id_on_a.clone(), + latest_height_on_a.revision_number(), + latest_height_on_a.revision_height(), + ); + 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 packet_timestamp = packet.timeout_timestamp_on_b; + if packet_timestamp.has_expired(&latest_timestamp) { + 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(ChannelError::MismatchedPacketSequence { + actual: packet.seq_on_a, + expected: next_seq_send_on_a, + }); + } + + Ok(()) +} + +/// Send the packet without any validation. +/// +/// A prior call to [`send_packet_validate`] MUST have succeeded. +pub fn send_packet_execute( + ctx_a: &mut impl SendPacketExecutionContext, + packet: Packet, +) -> 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)?; + + ctx_a.store_next_sequence_send(&seq_send_path_on_a, next_seq_send_on_a.increment())?; + } + + ctx_a.store_packet_commitment( + &CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a), + compute_packet_commitment( + &packet.data, + &packet.timeout_height_on_b, + &packet.timeout_timestamp_on_b, + ), + )?; + + // emit events and logs + { + let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + let conn_id_on_a = &chan_end_on_a.connection_hops()[0]; + + ctx_a.log_message("success: packet send".to_string())?; + let event = IbcEvent::SendPacket(SendPacket::new( + packet, + chan_end_on_a.ordering, + conn_id_on_a.clone(), + )); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(event)?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/timeout.rs b/ibc-eureka-core/ics04-channel/src/handler/timeout.rs new file mode 100644 index 0000000000..da4e42d373 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/timeout.rs @@ -0,0 +1,254 @@ +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; +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::events::{IbcEvent, MessageEvent}; +use ibc_core_host::types::path::{ + ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, +}; +use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_core_router::module::Module; +use ibc_primitives::prelude::*; + +use super::timeout_on_close; + +pub enum TimeoutMsgType { + Timeout(MsgTimeout), + TimeoutOnClose(MsgTimeoutOnClose), +} + +pub fn timeout_packet_validate( + ctx_a: &ValCtx, + module: &dyn Module, + timeout_msg_type: TimeoutMsgType, +) -> Result<(), ChannelError> +where + ValCtx: ValidationContext, +{ + match &timeout_msg_type { + TimeoutMsgType::Timeout(msg) => validate(ctx_a, msg), + TimeoutMsgType::TimeoutOnClose(msg) => timeout_on_close::validate(ctx_a, msg), + }?; + + let (packet, signer) = match timeout_msg_type { + TimeoutMsgType::Timeout(msg) => (msg.packet, msg.signer), + TimeoutMsgType::TimeoutOnClose(msg) => (msg.packet, msg.signer), + }; + + 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<(), ChannelError> +where + ExecCtx: ExecutionContext, +{ + let (packet, signer) = match timeout_msg_type { + TimeoutMsgType::Timeout(msg) => (msg.packet, msg.signer), + TimeoutMsgType::TimeoutOnClose(msg) => (msg.packet, msg.signer), + }; + let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + // In all cases, this event is emitted + let event = IbcEvent::TimeoutPacket(TimeoutPacket::new(packet.clone(), chan_end_on_a.ordering)); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(event)?; + + let commitment_path_on_a = + CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a); + + // check if we're in the NO-OP case + if ctx_a.get_packet_commitment(&commitment_path_on_a).is_err() { + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return Ok(()); + }; + + let (extras, cb_result) = module.on_timeout_packet_execute(&packet, &signer); + + cb_result?; + + // apply state changes + let chan_end_on_a = { + ctx_a.delete_packet_commitment(&commitment_path_on_a)?; + + if let Order::Ordered = chan_end_on_a.ordering { + let mut chan_end_on_a = chan_end_on_a; + chan_end_on_a.state = State::Closed; + ctx_a.store_channel(&chan_end_path_on_a, chan_end_on_a.clone())?; + + chan_end_on_a + } else { + chan_end_on_a + } + }; + + // emit events and logs + { + ctx_a.log_message("success: packet timeout".to_string())?; + + if let Order::Ordered = chan_end_on_a.ordering { + let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone(); + + let event = IbcEvent::ChannelClosed(ChannelClosed::new( + packet.port_id_on_a.clone(), + packet.chan_id_on_a.clone(), + chan_end_on_a.counterparty().port_id.clone(), + chan_end_on_a.counterparty().channel_id.clone(), + conn_id_on_a, + chan_end_on_a.ordering, + )); + ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?; + ctx_a.emit_ibc_event(event)?; + } + + for module_event in extras.events { + ctx_a.emit_ibc_event(IbcEvent::Module(module_event))?; + } + + for log_message in extras.log { + ctx_a.log_message(log_message)?; + } + } + + Ok(()) +} + +fn validate(ctx_a: &Ctx, msg: &MsgTimeout) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let chan_end_on_a = ctx_a.channel_end(&ChannelEndPath::new( + &msg.packet.port_id_on_a, + &msg.packet.chan_id_on_a, + ))?; + + chan_end_on_a.verify_state_matches(&State::Open)?; + + let counterparty = Counterparty::new( + msg.packet.port_id_on_b.clone(), + Some(msg.packet.chan_id_on_b.clone()), + ); + + chan_end_on_a.verify_counterparty_matches(&counterparty)?; + + let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone(); + let conn_end_on_a = ctx_a.connection_end(&conn_id_on_a)?; + + //verify packet commitment + let commitment_path_on_a = CommitmentPath::new( + &msg.packet.port_id_on_a, + &msg.packet.chan_id_on_a, + msg.packet.seq_on_a, + ); + let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else { + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return Ok(()); + }; + + let expected_commitment_on_a = compute_packet_commitment( + &msg.packet.data, + &msg.packet.timeout_height_on_b, + &msg.packet.timeout_timestamp_on_b, + ); + + if commitment_on_a != expected_commitment_on_a { + return Err(ChannelError::MismatchedPacketCommitment { + expected: expected_commitment_on_a, + actual: commitment_on_a, + }); + } + + // Verify proofs + { + let client_id_on_a = conn_end_on_a.client_id(); + let client_val_ctx_a = ctx_a.get_client_validation_context(); + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + + 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)?; + + // check that timeout height or timeout timestamp has passed on the other end + let client_cons_state_path_on_a = ClientConsensusStatePath::new( + client_id_on_a.clone(), + msg.proof_height_on_b.revision_number(), + msg.proof_height_on_b.revision_height(), + ); + let consensus_state_of_b_on_a = + client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; + let 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(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, + }); + } + + 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 msg.packet.seq_on_a < msg.next_seq_recv_on_b { + 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); + + client_state_of_b_on_a.verify_membership( + conn_end_on_a.counterparty().prefix(), + &msg.proof_unreceived_on_b, + consensus_state_of_b_on_a.root(), + Path::SeqRecv(seq_recv_path_on_b), + msg.packet.seq_on_a.to_vec(), + ) + } + Order::Unordered => { + let receipt_path_on_b = ReceiptPath::new( + &msg.packet.port_id_on_b, + &msg.packet.chan_id_on_b, + msg.packet.seq_on_a, + ); + + client_state_of_b_on_a.verify_non_membership( + conn_end_on_a.counterparty().prefix(), + &msg.proof_unreceived_on_b, + consensus_state_of_b_on_a.root(), + Path::Receipt(receipt_path_on_b), + ) + } + Order::None => { + 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?; + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs b/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs new file mode 100644 index 0000000000..00d880577b --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs @@ -0,0 +1,163 @@ +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; +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_connection::types::error::ConnectionError; +use ibc_core_host::types::path::{ + ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, +}; +use ibc_core_host::ValidationContext; +use ibc_primitives::prelude::*; +use ibc_primitives::proto::Protobuf; + +pub fn validate(ctx_a: &Ctx, msg: &MsgTimeoutOnClose) -> Result<(), ChannelError> +where + Ctx: ValidationContext, +{ + ctx_a.validate_message_signer(&msg.signer)?; + + let packet = &msg.packet; + let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); + let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?; + + let counterparty = Counterparty::new( + packet.port_id_on_b.clone(), + Some(packet.chan_id_on_b.clone()), + ); + + chan_end_on_a.verify_counterparty_matches(&counterparty)?; + + let commitment_path_on_a = CommitmentPath::new( + &msg.packet.port_id_on_a, + &msg.packet.chan_id_on_a, + msg.packet.seq_on_a, + ); + + //verify the packet was sent, check the store + let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else { + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return Ok(()); + }; + + 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 { + expected: expected_commitment_on_a, + actual: commitment_on_a, + }); + } + + let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone(); + let conn_end_on_a = ctx_a.connection_end(&conn_id_on_a)?; + + // Verify proofs + { + let client_id_on_a = conn_end_on_a.client_id(); + let client_val_ctx_a = ctx_a.get_client_validation_context(); + let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?; + + 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( + client_id_on_a.clone(), + msg.proof_height_on_b.revision_number(), + msg.proof_height_on_b.revision_height(), + ); + let consensus_state_of_b_on_a = + client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; + let prefix_on_b = conn_end_on_a.counterparty().prefix(); + let port_id_on_b = chan_end_on_a.counterparty().port_id.clone(); + let chan_id_on_b = chan_end_on_a + .counterparty() + .channel_id() + .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(), + Some(packet.chan_id_on_a.clone()), + ); + let expected_chan_end_on_b = ChannelEnd::new( + State::Closed, + *chan_end_on_a.ordering(), + expected_counterparty, + expected_conn_hops_on_b, + chan_end_on_a.version().clone(), + )?; + + let chan_end_path_on_b = ChannelEndPath(port_id_on_b, chan_id_on_b.clone()); + + // 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(), + )?; + + 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(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); + + client_state_of_b_on_a.verify_membership( + conn_end_on_a.counterparty().prefix(), + &msg.proof_unreceived_on_b, + consensus_state_of_b_on_a.root(), + Path::SeqRecv(seq_recv_path_on_b), + packet.seq_on_a.to_vec(), + ) + } + Order::Unordered => { + let receipt_path_on_b = ReceiptPath::new( + &msg.packet.port_id_on_b, + &msg.packet.chan_id_on_b, + msg.packet.seq_on_a, + ); + + client_state_of_b_on_a.verify_non_membership( + conn_end_on_a.counterparty().prefix(), + &msg.proof_unreceived_on_b, + consensus_state_of_b_on_a.root(), + Path::Receipt(receipt_path_on_b), + ) + } + Order::None => { + 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?; + }; + + Ok(()) +} diff --git a/ibc-eureka-core/ics04-channel/src/lib.rs b/ibc-eureka-core/ics04-channel/src/lib.rs new file mode 100644 index 0000000000..1ccb4f25cf --- /dev/null +++ b/ibc-eureka-core/ics04-channel/src/lib.rs @@ -0,0 +1,26 @@ +//! ICS-04: Channel and Packet Semantics implementation to process channel open +//! handshake and incoming data packets. Exports data structures and +//! implementations of IBC core channel module. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod context; +pub mod handler; + +/// Re-exports ICS-04 data structures from the `ibc-core-channel-types` crate. +pub mod types { + #[doc(inline)] + pub use ibc_core_channel_types::*; +} diff --git a/ibc-eureka-core/ics04-channel/types/Cargo.toml b/ibc-eureka-core/ics04-channel/types/Cargo.toml new file mode 100644 index 0000000000..5cff829cea --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/Cargo.toml @@ -0,0 +1,103 @@ +[package] +name = "ibc-core-channel-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "channel", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-4 Channel Semantics data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +sha2 = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-client-types = { workspace = true } +ibc-core-connection-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[dev-dependencies] +rstest = { workspace = true } +serde-json = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "sha2/std", + "serde/std", + "subtle-encoding/std", + "ibc-core-client-types/std", + "ibc-core-connection-types/std", + "ibc-core-host-types/std", + "ibc-core-commitment-types/std", + "ibc-primitives/std", + "ibc-proto/std", + "tendermint/std", +] +serde = [ + "dep:serde", + "ibc-core-client-types/serde", + "ibc-core-connection-types/serde", + "ibc-core-host-types/serde", + "ibc-core-commitment-types/serde", + "ibc-primitives/serde", + "ibc-proto/serde", +] +schema = [ + "dep:schemars", + "ibc-core-client-types/schema", + "ibc-core-connection-types/schema", + "ibc-core-host-types/schema", + "ibc-core-commitment-types/schema", + "ibc-primitives/schema", + "ibc-proto/json-schema", + "serde", + "std", +] +borsh = [ + "dep:borsh", + "ibc-core-client-types/borsh", + "ibc-core-connection-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host-types/borsh", + "ibc-primitives/borsh", + "ibc-proto/borsh", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-core-client-types/parity-scale-codec", + "ibc-core-connection-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs b/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs new file mode 100644 index 0000000000..efbbe28ce3 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs @@ -0,0 +1,142 @@ +//! Defines acknowledgment types used by various IBC messages and applications. + +use core::fmt::{Display, Error as FmtError, Formatter}; + +use derive_more::Into; +use ibc_core_host_types::error::DecodingError; +use ibc_primitives::prelude::*; + +use crate::error::ChannelError; + +/// A generic Acknowledgement type that modules may interpret as they like. +/// +/// NOTE: An acknowledgement cannot be empty. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, Into)] +pub struct Acknowledgement(Vec); + +impl Acknowledgement { + // Returns the data as a slice of bytes. + pub fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl AsRef<[u8]> for Acknowledgement { + fn as_ref(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl TryFrom> for Acknowledgement { + type Error = DecodingError; + + fn try_from(bytes: Vec) -> Result { + if bytes.is_empty() { + Err(DecodingError::missing_raw_data("acknowledgment")) + } else { + Ok(Self(bytes)) + } + } +} + +/// Defines a convenience type for IBC applications to construct an +/// [`Acknowledgement`] based on the +/// success or failure of processing a received packet. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AcknowledgementStatus { + /// Successful Acknowledgement + /// e.g. `{"result":"AQ=="}` + #[cfg_attr(feature = "serde", serde(rename = "result"))] + Success(StatusValue), + /// Error Acknowledgement + /// e.g. `{"error":"cannot unmarshal ICS-20 transfer packet data"}` + #[cfg_attr(feature = "serde", serde(rename = "error"))] + Error(StatusValue), +} + +/// A wrapper type that guards variants of +/// [`AcknowledgementStatus`] +/// against being constructed with an empty value. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +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 { + let value = value.to_string(); + + if value.is_empty() { + return Err(ChannelError::MissingAcknowledgmentStatus); + } + + Ok(Self(value)) + } +} + +impl Display for StatusValue { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{status_value}", status_value = self.0) + } +} + +impl AcknowledgementStatus { + /// Creates a success acknowledgement status with the given value. + pub fn success(value: StatusValue) -> Self { + Self::Success(value) + } + + /// Creates an error acknowledgement status with the given value. + pub fn error(value: StatusValue) -> Self { + Self::Error(value) + } + + /// Returns true if the acknowledgement status is successful. + pub fn is_successful(&self) -> bool { + matches!(self, AcknowledgementStatus::Success(_)) + } +} + +impl Display for AcknowledgementStatus { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + AcknowledgementStatus::Success(v) | AcknowledgementStatus::Error(v) => write!(f, "{v}"), + } + } +} + +/// Converts an acknowledgement result into a vector of bytes. +impl From for Vec { + fn from(ack: AcknowledgementStatus) -> Self { + // WARNING: Make sure all branches always return a non-empty vector. + // Otherwise, the conversion to `Acknowledgement` will panic. + match ack { + AcknowledgementStatus::Success(v) => alloc::format!(r#"{{"result":"{v}"}}"#).into(), + AcknowledgementStatus::Error(v) => alloc::format!(r#"{{"error":"{v}"}}"#).into(), + } + } +} + +impl From for Acknowledgement { + fn from(ack_status: AcknowledgementStatus) -> Self { + let v: Vec = ack_status.into(); + + v.try_into() + .expect("token transfer internal error: ack is never supposed to be empty") + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/channel.rs b/ibc-eureka-core/ics04-channel/types/src/channel.rs new file mode 100644 index 0000000000..38140cb354 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/channel.rs @@ -0,0 +1,578 @@ +//! Implementation of IBC channels, as described in ICS-04. + +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; +use ibc_proto::ibc::core::channel::v1::{ + Channel as RawChannel, Counterparty as RawCounterparty, + IdentifiedChannel as RawIdentifiedChannel, +}; +use ibc_proto::Protobuf; + +use crate::error::ChannelError; +use crate::Version; + +/// A [`ChannelEnd`] along with its ID and the port it is bound to +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct IdentifiedChannelEnd { + pub port_id: PortId, + pub channel_id: ChannelId, + pub channel_end: ChannelEnd, +} + +impl IdentifiedChannelEnd { + pub fn new(port_id: PortId, channel_id: ChannelId, channel_end: ChannelEnd) -> Self { + IdentifiedChannelEnd { + port_id, + channel_id, + channel_end, + } + } +} + +impl Protobuf for IdentifiedChannelEnd {} + +impl TryFrom for IdentifiedChannelEnd { + type Error = DecodingError; + + fn try_from(value: RawIdentifiedChannel) -> Result { + if value.upgrade_sequence != 0 { + return Err(DecodingError::invalid_raw_data( + "channel upgrade sequence expected to be 0", + )); + } + + let raw_channel_end = RawChannel { + state: value.state, + ordering: value.ordering, + counterparty: value.counterparty, + connection_hops: value.connection_hops, + version: value.version, + upgrade_sequence: value.upgrade_sequence, + }; + + Ok(IdentifiedChannelEnd { + port_id: value.port_id.parse()?, + channel_id: value.channel_id.parse()?, + channel_end: raw_channel_end.try_into()?, + }) + } +} + +impl From for RawIdentifiedChannel { + fn from(value: IdentifiedChannelEnd) -> Self { + RawIdentifiedChannel { + state: value.channel_end.state as i32, + ordering: value.channel_end.ordering as i32, + counterparty: Some(value.channel_end.counterparty().clone().into()), + connection_hops: value + .channel_end + .connection_hops + .iter() + .map(|v| v.as_str().to_string()) + .collect(), + version: value.channel_end.version.to_string(), + port_id: value.port_id.to_string(), + channel_id: value.channel_id.to_string(), + upgrade_sequence: 0, + } + } +} + +/// One end of a channel +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ChannelEnd { + pub state: State, + pub ordering: Order, + pub remote: Counterparty, + pub connection_hops: Vec, + pub version: Version, +} + +impl Display for ChannelEnd { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!( + f, + "ChannelEnd {{ state: {}, ordering: {}, remote: {}, connection_hops: {}, version: {} }}", + self.state, self.ordering, self.remote, PrettySlice(&self.connection_hops), self.version + ) + } +} + +impl Protobuf for ChannelEnd {} + +impl TryFrom for ChannelEnd { + type Error = DecodingError; + + fn try_from(value: RawChannel) -> Result { + 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(DecodingError::missing_raw_data("channel counterparty"))? + .try_into()?; + + // Parse each item in connection_hops into a ConnectionId. + let connection_hops = value + .connection_hops + .into_iter() + .map(|conn_id| ConnectionId::from_str(conn_id.as_str())) + .collect::, _>>()?; + + let version = value.version.into(); + + 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) + } +} + +impl From for RawChannel { + fn from(value: ChannelEnd) -> Self { + RawChannel { + state: value.state as i32, + ordering: value.ordering as i32, + counterparty: Some(value.counterparty().clone().into()), + connection_hops: value + .connection_hops + .iter() + .map(|v| v.as_str().to_string()) + .collect(), + version: value.version.to_string(), + upgrade_sequence: 0, + } + } +} + +impl ChannelEnd { + /// Creates a new `ChannelEnd` without performing basic validation on its arguments. + /// + /// NOTE: This method is meant for the proto message conversion from the domain + /// `MsgChannelOpenInit` and `MsgChannelOpenTry` types to satisfy their `Protobuf` + /// trait bounds. + pub(super) fn new_without_validation( + state: State, + ordering: Order, + remote: Counterparty, + connection_hops: Vec, + version: Version, + ) -> Self { + Self { + state, + ordering, + remote, + connection_hops, + version, + } + } + + /// Creates a new `ChannelEnd` with performing basic validation on its arguments. + pub fn new( + state: State, + ordering: Order, + remote: Counterparty, + connection_hops: Vec, + version: Version, + ) -> Result { + let channel_end = + Self::new_without_validation(state, ordering, remote, connection_hops, version); + channel_end.validate_basic()?; + Ok(channel_end) + } + + /// Updates the ChannelEnd to assume a new State 's'. + pub fn set_state(&mut self, s: State) { + self.state = s; + } + + pub fn set_version(&mut self, v: Version) { + self.version = v; + } + + pub fn set_counterparty_channel_id(&mut self, c: ChannelId) { + self.remote.channel_id = Some(c); + } + + /// Returns `true` if this `ChannelEnd` is in state [`State::Open`]. + pub fn is_open(&self) -> bool { + self.state == State::Open + } + + pub fn state(&self) -> &State { + &self.state + } + + pub fn ordering(&self) -> &Order { + &self.ordering + } + + pub fn counterparty(&self) -> &Counterparty { + &self.remote + } + + pub fn connection_hops(&self) -> &Vec { + &self.connection_hops + } + + pub fn version(&self) -> &Version { + &self.version + } + + pub fn validate_basic(&self) -> Result<(), ChannelError> { + if self.state == State::Uninitialized { + return Err(ChannelError::InvalidState { + expected: "Channel state to not be Uninitialized".to_string(), + actual: self.state.to_string(), + }); + } + + if self.ordering == Order::None { + return Err(ChannelError::InvalidState { + expected: "Channel ordering to not be None".to_string(), + actual: self.ordering.to_string(), + }); + } + + Ok(()) + } + + /// Checks if the state of this channel end matches the expected state. + pub fn verify_state_matches(&self, expected: &State) -> Result<(), ChannelError> { + if !self.state.eq(expected) { + return Err(ChannelError::InvalidState { + expected: expected.to_string(), + actual: self.state.to_string(), + }); + } + Ok(()) + } + + /// Checks if the state of this channel end is not closed. + pub fn verify_not_closed(&self) -> Result<(), ChannelError> { + if self.state.eq(&State::Closed) { + return Err(ChannelError::InvalidState { + expected: "Channel state to not be Closed".to_string(), + actual: self.state.to_string(), + }); + } + Ok(()) + } + + /// Helper function to compare the order of this end with another order. + #[deprecated( + since = "0.50.1", + note = "Use `Eq` or `match` directly on the `Order` enum instead" + )] + pub fn order_matches(&self, other: &Order) -> bool { + self.ordering.eq(other) + } + + pub fn connection_hops_matches(&self, other: &Vec) -> bool { + self.connection_hops.eq(other) + } + + /// 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::MismatchedCounterparty { + expected: expected.clone(), + actual: self.counterparty().clone(), + }); + } + Ok(()) + } + + /// Checks if the `connection_hops` has a length of `expected`. + /// + /// Note: The current IBC version only supports one connection hop. + pub fn verify_connection_hops_length(&self) -> Result<(), ChannelError> { + verify_connection_hops_length(&self.connection_hops, 1) + } + + pub fn version_matches(&self, other: &Version) -> bool { + self.version().eq(other) + } +} + +/// Checks if the `connection_hops` has a length of `expected`. +pub(crate) fn verify_connection_hops_length( + connection_hops: &[ConnectionId], + expected: u64, +) -> Result<(), ChannelError> { + if connection_hops.len() as u64 != expected { + return Err(ChannelError::InvalidConnectionHopsLength { + expected, + actual: connection_hops.len() as u64, + }); + } + Ok(()) +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Counterparty { + pub port_id: PortId, + pub channel_id: Option, +} + +impl Counterparty { + pub fn new(port_id: PortId, channel_id: Option) -> Self { + Self { + port_id, + channel_id, + } + } + + pub fn port_id(&self) -> &PortId { + &self.port_id + } + + pub fn channel_id(&self) -> Option<&ChannelId> { + self.channel_id.as_ref() + } +} + +impl Display for Counterparty { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match &self.channel_id { + Some(channel_id) => write!( + f, + "Counterparty(port_id: {}, channel_id: {})", + self.port_id, channel_id + ), + None => write!( + f, + "Counterparty(port_id: {}, channel_id: None)", + self.port_id + ), + } + } +} + +impl Protobuf for Counterparty {} + +impl TryFrom for Counterparty { + type Error = DecodingError; + + fn try_from(raw_counterparty: RawCounterparty) -> Result { + let channel_id: Option = if raw_counterparty.channel_id.is_empty() { + None + } else { + Some(raw_counterparty.channel_id.parse()?) + }; + + Ok(Counterparty::new( + raw_counterparty.port_id.parse()?, + channel_id, + )) + } +} + +impl From for RawCounterparty { + fn from(value: Counterparty) -> Self { + RawCounterparty { + port_id: value.port_id.as_str().to_string(), + channel_id: value + .channel_id + .map_or_else(|| "".to_string(), |v| v.to_string()), + } + } +} + +/// Represents the channel ordering +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize), + borsh(use_discriminant = false) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Order { + None = 0isize, + Unordered = 1isize, + Ordered = 2isize, +} + +impl Display for Order { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.as_str()) + } +} + +impl Order { + /// Yields the Order as a string + pub fn as_str(&self) -> &'static str { + match self { + // Note: taken from [ibc-go](https://github.com/cosmos/ibc-go/blob/e3a32a61098d463cd00b8937e18cb671bd20c6b7/modules/core/04-channel/types/channel.pb.go#L95-L97) + Self::None => "ORDER_NONE_UNSPECIFIED", + Self::Unordered => "ORDER_UNORDERED", + Self::Ordered => "ORDER_ORDERED", + } + } + + // Parses the Order out from a i32. + pub fn from_i32(nr: i32) -> Result { + match nr { + 0 => Ok(Self::None), + 1 => Ok(Self::Unordered), + 2 => Ok(Self::Ordered), + _ => Err(ChannelError::InvalidState { + expected: "to be one of 0, 1, 2".to_string(), + actual: nr.to_string(), + }), + } + } +} + +impl FromStr for Order { + type Err = ChannelError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim_start_matches("order_") { + "uninitialized" => Ok(Self::None), + "unordered" => Ok(Self::Unordered), + "ordered" => Ok(Self::Ordered), + _ => Err(ChannelError::InvalidState { + expected: "to be one of 'uninitialized', 'unordered', 'ordered'".to_string(), + actual: s.to_string(), + }), + } + } +} + +/// Represents the state of a [`ChannelEnd`] +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize), + borsh(use_discriminant = false) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum State { + Uninitialized = 0isize, + Init = 1isize, + TryOpen = 2isize, + Open = 3isize, + Closed = 4isize, +} + +impl State { + /// Yields the state as a string + pub fn as_string(&self) -> &'static str { + match self { + Self::Uninitialized => "UNINITIALIZED", + Self::Init => "INIT", + Self::TryOpen => "TRYOPEN", + Self::Open => "OPEN", + Self::Closed => "CLOSED", + } + } + + // Parses the State out from a i32. + pub fn from_i32(s: i32) -> Result { + match s { + 0 => Ok(Self::Uninitialized), + 1 => Ok(Self::Init), + 2 => Ok(Self::TryOpen), + 3 => Ok(Self::Open), + 4 => Ok(Self::Closed), + _ => Err(ChannelError::InvalidState { + expected: "to be one of: 0, 1, 2, 3, 4".to_string(), + actual: s.to_string(), + }), + } + } + + /// Returns if this channel state is `Open`. + pub fn is_open(self) -> bool { + self == State::Open + } + + /// Returns if the channel with this state + /// has progressed less than or the same as the argument. + /// + /// # Example + /// ```rust,ignore + /// assert!(State::Init.less_or_equal_progress(State::Open)); + /// assert!(State::TryOpen.less_or_equal_progress(State::TryOpen)); + /// assert!(!State::Closed.less_or_equal_progress(State::Open)); + /// ``` + pub fn less_or_equal_progress(self, other: Self) -> bool { + self as u32 <= other as u32 + } +} + +/// Provides a `to_string` method. +impl Display for State { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.as_string()) + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/commitment.rs b/ibc-eureka-core/ics04-channel/types/src/commitment.rs new file mode 100644 index 0000000000..ece5fe641d --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/commitment.rs @@ -0,0 +1,146 @@ +//! Types and utilities related to packet commitments. + +use ibc_primitives::prelude::*; + +use super::acknowledgement::Acknowledgement; +use crate::timeout::{TimeoutHeight, TimeoutTimestamp}; + +/// Packet commitment +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PacketCommitment(Vec); + +impl PacketCommitment { + pub fn into_vec(self) -> Vec { + self.0 + } +} + +impl AsRef<[u8]> for PacketCommitment { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From> for PacketCommitment { + fn from(bytes: Vec) -> Self { + Self(bytes) + } +} + +/// Acknowledgement of commitment to be stored +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AcknowledgementCommitment(Vec); + +impl AcknowledgementCommitment { + pub fn into_vec(self) -> Vec { + self.0 + } +} + +impl AsRef<[u8]> for AcknowledgementCommitment { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From> for AcknowledgementCommitment { + fn from(bytes: Vec) -> Self { + Self(bytes) + } +} + +/// Compute the commitment for a packet. +/// +/// Note that the absence of `timeout_height` is treated as +/// `{revision_number: 0, revision_height: 0}` to be consistent with ibc-go, +/// where this value is used to mean "no timeout height": +/// +pub fn compute_packet_commitment( + packet_data: &[u8], + timeout_height: &TimeoutHeight, + timeout_timestamp: &TimeoutTimestamp, +) -> PacketCommitment { + let mut hash_input = [0; 8 * 3 + 32]; + + hash_input[..8].copy_from_slice(&timeout_timestamp.nanoseconds().to_be_bytes()); + hash_input[8..16].copy_from_slice(&timeout_height.commitment_revision_number().to_be_bytes()); + hash_input[16..24].copy_from_slice(&timeout_height.commitment_revision_height().to_be_bytes()); + hash_input[24..].copy_from_slice(&hash(packet_data)); + + hash(&hash_input).to_vec().into() +} + +/// Compute the commitment for an acknowledgement. +pub fn compute_ack_commitment(ack: &Acknowledgement) -> AcknowledgementCommitment { + hash(ack.as_ref()).to_vec().into() +} + +/// Helper function to hash a byte slice using SHA256. +/// +/// Note that computing commitments with anything apart from SHA256 will +/// break the Merkle proofs of the IBC provable store. +fn hash(data: &[u8]) -> [u8; 32] { + use sha2::Digest; + + sha2::Sha256::digest(data).into() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_compute_packet_commitment() { + let expected: [u8; 32] = [ + 0xa9, 0x28, 0xb5, 0x1f, 0x62, 0xbd, 0x54, 0x00, 0x91, 0xec, 0x45, 0x1f, 0x4e, 0xf3, + 0x45, 0x79, 0x4f, 0x05, 0x9e, 0x65, 0x91, 0x08, 0x16, 0x86, 0x61, 0x26, 0xdc, 0x36, + 0x4f, 0x84, 0xcc, 0x15, + ]; + let actual = compute_packet_commitment( + b"packet data", + &TimeoutHeight::At(ibc_core_client_types::Height::new(42, 24).unwrap()), + &TimeoutTimestamp::from(0x42), + ); + assert_eq!(&expected[..], actual.as_ref()); + } + + #[test] + fn test_compute_ack_commitment() { + let expected: [u8; 32] = [ + 0x05, 0x4e, 0xde, 0xc1, 0xd0, 0x21, 0x1f, 0x62, 0x4f, 0xed, 0x0c, 0xbc, 0xa9, 0xd4, + 0xf9, 0x40, 0x0b, 0x0e, 0x49, 0x1c, 0x43, 0x74, 0x2a, 0xf2, 0xc5, 0xb0, 0xab, 0xeb, + 0xf0, 0xc9, 0x90, 0xd8, + ]; + let ack = Acknowledgement::try_from(vec![0, 1, 2, 3]).unwrap(); + let actual = compute_ack_commitment(&ack); + assert_eq!(&expected[..], actual.as_ref()) + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/error.rs b/ibc-eureka-core/ics04-channel/types/src/error.rs new file mode 100644 index 0000000000..911aed095e --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/error.rs @@ -0,0 +1,96 @@ +//! Defines the main channel, port, and packet error types + +use displaydoc::Display; +use ibc_core_client_types::error::ClientError; +use ibc_core_client_types::Height; +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::commitment::PacketCommitment; +use crate::timeout::TimeoutTimestamp; +use crate::Version; + +/// Errors that arise from the ICS04 Channel module +#[derive(Debug, Display, derive_more::From)] +pub enum ChannelError { + /// 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, + }, + /// 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, + }, + /// 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, + /// mismatched counterparty: expected `{expected}`, actual `{actual}` + MismatchedCounterparty { + expected: Counterparty, + actual: Counterparty, + }, + /// mismatched packet sequence: expected `{expected}`, actual `{actual}` + MismatchedPacketSequence { + expected: Sequence, + actual: Sequence, + }, + /// mismatched packet commitments: expected `{expected:?}`, actual `{actual:?}` + MismatchedPacketCommitment { + expected: PacketCommitment, + actual: PacketCommitment, + }, + /// unsupported version: expected `{expected}`, actual `{actual}` + UnsupportedVersion { expected: Version, actual: Version }, + /// application specific error: `{description}` + AppSpecific { description: String }, +} + +impl From for ChannelError { + fn from(e: IdentifierError) -> Self { + Self::Decoding(DecodingError::Identifier(e)) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ChannelError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + 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-eureka-core/ics04-channel/types/src/events/channel_attributes.rs b/ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs new file mode 100644 index 0000000000..8032fb44d8 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs @@ -0,0 +1,172 @@ +//! This module holds all the abci event attributes for IBC events emitted +//! during the channel handshake. +use derive_more::From; +use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; +use tendermint::abci; + +use crate::Version; + +const CONNECTION_ID_ATTRIBUTE_KEY: &str = "connection_id"; +const CHANNEL_ID_ATTRIBUTE_KEY: &str = "channel_id"; +const PORT_ID_ATTRIBUTE_KEY: &str = "port_id"; +/// This attribute key is public so that OpenInit can use it to convert itself +/// to an `AbciEvent` +pub(super) const COUNTERPARTY_CHANNEL_ID_ATTRIBUTE_KEY: &str = "counterparty_channel_id"; +const COUNTERPARTY_PORT_ID_ATTRIBUTE_KEY: &str = "counterparty_port_id"; +const VERSION_ATTRIBUTE_KEY: &str = "version"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct PortIdAttribute { + pub port_id: PortId, +} + +impl From for abci::EventAttribute { + fn from(attr: PortIdAttribute) -> Self { + (PORT_ID_ATTRIBUTE_KEY, attr.port_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct ChannelIdAttribute { + pub channel_id: ChannelId, +} + +impl From for abci::EventAttribute { + fn from(attr: ChannelIdAttribute) -> Self { + (CHANNEL_ID_ATTRIBUTE_KEY, attr.channel_id.as_str()).into() + } +} +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct CounterpartyPortIdAttribute { + pub counterparty_port_id: PortId, +} + +impl From for abci::EventAttribute { + fn from(attr: CounterpartyPortIdAttribute) -> Self { + ( + COUNTERPARTY_PORT_ID_ATTRIBUTE_KEY, + attr.counterparty_port_id.as_str(), + ) + .into() + } +} +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct CounterpartyChannelIdAttribute { + pub counterparty_channel_id: ChannelId, +} + +impl From for abci::EventAttribute { + fn from(attr: CounterpartyChannelIdAttribute) -> Self { + ( + COUNTERPARTY_CHANNEL_ID_ATTRIBUTE_KEY, + attr.counterparty_channel_id.as_str(), + ) + .into() + } +} + +impl AsRef for CounterpartyChannelIdAttribute { + fn as_ref(&self) -> &ChannelId { + &self.counterparty_channel_id + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct ConnectionIdAttribute { + pub connection_id: ConnectionId, +} + +impl From for abci::EventAttribute { + fn from(attr: ConnectionIdAttribute) -> Self { + (CONNECTION_ID_ATTRIBUTE_KEY, attr.connection_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct VersionAttribute { + pub version: Version, +} + +impl From for abci::EventAttribute { + fn from(attr: VersionAttribute) -> Self { + (VERSION_ATTRIBUTE_KEY, attr.version.as_str()).into() + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/events/mod.rs b/ibc-eureka-core/ics04-channel/types/src/events/mod.rs new file mode 100644 index 0000000000..4b65a94c62 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/events/mod.rs @@ -0,0 +1,1267 @@ +//! Types for the IBC events emitted from Tendermint Websocket by the channels module. + +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; + +use self::channel_attributes::{ + ChannelIdAttribute, ConnectionIdAttribute, CounterpartyChannelIdAttribute, + CounterpartyPortIdAttribute, PortIdAttribute, VersionAttribute, + COUNTERPARTY_CHANNEL_ID_ATTRIBUTE_KEY, +}; +use self::packet_attributes::{ + AcknowledgementAttribute, ChannelOrderingAttribute, DstChannelIdAttribute, DstPortIdAttribute, + PacketConnectionIdAttribute, PacketDataAttribute, SequenceAttribute, SrcChannelIdAttribute, + SrcPortIdAttribute, TimeoutHeightAttribute, TimeoutTimestampAttribute, +}; +use super::acknowledgement::Acknowledgement; +use super::channel::Order; +use super::timeout::TimeoutHeight; +use super::Version; +use crate::packet::Packet; +use crate::timeout::TimeoutTimestamp; + +/// Channel event types corresponding to ibc-go's channel events: +/// https://github.com/cosmos/ibc-go/blob/c4413c5877f9ef883494da1721cb18caaba7f7f5/modules/core/04-channel/types/events.go#L52-L72 +const CHANNEL_OPEN_INIT_EVENT: &str = "channel_open_init"; +const CHANNEL_OPEN_TRY_EVENT: &str = "channel_open_try"; +const CHANNEL_OPEN_ACK_EVENT: &str = "channel_open_ack"; +const CHANNEL_OPEN_CONFIRM_EVENT: &str = "channel_open_confirm"; +const CHANNEL_CLOSE_INIT_EVENT: &str = "channel_close_init"; +const CHANNEL_CLOSE_CONFIRM_EVENT: &str = "channel_close_confirm"; +const CHANNEL_CLOSED_EVENT: &str = "channel_close"; + +/// Packet event types +const SEND_PACKET_EVENT: &str = "send_packet"; +const RECEIVE_PACKET_EVENT: &str = "recv_packet"; +const WRITE_ACK_EVENT: &str = "write_acknowledgement"; +const ACK_PACKET_EVENT: &str = "acknowledge_packet"; +const TIMEOUT_EVENT: &str = "timeout_packet"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenInit { + port_id_attr_on_a: PortIdAttribute, + chan_id_attr_on_a: ChannelIdAttribute, + port_id_attr_on_b: CounterpartyPortIdAttribute, + conn_id_attr_on_a: ConnectionIdAttribute, + version_attr_on_a: VersionAttribute, +} + +impl OpenInit { + pub fn new( + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + port_id_on_b: PortId, + conn_id_on_a: ConnectionId, + version_on_a: Version, + ) -> Self { + Self { + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + port_id_attr_on_b: port_id_on_b.into(), + conn_id_attr_on_a: conn_id_on_a.into(), + version_attr_on_a: version_on_a.into(), + } + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.port_id + } + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.channel_id + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.counterparty_port_id + } + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.conn_id_attr_on_a.connection_id + } + pub fn version_on_a(&self) -> &Version { + &self.version_attr_on_a.version + } + + pub fn event_type(&self) -> &str { + CHANNEL_OPEN_INIT_EVENT + } +} + +impl From for abci::Event { + fn from(o: OpenInit) -> Self { + abci::Event { + kind: CHANNEL_OPEN_INIT_EVENT.to_string(), + attributes: vec![ + o.port_id_attr_on_a.into(), + o.chan_id_attr_on_a.into(), + o.port_id_attr_on_b.into(), + (COUNTERPARTY_CHANNEL_ID_ATTRIBUTE_KEY, "").into(), + o.conn_id_attr_on_a.into(), + o.version_attr_on_a.into(), + ], + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenTry { + port_id_attr_on_b: PortIdAttribute, + chan_id_attr_on_b: ChannelIdAttribute, + port_id_attr_on_a: CounterpartyPortIdAttribute, + chan_id_attr_on_a: CounterpartyChannelIdAttribute, + conn_id_attr_on_b: ConnectionIdAttribute, + version_attr_on_b: VersionAttribute, +} + +impl OpenTry { + pub fn new( + port_id_on_b: PortId, + chan_id_on_b: ChannelId, + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + conn_id_on_b: ConnectionId, + version_on_b: Version, + ) -> Self { + Self { + port_id_attr_on_b: port_id_on_b.into(), + chan_id_attr_on_b: chan_id_on_b.into(), + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + conn_id_attr_on_b: conn_id_on_b.into(), + version_attr_on_b: version_on_b.into(), + } + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.port_id + } + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.channel_id + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.counterparty_port_id + } + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.counterparty_channel_id + } + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.conn_id_attr_on_b.connection_id + } + pub fn version_on_b(&self) -> &Version { + &self.version_attr_on_b.version + } + + pub fn event_type(&self) -> &str { + CHANNEL_OPEN_TRY_EVENT + } +} + +impl From for abci::Event { + fn from(o: OpenTry) -> Self { + abci::Event { + kind: CHANNEL_OPEN_TRY_EVENT.to_string(), + attributes: vec![ + o.port_id_attr_on_b.into(), + o.chan_id_attr_on_b.into(), + o.port_id_attr_on_a.into(), + o.chan_id_attr_on_a.into(), + o.conn_id_attr_on_b.into(), + o.version_attr_on_b.into(), + ], + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenAck { + port_id_attr_on_a: PortIdAttribute, + chan_id_attr_on_a: ChannelIdAttribute, + port_id_attr_on_b: CounterpartyPortIdAttribute, + chan_id_attr_on_b: CounterpartyChannelIdAttribute, + conn_id_attr_on_a: ConnectionIdAttribute, +} + +impl OpenAck { + pub fn new( + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + port_id_on_b: PortId, + chan_id_on_b: ChannelId, + conn_id_on_a: ConnectionId, + ) -> Self { + Self { + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + port_id_attr_on_b: port_id_on_b.into(), + chan_id_attr_on_b: chan_id_on_b.into(), + conn_id_attr_on_a: conn_id_on_a.into(), + } + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.port_id + } + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.channel_id + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.counterparty_port_id + } + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.counterparty_channel_id + } + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.conn_id_attr_on_a.connection_id + } + + pub fn event_type(&self) -> &str { + CHANNEL_OPEN_ACK_EVENT + } +} + +impl From for abci::Event { + fn from(o: OpenAck) -> Self { + abci::Event { + kind: CHANNEL_OPEN_ACK_EVENT.to_string(), + attributes: vec![ + o.port_id_attr_on_a.into(), + o.chan_id_attr_on_a.into(), + o.port_id_attr_on_b.into(), + o.chan_id_attr_on_b.into(), + o.conn_id_attr_on_a.into(), + ], + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpenConfirm { + port_id_attr_on_b: PortIdAttribute, + chan_id_attr_on_b: ChannelIdAttribute, + port_id_attr_on_a: CounterpartyPortIdAttribute, + chan_id_attr_on_a: CounterpartyChannelIdAttribute, + conn_id_attr_on_b: ConnectionIdAttribute, +} + +impl OpenConfirm { + pub fn new( + port_id_on_b: PortId, + chan_id_on_b: ChannelId, + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + conn_id_on_b: ConnectionId, + ) -> Self { + Self { + port_id_attr_on_b: port_id_on_b.into(), + chan_id_attr_on_b: chan_id_on_b.into(), + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + conn_id_attr_on_b: conn_id_on_b.into(), + } + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.port_id + } + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.channel_id + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.counterparty_port_id + } + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.counterparty_channel_id + } + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.conn_id_attr_on_b.connection_id + } + + pub fn event_type(&self) -> &str { + CHANNEL_OPEN_CONFIRM_EVENT + } +} + +impl From for abci::Event { + fn from(o: OpenConfirm) -> Self { + abci::Event { + kind: CHANNEL_OPEN_CONFIRM_EVENT.to_string(), + attributes: vec![ + o.port_id_attr_on_b.into(), + o.chan_id_attr_on_b.into(), + o.port_id_attr_on_a.into(), + o.chan_id_attr_on_a.into(), + o.conn_id_attr_on_b.into(), + ], + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CloseInit { + port_id_attr_on_a: PortIdAttribute, + chan_id_attr_on_a: ChannelIdAttribute, + port_id_attr_on_b: CounterpartyPortIdAttribute, + chan_id_attr_on_b: CounterpartyChannelIdAttribute, + conn_id_attr_on_a: ConnectionIdAttribute, +} + +impl CloseInit { + pub fn new( + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + port_id_on_b: PortId, + chan_id_on_b: ChannelId, + conn_id_on_a: ConnectionId, + ) -> Self { + Self { + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + port_id_attr_on_b: port_id_on_b.into(), + chan_id_attr_on_b: chan_id_on_b.into(), + conn_id_attr_on_a: conn_id_on_a.into(), + } + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.port_id + } + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.channel_id + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.counterparty_port_id + } + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.counterparty_channel_id + } + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.conn_id_attr_on_a.connection_id + } + + pub fn event_type(&self) -> &str { + CHANNEL_CLOSE_INIT_EVENT + } +} + +impl From for abci::Event { + fn from(o: CloseInit) -> Self { + abci::Event { + kind: CHANNEL_CLOSE_INIT_EVENT.to_string(), + attributes: vec![ + o.port_id_attr_on_a.into(), + o.chan_id_attr_on_a.into(), + o.port_id_attr_on_b.into(), + o.chan_id_attr_on_b.into(), + o.conn_id_attr_on_a.into(), + ], + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CloseConfirm { + port_id_attr_on_b: PortIdAttribute, + chan_id_attr_on_b: ChannelIdAttribute, + port_id_attr_on_a: CounterpartyPortIdAttribute, + chan_id_attr_on_a: CounterpartyChannelIdAttribute, + conn_id_attr_on_b: ConnectionIdAttribute, +} + +impl CloseConfirm { + pub fn new( + port_id_on_b: PortId, + chan_id_on_b: ChannelId, + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + conn_id_on_b: ConnectionId, + ) -> Self { + Self { + port_id_attr_on_b: port_id_on_b.into(), + chan_id_attr_on_b: chan_id_on_b.into(), + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + conn_id_attr_on_b: conn_id_on_b.into(), + } + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.port_id + } + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.channel_id + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.counterparty_port_id + } + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.counterparty_channel_id + } + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.conn_id_attr_on_b.connection_id + } + + pub fn event_type(&self) -> &str { + CHANNEL_CLOSE_CONFIRM_EVENT + } +} + +impl From for abci::Event { + fn from(o: CloseConfirm) -> Self { + abci::Event { + kind: CHANNEL_CLOSE_CONFIRM_EVENT.to_string(), + attributes: vec![ + o.port_id_attr_on_b.into(), + o.chan_id_attr_on_b.into(), + o.port_id_attr_on_a.into(), + o.chan_id_attr_on_a.into(), + o.conn_id_attr_on_b.into(), + ], + } + } +} + +/// A `ChannelClosed` event is emitted when a channel is closed as a result of a packet timing out. Note that +/// since optimistic packet sends (i.e. send a packet before channel handshake is complete) are supported, +/// we might not have a counterparty channel id value yet. This would happen if a packet is sent right +/// after a `ChannelOpenInit` message. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ChannelClosed { + port_id_attr_on_a: PortIdAttribute, + chan_id_attr_on_a: ChannelIdAttribute, + port_id_attr_on_b: CounterpartyPortIdAttribute, + maybe_chan_id_attr_on_b: Option, + conn_id_attr_on_a: ConnectionIdAttribute, + channel_ordering_attr: ChannelOrderingAttribute, +} + +impl ChannelClosed { + pub fn new( + port_id_on_a: PortId, + chan_id_on_a: ChannelId, + port_id_on_b: PortId, + maybe_chan_id_on_b: Option, + conn_id_on_a: ConnectionId, + channel_ordering: Order, + ) -> Self { + Self { + port_id_attr_on_a: port_id_on_a.into(), + chan_id_attr_on_a: chan_id_on_a.into(), + port_id_attr_on_b: port_id_on_b.into(), + maybe_chan_id_attr_on_b: maybe_chan_id_on_b.map(Into::into), + conn_id_attr_on_a: conn_id_on_a.into(), + channel_ordering_attr: channel_ordering.into(), + } + } + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_a.port_id + } + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_a.channel_id + } + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_b.counterparty_port_id + } + pub fn chan_id_on_a(&self) -> Option<&ChannelId> { + self.maybe_chan_id_attr_on_b.as_ref().map(AsRef::as_ref) + } + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.conn_id_attr_on_a.connection_id + } + pub fn channel_ordering(&self) -> &Order { + &self.channel_ordering_attr.order + } + + pub fn event_type(&self) -> &str { + CHANNEL_CLOSED_EVENT + } +} + +impl From for abci::Event { + fn from(ev: ChannelClosed) -> Self { + abci::Event { + kind: CHANNEL_CLOSED_EVENT.to_string(), + attributes: vec![ + ev.port_id_attr_on_a.into(), + ev.chan_id_attr_on_a.into(), + ev.port_id_attr_on_b.into(), + ev.maybe_chan_id_attr_on_b.map_or_else( + || (COUNTERPARTY_CHANNEL_ID_ATTRIBUTE_KEY, "").into(), + Into::into, + ), + ev.conn_id_attr_on_a.into(), + ev.channel_ordering_attr.into(), + ], + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SendPacket { + packet_data_attr: PacketDataAttribute, + timeout_height_attr_on_b: TimeoutHeightAttribute, + timeout_timestamp_attr_on_b: TimeoutTimestampAttribute, + seq_attr_on_a: SequenceAttribute, + port_id_attr_on_a: SrcPortIdAttribute, + chan_id_attr_on_a: SrcChannelIdAttribute, + port_id_attr_on_b: DstPortIdAttribute, + chan_id_attr_on_b: DstChannelIdAttribute, + channel_ordering_attr: ChannelOrderingAttribute, + conn_id_attr_on_a: PacketConnectionIdAttribute, +} + +impl SendPacket { + pub fn new(packet: Packet, channel_ordering: Order, src_connection_id: ConnectionId) -> Self { + Self { + packet_data_attr: packet.data.into(), + timeout_height_attr_on_b: packet.timeout_height_on_b.into(), + timeout_timestamp_attr_on_b: packet.timeout_timestamp_on_b.into(), + seq_attr_on_a: packet.seq_on_a.into(), + port_id_attr_on_a: packet.port_id_on_a.into(), + chan_id_attr_on_a: packet.chan_id_on_a.into(), + port_id_attr_on_b: packet.port_id_on_b.into(), + chan_id_attr_on_b: packet.chan_id_on_b.into(), + channel_ordering_attr: channel_ordering.into(), + conn_id_attr_on_a: src_connection_id.into(), + } + } + + pub fn packet_data(&self) -> &[u8] { + &self.packet_data_attr.packet_data + } + + pub fn timeout_height_on_b(&self) -> &TimeoutHeight { + &self.timeout_height_attr_on_b.timeout_height + } + + pub fn timeout_timestamp_on_b(&self) -> &TimeoutTimestamp { + &self.timeout_timestamp_attr_on_b.timeout_timestamp + } + + pub fn seq_on_a(&self) -> &Sequence { + &self.seq_attr_on_a.sequence + } + + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.src_port_id + } + + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.src_channel_id + } + + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.dst_port_id + } + + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.dst_channel_id + } + + pub fn channel_ordering(&self) -> &Order { + &self.channel_ordering_attr.order + } + + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.conn_id_attr_on_a.connection_id + } + + pub fn event_type(&self) -> &str { + SEND_PACKET_EVENT + } +} + +impl TryFrom for abci::Event { + type Error = DecodingError; + + fn try_from(v: SendPacket) -> Result { + let mut attributes = Vec::with_capacity(11); + attributes.append(&mut v.packet_data_attr.try_into()?); + attributes.push(v.timeout_height_attr_on_b.into()); + attributes.push(v.timeout_timestamp_attr_on_b.into()); + attributes.push(v.seq_attr_on_a.into()); + attributes.push(v.port_id_attr_on_a.into()); + attributes.push(v.chan_id_attr_on_a.into()); + attributes.push(v.port_id_attr_on_b.into()); + attributes.push(v.chan_id_attr_on_b.into()); + attributes.push(v.channel_ordering_attr.into()); + attributes.push(v.conn_id_attr_on_a.into()); + + Ok(abci::Event { + kind: SEND_PACKET_EVENT.to_string(), + attributes, + }) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ReceivePacket { + packet_data_attr: PacketDataAttribute, + timeout_height_attr_on_b: TimeoutHeightAttribute, + timeout_timestamp_attr_on_b: TimeoutTimestampAttribute, + seq_attr_on_a: SequenceAttribute, + port_id_attr_on_a: SrcPortIdAttribute, + chan_id_attr_on_a: SrcChannelIdAttribute, + port_id_attr_on_b: DstPortIdAttribute, + chan_id_attr_on_b: DstChannelIdAttribute, + channel_ordering_attr: ChannelOrderingAttribute, + conn_id_attr_on_b: PacketConnectionIdAttribute, +} + +impl ReceivePacket { + pub fn new(packet: Packet, channel_ordering: Order, dst_connection_id: ConnectionId) -> Self { + Self { + packet_data_attr: packet.data.into(), + timeout_height_attr_on_b: packet.timeout_height_on_b.into(), + timeout_timestamp_attr_on_b: packet.timeout_timestamp_on_b.into(), + seq_attr_on_a: packet.seq_on_a.into(), + port_id_attr_on_a: packet.port_id_on_a.into(), + chan_id_attr_on_a: packet.chan_id_on_a.into(), + port_id_attr_on_b: packet.port_id_on_b.into(), + chan_id_attr_on_b: packet.chan_id_on_b.into(), + channel_ordering_attr: channel_ordering.into(), + conn_id_attr_on_b: dst_connection_id.into(), + } + } + + pub fn packet_data(&self) -> &[u8] { + &self.packet_data_attr.packet_data + } + + pub fn timeout_height_on_b(&self) -> &TimeoutHeight { + &self.timeout_height_attr_on_b.timeout_height + } + + pub fn timeout_timestamp_on_b(&self) -> &TimeoutTimestamp { + &self.timeout_timestamp_attr_on_b.timeout_timestamp + } + + pub fn seq_on_b(&self) -> &Sequence { + &self.seq_attr_on_a.sequence + } + + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_a.src_port_id + } + + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_a.src_channel_id + } + + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_b.dst_port_id + } + + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_b.dst_channel_id + } + + pub fn channel_ordering(&self) -> &Order { + &self.channel_ordering_attr.order + } + + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.conn_id_attr_on_b.connection_id + } + + pub fn event_type(&self) -> &str { + RECEIVE_PACKET_EVENT + } +} + +impl TryFrom for abci::Event { + type Error = DecodingError; + + fn try_from(v: ReceivePacket) -> Result { + let mut attributes = Vec::with_capacity(11); + attributes.append(&mut v.packet_data_attr.try_into()?); + attributes.push(v.timeout_height_attr_on_b.into()); + attributes.push(v.timeout_timestamp_attr_on_b.into()); + attributes.push(v.seq_attr_on_a.into()); + attributes.push(v.port_id_attr_on_a.into()); + attributes.push(v.chan_id_attr_on_a.into()); + attributes.push(v.port_id_attr_on_b.into()); + attributes.push(v.chan_id_attr_on_b.into()); + attributes.push(v.channel_ordering_attr.into()); + attributes.push(v.conn_id_attr_on_b.into()); + + Ok(abci::Event { + kind: RECEIVE_PACKET_EVENT.to_string(), + attributes, + }) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WriteAcknowledgement { + packet_data: PacketDataAttribute, + timeout_height_attr_on_b: TimeoutHeightAttribute, + timeout_timestamp_attr_on_b: TimeoutTimestampAttribute, + seq_attr_on_a: SequenceAttribute, + port_id_attr_on_a: SrcPortIdAttribute, + chan_id_attr_on_a: SrcChannelIdAttribute, + port_id_attr_on_b: DstPortIdAttribute, + chan_id_attr_on_b: DstChannelIdAttribute, + acknowledgement: AcknowledgementAttribute, + conn_id_attr_on_b: PacketConnectionIdAttribute, +} + +impl WriteAcknowledgement { + pub fn new( + packet: Packet, + acknowledgement: Acknowledgement, + conn_id_on_b: ConnectionId, + ) -> Self { + Self { + packet_data: packet.data.into(), + timeout_height_attr_on_b: packet.timeout_height_on_b.into(), + timeout_timestamp_attr_on_b: packet.timeout_timestamp_on_b.into(), + seq_attr_on_a: packet.seq_on_a.into(), + port_id_attr_on_a: packet.port_id_on_a.into(), + chan_id_attr_on_a: packet.chan_id_on_a.into(), + port_id_attr_on_b: packet.port_id_on_b.into(), + chan_id_attr_on_b: packet.chan_id_on_b.into(), + acknowledgement: acknowledgement.into(), + conn_id_attr_on_b: conn_id_on_b.into(), + } + } + + pub fn packet_data(&self) -> &[u8] { + &self.packet_data.packet_data + } + + pub fn timeout_height_on_b(&self) -> &TimeoutHeight { + &self.timeout_height_attr_on_b.timeout_height + } + + pub fn timeout_timestamp_on_b(&self) -> &TimeoutTimestamp { + &self.timeout_timestamp_attr_on_b.timeout_timestamp + } + + pub fn seq_on_a(&self) -> &Sequence { + &self.seq_attr_on_a.sequence + } + + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.src_port_id + } + + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.src_channel_id + } + + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.dst_port_id + } + + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.dst_channel_id + } + + pub fn acknowledgement(&self) -> &Acknowledgement { + &self.acknowledgement.acknowledgement + } + + pub fn conn_id_on_b(&self) -> &ConnectionId { + &self.conn_id_attr_on_b.connection_id + } + + pub fn event_type(&self) -> &str { + WRITE_ACK_EVENT + } +} + +impl TryFrom for abci::Event { + type Error = DecodingError; + + fn try_from(v: WriteAcknowledgement) -> Result { + let mut attributes = Vec::with_capacity(11); + attributes.append(&mut v.packet_data.try_into()?); + attributes.push(v.timeout_height_attr_on_b.into()); + attributes.push(v.timeout_timestamp_attr_on_b.into()); + attributes.push(v.seq_attr_on_a.into()); + attributes.push(v.port_id_attr_on_a.into()); + attributes.push(v.chan_id_attr_on_a.into()); + attributes.push(v.port_id_attr_on_b.into()); + attributes.push(v.chan_id_attr_on_b.into()); + attributes.append(&mut v.acknowledgement.try_into()?); + attributes.push(v.conn_id_attr_on_b.into()); + + Ok(abci::Event { + kind: WRITE_ACK_EVENT.to_string(), + attributes, + }) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AcknowledgePacket { + timeout_height_attr_on_b: TimeoutHeightAttribute, + timeout_timestamp_attr_on_b: TimeoutTimestampAttribute, + seq_on_a: SequenceAttribute, + port_id_attr_on_a: SrcPortIdAttribute, + chan_id_attr_on_a: SrcChannelIdAttribute, + port_id_attr_on_b: DstPortIdAttribute, + chan_id_attr_on_b: DstChannelIdAttribute, + channel_ordering_attr: ChannelOrderingAttribute, + conn_id_attr_on_a: PacketConnectionIdAttribute, +} + +impl AcknowledgePacket { + pub fn new(packet: Packet, channel_ordering: Order, src_connection_id: ConnectionId) -> Self { + Self { + timeout_height_attr_on_b: packet.timeout_height_on_b.into(), + timeout_timestamp_attr_on_b: packet.timeout_timestamp_on_b.into(), + seq_on_a: packet.seq_on_a.into(), + port_id_attr_on_a: packet.port_id_on_a.into(), + chan_id_attr_on_a: packet.chan_id_on_a.into(), + port_id_attr_on_b: packet.port_id_on_b.into(), + chan_id_attr_on_b: packet.chan_id_on_b.into(), + channel_ordering_attr: channel_ordering.into(), + conn_id_attr_on_a: src_connection_id.into(), + } + } + + pub fn timeout_height_on_b(&self) -> &TimeoutHeight { + &self.timeout_height_attr_on_b.timeout_height + } + + pub fn timeout_timestamp_on_b(&self) -> &TimeoutTimestamp { + &self.timeout_timestamp_attr_on_b.timeout_timestamp + } + + pub fn seq_on_a(&self) -> &Sequence { + &self.seq_on_a.sequence + } + + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.src_port_id + } + + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.src_channel_id + } + + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.dst_port_id + } + + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.dst_channel_id + } + + pub fn channel_ordering(&self) -> &Order { + &self.channel_ordering_attr.order + } + + pub fn conn_id_on_a(&self) -> &ConnectionId { + &self.conn_id_attr_on_a.connection_id + } + + pub fn event_type(&self) -> &str { + ACK_PACKET_EVENT + } +} + +impl TryFrom for abci::Event { + type Error = DecodingError; + + fn try_from(v: AcknowledgePacket) -> Result { + Ok(abci::Event { + kind: ACK_PACKET_EVENT.to_string(), + attributes: vec![ + v.timeout_height_attr_on_b.into(), + v.timeout_timestamp_attr_on_b.into(), + v.seq_on_a.into(), + v.port_id_attr_on_a.into(), + v.chan_id_attr_on_a.into(), + v.port_id_attr_on_b.into(), + v.chan_id_attr_on_b.into(), + v.channel_ordering_attr.into(), + v.conn_id_attr_on_a.into(), + ], + }) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TimeoutPacket { + timeout_height_attr_on_b: TimeoutHeightAttribute, + timeout_timestamp_attr_on_b: TimeoutTimestampAttribute, + seq_attr_on_a: SequenceAttribute, + port_id_attr_on_a: SrcPortIdAttribute, + chan_id_attr_on_a: SrcChannelIdAttribute, + port_id_attr_on_b: DstPortIdAttribute, + chan_id_attr_on_b: DstChannelIdAttribute, + channel_ordering_attr: ChannelOrderingAttribute, +} + +impl TimeoutPacket { + pub fn new(packet: Packet, channel_ordering: Order) -> Self { + Self { + timeout_height_attr_on_b: packet.timeout_height_on_b.into(), + timeout_timestamp_attr_on_b: packet.timeout_timestamp_on_b.into(), + seq_attr_on_a: packet.seq_on_a.into(), + port_id_attr_on_a: packet.port_id_on_a.into(), + chan_id_attr_on_a: packet.chan_id_on_a.into(), + port_id_attr_on_b: packet.port_id_on_b.into(), + chan_id_attr_on_b: packet.chan_id_on_b.into(), + channel_ordering_attr: channel_ordering.into(), + } + } + + pub fn timeout_height_on_b(&self) -> &TimeoutHeight { + &self.timeout_height_attr_on_b.timeout_height + } + + pub fn timeout_timestamp_on_b(&self) -> &TimeoutTimestamp { + &self.timeout_timestamp_attr_on_b.timeout_timestamp + } + + pub fn seq_on_a(&self) -> &Sequence { + &self.seq_attr_on_a.sequence + } + + pub fn port_id_on_a(&self) -> &PortId { + &self.port_id_attr_on_a.src_port_id + } + + pub fn chan_id_on_a(&self) -> &ChannelId { + &self.chan_id_attr_on_a.src_channel_id + } + + pub fn port_id_on_b(&self) -> &PortId { + &self.port_id_attr_on_b.dst_port_id + } + + pub fn chan_id_on_b(&self) -> &ChannelId { + &self.chan_id_attr_on_b.dst_channel_id + } + + pub fn channel_ordering(&self) -> &Order { + &self.channel_ordering_attr.order + } + + pub fn event_type(&self) -> &str { + TIMEOUT_EVENT + } +} + +impl TryFrom for abci::Event { + type Error = DecodingError; + + fn try_from(v: TimeoutPacket) -> Result { + Ok(abci::Event { + kind: TIMEOUT_EVENT.to_string(), + attributes: vec![ + v.timeout_height_attr_on_b.into(), + v.timeout_timestamp_attr_on_b.into(), + v.seq_attr_on_a.into(), + v.port_id_attr_on_a.into(), + v.chan_id_attr_on_a.into(), + v.port_id_attr_on_b.into(), + v.chan_id_attr_on_b.into(), + v.channel_ordering_attr.into(), + ], + }) + } +} + +#[cfg(test)] +mod tests { + use tendermint::abci::Event as AbciEvent; + + use super::*; + + #[test] + fn ibc_to_abci_channel_events() { + struct Test { + kind: &'static str, + event: AbciEvent, + expected_keys: Vec<&'static str>, + expected_values: Vec<&'static str>, + } + + let port_id = PortId::transfer(); + let channel_id = ChannelId::zero(); + let connection_id = ConnectionId::zero(); + let counterparty_port_id = PortId::transfer(); + let counterparty_channel_id = ChannelId::new(1); + let version = Version::new("ics20-1".to_string()); + let expected_keys = vec![ + "port_id", + "channel_id", + "counterparty_port_id", + "counterparty_channel_id", + "connection_id", + "version", + ]; + let expected_values = vec![ + "transfer", + "channel-0", + "transfer", + "channel-1", + "connection-0", + "ics20-1", + ]; + + let tests: Vec = vec![ + Test { + kind: CHANNEL_OPEN_INIT_EVENT, + event: OpenInit::new( + port_id.clone(), + channel_id.clone(), + counterparty_port_id.clone(), + connection_id.clone(), + version.clone(), + ) + .into(), + expected_keys: expected_keys.clone(), + expected_values: expected_values + .iter() + .enumerate() + .map(|(i, v)| if i == 3 { "" } else { v }) + .collect(), + }, + Test { + kind: CHANNEL_OPEN_TRY_EVENT, + event: OpenTry::new( + port_id.clone(), + channel_id.clone(), + counterparty_port_id.clone(), + counterparty_channel_id.clone(), + connection_id.clone(), + version, + ) + .into(), + expected_keys: expected_keys.clone(), + expected_values: expected_values.clone(), + }, + Test { + kind: CHANNEL_OPEN_ACK_EVENT, + event: OpenAck::new( + port_id.clone(), + channel_id.clone(), + counterparty_port_id.clone(), + counterparty_channel_id.clone(), + connection_id.clone(), + ) + .into(), + expected_keys: expected_keys[0..5].to_vec(), + expected_values: expected_values[0..5].to_vec(), + }, + Test { + kind: CHANNEL_OPEN_CONFIRM_EVENT, + event: OpenConfirm::new( + port_id.clone(), + channel_id.clone(), + counterparty_port_id.clone(), + counterparty_channel_id.clone(), + connection_id.clone(), + ) + .into(), + expected_keys: expected_keys[0..5].to_vec(), + expected_values: expected_values[0..5].to_vec(), + }, + Test { + kind: CHANNEL_CLOSE_INIT_EVENT, + event: CloseInit::new( + port_id.clone(), + channel_id.clone(), + counterparty_port_id.clone(), + counterparty_channel_id.clone(), + connection_id.clone(), + ) + .into(), + expected_keys: expected_keys[0..5].to_vec(), + expected_values: expected_values[0..5].to_vec(), + }, + Test { + kind: CHANNEL_CLOSE_CONFIRM_EVENT, + event: CloseConfirm::new( + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + ) + .into(), + expected_keys: expected_keys[0..5].to_vec(), + expected_values: expected_values[0..5].to_vec(), + }, + ]; + + for t in tests { + assert_eq!(t.kind, t.event.kind); + assert_eq!(t.expected_keys.len(), t.event.attributes.len()); + for (i, e) in t.event.attributes.iter().enumerate() { + assert_eq!( + e.key_str().unwrap(), + t.expected_keys[i], + "key mismatch for {:?}", + t.kind + ); + } + assert_eq!(t.expected_values.len(), t.event.attributes.len()); + for (i, e) in t.event.attributes.iter().enumerate() { + assert_eq!( + e.value_str().unwrap(), + t.expected_values[i], + "value mismatch for {:?}", + t.kind + ); + } + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs b/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs new file mode 100644 index 0000000000..c7a2c41799 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs @@ -0,0 +1,334 @@ +//! This module holds all the abci event attributes for IBC events emitted +//! during packet-related datagrams. +//! +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; +use tendermint::abci; + +use crate::acknowledgement::Acknowledgement; +use crate::channel::Order; +use crate::timeout::{TimeoutHeight, TimeoutTimestamp}; + +const PKT_SEQ_ATTRIBUTE_KEY: &str = "packet_sequence"; +const PKT_DATA_ATTRIBUTE_KEY: &str = "packet_data"; +const PKT_DATA_HEX_ATTRIBUTE_KEY: &str = "packet_data_hex"; +const PKT_SRC_PORT_ATTRIBUTE_KEY: &str = "packet_src_port"; +const PKT_SRC_CHANNEL_ATTRIBUTE_KEY: &str = "packet_src_channel"; +const PKT_DST_PORT_ATTRIBUTE_KEY: &str = "packet_dst_port"; +const PKT_DST_CHANNEL_ATTRIBUTE_KEY: &str = "packet_dst_channel"; +const PKT_CHANNEL_ORDERING_ATTRIBUTE_KEY: &str = "packet_channel_ordering"; +const PKT_TIMEOUT_HEIGHT_ATTRIBUTE_KEY: &str = "packet_timeout_height"; +const PKT_TIMEOUT_TIMESTAMP_ATTRIBUTE_KEY: &str = "packet_timeout_timestamp"; +const PKT_ACK_ATTRIBUTE_KEY: &str = "packet_ack"; +const PKT_ACK_HEX_ATTRIBUTE_KEY: &str = "packet_ack_hex"; +const PKT_CONNECTION_ID_ATTRIBUTE_KEY: &str = "packet_connection"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct PacketDataAttribute { + pub packet_data: Vec, +} + +impl TryFrom for Vec { + type Error = DecodingError; + + fn try_from(attr: PacketDataAttribute) -> Result { + let tags = vec![ + (PKT_DATA_ATTRIBUTE_KEY, str::from_utf8(&attr.packet_data)?).into(), + ( + PKT_DATA_HEX_ATTRIBUTE_KEY, + str::from_utf8(&hex::encode(attr.packet_data)) + .expect("Never fails because hexadecimal is valid UTF8"), + ) + .into(), + ]; + + Ok(tags) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct TimeoutHeightAttribute { + pub timeout_height: TimeoutHeight, +} + +impl From for abci::EventAttribute { + fn from(attr: TimeoutHeightAttribute) -> Self { + match attr.timeout_height { + TimeoutHeight::Never => (PKT_TIMEOUT_HEIGHT_ATTRIBUTE_KEY, "0-0").into(), + TimeoutHeight::At(height) => { + (PKT_TIMEOUT_HEIGHT_ATTRIBUTE_KEY, height.to_string()).into() + } + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct TimeoutTimestampAttribute { + pub timeout_timestamp: TimeoutTimestamp, +} + +impl From for abci::EventAttribute { + fn from(attr: TimeoutTimestampAttribute) -> Self { + ( + PKT_TIMEOUT_TIMESTAMP_ATTRIBUTE_KEY, + attr.timeout_timestamp.nanoseconds().to_string(), + ) + .into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct SequenceAttribute { + pub sequence: Sequence, +} + +impl From for abci::EventAttribute { + fn from(attr: SequenceAttribute) -> Self { + (PKT_SEQ_ATTRIBUTE_KEY, attr.sequence.to_string()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct SrcPortIdAttribute { + pub src_port_id: PortId, +} + +impl From for abci::EventAttribute { + fn from(attr: SrcPortIdAttribute) -> Self { + (PKT_SRC_PORT_ATTRIBUTE_KEY, attr.src_port_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct SrcChannelIdAttribute { + pub src_channel_id: ChannelId, +} + +impl From for abci::EventAttribute { + fn from(attr: SrcChannelIdAttribute) -> Self { + (PKT_SRC_CHANNEL_ATTRIBUTE_KEY, attr.src_channel_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct DstPortIdAttribute { + pub dst_port_id: PortId, +} + +impl From for abci::EventAttribute { + fn from(attr: DstPortIdAttribute) -> Self { + (PKT_DST_PORT_ATTRIBUTE_KEY, attr.dst_port_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct DstChannelIdAttribute { + pub dst_channel_id: ChannelId, +} + +impl From for abci::EventAttribute { + fn from(attr: DstChannelIdAttribute) -> Self { + (PKT_DST_CHANNEL_ATTRIBUTE_KEY, attr.dst_channel_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct ChannelOrderingAttribute { + pub order: Order, +} + +impl From for abci::EventAttribute { + fn from(attr: ChannelOrderingAttribute) -> Self { + (PKT_CHANNEL_ORDERING_ATTRIBUTE_KEY, attr.order.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct PacketConnectionIdAttribute { + pub connection_id: ConnectionId, +} + +impl From for abci::EventAttribute { + fn from(attr: PacketConnectionIdAttribute) -> Self { + (PKT_CONNECTION_ID_ATTRIBUTE_KEY, attr.connection_id.as_str()).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +pub struct AcknowledgementAttribute { + pub acknowledgement: Acknowledgement, +} + +impl TryFrom for Vec { + type Error = DecodingError; + + fn try_from(attr: AcknowledgementAttribute) -> Result { + let tags = vec![ + ( + PKT_ACK_ATTRIBUTE_KEY, + // Note: this attribute forces us to assume that Packet data + // 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())?, + ) + .into(), + ( + PKT_ACK_HEX_ATTRIBUTE_KEY, + str::from_utf8(&hex::encode(attr.acknowledgement)) + .expect("Never fails because hexadecimal is always valid UTF-8"), + ) + .into(), + ]; + + Ok(tags) + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/lib.rs b/ibc-eureka-core/ics04-channel/types/src/lib.rs new file mode 100644 index 0000000000..6e7a0b3c6b --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/lib.rs @@ -0,0 +1,35 @@ +//! Implementation of the Channel and Packet Semantics (ICS-04) data structures. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +pub mod channel; +pub mod error; +pub mod events; + +pub mod msgs; +pub mod packet; +pub mod timeout; + +pub mod acknowledgement; +pub mod commitment; +mod version; +pub use version::Version; + +/// Re-exports ICS-04 proto types from the `ibc-proto` crate +pub mod proto { + pub use ibc_proto::ibc::core::channel::*; +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs new file mode 100644 index 0000000000..10839e91d1 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs @@ -0,0 +1,69 @@ +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::packet::Packet; + +pub const ACKNOWLEDGEMENT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgAcknowledgement"; + +/// +/// Message definition for packet acknowledgements. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgAcknowledgement { + pub packet: Packet, + pub acknowledgement: Acknowledgement, + /// Proof of packet acknowledgement on the receiving chain + pub proof_acked_on_b: CommitmentProofBytes, + /// Height at which the commitment proof in this message was taken + pub proof_height_on_b: Height, + pub signer: Signer, +} + +impl Protobuf for MsgAcknowledgement {} + +impl TryFrom for MsgAcknowledgement { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgAcknowledgement) -> Result { + Ok(MsgAcknowledgement { + packet: raw_msg + .packet + .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()?, + proof_height_on_b: raw_msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg acknowledgement proof height", + ))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgAcknowledgement { + fn from(domain_msg: MsgAcknowledgement) -> Self { + RawMsgAcknowledgement { + packet: Some(domain_msg.packet.into()), + acknowledgement: domain_msg.acknowledgement.into(), + signer: domain_msg.signer.to_string(), + proof_height: Some(domain_msg.proof_height_on_b.into()), + proof_acked: domain_msg.proof_acked_on_b.into(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs new file mode 100644 index 0000000000..f3cbf1171e --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs @@ -0,0 +1,69 @@ +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; + +pub const CHAN_CLOSE_CONFIRM_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelCloseConfirm"; + +/// +/// Message definition for the second step in the channel close handshake (the `ChanCloseConfirm` +/// datagram). +/// Per our convention, this message is sent to chain B. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgChannelCloseConfirm { + pub port_id_on_b: PortId, + pub chan_id_on_b: ChannelId, + pub proof_chan_end_on_a: CommitmentProofBytes, + pub proof_height_on_a: Height, + pub signer: Signer, +} + +impl Protobuf for MsgChannelCloseConfirm {} + +impl TryFrom for MsgChannelCloseConfirm { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgChannelCloseConfirm) -> Result { + if raw_msg.counterparty_upgrade_sequence != 0 { + 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()?, + proof_height_on_a: raw_msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg channel close confirm proof height", + ))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgChannelCloseConfirm { + fn from(domain_msg: MsgChannelCloseConfirm) -> Self { + RawMsgChannelCloseConfirm { + port_id: domain_msg.port_id_on_b.to_string(), + channel_id: domain_msg.chan_id_on_b.to_string(), + proof_init: domain_msg.proof_chan_end_on_a.clone().into(), + proof_height: Some(domain_msg.proof_height_on_a.into()), + signer: domain_msg.signer.to_string(), + counterparty_upgrade_sequence: 0, + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs new file mode 100644 index 0000000000..3d852e5fa1 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs @@ -0,0 +1,48 @@ +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; + +pub const CHAN_CLOSE_INIT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelCloseInit"; + +/// +/// Message definition for the first step in the channel close handshake (`ChanCloseInit` datagram). +/// Per our convention, this message is sent to chain A. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgChannelCloseInit { + pub port_id_on_a: PortId, + pub chan_id_on_a: ChannelId, + pub signer: Signer, +} + +impl Protobuf for MsgChannelCloseInit {} + +impl TryFrom for MsgChannelCloseInit { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgChannelCloseInit) -> Result { + Ok(MsgChannelCloseInit { + port_id_on_a: raw_msg.port_id.parse()?, + chan_id_on_a: raw_msg.channel_id.parse()?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgChannelCloseInit { + fn from(domain_msg: MsgChannelCloseInit) -> Self { + RawMsgChannelCloseInit { + port_id: domain_msg.port_id_on_a.to_string(), + channel_id: domain_msg.chan_id_on_a.to_string(), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs new file mode 100644 index 0000000000..4d1151ff61 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs @@ -0,0 +1,66 @@ +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::Version; + +pub const CHAN_OPEN_ACK_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenAck"; + +/// Message definition for the third step in the channel open handshake (`ChanOpenAck` datagram). +/// +/// Per our convention, this message is sent to chain A. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgChannelOpenAck { + pub port_id_on_a: PortId, + pub chan_id_on_a: ChannelId, + pub chan_id_on_b: ChannelId, + pub version_on_b: Version, + pub proof_chan_end_on_b: CommitmentProofBytes, + pub proof_height_on_b: Height, + pub signer: Signer, +} + +impl Protobuf for MsgChannelOpenAck {} + +impl TryFrom for MsgChannelOpenAck { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgChannelOpenAck) -> Result { + Ok(MsgChannelOpenAck { + port_id_on_a: raw_msg.port_id.parse()?, + 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()?, + proof_height_on_b: raw_msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::missing_raw_data("proof height"))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgChannelOpenAck { + fn from(domain_msg: MsgChannelOpenAck) -> Self { + RawMsgChannelOpenAck { + port_id: domain_msg.port_id_on_a.to_string(), + channel_id: domain_msg.chan_id_on_a.to_string(), + counterparty_channel_id: domain_msg.chan_id_on_b.to_string(), + counterparty_version: domain_msg.version_on_b.to_string(), + proof_try: domain_msg.proof_chan_end_on_b.into(), + proof_height: Some(domain_msg.proof_height_on_b.into()), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs new file mode 100644 index 0000000000..d28fe2a6e4 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs @@ -0,0 +1,62 @@ +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; + +pub const CHAN_OPEN_CONFIRM_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenConfirm"; + +/// +/// Message definition for the fourth step in the channel open handshake (`ChanOpenConfirm` +/// datagram). +/// Per our convention, this message is sent to chain B. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgChannelOpenConfirm { + pub port_id_on_b: PortId, + pub chan_id_on_b: ChannelId, + pub proof_chan_end_on_a: CommitmentProofBytes, + pub proof_height_on_a: Height, + pub signer: Signer, +} + +impl Protobuf for MsgChannelOpenConfirm {} + +impl TryFrom for MsgChannelOpenConfirm { + 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()?, + proof_height_on_a: raw_msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg channel open confirm proof height", + ))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgChannelOpenConfirm { + fn from(domain_msg: MsgChannelOpenConfirm) -> Self { + RawMsgChannelOpenConfirm { + port_id: domain_msg.port_id_on_b.to_string(), + channel_id: domain_msg.chan_id_on_b.to_string(), + proof_ack: domain_msg.proof_chan_end_on_a.into(), + proof_height: Some(domain_msg.proof_height_on_a.into()), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs new file mode 100644 index 0000000000..2299802d49 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs @@ -0,0 +1,95 @@ +use ibc_core_host_types::error::DecodingError; +use ibc_core_host_types::identifiers::{ConnectionId, PortId}; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; +use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; +use ibc_proto::Protobuf; + +use crate::channel::{verify_connection_hops_length, ChannelEnd, Counterparty, Order, State}; +use crate::error::ChannelError; +use crate::Version; + +pub const CHAN_OPEN_INIT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenInit"; + +/// +/// Message definition for the first step in the channel open handshake (`ChanOpenInit` datagram). +/// Per our convention, this message is sent to chain A. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgChannelOpenInit { + pub port_id_on_a: PortId, + pub connection_hops_on_a: Vec, + pub port_id_on_b: PortId, + pub ordering: Order, + pub signer: Signer, + /// Allow a relayer to specify a particular version by providing a non-empty version string + pub version_proposal: Version, +} + +impl MsgChannelOpenInit { + /// Checks if the `connection_hops` has a length of `expected`. + /// + /// Note: Current IBC version only supports one connection hop. + pub fn verify_connection_hops_length(&self) -> Result<(), ChannelError> { + verify_connection_hops_length(&self.connection_hops_on_a, 1) + } +} + +impl Protobuf for MsgChannelOpenInit {} + +impl TryFrom for MsgChannelOpenInit { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgChannelOpenInit) -> Result { + let chan_end_on_a: ChannelEnd = raw_msg + .channel + .ok_or(DecodingError::missing_raw_data("channel end"))? + .try_into()?; + + 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()?, + connection_hops_on_a: chan_end_on_a.connection_hops, + port_id_on_b: chan_end_on_a.remote.port_id, + ordering: chan_end_on_a.ordering, + signer: raw_msg.signer.into(), + version_proposal: chan_end_on_a.version, + }) + } +} + +impl From for RawMsgChannelOpenInit { + fn from(domain_msg: MsgChannelOpenInit) -> Self { + let chan_end_on_a = ChannelEnd::new_without_validation( + State::Init, + domain_msg.ordering, + Counterparty::new(domain_msg.port_id_on_b, None), + domain_msg.connection_hops_on_a, + domain_msg.version_proposal, + ); + RawMsgChannelOpenInit { + port_id: domain_msg.port_id_on_a.to_string(), + channel: Some(chan_end_on_a.into()), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs new file mode 100644 index 0000000000..042f2c07a6 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs @@ -0,0 +1,121 @@ +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; +use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; +use ibc_proto::Protobuf; + +use crate::channel::{verify_connection_hops_length, ChannelEnd, Counterparty, Order, State}; +use crate::error::ChannelError; +use crate::Version; + +pub const CHAN_OPEN_TRY_TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelOpenTry"; + +/// +/// Message definition for the second step in the channel open handshake (`ChanOpenTry` datagram). +/// Per our convention, this message is sent to chain B. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgChannelOpenTry { + pub port_id_on_b: PortId, + pub connection_hops_on_b: Vec, + pub port_id_on_a: PortId, + pub chan_id_on_a: ChannelId, + pub version_supported_on_a: Version, + pub proof_chan_end_on_a: CommitmentProofBytes, + pub proof_height_on_a: Height, + pub ordering: Order, + pub signer: Signer, + + #[deprecated(since = "0.22.0")] + /// Only kept here for proper conversion to/from the raw type + pub version_proposal: Version, +} + +impl MsgChannelOpenTry { + /// Checks if the `connection_hops` has a length of `expected`. + /// + /// Note: The current IBC version only supports one connection hop. + pub fn verify_connection_hops_length(&self) -> Result<(), ChannelError> { + verify_connection_hops_length(&self.connection_hops_on_b, 1) + } +} + +impl Protobuf for MsgChannelOpenTry {} + +impl TryFrom for MsgChannelOpenTry { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgChannelOpenTry) -> Result { + let chan_end_on_b: ChannelEnd = raw_msg + .channel + .ok_or(DecodingError::missing_raw_data("channel end not set"))? + .try_into()?; + + 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(DecodingError::invalid_raw_data("previous channel id must be empty; it has been deprecated as crossing hellos are no longer supported"))?; + } + + #[allow(deprecated)] + let msg = MsgChannelOpenTry { + port_id_on_b: raw_msg.port_id.parse()?, + 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( + 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()?, + proof_height_on_a: raw_msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::invalid_raw_data( + "msg channel open try proof height", + ))?, + signer: raw_msg.signer.into(), + version_proposal: chan_end_on_b.version, + }; + + Ok(msg) + } +} + +impl From for RawMsgChannelOpenTry { + fn from(domain_msg: MsgChannelOpenTry) -> Self { + let chan_end_on_b = ChannelEnd::new_without_validation( + State::TryOpen, + domain_msg.ordering, + Counterparty::new(domain_msg.port_id_on_a, Some(domain_msg.chan_id_on_a)), + domain_msg.connection_hops_on_b, + Version::empty(), // Excessive field to satisfy the type conversion + ); + #[allow(deprecated)] + RawMsgChannelOpenTry { + port_id: domain_msg.port_id_on_b.to_string(), + previous_channel_id: "".to_string(), // Excessive field to satisfy the type conversion", + channel: Some(chan_end_on_b.into()), + counterparty_version: domain_msg.version_supported_on_a.to_string(), + proof_init: domain_msg.proof_chan_end_on_a.clone().into(), + proof_height: Some(domain_msg.proof_height_on_a.into()), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs new file mode 100644 index 0000000000..8e518591cf --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs @@ -0,0 +1,79 @@ +//! Message definitions for all ICS4 domain types: channel open & close handshake datagrams, as well +//! as packets. + +mod acknowledgement; +mod chan_close_confirm; +mod chan_close_init; +mod chan_open_ack; +mod chan_open_confirm; +mod chan_open_init; +mod chan_open_try; +mod recv_packet; +mod timeout; +mod timeout_on_close; + +// Opening handshake messages. +// Packet specific messages. +pub use acknowledgement::*; +// Closing handshake messages. +pub use chan_close_confirm::*; +pub use chan_close_init::*; +pub use chan_open_ack::*; +pub use chan_open_confirm::*; +pub use chan_open_init::*; +pub use chan_open_try::*; +use ibc_core_host_types::identifiers::*; +use ibc_primitives::prelude::*; +pub use recv_packet::*; +pub use timeout::*; +pub use timeout_on_close::*; + +/// All channel messages +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, derive_more::From)] +pub enum ChannelMsg { + OpenInit(MsgChannelOpenInit), + OpenTry(MsgChannelOpenTry), + OpenAck(MsgChannelOpenAck), + OpenConfirm(MsgChannelOpenConfirm), + CloseInit(MsgChannelCloseInit), + CloseConfirm(MsgChannelCloseConfirm), +} + +/// All packet messages +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, derive_more::From)] +pub enum PacketMsg { + Recv(MsgRecvPacket), + Ack(MsgAcknowledgement), + Timeout(MsgTimeout), + TimeoutOnClose(MsgTimeoutOnClose), +} + +pub fn channel_msg_to_port_id(msg: &ChannelMsg) -> &PortId { + match msg { + ChannelMsg::OpenInit(msg) => &msg.port_id_on_a, + ChannelMsg::OpenTry(msg) => &msg.port_id_on_b, + ChannelMsg::OpenAck(msg) => &msg.port_id_on_a, + ChannelMsg::OpenConfirm(msg) => &msg.port_id_on_b, + ChannelMsg::CloseInit(msg) => &msg.port_id_on_a, + ChannelMsg::CloseConfirm(msg) => &msg.port_id_on_b, + } +} + +pub fn packet_msg_to_port_id(msg: &PacketMsg) -> &PortId { + match msg { + PacketMsg::Recv(msg) => &msg.packet.port_id_on_b, + PacketMsg::Ack(msg) => &msg.packet.port_id_on_a, + PacketMsg::Timeout(msg) => &msg.packet.port_id_on_a, + PacketMsg::TimeoutOnClose(msg) => &msg.packet.port_id_on_a, + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs new file mode 100644 index 0000000000..c2b14ab418 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs @@ -0,0 +1,63 @@ +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::packet::Packet; + +pub const RECV_PACKET_TYPE_URL: &str = "/ibc.core.channel.v1.MsgRecvPacket"; + +/// +/// Message definition for the "packet receiving" datagram. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgRecvPacket { + /// The packet to be received + pub packet: Packet, + /// Proof of packet commitment on the sending chain + pub proof_commitment_on_a: CommitmentProofBytes, + /// Height at which the commitment proof in this message were taken + pub proof_height_on_a: Height, + /// The signer of the message + pub signer: Signer, +} + +impl Protobuf for MsgRecvPacket {} + +impl TryFrom for MsgRecvPacket { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgRecvPacket) -> Result { + Ok(MsgRecvPacket { + packet: raw_msg + .packet + .ok_or(DecodingError::missing_raw_data("msg recv packet data"))? + .try_into()?, + 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(DecodingError::invalid_raw_data("msg recv proof height"))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgRecvPacket { + fn from(domain_msg: MsgRecvPacket) -> Self { + RawMsgRecvPacket { + packet: Some(domain_msg.packet.into()), + proof_commitment: domain_msg.proof_commitment_on_a.into(), + proof_height: Some(domain_msg.proof_height_on_a.into()), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs new file mode 100644 index 0000000000..5fc2558966 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs @@ -0,0 +1,69 @@ +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::packet::Packet; + +pub const TIMEOUT_TYPE_URL: &str = "/ibc.core.channel.v1.MsgTimeout"; + +/// +/// Message definition for packet timeout domain type, +/// which is sent on chain A and needs to prove that a previously sent packet was not received on chain B +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgTimeout { + pub packet: Packet, + pub next_seq_recv_on_b: Sequence, + pub proof_unreceived_on_b: CommitmentProofBytes, + pub proof_height_on_b: Height, + pub signer: Signer, +} + +impl Protobuf for MsgTimeout {} + +impl TryFrom for MsgTimeout { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgTimeout) -> Result { + if raw_msg.next_sequence_recv == 0 { + return Err(DecodingError::invalid_raw_data( + "msg timeout packet sequence cannot be 0", + )); + } + Ok(MsgTimeout { + packet: raw_msg + .packet + .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()?, + proof_height_on_b: raw_msg + .proof_height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(DecodingError::missing_raw_data("msg timeout proof height"))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgTimeout { + fn from(domain_msg: MsgTimeout) -> Self { + RawMsgTimeout { + packet: Some(domain_msg.packet.into()), + proof_unreceived: domain_msg.proof_unreceived_on_b.into(), + proof_height: Some(domain_msg.proof_height_on_b.into()), + next_sequence_recv: domain_msg.next_seq_recv_on_b.into(), + signer: domain_msg.signer.to_string(), + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs new file mode 100644 index 0000000000..34394b8561 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs @@ -0,0 +1,77 @@ +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::packet::Packet; + +pub const TIMEOUT_ON_CLOSE_TYPE_URL: &str = "/ibc.core.channel.v1.MsgTimeoutOnClose"; + +/// +/// Message definition for packet timeout domain type. +/// +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MsgTimeoutOnClose { + pub packet: Packet, + pub next_seq_recv_on_b: Sequence, + pub proof_unreceived_on_b: CommitmentProofBytes, + pub proof_close_on_b: CommitmentProofBytes, + pub proof_height_on_b: Height, + pub signer: Signer, +} + +impl Protobuf for MsgTimeoutOnClose {} + +impl TryFrom for MsgTimeoutOnClose { + type Error = DecodingError; + + fn try_from(raw_msg: RawMsgTimeoutOnClose) -> Result { + if raw_msg.next_sequence_recv == 0 { + return Err(DecodingError::invalid_raw_data( + "packet sequence cannot be 0", + )); + } + + if raw_msg.counterparty_upgrade_sequence != 0 { + return Err(DecodingError::invalid_raw_data("channel upgrade sequence")); + } + + Ok(MsgTimeoutOnClose { + packet: raw_msg + .packet + .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()?, + 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(DecodingError::invalid_raw_data("msg timeout proof height"))?, + signer: raw_msg.signer.into(), + }) + } +} + +impl From for RawMsgTimeoutOnClose { + fn from(domain_msg: MsgTimeoutOnClose) -> Self { + RawMsgTimeoutOnClose { + packet: Some(domain_msg.packet.into()), + proof_unreceived: domain_msg.proof_unreceived_on_b.into(), + proof_close: domain_msg.proof_close_on_b.into(), + proof_height: Some(domain_msg.proof_height_on_b.into()), + next_sequence_recv: domain_msg.next_seq_recv_on_b.into(), + signer: domain_msg.signer.to_string(), + counterparty_upgrade_sequence: 0, + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/packet.rs b/ibc-eureka-core/ics04-channel/types/src/packet.rs new file mode 100644 index 0000000000..e5f2e87796 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/packet.rs @@ -0,0 +1,327 @@ +//! 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::timeout::TimeoutTimestamp; + +/// Enumeration of proof carrying ICS4 message, helper for relayer. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PacketMsgType { + Recv, + Ack, + TimeoutUnordered, + TimeoutOrdered, + TimeoutOnClose, +} + +/// 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( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +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 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + PacketMsgType::Recv => write!(f, "(PacketMsgType::Recv)"), + PacketMsgType::Ack => write!(f, "(PacketMsgType::Ack)"), + PacketMsgType::TimeoutUnordered => write!(f, "(PacketMsgType::TimeoutUnordered)"), + PacketMsgType::TimeoutOrdered => write!(f, "(PacketMsgType::TimeoutOrdered)"), + PacketMsgType::TimeoutOnClose => write!(f, "(PacketMsgType::TimeoutOnClose)"), + } + } +} + +/// The packet type; this is what applications send to one another. +/// +/// Each application defines the structure of the `data` field. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct Packet { + pub seq_on_a: Sequence, + pub port_id_on_a: PortId, + pub chan_id_on_a: ChannelId, + pub port_id_on_b: PortId, + pub chan_id_on_b: ChannelId, + #[cfg_attr( + feature = "serde", + serde(serialize_with = "ibc_core_commitment_types::serializer::ser_hex_upper") + )] + pub data: Vec, + pub timeout_height_on_b: TimeoutHeight, + pub timeout_timestamp_on_b: TimeoutTimestamp, +} + +struct PacketData<'a>(&'a [u8]); + +impl<'a> core::fmt::Debug for PacketData<'a> { + fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!(formatter, "{:?}", self.0) + } +} + +impl core::fmt::Debug for Packet { + fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + // Remember: if you alter the definition of `Packet`, + // 1. update the formatter debug struct builder calls (return object of + // this function) + // 2. update this destructuring assignment accordingly + let Packet { + seq_on_a: _, + port_id_on_a: _, + chan_id_on_a: _, + port_id_on_b: _, + chan_id_on_b: _, + data, + timeout_height_on_b: _, + timeout_timestamp_on_b: _, + } = self; + let data_wrapper = PacketData(data); + + formatter + .debug_struct("Packet") + .field("sequence", &self.seq_on_a) + .field("source_port", &self.port_id_on_a) + .field("source_channel", &self.chan_id_on_a) + .field("destination_port", &self.port_id_on_b) + .field("destination_channel", &self.chan_id_on_b) + .field("data", &data_wrapper) + .field("timeout_height", &self.timeout_height_on_b) + .field("timeout_timestamp", &self.timeout_timestamp_on_b) + .finish() + } +} + +impl Packet { + /// Checks whether a packet from a + /// [`SendPacket`](crate::events::SendPacket) + /// event is timed-out relative to the current state of the + /// destination chain. + /// + /// Checks for time-out relative to the destination chain's + /// current timestamp `dst_chain_ts` as well as relative to + /// the height `dst_chain_height`. + /// + /// Note: a timed-out packet should result in a + /// [`MsgTimeout`](crate::msgs::MsgTimeout), + /// instead of the common-case where it results in + /// [`MsgRecvPacket`](crate::msgs::MsgRecvPacket). + pub fn timed_out(&self, dst_chain_ts: &Timestamp, dst_chain_height: Height) -> bool { + let height_timed_out = self.timeout_height_on_b.has_expired(dst_chain_height); + + let timestamp_timed_out = self.timeout_timestamp_on_b.has_expired(dst_chain_ts); + + height_timed_out || timestamp_timed_out + } +} + +/// Custom debug output to omit the packet data +impl core::fmt::Display for Packet { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!( + f, + "seq:{}, path:{}/{}->{}/{}, toh:{}, tos:{})", + self.seq_on_a, + self.chan_id_on_a, + self.port_id_on_a, + self.chan_id_on_b, + self.port_id_on_b, + self.timeout_height_on_b, + self.timeout_timestamp_on_b + ) + } +} + +impl TryFrom for Packet { + type Error = DecodingError; + + fn try_from(raw_pkt: RawPacket) -> Result { + if Sequence::from(raw_pkt.sequence).is_zero() { + return Err(DecodingError::invalid_raw_data( + "packet sequence cannot be 0", + )); + } + + if raw_pkt.data.is_empty() { + return Err(DecodingError::missing_raw_data("packet data is not set"))?; + } + + // Note: ibc-go currently (July 2022) incorrectly treats the timeout + // heights `{revision_number : >0, revision_height: 0}` as valid + // timeouts. However, heights with `revision_height == 0` are invalid in + // Tendermint. We explicitly reject these values because they go against + // the Tendermint spec, and shouldn't be used. To timeout on the next + // revision_number as soon as the chain starts, + // `{revision_number: old_rev + 1, revision_height: 1}` + // should be used. + let packet_timeout_height: TimeoutHeight = raw_pkt.timeout_height.try_into()?; + + let timeout_timestamp_on_b: TimeoutTimestamp = raw_pkt.timeout_timestamp.into(); + + // 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(DecodingError::missing_raw_data( + "missing one of packet timeout height or timeout timestamp", + )); + } + + Ok(Packet { + seq_on_a: Sequence::from(raw_pkt.sequence), + port_id_on_a: raw_pkt.source_port.parse()?, + chan_id_on_a: raw_pkt.source_channel.parse()?, + port_id_on_b: raw_pkt.destination_port.parse()?, + chan_id_on_b: raw_pkt.destination_channel.parse()?, + data: raw_pkt.data, + timeout_height_on_b: packet_timeout_height, + timeout_timestamp_on_b, + }) + } +} + +impl From for RawPacket { + fn from(packet: Packet) -> Self { + RawPacket { + sequence: packet.seq_on_a.value(), + source_port: packet.port_id_on_a.to_string(), + source_channel: packet.chan_id_on_a.to_string(), + destination_port: packet.port_id_on_b.to_string(), + destination_channel: packet.chan_id_on_b.to_string(), + data: packet.data, + timeout_height: packet.timeout_height_on_b.into(), + timeout_timestamp: packet.timeout_timestamp_on_b.nanoseconds(), + } + } +} + +/// The packet state type. +/// +/// Each application defines the structure of the `data` field. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct PacketState { + pub port_id: PortId, + pub chan_id: ChannelId, + pub seq: Sequence, + #[cfg_attr( + feature = "serde", + serde(serialize_with = "ibc_core_commitment_types::serializer::ser_hex_upper") + )] + pub data: Vec, +} +impl core::fmt::Debug for PacketState { + fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + let data_wrapper = PacketData(&self.data); + + formatter + .debug_struct("PacketState") + .field("port", &self.port_id) + .field("channel", &self.chan_id) + .field("sequence", &self.seq) + .field("data", &data_wrapper) + .finish() + } +} + +/// Custom debug output to omit the packet data +impl core::fmt::Display for PacketState { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!( + f, + "seq:{}, path:{}/{}", + self.seq, self.chan_id, self.port_id, + ) + } +} + +impl TryFrom for PacketState { + type Error = DecodingError; + + fn try_from(raw_pkt: RawPacketState) -> Result { + if Sequence::from(raw_pkt.sequence).is_zero() { + return Err(DecodingError::invalid_raw_data( + "packet sequence cannot be 0", + )); + } + + if raw_pkt.data.is_empty() { + return Err(DecodingError::missing_raw_data("packet data not set"))?; + } + + Ok(PacketState { + seq: Sequence::from(raw_pkt.sequence), + port_id: raw_pkt.port_id.parse()?, + chan_id: raw_pkt.channel_id.parse()?, + data: raw_pkt.data, + }) + } +} + +impl From for RawPacketState { + fn from(packet: PacketState) -> Self { + Self { + sequence: packet.seq.value(), + port_id: packet.port_id.to_string(), + channel_id: packet.chan_id.to_string(), + data: packet.data, + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs b/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs new file mode 100644 index 0000000000..46d6ab49a9 --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs @@ -0,0 +1,205 @@ +//! Types and utilities pertaining to packet timeouts. + +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; + +/// 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. +/// +/// `TimeoutHeight` is treated differently from other heights because +/// +/// `RawHeight.timeout_height == {revision_number: 0, revision_height = 0}` +/// +/// is legal and meaningful, even though the Tendermint spec rejects this height +/// as invalid. Thus, it must be parsed specially, where this special case means +/// "no timeout". +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub enum TimeoutHeight { + Never, + At(Height), +} + +impl TimeoutHeight { + /// Returns if the timeout height is set. + pub fn is_set(&self) -> bool { + match self { + TimeoutHeight::At(_) => true, + TimeoutHeight::Never => false, + } + } + + pub fn no_timeout() -> Self { + Self::Never + } + + /// Revision number to be used in packet commitment computation + pub fn commitment_revision_number(&self) -> u64 { + match self { + Self::At(height) => height.revision_number(), + Self::Never => 0, + } + } + + /// Revision height to be used in packet commitment computation + pub fn commitment_revision_height(&self) -> u64 { + match self { + Self::At(height) => height.revision_height(), + Self::Never => 0, + } + } + + /// Check if a height is *strictly past* the timeout height, and thus is + /// deemed expired. + pub fn has_expired(&self, height: Height) -> bool { + match self { + Self::At(timeout_height) => height > *timeout_height, + // When there's no timeout, heights are never expired + Self::Never => false, + } + } + + /// Returns a string formatted for an ABCI event attribute value. + pub fn to_event_attribute_value(self) -> String { + match self { + TimeoutHeight::At(height) => height.to_string(), + TimeoutHeight::Never => "0-0".into(), + } + } +} + +impl TryFrom for TimeoutHeight { + type Error = DecodingError; + + // Note: it is important for `revision_number` to also be `0`, otherwise + // packet commitment proofs will be incorrect (proof construction in + // `ChannelReader::packet_commitment()` uses both `revision_number` and + // `revision_height`). Note also that ibc-go conforms to this convention. + fn try_from(raw_height: RawHeight) -> Result { + if raw_height.revision_number == 0 && raw_height.revision_height == 0 { + Ok(TimeoutHeight::Never) + } else { + let height = raw_height.try_into()?; + Ok(TimeoutHeight::At(height)) + } + } +} + +impl TryFrom> for TimeoutHeight { + type Error = DecodingError; + + fn try_from(maybe_raw_height: Option) -> Result { + match maybe_raw_height { + Some(raw_height) => Self::try_from(raw_height), + None => Ok(TimeoutHeight::Never), + } + } +} +/// We map "no timeout height" to `Some(RawHeight::zero)` due to a quirk +/// in ICS-4. See . +impl From for Option { + fn from(timeout_height: TimeoutHeight) -> Self { + let raw_height = match timeout_height { + TimeoutHeight::At(height) => height.into(), + TimeoutHeight::Never => RawHeight { + revision_number: 0, + revision_height: 0, + }, + }; + + Some(raw_height) + } +} + +impl From for TimeoutHeight { + fn from(height: Height) -> Self { + Self::At(height) + } +} + +impl Display for TimeoutHeight { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + TimeoutHeight::At(timeout_height) => write!(f, "{timeout_height}"), + TimeoutHeight::Never => write!(f, "no timeout height"), + } + } +} + +#[cfg(feature = "serde")] +mod serialize { + use serde::{Deserialize, Serialize}; + + use super::TimeoutHeight; + + impl Serialize for TimeoutHeight { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + // When there is no timeout, we cannot construct an ICS02 Height with + // revision number and height at zero, so we have to define an + // isomorphic struct to serialize it as if it were an ICS02 height. + #[derive(Serialize)] + struct Height { + revision_number: u64, + revision_height: u64, + } + + match self { + // If there is no timeout, we use our ad-hoc struct above + TimeoutHeight::Never => { + let zero = Height { + revision_number: 0, + revision_height: 0, + }; + + zero.serialize(serializer) + } + // Otherwise we can directly serialize the underlying height + TimeoutHeight::At(height) => height.serialize(serializer), + } + } + } + + impl<'de> Deserialize<'de> for TimeoutHeight { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use ibc_core_client_types::Height as Ics02Height; + + // Here we have to use a bespoke struct as well in order to deserialize + // a height which may have a revision height equal to zero. + #[derive(Deserialize)] + struct Height { + revision_number: u64, + revision_height: u64, + } + + Height::deserialize(deserializer).map(|height| { + Ics02Height::new(height.revision_number, height.revision_height) + // If it's a valid height with a non-zero revision height, then we have a timeout + .map(TimeoutHeight::At) + // Otherwise, no timeout + .unwrap_or(TimeoutHeight::Never) + }) + } + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/timeout/mod.rs b/ibc-eureka-core/ics04-channel/types/src/timeout/mod.rs new file mode 100644 index 0000000000..e07e1204df --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/timeout/mod.rs @@ -0,0 +1,5 @@ +mod height; +mod timestamp; + +pub use height::TimeoutHeight; +pub use timestamp::TimeoutTimestamp; diff --git a/ibc-eureka-core/ics04-channel/types/src/timeout/timestamp.rs b/ibc-eureka-core/ics04-channel/types/src/timeout/timestamp.rs new file mode 100644 index 0000000000..8cd45b712b --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/timeout/timestamp.rs @@ -0,0 +1,211 @@ +use core::fmt::{Display, Error as FmtError, Formatter}; +use core::ops::{Add, Sub}; +use core::time::Duration; + +use ibc_primitives::prelude::*; +use ibc_primitives::Timestamp; + +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. +/// +/// The IBC protocol represents timestamps as u64 Unix timestamps in +/// nanoseconds. A protocol value of 0 indicates that the timestamp is not set. +/// In this case, we use the explicit `Never` variant to distinguish the absence +/// of a timeout when converting from a zero protobuf value. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub enum TimeoutTimestamp { + Never, + At(Timestamp), +} + +impl TimeoutTimestamp { + /// Creates a new timeout timestamp from a given nanosecond value. + pub fn from_nanoseconds(nanoseconds: u64) -> Self { + Self::from(nanoseconds) + } + + /// Returns the timestamp in nanoseconds, where 0 indicates the absence + /// of a timeout. + pub fn nanoseconds(&self) -> u64 { + match self { + Self::At(timestamp) => timestamp.nanoseconds(), + Self::Never => 0, + } + } + + /// Returns `true` if the timeout timestamp is set. + pub fn is_set(&self) -> bool { + match self { + TimeoutTimestamp::At(_) => true, + TimeoutTimestamp::Never => false, + } + } + + /// Returns a timeout timestamp that never expires. + pub fn no_timeout() -> Self { + Self::Never + } + + /// Check if a timestamp is *strictly past* the timeout timestamp, and thus + /// is deemed expired. + pub fn has_expired(&self, timestamp: &Timestamp) -> bool { + match self { + Self::At(timeout_timestamp) => timestamp > timeout_timestamp, + // When there's no timeout, timestamps are never expired + Self::Never => false, + } + } +} + +impl From for TimeoutTimestamp { + fn from(timestamp: Timestamp) -> Self { + TimeoutTimestamp::At(timestamp) + } +} + +// Note: As any `u64` value is interpreted as a Unix timestamp in nanoseconds +// when used for timeouts, implementing `From` is appropriate. +impl From for TimeoutTimestamp { + fn from(timestamp: u64) -> Self { + if timestamp == 0 { + TimeoutTimestamp::Never + } else { + TimeoutTimestamp::At(Timestamp::from_nanoseconds(timestamp)) + } + } +} + +impl Display for TimeoutTimestamp { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + TimeoutTimestamp::At(timeout_timestamp) => write!(f, "{timeout_timestamp}"), + TimeoutTimestamp::Never => write!(f, "no timeout timestamp"), + } + } +} + +impl Add for TimeoutTimestamp { + type Output = Result; + + fn add(self, rhs: Duration) -> Self::Output { + match self { + TimeoutTimestamp::At(timestamp) => { + let new_timestamp = timestamp.add(rhs)?; + Ok(TimeoutTimestamp::At(new_timestamp)) + } + TimeoutTimestamp::Never => Err(ChannelError::MissingTimeout), + } + } +} + +impl Sub for TimeoutTimestamp { + type Output = Result; + + fn sub(self, rhs: Duration) -> Self::Output { + match self { + TimeoutTimestamp::At(timestamp) => { + let new_timestamp = timestamp.sub(rhs)?; + Ok(TimeoutTimestamp::At(new_timestamp)) + } + TimeoutTimestamp::Never => Err(ChannelError::MissingTimeout), + } + } +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + + fn never() -> TimeoutTimestamp { + TimeoutTimestamp::Never + } + + fn some(t: u64) -> TimeoutTimestamp { + TimeoutTimestamp::At(Timestamp::from_nanoseconds(t)) + } + + use super::*; + #[rstest] + #[case::zero_plus_zero(some(0), 0, some(0))] // NOTE: zero timestamp is 1970-01-01T00:00:00Z + #[case::zero_plus_one(some(0), 1, some(1))] + #[case::some_plus_zero(some(123456), 0, some(123456))] + #[case::some_plus_one(some(123456), 1, some(123457))] + fn test_timeout_add_arithmetic( + #[case] timeout_timestamp: TimeoutTimestamp, + #[case] duration: u64, + #[case] expect: TimeoutTimestamp, + ) { + let duration = Duration::from_nanos(duration); + let result = timeout_timestamp + duration; + assert_eq!(result.unwrap(), expect); + } + + #[rstest] + #[case::never(never(), 0)] + #[case::never_plus_one(never(), 1)] + fn test_invalid_timeout_add_arithmetic( + #[case] timeout_timestamp: TimeoutTimestamp, + #[case] duration: u64, + ) { + let duration = Duration::from_nanos(duration); + let result = timeout_timestamp + duration; + assert!(result.is_err()); + } + + #[rstest] + #[case::zero_minus_zero(some(0), 0, some(0))] + #[case::some_minus(some(123456), 123456, some(0))] + #[case::some_minus_zero(some(123456), 0, some(123456))] + #[case::some_minus_one(some(123456), 1, some(123455))] + fn test_timeout_sub_arithmetic( + #[case] timeout_timestamp: TimeoutTimestamp, + #[case] duration: u64, + #[case] expect: TimeoutTimestamp, + ) { + let duration = Duration::from_nanos(duration); + let result = timeout_timestamp - duration; + assert_eq!(result.unwrap(), expect); + } + + #[rstest] + #[case::never(never(), 0)] + #[case::never_minus_one(never(), 1)] + #[case::zero_minus_one(some(0), 1)] + fn test_invalid_sub_arithmetic( + #[case] timeout_timestamp: TimeoutTimestamp, + #[case] duration: u64, + ) { + let duration = Duration::from_nanos(duration); + let result = timeout_timestamp - duration; + assert!(result.is_err()); + } + + #[cfg(feature = "serde")] + #[rstest::rstest] + #[case::never(TimeoutTimestamp::Never)] + #[case::at_zero(TimeoutTimestamp::At(Timestamp::from_nanoseconds(0)))] + #[case::at_some(TimeoutTimestamp::At(Timestamp::from_nanoseconds(123456)))] + #[case::at_u64_max(TimeoutTimestamp::At(Timestamp::from_nanoseconds(u64::MAX)))] + fn test_timeout_timestamp_serde(#[case] timeout_timestamp: TimeoutTimestamp) { + let serialized = serde_json::to_string(&timeout_timestamp).unwrap(); + let deserialized: TimeoutTimestamp = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(timeout_timestamp, deserialized); + } +} diff --git a/ibc-eureka-core/ics04-channel/types/src/version.rs b/ibc-eureka-core/ics04-channel/types/src/version.rs new file mode 100644 index 0000000000..1cf5c894af --- /dev/null +++ b/ibc-eureka-core/ics04-channel/types/src/version.rs @@ -0,0 +1,81 @@ +//! Data type definition and utilities for the +//! version field of a channel end. +//! + +use core::convert::Infallible; +use core::fmt::{Display, Error as FmtError, Formatter}; +use core::str::FromStr; + +use ibc_primitives::prelude::*; + +use super::error::ChannelError; + +/// The version field for a `ChannelEnd`. +/// +/// This field is opaque to the core IBC protocol. +/// No explicit validation is necessary, and the +/// spec (v1) currently allows empty strings. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Version(String); + +impl Version { + pub fn new(v: String) -> Self { + Self(v) + } + + pub fn empty() -> Self { + Self::new("".to_string()) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn as_str(&self) -> &str { + &self.0 + } + + pub fn verify_is_expected(&self, expected: Version) -> Result<(), ChannelError> { + if self != &expected { + return Err(ChannelError::UnsupportedVersion { + expected, + actual: self.clone(), + }); + } + Ok(()) + } +} + +impl From for Version { + fn from(s: String) -> Self { + Self::new(s) + } +} + +impl FromStr for Version { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self::new(s.to_string())) + } +} + +impl Display for Version { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.0) + } +} diff --git a/ibc-eureka-core/ics23-commitment/types/Cargo.toml b/ibc-eureka-core/ics23-commitment/types/Cargo.toml new file mode 100644 index 0000000000..b627642fdb --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/Cargo.toml @@ -0,0 +1,81 @@ +[package] +name = "ibc-core-commitment-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "commitment", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-23 Vector Commitments data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true, features = [ "as_ref" ] } +displaydoc = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-proto = { workspace = true } +ibc-primitives = { workspace = true } +ics23 = { version = "0.12", default-features = false, features = [ "host-functions" ] } +ibc-core-host-types = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[dev-dependencies] +rstest = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "serde/std", + "subtle-encoding/std", + "ibc-primitives/std", + "ibc-core-host-types/std", + "ibc-proto/std", + "ics23/std", +] +serde = [ + "dep:serde", + "ibc-primitives/serde", + "ibc-core-host-types/serde", + "ibc-proto/serde", + "ics23/serde", +] +schema = [ + "dep:schemars", + "ibc-proto/json-schema", + "ibc-primitives/schema", + "ibc-core-host-types/schema", + "serde", + "std", +] +borsh = [ + "dep:borsh", + "ibc-proto/borsh", + "ibc-primitives/borsh", + "ibc-core-host-types/borsh", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-primitives/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics23-commitment/types/src/commitment.rs b/ibc-eureka-core/ics23-commitment/types/src/commitment.rs new file mode 100644 index 0000000000..af1f30153a --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/src/commitment.rs @@ -0,0 +1,182 @@ +//! Defines core commitment types + +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; +use ibc_proto::Protobuf; +use subtle_encoding::{Encoding, Hex}; + +use super::merkle::MerkleProof; + +/// Encodes a commitment root; most often a Merkle tree root hash. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +#[derive(Clone, PartialEq, Eq)] +pub struct CommitmentRoot { + #[cfg_attr( + feature = "serde", + serde(serialize_with = "crate::serializer::ser_hex_upper") + )] + bytes: Vec, +} + +impl fmt::Debug for CommitmentRoot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let hex = Hex::upper_case() + .encode_to_string(&self.bytes) + .map_err(|_| fmt::Error)?; + f.debug_tuple("CommitmentRoot").field(&hex).finish() + } +} + +impl CommitmentRoot { + pub fn from_bytes(bytes: &[u8]) -> Self { + Self { + bytes: Vec::from(bytes), + } + } + + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } + + pub fn into_vec(self) -> Vec { + self.bytes + } + + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } +} + +impl From> for CommitmentRoot { + fn from(bytes: Vec) -> Self { + Self { bytes } + } +} + +/// Demonstrates membership or non-membership for an element or set of elements, +/// verifiable in conjunction with a known commitment root. +/// +/// For example, in the case of a proof of membership in a Merkle tree, +/// this encodes a Merkle proof. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, PartialEq, Eq, derive_more::AsRef, derive_more::Into)] +#[as_ref(forward)] +pub struct CommitmentProofBytes { + #[cfg_attr( + feature = "serde", + serde(serialize_with = "crate::serializer::ser_hex_upper") + )] + bytes: Vec, +} + +impl fmt::Debug for CommitmentProofBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let hex = Hex::upper_case() + .encode_to_string(&self.bytes) + .map_err(|_| fmt::Error)?; + f.debug_tuple("CommitmentProof").field(&hex).finish() + } +} + +impl TryFrom> for CommitmentProofBytes { + type Error = DecodingError; + + fn try_from(bytes: Vec) -> Result { + if bytes.is_empty() { + Err(DecodingError::missing_raw_data("commitment proof bytes"))? + } else { + Ok(Self { bytes }) + } + } +} + +impl TryFrom for CommitmentProofBytes { + type Error = DecodingError; + + fn try_from(proof: RawMerkleProof) -> Result { + proof.to_vec().try_into() + } +} + +impl TryFrom for CommitmentProofBytes { + type Error = DecodingError; + + fn try_from(value: MerkleProof) -> Result { + Self::try_from(RawMerkleProof::from(value)) + } +} + +impl<'a> TryFrom<&'a CommitmentProofBytes> for MerkleProof { + type Error = DecodingError; + + fn try_from(value: &'a CommitmentProofBytes) -> Result { + Ok(Protobuf::::decode(value.as_ref())?) + } +} + +/// Defines a store prefix of the commitment proof. +/// +/// See [spec](https://github.com/cosmos/ibc/blob/main/spec/core/ics-023-vector-commitments/README.md#prefix). +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, PartialEq, Eq, Hash, derive_more::From)] +pub struct CommitmentPrefix { + bytes: Vec, +} + +impl CommitmentPrefix { + pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + Self { + bytes: bytes.as_ref().to_vec(), + } + } + + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } + + pub fn into_vec(self) -> Vec { + self.bytes + } + + pub fn empty() -> Self { + Self { bytes: Vec::new() } + } + + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } +} + +impl fmt::Debug for CommitmentPrefix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let converted = core::str::from_utf8(self.as_bytes()); + match converted { + Ok(s) => write!(f, "{s}"), + Err(_e) => write!(f, "", self.as_bytes()), + } + } +} diff --git a/ibc-eureka-core/ics23-commitment/types/src/error.rs b/ibc-eureka-core/ics23-commitment/types/src/error.rs new file mode 100644 index 0000000000..3554767ebe --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/src/error.rs @@ -0,0 +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 { + /// 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, + /// 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 { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Decoding(e) => Some(e), + _ => None, + } + } +} diff --git a/ibc-eureka-core/ics23-commitment/types/src/lib.rs b/ibc-eureka-core/ics23-commitment/types/src/lib.rs new file mode 100644 index 0000000000..a4cb15890a --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/src/lib.rs @@ -0,0 +1,31 @@ +//! ICS-23: Commitment implementation of a cryptographic scheme that verifies +//! state transitions between chains. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod commitment; +pub mod error; +pub mod merkle; +pub mod specs; + +#[cfg(feature = "serde")] +pub mod serializer; + +/// Re-exports ICS-23 proto types from the `ibc-proto` crate, which are +/// used in the implementation of dependent IBC crates. +pub mod proto { + pub use ibc_proto::ibc::core::commitment::*; + pub use ibc_proto::ics23; +} diff --git a/ibc-eureka-core/ics23-commitment/types/src/merkle.rs b/ibc-eureka-core/ics23-commitment/types/src/merkle.rs new file mode 100644 index 0000000000..d2bcea088a --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/src/merkle.rs @@ -0,0 +1,228 @@ +//! 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; +use ibc_proto::ibc::core::commitment::v1::{ + MerklePath as RawMerklePath, MerkleProof as RawMerkleProof, MerkleRoot, +}; +use ibc_proto::ics23::commitment_proof::Proof; +use ibc_proto::ics23::{ + calculate_existence_root, verify_membership, verify_non_membership, CommitmentProof, + HostFunctionsProvider, NonExistenceProof, +}; + +use crate::commitment::CommitmentRoot; +use crate::error::CommitmentError; +use crate::specs::ProofSpecs; + +/// A wrapper type representing a Merkle path, consisting of a sequence of path +/// bytes. +/// +/// This struct by definition is compatible with Cosmos SDK chains, but it is +/// also applicable to other blockchain implementations that follow similar path +/// structures. Note that while Cosmos SDK chains and some non-Cosmos chains +/// adhere to this definition, it may not be universally applicable to all +/// non-Cosmos chains. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq)] +pub struct MerklePath { + pub key_path: Vec, +} + +impl MerklePath { + /// Constructs a new `MerklePath` from a given `Vec`. + pub fn new(key_path: Vec) -> Self { + Self { key_path } + } +} + +impl From for MerklePath { + fn from(path: RawMerklePath) -> Self { + Self { + key_path: path + .key_path + .into_iter() + .map(|p| p.into_bytes().into()) + .collect(), + } + } +} + +// The conversion from `MerklePath` to `RawMerklePath` is not provided, as we +// cannot assume how the key paths of `Vec` type should be serialized +// to the `Vec`. + +impl From for MerkleRoot { + fn from(root: CommitmentRoot) -> Self { + Self { + hash: root.into_vec(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct MerkleProof { + pub proofs: Vec, +} + +impl Protobuf for MerkleProof {} + +impl TryFrom for MerkleProof { + type Error = DecodingError; + + fn try_from(proof: RawMerkleProof) -> Result { + Ok(Self { + proofs: proof.proofs, + }) + } +} + +impl From for RawMerkleProof { + fn from(proof: MerkleProof) -> Self { + Self { + proofs: proof.proofs, + } + } +} + +impl MerkleProof { + pub fn verify_membership( + &self, + specs: &ProofSpecs, + root: MerkleRoot, + keys: MerklePath, + value: Vec, + start_index: u64, + ) -> Result<(), CommitmentError> { + // validate arguments + if self.proofs.is_empty() { + return Err(CommitmentError::MissingMerkleProof); + } + if root.hash.is_empty() { + return Err(CommitmentError::MissingMerkleRoot); + } + let num = self.proofs.len(); + let ics23_specs = Vec::::from(specs.clone()); + if ics23_specs.len() != num { + return Err(CommitmentError::MismatchedNumberOfProofs { + expected: ics23_specs.len(), + actual: num, + }); + } + if keys.key_path.len() != num { + return Err(CommitmentError::MismatchedNumberOfProofs { + expected: keys.key_path.len(), + actual: num, + }); + } + if value.is_empty() { + return Err(CommitmentError::MissingVerifiedValue); + } + + let mut subroot = value.clone(); + let mut value = value; + // keys are represented from root-to-leaf + for ((proof, spec), key) in self + .proofs + .iter() + .zip(ics23_specs.iter()) + .zip(keys.key_path.iter().rev()) + .skip( + start_index + .try_into() + .expect("safe because if u64 is more than usize it will skip all anyway"), + ) + { + match &proof.proof { + Some(Proof::Exist(existence_proof)) => { + subroot = calculate_existence_root::(existence_proof) + .map_err(|_| CommitmentError::InvalidMerkleProof)?; + + if !verify_membership::(proof, spec, &subroot, key.as_ref(), &value) { + return Err(CommitmentError::FailedToVerifyMembership); + } + value.clone_from(&subroot); + } + _ => return Err(CommitmentError::InvalidMerkleProof), + } + } + + if root.hash != subroot { + return Err(CommitmentError::FailedToVerifyMembership); + } + + Ok(()) + } + + pub fn verify_non_membership( + &self, + specs: &ProofSpecs, + root: MerkleRoot, + keys: MerklePath, + ) -> Result<(), CommitmentError> { + // validate arguments + if self.proofs.is_empty() { + return Err(CommitmentError::MissingMerkleProof); + } + if root.hash.is_empty() { + return Err(CommitmentError::MissingMerkleRoot); + } + let num = self.proofs.len(); + let ics23_specs = Vec::::from(specs.clone()); + if ics23_specs.len() != num { + return Err(CommitmentError::MismatchedNumberOfProofs { + actual: num, + expected: ics23_specs.len(), + }); + } + if keys.key_path.len() != num { + return Err(CommitmentError::MismatchedNumberOfProofs { + actual: num, + expected: keys.key_path.len(), + }); + } + + // verify the absence of key in lowest subtree + let proof = self + .proofs + .first() + .ok_or(CommitmentError::InvalidMerkleProof)?; + let spec = ics23_specs + .first() + .ok_or(CommitmentError::InvalidMerkleProof)?; + // keys are represented from root-to-leaf + let key = keys + .key_path + .get(num - 1) + .ok_or(CommitmentError::InvalidMerkleProof)?; + match &proof.proof { + Some(Proof::Nonexist(non_existence_proof)) => { + let subroot = calculate_non_existence_root::(non_existence_proof)?; + + if !verify_non_membership::(proof, spec, &subroot, key.as_ref()) { + return Err(CommitmentError::FailedToVerifyMembership); + } + + // verify membership proofs starting from index 1 with value = subroot + self.verify_membership::(specs, root, keys, subroot, 1) + } + _ => Err(CommitmentError::InvalidMerkleProof), + } + } +} + +// TODO move to ics23 +fn calculate_non_existence_root( + proof: &NonExistenceProof, +) -> Result, CommitmentError> { + if let Some(left) = &proof.left { + calculate_existence_root::(left).map_err(|_| CommitmentError::InvalidMerkleProof) + } else if let Some(right) = &proof.right { + calculate_existence_root::(right).map_err(|_| CommitmentError::InvalidMerkleProof) + } else { + Err(CommitmentError::InvalidMerkleProof) + } +} diff --git a/ibc-eureka-core/ics23-commitment/types/src/serializer.rs b/ibc-eureka-core/ics23-commitment/types/src/serializer.rs new file mode 100644 index 0000000000..b3950b2183 --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/src/serializer.rs @@ -0,0 +1,14 @@ +use ibc_primitives::prelude::*; +use serde::ser::{Serialize, Serializer}; +use subtle_encoding::{Encoding, Hex}; + +pub fn ser_hex_upper(data: T, serializer: S) -> Result +where + S: Serializer, + T: AsRef<[u8]>, +{ + let hex = Hex::upper_case() + .encode_to_string(data) + .map_err(|e| serde::ser::Error::custom(format!("failed to serialize hex: {}", e)))?; + hex.serialize(serializer) +} diff --git a/ibc-eureka-core/ics23-commitment/types/src/specs.rs b/ibc-eureka-core/ics23-commitment/types/src/specs.rs new file mode 100644 index 0000000000..5d0f0f473f --- /dev/null +++ b/ibc-eureka-core/ics23-commitment/types/src/specs.rs @@ -0,0 +1,289 @@ +//! 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}; + +use crate::error::CommitmentError; +/// An array of proof specifications. +/// +/// This type encapsulates different types of proof specifications, mostly predefined, e.g., for +/// Cosmos-SDK. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct ProofSpecs(Vec); + +impl ProofSpecs { + /// Returns the specification for Cosmos-SDK proofs + pub fn cosmos() -> Self { + vec![ + ics23::iavl_spec(), // Format of proofs-iavl (iavl merkle proofs) + ics23::tendermint_spec(), // Format of proofs-tendermint (crypto/ merkle SimpleProof) + ] + .try_into() + .expect("should convert successfully") + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn validate(&self) -> Result<(), CommitmentError> { + if self.is_empty() { + 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. + // 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, + // `max_depth` must be greater than or equal to `min_depth` to ensure a valid range. + if proof_spec.0.max_depth < 0 + || proof_spec.0.min_depth < 0 + || (0 < proof_spec.0.min_depth + && 0 < proof_spec.0.max_depth + && proof_spec.0.max_depth < proof_spec.0.min_depth) + { + return Err(CommitmentError::InvalidRange { + min: proof_spec.0.min_depth, + max: proof_spec.0.max_depth, + }); + } + } + Ok(()) + } +} + +impl TryFrom> for ProofSpecs { + type Error = DecodingError; + + fn try_from(ics23_specs: Vec) -> Result { + // no proof specs provided + if ics23_specs.is_empty() { + return Err(DecodingError::missing_raw_data("proof specs")); + } + + ics23_specs + .into_iter() + .map(ProofSpec::try_from) + .collect::>() + .map(Self) + } +} + +impl From for Vec { + fn from(specs: ProofSpecs) -> Self { + specs.0.into_iter().map(Into::into).collect() + } +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +struct ProofSpec(RawProofSpec); + +impl TryFrom for ProofSpec { + 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, + // `max_depth` must be greater than or equal to `min_depth` to ensure a valid range. + if spec.max_depth < 0 + || spec.min_depth < 0 + || (0 < spec.min_depth && 0 < spec.max_depth && spec.max_depth < spec.min_depth) + { + return Err(DecodingError::invalid_raw_data(format!( + "proof spec range [`{}`, `{}`]", + spec.min_depth, spec.max_depth + ))); + } + + let leaf_spec = spec + .leaf_spec + .map(LeafOp::try_from) + .transpose()? + .map(|lop| lop.0); + let inner_spec = spec + .inner_spec + .map(InnerSpec::try_from) + .transpose()? + .map(|ispec| ispec.0); + + Ok(Self(RawProofSpec { + leaf_spec, + inner_spec, + max_depth: spec.max_depth, + min_depth: spec.min_depth, + prehash_key_before_comparison: spec.prehash_key_before_comparison, + })) + } +} + +impl From for RawProofSpec { + fn from(spec: ProofSpec) -> Self { + spec.0 + } +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +struct LeafOp(RawLeafOp); + +impl TryFrom for LeafOp { + type Error = DecodingError; + + fn try_from(leaf_op: RawLeafOp) -> Result { + let _ = HashOp::try_from(leaf_op.hash) + .map_err(|_| DecodingError::invalid_raw_data("leaf op hash"))?; + let _ = HashOp::try_from(leaf_op.prehash_key) + .map_err(|_| DecodingError::invalid_raw_data("leaf op prehash key"))?; + let _ = HashOp::try_from(leaf_op.prehash_value) + .map_err(|_| DecodingError::invalid_raw_data("leaf op prehash value"))?; + let _ = LengthOp::try_from(leaf_op.length) + .map_err(|_| DecodingError::invalid_raw_data("leaf op length"))?; + + Ok(Self(leaf_op)) + } +} + +impl From for RawLeafOp { + fn from(leaf_op: LeafOp) -> Self { + leaf_op.0 + } +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +struct InnerSpec(RawInnerSpec); + +impl TryFrom for InnerSpec { + type Error = DecodingError; + + fn try_from(inner_spec: RawInnerSpec) -> Result { + if inner_spec.child_size <= 0 { + return Err(DecodingError::invalid_raw_data("inner spec child size")); + } + + // Negative prefix lengths are not allowed and the maximum prefix length must + // be greater than or equal to the minimum prefix length. + if inner_spec.min_prefix_length < 0 + || inner_spec.max_prefix_length < 0 + || inner_spec.max_prefix_length < inner_spec.min_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 { + child_order: inner_spec.child_order, + child_size: inner_spec.child_size, + min_prefix_length: inner_spec.min_prefix_length, + max_prefix_length: inner_spec.max_prefix_length, + empty_child: inner_spec.empty_child, + hash: inner_spec.hash, + })) + } +} + +impl From for RawInnerSpec { + fn from(inner_spec: InnerSpec) -> Self { + inner_spec.0 + } +} + +#[cfg(test)] +mod tests { + use ibc_proto::ics23::{InnerSpec as RawInnerSpec, ProofSpec as RawProofSpec}; + use rstest::rstest; + + use super::*; + + #[rstest] + #[case(0, 0)] + #[case(2, 2)] + #[case(5, 6)] + #[should_panic(expected = "InvalidRawData")] + #[case(-3,3)] + #[should_panic(expected = "InvalidRawData")] + #[case(2,-6)] + #[should_panic(expected = "InvalidRawData")] + #[case(-2,-6)] + #[should_panic(expected = "InvalidRawData")] + #[case(-6,-2)] + #[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 { + leaf_spec: None, + inner_spec: None, + max_depth, + min_depth, + prehash_key_before_comparison: false, + }; + ProofSpec::try_from(raw_proof_spec).unwrap(); + } + + #[rstest] + #[case(0, 0)] + #[case(1, 2)] + #[case(2, 2)] + #[should_panic(expected = "InvalidRawData")] + #[case(2, 1)] + #[should_panic(expected = "InvalidRawData")] + #[case(-2,1)] + #[should_panic(expected = "InvalidRawData")] + #[case(2,-1)] + #[should_panic(expected = "InvalidRawData")] + #[case(-2,-1)] + #[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 { + child_order: vec![1], + child_size: 2, + min_prefix_length, + max_prefix_length, + empty_child: vec![], + hash: 1, + }; + InnerSpec::try_from(raw_inner_spec).unwrap(); + } + + #[rstest] + #[case(0, 0, 0, 0)] + #[case(9, 9, 9, 8)] + #[should_panic(expected = "leaf op hash")] + #[case(-1, 4, 4, 4)] + #[should_panic(expected = "leaf op hash")] + #[case(10, 4, 4, 4)] + #[should_panic(expected = "leaf op prehash key")] + #[case(4, -1, 4, 4)] + #[should_panic(expected = "leaf op prehash key")] + #[case(4, 10, 4, 4)] + #[should_panic(expected = "leaf op prehash value")] + #[case(4, 4, -1, 4)] + #[should_panic(expected = "leaf op prehash value")] + #[case(4, 4, 10, 4)] + #[should_panic(expected = "leaf op length")] + #[case(4, 4, 4, -1)] + #[should_panic(expected = "leaf op length")] + #[case(4, 4, 4, 9)] + fn test_leaf_op_try_from( + #[case] hash: i32, + #[case] prehash_key: i32, + #[case] prehash_value: i32, + #[case] length: i32, + ) { + let raw_leaf_op = RawLeafOp { + hash, + prehash_key, + prehash_value, + length, + prefix: vec![], + }; + LeafOp::try_from(raw_leaf_op).unwrap(); + } +} diff --git a/ibc-eureka-core/ics24-host/Cargo.toml b/ibc-eureka-core/ics24-host/Cargo.toml new file mode 100644 index 0000000000..c97fb73244 --- /dev/null +++ b/ibc-eureka-core/ics24-host/Cargo.toml @@ -0,0 +1,94 @@ +[package] +name = "ibc-core-host" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "host" ] +readme = "./../README.md" + +description = """ + Maintained by `ibc-rs`, contains essential top-level traits designed for the seamless integration + of host chains, facilitating access to the host's storage, the efficient retrieval of states and + metadata crucial for the execution of any IBC datagrams. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +derive_more = { workspace = true } +displaydoc = { workspace = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-client-types = { workspace = true } +ibc-core-client-context = { workspace = true } +ibc-core-connection-types = { workspace = true } +ibc-core-channel-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } + +[dev-dependencies] +rstest = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "subtle-encoding/std", + "ibc-core-client-types/std", + "ibc-core-client-context/std", + "ibc-core-connection-types/std", + "ibc-core-channel-types/std", + "ibc-core-commitment-types/std", + "ibc-core-host-types/std", + "ibc-core-handler-types/std", + "ibc-primitives/std", +] +serde = [ + "ibc-core-client-types/serde", + "ibc-core-client-context/serde", + "ibc-core-connection-types/serde", + "ibc-core-channel-types/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host-types/serde", + "ibc-core-handler-types/serde", + "ibc-primitives/serde", +] +schema = [ + "ibc-core-client-types/schema", + "ibc-core-client-context/schema", + "ibc-core-connection-types/schema", + "ibc-core-channel-types/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host-types/schema", + "ibc-core-handler-types/schema", + "ibc-primitives/schema", + "serde", + "std", +] +borsh = [ + "ibc-core-client-types/borsh", + "ibc-core-client-context/borsh", + "ibc-core-connection-types/borsh", + "ibc-core-channel-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host-types/borsh", + "ibc-core-handler-types/borsh", + "ibc-primitives/borsh", +] +parity-scale-codec = [ + "ibc-core-client-types/parity-scale-codec", + "ibc-core-connection-types/parity-scale-codec", + "ibc-core-channel-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics24-host/cosmos/Cargo.toml b/ibc-eureka-core/ics24-host/cosmos/Cargo.toml new file mode 100644 index 0000000000..50e06f273b --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/Cargo.toml @@ -0,0 +1,112 @@ +[package] +name = "ibc-core-host-cosmos" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "tendermint" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, contains Cosmos-specific helper traits and implementations + to facilitate IBC integration, ensuring proper interaction with modules/components + beyond the IBC modules on host chains. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +serde = { workspace = true, optional = true } +sha2 = { workspace = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-client-tendermint = { workspace = true } +ibc-app-transfer-types = { workspace = true } +ibc-core-client-types = { workspace = true } +ibc-core-client-context = { workspace = true } +ibc-core-connection-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "serde/std", + "sha2/std", + "subtle-encoding/std", + "ibc-core-client-types/std", + "ibc-core-client-context/std", + "ibc-core-connection-types/std", + "ibc-core-commitment-types/std", + "ibc-core-host-types/std", + "ibc-core-handler-types/std", + "ibc-primitives/std", + "ibc-proto/std", +] +serde = [ + "dep:serde", + "ibc-client-tendermint/serde", + "ibc-core-client-types/serde", + "ibc-core-client-context/serde", + "ibc-core-connection-types/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host-types/serde", + "ibc-core-handler-types/serde", + "ibc-primitives/serde", + "ibc-proto/serde", +] +schema = [ + "ibc-client-tendermint/schema", + "ibc-core-client-types/schema", + "ibc-core-client-context/schema", + "ibc-core-connection-types/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host-types/schema", + "ibc-core-handler-types/schema", + "ibc-primitives/schema", + "ibc-proto/json-schema", + "serde", + "std", +] +borsh = [ + "dep:borsh", + "ibc-client-tendermint/borsh", + "ibc-core-client-types/borsh", + "ibc-core-client-context/borsh", + "ibc-core-connection-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host-types/borsh", + "ibc-primitives/borsh", + "ibc-proto/borsh", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-client-tendermint/parity-scale-codec", + "ibc-core-client-types/parity-scale-codec", + "ibc-core-connection-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics24-host/cosmos/src/lib.rs b/ibc-eureka-core/ics24-host/cosmos/src/lib.rs new file mode 100644 index 0000000000..7225534a98 --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/lib.rs @@ -0,0 +1,30 @@ +//! Provides convenience traits and implementations for Tendermint-based hosts +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] +pub mod upgrade_proposal; + +pub mod utils; + +mod validate_self_client; +pub use validate_self_client::ValidateSelfClientContext; + +/// Re-exports necessary proto types for implementing the tendermint client +/// upgradeability feature. +pub mod proto { + pub use ibc_proto::cosmos::upgrade::*; +} + +/// ABCI store/query path for the IBC sub-store +pub const IBC_QUERY_PATH: &str = "store/ibc/key"; + +/// ABCI store/query path for the upgrade sub-store +pub const SDK_UPGRADE_QUERY_PATH: &str = "store/upgrade/key"; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs new file mode 100644 index 0000000000..89ec14ff3a --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs @@ -0,0 +1,63 @@ +//! Helper Context for handling upgrade client proposals. +//! +//! Currently. this interface has been defined to support Tendermint-based +//! chains. You can check out a sample implementation in the +//! [Basecoin-rs](https://github.com/informalsystems/basecoin-rs) repository. +//! If it proves to be generic enough, we may move it to the ICS02 section. + +use ibc_core_client_context::ClientValidationContext; +use ibc_core_host_types::error::HostError; +use ibc_core_host_types::path::{UpgradeClientStatePath, UpgradeConsensusStatePath}; + +use super::Plan; + +pub type UpgradedClientStateRef = + <::V as ClientValidationContext>::ClientStateRef; + +pub type UpgradedConsensusStateRef = + <::V as ClientValidationContext>::ConsensusStateRef; + +/// Helper context to validate client upgrades, providing methods to retrieve +/// an upgrade plan and related upgraded client and consensus states. +pub trait UpgradeValidationContext { + type V: ClientValidationContext; + + /// Returns the upgrade plan that is scheduled and has not been executed yet. + fn upgrade_plan(&self) -> Result; + + /// Returns the upgraded client state at the specified upgrade path. + fn upgraded_client_state( + &self, + upgrade_path: &UpgradeClientStatePath, + ) -> Result, HostError>; + + /// Returns the upgraded consensus state at the specified upgrade path. + fn upgraded_consensus_state( + &self, + upgrade_path: &UpgradeConsensusStatePath, + ) -> 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<(), HostError>; + + /// Clears the upgrade plan at the specified height. + 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<(), 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<(), HostError>; +} diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/events.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/events.rs new file mode 100644 index 0000000000..b4e3550c98 --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/events.rs @@ -0,0 +1,174 @@ +//! Definitions of events emitted when an upgrade client is proposed or executed. + +use derive_more::From; +use ibc_primitives::prelude::*; +use tendermint::abci; + +const UPGRADE_CHAIN_EVENT: &str = "upgrade_chain"; +const UPGRADE_CLIENT_PROPOSAL_EVENT: &str = "upgrade_client_proposal"; + +const KEY_UPGRADE_STORE_ATTRIBUTE_KEY: &str = "upgrade_store"; +const UPGRADE_PLAN_HEIGHT_ATTRIBUTE_KEY: &str = "upgrade_plan_height"; +const UPGRADE_PLAN_TITLE_ATTRIBUTE_KEY: &str = "title"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct UpgradeStoreAttribute { + upgrade_store: String, +} + +impl From for abci::EventAttribute { + fn from(attr: UpgradeStoreAttribute) -> Self { + (KEY_UPGRADE_STORE_ATTRIBUTE_KEY, attr.upgrade_store).into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct UpgradePlanHeightAttribute { + plan_height: u64, +} + +impl From for abci::EventAttribute { + fn from(attr: UpgradePlanHeightAttribute) -> Self { + ( + UPGRADE_PLAN_HEIGHT_ATTRIBUTE_KEY, + attr.plan_height.to_string(), + ) + .into() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, From, PartialEq, Eq)] +struct UpgradePlanTitleAttribute { + title: String, +} + +impl From for abci::EventAttribute { + fn from(attr: UpgradePlanTitleAttribute) -> Self { + (UPGRADE_PLAN_TITLE_ATTRIBUTE_KEY, attr.title).into() + } +} + +/// Event type emitted by the host chain when an upgrade plan is executed. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct UpgradeChain { + // The height at which the upgrade performed. + plan_height: UpgradePlanHeightAttribute, + // The key of the store where the upgrade plan is stored. + upgrade_store: UpgradeStoreAttribute, +} + +impl UpgradeChain { + pub fn new(plan_height: u64, upgrade_store: String) -> Self { + Self { + plan_height: UpgradePlanHeightAttribute::from(plan_height), + upgrade_store: UpgradeStoreAttribute::from(upgrade_store), + } + } + pub fn event_type(&self) -> &str { + UPGRADE_CHAIN_EVENT + } +} + +impl From for abci::Event { + fn from(u: UpgradeChain) -> Self { + Self { + kind: UPGRADE_CHAIN_EVENT.to_owned(), + attributes: vec![u.plan_height.into(), u.upgrade_store.into()], + } + } +} + +/// Event type emitted by the host chain when an upgrade plan is proposed. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct UpgradeClientProposal { + // The title of the upgrade plan + plan_title: UpgradePlanTitleAttribute, + // The height at which the upgrade must be performed. + plan_height: UpgradePlanHeightAttribute, +} + +impl UpgradeClientProposal { + pub fn new(plan_title: String, plan_height: u64) -> Self { + Self { + plan_title: UpgradePlanTitleAttribute::from(plan_title), + plan_height: UpgradePlanHeightAttribute::from(plan_height), + } + } + pub fn event_type(&self) -> &str { + UPGRADE_CLIENT_PROPOSAL_EVENT + } +} + +impl From for abci::Event { + fn from(u: UpgradeClientProposal) -> Self { + Self { + kind: UPGRADE_CLIENT_PROPOSAL_EVENT.to_owned(), + attributes: vec![u.plan_title.into(), u.plan_height.into()], + } + } +} diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs new file mode 100644 index 0000000000..419a1e46ce --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs @@ -0,0 +1,47 @@ +use ibc_client_tendermint::types::ClientState as TmClientState; +use ibc_core_client_types::error::UpgradeClientError; +use ibc_core_host_types::path::UpgradeClientStatePath; +use ibc_primitives::prelude::*; +use tendermint::abci::Event as TmEvent; + +use super::UpgradedClientStateRef; +use crate::upgrade_proposal::{UpgradeClientProposal, UpgradeExecutionContext, UpgradeProposal}; + +/// Executes an upgrade client proposal. +/// +/// It clears both IBC client and consensus states if a previous plan was set. +/// Then it will schedule an upgrade and finally set the upgraded client state +/// in the upgrade store. +pub fn execute_upgrade_client_proposal( + ctx: &mut Ctx, + proposal: UpgradeProposal, +) -> Result +where + Ctx: UpgradeExecutionContext, + UpgradedClientStateRef: From, +{ + let plan = proposal.plan; + + if ctx.upgrade_plan().is_ok() { + ctx.clear_upgrade_plan(plan.height)?; + } + + let mut client_state = + TmClientState::try_from(proposal.upgraded_client_state).map_err(|e| { + UpgradeClientError::InvalidUpgradeProposal { + description: e.to_string(), + } + })?; + + client_state.zero_custom_fields(); + + ctx.schedule_upgrade(plan.clone())?; + + let upgraded_client_state_path = UpgradeClientStatePath::new_with_default_path(plan.height); + + ctx.store_upgraded_client_state(upgraded_client_state_path, client_state.into())?; + + let event = TmEvent::from(UpgradeClientProposal::new(proposal.title, plan.height)); + + Ok(event) +} diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/mod.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/mod.rs new file mode 100644 index 0000000000..dbeb530740 --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/mod.rs @@ -0,0 +1,13 @@ +//! Provides utilities related to chain upgrades. + +mod context; +mod events; +mod handler; +mod plan; +mod proposal; + +pub use context::*; +pub use events::{UpgradeChain, UpgradeClientProposal}; +pub use handler::execute_upgrade_client_proposal; +pub use plan::Plan; +pub use proposal::*; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs new file mode 100644 index 0000000000..6541ad049a --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs @@ -0,0 +1,97 @@ +//! Definition of domain `Plan` type. + +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; +use ibc_proto::Protobuf; + +pub const TYPE_URL: &str = "/cosmos.upgrade.v1beta1.Plan"; + +/// Specifies information about a planned upgrade and at which height it should +/// be performed. +/// +/// Note: Time based upgrade logic has been removed from the SDK, so the `time` +/// field of the proto is deprecated and should be empty. +#[derive(Clone, Debug)] +pub struct Plan { + // Sets the name for the upgrade. This name might be used by the upgraded + // version of a host chain to apply any special "on-upgrade" commands during + // the first block generation after the upgrade is applied. + pub name: String, + // The height at which the upgrade must be performed. + pub height: u64, + // Any application specific upgrade info to be included on-chain + pub info: String, +} + +impl Protobuf for Plan {} + +impl TryFrom for Plan { + type Error = DecodingError; + + fn try_from(raw: RawPlan) -> Result { + if raw.name.is_empty() { + return Err(DecodingError::missing_raw_data("upgrade plan name")); + } + + #[allow(deprecated)] + if raw.time.is_some() { + return Err(DecodingError::invalid_raw_data( + "upgrade plan time must be empty", + )); + } + + #[allow(deprecated)] + if raw.upgraded_client_state.is_some() { + 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)?, + info: raw.info, + }) + } +} + +impl From for RawPlan { + fn from(value: Plan) -> Self { + #[allow(deprecated)] + Self { + name: value.name, + time: None, + height: i64::try_from(value.height).expect("height overflow"), + info: value.info, + upgraded_client_state: None, + } + } +} + +impl Protobuf for Plan {} + +impl TryFrom for Plan { + type Error = DecodingError; + + fn try_from(any: Any) -> Result { + 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, + }) + } + } +} + +impl From for Any { + fn from(value: Plan) -> Self { + Any { + type_url: TYPE_URL.to_string(), + value: Protobuf::::encode_vec(value), + } + } +} diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs new file mode 100644 index 0000000000..a98441ce7d --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs @@ -0,0 +1,72 @@ +//! Definition of domain `UpgradeProposal` type for handling upgrade client proposal + +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; +use ibc_proto::Protobuf; + +use super::Plan; + +pub const UPGRADE_PROPOSAL_TYPE_URL: &str = "/ibc.core.client.v1.UpgradeProposal"; + +/// Defines a governance proposal of type `Content` that enables the initiation +/// of an IBC breaking upgrade and specifies the new client state that should be +/// utilized following the upgrade. +#[derive(Clone, Debug)] +pub struct UpgradeProposal { + // Title of the proposal + pub title: String, + // Description of the proposal + pub description: String, + // The upgrade plan + pub plan: Plan, + // The upgraded client state + pub upgraded_client_state: Any, +} + +impl Protobuf for UpgradeProposal {} + +impl TryFrom for UpgradeProposal { + type Error = DecodingError; + + fn try_from(raw: RawUpgradeProposal) -> Result { + if raw.title.is_empty() { + return Err(DecodingError::missing_raw_data("upgrade proposal title")); + } + + if raw.description.is_empty() { + return Err(DecodingError::missing_raw_data( + "upgrade proposal description", + )); + } + + let plan = if let Some(plan) = raw.plan { + plan.try_into()? + } else { + return Err(DecodingError::missing_raw_data("upgrade proposal plan")); + }; + + let upgraded_client_state = raw.upgraded_client_state.ok_or_else(|| { + DecodingError::missing_raw_data("upgrade proposal upgraded client state") + })?; + + Ok(Self { + title: raw.title, + description: raw.description, + plan, + upgraded_client_state, + }) + } +} + +impl From for RawUpgradeProposal { + fn from(value: UpgradeProposal) -> Self { + Self { + title: value.title, + description: value.description, + plan: Some(value.plan.into()), + upgraded_client_state: Some(value.upgraded_client_state), + } + } +} diff --git a/ibc-eureka-core/ics24-host/cosmos/src/utils.rs b/ibc-eureka-core/ics24-host/cosmos/src/utils.rs new file mode 100644 index 0000000000..613a48f362 --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/utils.rs @@ -0,0 +1,57 @@ +use ibc_app_transfer_types::VERSION; +use ibc_core_host_types::identifiers::{ChannelId, PortId}; +use ibc_primitives::prelude::*; +use sha2::{Digest, Sha256}; + +/// Helper function to generate an escrow address for a given port and channel +/// ids according to the format specified in the Cosmos SDK +/// [`ADR-028`](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-028-public-key-addresses.md) +pub fn cosmos_adr028_escrow_address(port_id: &PortId, channel_id: &ChannelId) -> Vec { + let contents = format!("{port_id}/{channel_id}"); + + let mut hasher = Sha256::new(); + hasher.update(VERSION.as_bytes()); + hasher.update([0]); + hasher.update(contents.as_bytes()); + + let mut hash = hasher.finalize().to_vec(); + hash.truncate(20); + hash +} + +#[cfg(test)] +mod tests { + use subtle_encoding::bech32; + + use super::*; + + #[test] + fn test_cosmos_escrow_address() { + fn assert_eq_escrow_address(port_id: &str, channel_id: &str, address: &str) { + let port_id = port_id.parse().unwrap(); + let channel_id = channel_id.parse().unwrap(); + let gen_address = { + let addr = cosmos_adr028_escrow_address(&port_id, &channel_id); + bech32::encode("cosmos", addr) + }; + assert_eq!(gen_address, address.to_owned()) + } + + // addresses obtained using `gaiad query ibc-transfer escrow-address [port-id] [channel-id]` + assert_eq_escrow_address( + "transfer", + "channel-141", + "cosmos1x54ltnyg88k0ejmk8ytwrhd3ltm84xehrnlslf", + ); + assert_eq_escrow_address( + "transfer", + "channel-207", + "cosmos1ju6tlfclulxumtt2kglvnxduj5d93a64r5czge", + ); + assert_eq_escrow_address( + "transfer", + "channel-187", + "cosmos177x69sver58mcfs74x6dg0tv6ls4s3xmmcaw53", + ); + } +} diff --git a/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs b/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs new file mode 100644 index 0000000000..96b13306cd --- /dev/null +++ b/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs @@ -0,0 +1,124 @@ +use core::time::Duration; + +use ibc_client_tendermint::types::ClientState as TmClientState; +use ibc_core_client_types::Height; +use ibc_core_commitment_types::specs::ProofSpecs; +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; + +/// Provides a default implementation intended for implementing the +/// `ValidationContext::validate_self_client` API. +/// +/// This validation logic tailored for Tendermint client states of a host chain +/// operating across various counterparty chains. +pub trait ValidateSelfClientContext { + fn validate_self_tendermint_client( + &self, + client_state_of_host_on_counterparty: TmClientState, + ) -> Result<(), HostError> { + client_state_of_host_on_counterparty + .validate() + .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(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(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(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(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(HostError::invalid_state(format!( + "client proof specs; expected `{:?}`, actual `{:?}`", + self.proof_specs(), + client_state_of_host_on_counterparty.proof_specs + ))); + } + + let _ = { + let trust_level = client_state_of_host_on_counterparty.trust_level; + + TendermintTrustThresholdFraction::new( + trust_level.numerator(), + trust_level.denominator(), + ) + .map_err(HostError::invalid_state)? + }; + + if 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(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(HostError::invalid_state(format!( + "upgrade path; expected `{:?}`, actual `{:?}`", + self.upgrade_path(), + client_state_of_host_on_counterparty.upgrade_path + ))); + } + + Ok(()) + } + + /// Returns the chain id of the host + fn chain_id(&self) -> &ChainId; + + /// Returns the host current height + fn host_current_height(&self) -> Height; + + /// Returns the proof specs of the host + fn proof_specs(&self) -> &ProofSpecs; + + /// Returns the unbonding period of the host + fn unbonding_period(&self) -> Duration; + + /// Returns the host upgrade path. May be empty. + fn upgrade_path(&self) -> &[String]; +} diff --git a/ibc-eureka-core/ics24-host/src/context.rs b/ibc-eureka-core/ics24-host/src/context.rs new file mode 100644 index 0000000000..a9a84ab53a --- /dev/null +++ b/ibc-eureka-core/ics24-host/src/context.rs @@ -0,0 +1,258 @@ +use core::time::Duration; + +use ibc_core_channel_types::channel::ChannelEnd; +use ibc_core_channel_types::commitment::{AcknowledgementCommitment, PacketCommitment}; +use ibc_core_channel_types::packet::Receipt; +use ibc_core_client_context::prelude::*; +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::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, + SeqAckPath, SeqRecvPath, SeqSendPath, +}; +use ibc_primitives::prelude::*; +use ibc_primitives::{Signer, Timestamp}; + +use crate::utils::calculate_block_delay; + +/// Context to be implemented by the host that provides all "read-only" methods. +/// +/// Trait used for the top-level `validate` entrypoint in the `ibc-core` crate. +pub trait ValidationContext { + type V: ClientValidationContext; + /// The client state type for the host chain. + type HostClientState: ClientStateValidation; + /// The consensus state type for the host chain. + type HostConsensusState: ConsensusState; + + /// Retrieve the context that implements all clients' `ValidationContext`. + fn get_client_validation_context(&self) -> &Self::V; + + /// Returns the current height of the local chain. + fn host_height(&self) -> Result; + + /// Returns the current timestamp of the local chain. + 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; + + /// 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; + + /// Returns the ConnectionEnd for the given identifier `conn_id`. + 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. + /// + /// For more information on the specific requirements for validating the + /// client state of a host chain, please refer to the [ICS24 host + /// requirements](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#client-state-validation) + /// + /// Additionally, implementations specific to individual chains can be found + /// in the `ibc-core/ics24-host` module. + fn validate_self_client( + &self, + client_state_of_host_on_counterparty: Self::HostClientState, + ) -> 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; + + /// Function required by ICS-03. Returns the list of all possible versions that the connection + /// handshake protocol supports. + fn get_compatible_versions(&self) -> Vec { + ConnectionVersion::compatibles() + } + + /// Function required by ICS-03. Returns one version out of the supplied list of versions, which the + /// connection handshake protocol prefers. + fn pick_version( + &self, + counterparty_candidate_versions: &[ConnectionVersion], + ) -> Result { + pick_version( + &self.get_compatible_versions(), + counterparty_candidate_versions, + ) + .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; + + /// 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; + + /// 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; + + /// 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; + + /// Returns the packet commitment for the given store path + fn get_packet_commitment( + &self, + commitment_path: &CommitmentPath, + ) -> 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; + + /// 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; + + /// Returns the maximum expected time per block + fn max_expected_time_per_block(&self) -> Duration; + + /// Calculates the block delay period using the connection's delay period and the maximum + /// expected time per block. + fn block_delay(&self, delay_period_time: &Duration) -> u64 { + calculate_block_delay(delay_period_time, &self.max_expected_time_per_block()) + } + + /// 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<(), HostError>; +} + +/// Context to be implemented by the host that provides all "write-only" methods. +/// +/// Trait used for the top-level `execute` and `dispatch` entrypoints in the `ibc-core` crate. +pub trait ExecutionContext: ValidationContext { + type E: ClientExecutionContext; + + /// Retrieve the context that implements all clients' `ExecutionContext`. + fn get_client_execution_context(&mut self) -> &mut Self::E; + + /// Called upon client creation. + /// Increases the counter, that keeps track of how many clients have been created. + 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<(), 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<(), 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<(), HostError>; + + /// Stores the given packet commitment at the given store path + fn store_packet_commitment( + &mut self, + commitment_path: &CommitmentPath, + commitment: PacketCommitment, + ) -> Result<(), HostError>; + + /// Deletes the packet commitment at the given store path + fn delete_packet_commitment( + &mut self, + commitment_path: &CommitmentPath, + ) -> Result<(), HostError>; + + /// Stores the given packet receipt at the given store path + fn store_packet_receipt( + &mut self, + receipt_path: &ReceiptPath, + receipt: Receipt, + ) -> 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<(), HostError>; + + /// Deletes the packet acknowledgement at the given store path + 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<(), 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<(), 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<(), 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<(), 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<(), HostError>; + + /// Emit the given IBC event + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), HostError>; + + /// Log the given message. + fn log_message(&mut self, message: String) -> Result<(), HostError>; +} + +/// Convenient type alias for `ClientStateRef`, providing access to client +/// validation methods within the context. +pub type ClientStateRef = + <::V as ClientValidationContext>::ClientStateRef; + +/// Convenient type alias for `ClientStateMut`, providing access to client +/// execution methods within the context. +pub type ClientStateMut = + <::E as ClientExecutionContext>::ClientStateMut; + +/// Convenient type alias for `ConsensusStateRef`, providing access to client +/// validation methods within the context. +pub type ConsensusStateRef = + <::V as ClientValidationContext>::ConsensusStateRef; diff --git a/ibc-eureka-core/ics24-host/src/lib.rs b/ibc-eureka-core/ics24-host/src/lib.rs new file mode 100644 index 0000000000..7d64b4e387 --- /dev/null +++ b/ibc-eureka-core/ics24-host/src/lib.rs @@ -0,0 +1,29 @@ +//! Provides essential top-level traits designed for the seamless integration of +//! host chains with ibc-rs. It streamlines access to the host's storage, +//! facilitating the efficient retrieval of states and metadata crucial for the +//! execution of IBC logics. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(any(test, feature = "std"))] +extern crate std; + +pub(crate) mod utils; + +mod context; +pub use context::*; + +/// Re-exports ICS-24 data structures from `ibc-core-host-types` crate. +pub mod types { + #[doc(inline)] + pub use ibc_core_host_types::*; +} diff --git a/ibc-eureka-core/ics24-host/src/utils.rs b/ibc-eureka-core/ics24-host/src/utils.rs new file mode 100644 index 0000000000..9ba2cbe086 --- /dev/null +++ b/ibc-eureka-core/ics24-host/src/utils.rs @@ -0,0 +1,47 @@ +use core::time::Duration; + +pub fn calculate_block_delay( + delay_period_time: &Duration, + max_expected_time_per_block: &Duration, +) -> u64 { + let delay_period_time = delay_period_time.as_secs(); + let max_expected_time_per_block = max_expected_time_per_block.as_secs(); + if max_expected_time_per_block == 0 { + return 0; + } + if delay_period_time % max_expected_time_per_block == 0 { + return delay_period_time / max_expected_time_per_block; + } + + // TODO: Use `u64::div_ceil` here instead + (delay_period_time / max_expected_time_per_block) + 1 +} + +#[cfg(test)] +mod tests { + + use rstest::rstest; + + use super::*; + + #[rstest] + #[case::remainder_zero(10, 2, 5)] + #[case::remainder_not_zero(10, 3, 4)] + #[case::max_expected_zero(10, 0, 0)] + #[case::delay_period_zero(0, 2, 0)] + #[case::both_zero(0, 0, 0)] + #[case::delay_less_than_max(10, 11, 1)] + fn test_calculate_block_delay_zero( + #[case] delay_period_time: u64, + #[case] max_expected_time_per_block: u64, + #[case] expected: u64, + ) { + assert_eq!( + calculate_block_delay( + &Duration::from_secs(delay_period_time), + &Duration::from_secs(max_expected_time_per_block) + ), + expected + ); + } +} diff --git a/ibc-eureka-core/ics24-host/types/Cargo.toml b/ibc-eureka-core/ics24-host/types/Cargo.toml new file mode 100644 index 0000000000..e4953c7205 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "ibc-core-host-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "host", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-24 Host Requirements data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +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 } + +# ibc dependencies +ibc-primitives = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[dev-dependencies] +rstest = { workspace = true } +serde-json = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "ibc-primitives/std", +] +serde = [ + "dep:serde", + "ibc-primitives/serde", +] +schema = [ + "dep:schemars", + "ibc-primitives/schema", + "serde", + "std", +] +borsh = [ + "dep:borsh", + "ibc-primitives/borsh", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics24-host/types/src/error.rs b/ibc-eureka-core/ics24-host/types/src/error.rs new file mode 100644 index 0000000000..3eb5b75e04 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/error.rs @@ -0,0 +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 { + /// 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-eureka-core/ics24-host/types/src/identifiers/chain_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs new file mode 100644 index 0000000000..c96ccc36f3 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs @@ -0,0 +1,460 @@ +use core::fmt::{Debug, Display, Error as FmtError, Formatter}; +use core::str::FromStr; + +use ibc_primitives::prelude::*; +#[cfg(feature = "serde")] +use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor}; + +use crate::error::IdentifierError; +use crate::validate::{ + validate_identifier_chars, validate_identifier_length, validate_prefix_length, +}; + +/// Defines the domain type for chain identifiers. +/// +/// A valid `ChainId` follows the format {chain name}-{revision number} where +/// the revision number indicates how many times the chain has been upgraded. +/// Creating `ChainId`s not in this format will result in an error. +/// +/// It should be noted this format is not standardized yet, though it is widely +/// accepted and compatible with Cosmos SDK driven chains. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ChainId { + id: String, + revision_number: u64, +} + +impl ChainId { + /// Creates a new `ChainId` with the given chain identifier. + /// + /// It checks the identifier for valid characters according to `ICS-24` + /// specification and returns a `ChainId` successfully. + /// Stricter checks beyond `ICS-24` rests with the users, + /// based on their requirements. + /// + /// If the chain identifier is in the {chain name}-{revision number} format, + /// the revision number is parsed. Otherwise, the revision number is set to 0. + /// + /// ``` + /// use ibc_core_host_types::identifiers::ChainId; + /// + /// let chain_id = "chainA"; + /// let id = ChainId::new(chain_id).unwrap(); + /// assert_eq!(id.revision_number(), 0); + /// assert_eq!(id.as_str(), chain_id); + /// + /// let chain_id = "chainA-12"; + /// let id = ChainId::new(chain_id).unwrap(); + /// assert_eq!(id.revision_number(), 12); + /// assert_eq!(id.as_str(), chain_id); + /// ``` + pub fn new(chain_id: &str) -> Result { + Self::from_str(chain_id) + } + + /// Get a reference to the underlying string. + pub fn as_str(&self) -> &str { + &self.id + } + + pub fn split_chain_id(&self) -> Result<(&str, u64), IdentifierError> { + parse_chain_id_string(self.as_str()) + } + + /// Extract the revision number from the chain identifier + pub fn revision_number(&self) -> u64 { + self.revision_number + } + + /// Increases `ChainId`s revision number by one. + /// Fails if the chain identifier is not in + /// `{chain_name}-{revision_number}` format or + /// the revision number overflows. + /// + /// ``` + /// use ibc_core_host_types::identifiers::ChainId; + /// + /// let mut chain_id = ChainId::new("chainA-1").unwrap(); + /// assert!(chain_id.increment_revision_number().is_ok()); + /// assert_eq!(chain_id.revision_number(), 2); + /// + /// let mut chain_id = ChainId::new(&format!("chainA-{}", u64::MAX)).unwrap(); + /// assert!(chain_id.increment_revision_number().is_err()); + /// assert_eq!(chain_id.revision_number(), u64::MAX); + /// ``` + pub fn increment_revision_number(&mut self) -> Result<(), IdentifierError> { + let (chain_name, _) = self.split_chain_id()?; + let inc_revision_number = self + .revision_number + .checked_add(1) + .ok_or(IdentifierError::OverflowedRevisionNumber)?; + self.id = format!("{}-{}", chain_name, inc_revision_number); + self.revision_number = inc_revision_number; + Ok(()) + } + + /// A convenient method to check if the `ChainId` forms a valid identifier + /// with the desired min/max length. However, ICS-24 does not specify a + /// certain min or max lengths for chain identifiers. + pub fn validate_length(&self, min_length: u64, max_length: u64) -> Result<(), IdentifierError> { + match self.split_chain_id() { + Ok((chain_name, _)) => validate_prefix_length(chain_name, min_length, max_length), + _ => validate_identifier_length(&self.id, min_length, max_length), + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for ChainId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + const FIELDS: &[&str] = &["id", "revision_number"]; + + enum Field { + Id, + RevisionNumber, + } + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "id" => Ok(Field::Id), + "revisionNumber" | "revision_number" => Ok(Field::RevisionNumber), + _ => Err(Error::unknown_field(value, FIELDS)), + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct ChainIdVisitor; + + impl<'de> Visitor<'de> for ChainIdVisitor { + type Value = ChainId; + + fn expecting(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result { + formatter.write_str("struct ChainId") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut id = None; + let mut revision_number = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Id => { + if id.is_some() { + return Err(Error::duplicate_field("id")); + } + + let next_value = map.next_value::<&str>()?; + + let chain_id = ChainId::from_str(next_value) + .map_err(|_| Error::custom("failed to parse ChainId `id` field"))?; + + id = Some(chain_id.id); + revision_number = Some(chain_id.revision_number); + } + Field::RevisionNumber => { + let next_value = map.next_value::<&str>()?; + let rev = u64::from_str(next_value).unwrap_or(0); + + if let Some(rn) = revision_number { + if rev != 0 && rn != rev { + return Err(Error::custom(format_args!( + "chain ID revision numbers do not match; got `{}` and `{}`", + rn, rev, + ))); + } + } else { + revision_number = Some(rev); + } + } + } + } + + let id = id.ok_or_else(|| Error::missing_field("id"))?; + + Ok(ChainId { + id, + revision_number: revision_number.unwrap_or(0), + }) + } + } + + deserializer.deserialize_struct("ChainId", FIELDS, ChainIdVisitor) + } +} + +#[cfg(feature = "borsh")] +mod borsh_impls { + use borsh::io::{self, Error, ErrorKind, Read}; + use borsh::BorshDeserialize; + + use super::*; + + impl BorshDeserialize for ChainId { + fn deserialize_reader(reader: &mut R) -> io::Result { + let (id, revision_number) = <(String, u64)>::deserialize_reader(reader)?; + + match parse_chain_id_string(&id) { + Ok((_, rn)) => { + if revision_number != 0 && rn != revision_number { + return Err(Error::new( + ErrorKind::Other, + "chain ID revision numbers do not match", + )); + } + } + _ => { + if revision_number != 0 { + return Err(Error::new(ErrorKind::Other, "failed to parse chain ID")); + } + } + } + + Ok(ChainId { + id, + revision_number, + }) + } + } +} + +/// Construct a `ChainId` from a string literal only if it forms a valid +/// identifier. +impl FromStr for ChainId { + type Err = IdentifierError; + + fn from_str(id: &str) -> Result { + // Identifier string must have a maximum length of 64 characters. + + // Validates the chain name for allowed characters according to ICS-24. + validate_identifier_chars(id)?; + if let Ok((chain_name, revision_number)) = parse_chain_id_string(id) { + // Validate if the chain name with revision number has a valid length. + validate_prefix_length(chain_name, 1, 64)?; + Ok(Self { + id: id.into(), + revision_number, + }) + } else { + // Validate if the identifier has a valid length. + validate_identifier_length(id, 1, 64)?; + Ok(Self { + id: id.into(), + revision_number: 0, + }) + } + } +} + +impl From for String { + fn from(chain_id: ChainId) -> String { + chain_id.id + } +} + +impl Display for ChainId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.id) + } +} + +/// Parses a string intended to represent a `ChainId` and, if successful, +/// returns a tuple containing the chain name and revision number. +fn parse_chain_id_string(chain_id_str: &str) -> Result<(&str, u64), IdentifierError> { + chain_id_str + .rsplit_once('-') + .filter(|(_, rev_number_str)| { + // Validates the revision number not to start with leading zeros, like "01". + // Zero is the only allowed revision number with leading zero. + rev_number_str.as_bytes().first() != Some(&b'0') || rev_number_str.len() == 1 + }) + .and_then(|(chain_name, rev_number_str)| { + // Parses the revision number string into a `u64` and checks its validity. + rev_number_str + .parse() + .ok() + .map(|revision_number| (chain_name, revision_number)) + }) + .ok_or(IdentifierError::FailedToParse { + description: format!("revision number for chain ID `{chain_id_str}`"), + }) +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + + use super::*; + + #[rstest] + #[case("chainA-0", "chainA", 0)] + #[case("chainA-1", "chainA", 1)] + #[case("chainA--1", "chainA-", 1)] + #[case("chainA-1-2", "chainA-1", 2)] + #[case("111-2", "111", 2)] + #[case("----1", "---", 1)] + #[case("._+-1", "._+", 1)] + #[case(&("A".repeat(43) + "-3"), &("A".repeat(43)), 3)] + fn test_valid_chain_id_with_rev( + #[case] raw_chain_id: &str, + #[case] chain_name: &str, + #[case] revision_number: u64, + ) { + let chain_id = ChainId::new(raw_chain_id).unwrap(); + assert!(chain_id.validate_length(1, 64).is_ok()); + assert_eq!( + chain_id, + ChainId { + id: format!("{chain_name}-{revision_number}"), + revision_number + } + ); + } + + #[rstest] + #[case("chainA")] + #[case("chainA.2")] + #[case("123")] + #[case("._+")] + #[case("chainA-")] + #[case("chainA-a")] + #[case("chainA-01")] + #[case("chainA-1-")] + #[case(&"A".repeat(64))] + #[case::special_case("chainA-0")] + fn test_valid_chain_id_without_rev(#[case] chain_name: &str) { + let chain_id = ChainId::new(chain_name).unwrap(); + assert!(chain_id.validate_length(1, 64).is_ok()); + assert_eq!( + chain_id, + ChainId { + id: chain_name.into(), + revision_number: 0 + } + ); + } + + #[rstest] + #[case(&"A".repeat(65))] + #[case(&("A".repeat(44) + "-123"))] + #[case("-1")] + #[case(" ----1")] + #[case(" ")] + #[case(" chainA")] + #[case("chain A")] + #[case(" chainA.2")] + #[case(" chainA.2-1")] + #[case(" 1")] + #[case(" -")] + #[case(" -1")] + #[case("/chainA-1")] + fn test_invalid_chain_id_from_str(#[case] chain_id_str: &str) { + assert!(ChainId::new(chain_id_str).is_err()); + } + + #[test] + fn test_inc_revision_number() { + let mut chain_id = ChainId::new("chainA-1").unwrap(); + + assert!(chain_id.increment_revision_number().is_ok()); + assert_eq!(chain_id.revision_number(), 2); + assert_eq!(chain_id.as_str(), "chainA-2"); + + assert!(chain_id.increment_revision_number().is_ok()); + assert_eq!(chain_id.revision_number(), 3); + assert_eq!(chain_id.as_str(), "chainA-3"); + } + + #[test] + fn test_failed_inc_revision_number() { + let mut chain_id = ChainId::new("chainA").unwrap(); + + assert!(chain_id.increment_revision_number().is_err()); + assert_eq!(chain_id.revision_number(), 0); + assert_eq!(chain_id.as_str(), "chainA"); + } + + #[cfg(feature = "serde")] + #[rstest] + #[case(r#"{"id":"foo-42","revision_number":"42"}"#)] + #[case(r#"{"id":"foo-42","revision_number":"0"}"#)] + #[case(r#"{"id":"foo-bar-42","revision_number":"0"}"#)] + fn test_valid_chain_id_json_deserialization(#[case] chain_id_json: &str) { + let chain_id = serde_json::from_str::(chain_id_json); + assert!(chain_id.is_ok()); + + let chain_id = chain_id.unwrap(); + + let (_id, rev_num) = chain_id.split_chain_id().unwrap(); + + assert_eq!(rev_num, chain_id.revision_number()); + } + + #[cfg(feature = "serde")] + #[rstest] + #[case(r#"{"id":"foo-42","revision_number":"69"}"#)] + #[case(r#"{"id":"foo-0","revision_number":"69"}"#)] + #[case(r#"{"id":"/foo-42","revision_number":"0"}"#)] + fn test_invalid_chain_id_json_deserialization(#[case] chain_id_json: &str) { + assert!(serde_json::from_str::(chain_id_json).is_err()) + } + + #[cfg(feature = "borsh")] + #[rstest] + #[case(b"\x06\0\0\0foo-42\x45\0\0\0\0\0\0\0")] + fn test_invalid_chain_id_borsh_deserialization(#[case] chain_id_bytes: &[u8]) { + assert!(borsh::from_slice::(chain_id_bytes).is_err()) + } + + #[cfg(feature = "borsh")] + fn borsh_ser_de_roundtrip(chain_id: ChainId) { + let chain_id_bytes = borsh::to_vec(&chain_id).unwrap(); + let res = borsh::from_slice::(&chain_id_bytes).unwrap(); + + assert_eq!(chain_id, res); + } + + #[cfg(feature = "borsh")] + #[test] + fn test_valid_borsh_ser_de_roundtrip() { + borsh_ser_de_roundtrip(ChainId::new("foo-42").unwrap()); + borsh_ser_de_roundtrip(ChainId::new("foo").unwrap()); + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs new file mode 100644 index 0000000000..c8f9e30d77 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs @@ -0,0 +1,99 @@ +use core::fmt::{Debug, Display, Error as FmtError, Formatter}; +use core::str::FromStr; + +use derive_more::Into; +use ibc_primitives::prelude::*; + +use crate::error::IdentifierError; +use crate::validate::validate_channel_identifier; + +const CHANNEL_ID_PREFIX: &str = "channel"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Into)] +pub struct ChannelId(String); + +impl ChannelId { + /// Builds a new channel identifier. Like client and connection identifiers, channel ids are + /// deterministically formed from two elements: a prefix `prefix`, and a monotonically + /// increasing `counter`, separated by a dash "-". + /// The prefix is currently determined statically (see `ChannelId::prefix()`) so this method + /// accepts a single argument, the `counter`. + /// + /// ``` + /// # use ibc_core_host_types::identifiers::ChannelId; + /// let chan_id = ChannelId::new(27); + /// assert_eq!(chan_id.to_string(), "channel-27"); + /// ``` + pub fn new(identifier: u64) -> Self { + let id = format!("{}-{}", Self::prefix(), identifier); + Self(id) + } + + /// Returns the static prefix to be used across all channel identifiers. + pub fn prefix() -> &'static str { + CHANNEL_ID_PREFIX + } + + /// Get this identifier as a borrowed `&str` + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Get this identifier as a borrowed byte slice + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + + pub fn zero() -> Self { + Self::new(0) + } +} + +/// This implementation provides a `to_string` method. +impl Display for ChannelId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.0) + } +} + +impl FromStr for ChannelId { + type Err = IdentifierError; + + fn from_str(s: &str) -> Result { + validate_channel_identifier(s).map(|_| Self(s.to_string())) + } +} + +impl AsRef for ChannelId { + fn as_ref(&self) -> &str { + &self.0 + } +} + +/// Equality check against string literal (satisfies &ChannelId == &str). +/// ``` +/// use core::str::FromStr; +/// use ibc_core_host_types::identifiers::ChannelId; +/// let channel_id = ChannelId::from_str("channel-0"); +/// assert!(channel_id.is_ok()); +/// channel_id.map(|id| {assert_eq!(&id, "channel-0")}); +/// ``` +impl PartialEq for ChannelId { + fn eq(&self, other: &str) -> bool { + self.as_str().eq(other) + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs new file mode 100644 index 0000000000..d0128336b4 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs @@ -0,0 +1,126 @@ +use core::str::FromStr; + +use derive_more::Into; +use ibc_primitives::prelude::*; + +use crate::error::IdentifierError; +use crate::validate::{validate_client_identifier, validate_client_type}; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Into, derive_more::Display)] +pub struct ClientId(String); + +impl ClientId { + /// Builds a new client identifier. + /// + /// Client identifiers are deterministically formed from two elements: + /// a prefix derived from the client type `ctype`, and a monotonically + /// increasing `counter`; these are separated by a dash "-". + /// + /// See also [`ClientType::build_client_id`](super::ClientType::build_client_id) + /// method. + /// + /// # Example + /// + /// ``` + /// # use ibc_core_host_types::identifiers::ClientId; + /// # use ibc_core_host_types::identifiers::ClientType; + /// # use std::str::FromStr; + /// let client_type = ClientType::from_str("07-tendermint").unwrap(); + /// let client_id = &client_type.build_client_id(0); + /// assert_eq!(client_id.as_str(), "07-tendermint-0"); + /// ``` + pub fn new(client_type: &str, counter: u64) -> Result { + let client_type = client_type.trim(); + validate_client_type(client_type).map(|()| Self::format(client_type, counter)) + } + + pub(super) fn format(client_type: &str, counter: u64) -> Self { + let client_id = format!("{client_type}-{counter}"); + if cfg!(debug_assertions) { + validate_client_type(client_type).expect("valid client type"); + validate_client_identifier(&client_id).expect("valid client id"); + } + Self(client_id) + } + + /// Get this identifier as a borrowed `&str` + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Get this identifier as a borrowed byte slice + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + + /// Check if the client identifier is for 08-wasm light client. + pub fn is_wasm_client_id(&self) -> bool { + const WASM_CLIENT_PREFIX: &str = "08-wasm-"; + + // prefixed with wasm client type identifier. + self.0.starts_with(WASM_CLIENT_PREFIX) + // followed by non-empty string. + && self.0.len() > WASM_CLIENT_PREFIX.len() + // and the rest of the string is numeric. + && self.0.chars().skip(WASM_CLIENT_PREFIX.len()).all(char::is_numeric) + } +} + +impl FromStr for ClientId { + type Err = IdentifierError; + + fn from_str(s: &str) -> Result { + validate_client_identifier(s).map(|_| Self(s.to_string())) + } +} + +/// Equality check against string literal (satisfies &ClientId == &str). +/// ``` +/// use core::str::FromStr; +/// use ibc_core_host_types::identifiers::ClientId; +/// let client_id = ClientId::from_str("clientidtwo"); +/// assert!(client_id.is_ok()); +/// client_id.map(|id| {assert_eq!(&id, "clientidtwo")}); +/// ``` +impl PartialEq for ClientId { + fn eq(&self, other: &str) -> bool { + self.as_str().eq(other) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[rstest::rstest] + #[case("08-wasm-1", true)] + #[case("08-wasm-", false)] + #[case("08-wasm-abc", false)] + #[case("08-wasm-1-2", false)] + #[case("08-wasm", false)] + #[case("wasm", false)] + #[case("08-", false)] + fn test_is_wasm_client_id(#[case] client_id: &str, #[case] expected: bool) { + assert_eq!( + matches!( + client_id.parse().map(|id: ClientId| id.is_wasm_client_id()), + Ok(true) + ), + expected + ); + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs new file mode 100644 index 0000000000..8f0547aa47 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs @@ -0,0 +1,101 @@ +//! Defines the `ClientType` format, typically used in chain IDs. + +use core::str::FromStr; + +use ibc_primitives::prelude::*; + +use super::ClientId; +use crate::error::IdentifierError; +use crate::validate::validate_client_type; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// Type of the client, depending on the specific consensus algorithm. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)] +pub struct ClientType(String); + +impl ClientType { + /// Constructs a new `ClientType` from the given `String` if it ends with a valid client identifier. + pub fn new(client_type: &str) -> Result { + let client_type = client_type.trim(); + validate_client_type(client_type).map(|()| Self(client_type.into())) + } + + /// Constructs a new [`ClientId`] with this type of client type and given + /// `counter`. + /// + /// This is equivalent to `ClientId::new(self.as_str(), counter)` but + /// infallible since client type is assumed to be correct. + /// + /// ``` + /// # use ibc_core_host_types::identifiers::ClientId; + /// # use ibc_core_host_types::identifiers::ClientType; + /// # use std::str::FromStr; + /// let client_type = ClientType::from_str("07-tendermint").unwrap(); + /// let client_id = client_type.build_client_id(14); + /// assert_eq!(client_id.as_str(), "07-tendermint-14"); + /// ``` + pub fn build_client_id(&self, counter: u64) -> ClientId { + ClientId::format(self.as_str(), counter) + } + + /// Yields this identifier as a borrowed `&str` + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl FromStr for ClientType { + type Err = IdentifierError; + + fn from_str(s: &str) -> Result { + Self::new(s) + } +} + +#[cfg(test)] +mod test { + use rstest::rstest; + + use super::*; + + #[rstest] + #[case::tendermint("07-tendermint")] + #[case::wasm("08-wasm")] + #[case::zero_double_digits("00-dummy")] + #[case::zero_single_digit("0-dummy")] + #[case::length_seven("1234567")] + fn client_type_from_str(#[case] client_str: &str) { + let client_type = ClientType::from_str(client_str).unwrap(); + assert_eq!(client_type.as_str(), client_str); + } + + #[rstest] + #[case::length_less_than_seven("00-foo")] + #[case::length_six("123456")] + fn client_type_from_str_fails(#[case] client_str: &str) { + let client_type = ClientType::from_str(client_str); + assert!(client_type.is_err()); + } + + #[rstest] + #[case::tendermint("07-tendermint", 118)] + #[case::wasm("08-wasm", 2)] + #[case::zero_counter("01-dummy", 0)] + fn client_type_build_client_id(#[case] client_str: &str, #[case] counter: u64) { + let client_type = ClientType::from_str(client_str).unwrap(); + let client_id = client_type.build_client_id(counter); + assert_eq!(client_id.as_str(), format!("{}-{}", client_str, counter)); + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs new file mode 100644 index 0000000000..9fe9657cc3 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs @@ -0,0 +1,93 @@ +use core::fmt::{Display, Error as FmtError, Formatter}; +use core::str::FromStr; + +use derive_more::Into; +use ibc_primitives::prelude::*; + +use crate::error::IdentifierError; +use crate::validate::validate_connection_identifier; + +const CONNECTION_ID_PREFIX: &str = "connection"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Into)] +pub struct ConnectionId(String); + +impl ConnectionId { + /// Builds a new connection identifier. Connection identifiers are deterministically formed from + /// two elements: a prefix `prefix`, and a monotonically increasing `counter`; these are + /// separated by a dash "-". The prefix is currently determined statically (see + /// `ConnectionId::prefix()`) so this method accepts a single argument, the `counter`. + /// + /// ``` + /// # use ibc_core_host_types::identifiers::ConnectionId; + /// let conn_id = ConnectionId::new(11); + /// assert_eq!(&conn_id, "connection-11"); + /// ``` + pub fn new(identifier: u64) -> Self { + let id = format!("{}-{}", Self::prefix(), identifier); + Self(id) + } + + /// Returns the static prefix to be used across all connection identifiers. + pub fn prefix() -> &'static str { + CONNECTION_ID_PREFIX + } + + /// Get this identifier as a borrowed `&str` + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Get this identifier as a borrowed byte slice + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + + /// Return ConnectionId with identifier 0 + pub fn zero() -> Self { + Self::new(0) + } +} + +/// This implementation provides a `to_string` method. +impl Display for ConnectionId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.0) + } +} + +impl FromStr for ConnectionId { + type Err = IdentifierError; + + fn from_str(s: &str) -> Result { + validate_connection_identifier(s).map(|_| Self(s.to_string())) + } +} + +/// Equality check against string literal (satisfies &ConnectionId == &str). +/// ``` +/// use core::str::FromStr; +/// use ibc_core_host_types::identifiers::ConnectionId; +/// let conn_id = ConnectionId::from_str("connection-0"); +/// assert!(conn_id.is_ok()); +/// conn_id.map(|id| {assert_eq!(&id, "connection-0")}); +/// ``` +impl PartialEq for ConnectionId { + fn eq(&self, other: &str) -> bool { + self.as_str().eq(other) + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/mod.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/mod.rs new file mode 100644 index 0000000000..9ae79f6735 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/mod.rs @@ -0,0 +1,17 @@ +//! Defines identifier types + +mod chain_id; +mod channel_id; +mod client_id; +mod client_type; +mod connection_id; +mod port_id; +mod sequence; + +pub use chain_id::ChainId; +pub use channel_id::ChannelId; +pub use client_id::ClientId; +pub use client_type::ClientType; +pub use connection_id::ConnectionId; +pub use port_id::PortId; +pub use sequence::Sequence; diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/port_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/port_id.rs new file mode 100644 index 0000000000..e1a0e062da --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/port_id.rs @@ -0,0 +1,73 @@ +use core::fmt::{Display, Error as FmtError, Formatter}; +use core::str::FromStr; + +use derive_more::Into; +use ibc_primitives::prelude::*; + +use crate::error::IdentifierError; +use crate::validate::validate_port_identifier; + +const TRANSFER_PORT_ID: &str = "transfer"; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Into)] +pub struct PortId(String); + +impl PortId { + pub fn new(id: String) -> Result { + Self::from_str(&id) + } + + /// Infallible creation of the well-known transfer port + pub fn transfer() -> Self { + Self(TRANSFER_PORT_ID.to_string()) + } + + /// Get this identifier as a borrowed `&str` + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Get this identifier as a borrowed byte slice + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + + pub fn validate(&self) -> Result<(), IdentifierError> { + validate_port_identifier(self.as_str()) + } +} + +/// This implementation provides a `to_string` method. +impl Display for PortId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.0) + } +} + +impl FromStr for PortId { + type Err = IdentifierError; + + fn from_str(s: &str) -> Result { + validate_port_identifier(s).map(|_| Self(s.to_string())) + } +} + +impl AsRef for PortId { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/sequence.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/sequence.rs new file mode 100644 index 0000000000..0ab91f24cb --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/sequence.rs @@ -0,0 +1,73 @@ +use ibc_primitives::prelude::*; + +use crate::error::IdentifierError; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +/// The sequence number of a packet enforces ordering among packets from the same source. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Sequence(u64); + +impl core::str::FromStr for Sequence { + type Err = IdentifierError; + + fn from_str(s: &str) -> Result { + Ok(Self::from(s.parse::().map_err(|e| { + IdentifierError::FailedToParse { + description: format!("sequence `{s}`: {e}"), + } + })?)) + } +} + +impl Sequence { + /// Gives the sequence number. + pub fn value(&self) -> u64 { + self.0 + } + + /// Returns `true` if the sequence number is zero. + pub fn is_zero(&self) -> bool { + self.0 == 0 + } + + /// Increments the sequence number by one. + pub fn increment(&self) -> Sequence { + Sequence(self.0 + 1) + } + + /// Encodes the sequence number into a byte array in big endian. + pub fn to_vec(&self) -> Vec { + self.0.to_be_bytes().to_vec() + } +} + +impl From for Sequence { + fn from(seq: u64) -> Self { + Sequence(seq) + } +} + +impl From for u64 { + fn from(s: Sequence) -> u64 { + s.0 + } +} + +impl core::fmt::Display for Sequence { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!(f, "{}", self.0) + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/lib.rs b/ibc-eureka-core/ics24-host/types/src/lib.rs new file mode 100644 index 0000000000..1fae79ac6a --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/lib.rs @@ -0,0 +1,23 @@ +//! ICS-24: Host defines the minimal set of interfaces that a state machine +//! hosting an IBC-enabled chain must implement. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +pub mod error; +pub mod identifiers; +pub mod path; +pub(crate) mod validate; diff --git a/ibc-eureka-core/ics24-host/types/src/path.rs b/ibc-eureka-core/ics24-host/types/src/path.rs new file mode 100644 index 0000000000..61ce318130 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/path.rs @@ -0,0 +1,1518 @@ +//! Defines all store paths used by IBC + +/// Path-space as listed in ICS-024 +/// https://github.com/cosmos/ibc/tree/master/spec/core/ics-024-host-requirements#path-space +/// Some of these are implemented in other ICSs, but ICS-024 has a nice summary table. +/// +use core::str::FromStr; + +use derive_more::{Display, From}; +use ibc_primitives::prelude::*; + +use crate::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; + +pub const NEXT_CLIENT_SEQUENCE: &str = "nextClientSequence"; +pub const NEXT_CONNECTION_SEQUENCE: &str = "nextConnectionSequence"; +pub const NEXT_CHANNEL_SEQUENCE: &str = "nextChannelSequence"; + +pub const CLIENT_PREFIX: &str = "clients"; +pub const CLIENT_STATE: &str = "clientState"; +pub const CONSENSUS_STATE_PREFIX: &str = "consensusStates"; +pub const CONNECTION_PREFIX: &str = "connections"; +pub const CHANNEL_PREFIX: &str = "channels"; +pub const CHANNEL_END_PREFIX: &str = "channelEnds"; +pub const PORT_PREFIX: &str = "ports"; +pub const SEQUENCE_PREFIX: &str = "sequences"; +pub const NEXT_SEQ_SEND_PREFIX: &str = "nextSequenceSend"; +pub const NEXT_SEQ_RECV_PREFIX: &str = "nextSequenceRecv"; +pub const NEXT_SEQ_ACK_PREFIX: &str = "nextSequenceAck"; +pub const PACKET_COMMITMENT_PREFIX: &str = "commitments"; +pub const PACKET_ACK_PREFIX: &str = "acks"; +pub const PACKET_RECEIPT_PREFIX: &str = "receipts"; + +pub const ITERATE_CONSENSUS_STATE_PREFIX: &str = "iterateConsensusStates"; +pub const PROCESSED_TIME: &str = "processedTime"; +pub const PROCESSED_HEIGHT: &str = "processedHeight"; + +/// ABCI client upgrade keys +/// - The key identifying the upgraded IBC state within the upgrade sub-store +pub const UPGRADED_IBC_STATE: &str = "upgradedIBCState"; +/// - The key identifying the upgraded client state +pub const UPGRADED_CLIENT_STATE: &str = "upgradedClient"; +/// - The key identifying the upgraded consensus state +pub const UPGRADED_CLIENT_CONSENSUS_STATE: &str = "upgradedConsState"; + +/// Represents a general-purpose path structure using the byte representation of +/// a path. This struct abstracts over different path types and can handle bytes +/// obtained from various serialization formats (e.g., Protobuf, Borsh). +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From)] +pub struct PathBytes(Vec); + +impl PathBytes { + pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + Self(bytes.as_ref().to_vec()) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn into_vec(self) -> Vec { + self.0 + } + + /// Flattens a list of path bytes into a single path. + pub fn flatten>(paths: Vec) -> Self { + let mut bytes = Vec::new(); + paths.iter().for_each(|path| { + bytes.extend_from_slice(path.as_ref()); + }); + Self(bytes) + } +} + +impl AsRef<[u8]> for PathBytes { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// The Path enum abstracts out the different sub-paths. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From, Display)] +pub enum Path { + NextClientSequence(NextClientSequencePath), + NextConnectionSequence(NextConnectionSequencePath), + NextChannelSequence(NextChannelSequencePath), + ClientState(ClientStatePath), + ClientConsensusState(ClientConsensusStatePath), + ClientUpdateTime(ClientUpdateTimePath), + ClientUpdateHeight(ClientUpdateHeightPath), + ClientConnection(ClientConnectionPath), + Connection(ConnectionPath), + Ports(PortPath), + ChannelEnd(ChannelEndPath), + SeqSend(SeqSendPath), + SeqRecv(SeqRecvPath), + SeqAck(SeqAckPath), + Commitment(CommitmentPath), + Ack(AckPath), + Receipt(ReceiptPath), + UpgradeClientState(UpgradeClientStatePath), + UpgradeConsensusState(UpgradeConsensusStatePath), +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{NEXT_CLIENT_SEQUENCE}")] +pub struct NextClientSequencePath; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{NEXT_CONNECTION_SEQUENCE}")] +pub struct NextConnectionSequencePath; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{NEXT_CHANNEL_SEQUENCE}")] +pub struct NextChannelSequencePath; + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display, From)] +#[display(fmt = "{CLIENT_PREFIX}/{_0}/{CLIENT_STATE}")] +pub struct ClientStatePath(pub ClientId); + +impl ClientStatePath { + pub fn new(client_id: ClientId) -> ClientStatePath { + ClientStatePath(client_id) + } + + /// Returns the client store prefix under which all the client states are + /// stored: "clients". + pub fn prefix() -> String { + CLIENT_PREFIX.to_string() + } + + /// Returns the final part (leaf) of the path under which an individual + /// client state is stored: "clientState". + pub fn leaf() -> String { + CLIENT_STATE.to_string() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display( + fmt = "{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}/{revision_number}-{revision_height}" +)] +pub struct ClientConsensusStatePath { + pub client_id: ClientId, + pub revision_number: u64, + pub revision_height: u64, +} + +// Returns the full consensus state path of specific client in the format: +// "clients/{client_id}/consensusStates" as a string. +pub fn full_consensus_state_path(client_id: &ClientId) -> String { + format!("{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}") +} + +impl ClientConsensusStatePath { + /// Constructs a new `ClientConsensusStatePath`. + pub fn new( + client_id: ClientId, + revision_number: u64, + revision_height: u64, + ) -> ClientConsensusStatePath { + ClientConsensusStatePath { + client_id, + revision_number, + revision_height, + } + } + + /// Returns the path representing the parent group under which all consensus + /// states are stored: "clients/{client_id}/consensusStates". + pub fn parent(&self) -> String { + full_consensus_state_path(&self.client_id) + } + + /// Returns the final part (leaf) of the path under which an individual + /// consensus state is stored: + /// "consensusStates/{revision_number}-{revision_height}". + pub fn leaf(&self) -> String { + format!( + "{CONSENSUS_STATE_PREFIX}/{}-{}", + self.revision_number, self.revision_height + ) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display( + fmt = "{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}/{revision_number}-{revision_height}/{PROCESSED_TIME}" +)] +pub struct ClientUpdateTimePath { + pub client_id: ClientId, + pub revision_number: u64, + pub revision_height: u64, +} + +impl ClientUpdateTimePath { + /// Constructs a new `ClientUpdateTimePath`. + pub fn new(client_id: ClientId, revision_number: u64, revision_height: u64) -> Self { + Self { + client_id, + revision_number, + revision_height, + } + } + + /// Returns the path representing the parent group under which all the + /// processed times are stored: "clients/{client_id}/consensusStates". + pub fn parent(&self) -> String { + full_consensus_state_path(&self.client_id) + } + + /// Returns the final part (leaf) of the path under which an individual + /// processed time is stored: + /// "consensusStates/{revision_number}-{revision_height}/processedTime". + pub fn leaf(&self) -> String { + format!( + "{CONSENSUS_STATE_PREFIX}/{}-{}/{PROCESSED_TIME}", + self.revision_number, self.revision_height + ) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display( + fmt = "{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}/{revision_number}-{revision_height}/{PROCESSED_HEIGHT}" +)] +pub struct ClientUpdateHeightPath { + pub client_id: ClientId, + pub revision_number: u64, + pub revision_height: u64, +} + +impl ClientUpdateHeightPath { + pub fn new(client_id: ClientId, revision_number: u64, revision_height: u64) -> Self { + Self { + client_id, + revision_number, + revision_height, + } + } + + /// Returns the path representing the parent group under which all the + /// processed heights are stored: "clients/{client_id}/consensusStates". + pub fn parent(&self) -> String { + full_consensus_state_path(&self.client_id) + } + + /// Returns the final part (leaf) of the path under which an individual + /// processed height is stored: + /// "consensusStates/{revision_number}-{revision_height}/processedHeight". + pub fn leaf(&self) -> String { + format!( + "{CONSENSUS_STATE_PREFIX}/{}-{}/{PROCESSED_HEIGHT}", + self.revision_number, self.revision_height + ) + } +} + +/// This iteration key is namely used by the `ibc-go` implementation as an +/// efficient approach for iterating over consensus states. This is specifically +/// incorporated to facilitate cross-compatibility with `ibc-go` when developing +/// CosmWasm-driven light clients with `ibc-rs`. +/// +/// The key is formatted like so: +/// `iterateConsensusStates{BigEndianRevisionBytes}{BigEndianHeightBytes}` +/// to ensure that the lexicographic order of iteration keys match the +/// height order of the consensus states. +/// +/// See `ibc-go` +/// [documentation](https://github.com/cosmos/ibc-go/blob/016a6095b577ecb9323edad508cff19d017636a1/modules/light-clients/07-tendermint/store.go#L19-L34) +/// for more details. +pub fn iteration_key(revision_number: u64, revision_height: u64) -> Vec { + let mut path = Vec::new(); + path.extend_from_slice(ITERATE_CONSENSUS_STATE_PREFIX.as_bytes()); + path.extend(revision_number.to_be_bytes()); + path.extend(revision_height.to_be_bytes()); + path +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{CLIENT_PREFIX}/{_0}/{CONNECTION_PREFIX}")] +pub struct ClientConnectionPath(pub ClientId); + +impl ClientConnectionPath { + pub fn new(client_id: ClientId) -> ClientConnectionPath { + ClientConnectionPath(client_id) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{CONNECTION_PREFIX}/{_0}")] +pub struct ConnectionPath(pub ConnectionId); + +impl ConnectionPath { + pub fn new(connection_id: &ConnectionId) -> ConnectionPath { + ConnectionPath(connection_id.clone()) + } + + /// Returns the connection store prefix under which all the connections are + /// stored: "connections". + pub fn prefix() -> String { + CONNECTION_PREFIX.to_string() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{PORT_PREFIX}/{_0}")] +pub struct PortPath(pub PortId); + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{CHANNEL_END_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")] +pub struct ChannelEndPath(pub PortId, pub ChannelId); + +impl ChannelEndPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId) -> ChannelEndPath { + ChannelEndPath(port_id.clone(), channel_id.clone()) + } + + /// Returns the channel end store prefix under which all the channel ends + /// are stored: "channelEnds". + pub fn prefix() -> String { + CHANNEL_END_PREFIX.to_string() + } + + /// Returns the parent group path under which all the sequences of a channel are + /// stored with the format: + /// "{prefix}/ports/{port_id}/channels/{channel_id}/sequences" + fn full_sequences_path(&self, prefix: &str) -> String { + format!( + "{prefix}/{PORT_PREFIX}/{}/{CHANNEL_PREFIX}/{}/{SEQUENCE_PREFIX}", + self.0, self.1, + ) + } + + /// Returns the parent group path under which all the commitment packets of + /// a channel are stored. + pub fn commitments_path(&self) -> String { + self.full_sequences_path(PACKET_COMMITMENT_PREFIX) + } + + /// Returns the parent group path under which all the ack packets of a + /// channel are stored. + pub fn acks_path(&self) -> String { + self.full_sequences_path(PACKET_ACK_PREFIX) + } + + /// Returns the parent group path under which all the receipt packets of a + /// channel are stored. + pub fn receipts_path(&self) -> String { + self.full_sequences_path(PACKET_RECEIPT_PREFIX) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{NEXT_SEQ_SEND_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")] +pub struct SeqSendPath(pub PortId, pub ChannelId); + +impl SeqSendPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId) -> SeqSendPath { + SeqSendPath(port_id.clone(), channel_id.clone()) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{NEXT_SEQ_RECV_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")] +pub struct SeqRecvPath(pub PortId, pub ChannelId); + +impl SeqRecvPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId) -> SeqRecvPath { + SeqRecvPath(port_id.clone(), channel_id.clone()) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{NEXT_SEQ_ACK_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")] +pub struct SeqAckPath(pub PortId, pub ChannelId); + +impl SeqAckPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId) -> SeqAckPath { + SeqAckPath(port_id.clone(), channel_id.clone()) + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display( + fmt = "{PACKET_COMMITMENT_PREFIX}/{PORT_PREFIX}/{port_id}/{CHANNEL_PREFIX}/{channel_id}/{SEQUENCE_PREFIX}/{sequence}" +)] +pub struct CommitmentPath { + pub port_id: PortId, + pub channel_id: ChannelId, + pub sequence: Sequence, +} + +impl CommitmentPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId, sequence: Sequence) -> CommitmentPath { + CommitmentPath { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence, + } + } + + /// Returns the commitment store prefix under which all the packet + /// commitments are stored: "commitments" + pub fn prefix() -> String { + PACKET_COMMITMENT_PREFIX.to_string() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display( + fmt = "{PACKET_ACK_PREFIX}/{PORT_PREFIX}/{port_id}/{CHANNEL_PREFIX}/{channel_id}/{SEQUENCE_PREFIX}/{sequence}" +)] +pub struct AckPath { + pub port_id: PortId, + pub channel_id: ChannelId, + pub sequence: Sequence, +} + +impl AckPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId, sequence: Sequence) -> AckPath { + AckPath { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence, + } + } + + /// Returns the ack store prefix under which all the packet acks are stored: + /// "acks" + pub fn prefix() -> String { + PACKET_ACK_PREFIX.to_string() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display( + fmt = "{PACKET_RECEIPT_PREFIX}/{PORT_PREFIX}/{port_id}/{CHANNEL_PREFIX}/{channel_id}/{SEQUENCE_PREFIX}/{sequence}" +)] +pub struct ReceiptPath { + pub port_id: PortId, + pub channel_id: ChannelId, + pub sequence: Sequence, +} + +impl ReceiptPath { + pub fn new(port_id: &PortId, channel_id: &ChannelId, sequence: Sequence) -> ReceiptPath { + ReceiptPath { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence, + } + } + + /// Returns the receipt store prefix under which all the packet receipts are + /// stored: "receipts" + pub fn prefix() -> String { + PACKET_RECEIPT_PREFIX.to_string() + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{upgrade_path}/{height}/{UPGRADED_CLIENT_STATE}")] +pub struct UpgradeClientStatePath { + pub upgrade_path: String, + pub height: u64, +} + +impl UpgradeClientStatePath { + /// Create with the default upgrade path + pub fn new_with_default_path(height: u64) -> Self { + Self { + upgrade_path: UPGRADED_IBC_STATE.to_string(), + height, + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] +#[display(fmt = "{upgrade_path}/{height}/{UPGRADED_CLIENT_CONSENSUS_STATE}")] +pub struct UpgradeConsensusStatePath { + pub upgrade_path: String, + pub height: u64, +} + +impl UpgradeConsensusStatePath { + /// Create with the default upgrade path + pub fn new_with_default_path(height: u64) -> Self { + Self { + upgrade_path: UPGRADED_IBC_STATE.to_string(), + height, + } + } +} + +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// Sub-paths which are not part of the specification, but are still +/// useful to represent for parsing purposes. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum SubPath { + Channels(ChannelId), + Sequences(Sequence), +} + +impl Path { + /// Indication if the path is provable. + pub fn is_provable(&self) -> bool { + !matches!(&self, Path::ClientConnection(_) | Path::Ports(_)) + } + + /// into_bytes implementation + pub fn into_bytes(self) -> Vec { + self.to_string().into_bytes() + } +} + +#[derive(Debug, displaydoc::Display)] +pub enum PathError { + /// `{path}` could not be parsed into a Path + ParseFailure { path: String }, +} + +#[cfg(feature = "std")] +impl std::error::Error for PathError {} + +/// The FromStr trait allows paths encoded as strings to be parsed into Paths. +impl FromStr for Path { + type Err = PathError; + + fn from_str(s: &str) -> Result { + let components: Vec<&str> = s.split('/').collect(); + + parse_next_sequence(&components) + .or_else(|| parse_client_paths(&components)) + .or_else(|| parse_connections(&components)) + .or_else(|| parse_ports(&components)) + .or_else(|| parse_channel_ends(&components)) + .or_else(|| parse_seqs(&components)) + .or_else(|| parse_commitments(&components)) + .or_else(|| parse_acks(&components)) + .or_else(|| parse_receipts(&components)) + .or_else(|| parse_upgrade_client_state(&components)) + .or_else(|| parse_upgrade_consensus_state(&components)) + .ok_or(PathError::ParseFailure { + path: s.to_string(), + }) + } +} + +fn parse_next_sequence(components: &[&str]) -> Option { + if components.len() != 1 { + return None; + } + + match *components.first()? { + NEXT_CLIENT_SEQUENCE => Some(NextClientSequencePath.into()), + NEXT_CONNECTION_SEQUENCE => Some(NextConnectionSequencePath.into()), + NEXT_CHANNEL_SEQUENCE => Some(NextChannelSequencePath.into()), + _ => None, + } +} + +fn parse_client_paths(components: &[&str]) -> Option { + let first = *components.first()?; + + if first != CLIENT_PREFIX { + return None; + } + + let client_id = ClientId::from_str(components[1]).ok()?; + + if components.len() == 3 { + match components[2] { + CLIENT_STATE => Some(ClientStatePath(client_id).into()), + CONNECTION_PREFIX => Some(ClientConnectionPath(client_id).into()), + _ => None, + } + } else if components.len() == 4 || components.len() == 5 { + match components[2] { + CONSENSUS_STATE_PREFIX => {} + _ => return None, + } + + let epoch_height: Vec<&str> = components[3].split('-').collect(); + + if epoch_height.len() != 2 { + return None; + } + + let revision_number = epoch_height[0]; + let revision_height = epoch_height[1]; + + let revision_number = revision_number.parse::().ok()?; + + let revision_height = revision_height.parse::().ok()?; + + match components.len() { + 4 => Some( + ClientConsensusStatePath { + client_id, + revision_number, + revision_height, + } + .into(), + ), + 5 => match components[4] { + PROCESSED_TIME => Some( + ClientUpdateTimePath { + client_id, + revision_number, + revision_height, + } + .into(), + ), + PROCESSED_HEIGHT => Some( + ClientUpdateHeightPath { + client_id, + revision_number, + revision_height, + } + .into(), + ), + _ => None, + }, + _ => None, + } + } else { + None + } +} + +fn parse_connections(components: &[&str]) -> Option { + if components.len() != 2 { + return None; + } + + let first = *components.first()?; + + if first != CONNECTION_PREFIX { + return None; + } + + let connection_id = *components.last()?; + + let connection_id = ConnectionId::from_str(connection_id).ok()?; + + Some(ConnectionPath(connection_id).into()) +} + +fn parse_ports(components: &[&str]) -> Option { + if components.len() != 2 { + return None; + } + + let first = *components.first()?; + + if first != PORT_PREFIX { + return None; + } + + let port_id = *components.last()?; + + let port_id = PortId::from_str(port_id).ok()?; + + Some(PortPath(port_id).into()) +} + +fn parse_channels(components: &[&str]) -> Option { + if components.len() != 2 { + return None; + } + + let first = *components.first()?; + + if first != CHANNEL_PREFIX { + return None; + } + + let channel_id = *components.last()?; + + let channel_id = ChannelId::from_str(channel_id).ok()?; + + Some(SubPath::Channels(channel_id)) +} + +fn parse_sequences(components: &[&str]) -> Option { + if components.len() != 2 { + return None; + } + + let first = *components.first()?; + + if first != SEQUENCE_PREFIX { + return None; + } + + let sequence_number = *components.last()?; + + match Sequence::from_str(sequence_number) { + Ok(seq) => Some(SubPath::Sequences(seq)), + Err(_) => None, + } +} + +fn parse_channel_ends(components: &[&str]) -> Option { + if components.len() != 5 { + return None; + } + + let first = *components.first()?; + + if first != CHANNEL_END_PREFIX { + return None; + } + + let port = parse_ports(&components[1..=2]); + let channel = parse_channels(&components[3..=4]); + + let Some(Path::Ports(PortPath(port_id))) = port else { + return None; + }; + + let Some(SubPath::Channels(channel_id)) = channel else { + return None; + }; + + Some(ChannelEndPath(port_id, channel_id).into()) +} + +fn parse_seqs(components: &[&str]) -> Option { + if components.len() != 5 { + return None; + } + + let first = *components.first()?; + + let port = parse_ports(&components[1..=2]); + let channel = parse_channels(&components[3..=4]); + + let Some(Path::Ports(PortPath(port_id))) = port else { + return None; + }; + + let Some(SubPath::Channels(channel_id)) = channel else { + return None; + }; + + match first { + NEXT_SEQ_SEND_PREFIX => Some(SeqSendPath(port_id, channel_id).into()), + NEXT_SEQ_RECV_PREFIX => Some(SeqRecvPath(port_id, channel_id).into()), + NEXT_SEQ_ACK_PREFIX => Some(SeqAckPath(port_id, channel_id).into()), + _ => None, + } +} + +fn parse_commitments(components: &[&str]) -> Option { + if components.len() != 7 { + return None; + } + + let first = *components.first()?; + + if first != PACKET_COMMITMENT_PREFIX { + return None; + } + + let port = parse_ports(&components[1..=2]); + let channel = parse_channels(&components[3..=4]); + let sequence = parse_sequences(&components[5..]); + + let Some(Path::Ports(PortPath(port_id))) = port else { + return None; + }; + + let Some(SubPath::Channels(channel_id)) = channel else { + return None; + }; + + let Some(SubPath::Sequences(sequence)) = sequence else { + return None; + }; + + Some( + CommitmentPath { + port_id, + channel_id, + sequence, + } + .into(), + ) +} + +fn parse_acks(components: &[&str]) -> Option { + if components.len() != 7 { + return None; + } + + let first = *components.first()?; + + if first != PACKET_ACK_PREFIX { + return None; + } + + let port = parse_ports(&components[1..=2]); + let channel = parse_channels(&components[3..=4]); + let sequence = parse_sequences(&components[5..]); + + let Some(Path::Ports(PortPath(port_id))) = port else { + return None; + }; + + let Some(SubPath::Channels(channel_id)) = channel else { + return None; + }; + + let Some(SubPath::Sequences(sequence)) = sequence else { + return None; + }; + + Some( + AckPath { + port_id, + channel_id, + sequence, + } + .into(), + ) +} + +fn parse_receipts(components: &[&str]) -> Option { + if components.len() != 7 { + return None; + } + + let first = *components.first()?; + + if first != PACKET_RECEIPT_PREFIX { + return None; + } + + let port = parse_ports(&components[1..=2]); + let channel = parse_channels(&components[3..=4]); + let sequence = parse_sequences(&components[5..]); + + let Some(Path::Ports(PortPath(port_id))) = port else { + return None; + }; + + let Some(SubPath::Channels(channel_id)) = channel else { + return None; + }; + + let Some(SubPath::Sequences(sequence)) = sequence else { + return None; + }; + + Some( + ReceiptPath { + port_id, + channel_id, + sequence, + } + .into(), + ) +} + +fn parse_upgrade_client_state(components: &[&str]) -> Option { + if components.len() != 3 { + return None; + } + + let last = *components.last()?; + + if last != UPGRADED_CLIENT_STATE { + return None; + } + + let upgrade_path = components.first()?.to_string(); + + let height = u64::from_str(components[1]).ok()?; + + Some( + UpgradeClientStatePath { + upgrade_path, + height, + } + .into(), + ) +} + +fn parse_upgrade_consensus_state(components: &[&str]) -> Option { + if components.len() != 3 { + return None; + } + + let last = *components.last()?; + + if last != UPGRADED_CLIENT_CONSENSUS_STATE { + return None; + } + + let upgrade_path = components.first()?.to_string(); + + let height = u64::from_str(components[1]).ok()?; + + Some( + UpgradeConsensusStatePath { + upgrade_path, + height, + } + .into(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + const DEFAULT_CLIENT_ID_STR: &str = "07-tendermint-0"; + impl ClientId { + pub fn new_dummy() -> Self { + ClientId::from_str(DEFAULT_CLIENT_ID_STR) + .expect("should not fail since we use a valid client id") + } + } + #[rstest::rstest] + #[case(NEXT_CLIENT_SEQUENCE, Path::NextClientSequence(NextClientSequencePath))] + #[case( + NEXT_CONNECTION_SEQUENCE, + Path::NextConnectionSequence(NextConnectionSequencePath) + )] + #[case( + NEXT_CHANNEL_SEQUENCE, + Path::NextChannelSequence(NextChannelSequencePath) + )] + #[case( + "clients/07-tendermint-0/clientState", + Path::ClientState(ClientStatePath(ClientId::new_dummy())) + )] + #[case( + "clients/07-tendermint-0/consensusStates/15-31", + Path::ClientConsensusState(ClientConsensusStatePath { + client_id: ClientId::new_dummy(), + revision_number: 15, + revision_height: 31, + }) + )] + #[case( + "clients/07-tendermint-0/consensusStates/15-31/processedTime", + Path::ClientUpdateTime(ClientUpdateTimePath { + client_id: ClientId::new_dummy(), + revision_number: 15, + revision_height: 31, + }) + )] + #[case( + "clients/07-tendermint-0/consensusStates/15-31/processedHeight", + Path::ClientUpdateHeight(ClientUpdateHeightPath { + client_id: ClientId::new_dummy(), + revision_number: 15, + revision_height: 31, + }) + )] + #[case( + "clients/07-tendermint-0/connections", + Path::ClientConnection(ClientConnectionPath(ClientId::new_dummy())) + )] + #[case( + "connections/connection-0", + Path::Connection(ConnectionPath(ConnectionId::zero())) + )] + #[case("ports/transfer", Path::Ports(PortPath(PortId::transfer())))] + #[case( + "channelEnds/ports/transfer/channels/channel-0", + Path::ChannelEnd(ChannelEndPath(PortId::transfer(), ChannelId::zero())) + )] + #[case( + "nextSequenceSend/ports/transfer/channels/channel-0", + Path::SeqSend(SeqSendPath(PortId::transfer(), ChannelId::zero())) + )] + #[case( + "nextSequenceRecv/ports/transfer/channels/channel-0", + Path::SeqRecv(SeqRecvPath(PortId::transfer(), ChannelId::zero())) + )] + #[case( + "nextSequenceAck/ports/transfer/channels/channel-0", + Path::SeqAck(SeqAckPath(PortId::transfer(), ChannelId::zero())) + )] + #[case( + "commitments/ports/transfer/channels/channel-0/sequences/0", + Path::Commitment(CommitmentPath { + port_id: PortId::transfer(), + channel_id: ChannelId::zero(), + sequence: Sequence::from(0), + }) + )] + #[case( + "acks/ports/transfer/channels/channel-0/sequences/0", + Path::Ack(AckPath { + port_id: PortId::transfer(), + channel_id: ChannelId::zero(), + sequence: Sequence::from(0), + }) + )] + #[case( + "receipts/ports/transfer/channels/channel-0/sequences/0", + Path::Receipt(ReceiptPath { + port_id: PortId::transfer(), + channel_id: ChannelId::zero(), + sequence: Sequence::from(0), + }) + )] + #[case( + "upgradedIBCState/0/upgradedClient", + Path::UpgradeClientState(UpgradeClientStatePath { + upgrade_path: UPGRADED_IBC_STATE.to_string(), + height: 0, + }) + )] + #[case( + "upgradedIBCState/0/upgradedConsState", + Path::UpgradeConsensusState(UpgradeConsensusStatePath { + upgrade_path: UPGRADED_IBC_STATE.to_string(), + height: 0, + }) + )] + fn test_successful_parsing(#[case] path_str: &str, #[case] path: Path) { + // can be parsed into Path + assert_eq!(Path::from_str(path_str).expect("no error"), path); + // can be converted back to string + assert_eq!(path_str, path.to_string()); + } + + #[rstest::rstest] + #[case("clients/clientType")] + #[case("channels/channel-0")] + #[case("sequences/0")] + fn test_failure_parsing(#[case] path_str: &str) { + // cannot be parsed into Path + assert!(Path::from_str(path_str).is_err()); + } + + #[test] + fn test_parse_client_paths_fn() { + let path = "clients/07-tendermint-0/clientState"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_client_paths(&components), + Some(Path::ClientState(ClientStatePath(ClientId::new_dummy()))) + ); + + let path = "clients/07-tendermint-0/consensusStates/15-31"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_client_paths(&components), + Some(Path::ClientConsensusState(ClientConsensusStatePath { + client_id: ClientId::new_dummy(), + revision_number: 15, + revision_height: 31, + })) + ); + } + + #[test] + fn test_parse_client_update_paths_fn() { + let path = "clients/07-tendermint-0/consensusStates/15-31/processedTime"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_client_paths(&components), + Some(Path::ClientUpdateTime(ClientUpdateTimePath { + client_id: ClientId::new_dummy(), + revision_number: 15, + revision_height: 31, + })) + ); + + let path = "clients/07-tendermint-0/consensusStates/15-31/processedHeight"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_client_paths(&components), + Some(Path::ClientUpdateHeight(ClientUpdateHeightPath { + client_id: ClientId::new_dummy(), + revision_number: 15, + revision_height: 31, + })) + ); + } + + #[test] + fn test_parse_connections_fn() { + let path = "connections/connection-0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_connections(&components), + Some(Path::Connection(ConnectionPath(ConnectionId::zero()))), + ); + } + + #[test] + fn test_parse_ports_fn() { + let path = "ports/transfer"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_ports(&components), + Some(Path::Ports(PortPath(PortId::transfer()))), + ); + } + + #[test] + fn test_parse_channels_fn() { + let path = "channels/channel-0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_channels(&components), + Some(SubPath::Channels(ChannelId::zero())), + ); + } + + #[test] + fn test_parse_sequences_fn() { + let path = "sequences/0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_sequences(&components), + Some(SubPath::Sequences(Sequence::from(0))) + ); + } + + #[test] + fn test_parse_channel_ends_fn() { + let path = "channelEnds/ports/transfer/channels/channel-0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_channel_ends(&components), + Some(Path::ChannelEnd(ChannelEndPath( + PortId::transfer(), + ChannelId::zero() + ))), + ); + } + + #[test] + fn test_parse_seqs_fn() { + let path = "nextSequenceSend/ports/transfer/channels/channel-0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_seqs(&components), + Some(Path::SeqSend(SeqSendPath( + PortId::transfer(), + ChannelId::zero() + ))), + ); + + let path = "nextSequenceRecv/ports/transfer/channels/channel-0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_seqs(&components), + Some(Path::SeqRecv(SeqRecvPath( + PortId::transfer(), + ChannelId::zero() + ))), + ); + + let path = "nextSequenceAck/ports/transfer/channels/channel-0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_seqs(&components), + Some(Path::SeqAck(SeqAckPath( + PortId::transfer(), + ChannelId::zero() + ))), + ); + } + + #[test] + fn test_parse_commitments_fn() { + let path = "commitments/ports/transfer/channels/channel-0/sequences/0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_commitments(&components), + Some(Path::Commitment(CommitmentPath { + port_id: PortId::transfer(), + channel_id: ChannelId::zero(), + sequence: Sequence::from(0), + })), + ); + } + + #[test] + fn test_parse_acks_fn() { + let path = "acks/ports/transfer/channels/channel-0/sequences/0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_acks(&components), + Some(Path::Ack(AckPath { + port_id: PortId::transfer(), + channel_id: ChannelId::zero(), + sequence: Sequence::from(0), + })), + ); + } + + #[test] + fn test_parse_receipts_fn() { + let path = "receipts/ports/transfer/channels/channel-0/sequences/0"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_receipts(&components), + Some(Path::Receipt(ReceiptPath { + port_id: PortId::transfer(), + channel_id: ChannelId::zero(), + sequence: Sequence::from(0), + })), + ); + } + + #[test] + fn test_parse_upgrade_client_state_fn() { + let path = "upgradedIBCState/0/upgradedClient"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_upgrade_client_state(&components), + Some(Path::UpgradeClientState(UpgradeClientStatePath { + upgrade_path: UPGRADED_IBC_STATE.to_string(), + height: 0, + })), + ); + } + + #[test] + fn test_parse_upgrade_consensus_state_fn() { + let path = "upgradedIBCState/0/upgradedConsState"; + let components: Vec<&str> = path.split('/').collect(); + + assert_eq!( + parse_upgrade_consensus_state(&components), + Some(Path::UpgradeConsensusState(UpgradeConsensusStatePath { + upgrade_path: UPGRADED_IBC_STATE.to_string(), + height: 0, + })), + ) + } +} diff --git a/ibc-eureka-core/ics24-host/types/src/validate.rs b/ibc-eureka-core/ics24-host/types/src/validate.rs new file mode 100644 index 0000000000..614cfd8a42 --- /dev/null +++ b/ibc-eureka-core/ics24-host/types/src/validate.rs @@ -0,0 +1,319 @@ +use ibc_primitives::prelude::*; + +use crate::error::IdentifierError as Error; +use crate::identifiers::{ChannelId, ConnectionId}; + +const VALID_SPECIAL_CHARS: &str = "._+-#[]<>"; + +/// Checks if the identifier only contains valid characters as specified in the +/// [`ICS-24`](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#paths-identifiers-separators)] +/// spec. +pub fn validate_identifier_chars(id: &str) -> Result<(), Error> { + // Check that the identifier comprises only valid characters: + // - Alphanumeric + // - `.`, `_`, `+`, `-`, `#` + // - `[`, `]`, `<`, `>` + if !id + .chars() + .all(|c| c.is_alphanumeric() || VALID_SPECIAL_CHARS.contains(c)) + { + return Err(Error::InvalidCharacter(id.into())); + } + + // All good! + Ok(()) +} + +/// Checks if the identifier forms a valid identifier with the given min/max length as specified in the +/// [`ICS-24`](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#paths-identifiers-separators)] +/// spec. +pub fn validate_identifier_length(id: &str, min: u64, max: u64) -> Result<(), Error> { + // Make sure min is at least one so we reject empty identifiers. + let min = min.max(1); + let length = id.len() as u64; + if (min..=max).contains(&length) { + Ok(()) + } else { + Err(Error::InvalidLength { + actual: id.into(), + min, + max, + }) + } +} + +/// Checks if a prefix forms a valid identifier with the given min/max identifier's length. +/// The prefix must be between `min_id_length - 2`, considering `u64::MIN` (1 char) and "-" +/// and `max_id_length - 21` characters, considering `u64::MAX` (20 chars) and "-". +pub fn validate_prefix_length( + prefix: &str, + min_id_length: u64, + max_id_length: u64, +) -> Result<(), Error> { + // Prefix must be at least `min_id_length - 2` characters long since the + // shortest identifier we can construct is `{prefix}-0` which extends prefix + // by 2 characters. + let min = min_id_length.saturating_sub(2); + // Prefix must be at most `max_id_length - 21` characters long since the + // longest identifier we can construct is `{prefix}-{u64::MAX}` which + // extends prefix by 21 characters. + let max = max_id_length.saturating_sub(21); + + validate_identifier_length(prefix, min, max) +} + +/// Checks if the identifier is a valid named u64 index: {name}-{u64}. +/// Example: "connection-0", "connection-100", "channel-0", "channel-100". +pub fn validate_named_u64_index(id: &str, name: &str) -> Result<(), Error> { + let number_s = id + .strip_prefix(name) + .ok_or_else(|| Error::InvalidPrefix(id.into()))? + .strip_prefix('-') + .ok_or_else(|| Error::InvalidPrefix(id.into()))?; + + if number_s.starts_with('0') && number_s.len() > 1 { + return Err(Error::InvalidPrefix(id.into())); + } + + _ = number_s.parse::().map_err(|e| Error::FailedToParse { + description: format!("named index `{id}`: {e}"), + })?; + + Ok(()) +} + +/// Default validator function for the Client types. +pub fn validate_client_type(id: &str) -> Result<(), Error> { + validate_identifier_chars(id)?; + validate_prefix_length(id, 9, 64) +} + +/// Default validator function for Client identifiers. +/// +/// A valid client identifier must be between 9-64 characters, as specified in +/// the ICS-24 spec. +pub fn validate_client_identifier(id: &str) -> Result<(), Error> { + validate_identifier_chars(id)?; + validate_identifier_length(id, 9, 64) +} + +/// Default validator function for Connection identifiers. +/// +/// A valid connection identifier must be between 10-64 characters, as specified +/// in the ICS-24 spec. +pub fn validate_connection_identifier(id: &str) -> Result<(), Error> { + validate_identifier_chars(id)?; + validate_identifier_length(id, 10, 64)?; + validate_named_u64_index(id, ConnectionId::prefix())?; + Ok(()) +} + +/// Default validator function for Port identifiers. +/// +/// A valid port identifier must be between 2-128 characters, as specified in the +/// ICS-24 spec. +pub fn validate_port_identifier(id: &str) -> Result<(), Error> { + validate_identifier_chars(id)?; + validate_identifier_length(id, 2, 128) +} + +/// Default validator function for Channel identifiers. +/// +/// A valid channel identifier must be between 8-64 characters, as specified in +/// the ICS-24 spec. +pub fn validate_channel_identifier(id: &str) -> Result<(), Error> { + validate_identifier_chars(id)?; + validate_identifier_length(id, 8, 64)?; + validate_named_u64_index(id, ChannelId::prefix())?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + + use super::*; + + #[test] + fn parse_invalid_port_id_min() { + // invalid min port id + let id = validate_port_identifier("p"); + assert!(id.is_err()) + } + + #[test] + fn parse_invalid_port_id_max() { + // invalid max port id (test string length is 130 chars) + let id = validate_port_identifier( + "9anxkcme6je544d5lnj46zqiiiygfqzf8w4bjecbnyj4lj6s7zlpst67yln64tixp9anxkcme6je544d5lnj46zqiiiygfqzf8w4bjecbnyj4lj6s7zlpst67yln64tixp", + ); + assert!(id.is_err()) + } + + #[test] + fn parse_invalid_connection_id_min() { + // invalid min connection id + let id = validate_connection_identifier("connect01"); + assert!(id.is_err()) + } + + #[test] + fn parse_connection_id_max() { + // invalid max connection id (test string length is 65) + let id = validate_connection_identifier( + "ihhankr30iy4nna65hjl2wjod7182io1t2s7u3ip3wqtbbn1sl0rgcntqc540r36r", + ); + assert!(id.is_err()) + } + + #[test] + fn parse_invalid_connection_id_indexed() { + // valid connection id with index + validate_connection_identifier("connection-0").expect("success"); + validate_connection_identifier("connection-123").expect("success"); + validate_connection_identifier("connection-18446744073709551615").expect("success"); + } + + #[test] + fn parse_invalid_connection_id_non_indexed() { + // invalid indexing for connection id + validate_connection_identifier("connection-0123").expect_err("failure"); + validate_connection_identifier("connection0123").expect_err("failure"); + validate_connection_identifier("connection000").expect_err("failure"); + // 1 << 64 = 18446744073709551616 + validate_connection_identifier("connection-18446744073709551616").expect_err("failure"); + } + + #[test] + fn parse_invalid_channel_id_min() { + // invalid channel id, must be at least 8 characters + let id = validate_channel_identifier("channel"); + assert!(id.is_err()) + } + + #[test] + fn parse_channel_id_max() { + // invalid channel id (test string length is 65) + let id = validate_channel_identifier( + "ihhankr30iy4nna65hjl2wjod7182io1t2s7u3ip3wqtbbn1sl0rgcntqc540r36r", + ); + assert!(id.is_err()) + } + + #[test] + fn parse_invalid_channel_id_indexed() { + // valid channel id with index + validate_channel_identifier("channel-0").expect("success"); + validate_channel_identifier("channel-123").expect("success"); + validate_channel_identifier("channel-18446744073709551615").expect("success"); + } + + #[test] + fn parse_invalid_channel_id_non_indexed() { + // invalid indexing for channel id + validate_channel_identifier("channel-0123").expect_err("failure"); + validate_channel_identifier("channel0123").expect_err("failure"); + validate_channel_identifier("channel000").expect_err("failure"); + // 1 << 64 = 18446744073709551616 + validate_channel_identifier("channel-18446744073709551616").expect_err("failure"); + } + + #[test] + fn parse_invalid_client_id_min() { + // invalid min client id + let id = validate_client_identifier("client"); + assert!(id.is_err()) + } + + #[test] + fn parse_client_id_max() { + // invalid max client id (test string length is 65) + let id = validate_client_identifier( + "f0isrs5enif9e4td3r2jcbxoevhz6u1fthn4aforq7ams52jn5m48eiesfht9ckpn", + ); + assert!(id.is_err()) + } + + #[test] + fn parse_invalid_id_chars() { + // invalid id chars + let id = validate_identifier_chars("channel@01"); + assert!(id.is_err()) + } + + #[test] + fn validate_chars_empty_id() { + // validate_identifier_chars allows empty identifiers + assert!(validate_identifier_chars("").is_ok()); + } + + #[test] + fn validate_length_empty_id() { + // validate_identifier_length does not allow empty identifiers + assert!(validate_identifier_length("", 0, 64).is_err()); + } + + #[test] + fn validate_min_gt_max_constraints() { + // validate_identifier_length rejects the id if min > max. + assert!(validate_identifier_length("foobar", 5, 3).is_err()); + } + + #[test] + fn parse_invalid_id_path_separator() { + // invalid id with path separator + let id = validate_identifier_chars("id/1"); + assert!(id.is_err()) + } + + #[test] + fn parse_healthy_client_type() { + let id = validate_client_type("07-tendermint"); + assert!(id.is_ok()) + } + + #[test] + fn parse_invalid_short_client_type() { + let id = validate_client_type("<7Char"); + assert!(id.is_err()) + } + + #[test] + fn parse_invalid_lengthy_client_type() { + let id = validate_client_type("InvalidClientTypeWithLengthOfClientId>65Char"); + assert!(id.is_err()) + } + + #[rstest] + #[case::zero_min_length("", 0, 64, false)] + #[case::empty_prefix("", 1, 64, false)] + #[case::max_is_low("a", 1, 10, false)] + #[case::min_greater_than_max("foobar", 5, 3, false)] + #[case::u64_max_is_too_big("a", 3, 21, false)] + #[case::u64_min_is_too_small("a", 4, 22, false)] + #[case::u64_min_max_boundary("a", 3, 22, true)] + #[case("chainA", 1, 32, true)] + #[case("chainA", 1, 64, true)] + fn test_prefix_length_validation( + #[case] prefix: &str, + #[case] min: u64, + #[case] max: u64, + #[case] success: bool, + ) { + let result = validate_prefix_length(prefix, min, max); + assert_eq!(result.is_ok(), success); + } + + #[rstest] + #[case::zero_padded("channel", "001", false)] + #[case::only_zero("connection", "000", false)] + #[case::zero("channel", "0", true)] + #[case::one("connection", "1", true)] + #[case::n1234("channel", "1234", true)] + #[case::u64_max("chan", "18446744073709551615", true)] + #[case::u64_max_plus_1("chan", "18446744073709551616", false)] + fn test_named_index_validation(#[case] name: &str, #[case] id: &str, #[case] success: bool) { + let result = validate_named_u64_index(format!("{name}-{id}").as_str(), name); + assert_eq!(result.is_ok(), success, "{result:?}"); + } +} diff --git a/ibc-eureka-core/ics25-handler/Cargo.toml b/ibc-eureka-core/ics25-handler/Cargo.toml new file mode 100644 index 0000000000..43dcf44ed1 --- /dev/null +++ b/ibc-eureka-core/ics25-handler/Cargo.toml @@ -0,0 +1,83 @@ +[package] +name = "ibc-core-handler" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "handler" ] +readme = "./../README.md" + +description = """ + Maintained by `ibc-rs`, exposes IBC handler entry points for an integrated IBC core modules. + These entry points are responsible for processing incoming IBC messages, performing validation, + and execution logic by invoking the appropriate module handler. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ibc-core-client = { workspace = true } +ibc-core-connection = { workspace = true } +ibc-core-channel = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-router = { workspace = true } +ibc-core-handler-types = { workspace = true } +ibc-core-host = { workspace = true } +ibc-primitives = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "ibc-core-client/std", + "ibc-core-connection/std", + "ibc-core-channel/std", + "ibc-core-commitment-types/std", + "ibc-core-host/std", + "ibc-core-router/std", + "ibc-core-handler-types/std", + "ibc-primitives/std", +] +serde = [ + "ibc-core-client/serde", + "ibc-core-connection/serde", + "ibc-core-channel/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host/serde", + "ibc-core-router/serde", + "ibc-core-handler-types/serde", + "ibc-primitives/serde", +] +borsh = [ + "ibc-core-client/borsh", + "ibc-core-connection/borsh", + "ibc-core-channel/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host/borsh", + "ibc-core-router/borsh", + "ibc-core-handler-types/borsh", + "ibc-primitives/borsh", +] +schema = [ + "ibc-core-client/schema", + "ibc-core-connection/schema", + "ibc-core-channel/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host/schema", + "ibc-core-router/schema", + "ibc-core-handler-types/schema", + "ibc-primitives/schema", + "serde", + "std", +] +parity-scale-codec = [ + "ibc-core-client/parity-scale-codec", + "ibc-core-connection/parity-scale-codec", + "ibc-core-channel/parity-scale-codec", + "ibc-core-host/parity-scale-codec", + "ibc-core-router/parity-scale-codec", + "ibc-core-handler-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics25-handler/src/entrypoint.rs b/ibc-eureka-core/ics25-handler/src/entrypoint.rs new file mode 100644 index 0000000000..f9a0cabc32 --- /dev/null +++ b/ibc-eureka-core/ics25-handler/src/entrypoint.rs @@ -0,0 +1,195 @@ +use ibc_core_channel::handler::{ + acknowledgement_packet_execute, acknowledgement_packet_validate, chan_close_confirm_execute, + chan_close_confirm_validate, chan_close_init_execute, chan_close_init_validate, + chan_open_ack_execute, chan_open_ack_validate, chan_open_confirm_execute, + chan_open_confirm_validate, chan_open_init_execute, chan_open_init_validate, + chan_open_try_execute, chan_open_try_validate, recv_packet_execute, recv_packet_validate, + timeout_packet_execute, timeout_packet_validate, TimeoutMsgType, +}; +use ibc_core_channel::types::msgs::{ + channel_msg_to_port_id, packet_msg_to_port_id, ChannelMsg, PacketMsg, +}; +use ibc_core_client::context::{ClientExecutionContext, ClientValidationContext}; +use ibc_core_client::handler::{create_client, update_client, upgrade_client}; +use ibc_core_client::types::error::ClientError; +use ibc_core_client::types::msgs::{ClientMsg, MsgUpdateOrMisbehaviour}; +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::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 +pub fn dispatch( + ctx: &mut Ctx, + router: &mut impl Router, + msg: MsgEnvelope, +) -> Result<(), HandlerError> +where + Ctx: ExecutionContext, + <::ClientStateRef as TryFrom>::Error: Into, + <::ClientStateMut as TryFrom>::Error: Into, + >::Error: Into, +{ + validate(ctx, router, msg.clone())?; + execute(ctx, router, msg) +} + +/// Entrypoint which only performs message validation +/// +/// If a transaction contains `n` messages `m_1` ... `m_n`, then +/// they MUST be processed as follows: +/// validate(m_1), execute(m_1), ..., validate(m_n), execute(m_n) +/// 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<(), HandlerError> +where + Ctx: ValidationContext, + <::ClientStateRef as TryFrom>::Error: Into, + >::Error: Into, +{ + match msg { + MsgEnvelope::Client(msg) => match msg { + ClientMsg::CreateClient(msg) => create_client::validate(ctx, msg)?, + ClientMsg::UpdateClient(msg) => { + update_client::validate(ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg))? + } + ClientMsg::Misbehaviour(msg) => { + update_client::validate(ctx, MsgUpdateOrMisbehaviour::Misbehaviour(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 + } + }, + 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)?, + }, + MsgEnvelope::Channel(msg) => { + let port_id = channel_msg_to_port_id(&msg); + 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::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)?, + } + } + MsgEnvelope::Packet(msg) => { + let port_id = packet_msg_to_port_id(&msg); + 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::MissingModule)?; + + match 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))? + } + PacketMsg::TimeoutOnClose(msg) => { + timeout_packet_validate(ctx, module, TimeoutMsgType::TimeoutOnClose(msg))? + } + } + } + }; + + Ok(()) +} + +/// Entrypoint which only performs message execution +pub fn execute( + ctx: &mut Ctx, + router: &mut impl Router, + msg: MsgEnvelope, +) -> 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::UpdateClient(msg) => { + update_client::execute(ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg))? + } + ClientMsg::Misbehaviour(msg) => { + update_client::execute(ctx, MsgUpdateOrMisbehaviour::Misbehaviour(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 + } + }, + 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)?, + }, + MsgEnvelope::Channel(msg) => { + let port_id = channel_msg_to_port_id(&msg); + 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::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)?, + } + } + MsgEnvelope::Packet(msg) => { + let port_id = packet_msg_to_port_id(&msg); + 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::MissingModule)?; + + match 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))? + } + PacketMsg::TimeoutOnClose(msg) => { + timeout_packet_execute(ctx, module, TimeoutMsgType::TimeoutOnClose(msg))? + } + } + } + } + + Ok(()) +} diff --git a/ibc-eureka-core/ics25-handler/src/lib.rs b/ibc-eureka-core/ics25-handler/src/lib.rs new file mode 100644 index 0000000000..783e8e6283 --- /dev/null +++ b/ibc-eureka-core/ics25-handler/src/lib.rs @@ -0,0 +1,33 @@ +//! Exposes IBC handler entry points for an integrated IBC core modules. These +//! entry points are responsible for processing incoming IBC messages, +//! performing validation, and execution logics by invoking the appropriate +//! module handler. +//! +//! When processing a given message `M`, if any method in this library returns +//! an error, the runtime is expected to rollback all state modifications made +//! to the context (e.g. [`ExecutionContext`](ibc_core_host::ExecutionContext)) +//! while processing `M`. If the transaction containing `M` consists of multiple +//! messages, then typically the state modifications from all messages are +//! expected to be rolled back as well. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(any(test, feature = "std"))] +extern crate std; + +pub mod entrypoint; + +/// Re-export IBC handler types from `ibc-core-handler-types` crate. +pub mod types { + #[doc(inline)] + pub use ibc_core_handler_types::*; +} diff --git a/ibc-eureka-core/ics25-handler/types/Cargo.toml b/ibc-eureka-core/ics25-handler/types/Cargo.toml new file mode 100644 index 0000000000..8e51544933 --- /dev/null +++ b/ibc-eureka-core/ics25-handler/types/Cargo.toml @@ -0,0 +1,109 @@ +[package] +name = "ibc-core-handler-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "handler", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-25 Handler Interface data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +serde = { workspace = true, optional = true } +schemars = { workspace = true, optional = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-client-types = { workspace = true } +ibc-core-connection-types = { workspace = true } +ibc-core-channel-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-core-router-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "serde/std", + "subtle-encoding/std", + "ibc-core-client-types/std", + "ibc-core-connection-types/std", + "ibc-core-channel-types/std", + "ibc-core-commitment-types/std", + "ibc-core-host-types/std", + "ibc-core-router-types/std", + "ibc-primitives/std", + "ibc-proto/std", + "tendermint/std", +] +serde = [ + "dep:serde", + "ibc-core-client-types/serde", + "ibc-core-connection-types/serde", + "ibc-core-channel-types/serde", + "ibc-core-commitment-types/serde", + "ibc-core-host-types/serde", + "ibc-core-router-types/serde", + "ibc-primitives/serde", + "ibc-proto/serde", +] +schema = [ + "dep:schemars", + "ibc-core-client-types/schema", + "ibc-core-connection-types/schema", + "ibc-core-channel-types/schema", + "ibc-core-commitment-types/schema", + "ibc-core-host-types/schema", + "ibc-core-router-types/schema", + "ibc-primitives/schema", + "ibc-proto/json-schema", + "serde", + "std", +] +borsh = [ + "dep:borsh", + "ibc-core-client-types/borsh", + "ibc-core-connection-types/borsh", + "ibc-core-channel-types/borsh", + "ibc-core-commitment-types/borsh", + "ibc-core-host-types/borsh", + "ibc-core-router-types/borsh", + "ibc-primitives/borsh", + "ibc-proto/borsh", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-core-client-types/parity-scale-codec", + "ibc-core-connection-types/parity-scale-codec", + "ibc-core-channel-types/parity-scale-codec", + "ibc-core-commitment-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-core-router-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics25-handler/types/src/error.rs b/ibc-eureka-core/ics25-handler/types/src/error.rs new file mode 100644 index 0000000000..fce14adc9c --- /dev/null +++ b/ibc-eureka-core/ics25-handler/types/src/error.rs @@ -0,0 +1,34 @@ +//! Defines the handler error type + +use derive_more::From; +use displaydoc::Display; +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 type that surfaces errors from the core ibc-rs crates. +#[derive(Debug, Display, From)] +pub enum HandlerError { + /// ICS02 Client error: {0} + Client(ClientError), + /// ICS03 Connection error: {0} + Connection(ConnectionError), + /// ICS04 Channel error: {0} + Channel(ChannelError), + /// ICS26 Routing error: {0} + Router(RouterError), +} + +#[cfg(feature = "std")] +impl std::error::Error for HandlerError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Client(e) => Some(e), + Self::Connection(e) => Some(e), + Self::Channel(e) => Some(e), + Self::Router(e) => Some(e), + } + } +} diff --git a/ibc-eureka-core/ics25-handler/types/src/events.rs b/ibc-eureka-core/ics25-handler/types/src/events.rs new file mode 100644 index 0000000000..77ebe79e1a --- /dev/null +++ b/ibc-eureka-core/ics25-handler/types/src/events.rs @@ -0,0 +1,170 @@ +//! Defines events emitted during handling of IBC messages + +use ibc_core_channel_types::events as ChannelEvents; +use ibc_core_client_types::events::{self as ClientEvents}; +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 tendermint::abci; + +const MESSAGE_EVENT: &str = "message"; + +/// Events created by the IBC component of a chain, destined for a relayer. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum IbcEvent { + CreateClient(ClientEvents::CreateClient), + UpdateClient(ClientEvents::UpdateClient), + UpgradeClient(ClientEvents::UpgradeClient), + ClientMisbehaviour(ClientEvents::ClientMisbehaviour), + + OpenInitConnection(ConnectionEvents::OpenInit), + OpenTryConnection(ConnectionEvents::OpenTry), + OpenAckConnection(ConnectionEvents::OpenAck), + OpenConfirmConnection(ConnectionEvents::OpenConfirm), + + OpenInitChannel(ChannelEvents::OpenInit), + OpenTryChannel(ChannelEvents::OpenTry), + OpenAckChannel(ChannelEvents::OpenAck), + OpenConfirmChannel(ChannelEvents::OpenConfirm), + CloseInitChannel(ChannelEvents::CloseInit), + CloseConfirmChannel(ChannelEvents::CloseConfirm), + + SendPacket(ChannelEvents::SendPacket), + ReceivePacket(ChannelEvents::ReceivePacket), + WriteAcknowledgement(ChannelEvents::WriteAcknowledgement), + AcknowledgePacket(ChannelEvents::AcknowledgePacket), + TimeoutPacket(ChannelEvents::TimeoutPacket), + ChannelClosed(ChannelEvents::ChannelClosed), + + Module(ModuleEvent), + Message(MessageEvent), +} + +impl TryFrom for abci::Event { + type Error = DecodingError; + + fn try_from(event: IbcEvent) -> Result { + Ok(match event { + IbcEvent::CreateClient(event) => event.into(), + IbcEvent::UpdateClient(event) => event.into(), + IbcEvent::UpgradeClient(event) => event.into(), + IbcEvent::ClientMisbehaviour(event) => event.into(), + IbcEvent::OpenInitConnection(event) => event.into(), + IbcEvent::OpenTryConnection(event) => event.into(), + IbcEvent::OpenAckConnection(event) => event.into(), + IbcEvent::OpenConfirmConnection(event) => event.into(), + IbcEvent::OpenInitChannel(event) => event.into(), + IbcEvent::OpenTryChannel(event) => event.into(), + IbcEvent::OpenAckChannel(event) => event.into(), + IbcEvent::OpenConfirmChannel(event) => event.into(), + IbcEvent::CloseInitChannel(event) => event.into(), + IbcEvent::CloseConfirmChannel(event) => event.into(), + 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 { + kind: MESSAGE_EVENT.to_string(), + attributes: vec![("module", event.module_attribute(), true).into()], + }, + }) + } +} + +impl IbcEvent { + pub fn event_type(&self) -> &str { + match self { + IbcEvent::CreateClient(event) => event.event_type(), + IbcEvent::UpdateClient(event) => event.event_type(), + IbcEvent::ClientMisbehaviour(event) => event.event_type(), + IbcEvent::UpgradeClient(event) => event.event_type(), + IbcEvent::OpenInitConnection(event) => event.event_type(), + IbcEvent::OpenTryConnection(event) => event.event_type(), + IbcEvent::OpenAckConnection(event) => event.event_type(), + IbcEvent::OpenConfirmConnection(event) => event.event_type(), + IbcEvent::OpenInitChannel(event) => event.event_type(), + IbcEvent::OpenTryChannel(event) => event.event_type(), + IbcEvent::OpenAckChannel(event) => event.event_type(), + IbcEvent::OpenConfirmChannel(event) => event.event_type(), + IbcEvent::CloseInitChannel(event) => event.event_type(), + IbcEvent::CloseConfirmChannel(event) => event.event_type(), + IbcEvent::SendPacket(event) => event.event_type(), + IbcEvent::ReceivePacket(event) => event.event_type(), + IbcEvent::WriteAcknowledgement(event) => event.event_type(), + IbcEvent::AcknowledgePacket(event) => event.event_type(), + IbcEvent::TimeoutPacket(event) => event.event_type(), + IbcEvent::ChannelClosed(event) => event.event_type(), + IbcEvent::Module(module_event) => module_event.kind.as_str(), + IbcEvent::Message(_) => MESSAGE_EVENT, + } + } +} + +/// An event type that is emitted by the Cosmos SDK. +/// +/// We need to emit it as well, as currently [hermes] relies on it. +/// +/// [hermes]: https://github.com/informalsystems/hermes +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MessageEvent { + Client, + Connection, + Channel, + // stores the module name + Module(String), +} + +impl MessageEvent { + /// The ABCI event attribute has only one attribute, with key `module`. + /// This method gets the associated value. + pub fn module_attribute(&self) -> String { + match self { + MessageEvent::Client => "ibc_client".to_string(), + MessageEvent::Connection => "ibc_connection".to_string(), + MessageEvent::Channel => "ibc_channel".to_string(), + MessageEvent::Module(module_name) => module_name.clone(), + } + } +} + +impl From for IbcEvent { + fn from(e: MessageEvent) -> Self { + IbcEvent::Message(e) + } +} + +impl From for IbcEvent { + fn from(e: ModuleEvent) -> Self { + IbcEvent::Module(e) + } +} diff --git a/ibc-eureka-core/ics25-handler/types/src/lib.rs b/ibc-eureka-core/ics25-handler/types/src/lib.rs new file mode 100644 index 0000000000..c30845afca --- /dev/null +++ b/ibc-eureka-core/ics25-handler/types/src/lib.rs @@ -0,0 +1,21 @@ +//! Encapsulates essential data structures, facilitating the seamless +//! interaction between an implemented IBC module using ibc-rs and the +//! underlying host blockchain. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod error; +pub mod events; +pub mod msgs; diff --git a/ibc-eureka-core/ics25-handler/types/src/msgs.rs b/ibc-eureka-core/ics25-handler/types/src/msgs.rs new file mode 100644 index 0000000000..a47a9b7021 --- /dev/null +++ b/ibc-eureka-core/ics25-handler/types/src/msgs.rs @@ -0,0 +1,130 @@ +use ibc_core_channel_types::msgs::{ + ChannelMsg, MsgAcknowledgement, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck, + MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, MsgRecvPacket, MsgTimeout, + MsgTimeoutOnClose, PacketMsg, ACKNOWLEDGEMENT_TYPE_URL, CHAN_CLOSE_CONFIRM_TYPE_URL, + CHAN_CLOSE_INIT_TYPE_URL, CHAN_OPEN_ACK_TYPE_URL, CHAN_OPEN_CONFIRM_TYPE_URL, + CHAN_OPEN_INIT_TYPE_URL, CHAN_OPEN_TRY_TYPE_URL, RECV_PACKET_TYPE_URL, + TIMEOUT_ON_CLOSE_TYPE_URL, TIMEOUT_TYPE_URL, +}; +#[allow(deprecated)] +use ibc_core_client_types::msgs::{ + ClientMsg, MsgCreateClient, MsgSubmitMisbehaviour, MsgUpdateClient, MsgUpgradeClient, + CREATE_CLIENT_TYPE_URL, SUBMIT_MISBEHAVIOUR_TYPE_URL, UPDATE_CLIENT_TYPE_URL, + UPGRADE_CLIENT_TYPE_URL, +}; +use ibc_core_connection_types::msgs::{ + ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, + MsgConnectionOpenTry, CONN_OPEN_ACK_TYPE_URL, CONN_OPEN_CONFIRM_TYPE_URL, + CONN_OPEN_INIT_TYPE_URL, CONN_OPEN_TRY_TYPE_URL, +}; +use ibc_core_host_types::error::DecodingError; +use ibc_primitives::prelude::*; +use ibc_proto::google::protobuf::Any; +use ibc_proto::Protobuf; + +/// Enumeration of all messages that the local ICS26 module is capable of routing. +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Clone, Debug, PartialEq, Eq, derive_more::From)] +pub enum MsgEnvelope { + Client(ClientMsg), + Connection(ConnectionMsg), + Channel(ChannelMsg), + Packet(PacketMsg), +} + +#[allow(deprecated)] +impl TryFrom for MsgEnvelope { + 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)?; + Ok(MsgEnvelope::Client(ClientMsg::CreateClient(domain_msg))) + } + UPDATE_CLIENT_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Client(ClientMsg::UpgradeClient(domain_msg))) + } + SUBMIT_MISBEHAVIOUR_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Connection(ConnectionMsg::OpenInit(domain_msg))) + } + CONN_OPEN_TRY_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Connection(ConnectionMsg::OpenAck(domain_msg))) + } + CONN_OPEN_CONFIRM_TYPE_URL => { + let domain_msg = MsgConnectionOpenConfirm::decode_vec(&any_msg.value)?; + Ok(MsgEnvelope::Connection(ConnectionMsg::OpenConfirm( + domain_msg, + ))) + } + + // ICS04 channel messages + CHAN_OPEN_INIT_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Channel(ChannelMsg::OpenTry(domain_msg))) + } + CHAN_OPEN_ACK_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Channel(ChannelMsg::OpenConfirm(domain_msg))) + } + CHAN_CLOSE_INIT_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Channel(ChannelMsg::CloseConfirm(domain_msg))) + } + // ICS04 packet messages + RECV_PACKET_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Packet(PacketMsg::Ack(domain_msg))) + } + TIMEOUT_TYPE_URL => { + 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)?; + Ok(MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(domain_msg))) + } + + _ => Err(DecodingError::UnknownTypeUrl(any_msg.type_url))?, + } + } +} diff --git a/ibc-eureka-core/ics26-routing/Cargo.toml b/ibc-eureka-core/ics26-routing/Cargo.toml new file mode 100644 index 0000000000..cf809acf14 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "ibc-core-router" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "routing" ] +readme = "./../README.md" + +description = """ + Maintained by `ibc-rs`, contains necessary traits to implement the routing and callback functionality + (IBC router module) used for connecting the application layer to the transport layer of an IBC enabled chain. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +derive_more = { workspace = true } +displaydoc = { workspace = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-primitives = { workspace = true } +ibc-core-channel-types = { workspace = true } +ibc-core-host-types = { workspace = true } +ibc-core-router-types = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "subtle-encoding/std", + "ibc-primitives/std", + "ibc-core-channel-types/std", + "ibc-core-host-types/std", + "ibc-core-router-types/std", +] +serde = [ + "ibc-primitives/serde", + "ibc-core-channel-types/serde", + "ibc-core-host-types/serde", + "ibc-core-router-types/serde", +] +borsh = [ + "ibc-primitives/borsh", + "ibc-core-channel-types/borsh", + "ibc-core-host-types/borsh", + "ibc-core-router-types/borsh", +] +schema = [ + "ibc-core-channel-types/schema", + "ibc-core-host-types/schema", + "ibc-core-router-types/schema", + "ibc-primitives/schema", + "serde", + "std", +] +parity-scale-codec = [ + "ibc-core-channel-types/parity-scale-codec", + "ibc-core-host-types/parity-scale-codec", + "ibc-core-router-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics26-routing/src/lib.rs b/ibc-eureka-core/ics26-routing/src/lib.rs new file mode 100644 index 0000000000..b5b585ee59 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/src/lib.rs @@ -0,0 +1,24 @@ +//! This library contains necessary traits to implement an IBC router module when integrating with `ibc-rs`. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(feature = "std")] +extern crate std; + +pub mod module; +pub mod router; + +/// Re-exports router data structures from the `ibc-core-router-types` crate +pub mod types { + #[doc(inline)] + pub use ibc_core_router_types::*; +} diff --git a/ibc-eureka-core/ics26-routing/src/module.rs b/ibc-eureka-core/ics26-routing/src/module.rs new file mode 100644 index 0000000000..a0877b98d7 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/src/module.rs @@ -0,0 +1,161 @@ +/// The trait that defines an IBC application +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; +use ibc_core_channel_types::packet::Packet; +use ibc_core_channel_types::Version; +use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc_core_router_types::module::ModuleExtras; +use ibc_primitives::prelude::*; +use ibc_primitives::Signer; + +pub trait Module: Debug { + fn on_chan_open_init_validate( + &self, + order: Order, + connection_hops: &[ConnectionId], + port_id: &PortId, + channel_id: &ChannelId, + counterparty: &Counterparty, + version: &Version, + ) -> Result; + + fn on_chan_open_init_execute( + &mut self, + order: Order, + connection_hops: &[ConnectionId], + port_id: &PortId, + channel_id: &ChannelId, + counterparty: &Counterparty, + version: &Version, + ) -> Result<(ModuleExtras, Version), ChannelError>; + + fn on_chan_open_try_validate( + &self, + order: Order, + connection_hops: &[ConnectionId], + port_id: &PortId, + channel_id: &ChannelId, + counterparty: &Counterparty, + counterparty_version: &Version, + ) -> Result; + + fn on_chan_open_try_execute( + &mut self, + order: Order, + connection_hops: &[ConnectionId], + port_id: &PortId, + channel_id: &ChannelId, + counterparty: &Counterparty, + counterparty_version: &Version, + ) -> Result<(ModuleExtras, Version), ChannelError>; + + fn on_chan_open_ack_validate( + &self, + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty_version: &Version, + ) -> Result<(), ChannelError> { + Ok(()) + } + + fn on_chan_open_ack_execute( + &mut self, + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty_version: &Version, + ) -> Result { + Ok(ModuleExtras::empty()) + } + + fn on_chan_open_confirm_validate( + &self, + _port_id: &PortId, + _channel_id: &ChannelId, + ) -> Result<(), ChannelError> { + Ok(()) + } + + fn on_chan_open_confirm_execute( + &mut self, + _port_id: &PortId, + _channel_id: &ChannelId, + ) -> Result { + Ok(ModuleExtras::empty()) + } + + fn on_chan_close_init_validate( + &self, + _port_id: &PortId, + _channel_id: &ChannelId, + ) -> Result<(), ChannelError> { + Ok(()) + } + + fn on_chan_close_init_execute( + &mut self, + _port_id: &PortId, + _channel_id: &ChannelId, + ) -> Result { + Ok(ModuleExtras::empty()) + } + + fn on_chan_close_confirm_validate( + &self, + _port_id: &PortId, + _channel_id: &ChannelId, + ) -> Result<(), ChannelError> { + Ok(()) + } + + fn on_chan_close_confirm_execute( + &mut self, + _port_id: &PortId, + _channel_id: &ChannelId, + ) -> Result { + Ok(ModuleExtras::empty()) + } + + // Note: no `on_recv_packet_validate()` + // the `onRecvPacket` callback always succeeds + // if any error occurs, than an "error acknowledgement" + // must be returned + + fn on_recv_packet_execute( + &mut self, + packet: &Packet, + relayer: &Signer, + ) -> (ModuleExtras, Acknowledgement); + + fn on_acknowledgement_packet_validate( + &self, + _packet: &Packet, + _acknowledgement: &Acknowledgement, + _relayer: &Signer, + ) -> Result<(), ChannelError>; + + fn on_acknowledgement_packet_execute( + &mut self, + _packet: &Packet, + _acknowledgement: &Acknowledgement, + _relayer: &Signer, + ) -> (ModuleExtras, Result<(), ChannelError>); + + /// Note: `MsgTimeout` and `MsgTimeoutOnClose` use the same callback + + fn on_timeout_packet_validate( + &self, + packet: &Packet, + relayer: &Signer, + ) -> Result<(), ChannelError>; + + /// Note: `MsgTimeout` and `MsgTimeoutOnClose` use the same callback + + fn on_timeout_packet_execute( + &mut self, + packet: &Packet, + relayer: &Signer, + ) -> (ModuleExtras, Result<(), ChannelError>); +} diff --git a/ibc-eureka-core/ics26-routing/src/router.rs b/ibc-eureka-core/ics26-routing/src/router.rs new file mode 100644 index 0000000000..b73d16d5c1 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/src/router.rs @@ -0,0 +1,18 @@ +//! Defines the `Router`, which binds modules to ports + +use ibc_core_host_types::identifiers::PortId; +use ibc_core_router_types::module::ModuleId; + +use crate::module::Module; + +/// Router as defined in ICS-26, which binds modules to ports. +pub trait Router { + /// Returns a reference to a `Module` registered against the specified `ModuleId` + fn get_route(&self, module_id: &ModuleId) -> Option<&dyn Module>; + + /// Returns a mutable reference to a `Module` registered against the specified `ModuleId` + fn get_route_mut(&mut self, module_id: &ModuleId) -> Option<&mut dyn Module>; + + /// Return the module_id associated with a given port_id + fn lookup_module(&self, port_id: &PortId) -> Option; +} diff --git a/ibc-eureka-core/ics26-routing/types/Cargo.toml b/ibc-eureka-core/ics26-routing/types/Cargo.toml new file mode 100644 index 0000000000..71acf4beee --- /dev/null +++ b/ibc-eureka-core/ics26-routing/types/Cargo.toml @@ -0,0 +1,80 @@ +[package] +name = "ibc-core-router-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = [ "blockchain", "cosmos", "ibc", "routing", "types" ] +readme = "./../../README.md" + +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-26 Routing Module data structures and domain types, + as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal applicability + to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +subtle-encoding = { workspace = true } + +# ibc dependencies +ibc-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } + +# cosmos dependencies +tendermint = { workspace = true } + +# parity dependencies +parity-scale-codec = { workspace = true, optional = true } +scale-info = { workspace = true, optional = true } + +[features] +default = [ "std" ] +std = [ + "displaydoc/std", + "serde/std", + "subtle-encoding/std", + "ibc-primitives/std", + "ibc-core-host-types/std", + "ibc-proto/std", + "tendermint/std", +] +serde = [ + "dep:serde", + "ibc-primitives/serde", + "ibc-core-host-types/serde", + "ibc-primitives/serde", + "ibc-proto/serde", +] +borsh = [ + "dep:borsh", + "ibc-core-host-types/borsh", + "ibc-primitives/borsh", + "ibc-proto/borsh", +] +schema = [ + "dep:schemars", + "ibc-core-host-types/schema", + "ibc-primitives/schema", + "ibc-proto/json-schema", + "serde", + "std", +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-core-host-types/parity-scale-codec", + "ibc-primitives/parity-scale-codec", + "ibc-proto/parity-scale-codec", +] diff --git a/ibc-eureka-core/ics26-routing/types/src/error.rs b/ibc-eureka-core/ics26-routing/types/src/error.rs new file mode 100644 index 0000000000..19ad7c2adf --- /dev/null +++ b/ibc-eureka-core/ics26-routing/types/src/error.rs @@ -0,0 +1,15 @@ +use displaydoc::Display; +use ibc_core_host_types::error::HostError; +use ibc_primitives::prelude::*; + +/// Error type for the router module. +#[derive(Debug, Display, derive_more::From)] +pub enum RouterError { + /// host error: {0} + Host(HostError), + /// missing module + MissingModule, +} + +#[cfg(feature = "std")] +impl std::error::Error for RouterError {} diff --git a/ibc-eureka-core/ics26-routing/types/src/event.rs b/ibc-eureka-core/ics26-routing/types/src/event.rs new file mode 100644 index 0000000000..b3754b2d78 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/types/src/event.rs @@ -0,0 +1,67 @@ +use ibc_primitives::prelude::*; +use tendermint::abci; + +/// The event type emitted by IBC applications +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ModuleEvent { + pub kind: String, + pub attributes: Vec, +} + +impl From for abci::Event { + fn from(event: ModuleEvent) -> Self { + let attributes = event.attributes.into_iter().map(Into::into).collect(); + abci::Event { + kind: event.kind, + attributes, + } + } +} + +/// A single key/value pair in a [`ModuleEvent`] +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ModuleEventAttribute { + pub key: String, + pub value: String, +} + +impl From<(K, V)> for ModuleEventAttribute { + fn from((k, v): (K, V)) -> Self { + Self { + key: k.to_string(), + value: v.to_string(), + } + } +} + +impl From for abci::EventAttribute { + fn from(attr: ModuleEventAttribute) -> Self { + (attr.key, attr.value).into() + } +} diff --git a/ibc-eureka-core/ics26-routing/types/src/lib.rs b/ibc-eureka-core/ics26-routing/types/src/lib.rs new file mode 100644 index 0000000000..78d5fd12b2 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/types/src/lib.rs @@ -0,0 +1,20 @@ +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +pub mod error; +pub mod event; +pub mod module; diff --git a/ibc-eureka-core/ics26-routing/types/src/module.rs b/ibc-eureka-core/ics26-routing/types/src/module.rs new file mode 100644 index 0000000000..82867fde01 --- /dev/null +++ b/ibc-eureka-core/ics26-routing/types/src/module.rs @@ -0,0 +1,74 @@ +use alloc::borrow::Borrow; +use core::fmt::{Debug, Display, Error as FmtError, Formatter}; + +use ibc_primitives::prelude::*; + +use crate::event::ModuleEvent; + +/// Module name, internal to the chain. +/// +/// That is, the IBC protocol never exposes this name. Note that this is +/// different from IBC host [identifiers][ibc_core_host_types::identifiers], +/// which are exposed to other chains by the protocol. +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ModuleId(String); + +impl ModuleId { + pub fn new(s: String) -> Self { + Self(s) + } +} + +impl Display for ModuleId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!(f, "{}", self.0) + } +} + +impl Borrow for ModuleId { + fn borrow(&self) -> &str { + self.0.as_str() + } +} + +/// Logs and events produced during module callbacks +#[cfg_attr( + feature = "parity-scale-codec", + derive( + parity_scale_codec::Encode, + parity_scale_codec::Decode, + scale_info::TypeInfo + ) +)] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +pub struct ModuleExtras { + pub events: Vec, + pub log: Vec, +} + +impl ModuleExtras { + pub fn empty() -> Self { + ModuleExtras { + events: Vec::new(), + log: Vec::new(), + } + } +} diff --git a/ibc-eureka-core/src/lib.rs b/ibc-eureka-core/src/lib.rs new file mode 100644 index 0000000000..30d04e3848 --- /dev/null +++ b/ibc-eureka-core/src/lib.rs @@ -0,0 +1,91 @@ +//! Re-exports data structures and implementations of all the IBC core (TAO) modules/components. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] +#![deny( + warnings, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +/// Re-exports IBC handler entrypoints from the `ibc-core-handler` crate for +/// added convenience. +pub mod entrypoint { + #[doc(inline)] + pub use ibc_core_handler::entrypoint::*; +} + +/// Re-exports IBC primitive types from the `ibc-primitives` crate +pub mod primitives { + #[doc(inline)] + pub use ibc_primitives::*; +} + +/// Re-exports ICS-02 implementation from the `ibc-core-client` crate +pub mod client { + #[doc(inline)] + pub use ibc_core_client::*; +} + +/// Re-exports ICS-03 implementation from the `ibc-core-connection` crate +pub mod connection { + #[doc(inline)] + pub use ibc_core_connection::*; +} + +/// Re-exports ICS-04 implementation from the `ibc-core-channel` crate +pub mod channel { + #[doc(inline)] + pub use ibc_core_channel::*; +} + +/// Re-exports ICS-23 data structures from the `ibc-core-commitment-types` crate +pub mod commitment_types { + #[doc(inline)] + pub use ibc_core_commitment_types::*; +} + +/// Re-exports ICS-24 implementation from the `ibc-core-host` crate +pub mod host { + #[doc(inline)] + pub use ibc_core_host::*; +} + +/// Re-exports ICS-25 implementation from the `ibc-core-handler` crate +pub mod handler { + #[doc(inline)] + pub use ibc_core_handler::*; +} + +/// Re-exports ICS-26 implementation from the `ibc-core-router` crate +pub mod router { + #[doc(inline)] + pub use ibc_core_router::*; +} + +/// Re-exports convenient derive macros from `ibc-derive` crate. +pub mod derive { + /// To specify the generic arguments for `ClientState`, use the following + /// attributes: + /// + /// - `#[validation()]` + /// - `#[execution()]` + /// + /// The argument to the `validation` or `execution` attributes may contain + /// lifetimes or generic types, and even that types might be bounded by + /// traits. For instance: + /// + /// - `#[validation(Context)]` + /// - `#[validation(Context<'a, S>)]` + /// - `#[validation(Context<'a, S: Clone>)]` + pub use ibc_derive::IbcCoreClientState as ClientState; + /// A derive macro for implementing the + /// [`ConsensusState`](crate::client::context::consensus_state::ConsensusState) trait for + /// enums. Enums with variants that also implement the + /// [`ConsensusState`](crate::client::context::consensus_state::ConsensusState) trait can + /// leverage this macro for automatic implementation. + pub use ibc_derive::IbcCoreConsensusState as ConsensusState; +} From e16d980a5bbba32b67e935a97b0e4fa529b09a1f Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 21 Oct 2024 11:58:13 +0200 Subject: [PATCH 2/5] rename ibc-core to ibc-eureka-core --- ibc-eureka-core/Cargo.toml | 90 +++++++++---------- ibc-eureka-core/README.md | 38 ++++---- ibc-eureka-core/ics02-client/Cargo.toml | 66 +++++++------- .../ics02-client/context/Cargo.toml | 50 +++++------ .../ics02-client/context/src/client_state.rs | 10 +-- .../context/src/consensus_state.rs | 4 +- .../ics02-client/context/src/context.rs | 8 +- .../ics02-client/context/src/lib.rs | 2 +- .../ics02-client/src/handler/create_client.rs | 14 +-- .../src/handler/recover_client.rs | 10 +-- .../ics02-client/src/handler/update_client.rs | 16 ++-- .../src/handler/upgrade_client.rs | 14 +-- ibc-eureka-core/ics02-client/src/lib.rs | 10 +-- ibc-eureka-core/ics02-client/types/Cargo.toml | 30 +++---- .../ics02-client/types/src/error.rs | 6 +- .../ics02-client/types/src/events.rs | 4 +- .../ics02-client/types/src/height.rs | 2 +- .../types/src/msgs/create_client.rs | 2 +- .../types/src/msgs/misbehaviour.rs | 4 +- .../ics02-client/types/src/msgs/mod.rs | 2 +- .../types/src/msgs/recover_client.rs | 4 +- .../types/src/msgs/update_client.rs | 4 +- .../types/src/msgs/upgrade_client.rs | 6 +- .../ics02-client/types/src/status.rs | 2 +- ibc-eureka-core/ics03-connection/Cargo.toml | 56 ++++++------ ibc-eureka-core/ics03-connection/src/delay.rs | 10 +-- .../src/handler/conn_open_ack.rs | 20 ++--- .../src/handler/conn_open_confirm.rs | 18 ++-- .../src/handler/conn_open_init.rs | 18 ++-- .../src/handler/conn_open_try.rs | 20 ++--- .../ics03-connection/src/handler/mod.rs | 8 +- ibc-eureka-core/ics03-connection/src/lib.rs | 4 +- .../ics03-connection/types/Cargo.toml | 42 ++++----- .../ics03-connection/types/src/connection.rs | 6 +- .../ics03-connection/types/src/error.rs | 6 +- .../ics03-connection/types/src/events.rs | 4 +- .../types/src/msgs/conn_open_ack.rs | 8 +- .../types/src/msgs/conn_open_confirm.rs | 8 +- .../types/src/msgs/conn_open_init.rs | 4 +- .../types/src/msgs/conn_open_try.rs | 8 +- .../ics03-connection/types/src/version.rs | 2 +- ibc-eureka-core/ics04-channel/Cargo.toml | 90 +++++++++---------- ibc-eureka-core/ics04-channel/src/context.rs | 18 ++-- .../src/handler/acknowledgement.rs | 26 +++--- .../src/handler/chan_close_confirm.rs | 24 ++--- .../src/handler/chan_close_init.rs | 20 ++--- .../src/handler/chan_open_ack.rs | 22 ++--- .../src/handler/chan_open_confirm.rs | 22 ++--- .../src/handler/chan_open_init.rs | 20 ++--- .../src/handler/chan_open_try.rs | 24 ++--- .../ics04-channel/src/handler/recv_packet.rs | 28 +++--- .../ics04-channel/src/handler/send_packet.rs | 16 ++-- .../ics04-channel/src/handler/timeout.rs | 22 ++--- .../src/handler/timeout_on_close.rs | 18 ++-- ibc-eureka-core/ics04-channel/src/lib.rs | 4 +- .../ics04-channel/types/Cargo.toml | 54 +++++------ .../types/src/acknowledgement.rs | 2 +- .../ics04-channel/types/src/channel.rs | 4 +- .../ics04-channel/types/src/commitment.rs | 2 +- .../ics04-channel/types/src/error.rs | 10 +-- .../types/src/events/channel_attributes.rs | 2 +- .../ics04-channel/types/src/events/mod.rs | 4 +- .../types/src/events/packet_attributes.rs | 4 +- .../types/src/msgs/acknowledgement.rs | 6 +- .../types/src/msgs/chan_close_confirm.rs | 8 +- .../types/src/msgs/chan_close_init.rs | 4 +- .../types/src/msgs/chan_open_ack.rs | 8 +- .../types/src/msgs/chan_open_confirm.rs | 8 +- .../types/src/msgs/chan_open_init.rs | 4 +- .../types/src/msgs/chan_open_try.rs | 8 +- .../ics04-channel/types/src/msgs/mod.rs | 2 +- .../types/src/msgs/recv_packet.rs | 6 +- .../ics04-channel/types/src/msgs/timeout.rs | 8 +- .../types/src/msgs/timeout_on_close.rs | 8 +- .../ics04-channel/types/src/packet.rs | 10 +-- .../ics04-channel/types/src/timeout/height.rs | 6 +- .../ics23-commitment/types/Cargo.toml | 20 ++--- .../ics23-commitment/types/src/commitment.rs | 2 +- .../ics23-commitment/types/src/error.rs | 2 +- .../ics23-commitment/types/src/merkle.rs | 4 +- .../ics23-commitment/types/src/specs.rs | 2 +- ibc-eureka-core/ics24-host/Cargo.toml | 86 +++++++++--------- ibc-eureka-core/ics24-host/cosmos/Cargo.toml | 78 ++++++++-------- .../cosmos/src/upgrade_proposal/context.rs | 6 +- .../cosmos/src/upgrade_proposal/handler.rs | 4 +- .../cosmos/src/upgrade_proposal/plan.rs | 2 +- .../cosmos/src/upgrade_proposal/proposal.rs | 2 +- .../ics24-host/cosmos/src/utils.rs | 2 +- .../cosmos/src/validate_self_client.rs | 8 +- ibc-eureka-core/ics24-host/src/context.rs | 30 +++---- ibc-eureka-core/ics24-host/src/lib.rs | 4 +- ibc-eureka-core/ics24-host/types/Cargo.toml | 2 +- .../types/src/identifiers/chain_id.rs | 4 +- .../types/src/identifiers/channel_id.rs | 4 +- .../types/src/identifiers/client_id.rs | 6 +- .../types/src/identifiers/client_type.rs | 4 +- .../types/src/identifiers/connection_id.rs | 4 +- ibc-eureka-core/ics25-handler/Cargo.toml | 86 +++++++++--------- .../ics25-handler/src/entrypoint.rs | 28 +++--- ibc-eureka-core/ics25-handler/src/lib.rs | 6 +- .../ics25-handler/types/Cargo.toml | 78 ++++++++-------- .../ics25-handler/types/src/error.rs | 8 +- .../ics25-handler/types/src/events.rs | 10 +-- .../ics25-handler/types/src/msgs.rs | 8 +- ibc-eureka-core/ics26-routing/Cargo.toml | 40 ++++----- ibc-eureka-core/ics26-routing/src/lib.rs | 4 +- ibc-eureka-core/ics26-routing/src/module.rs | 14 +-- ibc-eureka-core/ics26-routing/src/router.rs | 4 +- .../ics26-routing/types/Cargo.toml | 18 ++-- .../ics26-routing/types/src/error.rs | 2 +- .../ics26-routing/types/src/module.rs | 2 +- ibc-eureka-core/src/lib.rs | 32 +++---- 112 files changed, 892 insertions(+), 886 deletions(-) diff --git a/ibc-eureka-core/Cargo.toml b/ibc-eureka-core/Cargo.toml index eebb123180..e017a93954 100644 --- a/ibc-eureka-core/Cargo.toml +++ b/ibc-eureka-core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core" +name = "ibc-eureka-core" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -18,67 +18,67 @@ description = """ all-features = true [dependencies] -ibc-core-client = { workspace = true } -ibc-core-connection = { workspace = true } -ibc-core-channel = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host = { workspace = true } -ibc-core-router = { workspace = true } -ibc-core-handler = { workspace = true } -ibc-derive = { workspace = true } -ibc-primitives = { workspace = true } +ibc-eureka-core-client = { workspace = true } +ibc-eureka-core-connection = { workspace = true } +ibc-eureka-core-channel = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host = { workspace = true } +ibc-eureka-core-router = { workspace = true } +ibc-eureka-core-handler = { workspace = true } +ibc-derive = { workspace = true } +ibc-primitives = { workspace = true } [features] default = [ "std" ] std = [ - "ibc-core-client/std", - "ibc-core-connection/std", - "ibc-core-channel/std", - "ibc-core-commitment-types/std", - "ibc-core-host/std", - "ibc-core-router/std", - "ibc-core-handler/std", + "ibc-eureka-core-client/std", + "ibc-eureka-core-connection/std", + "ibc-eureka-core-channel/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host/std", + "ibc-eureka-core-router/std", + "ibc-eureka-core-handler/std", "ibc-primitives/std", ] serde = [ - "ibc-core-client/serde", - "ibc-core-connection/serde", - "ibc-core-channel/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host/serde", - "ibc-core-router/serde", - "ibc-core-handler/serde", + "ibc-eureka-core-client/serde", + "ibc-eureka-core-connection/serde", + "ibc-eureka-core-channel/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host/serde", + "ibc-eureka-core-router/serde", + "ibc-eureka-core-handler/serde", "ibc-primitives/serde", ] borsh = [ - "ibc-core-client/borsh", - "ibc-core-connection/borsh", - "ibc-core-channel/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host/borsh", - "ibc-core-router/borsh", - "ibc-core-handler/borsh", + "ibc-eureka-core-client/borsh", + "ibc-eureka-core-connection/borsh", + "ibc-eureka-core-channel/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host/borsh", + "ibc-eureka-core-router/borsh", + "ibc-eureka-core-handler/borsh", "ibc-primitives/borsh", ] schema = [ - "ibc-core-client/schema", - "ibc-core-connection/schema", - "ibc-core-channel/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host/schema", - "ibc-core-router/schema", - "ibc-core-handler/schema", + "ibc-eureka-core-client/schema", + "ibc-eureka-core-connection/schema", + "ibc-eureka-core-channel/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host/schema", + "ibc-eureka-core-router/schema", + "ibc-eureka-core-handler/schema", "ibc-primitives/schema", "serde", "std", ] parity-scale-codec = [ - "ibc-core-client/parity-scale-codec", - "ibc-core-connection/parity-scale-codec", - "ibc-core-channel/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host/parity-scale-codec", - "ibc-core-router/parity-scale-codec", - "ibc-core-handler/parity-scale-codec", + "ibc-eureka-core-client/parity-scale-codec", + "ibc-eureka-core-connection/parity-scale-codec", + "ibc-eureka-core-channel/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host/parity-scale-codec", + "ibc-eureka-core-router/parity-scale-codec", + "ibc-eureka-core-handler/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/README.md b/ibc-eureka-core/README.md index d8460a5b5a..f157b1b464 100644 --- a/ibc-eureka-core/README.md +++ b/ibc-eureka-core/README.md @@ -8,11 +8,11 @@ IBC is a distributed protocol that enables communication between distinct sovereign blockchains and IBC core is the part of the protocol that handles the transport, authentication, and ordering (TAO) of data packets. -The structure within the `ibc-core` crate is designed to provide flexibility for -external users. You can choose to utilize the entire `ibc-core` crate, or +The structure within the `ibc-eureka-core` crate is designed to provide flexibility for +external users. You can choose to utilize the entire `ibc-eureka-core` crate, or selectively import specific libraries. From there, you also have the flexibility -of bringing in an entire sub-module (e.g. the `ibc-core-client` crate), or only -a module's associated data structures (e.g. `ibc-core-client-types`). +of bringing in an entire sub-module (e.g. the `ibc-eureka-core-client` crate), or only +a module's associated data structures (e.g. `ibc-eureka-core-client-types`). This versatility empowers hosts, including chain integrators, relayers, or any IBC tooling projects, to build their solution on top of the layers that best @@ -20,40 +20,40 @@ suit their particular requirements. ## Sub-Crates -Currently, the `ibc-core` crate contains the implementation of the following IBC +Currently, the `ibc-eureka-core` crate contains the implementation of the following IBC core specifications: ### ICS-02: Client Semantics -- [ibc-core-client](./../ibc-core/ics02-client) -- [ibc-core-client-context](./../ibc-core/ics02-client/context) -- [ibc-core-client-types](./../ibc-core/ics02-client/types) +- [ibc-eureka-core-client](./../ibc-eureka-core/ics02-client) +- [ibc-eureka-core-client-context](./../ibc-eureka-core/ics02-client/context) +- [ibc-eureka-core-client-types](./../ibc-eureka-core/ics02-client/types) ### ICS-03: Connection Semantics -- [ibc-core-connection](./../ibc-core/ics03-connection) -- [ibc-core-connection-types](./../ibc-core/ics03-connection/types) +- [ibc-eureka-core-connection](./../ibc-eureka-core/ics03-connection) +- [ibc-eureka-core-connection-types](./../ibc-eureka-core/ics03-connection/types) ### ICS-04: Channel and Packet Semantics -- [ibc-core-channel](./../ibc-core/ics04-channel) -- [ibc-core-channel-types](./../ibc-core/ics04-channel/types) +- [ibc-eureka-core-channel](./../ibc-eureka-core/ics04-channel) +- [ibc-eureka-core-channel-types](./../ibc-eureka-core/ics04-channel/types) ### ICS-24: Host Requirements -- [ibc-core-host](./../ibc-core/ics24-host) -- [ibc-core-host-cosmos](./../ibc-core/ics24-host/cosmos) -- [ibc-core-host-types](./../ibc-core/ics24-host/types) +- [ibc-eureka-core-host](./../ibc-eureka-core/ics24-host) +- [ibc-eureka-core-host-cosmos](./../ibc-eureka-core/ics24-host/cosmos) +- [ibc-eureka-core-host-types](./../ibc-eureka-core/ics24-host/types) ### ICS-25: Handler Interface -- [ibc-core-handler](./../ibc-core/ics25-handler) -- [ibc-core-handler-types](./../ibc-core/ics25-handler/types) +- [ibc-eureka-core-handler](./../ibc-eureka-core/ics25-handler) +- [ibc-eureka-core-handler-types](./../ibc-eureka-core/ics25-handler/types) ### ICS-26: Routing Module -- [ibc-core-routing](./../ibc-core/ics26-routing) -- [ibc-core-routing-types](./../ibc-core/ics26-routing/types) +- [ibc-eureka-core-routing](./../ibc-eureka-core/ics26-routing) +- [ibc-eureka-core-routing-types](./../ibc-eureka-core/ics26-routing/types) ## Divergence from the Interchain Standards (ICS) diff --git a/ibc-eureka-core/ics02-client/Cargo.toml b/ibc-eureka-core/ics02-client/Cargo.toml index 7502b6a259..9629e51aed 100644 --- a/ibc-eureka-core/ics02-client/Cargo.toml +++ b/ibc-eureka-core/ics02-client/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-client" +name = "ibc-eureka-core-client" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -11,61 +11,61 @@ readme = "./../README.md" description = """ Maintained by `ibc-rs`, contains the implementation of ICS-02 Client Semantics and - re-exports essential data structures and domain types from `ibc-core-client-types` crate. + re-exports essential data structures and domain types from `ibc-eureka-core-client-types` crate. """ [package.metadata.docs.rs] all-features = true [dependencies] -ibc-core-client-types = { workspace = true } -ibc-core-client-context = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-primitives = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-client-context = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } [features] default = [ "std" ] std = [ - "ibc-core-client-types/std", - "ibc-core-client-context/std", - "ibc-core-commitment-types/std", - "ibc-core-host/std", - "ibc-core-handler-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-client-context/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host/std", + "ibc-eureka-core-handler-types/std", "ibc-primitives/std", ] serde = [ - "ibc-core-client-types/serde", - "ibc-core-client-context/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host/serde", - "ibc-core-handler-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-client-context/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host/serde", + "ibc-eureka-core-handler-types/serde", "ibc-primitives/serde", ] borsh = [ - "ibc-core-client-types/borsh", - "ibc-core-client-context/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host/borsh", - "ibc-core-handler-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-client-context/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host/borsh", + "ibc-eureka-core-handler-types/borsh", "ibc-primitives/borsh", ] schema = [ - "ibc-core-client-types/schema", - "ibc-core-client-context/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host/schema", - "ibc-core-handler-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-client-context/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host/schema", + "ibc-eureka-core-handler-types/schema", "ibc-primitives/schema", "serde", "std", ] parity-scale-codec = [ - "ibc-core-client-types/parity-scale-codec", - "ibc-core-client-context/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-client-context/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics02-client/context/Cargo.toml b/ibc-eureka-core/ics02-client/context/Cargo.toml index 9085da29e7..93a0f97993 100644 --- a/ibc-eureka-core/ics02-client/context/Cargo.toml +++ b/ibc-eureka-core/ics02-client/context/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-client-context" +name = "ibc-eureka-core-client-context" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -25,11 +25,11 @@ displaydoc = { workspace = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-client-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-primitives = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -39,39 +39,39 @@ default = [ "std" ] std = [ "displaydoc/std", "subtle-encoding/std", - "ibc-core-client-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", - "ibc-core-handler-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host-types/std", + "ibc-eureka-core-handler-types/std", "ibc-primitives/std", "tendermint/std", ] serde = [ - "ibc-core-client-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host-types/serde", - "ibc-core-handler-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-handler-types/serde", "ibc-primitives/serde", ] borsh = [ - "ibc-core-client-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", - "ibc-core-handler-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", + "ibc-eureka-core-handler-types/borsh", "ibc-primitives/borsh", ] schema = [ - "ibc-core-client-types/schema", - "ibc-core-host-types/schema", - "ibc-core-handler-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-handler-types/schema", "ibc-primitives/schema", "serde", "std", ] parity-scale-codec = [ - "ibc-core-client-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics02-client/context/src/client_state.rs b/ibc-eureka-core/ics02-client/context/src/client_state.rs index bbcc24aa7a..891e113f2c 100644 --- a/ibc-eureka-core/ics02-client/context/src/client_state.rs +++ b/ibc-eureka-core/ics02-client/context/src/client_state.rs @@ -1,12 +1,12 @@ //! Defines `ClientState`, the core type to be implemented by light clients -use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::{Height, Status}; -use ibc_core_commitment_types::commitment::{ +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::{Height, Status}; +use ibc_eureka_core_commitment_types::commitment::{ CommitmentPrefix, CommitmentProofBytes, CommitmentRoot, }; -use ibc_core_host_types::identifiers::{ClientId, ClientType}; -use ibc_core_host_types::path::{Path, PathBytes}; +use ibc_eureka_core_host_types::identifiers::{ClientId, ClientType}; +use ibc_eureka_core_host_types::path::{Path, PathBytes}; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; use ibc_primitives::Timestamp; diff --git a/ibc-eureka-core/ics02-client/context/src/consensus_state.rs b/ibc-eureka-core/ics02-client/context/src/consensus_state.rs index 2149c76ff9..8198567d7b 100644 --- a/ibc-eureka-core/ics02-client/context/src/consensus_state.rs +++ b/ibc-eureka-core/ics02-client/context/src/consensus_state.rs @@ -1,7 +1,7 @@ //! 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_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_commitment_types::commitment::CommitmentRoot; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; use ibc_primitives::Timestamp; diff --git a/ibc-eureka-core/ics02-client/context/src/context.rs b/ibc-eureka-core/ics02-client/context/src/context.rs index e9a47bf0d2..7c219cf007 100644 --- a/ibc-eureka-core/ics02-client/context/src/context.rs +++ b/ibc-eureka-core/ics02-client/context/src/context.rs @@ -1,7 +1,7 @@ -use ibc_core_client_types::Height; -use ibc_core_host_types::error::HostError; -use ibc_core_host_types::identifiers::ClientId; -use ibc_core_host_types::path::{ClientConsensusStatePath, ClientStatePath}; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_host_types::error::HostError; +use ibc_eureka_core_host_types::identifiers::ClientId; +use ibc_eureka_core_host_types::path::{ClientConsensusStatePath, ClientStatePath}; use ibc_primitives::prelude::*; use ibc_primitives::Timestamp; diff --git a/ibc-eureka-core/ics02-client/context/src/lib.rs b/ibc-eureka-core/ics02-client/context/src/lib.rs index 7b873f38f8..aaa3271a7d 100644 --- a/ibc-eureka-core/ics02-client/context/src/lib.rs +++ b/ibc-eureka-core/ics02-client/context/src/lib.rs @@ -33,5 +33,5 @@ pub mod prelude { pub mod types { #[doc(inline)] - pub use ibc_core_client_types::*; + pub use ibc_eureka_core_client_types::*; } diff --git a/ibc-eureka-core/ics02-client/src/handler/create_client.rs b/ibc-eureka-core/ics02-client/src/handler/create_client.rs index 511f45f2c2..d2ee9034d2 100644 --- a/ibc-eureka-core/ics02-client/src/handler/create_client.rs +++ b/ibc-eureka-core/ics02-client/src/handler/create_client.rs @@ -1,12 +1,12 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgCreateClient`. -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_client_types::Status; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::{ClientStateMut, ClientStateRef, ExecutionContext, ValidationContext}; +use ibc_eureka_core_client_context::prelude::*; +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::events::CreateClient; +use ibc_eureka_core_client_types::msgs::MsgCreateClient; +use ibc_eureka_core_client_types::Status; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::{ClientStateMut, ClientStateRef, ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; diff --git a/ibc-eureka-core/ics02-client/src/handler/recover_client.rs b/ibc-eureka-core/ics02-client/src/handler/recover_client.rs index a239ed8fc2..53f8d623db 100644 --- a/ibc-eureka-core/ics02-client/src/handler/recover_client.rs +++ b/ibc-eureka-core/ics02-client/src/handler/recover_client.rs @@ -1,10 +1,10 @@ //! Protocol logic for processing ICS02 messages of type `MsgRecoverClient`. -use ibc_core_client_context::prelude::*; -use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::msgs::MsgRecoverClient; -use ibc_core_host::types::path::ClientConsensusStatePath; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_client_context::prelude::*; +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::msgs::MsgRecoverClient; +use ibc_eureka_core_host::types::path::ClientConsensusStatePath; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; /// Performs the validation steps associated with the client recovery process. This /// includes validating that the parameters of the subject and substitute clients match, diff --git a/ibc-eureka-core/ics02-client/src/handler/update_client.rs b/ibc-eureka-core/ics02-client/src/handler/update_client.rs index 51a30c3b1b..6bf6ad453e 100644 --- a/ibc-eureka-core/ics02-client/src/handler/update_client.rs +++ b/ibc-eureka-core/ics02-client/src/handler/update_client.rs @@ -1,13 +1,13 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgUpdateAnyClient`. -use ibc_core_client_context::prelude::*; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::error::HostError; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_client_context::prelude::*; +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::events::{ClientMisbehaviour, UpdateClient}; +use ibc_eureka_core_client_types::msgs::MsgUpdateOrMisbehaviour; +use ibc_eureka_core_client_types::UpdateKind; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::error::HostError; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::ToVec; diff --git a/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs b/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs index ae119faeb8..bd509a74aa 100644 --- a/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs +++ b/ibc-eureka-core/ics02-client/src/handler/upgrade_client.rs @@ -1,12 +1,12 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgUpgradeAnyClient`. //! -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::ClientConsensusStatePath; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_client_context::prelude::*; +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::events::UpgradeClient; +use ibc_eureka_core_client_types::msgs::MsgUpgradeClient; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::ClientConsensusStatePath; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; pub fn validate(ctx: &Ctx, msg: MsgUpgradeClient) -> Result<(), ClientError> diff --git a/ibc-eureka-core/ics02-client/src/lib.rs b/ibc-eureka-core/ics02-client/src/lib.rs index b692624c84..769ab2577d 100644 --- a/ibc-eureka-core/ics02-client/src/lib.rs +++ b/ibc-eureka-core/ics02-client/src/lib.rs @@ -1,5 +1,5 @@ //! ICS-02: Client Semantics implementation for verifying remote IBC-enabled chains, -//! along with re-exporting data structures from `ibc-core-client-types` crate. +//! along with re-exporting data structures from `ibc-eureka-core-client-types` crate. #![no_std] #![forbid(unsafe_code)] #![cfg_attr(not(test), deny(clippy::unwrap_used))] @@ -17,15 +17,15 @@ extern crate std; pub mod handler; -/// Re-exports ICS-02 traits from `ibc-core-client-context` for custom IBC +/// Re-exports ICS-02 traits from `ibc-eureka-core-client-context` for custom IBC /// client implementation. pub mod context { #[doc(inline)] - pub use ibc_core_client_context::*; + pub use ibc_eureka_core_client_context::*; } -/// Re-exports ICS-02 data structures from the `ibc-core-client-types` crate. +/// Re-exports ICS-02 data structures from the `ibc-eureka-core-client-types` crate. pub mod types { #[doc(inline)] - pub use ibc_core_client_types::*; + pub use ibc_eureka_core_client_types::*; } diff --git a/ibc-eureka-core/ics02-client/types/Cargo.toml b/ibc-eureka-core/ics02-client/types/Cargo.toml index abc812cd43..f26119a493 100644 --- a/ibc-eureka-core/ics02-client/types/Cargo.toml +++ b/ibc-eureka-core/ics02-client/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-client-types" +name = "ibc-eureka-core-client-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -28,10 +28,10 @@ serde = { workspace = true, optional = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -49,30 +49,30 @@ std = [ "displaydoc/std", "serde/std", "subtle-encoding/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host-types/std", "ibc-primitives/std", "ibc-proto/std", "tendermint/std", ] serde = [ - "ibc-core-host-types/serde", - "ibc-core-commitment-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-commitment-types/serde", "ibc-primitives/serde", "ibc-proto/serde", "dep:serde", ] borsh = [ "dep:borsh", - "ibc-core-host-types/borsh", - "ibc-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", + "ibc-eureka-core-commitment-types/borsh", "ibc-primitives/borsh", "ibc-proto/borsh", ] schema = [ "dep:schemars", - "ibc-core-host-types/schema", - "ibc-core-commitment-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-commitment-types/schema", "ibc-primitives/schema", "ibc-proto/json-schema", "serde", @@ -81,8 +81,8 @@ schema = [ parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", - "ibc-core-host-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics02-client/types/src/error.rs b/ibc-eureka-core/ics02-client/types/src/error.rs index 49ced05dd0..e362da2dfd 100644 --- a/ibc-eureka-core/ics02-client/types/src/error.rs +++ b/ibc-eureka-core/ics02-client/types/src/error.rs @@ -3,9 +3,9 @@ use core::convert::Infallible; use displaydoc::Display; -use ibc_core_commitment_types::error::CommitmentError; -use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_commitment_types::error::CommitmentError; +use ibc_eureka_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; diff --git a/ibc-eureka-core/ics02-client/types/src/events.rs b/ibc-eureka-core/ics02-client/types/src/events.rs index 744776b88a..a0a4131010 100644 --- a/ibc-eureka-core/ics02-client/types/src/events.rs +++ b/ibc-eureka-core/ics02-client/types/src/events.rs @@ -1,8 +1,8 @@ //! 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_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ClientId, ClientType}; use ibc_primitives::prelude::*; use subtle_encoding::hex; use tendermint::abci; diff --git a/ibc-eureka-core/ics02-client/types/src/height.rs b/ibc-eureka-core/ics02-client/types/src/height.rs index 612351863f..2856f2b72e 100644 --- a/ibc-eureka-core/ics02-client/types/src/height.rs +++ b/ibc-eureka-core/ics02-client/types/src/height.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering; use core::str::FromStr; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::ibc::core::client::v1::Height as RawHeight; use ibc_proto::Protobuf; diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs index 1891a143e9..e0674b3830 100644 --- a/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs +++ b/ibc-eureka-core/ics02-client/types/src/msgs/create_client.rs @@ -1,6 +1,6 @@ //! Definition of domain type message `MsgCreateClient`. -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs b/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs index 1e495393bb..ea5167e730 100644 --- a/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs +++ b/ibc-eureka-core/ics02-client/types/src/msgs/misbehaviour.rs @@ -1,7 +1,7 @@ //! Definition of domain type message `MsgSubmitMisbehaviour`. -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any as ProtoAny; diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs b/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs index fbb76a8211..7c02331c30 100644 --- a/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs +++ b/ibc-eureka-core/ics02-client/types/src/msgs/mod.rs @@ -2,7 +2,7 @@ //! Defines the client message types that are sent to the chain by the relayer. -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs index 5ef857cbaf..52818c83b2 100644 --- a/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs +++ b/ibc-eureka-core/ics02-client/types/src/msgs/recover_client.rs @@ -1,7 +1,7 @@ //! Definition of domain type message `MsgRecoverClient`. -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::client::v1::MsgRecoverClient as RawMsgRecoverClient; diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs index 16a5d250d8..51a213c85e 100644 --- a/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs +++ b/ibc-eureka-core/ics02-client/types/src/msgs/update_client.rs @@ -1,7 +1,7 @@ //! Definition of domain type message `MsgUpdateClient`. -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs b/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs index d581f8e1bc..f22d7b80e9 100644 --- a/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs +++ b/ibc-eureka-core/ics02-client/types/src/msgs/upgrade_client.rs @@ -2,9 +2,9 @@ use core::str::FromStr; -use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics02-client/types/src/status.rs b/ibc-eureka-core/ics02-client/types/src/status.rs index 39e4f36f2b..22c7619bb7 100644 --- a/ibc-eureka-core/ics02-client/types/src/status.rs +++ b/ibc-eureka-core/ics02-client/types/src/status.rs @@ -1,7 +1,7 @@ use core::fmt::{Debug, Display, Formatter}; use core::str::FromStr; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use crate::error::ClientError; diff --git a/ibc-eureka-core/ics03-connection/Cargo.toml b/ibc-eureka-core/ics03-connection/Cargo.toml index a6b19b7ab0..e24b0be06f 100644 --- a/ibc-eureka-core/ics03-connection/Cargo.toml +++ b/ibc-eureka-core/ics03-connection/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-connection" +name = "ibc-eureka-core-connection" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -11,60 +11,60 @@ readme = "./../README.md" description = """ Maintained by `ibc-rs`, contains the implementation of the ICS-03 Connection Semantics and - re-exports essential data structures and domain types from `ibc-core-connection-types` crate. + re-exports essential data structures and domain types from `ibc-eureka-core-connection-types` crate. """ [package.metadata.docs.rs] all-features = true [dependencies] -ibc-core-client = { workspace = true } -ibc-core-connection-types = { workspace = true } -ibc-core-host = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-client-wasm-types = { workspace = true, optional = true } +ibc-eureka-core-client = { workspace = true } +ibc-eureka-core-connection-types = { workspace = true } +ibc-eureka-core-host = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-client-wasm-types = { workspace = true, optional = true } prost = { workspace = true, optional = true } [features] default = [ "std" ] std = [ - "ibc-core-client/std", - "ibc-core-connection-types/std", - "ibc-core-host/std", - "ibc-core-handler-types/std", + "ibc-eureka-core-client/std", + "ibc-eureka-core-connection-types/std", + "ibc-eureka-core-host/std", + "ibc-eureka-core-handler-types/std", "ibc-primitives/std", "wasm-client", ] serde = [ - "ibc-core-client/serde", - "ibc-core-connection-types/serde", - "ibc-core-host/serde", - "ibc-core-handler-types/serde", + "ibc-eureka-core-client/serde", + "ibc-eureka-core-connection-types/serde", + "ibc-eureka-core-host/serde", + "ibc-eureka-core-handler-types/serde", "ibc-primitives/serde", ] schema = [ - "ibc-core-client/schema", - "ibc-core-connection-types/schema", - "ibc-core-host/schema", - "ibc-core-handler-types/schema", + "ibc-eureka-core-client/schema", + "ibc-eureka-core-connection-types/schema", + "ibc-eureka-core-host/schema", + "ibc-eureka-core-handler-types/schema", "ibc-primitives/schema", "serde", "std", ] borsh = [ - "ibc-core-client/borsh", - "ibc-core-connection-types/borsh", - "ibc-core-host/borsh", - "ibc-core-handler-types/borsh", + "ibc-eureka-core-client/borsh", + "ibc-eureka-core-connection-types/borsh", + "ibc-eureka-core-host/borsh", + "ibc-eureka-core-handler-types/borsh", "ibc-primitives/borsh", ] parity-scale-codec = [ - "ibc-core-client/parity-scale-codec", - "ibc-core-connection-types/parity-scale-codec", - "ibc-core-host/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", + "ibc-eureka-core-client/parity-scale-codec", + "ibc-eureka-core-connection-types/parity-scale-codec", + "ibc-eureka-core-host/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] wasm-client = [ diff --git a/ibc-eureka-core/ics03-connection/src/delay.rs b/ibc-eureka-core/ics03-connection/src/delay.rs index b0f427b22b..d49cdb6ad7 100644 --- a/ibc-eureka-core/ics03-connection/src/delay.rs +++ b/ibc-eureka-core/ics03-connection/src/delay.rs @@ -1,8 +1,8 @@ -use ibc_core_client::context::ClientValidationContext; -use ibc_core_client::types::Height; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::ConnectionEnd; -use ibc_core_host::ValidationContext; +use ibc_eureka_core_client::context::ClientValidationContext; +use ibc_eureka_core_client::types::Height; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_connection_types::ConnectionEnd; +use ibc_eureka_core_host::ValidationContext; pub fn verify_conn_delay_passed( ctx: &Ctx, diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs index a91a16c761..72fba2140c 100644 --- a/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_ack.rs @@ -1,15 +1,15 @@ //! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenAck`. -use ibc_core_client::context::prelude::*; -use ibc_core_client::types::error::ClientError; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenAck; -use ibc_core_connection_types::msgs::MsgConnectionOpenAck; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::ClientId; -use ibc_core_host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_client::types::error::ClientError; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_connection_types::events::OpenAck; +use ibc_eureka_core_connection_types::msgs::MsgConnectionOpenAck; +use ibc_eureka_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::identifiers::ClientId; +use ibc_eureka_core_host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; use ibc_primitives::ToVec; diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs index ba934493d0..fcdcadaef7 100644 --- a/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_confirm.rs @@ -1,14 +1,14 @@ //! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenConfirm`. -use ibc_core_client::context::prelude::*; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenConfirm; -use ibc_core_connection_types::msgs::MsgConnectionOpenConfirm; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; -use ibc_core_host::types::path::{ClientConsensusStatePath, ConnectionPath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_connection_types::events::OpenConfirm; +use ibc_eureka_core_connection_types::msgs::MsgConnectionOpenConfirm; +use ibc_eureka_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::identifiers::{ClientId, ConnectionId}; +use ibc_eureka_core_host::types::path::{ClientConsensusStatePath, ConnectionPath, Path}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs index 267603a7fb..3be0b28000 100644 --- a/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_init.rs @@ -1,13 +1,13 @@ //! Protocol logic specific to ICS3 messages of type `MsgConnectionOpenInit`. -use ibc_core_client::context::prelude::*; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenInit; -use ibc_core_connection_types::msgs::MsgConnectionOpenInit; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::ConnectionId; -use ibc_core_host::types::path::{ClientConnectionPath, ConnectionPath}; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_connection_types::events::OpenInit; +use ibc_eureka_core_connection_types::msgs::MsgConnectionOpenInit; +use ibc_eureka_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::identifiers::ConnectionId; +use ibc_eureka_core_host::types::path::{ClientConnectionPath, ConnectionPath}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; pub fn validate(ctx_a: &Ctx, msg: MsgConnectionOpenInit) -> Result<(), ConnectionError> diff --git a/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs b/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs index 2b47e488d7..49193a6c71 100644 --- a/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs +++ b/ibc-eureka-core/ics03-connection/src/handler/conn_open_try.rs @@ -1,16 +1,16 @@ //! Protocol logic specific to processing ICS3 messages of type `MsgConnectionOpenTry`.; -use ibc_core_client::context::prelude::*; -use ibc_core_client::types::error::ClientError; -use ibc_core_connection_types::error::ConnectionError; -use ibc_core_connection_types::events::OpenTry; -use ibc_core_connection_types::msgs::MsgConnectionOpenTry; -use ibc_core_connection_types::{ConnectionEnd, Counterparty, State}; -use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::{ClientId, ConnectionId}; -use ibc_core_host::types::path::{ +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_client::types::error::ClientError; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_connection_types::events::OpenTry; +use ibc_eureka_core_connection_types::msgs::MsgConnectionOpenTry; +use ibc_eureka_core_connection_types::{ConnectionEnd, Counterparty, State}; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::identifiers::{ClientId, ConnectionId}; +use ibc_eureka_core_host::types::path::{ ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path, }; -use ibc_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; use ibc_primitives::ToVec; diff --git a/ibc-eureka-core/ics03-connection/src/handler/mod.rs b/ibc-eureka-core/ics03-connection/src/handler/mod.rs index 791d1c7787..eb86a2b804 100644 --- a/ibc-eureka-core/ics03-connection/src/handler/mod.rs +++ b/ibc-eureka-core/ics03-connection/src/handler/mod.rs @@ -1,8 +1,8 @@ -use ibc_core_client::types::error::ClientError; -use ibc_core_connection_types::error::ConnectionError; +use ibc_eureka_core_client::types::error::ClientError; +use ibc_eureka_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_eureka_core_host::types::error::DecodingError; +use ibc_eureka_core_host::types::identifiers::ClientId; use ibc_primitives::proto::Any; pub mod conn_open_ack; diff --git a/ibc-eureka-core/ics03-connection/src/lib.rs b/ibc-eureka-core/ics03-connection/src/lib.rs index 0892189d11..01f0cdbac8 100644 --- a/ibc-eureka-core/ics03-connection/src/lib.rs +++ b/ibc-eureka-core/ics03-connection/src/lib.rs @@ -19,8 +19,8 @@ extern crate std; pub mod delay; pub mod handler; -/// Re-exports ICS-03 data structures from the `ibc-core-connection-types` crate +/// Re-exports ICS-03 data structures from the `ibc-eureka-core-connection-types` crate pub mod types { #[doc(inline)] - pub use ibc_core_connection_types::*; + pub use ibc_eureka_core_connection_types::*; } diff --git a/ibc-eureka-core/ics03-connection/types/Cargo.toml b/ibc-eureka-core/ics03-connection/types/Cargo.toml index 39ecc8485a..34b05d79dc 100644 --- a/ibc-eureka-core/ics03-connection/types/Cargo.toml +++ b/ibc-eureka-core/ics03-connection/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-connection-types" +name = "ibc-eureka-core-connection-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -28,11 +28,11 @@ serde = { workspace = true, optional = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-client-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -47,26 +47,26 @@ std = [ "displaydoc/std", "subtle-encoding/std", "serde/std", - "ibc-core-client-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host-types/std", "ibc-primitives/std", "ibc-proto/std", "tendermint/std", ] serde = [ "dep:serde", - "ibc-core-client-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host-types/serde", "ibc-primitives/serde", "ibc-proto/serde", ] schema = [ "dep:schemars", - "ibc-core-client-types/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host-types/schema", "ibc-primitives/schema", "ibc-proto/json-schema", "serde", @@ -74,18 +74,18 @@ schema = [ ] borsh = [ "dep:borsh", - "ibc-core-client-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", "ibc-primitives/borsh", "ibc-proto/borsh", ] parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", - "ibc-core-client-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics03-connection/types/src/connection.rs b/ibc-eureka-core/ics03-connection/types/src/connection.rs index f518f86a50..81991d5481 100644 --- a/ibc-eureka-core/ics03-connection/types/src/connection.rs +++ b/ibc-eureka-core/ics03-connection/types/src/connection.rs @@ -3,9 +3,9 @@ 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_eureka_core_commitment_types::commitment::CommitmentPrefix; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ClientId, ConnectionId}; use ibc_primitives::prelude::*; use ibc_proto::ibc::core::connection::v1::{ ConnectionEnd as RawConnectionEnd, Counterparty as RawCounterparty, diff --git a/ibc-eureka-core/ics03-connection/types/src/error.rs b/ibc-eureka-core/ics03-connection/types/src/error.rs index fa4ad6297f..b466594de4 100644 --- a/ibc-eureka-core/ics03-connection/types/src/error.rs +++ b/ibc-eureka-core/ics03-connection/types/src/error.rs @@ -1,9 +1,9 @@ //! Defines the connection error type use displaydoc::Display; -use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::Height; -use ibc_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_host_types::error::{DecodingError, HostError, IdentifierError}; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; diff --git a/ibc-eureka-core/ics03-connection/types/src/events.rs b/ibc-eureka-core/ics03-connection/types/src/events.rs index 08f64d9e14..8a9f464950 100644 --- a/ibc-eureka-core/ics03-connection/types/src/events.rs +++ b/ibc-eureka-core/ics03-connection/types/src/events.rs @@ -1,6 +1,6 @@ //! Types for the IBC events emitted from Tendermint Websocket by the connection module. -use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; +use ibc_eureka_core_host_types::identifiers::{ClientId, ConnectionId}; use ibc_primitives::prelude::*; use tendermint::abci; @@ -307,7 +307,7 @@ mod tests { use core::str::FromStr; - use ibc_core_host_types::identifiers::ClientType; + use ibc_eureka_core_host_types::identifiers::ClientType; use tendermint::abci::Event as AbciEvent; use super::*; diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs index 9379c25e36..79b5843ead 100644 --- a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_ack.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ConnectionId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs index 24a58b87e1..853da67081 100644 --- a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_confirm.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ConnectionId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs index 57790a3870..146ac18913 100644 --- a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_init.rs @@ -1,7 +1,7 @@ use core::time::Duration; -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::ClientId; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnectionOpenInit; diff --git a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs index ea5f22f039..281d299ac1 100644 --- a/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs +++ b/ibc-eureka-core/ics03-connection/types/src/msgs/conn_open_try.rs @@ -1,9 +1,9 @@ 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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics03-connection/types/src/version.rs b/ibc-eureka-core/ics03-connection/types/src/version.rs index 89f577cc0a..78e7c95825 100644 --- a/ibc-eureka-core/ics03-connection/types/src/version.rs +++ b/ibc-eureka-core/ics03-connection/types/src/version.rs @@ -2,7 +2,7 @@ use core::fmt::Display; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::utils::PrettySlice; use ibc_proto::ibc::core::connection::v1::Version as RawVersion; diff --git a/ibc-eureka-core/ics04-channel/Cargo.toml b/ibc-eureka-core/ics04-channel/Cargo.toml index 1ad973cc18..b008bc10ad 100644 --- a/ibc-eureka-core/ics04-channel/Cargo.toml +++ b/ibc-eureka-core/ics04-channel/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-channel" +name = "ibc-eureka-core-channel" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -11,73 +11,73 @@ readme = "./../README.md" description = """ Maintained by `ibc-rs`, contains the implementation of the ICS-04 Channel & Packet Semantics and - re-exports essential data structures and domain types from `ibc-core-channel-types` crate. + re-exports essential data structures and domain types from `ibc-eureka-core-channel-types` crate. """ [package.metadata.docs.rs] all-features = true [dependencies] -ibc-core-client = { workspace = true } -ibc-core-connection = { workspace = true } -ibc-core-channel-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-core-router = { workspace = true } -ibc-primitives = { workspace = true } +ibc-eureka-core-client = { workspace = true } +ibc-eureka-core-connection = { workspace = true } +ibc-eureka-core-channel-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-eureka-core-router = { workspace = true } +ibc-primitives = { workspace = true } [features] default = [ "std" ] std = [ - "ibc-core-client/std", - "ibc-core-connection/std", - "ibc-core-channel-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host/std", - "ibc-core-handler-types/std", - "ibc-core-router/std", + "ibc-eureka-core-client/std", + "ibc-eureka-core-connection/std", + "ibc-eureka-core-channel-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host/std", + "ibc-eureka-core-handler-types/std", + "ibc-eureka-core-router/std", "ibc-primitives/std", ] serde = [ - "ibc-core-client/serde", - "ibc-core-connection/serde", - "ibc-core-channel-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host/serde", - "ibc-core-handler-types/serde", - "ibc-core-router/serde", + "ibc-eureka-core-client/serde", + "ibc-eureka-core-connection/serde", + "ibc-eureka-core-channel-types/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host/serde", + "ibc-eureka-core-handler-types/serde", + "ibc-eureka-core-router/serde", "ibc-primitives/serde", ] schema = [ - "ibc-core-client/schema", - "ibc-core-connection/schema", - "ibc-core-channel-types/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host/schema", - "ibc-core-handler-types/schema", - "ibc-core-router/schema", + "ibc-eureka-core-client/schema", + "ibc-eureka-core-connection/schema", + "ibc-eureka-core-channel-types/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host/schema", + "ibc-eureka-core-handler-types/schema", + "ibc-eureka-core-router/schema", "ibc-primitives/schema", "serde", "std", ] borsh = [ - "ibc-core-client/borsh", - "ibc-core-connection/borsh", - "ibc-core-channel-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host/borsh", - "ibc-core-handler-types/borsh", - "ibc-core-router/borsh", + "ibc-eureka-core-client/borsh", + "ibc-eureka-core-connection/borsh", + "ibc-eureka-core-channel-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host/borsh", + "ibc-eureka-core-handler-types/borsh", + "ibc-eureka-core-router/borsh", "ibc-primitives/borsh", ] parity-scale-codec = [ - "ibc-core-client/parity-scale-codec", - "ibc-core-connection/parity-scale-codec", - "ibc-core-channel-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", - "ibc-core-router/parity-scale-codec", + "ibc-eureka-core-client/parity-scale-codec", + "ibc-eureka-core-connection/parity-scale-codec", + "ibc-eureka-core-channel-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", + "ibc-eureka-core-router/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics04-channel/src/context.rs b/ibc-eureka-core/ics04-channel/src/context.rs index 92844b4d46..cf5ecfe468 100644 --- a/ibc-eureka-core/ics04-channel/src/context.rs +++ b/ibc-eureka-core/ics04-channel/src/context.rs @@ -1,14 +1,14 @@ //! ICS4 (channel) context. -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::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}; +use ibc_eureka_core_channel_types::channel::ChannelEnd; +use ibc_eureka_core_channel_types::commitment::PacketCommitment; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::types::ConnectionEnd; +use ibc_eureka_core_handler_types::events::IbcEvent; +use ibc_eureka_core_host::types::error::HostError; +use ibc_eureka_core_host::types::identifiers::{ConnectionId, Sequence}; +use ibc_eureka_core_host::types::path::{ChannelEndPath, CommitmentPath, SeqSendPath}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; use ibc_primitives::prelude::*; /// Methods required in send packet validation, to be implemented by the host diff --git a/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs b/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs index fddea0d821..0fca79e624 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs @@ -1,17 +1,19 @@ -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; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ +use ibc_eureka_core_channel_types::channel::{Counterparty, Order, State as ChannelState}; +use ibc_eureka_core_channel_types::commitment::{ + compute_ack_commitment, compute_packet_commitment, +}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::AcknowledgePacket; +use ibc_eureka_core_channel_types::msgs::MsgAcknowledgement; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::delay::verify_conn_delay_passed; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, SeqAckPath, }; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; pub fn acknowledgement_packet_validate( diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs index 798f628e12..04c694b2cc 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_close_confirm.rs @@ -1,16 +1,18 @@ //! Protocol logic specific to ICS4 messages of type `MsgChannelCloseConfirm`. -use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_channel_types::channel::{ + ChannelEnd, Counterparty, State, State as ChannelState, +}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::CloseConfirm; +use ibc_eureka_core_channel_types::msgs::MsgChannelCloseConfirm; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::types::error::ConnectionError; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs index 6a0a6e1892..acee3b3e1a 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_close_init.rs @@ -1,14 +1,14 @@ //! Protocol logic specific to ICS4 messages of type `MsgChannelCloseInit`. -use ibc_core_channel_types::channel::State; -use ibc_core_channel_types::error::ChannelError; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::ChannelEndPath; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_channel_types::channel::State; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::CloseInit; +use ibc_eureka_core_channel_types::msgs::MsgChannelCloseInit; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::ChannelEndPath; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; pub fn chan_close_init_validate( diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs index c6d81a9fe8..9aef575dba 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_ack.rs @@ -1,15 +1,15 @@ //! Protocol logic specific to ICS4 messages of type `MsgChannelOpenAck`. -use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::OpenAck; +use ibc_eureka_core_channel_types::msgs::MsgChannelOpenAck; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::types::error::ConnectionError; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs index 2f1fc56d37..8a19350e5f 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_confirm.rs @@ -1,16 +1,16 @@ //! Protocol logic specific to ICS4 messages of type `MsgChannelOpenConfirm`. -use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_channel_types::channel::{ChannelEnd, Counterparty, State, State as ChannelState}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::OpenConfirm; +use ibc_eureka_core_channel_types::msgs::MsgChannelOpenConfirm; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::types::error::ConnectionError; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs index fe931efa0c..ac10bc8460 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_init.rs @@ -1,15 +1,15 @@ //! 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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::ChannelId; -use ibc_core_host::types::path::{ChannelEndPath, SeqAckPath, SeqRecvPath, SeqSendPath}; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_channel_types::channel::{ChannelEnd, Counterparty, State}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::OpenInit; +use ibc_eureka_core_channel_types::msgs::MsgChannelOpenInit; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::identifiers::ChannelId; +use ibc_eureka_core_host::types::path::{ChannelEndPath, SeqAckPath, SeqRecvPath, SeqSendPath}; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; pub fn chan_open_init_validate( diff --git a/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs b/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs index c8e7357004..adeeed2ef4 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/chan_open_try.rs @@ -1,19 +1,19 @@ //! Protocol logic specific to ICS4 messages of type `MsgChannelOpenTry`. -use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, State as ChannelState}; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::identifiers::ChannelId; -use ibc_core_host::types::path::{ +use ibc_eureka_core_channel_types::channel::{ChannelEnd, Counterparty, State as ChannelState}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::OpenTry; +use ibc_eureka_core_channel_types::msgs::MsgChannelOpenTry; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::types::error::ConnectionError; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::identifiers::ChannelId; +use ibc_eureka_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, Path, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; diff --git a/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs b/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs index 60bf301990..0056c54733 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/recv_packet.rs @@ -1,19 +1,21 @@ -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; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ +use ibc_eureka_core_channel_types::channel::{Counterparty, Order, State as ChannelState}; +use ibc_eureka_core_channel_types::commitment::{ + compute_ack_commitment, compute_packet_commitment, +}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::{ReceivePacket, WriteAcknowledgement}; +use ibc_eureka_core_channel_types::msgs::MsgRecvPacket; +use ibc_eureka_core_channel_types::packet::Receipt; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::delay::verify_conn_delay_passed; +use ibc_eureka_core_connection::types::State as ConnectionState; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, }; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; pub fn recv_packet_validate(ctx_b: &ValCtx, msg: MsgRecvPacket) -> Result<(), ChannelError> diff --git a/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs b/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs index b393c74add..f4960a4904 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/send_packet.rs @@ -1,11 +1,11 @@ -use ibc_core_channel_types::channel::Counterparty; -use ibc_core_channel_types::commitment::compute_packet_commitment; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ +use ibc_eureka_core_channel_types::channel::Counterparty; +use ibc_eureka_core_channel_types::commitment::compute_packet_commitment; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::SendPacket; +use ibc_eureka_core_channel_types::packet::Packet; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, SeqSendPath, }; use ibc_primitives::prelude::*; diff --git a/ibc-eureka-core/ics04-channel/src/handler/timeout.rs b/ibc-eureka-core/ics04-channel/src/handler/timeout.rs index da4e42d373..bcb945f980 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/timeout.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/timeout.rs @@ -1,16 +1,16 @@ -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; -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::events::{IbcEvent, MessageEvent}; -use ibc_core_host::types::path::{ +use ibc_eureka_core_channel_types::channel::{Counterparty, Order, State}; +use ibc_eureka_core_channel_types::commitment::compute_packet_commitment; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::events::{ChannelClosed, TimeoutPacket}; +use ibc_eureka_core_channel_types::msgs::{MsgTimeout, MsgTimeoutOnClose}; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::delay::verify_conn_delay_passed; +use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent}; +use ibc_eureka_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, }; -use ibc_core_host::{ExecutionContext, ValidationContext}; -use ibc_core_router::module::Module; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::module::Module; use ibc_primitives::prelude::*; use super::timeout_on_close; diff --git a/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs b/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs index 00d880577b..d85f760676 100644 --- a/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs +++ b/ibc-eureka-core/ics04-channel/src/handler/timeout_on_close.rs @@ -1,14 +1,14 @@ -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; -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_connection::types::error::ConnectionError; -use ibc_core_host::types::path::{ +use ibc_eureka_core_channel_types::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc_eureka_core_channel_types::commitment::compute_packet_commitment; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::msgs::MsgTimeoutOnClose; +use ibc_eureka_core_client::context::prelude::*; +use ibc_eureka_core_connection::delay::verify_conn_delay_passed; +use ibc_eureka_core_connection::types::error::ConnectionError; +use ibc_eureka_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, }; -use ibc_core_host::ValidationContext; +use ibc_eureka_core_host::ValidationContext; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; diff --git a/ibc-eureka-core/ics04-channel/src/lib.rs b/ibc-eureka-core/ics04-channel/src/lib.rs index 1ccb4f25cf..59e165a94b 100644 --- a/ibc-eureka-core/ics04-channel/src/lib.rs +++ b/ibc-eureka-core/ics04-channel/src/lib.rs @@ -19,8 +19,8 @@ extern crate std; pub mod context; pub mod handler; -/// Re-exports ICS-04 data structures from the `ibc-core-channel-types` crate. +/// Re-exports ICS-04 data structures from the `ibc-eureka-core-channel-types` crate. pub mod types { #[doc(inline)] - pub use ibc_core_channel_types::*; + pub use ibc_eureka_core_channel_types::*; } diff --git a/ibc-eureka-core/ics04-channel/types/Cargo.toml b/ibc-eureka-core/ics04-channel/types/Cargo.toml index 5cff829cea..2c8e280ba9 100644 --- a/ibc-eureka-core/ics04-channel/types/Cargo.toml +++ b/ibc-eureka-core/ics04-channel/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-channel-types" +name = "ibc-eureka-core-channel-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -29,12 +29,12 @@ serde = { workspace = true, optional = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-client-types = { workspace = true } -ibc-core-connection-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-connection-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -54,29 +54,29 @@ std = [ "sha2/std", "serde/std", "subtle-encoding/std", - "ibc-core-client-types/std", - "ibc-core-connection-types/std", - "ibc-core-host-types/std", - "ibc-core-commitment-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-connection-types/std", + "ibc-eureka-core-host-types/std", + "ibc-eureka-core-commitment-types/std", "ibc-primitives/std", "ibc-proto/std", "tendermint/std", ] serde = [ "dep:serde", - "ibc-core-client-types/serde", - "ibc-core-connection-types/serde", - "ibc-core-host-types/serde", - "ibc-core-commitment-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-connection-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-commitment-types/serde", "ibc-primitives/serde", "ibc-proto/serde", ] schema = [ "dep:schemars", - "ibc-core-client-types/schema", - "ibc-core-connection-types/schema", - "ibc-core-host-types/schema", - "ibc-core-commitment-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-connection-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-commitment-types/schema", "ibc-primitives/schema", "ibc-proto/json-schema", "serde", @@ -84,20 +84,20 @@ schema = [ ] borsh = [ "dep:borsh", - "ibc-core-client-types/borsh", - "ibc-core-connection-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-connection-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", "ibc-primitives/borsh", "ibc-proto/borsh", ] parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", - "ibc-core-client-types/parity-scale-codec", - "ibc-core-connection-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-connection-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs b/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs index efbbe28ce3..f224c68917 100644 --- a/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs +++ b/ibc-eureka-core/ics04-channel/types/src/acknowledgement.rs @@ -3,7 +3,7 @@ use core::fmt::{Display, Error as FmtError, Formatter}; use derive_more::Into; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use crate::error::ChannelError; diff --git a/ibc-eureka-core/ics04-channel/types/src/channel.rs b/ibc-eureka-core/ics04-channel/types/src/channel.rs index 38140cb354..5aa0208b29 100644 --- a/ibc-eureka-core/ics04-channel/types/src/channel.rs +++ b/ibc-eureka-core/ics04-channel/types/src/channel.rs @@ -3,8 +3,8 @@ 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_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::utils::PrettySlice; use ibc_proto::ibc::core::channel::v1::{ diff --git a/ibc-eureka-core/ics04-channel/types/src/commitment.rs b/ibc-eureka-core/ics04-channel/types/src/commitment.rs index ece5fe641d..04d1dd16e8 100644 --- a/ibc-eureka-core/ics04-channel/types/src/commitment.rs +++ b/ibc-eureka-core/ics04-channel/types/src/commitment.rs @@ -126,7 +126,7 @@ mod test { ]; let actual = compute_packet_commitment( b"packet data", - &TimeoutHeight::At(ibc_core_client_types::Height::new(42, 24).unwrap()), + &TimeoutHeight::At(ibc_eureka_core_client_types::Height::new(42, 24).unwrap()), &TimeoutTimestamp::from(0x42), ); assert_eq!(&expected[..], actual.as_ref()); diff --git a/ibc-eureka-core/ics04-channel/types/src/error.rs b/ibc-eureka-core/ics04-channel/types/src/error.rs index 911aed095e..7792494a26 100644 --- a/ibc-eureka-core/ics04-channel/types/src/error.rs +++ b/ibc-eureka-core/ics04-channel/types/src/error.rs @@ -1,11 +1,11 @@ //! Defines the main channel, port, and packet error types use displaydoc::Display; -use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::Height; -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_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_host_types::error::{DecodingError, HostError, IdentifierError}; +use ibc_eureka_core_host_types::identifiers::Sequence; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; diff --git a/ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs b/ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs index 8032fb44d8..b8b993be8a 100644 --- a/ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs +++ b/ibc-eureka-core/ics04-channel/types/src/events/channel_attributes.rs @@ -1,7 +1,7 @@ //! This module holds all the abci event attributes for IBC events emitted //! during the channel handshake. use derive_more::From; -use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc_eureka_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; use tendermint::abci; use crate::Version; diff --git a/ibc-eureka-core/ics04-channel/types/src/events/mod.rs b/ibc-eureka-core/ics04-channel/types/src/events/mod.rs index 4b65a94c62..39f70fd033 100644 --- a/ibc-eureka-core/ics04-channel/types/src/events/mod.rs +++ b/ibc-eureka-core/ics04-channel/types/src/events/mod.rs @@ -3,8 +3,8 @@ 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_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; use ibc_primitives::prelude::*; use tendermint::abci; diff --git a/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs b/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs index c7a2c41799..f27941ccf6 100644 --- a/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs +++ b/ibc-eureka-core/ics04-channel/types/src/events/packet_attributes.rs @@ -4,8 +4,8 @@ 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_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; use ibc_primitives::prelude::*; use subtle_encoding::hex; use tendermint::abci; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs index 10839e91d1..6e20c5746a 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/acknowledgement.rs @@ -1,6 +1,6 @@ -use ibc_core_client_types::Height; -use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgAcknowledgement as RawMsgAcknowledgement; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs index f3cbf1171e..014aa4dab2 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_confirm.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs index 3d852e5fa1..f7800e60e2 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_close_init.rs @@ -1,5 +1,5 @@ -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::{ChannelId, PortId}; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs index 4d1151ff61..a6c1693a33 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_ack.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs index d28fe2a6e4..28309e7ca4 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_confirm.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs index 2299802d49..526ad2fe16 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_init.rs @@ -1,5 +1,5 @@ -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::identifiers::{ConnectionId, PortId}; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ConnectionId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs index 042f2c07a6..cbdebfceab 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/chan_open_try.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs index 8e518591cf..524c2eec83 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/mod.rs @@ -22,7 +22,7 @@ pub use chan_open_ack::*; pub use chan_open_confirm::*; pub use chan_open_init::*; pub use chan_open_try::*; -use ibc_core_host_types::identifiers::*; +use ibc_eureka_core_host_types::identifiers::*; use ibc_primitives::prelude::*; pub use recv_packet::*; pub use timeout::*; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs index c2b14ab418..0d95ddbee2 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/recv_packet.rs @@ -1,6 +1,6 @@ -use ibc_core_client_types::Height; -use ibc_core_commitment_types::commitment::CommitmentProofBytes; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs index 5fc2558966..759f4e7193 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::Sequence; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; diff --git a/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs index 34394b8561..4d24df664f 100644 --- a/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs +++ b/ibc-eureka-core/ics04-channel/types/src/msgs/timeout_on_close.rs @@ -1,7 +1,7 @@ -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_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentProofBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::identifiers::Sequence; use ibc_primitives::prelude::*; use ibc_primitives::Signer; use ibc_proto::ibc::core::channel::v1::MsgTimeoutOnClose as RawMsgTimeoutOnClose; diff --git a/ibc-eureka-core/ics04-channel/types/src/packet.rs b/ibc-eureka-core/ics04-channel/types/src/packet.rs index e5f2e87796..3861d37719 100644 --- a/ibc-eureka-core/ics04-channel/types/src/packet.rs +++ b/ibc-eureka-core/ics04-channel/types/src/packet.rs @@ -1,7 +1,7 @@ //! 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_eureka_core_client_types::Height; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_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}; @@ -91,7 +91,7 @@ pub struct Packet { pub chan_id_on_b: ChannelId, #[cfg_attr( feature = "serde", - serde(serialize_with = "ibc_core_commitment_types::serializer::ser_hex_upper") + serde(serialize_with = "ibc_eureka_core_commitment_types::serializer::ser_hex_upper") )] pub data: Vec, pub timeout_height_on_b: TimeoutHeight, @@ -263,7 +263,7 @@ pub struct PacketState { pub seq: Sequence, #[cfg_attr( feature = "serde", - serde(serialize_with = "ibc_core_commitment_types::serializer::ser_hex_upper") + serde(serialize_with = "ibc_eureka_core_commitment_types::serializer::ser_hex_upper") )] pub data: Vec, } diff --git a/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs b/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs index 46d6ab49a9..d95553b2b3 100644 --- a/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs +++ b/ibc-eureka-core/ics04-channel/types/src/timeout/height.rs @@ -2,8 +2,8 @@ use core::fmt::{Display, Error as FmtError, Formatter}; -use ibc_core_client_types::Height; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::ibc::core::client::v1::Height as RawHeight; @@ -183,7 +183,7 @@ mod serialize { where D: serde::Deserializer<'de>, { - use ibc_core_client_types::Height as Ics02Height; + use ibc_eureka_core_client_types::Height as Ics02Height; // Here we have to use a bespoke struct as well in order to deserialize // a height which may have a revision height equal to zero. diff --git a/ibc-eureka-core/ics23-commitment/types/Cargo.toml b/ibc-eureka-core/ics23-commitment/types/Cargo.toml index b627642fdb..784b22cac9 100644 --- a/ibc-eureka-core/ics23-commitment/types/Cargo.toml +++ b/ibc-eureka-core/ics23-commitment/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-commitment-types" +name = "ibc-eureka-core-commitment-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -28,10 +28,10 @@ serde = { workspace = true, optional = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-proto = { workspace = true } -ibc-primitives = { workspace = true } -ics23 = { version = "0.12", default-features = false, features = [ "host-functions" ] } -ibc-core-host-types = { workspace = true } +ibc-proto = { workspace = true } +ibc-primitives = { workspace = true } +ics23 = { version = "0.12", default-features = false, features = [ "host-functions" ] } +ibc-eureka-core-host-types = { workspace = true } # parity dependencies parity-scale-codec = { workspace = true, optional = true } @@ -47,14 +47,14 @@ std = [ "serde/std", "subtle-encoding/std", "ibc-primitives/std", - "ibc-core-host-types/std", + "ibc-eureka-core-host-types/std", "ibc-proto/std", "ics23/std", ] serde = [ "dep:serde", "ibc-primitives/serde", - "ibc-core-host-types/serde", + "ibc-eureka-core-host-types/serde", "ibc-proto/serde", "ics23/serde", ] @@ -62,7 +62,7 @@ schema = [ "dep:schemars", "ibc-proto/json-schema", "ibc-primitives/schema", - "ibc-core-host-types/schema", + "ibc-eureka-core-host-types/schema", "serde", "std", ] @@ -70,12 +70,12 @@ borsh = [ "dep:borsh", "ibc-proto/borsh", "ibc-primitives/borsh", - "ibc-core-host-types/borsh", + "ibc-eureka-core-host-types/borsh", ] parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", "ibc-primitives/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics23-commitment/types/src/commitment.rs b/ibc-eureka-core/ics23-commitment/types/src/commitment.rs index af1f30153a..5df403db63 100644 --- a/ibc-eureka-core/ics23-commitment/types/src/commitment.rs +++ b/ibc-eureka-core/ics23-commitment/types/src/commitment.rs @@ -2,7 +2,7 @@ use core::fmt; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_primitives::ToVec; use ibc_proto::ibc::core::commitment::v1::MerkleProof as RawMerkleProof; diff --git a/ibc-eureka-core/ics23-commitment/types/src/error.rs b/ibc-eureka-core/ics23-commitment/types/src/error.rs index 3554767ebe..4d0e7c609f 100644 --- a/ibc-eureka-core/ics23-commitment/types/src/error.rs +++ b/ibc-eureka-core/ics23-commitment/types/src/error.rs @@ -1,7 +1,7 @@ //! Defines the commitment error type use displaydoc::Display; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; #[derive(Debug, Display)] diff --git a/ibc-eureka-core/ics23-commitment/types/src/merkle.rs b/ibc-eureka-core/ics23-commitment/types/src/merkle.rs index d2bcea088a..4e67b6db40 100644 --- a/ibc-eureka-core/ics23-commitment/types/src/merkle.rs +++ b/ibc-eureka-core/ics23-commitment/types/src/merkle.rs @@ -1,7 +1,7 @@ //! Merkle proof utilities -use ibc_core_host_types::error::DecodingError; -use ibc_core_host_types::path::PathBytes; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::path::PathBytes; use ibc_primitives::prelude::*; use ibc_primitives::proto::Protobuf; use ibc_proto::ibc::core::commitment::v1::{ diff --git a/ibc-eureka-core/ics23-commitment/types/src/specs.rs b/ibc-eureka-core/ics23-commitment/types/src/specs.rs index 5d0f0f473f..67c62bf987 100644 --- a/ibc-eureka-core/ics23-commitment/types/src/specs.rs +++ b/ibc-eureka-core/ics23-commitment/types/src/specs.rs @@ -1,6 +1,6 @@ //! Defines proof specs, which encode the structure of proofs -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_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}; diff --git a/ibc-eureka-core/ics24-host/Cargo.toml b/ibc-eureka-core/ics24-host/Cargo.toml index c97fb73244..142c0f7bf8 100644 --- a/ibc-eureka-core/ics24-host/Cargo.toml +++ b/ibc-eureka-core/ics24-host/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-host" +name = "ibc-eureka-core-host" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -25,14 +25,14 @@ displaydoc = { workspace = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-client-types = { workspace = true } -ibc-core-client-context = { workspace = true } -ibc-core-connection-types = { workspace = true } -ibc-core-channel-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-primitives = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-client-context = { workspace = true } +ibc-eureka-core-connection-types = { workspace = true } +ibc-eureka-core-channel-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } [dev-dependencies] rstest = { workspace = true } @@ -42,53 +42,53 @@ default = [ "std" ] std = [ "displaydoc/std", "subtle-encoding/std", - "ibc-core-client-types/std", - "ibc-core-client-context/std", - "ibc-core-connection-types/std", - "ibc-core-channel-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", - "ibc-core-handler-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-client-context/std", + "ibc-eureka-core-connection-types/std", + "ibc-eureka-core-channel-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host-types/std", + "ibc-eureka-core-handler-types/std", "ibc-primitives/std", ] serde = [ - "ibc-core-client-types/serde", - "ibc-core-client-context/serde", - "ibc-core-connection-types/serde", - "ibc-core-channel-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host-types/serde", - "ibc-core-handler-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-client-context/serde", + "ibc-eureka-core-connection-types/serde", + "ibc-eureka-core-channel-types/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-handler-types/serde", "ibc-primitives/serde", ] schema = [ - "ibc-core-client-types/schema", - "ibc-core-client-context/schema", - "ibc-core-connection-types/schema", - "ibc-core-channel-types/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host-types/schema", - "ibc-core-handler-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-client-context/schema", + "ibc-eureka-core-connection-types/schema", + "ibc-eureka-core-channel-types/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-handler-types/schema", "ibc-primitives/schema", "serde", "std", ] borsh = [ - "ibc-core-client-types/borsh", - "ibc-core-client-context/borsh", - "ibc-core-connection-types/borsh", - "ibc-core-channel-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", - "ibc-core-handler-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-client-context/borsh", + "ibc-eureka-core-connection-types/borsh", + "ibc-eureka-core-channel-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", + "ibc-eureka-core-handler-types/borsh", "ibc-primitives/borsh", ] parity-scale-codec = [ - "ibc-core-client-types/parity-scale-codec", - "ibc-core-connection-types/parity-scale-codec", - "ibc-core-channel-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-connection-types/parity-scale-codec", + "ibc-eureka-core-channel-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics24-host/cosmos/Cargo.toml b/ibc-eureka-core/ics24-host/cosmos/Cargo.toml index 50e06f273b..6145d0d9c1 100644 --- a/ibc-eureka-core/ics24-host/cosmos/Cargo.toml +++ b/ibc-eureka-core/ics24-host/cosmos/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-host-cosmos" +name = "ibc-eureka-core-host-cosmos" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -28,16 +28,16 @@ sha2 = { workspace = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-client-tendermint = { workspace = true } -ibc-app-transfer-types = { workspace = true } -ibc-core-client-types = { workspace = true } -ibc-core-client-context = { workspace = true } -ibc-core-connection-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } +ibc-client-tendermint = { workspace = true } +ibc-app-transfer-types = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-client-context = { workspace = true } +ibc-eureka-core-connection-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -53,35 +53,35 @@ std = [ "serde/std", "sha2/std", "subtle-encoding/std", - "ibc-core-client-types/std", - "ibc-core-client-context/std", - "ibc-core-connection-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", - "ibc-core-handler-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-client-context/std", + "ibc-eureka-core-connection-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host-types/std", + "ibc-eureka-core-handler-types/std", "ibc-primitives/std", "ibc-proto/std", ] serde = [ "dep:serde", "ibc-client-tendermint/serde", - "ibc-core-client-types/serde", - "ibc-core-client-context/serde", - "ibc-core-connection-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host-types/serde", - "ibc-core-handler-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-client-context/serde", + "ibc-eureka-core-connection-types/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-handler-types/serde", "ibc-primitives/serde", "ibc-proto/serde", ] schema = [ "ibc-client-tendermint/schema", - "ibc-core-client-types/schema", - "ibc-core-client-context/schema", - "ibc-core-connection-types/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host-types/schema", - "ibc-core-handler-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-client-context/schema", + "ibc-eureka-core-connection-types/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-handler-types/schema", "ibc-primitives/schema", "ibc-proto/json-schema", "serde", @@ -90,11 +90,11 @@ schema = [ borsh = [ "dep:borsh", "ibc-client-tendermint/borsh", - "ibc-core-client-types/borsh", - "ibc-core-client-context/borsh", - "ibc-core-connection-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-client-context/borsh", + "ibc-eureka-core-connection-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", "ibc-primitives/borsh", "ibc-proto/borsh", ] @@ -102,11 +102,11 @@ parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", "ibc-client-tendermint/parity-scale-codec", - "ibc-core-client-types/parity-scale-codec", - "ibc-core-connection-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-connection-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs index 89ec14ff3a..2f56a15484 100644 --- a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/context.rs @@ -5,9 +5,9 @@ //! [Basecoin-rs](https://github.com/informalsystems/basecoin-rs) repository. //! If it proves to be generic enough, we may move it to the ICS02 section. -use ibc_core_client_context::ClientValidationContext; -use ibc_core_host_types::error::HostError; -use ibc_core_host_types::path::{UpgradeClientStatePath, UpgradeConsensusStatePath}; +use ibc_eureka_core_client_context::ClientValidationContext; +use ibc_eureka_core_host_types::error::HostError; +use ibc_eureka_core_host_types::path::{UpgradeClientStatePath, UpgradeConsensusStatePath}; use super::Plan; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs index 419a1e46ce..258231902a 100644 --- a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs @@ -1,6 +1,6 @@ use ibc_client_tendermint::types::ClientState as TmClientState; -use ibc_core_client_types::error::UpgradeClientError; -use ibc_core_host_types::path::UpgradeClientStatePath; +use ibc_eureka_core_client_types::error::UpgradeClientError; +use ibc_eureka_core_host_types::path::UpgradeClientStatePath; use ibc_primitives::prelude::*; use tendermint::abci::Event as TmEvent; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs index 6541ad049a..0729379e46 100644 --- a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs +++ b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs @@ -1,6 +1,6 @@ //! Definition of domain `Plan` type. -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::cosmos::upgrade::v1beta1::Plan as RawPlan; use ibc_proto::google::protobuf::Any; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs b/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs index a98441ce7d..8ed310aeea 100644 --- a/ibc-eureka-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs +++ b/ibc-eureka-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_host_types::error::DecodingError; +use ibc_eureka_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; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/utils.rs b/ibc-eureka-core/ics24-host/cosmos/src/utils.rs index 613a48f362..16151f959e 100644 --- a/ibc-eureka-core/ics24-host/cosmos/src/utils.rs +++ b/ibc-eureka-core/ics24-host/cosmos/src/utils.rs @@ -1,5 +1,5 @@ use ibc_app_transfer_types::VERSION; -use ibc_core_host_types::identifiers::{ChannelId, PortId}; +use ibc_eureka_core_host_types::identifiers::{ChannelId, PortId}; use ibc_primitives::prelude::*; use sha2::{Digest, Sha256}; diff --git a/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs b/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs index 96b13306cd..68443fb5ad 100644 --- a/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs +++ b/ibc-eureka-core/ics24-host/cosmos/src/validate_self_client.rs @@ -1,10 +1,10 @@ use core::time::Duration; use ibc_client_tendermint::types::ClientState as TmClientState; -use ibc_core_client_types::Height; -use ibc_core_commitment_types::specs::ProofSpecs; -use ibc_core_host_types::error::HostError; -use ibc_core_host_types::identifiers::ChainId; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::specs::ProofSpecs; +use ibc_eureka_core_host_types::error::HostError; +use ibc_eureka_core_host_types::identifiers::ChainId; use ibc_primitives::prelude::*; use tendermint::trust_threshold::TrustThresholdFraction as TendermintTrustThresholdFraction; diff --git a/ibc-eureka-core/ics24-host/src/context.rs b/ibc-eureka-core/ics24-host/src/context.rs index a9a84ab53a..8b9440d6ab 100644 --- a/ibc-eureka-core/ics24-host/src/context.rs +++ b/ibc-eureka-core/ics24-host/src/context.rs @@ -1,17 +1,17 @@ use core::time::Duration; -use ibc_core_channel_types::channel::ChannelEnd; -use ibc_core_channel_types::commitment::{AcknowledgementCommitment, PacketCommitment}; -use ibc_core_channel_types::packet::Receipt; -use ibc_core_client_context::prelude::*; -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::events::IbcEvent; -use ibc_core_host_types::error::HostError; -use ibc_core_host_types::identifiers::{ConnectionId, Sequence}; -use ibc_core_host_types::path::{ +use ibc_eureka_core_channel_types::channel::ChannelEnd; +use ibc_eureka_core_channel_types::commitment::{AcknowledgementCommitment, PacketCommitment}; +use ibc_eureka_core_channel_types::packet::Receipt; +use ibc_eureka_core_client_context::prelude::*; +use ibc_eureka_core_client_types::Height; +use ibc_eureka_core_commitment_types::commitment::CommitmentPrefix; +use ibc_eureka_core_connection_types::version::{pick_version, Version as ConnectionVersion}; +use ibc_eureka_core_connection_types::ConnectionEnd; +use ibc_eureka_core_handler_types::events::IbcEvent; +use ibc_eureka_core_host_types::error::HostError; +use ibc_eureka_core_host_types::identifiers::{ConnectionId, Sequence}; +use ibc_eureka_core_host_types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, CommitmentPath, ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; @@ -22,7 +22,7 @@ use crate::utils::calculate_block_delay; /// Context to be implemented by the host that provides all "read-only" methods. /// -/// Trait used for the top-level `validate` entrypoint in the `ibc-core` crate. +/// Trait used for the top-level `validate` entrypoint in the `ibc-eureka-core` crate. pub trait ValidationContext { type V: ClientValidationContext; /// The client state type for the host chain. @@ -58,7 +58,7 @@ pub trait ValidationContext { /// requirements](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#client-state-validation) /// /// Additionally, implementations specific to individual chains can be found - /// in the `ibc-core/ics24-host` module. + /// in the `ibc-eureka-core/ics24-host` module. fn validate_self_client( &self, client_state_of_host_on_counterparty: Self::HostClientState, @@ -144,7 +144,7 @@ pub trait ValidationContext { /// Context to be implemented by the host that provides all "write-only" methods. /// -/// Trait used for the top-level `execute` and `dispatch` entrypoints in the `ibc-core` crate. +/// Trait used for the top-level `execute` and `dispatch` entrypoints in the `ibc-eureka-core` crate. pub trait ExecutionContext: ValidationContext { type E: ClientExecutionContext; diff --git a/ibc-eureka-core/ics24-host/src/lib.rs b/ibc-eureka-core/ics24-host/src/lib.rs index 7d64b4e387..414c7129a6 100644 --- a/ibc-eureka-core/ics24-host/src/lib.rs +++ b/ibc-eureka-core/ics24-host/src/lib.rs @@ -22,8 +22,8 @@ pub(crate) mod utils; mod context; pub use context::*; -/// Re-exports ICS-24 data structures from `ibc-core-host-types` crate. +/// Re-exports ICS-24 data structures from `ibc-eureka-core-host-types` crate. pub mod types { #[doc(inline)] - pub use ibc_core_host_types::*; + pub use ibc_eureka_core_host_types::*; } diff --git a/ibc-eureka-core/ics24-host/types/Cargo.toml b/ibc-eureka-core/ics24-host/types/Cargo.toml index e4953c7205..899593d49a 100644 --- a/ibc-eureka-core/ics24-host/types/Cargo.toml +++ b/ibc-eureka-core/ics24-host/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-host-types" +name = "ibc-eureka-core-host-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs index c96ccc36f3..3a7895f993 100644 --- a/ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/chain_id.rs @@ -47,7 +47,7 @@ impl ChainId { /// the revision number is parsed. Otherwise, the revision number is set to 0. /// /// ``` - /// use ibc_core_host_types::identifiers::ChainId; + /// use ibc_eureka_core_host_types::identifiers::ChainId; /// /// let chain_id = "chainA"; /// let id = ChainId::new(chain_id).unwrap(); @@ -83,7 +83,7 @@ impl ChainId { /// the revision number overflows. /// /// ``` - /// use ibc_core_host_types::identifiers::ChainId; + /// use ibc_eureka_core_host_types::identifiers::ChainId; /// /// let mut chain_id = ChainId::new("chainA-1").unwrap(); /// assert!(chain_id.increment_revision_number().is_ok()); diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs index c8f9e30d77..f125fac0b6 100644 --- a/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/channel_id.rs @@ -34,7 +34,7 @@ impl ChannelId { /// accepts a single argument, the `counter`. /// /// ``` - /// # use ibc_core_host_types::identifiers::ChannelId; + /// # use ibc_eureka_core_host_types::identifiers::ChannelId; /// let chan_id = ChannelId::new(27); /// assert_eq!(chan_id.to_string(), "channel-27"); /// ``` @@ -87,7 +87,7 @@ impl AsRef for ChannelId { /// Equality check against string literal (satisfies &ChannelId == &str). /// ``` /// use core::str::FromStr; -/// use ibc_core_host_types::identifiers::ChannelId; +/// use ibc_eureka_core_host_types::identifiers::ChannelId; /// let channel_id = ChannelId::from_str("channel-0"); /// assert!(channel_id.is_ok()); /// channel_id.map(|id| {assert_eq!(&id, "channel-0")}); diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs index d0128336b4..abc03cc1d7 100644 --- a/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/client_id.rs @@ -36,8 +36,8 @@ impl ClientId { /// # Example /// /// ``` - /// # use ibc_core_host_types::identifiers::ClientId; - /// # use ibc_core_host_types::identifiers::ClientType; + /// # use ibc_eureka_core_host_types::identifiers::ClientId; + /// # use ibc_eureka_core_host_types::identifiers::ClientType; /// # use std::str::FromStr; /// let client_type = ClientType::from_str("07-tendermint").unwrap(); /// let client_id = &client_type.build_client_id(0); @@ -91,7 +91,7 @@ impl FromStr for ClientId { /// Equality check against string literal (satisfies &ClientId == &str). /// ``` /// use core::str::FromStr; -/// use ibc_core_host_types::identifiers::ClientId; +/// use ibc_eureka_core_host_types::identifiers::ClientId; /// let client_id = ClientId::from_str("clientidtwo"); /// assert!(client_id.is_ok()); /// client_id.map(|id| {assert_eq!(&id, "clientidtwo")}); diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs index 8f0547aa47..c21d346bf0 100644 --- a/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/client_type.rs @@ -39,8 +39,8 @@ impl ClientType { /// infallible since client type is assumed to be correct. /// /// ``` - /// # use ibc_core_host_types::identifiers::ClientId; - /// # use ibc_core_host_types::identifiers::ClientType; + /// # use ibc_eureka_core_host_types::identifiers::ClientId; + /// # use ibc_eureka_core_host_types::identifiers::ClientType; /// # use std::str::FromStr; /// let client_type = ClientType::from_str("07-tendermint").unwrap(); /// let client_id = client_type.build_client_id(14); diff --git a/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs b/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs index 9fe9657cc3..9fbd7e7af3 100644 --- a/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs +++ b/ibc-eureka-core/ics24-host/types/src/identifiers/connection_id.rs @@ -33,7 +33,7 @@ impl ConnectionId { /// `ConnectionId::prefix()`) so this method accepts a single argument, the `counter`. /// /// ``` - /// # use ibc_core_host_types::identifiers::ConnectionId; + /// # use ibc_eureka_core_host_types::identifiers::ConnectionId; /// let conn_id = ConnectionId::new(11); /// assert_eq!(&conn_id, "connection-11"); /// ``` @@ -81,7 +81,7 @@ impl FromStr for ConnectionId { /// Equality check against string literal (satisfies &ConnectionId == &str). /// ``` /// use core::str::FromStr; -/// use ibc_core_host_types::identifiers::ConnectionId; +/// use ibc_eureka_core_host_types::identifiers::ConnectionId; /// let conn_id = ConnectionId::from_str("connection-0"); /// assert!(conn_id.is_ok()); /// conn_id.map(|id| {assert_eq!(&id, "connection-0")}); diff --git a/ibc-eureka-core/ics25-handler/Cargo.toml b/ibc-eureka-core/ics25-handler/Cargo.toml index 43dcf44ed1..886388cd8f 100644 --- a/ibc-eureka-core/ics25-handler/Cargo.toml +++ b/ibc-eureka-core/ics25-handler/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-handler" +name = "ibc-eureka-core-handler" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -19,65 +19,65 @@ description = """ all-features = true [dependencies] -ibc-core-client = { workspace = true } -ibc-core-connection = { workspace = true } -ibc-core-channel = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-router = { workspace = true } -ibc-core-handler-types = { workspace = true } -ibc-core-host = { workspace = true } -ibc-primitives = { workspace = true } +ibc-eureka-core-client = { workspace = true } +ibc-eureka-core-connection = { workspace = true } +ibc-eureka-core-channel = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-router = { workspace = true } +ibc-eureka-core-handler-types = { workspace = true } +ibc-eureka-core-host = { workspace = true } +ibc-primitives = { workspace = true } [features] default = [ "std" ] std = [ - "ibc-core-client/std", - "ibc-core-connection/std", - "ibc-core-channel/std", - "ibc-core-commitment-types/std", - "ibc-core-host/std", - "ibc-core-router/std", - "ibc-core-handler-types/std", + "ibc-eureka-core-client/std", + "ibc-eureka-core-connection/std", + "ibc-eureka-core-channel/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host/std", + "ibc-eureka-core-router/std", + "ibc-eureka-core-handler-types/std", "ibc-primitives/std", ] serde = [ - "ibc-core-client/serde", - "ibc-core-connection/serde", - "ibc-core-channel/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host/serde", - "ibc-core-router/serde", - "ibc-core-handler-types/serde", + "ibc-eureka-core-client/serde", + "ibc-eureka-core-connection/serde", + "ibc-eureka-core-channel/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host/serde", + "ibc-eureka-core-router/serde", + "ibc-eureka-core-handler-types/serde", "ibc-primitives/serde", ] borsh = [ - "ibc-core-client/borsh", - "ibc-core-connection/borsh", - "ibc-core-channel/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host/borsh", - "ibc-core-router/borsh", - "ibc-core-handler-types/borsh", + "ibc-eureka-core-client/borsh", + "ibc-eureka-core-connection/borsh", + "ibc-eureka-core-channel/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host/borsh", + "ibc-eureka-core-router/borsh", + "ibc-eureka-core-handler-types/borsh", "ibc-primitives/borsh", ] schema = [ - "ibc-core-client/schema", - "ibc-core-connection/schema", - "ibc-core-channel/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host/schema", - "ibc-core-router/schema", - "ibc-core-handler-types/schema", + "ibc-eureka-core-client/schema", + "ibc-eureka-core-connection/schema", + "ibc-eureka-core-channel/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host/schema", + "ibc-eureka-core-router/schema", + "ibc-eureka-core-handler-types/schema", "ibc-primitives/schema", "serde", "std", ] parity-scale-codec = [ - "ibc-core-client/parity-scale-codec", - "ibc-core-connection/parity-scale-codec", - "ibc-core-channel/parity-scale-codec", - "ibc-core-host/parity-scale-codec", - "ibc-core-router/parity-scale-codec", - "ibc-core-handler-types/parity-scale-codec", + "ibc-eureka-core-client/parity-scale-codec", + "ibc-eureka-core-connection/parity-scale-codec", + "ibc-eureka-core-channel/parity-scale-codec", + "ibc-eureka-core-host/parity-scale-codec", + "ibc-eureka-core-router/parity-scale-codec", + "ibc-eureka-core-handler-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics25-handler/src/entrypoint.rs b/ibc-eureka-core/ics25-handler/src/entrypoint.rs index f9a0cabc32..5d128b67d3 100644 --- a/ibc-eureka-core/ics25-handler/src/entrypoint.rs +++ b/ibc-eureka-core/ics25-handler/src/entrypoint.rs @@ -1,4 +1,4 @@ -use ibc_core_channel::handler::{ +use ibc_eureka_core_channel::handler::{ acknowledgement_packet_execute, acknowledgement_packet_validate, chan_close_confirm_execute, chan_close_confirm_validate, chan_close_init_execute, chan_close_init_validate, chan_open_ack_execute, chan_open_ack_validate, chan_open_confirm_execute, @@ -6,23 +6,23 @@ use ibc_core_channel::handler::{ chan_open_try_execute, chan_open_try_validate, recv_packet_execute, recv_packet_validate, timeout_packet_execute, timeout_packet_validate, TimeoutMsgType, }; -use ibc_core_channel::types::msgs::{ +use ibc_eureka_core_channel::types::msgs::{ channel_msg_to_port_id, packet_msg_to_port_id, ChannelMsg, PacketMsg, }; -use ibc_core_client::context::{ClientExecutionContext, ClientValidationContext}; -use ibc_core_client::handler::{create_client, update_client, upgrade_client}; -use ibc_core_client::types::error::ClientError; -use ibc_core_client::types::msgs::{ClientMsg, MsgUpdateOrMisbehaviour}; -use ibc_core_connection::handler::{ +use ibc_eureka_core_client::context::{ClientExecutionContext, ClientValidationContext}; +use ibc_eureka_core_client::handler::{create_client, update_client, upgrade_client}; +use ibc_eureka_core_client::types::error::ClientError; +use ibc_eureka_core_client::types::msgs::{ClientMsg, MsgUpdateOrMisbehaviour}; +use ibc_eureka_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::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_eureka_core_connection::types::msgs::ConnectionMsg; +use ibc_eureka_core_handler_types::error::HandlerError; +use ibc_eureka_core_handler_types::msgs::MsgEnvelope; +use ibc_eureka_core_host::types::error::HostError; +use ibc_eureka_core_host::{ExecutionContext, ValidationContext}; +use ibc_eureka_core_router::router::Router; +use ibc_eureka_core_router::types::error::RouterError; use ibc_primitives::prelude::*; use ibc_primitives::proto::Any; diff --git a/ibc-eureka-core/ics25-handler/src/lib.rs b/ibc-eureka-core/ics25-handler/src/lib.rs index 783e8e6283..cce78b7083 100644 --- a/ibc-eureka-core/ics25-handler/src/lib.rs +++ b/ibc-eureka-core/ics25-handler/src/lib.rs @@ -5,7 +5,7 @@ //! //! When processing a given message `M`, if any method in this library returns //! an error, the runtime is expected to rollback all state modifications made -//! to the context (e.g. [`ExecutionContext`](ibc_core_host::ExecutionContext)) +//! to the context (e.g. [`ExecutionContext`](ibc_eureka_core_host::ExecutionContext)) //! while processing `M`. If the transaction containing `M` consists of multiple //! messages, then typically the state modifications from all messages are //! expected to be rolled back as well. @@ -26,8 +26,8 @@ extern crate std; pub mod entrypoint; -/// Re-export IBC handler types from `ibc-core-handler-types` crate. +/// Re-export IBC handler types from `ibc-eureka-core-handler-types` crate. pub mod types { #[doc(inline)] - pub use ibc_core_handler_types::*; + pub use ibc_eureka_core_handler_types::*; } diff --git a/ibc-eureka-core/ics25-handler/types/Cargo.toml b/ibc-eureka-core/ics25-handler/types/Cargo.toml index 8e51544933..9fd8e6418a 100644 --- a/ibc-eureka-core/ics25-handler/types/Cargo.toml +++ b/ibc-eureka-core/ics25-handler/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-handler-types" +name = "ibc-eureka-core-handler-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -28,14 +28,14 @@ schemars = { workspace = true, optional = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-client-types = { workspace = true } -ibc-core-connection-types = { workspace = true } -ibc-core-channel-types = { workspace = true } -ibc-core-commitment-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-core-router-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } +ibc-eureka-core-client-types = { workspace = true } +ibc-eureka-core-connection-types = { workspace = true } +ibc-eureka-core-channel-types = { workspace = true } +ibc-eureka-core-commitment-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-eureka-core-router-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -50,35 +50,35 @@ std = [ "displaydoc/std", "serde/std", "subtle-encoding/std", - "ibc-core-client-types/std", - "ibc-core-connection-types/std", - "ibc-core-channel-types/std", - "ibc-core-commitment-types/std", - "ibc-core-host-types/std", - "ibc-core-router-types/std", + "ibc-eureka-core-client-types/std", + "ibc-eureka-core-connection-types/std", + "ibc-eureka-core-channel-types/std", + "ibc-eureka-core-commitment-types/std", + "ibc-eureka-core-host-types/std", + "ibc-eureka-core-router-types/std", "ibc-primitives/std", "ibc-proto/std", "tendermint/std", ] serde = [ "dep:serde", - "ibc-core-client-types/serde", - "ibc-core-connection-types/serde", - "ibc-core-channel-types/serde", - "ibc-core-commitment-types/serde", - "ibc-core-host-types/serde", - "ibc-core-router-types/serde", + "ibc-eureka-core-client-types/serde", + "ibc-eureka-core-connection-types/serde", + "ibc-eureka-core-channel-types/serde", + "ibc-eureka-core-commitment-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-router-types/serde", "ibc-primitives/serde", "ibc-proto/serde", ] schema = [ "dep:schemars", - "ibc-core-client-types/schema", - "ibc-core-connection-types/schema", - "ibc-core-channel-types/schema", - "ibc-core-commitment-types/schema", - "ibc-core-host-types/schema", - "ibc-core-router-types/schema", + "ibc-eureka-core-client-types/schema", + "ibc-eureka-core-connection-types/schema", + "ibc-eureka-core-channel-types/schema", + "ibc-eureka-core-commitment-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-router-types/schema", "ibc-primitives/schema", "ibc-proto/json-schema", "serde", @@ -86,24 +86,24 @@ schema = [ ] borsh = [ "dep:borsh", - "ibc-core-client-types/borsh", - "ibc-core-connection-types/borsh", - "ibc-core-channel-types/borsh", - "ibc-core-commitment-types/borsh", - "ibc-core-host-types/borsh", - "ibc-core-router-types/borsh", + "ibc-eureka-core-client-types/borsh", + "ibc-eureka-core-connection-types/borsh", + "ibc-eureka-core-channel-types/borsh", + "ibc-eureka-core-commitment-types/borsh", + "ibc-eureka-core-host-types/borsh", + "ibc-eureka-core-router-types/borsh", "ibc-primitives/borsh", "ibc-proto/borsh", ] parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", - "ibc-core-client-types/parity-scale-codec", - "ibc-core-connection-types/parity-scale-codec", - "ibc-core-channel-types/parity-scale-codec", - "ibc-core-commitment-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", - "ibc-core-router-types/parity-scale-codec", + "ibc-eureka-core-client-types/parity-scale-codec", + "ibc-eureka-core-connection-types/parity-scale-codec", + "ibc-eureka-core-channel-types/parity-scale-codec", + "ibc-eureka-core-commitment-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", + "ibc-eureka-core-router-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics25-handler/types/src/error.rs b/ibc-eureka-core/ics25-handler/types/src/error.rs index fce14adc9c..2ce21e1b97 100644 --- a/ibc-eureka-core/ics25-handler/types/src/error.rs +++ b/ibc-eureka-core/ics25-handler/types/src/error.rs @@ -2,10 +2,10 @@ use derive_more::From; use displaydoc::Display; -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_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_client_types::error::ClientError; +use ibc_eureka_core_connection_types::error::ConnectionError; +use ibc_eureka_core_router_types::error::RouterError; use ibc_primitives::prelude::*; /// Top-level type that surfaces errors from the core ibc-rs crates. diff --git a/ibc-eureka-core/ics25-handler/types/src/events.rs b/ibc-eureka-core/ics25-handler/types/src/events.rs index 77ebe79e1a..40c29e4564 100644 --- a/ibc-eureka-core/ics25-handler/types/src/events.rs +++ b/ibc-eureka-core/ics25-handler/types/src/events.rs @@ -1,10 +1,10 @@ //! Defines events emitted during handling of IBC messages -use ibc_core_channel_types::events as ChannelEvents; -use ibc_core_client_types::events::{self as ClientEvents}; -use ibc_core_connection_types::events as ConnectionEvents; -use ibc_core_host_types::error::DecodingError; -use ibc_core_router_types::event::ModuleEvent; +use ibc_eureka_core_channel_types::events as ChannelEvents; +use ibc_eureka_core_client_types::events::{self as ClientEvents}; +use ibc_eureka_core_connection_types::events as ConnectionEvents; +use ibc_eureka_core_host_types::error::DecodingError; +use ibc_eureka_core_router_types::event::ModuleEvent; use ibc_primitives::prelude::*; use tendermint::abci; diff --git a/ibc-eureka-core/ics25-handler/types/src/msgs.rs b/ibc-eureka-core/ics25-handler/types/src/msgs.rs index a47a9b7021..e615328d92 100644 --- a/ibc-eureka-core/ics25-handler/types/src/msgs.rs +++ b/ibc-eureka-core/ics25-handler/types/src/msgs.rs @@ -1,4 +1,4 @@ -use ibc_core_channel_types::msgs::{ +use ibc_eureka_core_channel_types::msgs::{ ChannelMsg, MsgAcknowledgement, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck, MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, MsgRecvPacket, MsgTimeout, MsgTimeoutOnClose, PacketMsg, ACKNOWLEDGEMENT_TYPE_URL, CHAN_CLOSE_CONFIRM_TYPE_URL, @@ -7,17 +7,17 @@ use ibc_core_channel_types::msgs::{ TIMEOUT_ON_CLOSE_TYPE_URL, TIMEOUT_TYPE_URL, }; #[allow(deprecated)] -use ibc_core_client_types::msgs::{ +use ibc_eureka_core_client_types::msgs::{ ClientMsg, MsgCreateClient, MsgSubmitMisbehaviour, MsgUpdateClient, MsgUpgradeClient, CREATE_CLIENT_TYPE_URL, SUBMIT_MISBEHAVIOUR_TYPE_URL, UPDATE_CLIENT_TYPE_URL, UPGRADE_CLIENT_TYPE_URL, }; -use ibc_core_connection_types::msgs::{ +use ibc_eureka_core_connection_types::msgs::{ ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, MsgConnectionOpenTry, CONN_OPEN_ACK_TYPE_URL, CONN_OPEN_CONFIRM_TYPE_URL, CONN_OPEN_INIT_TYPE_URL, CONN_OPEN_TRY_TYPE_URL, }; -use ibc_core_host_types::error::DecodingError; +use ibc_eureka_core_host_types::error::DecodingError; use ibc_primitives::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::Protobuf; diff --git a/ibc-eureka-core/ics26-routing/Cargo.toml b/ibc-eureka-core/ics26-routing/Cargo.toml index cf809acf14..536d179956 100644 --- a/ibc-eureka-core/ics26-routing/Cargo.toml +++ b/ibc-eureka-core/ics26-routing/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-router" +name = "ibc-eureka-core-router" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -24,10 +24,10 @@ displaydoc = { workspace = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-primitives = { workspace = true } -ibc-core-channel-types = { workspace = true } -ibc-core-host-types = { workspace = true } -ibc-core-router-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-eureka-core-channel-types = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-eureka-core-router-types = { workspace = true } [features] default = [ "std" ] @@ -35,33 +35,33 @@ std = [ "displaydoc/std", "subtle-encoding/std", "ibc-primitives/std", - "ibc-core-channel-types/std", - "ibc-core-host-types/std", - "ibc-core-router-types/std", + "ibc-eureka-core-channel-types/std", + "ibc-eureka-core-host-types/std", + "ibc-eureka-core-router-types/std", ] serde = [ "ibc-primitives/serde", - "ibc-core-channel-types/serde", - "ibc-core-host-types/serde", - "ibc-core-router-types/serde", + "ibc-eureka-core-channel-types/serde", + "ibc-eureka-core-host-types/serde", + "ibc-eureka-core-router-types/serde", ] borsh = [ "ibc-primitives/borsh", - "ibc-core-channel-types/borsh", - "ibc-core-host-types/borsh", - "ibc-core-router-types/borsh", + "ibc-eureka-core-channel-types/borsh", + "ibc-eureka-core-host-types/borsh", + "ibc-eureka-core-router-types/borsh", ] schema = [ - "ibc-core-channel-types/schema", - "ibc-core-host-types/schema", - "ibc-core-router-types/schema", + "ibc-eureka-core-channel-types/schema", + "ibc-eureka-core-host-types/schema", + "ibc-eureka-core-router-types/schema", "ibc-primitives/schema", "serde", "std", ] parity-scale-codec = [ - "ibc-core-channel-types/parity-scale-codec", - "ibc-core-host-types/parity-scale-codec", - "ibc-core-router-types/parity-scale-codec", + "ibc-eureka-core-channel-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", + "ibc-eureka-core-router-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics26-routing/src/lib.rs b/ibc-eureka-core/ics26-routing/src/lib.rs index b5b585ee59..7d7a51e937 100644 --- a/ibc-eureka-core/ics26-routing/src/lib.rs +++ b/ibc-eureka-core/ics26-routing/src/lib.rs @@ -17,8 +17,8 @@ extern crate std; pub mod module; pub mod router; -/// Re-exports router data structures from the `ibc-core-router-types` crate +/// Re-exports router data structures from the `ibc-eureka-core-router-types` crate pub mod types { #[doc(inline)] - pub use ibc_core_router_types::*; + pub use ibc_eureka_core_router_types::*; } diff --git a/ibc-eureka-core/ics26-routing/src/module.rs b/ibc-eureka-core/ics26-routing/src/module.rs index a0877b98d7..c488a5c04d 100644 --- a/ibc-eureka-core/ics26-routing/src/module.rs +++ b/ibc-eureka-core/ics26-routing/src/module.rs @@ -1,13 +1,13 @@ /// The trait that defines an IBC application 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; -use ibc_core_channel_types::packet::Packet; -use ibc_core_channel_types::Version; -use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; -use ibc_core_router_types::module::ModuleExtras; +use ibc_eureka_core_channel_types::acknowledgement::Acknowledgement; +use ibc_eureka_core_channel_types::channel::{Counterparty, Order}; +use ibc_eureka_core_channel_types::error::ChannelError; +use ibc_eureka_core_channel_types::packet::Packet; +use ibc_eureka_core_channel_types::Version; +use ibc_eureka_core_host_types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc_eureka_core_router_types::module::ModuleExtras; use ibc_primitives::prelude::*; use ibc_primitives::Signer; diff --git a/ibc-eureka-core/ics26-routing/src/router.rs b/ibc-eureka-core/ics26-routing/src/router.rs index b73d16d5c1..f3f01c09df 100644 --- a/ibc-eureka-core/ics26-routing/src/router.rs +++ b/ibc-eureka-core/ics26-routing/src/router.rs @@ -1,7 +1,7 @@ //! Defines the `Router`, which binds modules to ports -use ibc_core_host_types::identifiers::PortId; -use ibc_core_router_types::module::ModuleId; +use ibc_eureka_core_host_types::identifiers::PortId; +use ibc_eureka_core_router_types::module::ModuleId; use crate::module::Module; diff --git a/ibc-eureka-core/ics26-routing/types/Cargo.toml b/ibc-eureka-core/ics26-routing/types/Cargo.toml index 71acf4beee..b823bde857 100644 --- a/ibc-eureka-core/ics26-routing/types/Cargo.toml +++ b/ibc-eureka-core/ics26-routing/types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ibc-core-router-types" +name = "ibc-eureka-core-router-types" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -28,9 +28,9 @@ serde = { workspace = true, optional = true } subtle-encoding = { workspace = true } # ibc dependencies -ibc-core-host-types = { workspace = true } -ibc-primitives = { workspace = true } -ibc-proto = { workspace = true } +ibc-eureka-core-host-types = { workspace = true } +ibc-primitives = { workspace = true } +ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } @@ -46,26 +46,26 @@ std = [ "serde/std", "subtle-encoding/std", "ibc-primitives/std", - "ibc-core-host-types/std", + "ibc-eureka-core-host-types/std", "ibc-proto/std", "tendermint/std", ] serde = [ "dep:serde", "ibc-primitives/serde", - "ibc-core-host-types/serde", + "ibc-eureka-core-host-types/serde", "ibc-primitives/serde", "ibc-proto/serde", ] borsh = [ "dep:borsh", - "ibc-core-host-types/borsh", + "ibc-eureka-core-host-types/borsh", "ibc-primitives/borsh", "ibc-proto/borsh", ] schema = [ "dep:schemars", - "ibc-core-host-types/schema", + "ibc-eureka-core-host-types/schema", "ibc-primitives/schema", "ibc-proto/json-schema", "serde", @@ -74,7 +74,7 @@ schema = [ parity-scale-codec = [ "dep:parity-scale-codec", "dep:scale-info", - "ibc-core-host-types/parity-scale-codec", + "ibc-eureka-core-host-types/parity-scale-codec", "ibc-primitives/parity-scale-codec", "ibc-proto/parity-scale-codec", ] diff --git a/ibc-eureka-core/ics26-routing/types/src/error.rs b/ibc-eureka-core/ics26-routing/types/src/error.rs index 19ad7c2adf..295df0c0cd 100644 --- a/ibc-eureka-core/ics26-routing/types/src/error.rs +++ b/ibc-eureka-core/ics26-routing/types/src/error.rs @@ -1,5 +1,5 @@ use displaydoc::Display; -use ibc_core_host_types::error::HostError; +use ibc_eureka_core_host_types::error::HostError; use ibc_primitives::prelude::*; /// Error type for the router module. diff --git a/ibc-eureka-core/ics26-routing/types/src/module.rs b/ibc-eureka-core/ics26-routing/types/src/module.rs index 82867fde01..50edc76c77 100644 --- a/ibc-eureka-core/ics26-routing/types/src/module.rs +++ b/ibc-eureka-core/ics26-routing/types/src/module.rs @@ -8,7 +8,7 @@ use crate::event::ModuleEvent; /// Module name, internal to the chain. /// /// That is, the IBC protocol never exposes this name. Note that this is -/// different from IBC host [identifiers][ibc_core_host_types::identifiers], +/// different from IBC host [identifiers][ibc_eureka_core_host_types::identifiers], /// which are exposed to other chains by the protocol. #[cfg_attr( feature = "parity-scale-codec", diff --git a/ibc-eureka-core/src/lib.rs b/ibc-eureka-core/src/lib.rs index 30d04e3848..2b9b50a86c 100644 --- a/ibc-eureka-core/src/lib.rs +++ b/ibc-eureka-core/src/lib.rs @@ -11,11 +11,11 @@ rust_2018_idioms )] -/// Re-exports IBC handler entrypoints from the `ibc-core-handler` crate for +/// Re-exports IBC handler entrypoints from the `ibc-eureka-core-handler` crate for /// added convenience. pub mod entrypoint { #[doc(inline)] - pub use ibc_core_handler::entrypoint::*; + pub use ibc_eureka_core_handler::entrypoint::*; } /// Re-exports IBC primitive types from the `ibc-primitives` crate @@ -24,46 +24,46 @@ pub mod primitives { pub use ibc_primitives::*; } -/// Re-exports ICS-02 implementation from the `ibc-core-client` crate +/// Re-exports ICS-02 implementation from the `ibc-eureka-core-client` crate pub mod client { #[doc(inline)] - pub use ibc_core_client::*; + pub use ibc_eureka_core_client::*; } -/// Re-exports ICS-03 implementation from the `ibc-core-connection` crate +/// Re-exports ICS-03 implementation from the `ibc-eureka-core-connection` crate pub mod connection { #[doc(inline)] - pub use ibc_core_connection::*; + pub use ibc_eureka_core_connection::*; } -/// Re-exports ICS-04 implementation from the `ibc-core-channel` crate +/// Re-exports ICS-04 implementation from the `ibc-eureka-core-channel` crate pub mod channel { #[doc(inline)] - pub use ibc_core_channel::*; + pub use ibc_eureka_core_channel::*; } -/// Re-exports ICS-23 data structures from the `ibc-core-commitment-types` crate +/// Re-exports ICS-23 data structures from the `ibc-eureka-core-commitment-types` crate pub mod commitment_types { #[doc(inline)] - pub use ibc_core_commitment_types::*; + pub use ibc_eureka_core_commitment_types::*; } -/// Re-exports ICS-24 implementation from the `ibc-core-host` crate +/// Re-exports ICS-24 implementation from the `ibc-eureka-core-host` crate pub mod host { #[doc(inline)] - pub use ibc_core_host::*; + pub use ibc_eureka_core_host::*; } -/// Re-exports ICS-25 implementation from the `ibc-core-handler` crate +/// Re-exports ICS-25 implementation from the `ibc-eureka-core-handler` crate pub mod handler { #[doc(inline)] - pub use ibc_core_handler::*; + pub use ibc_eureka_core_handler::*; } -/// Re-exports ICS-26 implementation from the `ibc-core-router` crate +/// Re-exports ICS-26 implementation from the `ibc-eureka-core-router` crate pub mod router { #[doc(inline)] - pub use ibc_core_router::*; + pub use ibc_eureka_core_router::*; } /// Re-exports convenient derive macros from `ibc-derive` crate. From 7ecdbadad26c150521362667d1766b4d00587add Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 21 Oct 2024 11:58:27 +0200 Subject: [PATCH 3/5] add ibc-eureka-core to workspace --- Cargo.toml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1091d731a4..c5ba0fbff4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,16 @@ members = [ "ibc-query", "ibc-testkit", + "ibc-eureka-core/ics26-routing/types", + "ibc-eureka-core/ics23-commitment/types", + "ibc-eureka-core/ics04-channel/types", + "ibc-eureka-core/ics25-handler/types", + "ibc-eureka-core/ics24-host", + "ibc-eureka-core/ics26-routing", + "ibc-eureka-core/ics04-channel", + "ibc-eureka-core/ics25-handler", + "ibc-eureka-core", + # internal crates that are not published "tests-integration", ] @@ -85,6 +95,13 @@ ibc-core-handler = { version = "0.55.1", path = "./ibc-core/ics25-handler", d 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-eureka-core-client = { version = "0.55.1", path = "./ibc-eureka-core/ics02-client", default-features = false } +ibc-eureka-core-connection = { version = "0.55.1", path = "./ibc-eureka-core/ics03-connection", default-features = false } +ibc-eureka-core-channel = { version = "0.55.1", path = "./ibc-eureka-core/ics04-channel", default-features = false } +ibc-eureka-core-host = { version = "0.55.1", path = "./ibc-eureka-core/ics24-host", default-features = false } +ibc-eureka-core-handler = { version = "0.55.1", path = "./ibc-eureka-core/ics25-handler", default-features = false } +ibc-eureka-core-router = { version = "0.55.1", path = "./ibc-eureka-core/ics26-routing", default-features = false } + ibc-client-tendermint = { version = "0.55.1", path = "./ibc-clients/ics07-tendermint", default-features = false } ibc-app-transfer = { version = "0.55.1", path = "./ibc-apps/ics20-transfer", default-features = false } @@ -104,6 +121,16 @@ ibc-client-wasm-types = { version = "0.55.1", path = "./ibc-clients/ics08- 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-eureka-core-client-context = { version = "0.55.1", path = "./ibc-eureka-core/ics02-client/context", default-features = false } +ibc-eureka-core-client-types = { version = "0.55.1", path = "./ibc-eureka-core/ics02-client/types", default-features = false } +ibc-eureka-core-channel-types = { version = "0.55.1", path = "./ibc-eureka-core/ics04-channel/types", default-features = false } +ibc-eureka-core-connection-types = { version = "0.55.1", path = "./ibc-eureka-core/ics03-connection/types", default-features = false } +ibc-eureka-core-commitment-types = { version = "0.55.1", path = "./ibc-eureka-core/ics23-commitment/types", default-features = false } +ibc-eureka-core-host-cosmos = { version = "0.55.1", path = "./ibc-eureka-core/ics24-host/cosmos", default-features = false } +ibc-eureka-core-host-types = { version = "0.55.1", path = "./ibc-eureka-core/ics24-host/types", default-features = false } +ibc-eureka-core-handler-types = { version = "0.55.1", path = "./ibc-eureka-core/ics25-handler/types", default-features = false } +ibc-eureka-core-router-types = { version = "0.55.1", path = "./ibc-eureka-core/ics26-routing/types", default-features = false } + ibc-proto = { version = "0.47.1", default-features = false } # cosmos dependencies From 80784fb9672d8eef401e070d423ff7608a378d17 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 21 Oct 2024 11:59:11 +0200 Subject: [PATCH 4/5] fix compilation --- ibc-eureka-core/ics03-connection/src/handler/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ibc-eureka-core/ics03-connection/src/handler/mod.rs b/ibc-eureka-core/ics03-connection/src/handler/mod.rs index eb86a2b804..0d2a0cb4a1 100644 --- a/ibc-eureka-core/ics03-connection/src/handler/mod.rs +++ b/ibc-eureka-core/ics03-connection/src/handler/mod.rs @@ -28,7 +28,8 @@ where use ibc_client_wasm_types::client_state::ClientState as WasmClientState; use prost::Message; - let wasm_client_state = WasmClientState::try_from(value)?; + let wasm_client_state = + WasmClientState::try_from(value).expect("TODO(rano): propagate the error"); let any_client_state = ::decode(wasm_client_state.data.as_slice()) .map_err(|e| ConnectionError::Decoding(DecodingError::Prost(e)))?; From 70e5b6345bac9972c56b4b55245d4e5154ec990b Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 30 Oct 2024 14:36:09 +0100 Subject: [PATCH 5/5] fix tests requring std --- Cargo.toml | 2 ++ ibc/Cargo.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c5ba0fbff4..139623def6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,8 @@ ibc-apps = { version = "0.55.1", path = "./ibc-apps", default-features = f 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-eureka-core = { version = "0.55.1", path = "./ibc-eureka-core", default-features = false } + ibc-derive = { version = "0.9.0", path = "./ibc-derive" } ibc-core-client = { version = "0.55.1", path = "./ibc-core/ics02-client", default-features = false } diff --git a/ibc/Cargo.toml b/ibc/Cargo.toml index 1005db2a0d..d8b3b57fb6 100644 --- a/ibc/Cargo.toml +++ b/ibc/Cargo.toml @@ -27,6 +27,7 @@ ibc-core = { workspace = true } ibc-core-host-cosmos = { workspace = true } ibc-derive = { workspace = true } ibc-primitives = { workspace = true } +ibc-eureka-core = { workspace = true } [features] default = [ "std" ] @@ -34,6 +35,7 @@ std = [ "ibc-apps/std", "ibc-clients/std", "ibc-core/std", + "ibc-eureka-core/std", "ibc-core-host-cosmos/std", "ibc-primitives/std", ]