From 1833df51985e1752a90e909a0e7377f8b10524e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Laferri=C3=A8re?= Date: Wed, 30 Aug 2023 17:09:04 -0400 Subject: [PATCH] Add borsh derive for `MsgTransfer` (#845) * add borsh derive for `MsgTransfer` * changelog --- .../845-borsh-derive-msgtransfer.md | 2 + .../ibc/src/applications/transfer/amount.rs | 67 ++++++++++++++++++- crates/ibc/src/applications/transfer/coin.rs | 4 ++ crates/ibc/src/applications/transfer/denom.rs | 16 +++++ .../applications/transfer/msgs/transfer.rs | 4 ++ .../ibc/src/applications/transfer/packet.rs | 4 ++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 .changelog/unreleased/improvements/845-borsh-derive-msgtransfer.md diff --git a/.changelog/unreleased/improvements/845-borsh-derive-msgtransfer.md b/.changelog/unreleased/improvements/845-borsh-derive-msgtransfer.md new file mode 100644 index 000000000..1bbcc3ed8 --- /dev/null +++ b/.changelog/unreleased/improvements/845-borsh-derive-msgtransfer.md @@ -0,0 +1,2 @@ +- Add borsh derive for `MsgTransfer` + ([#845](https://github.com/cosmos/ibc-rs/pull/845)) diff --git a/crates/ibc/src/applications/transfer/amount.rs b/crates/ibc/src/applications/transfer/amount.rs index 740e24d0c..9cee50d29 100644 --- a/crates/ibc/src/applications/transfer/amount.rs +++ b/crates/ibc/src/applications/transfer/amount.rs @@ -1,5 +1,6 @@ //! Contains the `Amount` type, which represents amounts of tokens transferred. +use crate::prelude::*; use core::{ops::Deref, str::FromStr}; use derive_more::{Display, From, Into}; @@ -28,6 +29,54 @@ impl parity_scale_codec::WrapperTypeDecode for Amount { #[cfg(feature = "parity-scale-codec")] impl parity_scale_codec::WrapperTypeEncode for Amount {} +#[cfg(feature = "borsh")] +impl borsh::BorshSerialize for Amount { + fn serialize( + &self, + writer: &mut W, + ) -> borsh::maybestd::io::Result<()> { + // Note: a "word" is 8 bytes (i.e. a u64) + let words = self.as_slice(); + let bytes: Vec = words.iter().flat_map(|word| word.to_be_bytes()).collect(); + + writer.write_all(&bytes) + } +} +#[cfg(feature = "borsh")] +impl borsh::BorshDeserialize for Amount { + fn deserialize_reader( + reader: &mut R, + ) -> borsh::maybestd::io::Result { + const NUM_BYTES_IN_U64: usize = 8; + const NUM_WORDS_IN_U256: usize = 4; + + let mut buf = [0; 32]; + let bytes_read = reader.read(&mut buf)?; + if bytes_read != 32 { + return Err(borsh::maybestd::io::Error::new( + borsh::maybestd::io::ErrorKind::InvalidInput, + format!("Expected to read 32 bytes, read {bytes_read}"), + )); + } + + let words: Vec = buf + .chunks_exact(NUM_BYTES_IN_U64) + .map(|word| { + let word: [u8; NUM_BYTES_IN_U64] = word + .try_into() + .expect("exact chunks of 8 bytes are expected to be 8 bytes"); + u64::from_be_bytes(word) + }) + .collect(); + + let four_words: [u64; NUM_WORDS_IN_U256] = words + .try_into() + .expect("U256 is always 4 four words, and we confirmed that we read 32 bytes"); + + Ok(four_words.into()) + } +} + impl Deref for Amount { type Target = [u64; 4]; @@ -83,10 +132,10 @@ where } #[cfg(test)] -#[cfg(feature = "serde")] mod tests { use super::Amount; + #[cfg(feature = "serde")] #[test] fn serde_amount() { let value = Amount::from(42); @@ -96,4 +145,20 @@ mod tests { let de: Amount = serde_json::from_slice(binary.as_ref()).expect("can deserialize"); assert_eq!(de, value); } + + #[cfg(feature = "borsh")] + #[test] + fn borsh_amount() { + use borsh::BorshDeserialize; + + let value = Amount::from(42); + let serialized = borsh::to_vec(&value).unwrap(); + + // Amount is supposed to be a U256 according to the spec, which is 32 bytes + assert_eq!(serialized.len(), 32); + + let value_deserialized = Amount::try_from_slice(&serialized).unwrap(); + + assert_eq!(value, value_deserialized); + } } diff --git a/crates/ibc/src/applications/transfer/coin.rs b/crates/ibc/src/applications/transfer/coin.rs index cad624121..7fd429640 100644 --- a/crates/ibc/src/applications/transfer/coin.rs +++ b/crates/ibc/src/applications/transfer/coin.rs @@ -25,6 +25,10 @@ pub type RawCoin = Coin; feature = "parity-scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode,) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Coin { /// Denomination diff --git a/crates/ibc/src/applications/transfer/denom.rs b/crates/ibc/src/applications/transfer/denom.rs index 963f6b1d7..77ab685ee 100644 --- a/crates/ibc/src/applications/transfer/denom.rs +++ b/crates/ibc/src/applications/transfer/denom.rs @@ -27,6 +27,10 @@ use crate::serializers::serde_string; scale_info::TypeInfo ) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Display)] pub struct BaseDenom(String); @@ -62,6 +66,10 @@ impl FromStr for BaseDenom { scale_info::TypeInfo ) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] @@ -97,6 +105,10 @@ impl Display for TracePrefix { scale_info::TypeInfo ) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, From)] @@ -198,6 +210,10 @@ impl Display for TracePath { scale_info::TypeInfo ) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] #[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. diff --git a/crates/ibc/src/applications/transfer/msgs/transfer.rs b/crates/ibc/src/applications/transfer/msgs/transfer.rs index 0b22755aa..a5d9024c5 100644 --- a/crates/ibc/src/applications/transfer/msgs/transfer.rs +++ b/crates/ibc/src/applications/transfer/msgs/transfer.rs @@ -29,6 +29,10 @@ pub(crate) const TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; feature = "parity-scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode,) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] pub struct MsgTransfer { /// the port on which the packet will be sent pub port_id_on_a: PortId, diff --git a/crates/ibc/src/applications/transfer/packet.rs b/crates/ibc/src/applications/transfer/packet.rs index 4b7ffb4be..fdcede335 100644 --- a/crates/ibc/src/applications/transfer/packet.rs +++ b/crates/ibc/src/applications/transfer/packet.rs @@ -24,6 +24,10 @@ use crate::signer::Signer; feature = "parity-scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode,) )] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshSerialize, borsh::BorshDeserialize) +)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct PacketData { pub token: PrefixedCoin,