From 099d9fa7c6359e228479c746090241225ccc10b5 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 May 2022 14:50:52 +0200 Subject: [PATCH] Basic transfer with pay packet is working --- Cargo.lock | 1 + modules/src/applications/ics29_fee/mod.rs | 1 + .../src/applications/ics29_fee/msgs/mod.rs | 3 + .../applications/ics29_fee/msgs/pay_packet.rs | 21 ++++ modules/src/applications/mod.rs | 1 + proto/src/lib.rs | 5 + relayer/src/transfer.rs | 24 +++-- .../src/tests/fee_middleware.rs | 88 ++++++++++++++++- tools/integration-test/src/tests/transfer.rs | 1 - tools/test-framework/Cargo.toml | 1 + tools/test-framework/src/prelude.rs | 1 + tools/test-framework/src/relayer/fee.rs | 96 +++++++++++++++++++ tools/test-framework/src/relayer/mod.rs | 1 + tools/test-framework/src/relayer/transfer.rs | 42 +++++--- 14 files changed, 254 insertions(+), 32 deletions(-) create mode 100644 modules/src/applications/ics29_fee/mod.rs create mode 100644 modules/src/applications/ics29_fee/msgs/mod.rs create mode 100644 modules/src/applications/ics29_fee/msgs/pay_packet.rs create mode 100644 tools/test-framework/src/relayer/fee.rs diff --git a/Cargo.lock b/Cargo.lock index 2dafc1ce04..bf7a429468 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1640,6 +1640,7 @@ dependencies = [ "ibc-relayer", "ibc-relayer-cli", "itertools", + "prost", "rand 0.8.5", "semver", "serde", diff --git a/modules/src/applications/ics29_fee/mod.rs b/modules/src/applications/ics29_fee/mod.rs new file mode 100644 index 0000000000..159c4b7c03 --- /dev/null +++ b/modules/src/applications/ics29_fee/mod.rs @@ -0,0 +1 @@ +mod msgs; diff --git a/modules/src/applications/ics29_fee/msgs/mod.rs b/modules/src/applications/ics29_fee/msgs/mod.rs new file mode 100644 index 0000000000..f1d22a0b71 --- /dev/null +++ b/modules/src/applications/ics29_fee/msgs/mod.rs @@ -0,0 +1,3 @@ +mod pay_packet; + +pub use pay_packet::*; diff --git a/modules/src/applications/ics29_fee/msgs/pay_packet.rs b/modules/src/applications/ics29_fee/msgs/pay_packet.rs new file mode 100644 index 0000000000..7c081dec75 --- /dev/null +++ b/modules/src/applications/ics29_fee/msgs/pay_packet.rs @@ -0,0 +1,21 @@ +use ibc_proto::ibc::applications::fee::v1::MsgPayPacketFee; + +use crate::prelude::*; +use crate::tx_msg::Msg; + +pub const TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; + +pub enum Error {} + +impl Msg for MsgPayPacketFee { + type ValidationError = Error; + type Raw = MsgPayPacketFee; + + fn route(&self) -> String { + crate::keys::ROUTER_KEY.to_string() + } + + fn type_url(&self) -> String { + TYPE_URL.to_string() + } +} diff --git a/modules/src/applications/mod.rs b/modules/src/applications/mod.rs index 9e6905bb39..5e8244dbfb 100644 --- a/modules/src/applications/mod.rs +++ b/modules/src/applications/mod.rs @@ -1,3 +1,4 @@ //! Various packet encoding semantics which underpin the various types of transactions. pub mod ics20_fungible_token_transfer; +pub mod ics29_fee; diff --git a/proto/src/lib.rs b/proto/src/lib.rs index d07b717776..dc78b49391 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -140,6 +140,11 @@ pub mod ibc { include_proto!("ibc.applications.transfer.v1.rs"); } } + pub mod fee { + pub mod v1 { + include_proto!("ibc.applications.fee.v1.rs"); + } + } pub mod interchain_accounts { pub mod v1 { include_proto!("ibc.applications.interchain_accounts.v1.rs"); diff --git a/relayer/src/transfer.rs b/relayer/src/transfer.rs index e44b04cda9..3c0a3b0b1e 100644 --- a/relayer/src/transfer.rs +++ b/relayer/src/transfer.rs @@ -10,6 +10,7 @@ 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; @@ -150,7 +151,7 @@ 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: Some(Coin { denom, amount: amount.to_string(), }), @@ -185,21 +186,18 @@ pub fn build_and_send_transfer_messages Result<(), Error> { @@ -19,9 +20,90 @@ impl BinaryChannelTest for ChannelWithFeeTest { &self, _config: &TestConfig, _relayer: RelayerDriver, - _chains: ConnectedChains, - _channel: ConnectedChannel, + chains: ConnectedChains, + channel: ConnectedChannel, ) -> Result<(), Error> { - Ok(()) + let chain_driver_a = chains.node_a.chain_driver(); + let chain_driver_b = chains.node_b.chain_driver(); + + let denom_a = chains.node_a.denom(); + let tx_config_a = chain_driver_a.tx_config(); + + let port_a = &channel.port_a.as_ref(); + let channel_id_a = &channel.channel_id_a.as_ref(); + + let wallets_a = chains.node_a.wallets(); + let wallets_b = chains.node_b.wallets(); + + let user_a1 = wallets_a.user1(); + let user_b1 = wallets_b.user1(); + + let relayer_a = wallets_a.relayer(); + + let balance_a = chain_driver_a.query_balance(&user_a1.address(), &denom_a)?; + + let relayer_balance_a = chain_driver_a.query_balance(&relayer_a.address(), &denom_a)?; + + let send_amount = 1000; + let receive_fee = 300; + let ack_fee = 200; + let timeout_fee = 100; + + let total_sent = send_amount + receive_fee + ack_fee + timeout_fee; + + chain_driver_a + .value() + .runtime + .block_on(ibc_token_transfer_with_fee( + &tx_config_a, + port_a, + channel_id_a, + &user_a1, + &user_b1.address(), + &denom_a, + send_amount, + receive_fee, + ack_fee, + timeout_fee, + ))?; + + let denom_b = derive_ibc_denom( + &channel.port_b.as_ref(), + &channel.channel_id_b.as_ref(), + &denom_a, + )?; + + info!( + "User A's balance after transfer: {}", + balance_a - total_sent + ); + + chain_driver_a.assert_eventual_wallet_amount( + &user_a1.address(), + balance_a - total_sent, + &denom_a, + )?; + + chain_driver_b.assert_eventual_wallet_amount( + &user_b1.address(), + send_amount, + &denom_b.as_ref(), + )?; + + // // receive fee and timeout fee should be refunded, + // // as there is no counterparty address registered. + // chain_driver_a.assert_eventual_wallet_amount( + // &user_a1.address(), + // balance_a - send_amount - ack_fee, + // &denom_a, + // )?; + + // chain_driver_a.assert_eventual_wallet_amount( + // &relayer_a.address(), + // relayer_balance_a + ack_fee, + // &denom_a, + // )?; + + suspend() } } diff --git a/tools/integration-test/src/tests/transfer.rs b/tools/integration-test/src/tests/transfer.rs index a5a76750c8..afac3170d6 100644 --- a/tools/integration-test/src/tests/transfer.rs +++ b/tools/integration-test/src/tests/transfer.rs @@ -1,4 +1,3 @@ -use ibc_test_framework::ibc::denom::derive_ibc_denom; use ibc_test_framework::prelude::*; use ibc_test_framework::util::random::random_u64_range; diff --git a/tools/test-framework/Cargo.toml b/tools/test-framework/Cargo.toml index a1729b95f3..cec69ed6b5 100644 --- a/tools/test-framework/Cargo.toml +++ b/tools/test-framework/Cargo.toml @@ -41,3 +41,4 @@ sha2 = "0.10.2" crossbeam-channel = "0.5.4" semver = "1.0.7" flex-error = "0.4.4" +prost = { version = "0.10" } diff --git a/tools/test-framework/src/prelude.rs b/tools/test-framework/src/prelude.rs index 624ed806a4..4abe634ef4 100644 --- a/tools/test-framework/src/prelude.rs +++ b/tools/test-framework/src/prelude.rs @@ -45,6 +45,7 @@ pub use crate::framework::nary::connection::{ pub use crate::framework::nary::node::{run_nary_node_test, NaryNodeTest, RunNaryNodeTest}; pub use crate::framework::overrides::TestOverrides; pub use crate::framework::supervisor::RunWithSupervisor; +pub use crate::ibc::denom::derive_ibc_denom; pub use crate::ibc::denom::Denom; pub use crate::relayer::channel::TaggedChannelEndExt; pub use crate::relayer::connection::{TaggedConnectionEndExt, TaggedConnectionExt}; diff --git a/tools/test-framework/src/relayer/fee.rs b/tools/test-framework/src/relayer/fee.rs new file mode 100644 index 0000000000..5a6a9be608 --- /dev/null +++ b/tools/test-framework/src/relayer/fee.rs @@ -0,0 +1,96 @@ +use ibc_proto::cosmos::base::v1beta1::Coin; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::applications::fee::v1::{Fee, MsgPayPacketFee}; +use ibc_relayer::chain::cosmos::types::config::TxConfig; +use prost::{EncodeError, Message}; + +use crate::error::{handle_generic_error, Error}; +use crate::ibc::denom::Denom; +use crate::relayer::transfer::build_transfer_message; +use crate::relayer::tx::simple_send_tx; +use crate::types::id::{TaggedChannelIdRef, TaggedPortIdRef}; +use crate::types::tagged::MonoTagged; +use crate::types::wallet::TaggedWallet; +use crate::types::wallet::{Wallet, WalletAddress}; + +fn encode_message(message: &M) -> Result, EncodeError> { + let mut buf = Vec::new(); + Message::encode(message, &mut buf)?; + Ok(buf) +} + +pub fn build_pay_packet_message( + port_id: &TaggedPortIdRef, + channel_id: &TaggedChannelIdRef, + payer: &MonoTagged, + denom: &MonoTagged, + receive_fee: u64, + ack_fee: u64, + timeout_fee: u64, +) -> Result { + const TYPE_URL: &str = "/ibc.applications.fee.v1.MsgPayPacketFee"; + + let denom_str = denom.value().to_string(); + + let fee = Fee { + recv_fee: vec![Coin { + denom: denom_str.clone(), + amount: receive_fee.to_string(), + }], + ack_fee: vec![Coin { + denom: denom_str.clone(), + amount: ack_fee.to_string(), + }], + timeout_fee: vec![Coin { + denom: denom_str, + amount: timeout_fee.to_string(), + }], + }; + + let message = MsgPayPacketFee { + fee: Some(fee), + source_port_id: port_id.value().to_string(), + source_channel_id: channel_id.value().to_string(), + signer: payer.value().0.clone(), + relayers: Vec::new(), + }; + + let encoded = encode_message(&message).map_err(handle_generic_error)?; + + Ok(Any { + type_url: TYPE_URL.to_string(), + value: encoded, + }) +} + +pub async fn ibc_token_transfer_with_fee( + tx_config: &MonoTagged, + port_id: &TaggedPortIdRef<'_, SrcChain, DstChain>, + channel_id: &TaggedChannelIdRef<'_, SrcChain, DstChain>, + sender: &MonoTagged, + recipient: &MonoTagged, + denom: &MonoTagged, + send_amount: u64, + receive_fee: u64, + ack_fee: u64, + timeout_fee: u64, +) -> Result<(), Error> { + let transfer_message = + build_transfer_message(port_id, channel_id, sender, recipient, denom, send_amount)?; + + let pay_message = build_pay_packet_message( + port_id, + channel_id, + &sender.address(), + denom, + receive_fee, + ack_fee, + timeout_fee, + )?; + + let messages = vec![transfer_message, pay_message]; + + simple_send_tx(tx_config.value(), &sender.value().key, messages).await?; + + Ok(()) +} diff --git a/tools/test-framework/src/relayer/mod.rs b/tools/test-framework/src/relayer/mod.rs index 44a848d41e..0f90d7b1be 100644 --- a/tools/test-framework/src/relayer/mod.rs +++ b/tools/test-framework/src/relayer/mod.rs @@ -22,6 +22,7 @@ pub mod chain; pub mod channel; pub mod connection; pub mod driver; +pub mod fee; pub mod foreign_client; pub mod refresh; pub mod transfer; diff --git a/tools/test-framework/src/relayer/transfer.rs b/tools/test-framework/src/relayer/transfer.rs index 4e0002b521..d4ac86aff0 100644 --- a/tools/test-framework/src/relayer/transfer.rs +++ b/tools/test-framework/src/relayer/transfer.rs @@ -8,8 +8,9 @@ use core::time::Duration; use ibc::signer::Signer; 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; +use ibc_relayer::transfer::build_transfer_message as raw_build_transfer_message; use crate::error::{handle_generic_error, Error}; use crate::ibc::denom::Denom; @@ -18,6 +19,30 @@ use crate::types::id::{TaggedChannelIdRef, TaggedPortIdRef}; use crate::types::tagged::*; use crate::types::wallet::{Wallet, WalletAddress}; +pub fn build_transfer_message( + port_id: &TaggedPortIdRef<'_, SrcChain, DstChain>, + channel_id: &TaggedChannelIdRef<'_, SrcChain, DstChain>, + sender: &MonoTagged, + recipient: &MonoTagged, + denom: &MonoTagged, + amount: u64, +) -> Result { + let timeout_timestamp = Timestamp::now() + .add(Duration::from_secs(60)) + .map_err(handle_generic_error)?; + + 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()), + Height::zero(), + timeout_timestamp, + )) +} + /** Perform a simplified version of IBC token transfer for testing purpose. @@ -44,20 +69,7 @@ pub async fn ibc_token_transfer( denom: &MonoTagged, amount: u64, ) -> Result<(), Error> { - let timeout_timestamp = Timestamp::now() - .add(Duration::from_secs(60)) - .map_err(handle_generic_error)?; - - let message = 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()), - Height::zero(), - timeout_timestamp, - ); + let message = build_transfer_message(port_id, channel_id, sender, recipient, denom, amount)?; simple_send_tx(tx_config.value(), &sender.value().key, vec![message]).await?;