Skip to content

Commit

Permalink
Complete ICS20 Implementation (informalsystems#1989)
Browse files Browse the repository at this point in the history
* Add ics26 Module trait

* newtype for Acknowledgement

* Define Router interface

* Improve Router trait

* Update mock router impl

* Test for router API

* Disallow duplicate module_ids in MockRouter

* Add RouterBuilder for seal-style API

* Fix failing test

* Fix CI after merge

* Chainable RouterBuilder::add_route()

* Fix test

* Add Router::has_route() trait method

* Add comments

* Separate mutating/non-mutating trait methods for shadow-paging style API

* Make most Module trait methods optional

* Default impl for MockModule::on_chan_open_try()

* Acknowledgement trait

* Extend router test

* Simplify OnRechPacketResult using FnOnce()

* Cleanup

* Use Cow<str> for CapabilityName::new()

* Use Cow<str> for CapabilityName::new()

* Use newtype ModuleId instead of trait assoc type

* Module callbacks' args as refs

* Fix mock impl

* WIP channel callbacks

* Fix ModuleId ctor validation

* TypedCapability

* Use typed capabilities

* Add Router::route_mut()

* Implement pre-dispatch channel message validation

* Avoid cloning message during channel msg dispatch

* Add ChannelReader::lookup_module_by_channel()

* Complete ics4 dispatch with callbacks

* Fix compile errors from rebase

* Fix CI test

* Improve mock impl for capabilities

* cargo fmt

* Set channel version returned from on_chan_open_try()

* Implement packet handler verification

* Add missing check already received packets on ordered channels

* Avoid cloning message during packet msg dispatch

* Implement RecvPacket NoOp

* cargo fmt and remove unused errors

* Make on_recv_packet() Ack result optional

* Don't return Result from on_recv_packet() and pass GenericAcknowledgement to on_acknowledgement_packet()

* Implement packet callbacks

* Allow callbacks to write logs and emit events

* Fix test

* Add .changelog entry

* Add a comment for state rollback expectation from dispatch()

* ics26_routing::handler::deliver() takes single message as input

* ics26_routing::handler::deliver() returns logs as well

* Remove ctx_ro

* Callbacks return ModuleOutput<T>

* Return HandlerOutputBuilder from channel and packet dispatch fn

* Revert "Callbacks return ModuleOutput<T>"

This reverts commit 1d430c9.

* Address review feedback for comments

* Extract ChannelMsg::lookup_module()

* Add ICS20 Denom type

* Add ICS20 TracePrefix & TracePath type

* Define Coin and Decimal types

* Impl conversions from/to RawDenomTrace

* Make better use of derive-more

* Impl conversions for Coin type

* Add HashedDenom and polish DenomTrace impl

* Define PacketData domain type and conversions

* Use Coin domain type in MsgTransfer

* Fix usage of Coin type in relayer code

* Always panic on decimal arith overflow

* Polish generic Signer impl

* Implement ICS20 signer type

* Coin type with generic Denom param

* Use IbcCoin in MsgTransfer and PrefixedCoin in PacketData

* Minor refactoring

* Fix test_util

* Impl AsRef<[u8]> for GenericAcknowledgement

* Ics20Context is no longer a supertrait of Ics26Context

* Add ICS20 Ack type

* Add ICS20 event enum placeholder

* Define all ICS20 expected keepers and context

* Update send_transfer() for recent context changes

* Give mut ref to set_channel_escrow_address()

* Define ICS20 callback functions

* Fix test build

* Move denom derive functions to integration tests

* Add version to chan_open_try callback

* Rename ChannelId::counter() to sequence()

* Add AppModule error variant to ChannelError

* Implement validation helpers for callbacks

* Define Ics20 callback errors

* Implement channel handshake callbacks

* Fix clippy errors

* Impl Deserialize for PacketData

* Manually implement AsRef<str> for Signer

* Fix ICS20 Ack success from impl

* Add more ICS20 errors

* Improve ack type ctor

* Add ctor for TracePrefix

* Provide denom trace methods to remove prefix and check for empty trace path

* Implement conversion from PrefixedCoin to IbcCoin

* Add Ics20Reader trait methods to check send/receive enabled

* Provided trait method get_channel_escrow_address()

* Add BankReader trait for is_blocked_account()

* Add FromStr bound for Ics20Context::AccountId

* Use IbcCoins in Bank traits

* Impl on_recv_packet() for cases where receiver chain is source

* Fallible OnRecvPacket::write_fn()

* Complete on_recv_packet impl

* Fix test build

* Fix clippy errors

* Implement remaining packet callbacks for ICS20

* Make set_denom_trace() fallible

* Don't derive AsRef

* Complete send_transfer impl

* Manual deserialize impl for Acknowledgement

* Add ctor for Acknowledgement

* Handle packet-data deserialize error separately

* Use U256 for Denom and move bigint.rs to modules/src

* Rename Denom to Amount

* Cleanup

* Fix trait definitions

* Use source enum

* Fix AccountReader trait

* Validate port_id

* More refactoring

* Rename Signer to Address

* Use Address instead of Signer where applicable

* Fix send_transfer packet creation

* Fix clippy warnings

* Define ICS20 events

* Extract relay code into separate files

* Fix clippy warnings

* Make HandlerOutput/Builder generic over events

* Define ModuleEvent

* Add AppModule variant to IbcEvent

* Impl Display for Acknowledgement

* Derive serde for ModuleId

* Add ModuleId to ModuleEvent

* Impl conversion from tuple for ModuleEventAttribute

* Impl conversions from ICS20 events to ModuleEvent

* Add event for transfer

* Remove bech32 validation from Address

* Change MsgTransfer receiver type to Address

* Extract MockContext IbcStore

* Improve conversion from Signer to AccountId

* Add deliver method to Module trait

* Implement conversions for MsgTransfer to/from Protobuf Any

* Store packet result directly in send_transfer()

* Make all ICS20 mods public

* Make all IbsStore fields public to enable access for app modules

* Implement Ics20Context for DummyTransferModule

* Fix failing test

* Manual Clone impl for MockContext

* Fix failing test

* Revert "Fix failing test"

This reverts commit 40aec61.

* Fix MockContext Clone impl

* Update trait definitions after merge with master

* Replace ModuleOutput with ModuleOutputBuilder in callbacks

* Add inout param ModuleOutputBuilder to Ics20 callback handlers

* Emit ICS20 receive packet events

* Module::deliver() must be able to emit IbcEvents

* Allow HandlerOutputBuilder to merge HandlerOutput

* Emit transfer event

* Add AckStatusEvent

* Emit ICS20 ack events

* Emit ICS20 timeout events

* Remove ABCI error code

* Remove #[allow(unused)]

* Add log for send transfer

* MsgReceipt abstraction

* Handle missing IbcEvent to AbciEvent conversions for RecvPacket and TimeoutOnClose events

* Implement ModuleEvent to AbciEvent conversion

* Make send_transfer() public

* Allow empty TracePaths

* Fix TracePath multiple prefix parse bug

* Fix TracePath empty str parse bug

* Improve DenomTrace FromStr

* Add denom validation test

* Add denom trace and serde tests

* TracePath tests

* Allow Denom with '/'

* Fix HashedDenom FromStr impl for empty hash

* Minor refactoring

* Add IbcCoin tests

* Add .changelog entry

* Fix clippy errors

* Disallow empty Signer and replace Address with it

* Use ToString as trait bound for ICS20 AccountId

* Change AccountId trait bound from FromStr to TryFrom<Signer>

* Remove Signer::new()

* Delete OCap related TODO

* Remove the PortKeeper

* Remove Module::deliver()

* Fix clippy warnings

* Rename transfer module

* Rename mod relay

* Fix tests failing due to empty signer

* Rename PORT_ID const to PORT_ID_STR

* Fix escrow addr gen

* Test cosmos escrow addr gen

* Rename receiver account

* Remove `has_denom_trace()`

* Remove `BankReader::is_blocked_account()`

* Remove `AccountId::ToString` bound

* Remove `AccountReader`

* Mint/burn into user accounts directly

* Use `chunks_exact()` instead of `windows().step_by()`

* Rename `DenomTrace::has_prefix()` to `trace_starts_with()`

* Move trace related methods into `TracePath`

* Fix add/remove prefix

* Add test for add/remove trace prefix

* Set denom trace only if not already set

* Use truncate instead of drain in cosmos_adr028_escrow_address()

* Rename DenomTrace as PrefixedDenom

* Modify all `BankKeeper` methods to use `PrefixedCoin` instead of `IbcCoin`

* Rename `Denom` as `BaseDenom`

* Impl Into<U256> and checked_add/sub() for Amount

* Add more TracePath tests

* Test TracePath is_empty()

* Remove IbcCoin

* Remove HashedDenom and HashedCoin

* Fix test compilation

* Add comment for send_transfer()

* Functions for determining source chain

* Minor refactoring

* cargo fmt

* Derive serde for PacketCommitment and AcknowledgementCommitment

* docstring and comment

Co-authored-by: Philippe Laferriere <[email protected]>
  • Loading branch information
hu55a1n1 and plafer authored Jun 1, 2022
1 parent dd53c5c commit 6db759e
Show file tree
Hide file tree
Showing 12 changed files with 69 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Complete ICS20 implementation ([#1759](https://github.com/informalsystems/ibc-rs/issues/1759))
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion relayer-cli/src/commands/tx/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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},
Expand Down
3 changes: 1 addition & 2 deletions relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}
Expand All @@ -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"
Expand Down
4 changes: 3 additions & 1 deletion relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions relayer/src/channel/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Version> {
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 {
Expand Down
49 changes: 13 additions & 36 deletions relayer/src/transfer.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<Self, Self::Err> {
Ok(Self(U256::from_str_radix(s, 10)?))
}
}

impl From<u64> for Amount {
fn from(amount: u64) -> Self {
Self(amount.into())
}
}

#[derive(Copy, Clone)]
pub struct TransferTimeout {
pub timeout_height: Height,
Expand Down Expand Up @@ -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,
Expand All @@ -168,10 +148,7 @@ pub fn build_and_send_transfer_messages<SrcChain: ChainHandle, DstChain: ChainHa
packet_dst_chain: &DstChain, // the chain whose account eventually gets credited
opts: &TransferOptions,
) -> Result<Vec<IbcEvent>, 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)?;

Expand All @@ -188,10 +165,10 @@ pub fn build_and_send_transfer_messages<SrcChain: ChainHandle, DstChain: ChainHa
let msg = MsgTransfer {
source_port: opts.packet_src_port_id.clone(),
source_channel: opts.packet_src_channel_id,
token: Some(ibc_proto::cosmos::base::v1beta1::Coin {
token: Coin {
denom: opts.denom.clone(),
amount: opts.amount.to_string(),
}),
},
sender,
receiver,
timeout_height: timeout.timeout_height,
Expand Down
1 change: 0 additions & 1 deletion relayer/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
mod block_on;
pub use block_on::block_on;

pub mod bigint;
pub mod diff;
pub mod iter;
pub mod lock;
Expand Down
8 changes: 0 additions & 8 deletions relayer/src/util/bigint.rs

This file was deleted.

4 changes: 2 additions & 2 deletions tools/integration-test/src/tests/manual/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use core::time::Duration;
use ibc::events::IbcEvent;
use ibc_relayer::config::{types::MaxMsgNum, Config};
use ibc_relayer::transfer::{build_and_send_transfer_messages, Amount, TransferOptions};
use ibc_relayer::transfer::{build_and_send_transfer_messages, TransferOptions};
use ibc_test_framework::prelude::*;

#[test]
Expand Down Expand Up @@ -84,7 +84,7 @@ fn tx_raw_ft_transfer<SrcChain: ChainHandle, DstChain: ChainHandle>(
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,
Expand Down
31 changes: 26 additions & 5 deletions tools/test-framework/src/ibc/denom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -56,10 +58,30 @@ pub fn derive_ibc_denom<ChainA, ChainB>(
channel_id: &TaggedChannelIdRef<ChainB, ChainA>,
denom: &TaggedDenomRef<ChainA>,
) -> Result<TaggedDenom<ChainB>, Error> {
fn derive_denom(
port_id: &PortId,
channel_id: &ChannelId,
denom: &str,
) -> Result<String, Error> {
let transfer_path = format!("{}/{}/{}", port_id, channel_id, denom);
derive_denom_with_path(&transfer_path)
}

/// Derive the transferred token denomination using
/// <https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-001-coin-source-tracing.md>
fn derive_denom_with_path(transfer_path: &str) -> Result<String, Error> {
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),
Expand All @@ -69,8 +91,7 @@ pub fn derive_ibc_denom<ChainA, ChainB>(
}
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,
Expand Down
23 changes: 19 additions & 4 deletions tools/test-framework/src/relayer/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,13 +33,26 @@ pub fn build_transfer_message<SrcChain, DstChain>(
.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,
))
Expand Down

0 comments on commit 6db759e

Please sign in to comment.