diff --git a/.changelog/unreleased/improvements/ibc/1759-complete-ics20.md b/.changelog/unreleased/improvements/ibc/1759-complete-ics20.md new file mode 100644 index 0000000000..8d7cbbbbad --- /dev/null +++ b/.changelog/unreleased/improvements/ibc/1759-complete-ics20.md @@ -0,0 +1 @@ +- Complete ICS20 implementation ([#1759](https://github.com/informalsystems/ibc-rs/issues/1759)) diff --git a/Cargo.lock b/Cargo.lock index 109d9347c4..0ea314a894 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1488,6 +1488,7 @@ dependencies = [ "time 0.3.9", "tracing", "tracing-subscriber", + "uint", ] [[package]] @@ -1578,7 +1579,6 @@ dependencies = [ "tonic", "tracing", "tracing-subscriber", - "uint", "uuid 1.1.0", ] diff --git a/relayer-cli/src/commands/tx/transfer.rs b/relayer-cli/src/commands/tx/transfer.rs index 8875e19172..860a7c51e2 100644 --- a/relayer-cli/src/commands/tx/transfer.rs +++ b/relayer-cli/src/commands/tx/transfer.rs @@ -3,6 +3,7 @@ use abscissa_core::{config::Override, Command, FrameworkErrorKind, Runnable}; use core::time::Duration; use ibc::{ + applications::transfer::Amount, core::{ ics02_client::client_state::ClientState, ics02_client::height::Height, @@ -14,7 +15,6 @@ use ibc_relayer::chain::handle::ChainHandle; use ibc_relayer::chain::requests::{ QueryChannelRequest, QueryClientStateRequest, QueryConnectionRequest, }; -use ibc_relayer::transfer::Amount; use ibc_relayer::{ config::Config, transfer::{build_and_send_transfer_messages, TransferOptions}, diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index a624793ef6..80956ac780 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -37,7 +37,7 @@ serde_json = { version = "1" } bytes = "1.1.0" prost = { version = "0.10" } prost-types = { version = "0.10" } -tonic = { version = "0.7", features = ["tls", "tls-roots"] } +tonic = { version = "0.7.2", features = ["tls", "tls-roots"] } futures = "0.3.21" crossbeam-channel = "0.5.4" k256 = { version = "0.10.4", features = ["ecdsa-core", "ecdsa", "sha256"]} @@ -58,7 +58,6 @@ flex-error = { version = "0.4.4", default-features = false } signature = "1.4.0" anyhow = "1.0" semver = "1.0" -uint = "0.9" humantime = "2.1.0" nanoid = "0.4.0" regex = "1.5.5" diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index fc671656c3..34b931dae7 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -603,7 +603,9 @@ impl ChainEndpoint for CosmosSdkChain { .map_err(|e| Error::key_not_found(self.config.key_name.clone(), e))?; let bech32 = encode_to_bech32(&key.address.to_hex(), &self.config.account_prefix)?; - Ok(Signer::new(bech32)) + bech32 + .parse() + .map_err(|e| Error::ics02(ClientError::signer(e))) } /// Get the chain configuration diff --git a/relayer/src/channel/version.rs b/relayer/src/channel/version.rs index 56218a267e..dd2d74cb4d 100644 --- a/relayer/src/channel/version.rs +++ b/relayer/src/channel/version.rs @@ -5,13 +5,13 @@ //! handshake. use ibc::{ - applications::ics20_fungible_token_transfer, + applications::transfer, core::{ics04_channel::Version, ics24_host::identifier::PortId}, }; /// Returns the default channel version, depending on the the given [`PortId`]. pub fn default_by_port(port_id: &PortId) -> Option { - if port_id.as_str() == ics20_fungible_token_transfer::PORT_ID { + if port_id.as_str() == transfer::PORT_ID_STR { // https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#forwards-compatibility Some(Version::ics20()) } else { diff --git a/relayer/src/transfer.rs b/relayer/src/transfer.rs index 45b5930c5d..b21f4dd73b 100644 --- a/relayer/src/transfer.rs +++ b/relayer/src/transfer.rs @@ -1,23 +1,22 @@ -use core::fmt::{Display, Formatter}; -use core::str::FromStr; use core::time::Duration; use flex_error::{define_error, DetailOnly}; -use ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; +use ibc::applications::transfer::error::Error as Ics20Error; +use ibc::applications::transfer::msgs::transfer::MsgTransfer; +use ibc::applications::transfer::Amount; use ibc::core::ics24_host::identifier::{ChainId, ChannelId, PortId}; use ibc::events::IbcEvent; use ibc::signer::Signer; use ibc::timestamp::{Timestamp, TimestampOverflowError}; use ibc::tx_msg::Msg; use ibc::Height; +use ibc_proto::cosmos::base::v1beta1::Coin; use ibc_proto::google::protobuf::Any; -use uint::FromStrRadixErr; use crate::chain::handle::ChainHandle; use crate::chain::tracking::TrackedMsgs; use crate::chain::ChainStatus; use crate::error::Error; -use crate::util::bigint::U256; define_error! { TransferError { @@ -55,34 +54,15 @@ define_error! { e.event) }, + TokenTransfer + [ Ics20Error ] + |_| { "Token transfer error" }, + ZeroTimeout | _ | { "packet timeout height and packet timeout timestamp cannot both be 0" }, } } -#[derive(Clone, Copy, Debug, Default)] -pub struct Amount(pub U256); - -impl Display for Amount { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl FromStr for Amount { - type Err = FromStrRadixErr; - - fn from_str(s: &str) -> Result { - Ok(Self(U256::from_str_radix(s, 10)?)) - } -} - -impl From for Amount { - fn from(amount: u64) -> Self { - Self(amount.into()) - } -} - #[derive(Copy, Clone)] pub struct TransferTimeout { pub timeout_height: Height, @@ -150,10 +130,10 @@ pub fn build_transfer_message( let msg = MsgTransfer { source_port: packet_src_port_id, source_channel: packet_src_channel_id, - token: Some(ibc_proto::cosmos::base::v1beta1::Coin { + token: Coin { denom, amount: amount.to_string(), - }), + }, sender, receiver, timeout_height, @@ -168,10 +148,7 @@ pub fn build_and_send_transfer_messages Result, TransferError> { - let receiver = match &opts.receiver { - None => packet_dst_chain.get_signer().map_err(TransferError::key)?, - Some(r) => r.clone().into(), - }; + let receiver = packet_dst_chain.get_signer().map_err(TransferError::key)?; let sender = packet_src_chain.get_signer().map_err(TransferError::key)?; @@ -188,10 +165,10 @@ pub fn build_and_send_transfer_messages( let transfer_options = TransferOptions { packet_src_port_id: channel.port_a.value().clone(), packet_src_channel_id: *channel.channel_id_a.value(), - amount: Amount(amount.into()), + amount: amount.into(), denom: denom.value().to_string(), receiver: Some(recipient.value().0.clone()), timeout_height_offset, diff --git a/tools/test-framework/src/ibc/denom.rs b/tools/test-framework/src/ibc/denom.rs index e0059c3887..ec4bcabc6c 100644 --- a/tools/test-framework/src/ibc/denom.rs +++ b/tools/test-framework/src/ibc/denom.rs @@ -4,7 +4,9 @@ use core::fmt::{self, Display}; use eyre::Report as Error; -use ibc::applications::ics20_fungible_token_transfer as token_transfer; +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use sha2::{Digest, Sha256}; +use subtle_encoding::hex; use crate::types::id::{TaggedChannelIdRef, TaggedPortIdRef}; use crate::types::tagged::*; @@ -56,10 +58,30 @@ pub fn derive_ibc_denom( channel_id: &TaggedChannelIdRef, denom: &TaggedDenomRef, ) -> Result, Error> { + fn derive_denom( + port_id: &PortId, + channel_id: &ChannelId, + denom: &str, + ) -> Result { + let transfer_path = format!("{}/{}/{}", port_id, channel_id, denom); + derive_denom_with_path(&transfer_path) + } + + /// Derive the transferred token denomination using + /// + fn derive_denom_with_path(transfer_path: &str) -> Result { + let mut hasher = Sha256::new(); + hasher.update(transfer_path.as_bytes()); + + let denom_bytes = hasher.finalize(); + let denom_hex = String::from_utf8(hex::encode_upper(denom_bytes))?; + + Ok(format!("ibc/{}", denom_hex)) + } + match denom.value() { Denom::Base(denom) => { - let hashed = - token_transfer::derive_ibc_denom(port_id.value(), channel_id.value(), denom)?; + let hashed = derive_denom(port_id.value(), channel_id.value(), denom)?; Ok(MonoTagged::new(Denom::Ibc { path: format!("{}/{}", port_id, channel_id), @@ -69,8 +91,7 @@ pub fn derive_ibc_denom( } Denom::Ibc { path, denom, .. } => { let new_path = format!("{}/{}/{}", port_id, channel_id, path); - let hashed = - token_transfer::derive_ibc_denom_with_path(&format!("{}/{}", new_path, denom))?; + let hashed = derive_denom_with_path(&format!("{}/{}", new_path, denom))?; Ok(MonoTagged::new(Denom::Ibc { path: new_path, diff --git a/tools/test-framework/src/relayer/transfer.rs b/tools/test-framework/src/relayer/transfer.rs index d4ac86aff0..3af20851a5 100644 --- a/tools/test-framework/src/relayer/transfer.rs +++ b/tools/test-framework/src/relayer/transfer.rs @@ -5,12 +5,14 @@ use core::ops::Add; use core::time::Duration; -use ibc::signer::Signer; + +use ibc::applications::transfer::error::Error as Ics20Error; use ibc::timestamp::Timestamp; use ibc::Height; use ibc_proto::google::protobuf::Any; use ibc_relayer::chain::cosmos::types::config::TxConfig; use ibc_relayer::transfer::build_transfer_message as raw_build_transfer_message; +use ibc_relayer::transfer::TransferError; use crate::error::{handle_generic_error, Error}; use crate::ibc::denom::Denom; @@ -31,13 +33,26 @@ pub fn build_transfer_message( .add(Duration::from_secs(60)) .map_err(handle_generic_error)?; + let sender = sender + .value() + .address + .0 + .parse() + .map_err(|e| TransferError::token_transfer(Ics20Error::signer(e)))?; + + let receiver = recipient + .value() + .0 + .parse() + .map_err(|e| TransferError::token_transfer(Ics20Error::signer(e)))?; + Ok(raw_build_transfer_message( (*port_id.value()).clone(), **channel_id.value(), amount.into(), - denom.value().to_string(), - Signer::new(sender.value().address.0.clone()), - Signer::new(recipient.value().0.clone()), + denom.to_string(), + sender, + receiver, Height::zero(), timeout_timestamp, ))