Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve MsgTransfer #568

Merged
merged 3 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changelog/unreleased/breaking-changes/567-msgtransfer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Improve MsgTransfer struct
([#567](https://github.com/cosmos/ibc-rs/issues/567))
67 changes: 35 additions & 32 deletions crates/ibc/src/applications/transfer/msgs/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
//! This is the definition of a transfer messages that an application submits to a chain.

use crate::applications::transfer::packet::PacketData;
use crate::prelude::*;

use ibc_proto::cosmos::base::v1beta1::Coin;
use ibc_proto::google::protobuf::Any;
use ibc_proto::ibc::applications::transfer::v1::MsgTransfer as RawMsgTransfer;
use ibc_proto::protobuf::Protobuf;

use crate::applications::transfer::error::TokenTransferError;
use crate::core::ics04_channel::timeout::TimeoutHeight;
use crate::core::ics24_host::identifier::{ChannelId, PortId};
use crate::signer::Signer;
use crate::timestamp::Timestamp;
use crate::tx_msg::Msg;

Expand All @@ -24,17 +23,13 @@ pub const TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer";
/// have to specify the information related to the transfer of the token, and
/// let the library figure out how to build the packet properly.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MsgTransfer<C = Coin> {
pub struct MsgTransfer {
/// the port on which the packet will be sent
pub port_id_on_a: PortId,
/// the channel by which the packet will be sent
pub chan_id_on_a: ChannelId,
/// the tokens to be transferred
pub token: C,
/// the sender address
pub sender: Signer,
/// the recipient address on the destination chain
pub receiver: Signer,
/// token transfer packet data of the packet that will be sent
pub packet_data: PacketData,
/// Timeout height relative to the current block height.
/// The timeout is disabled when set to None.
pub timeout_height_on_b: TimeoutHeight,
Expand Down Expand Up @@ -80,12 +75,18 @@ impl TryFrom<RawMsgTransfer> for MsgTransfer {
validation_error: e,
}
})?,
token: raw_msg.token.ok_or(TokenTransferError::InvalidToken)?,
sender: raw_msg.sender.parse().map_err(TokenTransferError::Signer)?,
receiver: raw_msg
.receiver
.parse()
.map_err(TokenTransferError::Signer)?,
packet_data: PacketData {
token: raw_msg
.token
.ok_or(TokenTransferError::InvalidToken)?
.try_into()
.map_err(|_| TokenTransferError::InvalidToken)?,
sender: raw_msg.sender.parse().map_err(TokenTransferError::Signer)?,
receiver: raw_msg
.receiver
.parse()
.map_err(TokenTransferError::Signer)?,
},
timeout_height_on_b,
timeout_timestamp_on_b,
})
Expand All @@ -97,9 +98,9 @@ impl From<MsgTransfer> for RawMsgTransfer {
RawMsgTransfer {
source_port: domain_msg.port_id_on_a.to_string(),
source_channel: domain_msg.chan_id_on_a.to_string(),
token: Some(domain_msg.token),
sender: domain_msg.sender.to_string(),
receiver: domain_msg.receiver.to_string(),
token: Some(domain_msg.packet_data.token.into()),
sender: domain_msg.packet_data.sender.to_string(),
receiver: domain_msg.packet_data.receiver.to_string(),
timeout_height: domain_msg.timeout_height_on_b.into(),
timeout_timestamp: domain_msg.timeout_timestamp_on_b.nanoseconds(),
}
Expand Down Expand Up @@ -136,7 +137,7 @@ pub mod test_util {
use crate::core::ics04_channel::timeout::TimeoutHeight;
use crate::signer::Signer;
use crate::{
applications::transfer::{BaseCoin, PrefixedCoin},
applications::transfer::BaseCoin,
core::ics24_host::identifier::{ChannelId, PortId},
test_utils::get_dummy_bech32_account,
timestamp::Timestamp,
Expand All @@ -147,35 +148,37 @@ pub mod test_util {
pub fn get_dummy_msg_transfer(
timeout_height: TimeoutHeight,
timeout_timestamp: Option<Timestamp>,
) -> MsgTransfer<PrefixedCoin> {
) -> MsgTransfer {
let address: Signer = get_dummy_bech32_account().as_str().parse().unwrap();
MsgTransfer {
port_id_on_a: PortId::default(),
chan_id_on_a: ChannelId::default(),
token: BaseCoin {
denom: "uatom".parse().unwrap(),
amount: U256::from(10).into(),
}
.into(),
sender: address.clone(),
receiver: address,
packet_data: PacketData {
token: BaseCoin {
denom: "uatom".parse().unwrap(),
amount: U256::from(10).into(),
}
.into(),
sender: address.clone(),
receiver: address,
},
timeout_timestamp_on_b: timeout_timestamp
.unwrap_or_else(|| Timestamp::now().add(Duration::from_secs(10)).unwrap()),
timeout_height_on_b: timeout_height,
}
}

pub fn get_dummy_transfer_packet(msg: MsgTransfer<PrefixedCoin>, sequence: Sequence) -> Packet {
pub fn get_dummy_transfer_packet(msg: MsgTransfer, sequence: Sequence) -> Packet {
let coin = Coin {
denom: msg.token.denom.clone(),
amount: msg.token.amount,
denom: msg.packet_data.token.denom.clone(),
amount: msg.packet_data.token.amount,
};

let data = {
let data = PacketData {
token: coin,
sender: msg.sender.clone(),
receiver: msg.receiver.clone(),
sender: msg.packet_data.sender.clone(),
receiver: msg.packet_data.receiver.clone(),
};
serde_json::to_vec(&data).expect("PacketData's infallible Serialize impl failed")
};
Expand Down
127 changes: 54 additions & 73 deletions crates/ibc/src/applications/transfer/relay/send_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use crate::applications::transfer::context::{
};
use crate::applications::transfer::error::TokenTransferError;
use crate::applications::transfer::events::TransferEvent;
use crate::applications::transfer::is_sender_chain_source;
use crate::applications::transfer::msgs::transfer::MsgTransfer;
use crate::applications::transfer::packet::PacketData;
use crate::applications::transfer::{is_sender_chain_source, Coin, PrefixedCoin};
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};
Expand All @@ -15,21 +14,16 @@ use crate::prelude::*;
/// This function handles the transfer sending logic.
/// If this method returns an error, the runtime is expected to rollback all state modifications to
/// the `Ctx` caused by all messages from the transaction that this `msg` is a part of.
pub fn send_transfer<C, Ctx>(ctx_a: &mut Ctx, msg: MsgTransfer<C>) -> Result<(), TokenTransferError>
pub fn send_transfer<Ctx>(ctx_a: &mut Ctx, msg: MsgTransfer) -> Result<(), TokenTransferError>
where
C: TryInto<PrefixedCoin> + Clone,
Ctx: TokenTransferExecutionContext,
{
send_transfer_validate(ctx_a, msg.clone())?;
send_transfer_execute(ctx_a, msg)
}

pub fn send_transfer_validate<C, Ctx>(
ctx_a: &Ctx,
msg: MsgTransfer<C>,
) -> Result<(), TokenTransferError>
pub fn send_transfer_validate<Ctx>(ctx_a: &Ctx, msg: MsgTransfer) -> Result<(), TokenTransferError>
where
C: TryInto<PrefixedCoin>,
Ctx: TokenTransferValidationContext,
{
ctx_a.can_send_coins()?;
Expand All @@ -54,60 +48,52 @@ where
.get_next_sequence_send(&seq_send_path_on_a)
.map_err(TokenTransferError::ContextError)?;

let token = msg
.token
.try_into()
.map_err(|_| TokenTransferError::InvalidToken)?;
let denom = token.denom.clone();
let coin = Coin {
denom: denom.clone(),
amount: token.amount,
};
let token = &msg.packet_data.token;

let sender: Ctx::AccountId = msg
.packet_data
.sender
.clone()
.try_into()
.map_err(|_| TokenTransferError::ParseAccountFailure)?;

if is_sender_chain_source(msg.port_id_on_a.clone(), msg.chan_id_on_a.clone(), &denom) {
if is_sender_chain_source(
msg.port_id_on_a.clone(),
msg.chan_id_on_a.clone(),
&token.denom,
) {
let escrow_address = ctx_a.get_escrow_account(&msg.port_id_on_a, &msg.chan_id_on_a)?;
ctx_a.send_coins_validate(&sender, &escrow_address, &coin)?;
ctx_a.send_coins_validate(&sender, &escrow_address, token)?;
} else {
ctx_a.burn_coins_validate(&sender, &coin)?;
ctx_a.burn_coins_validate(&sender, token)?;
}

let data = {
let data = PacketData {
token: coin,
sender: msg.sender.clone(),
receiver: msg.receiver.clone(),
};
serde_json::to_vec(&data).expect("PacketData's infallible Serialize impl failed")
};

let packet = Packet {
seq_on_a: sequence,
port_id_on_a: msg.port_id_on_a,
chan_id_on_a: msg.chan_id_on_a,
port_id_on_b,
chan_id_on_b,
data,
timeout_height_on_b: msg.timeout_height_on_b,
timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
let packet = {
let data = serde_json::to_vec(&msg.packet_data)
.expect("PacketData's infallible Serialize impl failed");

Packet {
seq_on_a: sequence,
port_id_on_a: msg.port_id_on_a,
chan_id_on_a: msg.chan_id_on_a,
port_id_on_b,
chan_id_on_b,
data,
timeout_height_on_b: msg.timeout_height_on_b,
timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
}
};

send_packet_validate(ctx_a, &packet).map_err(TokenTransferError::ContextError)?;

Ok(())
}

pub fn send_transfer_execute<C, Ctx>(
pub fn send_transfer_execute<Ctx>(
ctx_a: &mut Ctx,
msg: MsgTransfer<C>,
msg: MsgTransfer,
) -> Result<(), TokenTransferError>
where
C: TryInto<PrefixedCoin>,
Ctx: TokenTransferExecutionContext,
{
let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
Expand All @@ -131,60 +117,55 @@ where
.get_next_sequence_send(&seq_send_path_on_a)
.map_err(TokenTransferError::ContextError)?;

let token = msg
.token
.try_into()
.map_err(|_| TokenTransferError::InvalidToken)?;
let denom = token.denom.clone();
let coin = Coin {
denom: denom.clone(),
amount: token.amount,
};
let token = &msg.packet_data.token;

let sender = msg
.packet_data
.sender
.clone()
.try_into()
.map_err(|_| TokenTransferError::ParseAccountFailure)?;

if is_sender_chain_source(msg.port_id_on_a.clone(), msg.chan_id_on_a.clone(), &denom) {
if is_sender_chain_source(
msg.port_id_on_a.clone(),
msg.chan_id_on_a.clone(),
&token.denom,
) {
let escrow_address = ctx_a.get_escrow_account(&msg.port_id_on_a, &msg.chan_id_on_a)?;
ctx_a.send_coins_execute(&sender, &escrow_address, &coin)?;
ctx_a.send_coins_execute(&sender, &escrow_address, token)?;
} else {
ctx_a.burn_coins_execute(&sender, &coin)?;
ctx_a.burn_coins_execute(&sender, token)?;
}

let data = {
let data = PacketData {
token: coin,
sender: msg.sender.clone(),
receiver: msg.receiver.clone(),
let packet = {
let data = {
serde_json::to_vec(&msg.packet_data)
.expect("PacketData's infallible Serialize impl failed")
};
serde_json::to_vec(&data).expect("PacketData's infallible Serialize impl failed")
};

let packet = Packet {
seq_on_a: sequence,
port_id_on_a: msg.port_id_on_a,
chan_id_on_a: msg.chan_id_on_a,
port_id_on_b: port_on_b,
chan_id_on_b: chan_on_b,
data,
timeout_height_on_b: msg.timeout_height_on_b,
timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
Packet {
seq_on_a: sequence,
port_id_on_a: msg.port_id_on_a,
chan_id_on_a: msg.chan_id_on_a,
port_id_on_b: port_on_b,
chan_id_on_b: chan_on_b,
data,
timeout_height_on_b: msg.timeout_height_on_b,
timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
}
};

send_packet_execute(ctx_a, packet).map_err(TokenTransferError::ContextError)?;

{
ctx_a.log_message(format!(
"IBC fungible token transfer: {} --({})--> {}",
msg.sender, token, msg.receiver
msg.packet_data.sender, token, msg.packet_data.receiver
));

let transfer_event = TransferEvent {
sender: msg.sender,
receiver: msg.receiver,
sender: msg.packet_data.sender,
receiver: msg.packet_data.receiver,
};
ctx_a.emit_ibc_event(ModuleEvent::from(transfer_event).into());
}
Expand Down
14 changes: 7 additions & 7 deletions crates/ibc/src/core/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ mod tests {
#[derive(Clone, Debug)]
enum TestMsg {
Ics26(MsgEnvelope),
Ics20(MsgTransfer<PrefixedCoin>),
Ics20(MsgTransfer),
}

impl From<MsgEnvelope> for TestMsg {
Expand All @@ -113,8 +113,8 @@ mod tests {
}
}

impl From<MsgTransfer<PrefixedCoin>> for TestMsg {
fn from(msg: MsgTransfer<PrefixedCoin>) -> Self {
impl From<MsgTransfer> for TestMsg {
fn from(msg: MsgTransfer) -> Self {
Self::Ics20(msg)
}
}
Expand Down Expand Up @@ -207,15 +207,15 @@ mod tests {
msg_to_on_close.packet.timeout_height_on_b = msg_transfer_two.timeout_height_on_b;
msg_to_on_close.packet.timeout_timestamp_on_b = msg_transfer_two.timeout_timestamp_on_b;

let denom = msg_transfer_two.token.denom.clone();
let denom = msg_transfer_two.packet_data.token.denom.clone();
let packet_data = {
let data = PacketData {
token: PrefixedCoin {
denom,
amount: msg_transfer_two.token.amount,
amount: msg_transfer_two.packet_data.token.amount,
},
sender: msg_transfer_two.sender.clone(),
receiver: msg_transfer_two.receiver.clone(),
sender: msg_transfer_two.packet_data.sender.clone(),
receiver: msg_transfer_two.packet_data.receiver.clone(),
};
serde_json::to_vec(&data).expect("PacketData's infallible Serialize impl failed")
};
Expand Down