Skip to content

Commit 7583934

Browse files
committed
GMP precompile: manually support XCM MultiLocation V1 for backward compatibility (#3327)
* GMP: manually support XCM MultiLocation V1 for backward compatibility * test: ensure the GMP precompile support XCM MultiLocation V1 * add rust test with a real wormhole payload * biome * add comment
1 parent d4287de commit 7583934

File tree

2 files changed

+382
-1
lines changed

2 files changed

+382
-1
lines changed

precompiles/gmp/src/types.rs

Lines changed: 280 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,35 @@ use parity_scale_codec::{Decode, Encode};
2020
use precompile_utils::prelude::*;
2121
use sp_core::{H256, U256};
2222
use sp_std::vec::Vec;
23-
use xcm::VersionedLocation;
23+
24+
// The Polkadot-sdk removed support for XCM Location V1, but the GMP precompile still needs to support it,
25+
// so we have to wrap VersionedLocation to re-add support for XCM Location V1.
26+
#[derive(Encode, Decode, Debug)]
27+
pub enum VersionedLocation {
28+
#[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index
29+
V2(deprecated_xcm_v2::MultiLocationV2),
30+
#[codec(index = 3)]
31+
V3(xcm::v3::MultiLocation),
32+
#[codec(index = 4)]
33+
V4(xcm::v4::Location),
34+
#[codec(index = 5)]
35+
V5(xcm::v5::Location),
36+
}
37+
38+
impl TryFrom<VersionedLocation> for xcm::latest::Location {
39+
type Error = ();
40+
41+
fn try_from(value: VersionedLocation) -> Result<Self, Self::Error> {
42+
match value {
43+
VersionedLocation::V2(location) => {
44+
xcm::VersionedLocation::V3(location.try_into()?).try_into()
45+
}
46+
VersionedLocation::V3(location) => xcm::VersionedLocation::V3(location).try_into(),
47+
VersionedLocation::V4(location) => xcm::VersionedLocation::V4(location).try_into(),
48+
VersionedLocation::V5(location) => xcm::VersionedLocation::V5(location).try_into(),
49+
}
50+
}
51+
}
2452

2553
// A user action which will attempt to route the transferred assets to the account/chain specified
2654
// by the given Location. Recall that a Location can contain both a chain and an account
@@ -91,3 +119,254 @@ pub struct WormholeTransferWithPayloadData {
91119
pub from_address: H256,
92120
pub payload: BoundedBytes<crate::GetCallDataLimit>,
93121
}
122+
123+
/// Reimplement the deprecated xcm v2 Location types to allow for backwards compatibility
124+
mod deprecated_xcm_v2 {
125+
use super::*;
126+
127+
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
128+
pub struct MultiLocationV2 {
129+
pub parents: u8,
130+
pub interior: JunctionsV2,
131+
}
132+
133+
impl TryFrom<MultiLocationV2> for xcm::v3::MultiLocation {
134+
type Error = ();
135+
136+
fn try_from(value: MultiLocationV2) -> Result<Self, Self::Error> {
137+
Ok(xcm::v3::MultiLocation::new(
138+
value.parents,
139+
xcm::v3::Junctions::try_from(value.interior)?,
140+
))
141+
}
142+
}
143+
144+
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
145+
pub enum JunctionsV2 {
146+
/// The interpreting consensus system.
147+
Here,
148+
/// A relative path comprising 1 junction.
149+
X1(JunctionV2),
150+
/// A relative path comprising 2 junctions.
151+
X2(JunctionV2, JunctionV2),
152+
/// A relative path comprising 3 junctions.
153+
X3(JunctionV2, JunctionV2, JunctionV2),
154+
/// A relative path comprising 4 junctions.
155+
X4(JunctionV2, JunctionV2, JunctionV2, JunctionV2),
156+
/// A relative path comprising 5 junctions.
157+
X5(JunctionV2, JunctionV2, JunctionV2, JunctionV2, JunctionV2),
158+
/// A relative path comprising 6 junctions.
159+
X6(
160+
JunctionV2,
161+
JunctionV2,
162+
JunctionV2,
163+
JunctionV2,
164+
JunctionV2,
165+
JunctionV2,
166+
),
167+
/// A relative path comprising 7 junctions.
168+
X7(
169+
JunctionV2,
170+
JunctionV2,
171+
JunctionV2,
172+
JunctionV2,
173+
JunctionV2,
174+
JunctionV2,
175+
JunctionV2,
176+
),
177+
/// A relative path comprising 8 junctions.
178+
X8(
179+
JunctionV2,
180+
JunctionV2,
181+
JunctionV2,
182+
JunctionV2,
183+
JunctionV2,
184+
JunctionV2,
185+
JunctionV2,
186+
JunctionV2,
187+
),
188+
}
189+
190+
impl TryFrom<JunctionsV2> for xcm::v3::Junctions {
191+
type Error = ();
192+
193+
fn try_from(value: JunctionsV2) -> Result<Self, Self::Error> {
194+
use JunctionsV2::*;
195+
Ok(match value {
196+
Here => Self::Here,
197+
X1(j1) => Self::X1(j1.try_into()?),
198+
X2(j1, j2) => Self::X2(j1.try_into()?, j2.try_into()?),
199+
X3(j1, j2, j3) => Self::X3(j1.try_into()?, j2.try_into()?, j3.try_into()?),
200+
X4(j1, j2, j3, j4) => Self::X4(
201+
j1.try_into()?,
202+
j2.try_into()?,
203+
j3.try_into()?,
204+
j4.try_into()?,
205+
),
206+
X5(j1, j2, j3, j4, j5) => Self::X5(
207+
j1.try_into()?,
208+
j2.try_into()?,
209+
j3.try_into()?,
210+
j4.try_into()?,
211+
j5.try_into()?,
212+
),
213+
X6(j1, j2, j3, j4, j5, j6) => Self::X6(
214+
j1.try_into()?,
215+
j2.try_into()?,
216+
j3.try_into()?,
217+
j4.try_into()?,
218+
j5.try_into()?,
219+
j6.try_into()?,
220+
),
221+
X7(j1, j2, j3, j4, j5, j6, j7) => Self::X7(
222+
j1.try_into()?,
223+
j2.try_into()?,
224+
j3.try_into()?,
225+
j4.try_into()?,
226+
j5.try_into()?,
227+
j6.try_into()?,
228+
j7.try_into()?,
229+
),
230+
X8(j1, j2, j3, j4, j5, j6, j7, j8) => Self::X8(
231+
j1.try_into()?,
232+
j2.try_into()?,
233+
j3.try_into()?,
234+
j4.try_into()?,
235+
j5.try_into()?,
236+
j6.try_into()?,
237+
j7.try_into()?,
238+
j8.try_into()?,
239+
),
240+
})
241+
}
242+
}
243+
244+
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
245+
pub enum JunctionV2 {
246+
/// An indexed parachain belonging to and operated by the context.
247+
///
248+
/// Generally used when the context is a Polkadot Relay-chain.
249+
Parachain(#[codec(compact)] u32),
250+
/// A 32-byte identifier for an account of a specific network that is respected as a sovereign
251+
/// endpoint within the context.
252+
///
253+
/// Generally used when the context is a Substrate-based chain.
254+
AccountId32 { network: NetworkIdV2, id: [u8; 32] },
255+
/// An 8-byte index for an account of a specific network that is respected as a sovereign
256+
/// endpoint within the context.
257+
///
258+
/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
259+
AccountIndex64 {
260+
network: NetworkIdV2,
261+
#[codec(compact)]
262+
index: u64,
263+
},
264+
/// A 20-byte identifier for an account of a specific network that is respected as a sovereign
265+
/// endpoint within the context.
266+
///
267+
/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
268+
AccountKey20 { network: NetworkIdV2, key: [u8; 20] },
269+
/// An instanced, indexed pallet that forms a constituent part of the context.
270+
///
271+
/// Generally used when the context is a Frame-based chain.
272+
PalletInstance(u8),
273+
/// A non-descript index within the context location.
274+
///
275+
/// Usage will vary widely owing to its generality.
276+
///
277+
/// NOTE: Try to avoid using this and instead use a more specific item.
278+
GeneralIndex(#[codec(compact)] u128),
279+
/// A nondescript datum acting as a key within the context location.
280+
///
281+
/// Usage will vary widely owing to its generality.
282+
///
283+
/// NOTE: Try to avoid using this and instead use a more specific item.
284+
GeneralKey(sp_runtime::WeakBoundedVec<u8, sp_core::ConstU32<32>>),
285+
/// The unambiguous child.
286+
///
287+
/// Not currently used except as a fallback when deriving ancestry.
288+
OnlyChild,
289+
// The GMP precompile doesn't need to support Plurality Junction
290+
//Plurality { id: BodyId, part: BodyPart },
291+
}
292+
293+
impl TryFrom<JunctionV2> for xcm::v3::Junction {
294+
type Error = ();
295+
296+
fn try_from(value: JunctionV2) -> Result<Self, ()> {
297+
use JunctionV2::*;
298+
Ok(match value {
299+
Parachain(id) => Self::Parachain(id),
300+
AccountId32 { network, id } => Self::AccountId32 {
301+
network: network.into(),
302+
id,
303+
},
304+
AccountIndex64 { network, index } => Self::AccountIndex64 {
305+
network: network.into(),
306+
index,
307+
},
308+
AccountKey20 { network, key } => Self::AccountKey20 {
309+
network: network.into(),
310+
key,
311+
},
312+
PalletInstance(index) => Self::PalletInstance(index),
313+
GeneralIndex(id) => Self::GeneralIndex(id),
314+
GeneralKey(key) => match key.len() {
315+
len @ 0..=32 => Self::GeneralKey {
316+
length: len as u8,
317+
data: {
318+
let mut data = [0u8; 32];
319+
data[..len].copy_from_slice(&key[..]);
320+
data
321+
},
322+
},
323+
_ => return Err(()),
324+
},
325+
OnlyChild => Self::OnlyChild,
326+
})
327+
}
328+
}
329+
330+
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
331+
pub enum NetworkIdV2 {
332+
/// Unidentified/any.
333+
Any,
334+
/// Some named network.
335+
Named(sp_runtime::WeakBoundedVec<u8, sp_core::ConstU32<32>>),
336+
/// The Polkadot Relay chain
337+
Polkadot,
338+
/// Kusama.
339+
Kusama,
340+
}
341+
342+
impl From<NetworkIdV2> for Option<xcm::v3::NetworkId> {
343+
fn from(old: NetworkIdV2) -> Option<xcm::v3::NetworkId> {
344+
use NetworkIdV2::*;
345+
match old {
346+
Any => None,
347+
Named(_) => None,
348+
Polkadot => Some(xcm::v3::NetworkId::Polkadot),
349+
Kusama => Some(xcm::v3::NetworkId::Kusama),
350+
}
351+
}
352+
}
353+
}
354+
355+
#[cfg(test)]
356+
mod tests {
357+
use super::*;
358+
359+
#[test]
360+
fn test_versioned_user_action_decode() {
361+
// Encoded payload from this wormhole transfer:
362+
// https://wormholescan.io/#/tx/0x7a6985578742291842d25d80091cb8661f9ebf9301b266d6d4cd324758310569?view=advanced
363+
let encoded_payload = hex::decode(
364+
"0001010200c91f0100c862582c20ec0a5429c6d2239da9908f4b6c93ab4e2589784f8a5452f65f0e45",
365+
)
366+
.unwrap();
367+
368+
// Ensure we can decode the VersionedUserAction
369+
let _versioned_user_action = VersionedUserAction::decode(&mut &encoded_payload[..])
370+
.expect("Failed to decode VersionedUserAction");
371+
}
372+
}

0 commit comments

Comments
 (0)