From 68b4ca6900335724d25a4892650fee78dba6bc7c Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Fri, 17 Nov 2023 07:26:39 -0800 Subject: [PATCH] feat: migrate `applications` module under the `ibc` crate into `ibc-apps` (#967) * feat: restructure and split off applications codebase into ibc-apps dir * imp: rename transfer dir to ics20_transfer * feat: add ibc-apps crate * fix: remove redundant dep + fix cargo doc * docs: add README and descriptions * docs: update main README page * nit: docstrings * nit: docstrings * imp: rename folder to ics20-transfer * chore: move serializers into ics20-transfer/types * fix: apply reviewer comments * imp: add docstring for cosmos_adr028_escrow_address * fix: add missing features + use workspace deps for ibc crates * imp: place re-exports under mod * nit: apply suggestions from code review Co-authored-by: Sean Chen Signed-off-by: Farhad Shabani * fix: cargo fmt --------- Signed-off-by: Farhad Shabani Co-authored-by: Sean Chen --- Cargo.toml | 9 +- README.md | 1 + clippy.toml | 9 + crates/ibc-apps/Cargo.toml | 37 ++++ crates/ibc-apps/README.md | 48 ++++ crates/ibc-apps/ics20-transfer/Cargo.toml | 57 +++++ crates/ibc-apps/ics20-transfer/src/context.rs | 136 ++++++++++++ .../ics20-transfer/src/handler/mod.rs} | 16 +- .../src/handler}/on_recv_packet.rs | 17 +- .../src/handler}/send_transfer.rs | 27 +-- crates/ibc-apps/ics20-transfer/src/lib.rs | 32 +++ .../ics20-transfer/src/module.rs} | 207 ++++++------------ .../ibc-apps/ics20-transfer/types/Cargo.toml | 55 +++++ .../ics20-transfer/types/src}/amount.rs | 9 +- .../ics20-transfer/types/src}/coin.rs | 3 +- .../ics20-transfer/types/src}/denom.rs | 10 +- .../ics20-transfer/types/src}/error.rs | 12 +- .../ics20-transfer/types/src}/events.rs | 30 ++- .../ibc-apps/ics20-transfer/types/src/lib.rs | 69 ++++++ .../ics20-transfer/types/src}/memo.rs | 2 +- .../ics20-transfer/types/src/msgs/mod.rs} | 1 - .../types/src}/msgs/transfer.rs | 17 +- .../ics20-transfer/types/src}/packet.rs | 8 +- .../ics20-transfer/types/src/serializers.rs | 27 +++ crates/ibc-apps/src/lib.rs | 20 ++ crates/ibc-testkit/Cargo.toml | 4 +- .../ibc/applications/transfer/context.rs | 10 +- .../src/testapp/ibc/core/router/types.rs | 2 +- .../utils/dummies/applications/transfer.rs | 6 +- .../tests/applications/transfer.rs | 11 +- .../tests/core/ics04_channel/chan_open_ack.rs | 2 +- crates/ibc-testkit/tests/core/router.rs | 7 +- crates/ibc/Cargo.toml | 5 +- crates/ibc/src/applications/mod.rs | 4 - crates/ibc/src/applications/transfer/mod.rs | 41 ---- .../src/core/ics04_channel/acknowledgement.rs | 68 ------ crates/ibc/src/core/ics04_channel/handler.rs | 22 +- crates/ibc/src/core/ics04_channel/mod.rs | 2 +- crates/ibc/src/lib.rs | 5 - crates/ibc/src/serializers.rs | 29 --- 40 files changed, 674 insertions(+), 403 deletions(-) create mode 100644 clippy.toml create mode 100644 crates/ibc-apps/Cargo.toml create mode 100644 crates/ibc-apps/README.md create mode 100644 crates/ibc-apps/ics20-transfer/Cargo.toml create mode 100644 crates/ibc-apps/ics20-transfer/src/context.rs rename crates/{ibc/src/applications/transfer/relay.rs => ibc-apps/ics20-transfer/src/handler/mod.rs} (84%) rename crates/{ibc/src/applications/transfer/relay => ibc-apps/ics20-transfer/src/handler}/on_recv_packet.rs (90%) rename crates/{ibc/src/applications/transfer/relay => ibc-apps/ics20-transfer/src/handler}/send_transfer.rs (88%) create mode 100644 crates/ibc-apps/ics20-transfer/src/lib.rs rename crates/{ibc/src/applications/transfer/context.rs => ibc-apps/ics20-transfer/src/module.rs} (62%) create mode 100644 crates/ibc-apps/ics20-transfer/types/Cargo.toml rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/amount.rs (95%) rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/coin.rs (99%) rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/denom.rs (98%) rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/error.rs (93%) rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/events.rs (83%) create mode 100644 crates/ibc-apps/ics20-transfer/types/src/lib.rs rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/memo.rs (98%) rename crates/{ibc/src/applications/transfer/msgs.rs => ibc-apps/ics20-transfer/types/src/msgs/mod.rs} (98%) rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/msgs/transfer.rs (91%) rename crates/{ibc/src/applications/transfer => ibc-apps/ics20-transfer/types/src}/packet.rs (95%) create mode 100644 crates/ibc-apps/ics20-transfer/types/src/serializers.rs create mode 100644 crates/ibc-apps/src/lib.rs delete mode 100644 crates/ibc/src/applications/mod.rs delete mode 100644 crates/ibc/src/applications/transfer/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 7f65e463b..a2ac8e33a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,9 @@ resolver = "2" members = [ "crates/ibc", + "crates/ibc-apps", + "crates/ibc-apps/ics20-transfer", + "crates/ibc-apps/ics20-transfer/types", "crates/ibc-derive", "crates/ibc-testkit", "crates/ibc-query", @@ -45,7 +48,11 @@ tracing-subscriber = { version = "0.3.17", features = ["fmt", "env-filter", "jso typed-builder = { version = "0.18.0"} # ibc dependencies -ibc-derive = { version = "0.3.0", path = "../ibc-derive" } +ibc = { version = "0.47.0", path = "./crates/ibc", default-features = false } +ibc-testkit = { version = "0.47.0", path = "./crates/ibc-testkit", default-features = false} +ibc-app-transfer = { version = "0.47.0", path = "./crates/ibc-apps/ics20-transfer", default-features = false } +ibc-app-transfer-types = { version = "0.47.0", path = "./crates/ibc-apps/ics20-transfer/types", default-features = false } +ibc-derive = { version = "0.3.0", path = "./crates/ibc-derive" } ibc-proto = { version = "0.38.0", default-features = false } ics23 = { version = "0.11", default-features = false } diff --git a/README.md b/README.md index 6bcd01fe8..2c5c8a8a5 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ the `ibc` rust crate which defines the main data structures and on-chain logic f ## Libraries - [ibc](crates/ibc/README.md) - Data structures and on-chain logic for the IBC protocol. +- [ibc-apps](crates/ibc-apps/README.md) - Contains implementations of various IBC applications. - [ibc-derive](crates/ibc-derive/README.md) - Derive macros for `ClientState` and `ConsensusState` traits, reducing boilerplate. - [ibc-testkit](crates/ibc-testkit/README.md) - Testing toolkit to aid `ibc-rs` and host chains in writing integration tests. diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..87ee0dae3 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,9 @@ +disallowed-types = [ + # { path = "usize", reason = "variable size" }, # cannot on now, because mocks use it and serde, even if there is no usize in type + { path = "f64", reason = "not supported in CosmWasm" }, + { path = "f32", reason = "not supported in CosmWasm" }, +] + +disallowed-methods = [ + "std::time::Duration::as_secs_f64", +] diff --git a/crates/ibc-apps/Cargo.toml b/crates/ibc-apps/Cargo.toml new file mode 100644 index 000000000..f0c4420cf --- /dev/null +++ b/crates/ibc-apps/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "ibc-apps" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = ["blockchain", "cosmos", "ibc", "applications"] +readme = "README.md" +description = """ + `ibc-apps` provides a comprehensive set of libraries for IBC applications, + facilitating seamless integration of IBC business logic into any blockchain system. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ibc-app-transfer = { workspace = true } + +[features] +default = ["std"] +std = [ + "ibc-app-transfer/std", +] +serde = [ + "ibc-app-transfer/serde", +] +schema = [ + "ibc-app-transfer/schema", + "serde", + "std", +] +borsh = [ + "ibc-app-transfer/borsh", +] diff --git a/crates/ibc-apps/README.md b/crates/ibc-apps/README.md new file mode 100644 index 000000000..d74d4ad46 --- /dev/null +++ b/crates/ibc-apps/README.md @@ -0,0 +1,48 @@ +# `ibc-apps` + +This crate is a top-level library that re-exports Inter-Blockchain +Communication (IBC) applications implemented in Rust. It serves as a centralized hub, +simplifying the process of importing and integrating various IBC applications +into your blockchain. IBC is a distributed protocol that enables communication +between distinct sovereign blockchains. IBC applications abstract away the core +transport and authentication layers, letter blockchain app developers +focus solely on business logic implementation. + +The structure within the `ibc-apps` crate is designed to provide flexibility for +external users. It allows users to either utilize the entire `ibc-apps` crate, +or selectively import specific sub-crates, whether you need a certain IBC +application (e.g. `ibc-app-transfer` crate) or only their associated data +structures (e.g. `ibc-app-transfer-types`). This versatility empowers hosts, +including chain integrators, relayers, or any IBC tooling projects, to build +their solutions on top of the layers that best suit their requirements. + +## Libraries + +Currently, the `ibc-apps` crate contains the implementation of the following IBC +applications: + +### ICS-20: Fungible Token Transfer Application + +- [ibc-app-transfer](crates/ibc-apps/ics20-transfer) +- [ibc-app-transfer-types](crates/ibc-apps/ics20-transfer/types) + +## Contributing + +IBC is specified in English in the [cosmos/ibc repo][ibc]. Any +protocol changes or clarifications should be contributed there. + +If you're interested in contributing, please comment on an issue or open a new +one! + +See also [CONTRIBUTING.md](./../../CONTRIBUTING.md). + +## Resources + +- [IBC Website][ibc-homepage] +- [IBC Specification][ibc] +- [IBC Go implementation][ibc-go] + +[//]: # (general links) +[ibc]: https://github.com/cosmos/ibc +[ibc-go]: https://github.com/cosmos/ibc-go +[ibc-homepage]: https://cosmos.network/ibc diff --git a/crates/ibc-apps/ics20-transfer/Cargo.toml b/crates/ibc-apps/ics20-transfer/Cargo.toml new file mode 100644 index 000000000..821e3381b --- /dev/null +++ b/crates/ibc-apps/ics20-transfer/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "ibc-app-transfer" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = ["blockchain", "cosmos", "ibc", "transfer", "ics20"] +readme = "./../README.md" +description = """ + Contains the core implementation of the ICS-20 token transfer application logic + along with re-exporting the data structures from `ibc-app-transfer-types` crate. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +serde_json = { workspace = true, optional = true } +sha2 = { workspace = true } + +# ibc dependencies +ibc = { workspace = true } +ibc-app-transfer-types = { workspace = true } + +[dev-dependencies] +subtle-encoding = { workspace = true } + +[features] +default = ["std"] +std = [ + "ibc-app-transfer-types/std", + "ibc/std", + "serde_json/std", + "sha2/std", +] +serde = [ + "ibc-app-transfer-types/serde", + "ibc/serde", + "serde_json" +] +schema = [ + "ibc-app-transfer-types/schema", + "ibc/schema", + "serde", + "std", +] +borsh = [ + "ibc-app-transfer-types/borsh", + "ibc/borsh", +] +parity-scale-codec = [ + "ibc-app-transfer-types/parity-scale-codec", + "ibc/parity-scale-codec", +] diff --git a/crates/ibc-apps/ics20-transfer/src/context.rs b/crates/ibc-apps/ics20-transfer/src/context.rs new file mode 100644 index 000000000..80899b12b --- /dev/null +++ b/crates/ibc-apps/ics20-transfer/src/context.rs @@ -0,0 +1,136 @@ +//! Defines the main context traits and IBC module callbacks + +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; +use ibc::Signer; +use ibc_app_transfer_types::error::TokenTransferError; +use ibc_app_transfer_types::{PrefixedCoin, PrefixedDenom, VERSION}; +use sha2::{Digest, Sha256}; + +/// Methods required in token transfer validation, to be implemented by the host +pub trait TokenTransferValidationContext { + type AccountId: TryFrom; + + /// get_port returns the portID for the transfer module. + fn get_port(&self) -> Result; + + /// Returns the escrow account id for a port and channel combination + fn get_escrow_account( + &self, + port_id: &PortId, + channel_id: &ChannelId, + ) -> Result; + + /// Returns Ok() if the host chain supports sending coins. + fn can_send_coins(&self) -> Result<(), TokenTransferError>; + + /// Returns Ok() if the host chain supports receiving coins. + fn can_receive_coins(&self) -> Result<(), TokenTransferError>; + + /// Validates the sender and receiver accounts and the coin inputs + fn send_coins_validate( + &self, + from_account: &Self::AccountId, + to_account: &Self::AccountId, + coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError>; + + /// Validates the receiver account and the coin input + fn mint_coins_validate( + &self, + account: &Self::AccountId, + coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError>; + + /// Validates the sender account and the coin input + fn burn_coins_validate( + &self, + account: &Self::AccountId, + coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError>; + + /// Returns a hash of the prefixed denom. + /// Implement only if the host chain supports hashed denominations. + fn denom_hash_string(&self, _denom: &PrefixedDenom) -> Option { + None + } +} + +/// Methods required in token transfer execution, to be implemented by the host +pub trait TokenTransferExecutionContext: TokenTransferValidationContext { + /// This function should enable sending ibc fungible tokens from one account to another + fn send_coins_execute( + &mut self, + from_account: &Self::AccountId, + to_account: &Self::AccountId, + coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError>; + + /// This function to enable minting ibc tokens to a user account + fn mint_coins_execute( + &mut self, + account: &Self::AccountId, + coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError>; + + /// This function should enable burning of minted tokens in a user account + fn burn_coins_execute( + &mut self, + account: &Self::AccountId, + coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError>; +} + +/// 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::*; + use crate::context::cosmos_adr028_escrow_address; + + #[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/crates/ibc/src/applications/transfer/relay.rs b/crates/ibc-apps/ics20-transfer/src/handler/mod.rs similarity index 84% rename from crates/ibc/src/applications/transfer/relay.rs rename to crates/ibc-apps/ics20-transfer/src/handler/mod.rs index 227ac8243..730017f6d 100644 --- a/crates/ibc/src/applications/transfer/relay.rs +++ b/crates/ibc-apps/ics20-transfer/src/handler/mod.rs @@ -1,15 +1,15 @@ //! Implements the processing logic for ICS20 (token transfer) message. - -use super::context::{TokenTransferExecutionContext, TokenTransferValidationContext}; -use crate::applications::transfer::error::TokenTransferError; -use crate::applications::transfer::is_sender_chain_source; -use crate::applications::transfer::packet::PacketData; -use crate::core::ics04_channel::packet::Packet; -use crate::prelude::*; - pub mod on_recv_packet; pub mod send_transfer; +use ibc::core::ics04_channel::packet::Packet; +use ibc::prelude::*; +use ibc_app_transfer_types::error::TokenTransferError; +use ibc_app_transfer_types::is_sender_chain_source; +use ibc_app_transfer_types::packet::PacketData; + +use crate::context::{TokenTransferExecutionContext, TokenTransferValidationContext}; + pub fn refund_packet_token_execute( ctx_a: &mut impl TokenTransferExecutionContext, packet: &Packet, diff --git a/crates/ibc/src/applications/transfer/relay/on_recv_packet.rs b/crates/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs similarity index 90% rename from crates/ibc/src/applications/transfer/relay/on_recv_packet.rs rename to crates/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs index 9e9e70c2d..d150d8399 100644 --- a/crates/ibc/src/applications/transfer/relay/on_recv_packet.rs +++ b/crates/ibc-apps/ics20-transfer/src/handler/on_recv_packet.rs @@ -1,11 +1,12 @@ -use crate::applications::transfer::context::TokenTransferExecutionContext; -use crate::applications::transfer::error::TokenTransferError; -use crate::applications::transfer::events::DenomTraceEvent; -use crate::applications::transfer::packet::PacketData; -use crate::applications::transfer::{is_receiver_chain_source, TracePrefix}; -use crate::core::ics04_channel::packet::Packet; -use crate::core::router::ModuleExtras; -use crate::prelude::*; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::router::ModuleExtras; +use ibc::prelude::*; +use ibc_app_transfer_types::error::TokenTransferError; +use ibc_app_transfer_types::events::DenomTraceEvent; +use ibc_app_transfer_types::packet::PacketData; +use ibc_app_transfer_types::{is_receiver_chain_source, TracePrefix}; + +use crate::context::TokenTransferExecutionContext; /// This function handles the transfer receiving logic. /// diff --git a/crates/ibc/src/applications/transfer/relay/send_transfer.rs b/crates/ibc-apps/ics20-transfer/src/handler/send_transfer.rs similarity index 88% rename from crates/ibc/src/applications/transfer/relay/send_transfer.rs rename to crates/ibc-apps/ics20-transfer/src/handler/send_transfer.rs index 112462cd0..9fe2662b5 100644 --- a/crates/ibc/src/applications/transfer/relay/send_transfer.rs +++ b/crates/ibc-apps/ics20-transfer/src/handler/send_transfer.rs @@ -1,18 +1,15 @@ -use crate::applications::transfer::context::{ - TokenTransferExecutionContext, TokenTransferValidationContext, -}; -use crate::applications::transfer::error::TokenTransferError; -use crate::applications::transfer::events::TransferEvent; -use crate::applications::transfer::msgs::transfer::MsgTransfer; -use crate::applications::transfer::{is_sender_chain_source, MODULE_ID_STR}; -use crate::core::events::{MessageEvent, ModuleEvent}; -use crate::core::ics04_channel::context::{ - SendPacketExecutionContext, SendPacketValidationContext, -}; -use crate::core::ics04_channel::handler::send_packet::{send_packet_execute, send_packet_validate}; -use crate::core::ics04_channel::packet::Packet; -use crate::core::ics24_host::path::{ChannelEndPath, SeqSendPath}; -use crate::prelude::*; +use ibc::core::events::{MessageEvent, ModuleEvent}; +use ibc::core::ics04_channel::context::{SendPacketExecutionContext, SendPacketValidationContext}; +use ibc::core::ics04_channel::handler::send_packet::{send_packet_execute, send_packet_validate}; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::ics24_host::path::{ChannelEndPath, SeqSendPath}; +use ibc::prelude::*; +use ibc_app_transfer_types::error::TokenTransferError; +use ibc_app_transfer_types::events::TransferEvent; +use ibc_app_transfer_types::msgs::transfer::MsgTransfer; +use ibc_app_transfer_types::{is_sender_chain_source, MODULE_ID_STR}; + +use crate::context::{TokenTransferExecutionContext, TokenTransferValidationContext}; /// Initiate a token transfer. Equivalent to calling [`send_transfer_validate`], followed by [`send_transfer_execute`]. pub fn send_transfer( diff --git a/crates/ibc-apps/ics20-transfer/src/lib.rs b/crates/ibc-apps/ics20-transfer/src/lib.rs new file mode 100644 index 000000000..8c2c31d9c --- /dev/null +++ b/crates/ibc-apps/ics20-transfer/src/lib.rs @@ -0,0 +1,32 @@ +//! Implementation of the IBC [fungible token transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md) (ICS-20) application logic. +#![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_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] +#![allow(clippy::result_large_err)] + +#[cfg(any(test, feature = "std"))] +extern crate std; + +/// Re-exports the implementation of the IBC [fungible token +/// transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md) +/// (ICS-20) data structures. +pub mod types { + #[doc(inline)] + pub use ibc_app_transfer_types::*; +} + +#[cfg(feature = "serde")] +pub mod context; +#[cfg(feature = "serde")] +pub mod handler; +#[cfg(feature = "serde")] +pub mod module; diff --git a/crates/ibc/src/applications/transfer/context.rs b/crates/ibc-apps/ics20-transfer/src/module.rs similarity index 62% rename from crates/ibc/src/applications/transfer/context.rs rename to crates/ibc-apps/ics20-transfer/src/module.rs index 33d080f30..1c86d139e 100644 --- a/crates/ibc/src/applications/transfer/context.rs +++ b/crates/ibc-apps/ics20-transfer/src/module.rs @@ -1,112 +1,20 @@ -//! Defines the main context traits and IBC module callbacks -use sha2::{Digest, Sha256}; - -use super::ack_success_b64; -use super::error::TokenTransferError; -use crate::applications::transfer::events::{AckEvent, AckStatusEvent, RecvEvent, TimeoutEvent}; -use crate::applications::transfer::packet::PacketData; -use crate::applications::transfer::relay::on_recv_packet::process_recv_packet_execute; -use crate::applications::transfer::relay::{ - refund_packet_token_execute, refund_packet_token_validate, -}; -use crate::applications::transfer::{PrefixedCoin, PrefixedDenom, VERSION}; -use crate::core::ics04_channel::acknowledgement::{Acknowledgement, AcknowledgementStatus}; -use crate::core::ics04_channel::channel::{Counterparty, Order}; -use crate::core::ics04_channel::packet::Packet; -use crate::core::ics04_channel::Version; -use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; -use crate::core::router::ModuleExtras; -use crate::core::ContextError; -use crate::prelude::*; -use crate::signer::Signer; - -/// Methods required in token transfer validation, to be implemented by the host -pub trait TokenTransferValidationContext { - type AccountId: TryFrom; - - /// get_port returns the portID for the transfer module. - fn get_port(&self) -> Result; - - /// Returns the escrow account id for a port and channel combination - fn get_escrow_account( - &self, - port_id: &PortId, - channel_id: &ChannelId, - ) -> Result; - - /// Returns Ok() if the host chain supports sending coins. - fn can_send_coins(&self) -> Result<(), TokenTransferError>; - - /// Returns Ok() if the host chain supports receiving coins. - fn can_receive_coins(&self) -> Result<(), TokenTransferError>; - - /// Validates the sender and receiver accounts and the coin inputs - fn send_coins_validate( - &self, - from_account: &Self::AccountId, - to_account: &Self::AccountId, - coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; - - /// Validates the receiver account and the coin input - fn mint_coins_validate( - &self, - account: &Self::AccountId, - coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; - - /// Validates the sender account and the coin input - fn burn_coins_validate( - &self, - account: &Self::AccountId, - coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; - - /// Returns a hash of the prefixed denom. - /// Implement only if the host chain supports hashed denominations. - fn denom_hash_string(&self, _denom: &PrefixedDenom) -> Option { - None - } -} - -/// Methods required in token transfer execution, to be implemented by the host -pub trait TokenTransferExecutionContext: TokenTransferValidationContext { - /// This function should enable sending ibc fungible tokens from one account to another - fn send_coins_execute( - &mut self, - from_account: &Self::AccountId, - to_account: &Self::AccountId, - coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; - - /// This function to enable minting ibc tokens to a user account - fn mint_coins_execute( - &mut self, - account: &Self::AccountId, - coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; - - /// This function should enable burning of minted tokens in a user account - fn burn_coins_execute( - &mut self, - account: &Self::AccountId, - coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError>; -} - -// 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 -} +use ibc::core::ics04_channel::acknowledgement::{Acknowledgement, AcknowledgementStatus}; +use ibc::core::ics04_channel::channel::{Counterparty, Order}; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use ibc::core::router::ModuleExtras; +use ibc::core::ContextError; +use ibc::prelude::*; +use ibc::Signer; +use ibc_app_transfer_types::error::TokenTransferError; +use ibc_app_transfer_types::events::{AckEvent, AckStatusEvent, RecvEvent, TimeoutEvent}; +use ibc_app_transfer_types::packet::PacketData; +use ibc_app_transfer_types::{ack_success_b64, VERSION}; + +use crate::context::{TokenTransferExecutionContext, TokenTransferValidationContext}; +use crate::handler::on_recv_packet::process_recv_packet_execute; +use crate::handler::{refund_packet_token_execute, refund_packet_token_validate}; pub fn on_chan_open_init_validate( ctx: &impl TokenTransferValidationContext, @@ -411,39 +319,70 @@ pub fn on_timeout_packet_execute( } #[cfg(test)] -mod tests { - use subtle_encoding::bech32; +mod test { + use ibc_app_transfer_types::ack_success_b64; + use ibc_app_transfer_types::error::TokenTransferError; use super::*; - use crate::applications::transfer::context::cosmos_adr028_escrow_address; #[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()) + fn test_ack_ser() { + fn ser_json_assert_eq(ack: AcknowledgementStatus, json_str: &str) { + let ser = serde_json::to_string(&ack).unwrap(); + assert_eq!(ser, json_str) } - // addresses obtained using `gaiad query ibc-transfer escrow-address [port-id] [channel-id]` - assert_eq_escrow_address( - "transfer", - "channel-141", - "cosmos1x54ltnyg88k0ejmk8ytwrhd3ltm84xehrnlslf", + ser_json_assert_eq( + AcknowledgementStatus::success(ack_success_b64()), + r#"{"result":"AQ=="}"#, ); - assert_eq_escrow_address( - "transfer", - "channel-207", - "cosmos1ju6tlfclulxumtt2kglvnxduj5d93a64r5czge", + ser_json_assert_eq( + AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()), + r#"{"error":"failed to deserialize packet data"}"#, ); - assert_eq_escrow_address( - "transfer", - "channel-187", - "cosmos177x69sver58mcfs74x6dg0tv6ls4s3xmmcaw53", + } + + #[test] + fn test_ack_success_to_vec() { + let ack_success: Vec = AcknowledgementStatus::success(ack_success_b64()).into(); + + // Check that it's the same output as ibc-go + // Note: this also implicitly checks that the ack bytes are non-empty, + // which would make the conversion to `Acknowledgement` panic + assert_eq!(ack_success, r#"{"result":"AQ=="}"#.as_bytes()); + } + + #[test] + fn test_ack_error_to_vec() { + let ack_error: Vec = + AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()) + .into(); + + // Check that it's the same output as ibc-go + // Note: this also implicitly checks that the ack bytes are non-empty, + // which would make the conversion to `Acknowledgement` panic + assert_eq!( + ack_error, + r#"{"error":"failed to deserialize packet data"}"#.as_bytes() ); } + + #[test] + fn test_ack_de() { + fn de_json_assert_eq(json_str: &str, ack: AcknowledgementStatus) { + let de = serde_json::from_str::(json_str).unwrap(); + assert_eq!(de, ack) + } + + de_json_assert_eq( + r#"{"result":"AQ=="}"#, + AcknowledgementStatus::success(ack_success_b64()), + ); + de_json_assert_eq( + r#"{"error":"failed to deserialize packet data"}"#, + AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()), + ); + + assert!(serde_json::from_str::(r#"{"success":"AQ=="}"#).is_err()); + } } diff --git a/crates/ibc-apps/ics20-transfer/types/Cargo.toml b/crates/ibc-apps/ics20-transfer/types/Cargo.toml new file mode 100644 index 000000000..495a77de5 --- /dev/null +++ b/crates/ibc-apps/ics20-transfer/types/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "ibc-app-transfer-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = ["blockchain", "cosmos", "ibc", "transfer", "ics20"] +readme = "./../../README.md" +description = """ + Contains the universal data structures of the ICS-20 fungible token transfer application + that can be used across various IBC implementations +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +primitive-types = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true} +uint = { version = "0.9", default-features = false } + +# ibc dependencies +ibc = { workspace = true } +ibc-proto = { workspace = true } + +## parity dependencies +parity-scale-codec = { workspace = true , optional = true } +scale-info = { workspace = true , optional = true } + +[dev-dependencies] +ibc-testkit = { workspace = true } +rstest = { workspace = true } + +[features] +default = ["std"] +std = [ + "ibc-proto/std", + "serde/std", + "serde_json/std", + "displaydoc/std", + "uint/std", + "primitive-types/std", +] +borsh = ["dep:borsh", "ibc/borsh", "ibc-proto/borsh"] +serde = ["dep:serde", "serde_json", "ibc/serde", "ibc-proto/serde"] +schema = ["dep:schemars", "ibc/schema", "ibc-proto/json-schema", "serde", "std"] +parity-scale-codec = ["dep:parity-scale-codec", "dep:scale-info", "ibc/parity-scale-codec", "ibc-proto/parity-scale-codec"] \ No newline at end of file diff --git a/crates/ibc/src/applications/transfer/amount.rs b/crates/ibc-apps/ics20-transfer/types/src/amount.rs similarity index 95% rename from crates/ibc/src/applications/transfer/amount.rs rename to crates/ibc-apps/ics20-transfer/types/src/amount.rs index b14a1797a..cc793200b 100644 --- a/crates/ibc/src/applications/transfer/amount.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/amount.rs @@ -1,17 +1,12 @@ //! Contains the `Amount` type, which represents amounts of tokens transferred. - use core::ops::Deref; use core::str::FromStr; use derive_more::{Display, From, Into}; +use ibc::prelude::*; use primitive_types::U256; use super::error::TokenTransferError; -#[cfg(feature = "schema")] -use crate::alloc::borrow::ToOwned; -#[cfg(feature = "schema")] -use crate::alloc::string::String; -use crate::prelude::*; /// A type for representing token transfer amounts. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -19,7 +14,7 @@ use crate::prelude::*; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Display, From, Into)] pub struct Amount( #[cfg_attr(feature = "schema", schemars(with = "String"))] - #[serde(serialize_with = "crate::serializers::serde_string::serialize")] + #[serde(serialize_with = "crate::serializers::serialize")] #[serde(deserialize_with = "deserialize")] U256, ); diff --git a/crates/ibc/src/applications/transfer/coin.rs b/crates/ibc-apps/ics20-transfer/types/src/coin.rs similarity index 99% rename from crates/ibc/src/applications/transfer/coin.rs rename to crates/ibc-apps/ics20-transfer/types/src/coin.rs index be7a41172..70f3d832b 100644 --- a/crates/ibc/src/applications/transfer/coin.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/coin.rs @@ -1,14 +1,13 @@ //! Defines coin types; the objects that are being transferred. - use core::fmt::{Display, Error as FmtError, Formatter}; use core::str::FromStr; +use ibc::prelude::*; use ibc_proto::cosmos::base::v1beta1::Coin as ProtoCoin; use super::amount::Amount; use super::denom::{BaseDenom, PrefixedDenom}; use super::error::TokenTransferError; -use crate::prelude::*; /// A `Coin` type with fully qualified `PrefixedDenom`. pub type PrefixedCoin = Coin; diff --git a/crates/ibc/src/applications/transfer/denom.rs b/crates/ibc-apps/ics20-transfer/types/src/denom.rs similarity index 98% rename from crates/ibc/src/applications/transfer/denom.rs rename to crates/ibc-apps/ics20-transfer/types/src/denom.rs index 855b542ae..42847ceba 100644 --- a/crates/ibc/src/applications/transfer/denom.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/denom.rs @@ -1,16 +1,14 @@ //! Defines types to represent "denominations" [as defined in ICS-20](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md#data-structures) - use core::fmt::{Display, Error as FmtError, Formatter}; use core::str::FromStr; use derive_more::{Display, From}; +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; use ibc_proto::ibc::applications::transfer::v1::DenomTrace as RawDenomTrace; use super::error::TokenTransferError; -use crate::core::ics24_host::identifier::{ChannelId, PortId}; -use crate::prelude::*; -#[cfg(feature = "serde")] -use crate::serializers::serde_string; +use crate::serializers; /// The "base" of a denomination. /// @@ -218,7 +216,7 @@ impl Display for TracePath { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct PrefixedDenom { /// A series of `{port-id}/{channel-id}`s for tracing the source of the token. - #[cfg_attr(feature = "serde", serde(with = "serde_string"))] + #[cfg_attr(feature = "serde", serde(with = "serializers"))] #[cfg_attr(feature = "schema", schemars(with = "String"))] pub trace_path: TracePath, /// Base denomination of the relayed fungible token. diff --git a/crates/ibc/src/applications/transfer/error.rs b/crates/ibc-apps/ics20-transfer/types/src/error.rs similarity index 93% rename from crates/ibc/src/applications/transfer/error.rs rename to crates/ibc-apps/ics20-transfer/types/src/error.rs index d47decc33..8372f8aa5 100644 --- a/crates/ibc/src/applications/transfer/error.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/error.rs @@ -1,17 +1,15 @@ //! Defines the token transfer error type - use core::convert::Infallible; use core::str::Utf8Error; use displaydoc::Display; +use ibc::core::ics04_channel::acknowledgement::StatusValue; +use ibc::core::ics04_channel::channel::Order; +use ibc::core::ics24_host::identifier::{ChannelId, IdentifierError, PortId}; +use ibc::core::ContextError; +use ibc::prelude::*; use uint::FromDecStrErr; -use crate::core::ics04_channel::acknowledgement::StatusValue; -use crate::core::ics04_channel::channel::Order; -use crate::core::ics24_host::identifier::{ChannelId, IdentifierError, PortId}; -use crate::core::ContextError; -use crate::prelude::*; - #[derive(Display, Debug)] pub enum TokenTransferError { /// context error: `{0}` diff --git a/crates/ibc/src/applications/transfer/events.rs b/crates/ibc-apps/ics20-transfer/types/src/events.rs similarity index 83% rename from crates/ibc/src/applications/transfer/events.rs rename to crates/ibc-apps/ics20-transfer/types/src/events.rs index 45eff108a..bee96cf72 100644 --- a/crates/ibc/src/applications/transfer/events.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/events.rs @@ -1,11 +1,11 @@ //! Defines all token transfer event types +use ibc::core::events::ModuleEvent; +use ibc::core::ics04_channel::acknowledgement::AcknowledgementStatus; +use ibc::prelude::*; +use ibc::Signer; use super::Memo; -use crate::applications::transfer::{Amount, PrefixedDenom, MODULE_ID_STR}; -use crate::core::events::ModuleEvent; -use crate::core::ics04_channel::acknowledgement::AcknowledgementStatus; -use crate::prelude::*; -use crate::signer::Signer; +use crate::{Amount, PrefixedDenom, MODULE_ID_STR}; const EVENT_TYPE_PACKET: &str = "fungible_token_packet"; const EVENT_TYPE_TIMEOUT: &str = "timeout"; @@ -22,8 +22,8 @@ pub enum Event { Transfer(TransferEvent), } -/// Event emitted in the [`onRecvPacket`][super::context::on_recv_packet_execute] -/// module callback to indicate the that the `RecvPacket` message was processed +/// Event emitted in the `onRecvPacket` module callback to indicate that the +/// `RecvPacket` message was processed pub struct RecvEvent { pub sender: Signer, pub receiver: Signer, @@ -58,8 +58,7 @@ impl From for ModuleEvent { } } -/// Event emitted in the [`onAcknowledgePacket`][super::context::on_acknowledgement_packet_execute] -/// module callback +/// Event emitted by the `onAcknowledgePacket` module callback pub struct AckEvent { pub sender: Signer, pub receiver: Signer, @@ -94,8 +93,8 @@ impl From for ModuleEvent { } } -/// Event emitted in the [`onAcknowledgePacket`][super::context::on_acknowledgement_packet_execute] -/// module callback to indicate whether the acknowledgement is a success or a failure +/// Event emitted by the `onAcknowledgePacket` module callback to indicate +/// whether the acknowledgement is a success or a failure pub struct AckStatusEvent { pub acknowledgement: AcknowledgementStatus, } @@ -115,8 +114,7 @@ impl From for ModuleEvent { } } -/// Event emitted in the [`onTimeoutPacket`][super::context::on_timeout_packet_execute] -/// module callback +/// Event emitted by the `onTimeoutPacket` module callback pub struct TimeoutEvent { pub refund_receiver: Signer, pub refund_denom: PrefixedDenom, @@ -145,8 +143,7 @@ impl From for ModuleEvent { } } -/// Event emitted in the [`onRecvPacket`][super::context::on_recv_packet_execute] -/// module callback when new tokens are minted +/// Event emitted by the `onRecvPacket` module callback when new tokens are minted pub struct DenomTraceEvent { pub trace_hash: Option, pub denom: PrefixedDenom, @@ -166,8 +163,7 @@ impl From for ModuleEvent { } } -/// Event emitted in [`sendTransfer`][super::send_transfer] after a successful -/// transfer +/// Event emitted after a successful `sendTransfer` pub struct TransferEvent { pub sender: Signer, pub receiver: Signer, diff --git a/crates/ibc-apps/ics20-transfer/types/src/lib.rs b/crates/ibc-apps/ics20-transfer/types/src/lib.rs new file mode 100644 index 000000000..723a146d3 --- /dev/null +++ b/crates/ibc-apps/ics20-transfer/types/src/lib.rs @@ -0,0 +1,69 @@ +//! Implementation of the IBC [fungible token transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md) (ICS-20) 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_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(any(test, feature = "std"))] +extern crate std; + +#[cfg(feature = "serde")] +mod amount; +#[cfg(feature = "serde")] +pub use amount::*; +#[cfg(feature = "serde")] +mod coin; +#[cfg(feature = "serde")] +pub use coin::*; +#[cfg(feature = "serde")] +mod denom; +#[cfg(feature = "serde")] +pub use denom::*; +#[cfg(feature = "serde")] +pub mod events; +#[cfg(feature = "serde")] +pub mod msgs; +#[cfg(feature = "serde")] +pub mod packet; + +#[cfg(feature = "serde")] +pub(crate) mod serializers; + +pub mod error; +mod memo; +pub use memo::*; + +/// Re-exports ICS-20 token transfer proto types from the `ibc-proto-rs` crate +/// for added convenience +pub mod proto { + pub use ibc_proto::ibc::apps::transfer; +} + +/// Module identifier for the ICS20 application. +pub const MODULE_ID_STR: &str = "transfer"; + +/// The port identifier that the ICS20 applications +/// typically bind with. +pub const PORT_ID_STR: &str = "transfer"; + +/// ICS20 application current version. +pub const VERSION: &str = "ics20-1"; + +/// The successful string used for creating an acknowledgement status, +/// equivalent to `base64::encode(0x01)`. +pub const ACK_SUCCESS_B64: &str = "AQ=="; + +use ibc::core::ics04_channel::acknowledgement::StatusValue; + +/// Returns a successful acknowledgement status for the token transfer application. +pub fn ack_success_b64() -> StatusValue { + StatusValue::new(ACK_SUCCESS_B64).expect("ack status value is never supposed to be empty") +} diff --git a/crates/ibc/src/applications/transfer/memo.rs b/crates/ibc-apps/ics20-transfer/types/src/memo.rs similarity index 98% rename from crates/ibc/src/applications/transfer/memo.rs rename to crates/ibc-apps/ics20-transfer/types/src/memo.rs index 3d31f206e..de7368fa6 100644 --- a/crates/ibc/src/applications/transfer/memo.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/memo.rs @@ -7,7 +7,7 @@ use core::fmt::{ }; use core::str::FromStr; -use crate::prelude::*; +use ibc::prelude::*; /// Represents the token transfer memo #[cfg_attr( diff --git a/crates/ibc/src/applications/transfer/msgs.rs b/crates/ibc-apps/ics20-transfer/types/src/msgs/mod.rs similarity index 98% rename from crates/ibc/src/applications/transfer/msgs.rs rename to crates/ibc-apps/ics20-transfer/types/src/msgs/mod.rs index c019a3120..9c6780741 100644 --- a/crates/ibc/src/applications/transfer/msgs.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/msgs/mod.rs @@ -1,3 +1,2 @@ //! Defines the token transfer message type - pub mod transfer; diff --git a/crates/ibc/src/applications/transfer/msgs/transfer.rs b/crates/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs similarity index 91% rename from crates/ibc/src/applications/transfer/msgs/transfer.rs rename to crates/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs index 5ea2d66d7..eb061d9a2 100644 --- a/crates/ibc/src/applications/transfer/msgs/transfer.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/msgs/transfer.rs @@ -1,17 +1,16 @@ //! Defines the token transfer message type - +use ibc::core::ics04_channel::error::PacketError; +use ibc::core::ics04_channel::timeout::TimeoutHeight; +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::core::timestamp::Timestamp; +use ibc::core::{ContextError, Msg}; +use ibc::prelude::*; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::applications::transfer::v1::MsgTransfer as RawMsgTransfer; use ibc_proto::Protobuf; -use crate::applications::transfer::error::TokenTransferError; -use crate::applications::transfer::packet::PacketData; -use crate::core::ics04_channel::error::PacketError; -use crate::core::ics04_channel::timeout::TimeoutHeight; -use crate::core::ics24_host::identifier::{ChannelId, PortId}; -use crate::core::timestamp::Timestamp; -use crate::core::{ContextError, Msg}; -use crate::prelude::*; +use crate::error::TokenTransferError; +use crate::packet::PacketData; pub(crate) const TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; diff --git a/crates/ibc/src/applications/transfer/packet.rs b/crates/ibc-apps/ics20-transfer/types/src/packet.rs similarity index 95% rename from crates/ibc/src/applications/transfer/packet.rs rename to crates/ibc-apps/ics20-transfer/types/src/packet.rs index 63058b2ed..bb556af34 100644 --- a/crates/ibc/src/applications/transfer/packet.rs +++ b/crates/ibc-apps/ics20-transfer/types/src/packet.rs @@ -1,16 +1,14 @@ //! Contains the `PacketData` type that defines the structure of token transfers' packet bytes -use alloc::string::ToString; use core::convert::TryFrom; use core::str::FromStr; +use ibc::prelude::*; +use ibc::Signer; use ibc_proto::ibc::applications::transfer::v2::FungibleTokenPacketData as RawPacketData; use super::error::TokenTransferError; use super::{Amount, Memo, PrefixedCoin, PrefixedDenom}; -#[cfg(feature = "schema")] -use crate::alloc::borrow::ToOwned; -use crate::signer::Signer; /// Defines the structure of token transfers' packet bytes #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -69,7 +67,7 @@ mod tests { use primitive_types::U256; use super::*; - use crate::applications::transfer::BaseCoin; + use crate::BaseCoin; impl PacketData { pub fn new_dummy() -> Self { diff --git a/crates/ibc-apps/ics20-transfer/types/src/serializers.rs b/crates/ibc-apps/ics20-transfer/types/src/serializers.rs new file mode 100644 index 000000000..57f6e20d3 --- /dev/null +++ b/crates/ibc-apps/ics20-transfer/types/src/serializers.rs @@ -0,0 +1,27 @@ +use core::fmt::Display; +use core::str::FromStr; + +use ibc::prelude::*; +use serde::{de, Deserialize, Deserializer, Serializer}; + +// Note: This method serializes to a String instead of a str +// in order to avoid a wasm compilation issue. Specifically, +// str (de)serialization hits some kind of f64/f32 case +// when compiled into wasm, but this fails validation on +// f32/f64 wasm runtimes. +pub fn serialize(value: &T, serializer: S) -> Result +where + T: Display, + S: Serializer, +{ + serializer.serialize_str(value.to_string().as_ref()) +} + +pub fn deserialize<'de, T, D>(deserializer: D) -> Result +where + T: FromStr, + T::Err: Display, + D: Deserializer<'de>, +{ + T::from_str(::deserialize(deserializer)?.as_str()).map_err(de::Error::custom) +} diff --git a/crates/ibc-apps/src/lib.rs b/crates/ibc-apps/src/lib.rs new file mode 100644 index 000000000..3693d65d4 --- /dev/null +++ b/crates/ibc-apps/src/lib.rs @@ -0,0 +1,20 @@ +//! Re-exports implementations and data structures of different IBC applications. +#![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 the implementation of the IBC [fungible token +/// transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md) +/// (ICS-20) application logic. +pub mod transfer { + #[doc(inline)] + pub use ibc_app_transfer::*; +} diff --git a/crates/ibc-testkit/Cargo.toml b/crates/ibc-testkit/Cargo.toml index 8a8c9137d..57b1e061b 100644 --- a/crates/ibc-testkit/Cargo.toml +++ b/crates/ibc-testkit/Cargo.toml @@ -30,7 +30,8 @@ tracing = { workspace = true } typed-builder = { workspace = true } # ibc dependencies -ibc = { version = "0.47.0" , path = "../ibc" } # NOTE: since `ibc-testkit` does not well support `no_std` yet, we keep `ibc` default features enabled +ibc = { version = "0.47.0", path = "../ibc" } # NOTE: since `ibc-testkit` does not well support `no_std` yet, we keep `ibc` default features enabled +ibc-app-transfer = { version = "0.47.0", path = "../ibc-apps/ics20-transfer", default-features = false, features = ["serde"] } # cosmos dependencies tendermint = { workspace = true } @@ -46,6 +47,7 @@ test-log = { workspace = true } default = ["std"] std = [ "ibc/std", + "ibc-app-transfer/std", "tracing/std", "prost/std", "serde/std", diff --git a/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs index f8ec4ae88..1e31a3254 100644 --- a/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs +++ b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs @@ -1,11 +1,11 @@ -use ibc::applications::transfer::context::{ - cosmos_adr028_escrow_address, TokenTransferExecutionContext, TokenTransferValidationContext, -}; -use ibc::applications::transfer::error::TokenTransferError; -use ibc::applications::transfer::PrefixedCoin; use ibc::core::ics24_host::identifier::{ChannelId, PortId}; use ibc::prelude::*; use ibc::Signer; +use ibc_app_transfer::context::{ + cosmos_adr028_escrow_address, TokenTransferExecutionContext, TokenTransferValidationContext, +}; +use ibc_app_transfer::types::error::TokenTransferError; +use ibc_app_transfer::types::PrefixedCoin; use subtle_encoding::bech32; use super::types::DummyTransferModule; diff --git a/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs b/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs index b0aaaae1b..2874f43cd 100644 --- a/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs +++ b/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs @@ -1,10 +1,10 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; -use ibc::applications::transfer::MODULE_ID_STR; use ibc::core::ics24_host::identifier::PortId; use ibc::core::router::{Module, ModuleId}; use ibc::prelude::*; +use ibc_app_transfer::types::MODULE_ID_STR; use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; diff --git a/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs b/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs index 0bf50c99b..49581488a 100644 --- a/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs +++ b/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs @@ -1,13 +1,13 @@ use alloc::string::ToString; -use ibc::applications::transfer::msgs::transfer::MsgTransfer; -use ibc::applications::transfer::packet::PacketData; -use ibc::applications::transfer::{Memo, PrefixedCoin}; use ibc::core::ics04_channel::packet::{Packet, Sequence}; use ibc::core::ics04_channel::timeout::TimeoutHeight; use ibc::core::ics24_host::identifier::{ChannelId, PortId}; use ibc::core::timestamp::Timestamp; use ibc::Signer; +use ibc_app_transfer::types::msgs::transfer::MsgTransfer; +use ibc_app_transfer::types::packet::PacketData; +use ibc_app_transfer::types::{Memo, PrefixedCoin}; use typed_builder::TypedBuilder; use crate::utils::dummies::core::signer::dummy_account_id; diff --git a/crates/ibc-testkit/tests/applications/transfer.rs b/crates/ibc-testkit/tests/applications/transfer.rs index 64d56b8c2..9ae6f664f 100644 --- a/crates/ibc-testkit/tests/applications/transfer.rs +++ b/crates/ibc-testkit/tests/applications/transfer.rs @@ -1,12 +1,13 @@ -use ibc::applications::transfer::context::{ - cosmos_adr028_escrow_address, on_chan_open_init_execute, on_chan_open_init_validate, - on_chan_open_try_execute, on_chan_open_try_validate, -}; -use ibc::applications::transfer::VERSION; use ibc::core::ics04_channel::channel::{Counterparty, Order}; use ibc::core::ics04_channel::Version; use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; use ibc::prelude::*; +use ibc_app_transfer::context::cosmos_adr028_escrow_address; +use ibc_app_transfer::module::{ + on_chan_open_init_execute, on_chan_open_init_validate, on_chan_open_try_execute, + on_chan_open_try_validate, +}; +use ibc_app_transfer::types::VERSION; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use subtle_encoding::bech32; diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs index 25a7d36c5..14778bc25 100644 --- a/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs @@ -1,4 +1,3 @@ -use ibc::applications::transfer::MODULE_ID_STR; use ibc::core::events::{IbcEvent, MessageEvent}; use ibc::core::ics03_connection::connection::{ ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, @@ -13,6 +12,7 @@ use ibc::core::timestamp::ZERO_DURATION; use ibc::core::{execute, validate, MsgEnvelope}; use ibc::prelude::*; use ibc::Height; +use ibc_app_transfer::types::MODULE_ID_STR; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; use ibc_testkit::testapp::ibc::core::types::MockContext; diff --git a/crates/ibc-testkit/tests/core/router.rs b/crates/ibc-testkit/tests/core/router.rs index 94a5a0c2a..e27739858 100644 --- a/crates/ibc-testkit/tests/core/router.rs +++ b/crates/ibc-testkit/tests/core/router.rs @@ -1,6 +1,3 @@ -use ibc::applications::transfer::error::TokenTransferError; -use ibc::applications::transfer::msgs::transfer::MsgTransfer; -use ibc::applications::transfer::{send_transfer, BaseCoin}; use ibc::core::events::{IbcEvent, MessageEvent}; use ibc::core::ics02_client::msgs::create_client::MsgCreateClient; use ibc::core::ics02_client::msgs::update_client::MsgUpdateClient; @@ -23,6 +20,10 @@ use ibc::core::timestamp::Timestamp; use ibc::core::{dispatch, MsgEnvelope, RouterError, ValidationContext}; use ibc::prelude::*; use ibc::Height; +use ibc_app_transfer::handler::send_transfer::send_transfer; +use ibc_app_transfer::types::error::TokenTransferError; +use ibc_app_transfer::types::msgs::transfer::MsgTransfer; +use ibc_app_transfer::types::BaseCoin; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; diff --git a/crates/ibc/Cargo.toml b/crates/ibc/Cargo.toml index 8bd87b26f..9a9cc1805 100644 --- a/crates/ibc/Cargo.toml +++ b/crates/ibc/Cargo.toml @@ -22,7 +22,6 @@ borsh = { workspace = true, optional = true } bytes = { workspace = true } derive_more = { workspace = true } displaydoc = { workspace = true } -primitive-types = { workspace = true } prost = { workspace = true } serde_derive = { workspace = true, optional = true } serde = { workspace = true, optional = true } @@ -32,9 +31,9 @@ sha2 = { workspace = true, default-features = false } time = { workspace = true, default-features = false } schemars = { workspace = true, optional = true } typed-builder = { workspace = true, optional = true } -uint = { version = "0.9", default-features = false } # ibc dependencies +# ibc-apps = { version = "0.47.0", path = "../ibc-apps" } ibc-derive = { version = "0.3.0", path = "../ibc-derive" } ibc-proto = { workspace = true } ics23 = { workspace = true, features = ["host-functions"] } @@ -70,8 +69,6 @@ std = [ "serde_json/std", "sha2/std", "displaydoc/std", - "uint/std", - "primitive-types/std", "tendermint/clock", "tendermint/std", ] diff --git a/crates/ibc/src/applications/mod.rs b/crates/ibc/src/applications/mod.rs deleted file mode 100644 index bfbfd10f6..000000000 --- a/crates/ibc/src/applications/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Implementation of IBC applications - -#[cfg(feature = "serde")] -pub mod transfer; diff --git a/crates/ibc/src/applications/transfer/mod.rs b/crates/ibc/src/applications/transfer/mod.rs deleted file mode 100644 index 198a9f82e..000000000 --- a/crates/ibc/src/applications/transfer/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Implementation of the [fungible token transfer module](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md) (ICS-20) - -pub mod amount; -pub mod coin; -pub mod context; -pub mod denom; -pub mod error; -pub mod events; -pub mod memo; -pub mod msgs; -pub mod packet; - -pub use amount::*; -pub use coin::*; -pub use denom::*; -pub use memo::*; - -mod relay; - -pub use relay::send_transfer::{send_transfer, send_transfer_execute, send_transfer_validate}; - -/// Module identifier for the ICS20 application. -pub const MODULE_ID_STR: &str = "transfer"; - -/// The port identifier that the ICS20 applications -/// typically bind with. -pub const PORT_ID_STR: &str = "transfer"; - -/// ICS20 application current version. -pub const VERSION: &str = "ics20-1"; - -/// The successful string used for creating an acknowledgement status, -/// equivalent to `base64::encode(0x01)`. -pub const ACK_SUCCESS_B64: &str = "AQ=="; - -use crate::core::ics04_channel::acknowledgement::StatusValue; - -/// Returns a successful acknowledgement status for the token transfer application. -pub fn ack_success_b64() -> StatusValue { - StatusValue::new(ACK_SUCCESS_B64).expect("ack status value is never supposed to be empty") -} diff --git a/crates/ibc/src/core/ics04_channel/acknowledgement.rs b/crates/ibc/src/core/ics04_channel/acknowledgement.rs index c7a91c754..732234103 100644 --- a/crates/ibc/src/core/ics04_channel/acknowledgement.rs +++ b/crates/ibc/src/core/ics04_channel/acknowledgement.rs @@ -140,71 +140,3 @@ impl From for Acknowledgement { .expect("token transfer internal error: ack is never supposed to be empty") } } - -#[cfg(test)] -mod test { - use super::*; - use crate::applications::transfer::ack_success_b64; - use crate::applications::transfer::error::TokenTransferError; - - #[test] - fn test_ack_ser() { - fn ser_json_assert_eq(ack: AcknowledgementStatus, json_str: &str) { - let ser = serde_json::to_string(&ack).unwrap(); - assert_eq!(ser, json_str) - } - - ser_json_assert_eq( - AcknowledgementStatus::success(ack_success_b64()), - r#"{"result":"AQ=="}"#, - ); - ser_json_assert_eq( - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()), - r#"{"error":"failed to deserialize packet data"}"#, - ); - } - - #[test] - fn test_ack_success_to_vec() { - let ack_success: Vec = AcknowledgementStatus::success(ack_success_b64()).into(); - - // Check that it's the same output as ibc-go - // Note: this also implicitly checks that the ack bytes are non-empty, - // which would make the conversion to `Acknowledgement` panic - assert_eq!(ack_success, r#"{"result":"AQ=="}"#.as_bytes()); - } - - #[test] - fn test_ack_error_to_vec() { - let ack_error: Vec = - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()) - .into(); - - // Check that it's the same output as ibc-go - // Note: this also implicitly checks that the ack bytes are non-empty, - // which would make the conversion to `Acknowledgement` panic - assert_eq!( - ack_error, - r#"{"error":"failed to deserialize packet data"}"#.as_bytes() - ); - } - - #[test] - fn test_ack_de() { - fn de_json_assert_eq(json_str: &str, ack: AcknowledgementStatus) { - let de = serde_json::from_str::(json_str).unwrap(); - assert_eq!(de, ack) - } - - de_json_assert_eq( - r#"{"result":"AQ=="}"#, - AcknowledgementStatus::success(ack_success_b64()), - ); - de_json_assert_eq( - r#"{"error":"failed to deserialize packet data"}"#, - AcknowledgementStatus::error(TokenTransferError::PacketDataDeserialization.into()), - ); - - assert!(serde_json::from_str::(r#"{"success":"AQ=="}"#).is_err()); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler.rs b/crates/ibc/src/core/ics04_channel/handler.rs index 0950dc923..ab39f168a 100644 --- a/crates/ibc/src/core/ics04_channel/handler.rs +++ b/crates/ibc/src/core/ics04_channel/handler.rs @@ -1,13 +1,13 @@ //! This module implements the processing logic for ICS4 (channel) messages. -pub(crate) mod acknowledgement; -pub(crate) mod chan_close_confirm; -pub(crate) mod chan_close_init; -pub(crate) mod chan_open_ack; -pub(crate) mod chan_open_confirm; -pub(crate) mod chan_open_init; -pub(crate) mod chan_open_try; -pub(crate) mod recv_packet; -pub(crate) mod send_packet; -pub(crate) mod timeout; -pub(crate) mod timeout_on_close; +pub mod acknowledgement; +pub mod chan_close_confirm; +pub mod chan_close_init; +pub mod chan_open_ack; +pub mod chan_open_confirm; +pub mod chan_open_init; +pub mod chan_open_try; +pub mod recv_packet; +pub mod send_packet; +pub mod timeout; +pub mod timeout_on_close; diff --git a/crates/ibc/src/core/ics04_channel/mod.rs b/crates/ibc/src/core/ics04_channel/mod.rs index 40e6538cf..a969a9cde 100644 --- a/crates/ibc/src/core/ics04_channel/mod.rs +++ b/crates/ibc/src/core/ics04_channel/mod.rs @@ -6,7 +6,7 @@ pub mod context; pub mod error; pub mod events; -pub(crate) mod handler; +pub mod handler; pub mod msgs; pub mod packet; pub mod timeout; diff --git a/crates/ibc/src/lib.rs b/crates/ibc/src/lib.rs index 16318bdfb..868b36c42 100644 --- a/crates/ibc/src/lib.rs +++ b/crates/ibc/src/lib.rs @@ -24,9 +24,6 @@ //! client interface that is defined in `Core`) for specific consensus algorithms. A chain uses these //! verification algorithms to verify the state of remote chains. //! -//! + [Applications](applications) consists of implementations of some IBC applications. This is the part of -//! the protocol that abstracts away the core protocol and focuses solely on business logic. -//! //! 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`](core::ExecutionContext)) while processing `M`. If a transaction on your @@ -50,7 +47,6 @@ pub use signer::Signer; /// Represents a block height pub use crate::core::ics02_client::height::Height; -pub mod applications; pub mod clients; pub mod core; pub mod hosts; @@ -65,7 +61,6 @@ mod serializers; /// Re-exports pertinent ibc proto types from the `ibc-proto-rs` crate for added convenience pub mod proto { pub use ibc_proto::google::protobuf::Any; - pub use ibc_proto::ibc::apps::transfer; pub use ibc_proto::ibc::lightclients::tendermint; pub use ibc_proto::ibc::{core, mock}; pub use ibc_proto::{ics23, Protobuf}; diff --git a/crates/ibc/src/serializers.rs b/crates/ibc/src/serializers.rs index a4b56473b..081cd05fe 100644 --- a/crates/ibc/src/serializers.rs +++ b/crates/ibc/src/serializers.rs @@ -12,35 +12,6 @@ where hex.serialize(serializer) } -pub mod serde_string { - use core::fmt::Display; - use core::str::FromStr; - - use serde::{de, Deserialize, Deserializer, Serializer}; - - use crate::prelude::*; - - // Note: used String version (slower + heap) instead of str, - // because both str ser/de hit some kind of f64/f32 case when compiled into wasm - // and fails to be validated f32/f64 wasm runtimes - pub fn serialize(value: &T, serializer: S) -> Result - where - T: Display, - S: Serializer, - { - serializer.serialize_str(value.to_string().as_ref()) - } - - pub fn deserialize<'de, T, D>(deserializer: D) -> Result - where - T: FromStr, - T::Err: Display, - D: Deserializer<'de>, - { - T::from_str(::deserialize(deserializer)?.as_str()).map_err(de::Error::custom) - } -} - /// Test that a struct `T` can be: /// /// - parsed out of the provided JSON data