From f757a7083ebe3839e4495519c8207c0e3cbbafc3 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) ## Describe your changes 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 --- crates/bin/pcli/src/command/tx.rs | 1 + .../src/gen/proto_descriptor.bin.no_lfs | Bin 82425 -> 82425 bytes .../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 627545 -> 627860 bytes .../penumbra/core/component/ibc/v1/ibc.proto | 5 +++++ 8 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/cnidarium/src/gen/proto_descriptor.bin.no_lfs b/crates/cnidarium/src/gen/proto_descriptor.bin.no_lfs index 09181ca63e0d92d8142235c3003f76694fd27e4c..762d818a29e60203069925bec510cfda0269d7ba 100644 GIT binary patch delta 75 zcmey_%=)vLbwl@3=EBm7$$d-JLG6AjkjwRe3B0OCX-oB#j- delta 75 zcmey_%=)vLbwl@3=G^@J$$d-JLG6AjkjwRe3B0MT|JDgXcg 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 70880248d1fa01386236afd3ab38f2d09499eaa5..33adc2384712dd0c85de944fc4df6b052ce75997 100644 GIT binary patch delta 1342 zcmX|>-D?v|7{+&JHk;k4R$XoSVXLFiw2dFsdUeoh(;O(K)@mb-P-@&Z+ccPT>t;ib zR{^~c5kyefzo3F%ox?#QXb(s5QvU!iy;i7KqNw0Av%9?*y3ae$^P6{e-}J-N^pj`l zzaMPUTg|^7KinJ=fB${Cxt)@Q=dXtjqHvON&fRst>aN)}UUr*HHHVA9=hq9ucQ?Ov zFTbU>^IIhG2iYEJU-?S@SJe+~>l=Bc9<~M;t1dUjMoV?4?)I2m9X97oS(!nh{r)@A zsrj~_>6WG_tHD1#>Vfu`pX4;v$JM@Zo(-~3)UFrj=qK&kw#qb(5W}KpNl)KLm1n)B zs_)s}ZQk&`>RN@DZQtg;%aAmUKlHRTNW4V}(F=FddykEILX0qb-7SCU?#etVuKOfgm>7cQA@U^3mUGQefn9M+E zLWN%x3iR2IkOzg>Z2Cm(1bue&l%yvuZBDrw!jo{#8NCvo6!3y*uZ0PL!aFNOmxqG0 z5M3S$yh3z&!_wxJYa!2oYd+!`f>$KrM}6LGWP4DE74aJuqY1SS^ddjP z1~Gr8r7b8+L8n0nI~R;Db`?Wp3i4u*HRyb1k?1mp8X$|waOj}GSj5nsk$!BIf;R>o z&y;rjV&+Q8bd*-%FO6i_S?R~j*}c#zZf<2gXJ%(}7{W8}^ZfRGnK!#{vOivB-#y=>_dA`J zFZOcs_rvGCUnzC*c0V_Pi!JQ<7+c}{Q|-oO8;kDd@Z_c!D!Vi6Q6$8RJK|C50uG8!H$WJP|7jl}zy!6+@{I z_HDY_Es&&knDR0;6D#-&nXLW{5wu7qb&23J#ldF?Vnr`*fgn~)Pv{s#iA?JlMXWT4 z$uUaacsDLe>*N$BB#a4GHWs648Ej=Ysd!9IThaYOfLGBxC>Sf=h{8buS2;GSaF$?8 z#+3+X!L{V36wZ>kZk6O!2Fez+s_9U0R#Ta{243~#tm5I#mW`E&XT!DZjwzlkc>y`E zA`na}9xK3uM1(^Pu^@XIwXVe=K@ZNJQ*=kLRpV+zcVJs}#}(a?^qOuD0>jtz@K7+; z^zcx?)%5VLU~9&;2JJLt)^)c~pw|;e;~H{xF%>J&>;4Iz!Ap69 zts5KRvPUyR>v*N>ST0YJ8{rd=uIC%XRw>+p(Qu+=gaV_1Wn55xl$zmRj~4UIfnP3T z)5+?z34e1o&kM?rWo(68oR;#ffnRQ6%gJfK+`?A=6!+017i<%MvG1JvLz{T|X)w6# pu^k@bZmLa&RSI`tv`5mh0;9b%m!LQd$HK9392~bdm+)>q`wvrHds_ei diff --git a/proto/penumbra/penumbra/core/component/ibc/v1/ibc.proto b/proto/penumbra/penumbra/core/component/ibc/v1/ibc.proto index 733ca5a3af..de0d70d347 100644 --- a/proto/penumbra/penumbra/core/component/ibc/v1/ibc.proto +++ b/proto/penumbra/penumbra/core/component/ibc/v1/ibc.proto @@ -54,6 +54,11 @@ message 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. bool use_compat_address = 8; + + // 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. + string ics20_memo = 9; } message ClientData {