From 45652ab38d95046222a86f44e2a1805ef12d34b7 Mon Sep 17 00:00:00 2001 From: Erwan Or Date: Fri, 22 Nov 2024 11:45:04 -0500 Subject: [PATCH] UIP-5: Outbound PFM support (#4940) Implementation of the candidate UIP-5: outbound PFM support (see https://forum.penumbra.zone/t/pre-uip-outbound-packet-forwarding-middleware-support/121) Should be reviewed for correctness and adherence to the spec. Note that this only includes the required protocol changes to allow clients to use the new memo field (a prerequisite for PFM support), and does not implement outbound packet forwarding directly (that is to be done in the client). By @avahowell cherry-picked from https://github.com/penumbra-zone/penumbra/pull/4923 Co-authored-by: Ava Howell (cherry picked from commit 5a66403e91b82aa1234d8dd12d72ab08e5e2f416) --- crates/bin/pcli/src/command/tx.rs | 1 + .../app/tests/common/ibc_tests/relayer.rs | 1 + .../shielded-pool/src/ics20_withdrawal.rs | 9 ++++++++- .../src/gen/penumbra.core.component.ibc.v1.rs | 5 +++++ .../penumbra.core.component.ibc.v1.serde.rs | 18 ++++++++++++++++++ .../proto/src/gen/proto_descriptor.bin.no_lfs | Bin 648288 -> 648603 bytes .../penumbra/core/component/ibc/v1/ibc.proto | 5 +++++ 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/crates/bin/pcli/src/command/tx.rs b/crates/bin/pcli/src/command/tx.rs index 9289d1fb5c..5f9be82c63 100644 --- a/crates/bin/pcli/src/command/tx.rs +++ b/crates/bin/pcli/src/command/tx.rs @@ -1133,6 +1133,7 @@ impl TxCmd { // TODO: impl From for ChannelId source_channel: ChannelId::from_str(format!("channel-{}", channel).as_ref())?, use_compat_address: *use_compat_address, + ics20_memo: "".to_string(), }; let plan = Planner::new(OsRng) diff --git a/crates/core/app/tests/common/ibc_tests/relayer.rs b/crates/core/app/tests/common/ibc_tests/relayer.rs index 488e68dcca..2998d40b64 100644 --- a/crates/core/app/tests/common/ibc_tests/relayer.rs +++ b/crates/core/app/tests/common/ibc_tests/relayer.rs @@ -1485,6 +1485,7 @@ impl MockRelayer { source_channel: ChannelId::from_str("channel-0")?, // Penumbra <-> Penumbra so false use_compat_address: false, + ics20_memo: "".to_string(), }; // There will need to be `Spend` and `Output` actions // within the transaction in order for it to balance diff --git a/crates/core/component/shielded-pool/src/ics20_withdrawal.rs b/crates/core/component/shielded-pool/src/ics20_withdrawal.rs index c452d5d703..a4881edd3c 100644 --- a/crates/core/component/shielded-pool/src/ics20_withdrawal.rs +++ b/crates/core/component/shielded-pool/src/ics20_withdrawal.rs @@ -41,6 +41,11 @@ pub struct Ics20Withdrawal { // Whether to use a "compat" (bech32, non-m) address for the return address in the withdrawal, // for compatability with chains that expect to be able to parse the return address as bech32. pub use_compat_address: bool, + + // Arbitrary string data to be included in the `memo` field + // of the ICS-20 FungibleTokenPacketData for this withdrawal. + // Commonly used for packet forwarding support, or other protocols that may support usage of the memo field. + pub ics20_memo: String, } #[cfg(feature = "component")] @@ -118,6 +123,7 @@ impl From for pb::Ics20Withdrawal { timeout_time: w.timeout_time, source_channel: w.source_channel.to_string(), use_compat_address: w.use_compat_address, + ics20_memo: w.ics20_memo.to_string(), } } } @@ -148,6 +154,7 @@ impl TryFrom for Ics20Withdrawal { timeout_time: s.timeout_time, source_channel: ChannelId::from_str(&s.source_channel)?, use_compat_address: s.use_compat_address, + ics20_memo: s.ics20_memo, }) } } @@ -164,7 +171,7 @@ impl From for pb::FungibleTokenPacketData { denom: w.denom.to_string(), receiver: w.destination_chain_address, sender: return_address, - memo: "".to_string(), + memo: w.ics20_memo, } } } diff --git a/crates/proto/src/gen/penumbra.core.component.ibc.v1.rs b/crates/proto/src/gen/penumbra.core.component.ibc.v1.rs index 320e259a47..2eb7b4651d 100644 --- a/crates/proto/src/gen/penumbra.core.component.ibc.v1.rs +++ b/crates/proto/src/gen/penumbra.core.component.ibc.v1.rs @@ -73,6 +73,11 @@ pub struct Ics20Withdrawal { /// for compatability with chains that expect to be able to parse the return address as bech32. #[prost(bool, tag = "8")] pub use_compat_address: bool, + /// Arbitrary string data to be included in the `memo` field + /// of the ICS-20 FungibleTokenPacketData for this withdrawal. + /// Commonly used for packet forwarding support, or other protocols that may support usage of the memo field. + #[prost(string, tag = "9")] + pub ics20_memo: ::prost::alloc::string::String, } impl ::prost::Name for Ics20Withdrawal { const NAME: &'static str = "Ics20Withdrawal"; diff --git a/crates/proto/src/gen/penumbra.core.component.ibc.v1.serde.rs b/crates/proto/src/gen/penumbra.core.component.ibc.v1.serde.rs index b4bcbf3624..58fd339e65 100644 --- a/crates/proto/src/gen/penumbra.core.component.ibc.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.ibc.v1.serde.rs @@ -1057,6 +1057,9 @@ impl serde::Serialize for Ics20Withdrawal { if self.use_compat_address { len += 1; } + if !self.ics20_memo.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.ibc.v1.Ics20Withdrawal", len)?; if let Some(v) = self.amount.as_ref() { struct_ser.serialize_field("amount", v)?; @@ -1083,6 +1086,9 @@ impl serde::Serialize for Ics20Withdrawal { if self.use_compat_address { struct_ser.serialize_field("useCompatAddress", &self.use_compat_address)?; } + if !self.ics20_memo.is_empty() { + struct_ser.serialize_field("ics20Memo", &self.ics20_memo)?; + } struct_ser.end() } } @@ -1107,6 +1113,8 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { "sourceChannel", "use_compat_address", "useCompatAddress", + "ics20_memo", + "ics20Memo", ]; #[allow(clippy::enum_variant_names)] @@ -1119,6 +1127,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { TimeoutTime, SourceChannel, UseCompatAddress, + Ics20Memo, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1149,6 +1158,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { "timeoutTime" | "timeout_time" => Ok(GeneratedField::TimeoutTime), "sourceChannel" | "source_channel" => Ok(GeneratedField::SourceChannel), "useCompatAddress" | "use_compat_address" => Ok(GeneratedField::UseCompatAddress), + "ics20Memo" | "ics20_memo" => Ok(GeneratedField::Ics20Memo), _ => Ok(GeneratedField::__SkipField__), } } @@ -1176,6 +1186,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { let mut timeout_time__ = None; let mut source_channel__ = None; let mut use_compat_address__ = None; + let mut ics20_memo__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Amount => { @@ -1228,6 +1239,12 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { } use_compat_address__ = Some(map_.next_value()?); } + GeneratedField::Ics20Memo => { + if ics20_memo__.is_some() { + return Err(serde::de::Error::duplicate_field("ics20Memo")); + } + ics20_memo__ = Some(map_.next_value()?); + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -1242,6 +1259,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { timeout_time: timeout_time__.unwrap_or_default(), source_channel: source_channel__.unwrap_or_default(), use_compat_address: use_compat_address__.unwrap_or_default(), + ics20_memo: ics20_memo__.unwrap_or_default(), }) } } diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index d0511fd1ddedf5cf99077bb3b1f61779d626627a..047ad2e205b0d73bdc1dcd235f2640ed77b5df5a 100644 GIT binary patch delta 1307 zcmX|A&uZ)m*h|SgQ(xz}_19WK2+9ehx! zUtL}bXCKkRXwUNz>kZ{`FWB$+&aub9pp`m*N~c?PqSFK93#0qpA*% z4i7y)_)a40p%ys47kF*2KZK(b$bo|nBNBX^uA9_XJw+{D8*zSyr_^{UHJ_Cr6UaWB zAPSV9m1-oP1HStXu1?!F_cor8#i6Q6;nbZ;h*sa8NKp?KzBFVvSC70(_$Z-G2 zt%ijyx;|PZxtL9%#ZT0xt zfIzIk#d2(kwLV6~n= zFmK3gHrsgBn3Y!gjyVN@6t1WD0=3})9ervJBZ)rZZ&yZlr+l@3PG%wI ztMzkASGYvWTrk(zH;d;~%VF(%niCvk7dtTvJFeR8hL4+6&UDE)+5j;yx)a3&!LIwu Wt0u)^I4PVoP6o#sziQh5{rVMPR>Ri- delta 1017 zcmX|<&uUXa6vpqJGxy&_G)|k8rcF%?No+*4f*@@YX=0*DX-d-?Z$gBUzJ$Ao7F@Uy z1n zTrEGVagM@G+pMB9P_Cr5p+mvh&K8pzc_fyxC8ghM^PZa3=!n!Eprvk|b*7NA{ z85OS%@Jq*7u0WB)Xd$FQC1kEn;SP+@i6fF6b9C)TN*TP$=M&FJ6`Wu$=l1Y58M soKWpfqIW|o