1use core::marker::PhantomData;
20use frame_support::traits::{Contains, EnsureOrigin, Get, GetBacking, OriginTrait};
21use frame_system::RawOrigin as SystemRawOrigin;
22use polkadot_parachain_primitives::primitives::IsSystem;
23use sp_runtime::traits::TryConvert;
24use xcm::latest::{BodyId, BodyPart, Junction, Junctions::*, Location, NetworkId, OriginKind};
25use xcm_executor::traits::{ConvertLocation, ConvertOrigin};
26
27pub struct SovereignSignedViaLocation<LocationConverter, RuntimeOrigin>(
30 PhantomData<(LocationConverter, RuntimeOrigin)>,
31);
32impl<LocationConverter: ConvertLocation<RuntimeOrigin::AccountId>, RuntimeOrigin: OriginTrait>
33 ConvertOrigin<RuntimeOrigin> for SovereignSignedViaLocation<LocationConverter, RuntimeOrigin>
34where
35 RuntimeOrigin::AccountId: Clone,
36{
37 fn convert_origin(
38 origin: impl Into<Location>,
39 kind: OriginKind,
40 ) -> Result<RuntimeOrigin, Location> {
41 let origin = origin.into();
42 tracing::trace!(
43 target: "xcm::origin_conversion",
44 ?origin, ?kind,
45 "SovereignSignedViaLocation",
46 );
47 if let OriginKind::SovereignAccount = kind {
48 let location = LocationConverter::convert_location(&origin).ok_or(origin)?;
49 Ok(RuntimeOrigin::signed(location).into())
50 } else {
51 Err(origin)
52 }
53 }
54}
55
56pub struct ParentAsSuperuser<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
57impl<RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin> for ParentAsSuperuser<RuntimeOrigin> {
58 fn convert_origin(
59 origin: impl Into<Location>,
60 kind: OriginKind,
61 ) -> Result<RuntimeOrigin, Location> {
62 let origin = origin.into();
63 tracing::trace!(target: "xcm::origin_conversion", ?origin, ?kind, "ParentAsSuperuser",);
64 if kind == OriginKind::Superuser && origin.contains_parents_only(1) {
65 Ok(RuntimeOrigin::root())
66 } else {
67 Err(origin)
68 }
69 }
70}
71
72pub struct ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>(
73 PhantomData<(ParaId, RuntimeOrigin)>,
74);
75impl<ParaId: IsSystem + From<u32>, RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin>
76 for ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>
77{
78 fn convert_origin(
79 origin: impl Into<Location>,
80 kind: OriginKind,
81 ) -> Result<RuntimeOrigin, Location> {
82 let origin = origin.into();
83 tracing::trace!(target: "xcm::origin_conversion", ?origin, ?kind, "ChildSystemParachainAsSuperuser",);
84 match (kind, origin.unpack()) {
85 (OriginKind::Superuser, (0, [Junction::Parachain(id)]))
86 if ParaId::from(*id).is_system() =>
87 Ok(RuntimeOrigin::root()),
88 _ => Err(origin),
89 }
90 }
91}
92
93pub struct SiblingSystemParachainAsSuperuser<ParaId, RuntimeOrigin>(
94 PhantomData<(ParaId, RuntimeOrigin)>,
95);
96impl<ParaId: IsSystem + From<u32>, RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin>
97 for SiblingSystemParachainAsSuperuser<ParaId, RuntimeOrigin>
98{
99 fn convert_origin(
100 origin: impl Into<Location>,
101 kind: OriginKind,
102 ) -> Result<RuntimeOrigin, Location> {
103 let origin = origin.into();
104 tracing::trace!(
105 target: "xcm::origin_conversion",
106 ?origin, ?kind,
107 "SiblingSystemParachainAsSuperuser",
108 );
109 match (kind, origin.unpack()) {
110 (OriginKind::Superuser, (1, [Junction::Parachain(id)]))
111 if ParaId::from(*id).is_system() =>
112 Ok(RuntimeOrigin::root()),
113 _ => Err(origin),
114 }
115 }
116}
117
118pub struct ChildParachainAsNative<ParachainOrigin, RuntimeOrigin>(
119 PhantomData<(ParachainOrigin, RuntimeOrigin)>,
120);
121impl<ParachainOrigin: From<u32>, RuntimeOrigin: From<ParachainOrigin>> ConvertOrigin<RuntimeOrigin>
122 for ChildParachainAsNative<ParachainOrigin, RuntimeOrigin>
123{
124 fn convert_origin(
125 origin: impl Into<Location>,
126 kind: OriginKind,
127 ) -> Result<RuntimeOrigin, Location> {
128 let origin = origin.into();
129 tracing::trace!(target: "xcm::origin_conversion", ?origin, ?kind, "ChildParachainAsNative");
130 match (kind, origin.unpack()) {
131 (OriginKind::Native, (0, [Junction::Parachain(id)])) =>
132 Ok(RuntimeOrigin::from(ParachainOrigin::from(*id))),
133 _ => Err(origin),
134 }
135 }
136}
137
138pub struct SiblingParachainAsNative<ParachainOrigin, RuntimeOrigin>(
139 PhantomData<(ParachainOrigin, RuntimeOrigin)>,
140);
141impl<ParachainOrigin: From<u32>, RuntimeOrigin: From<ParachainOrigin>> ConvertOrigin<RuntimeOrigin>
142 for SiblingParachainAsNative<ParachainOrigin, RuntimeOrigin>
143{
144 fn convert_origin(
145 origin: impl Into<Location>,
146 kind: OriginKind,
147 ) -> Result<RuntimeOrigin, Location> {
148 let origin = origin.into();
149 tracing::trace!(
150 target: "xcm::origin_conversion",
151 ?origin, ?kind,
152 "SiblingParachainAsNative",
153 );
154 match (kind, origin.unpack()) {
155 (OriginKind::Native, (1, [Junction::Parachain(id)])) =>
156 Ok(RuntimeOrigin::from(ParachainOrigin::from(*id))),
157 _ => Err(origin),
158 }
159 }
160}
161
162pub struct RelayChainAsNative<RelayOrigin, RuntimeOrigin>(
164 PhantomData<(RelayOrigin, RuntimeOrigin)>,
165);
166impl<RelayOrigin: Get<RuntimeOrigin>, RuntimeOrigin> ConvertOrigin<RuntimeOrigin>
167 for RelayChainAsNative<RelayOrigin, RuntimeOrigin>
168{
169 fn convert_origin(
170 origin: impl Into<Location>,
171 kind: OriginKind,
172 ) -> Result<RuntimeOrigin, Location> {
173 let origin = origin.into();
174 tracing::trace!(target: "xcm::origin_conversion", ?origin, ?kind, "RelayChainAsNative");
175 if kind == OriginKind::Native && origin.contains_parents_only(1) {
176 Ok(RelayOrigin::get())
177 } else {
178 Err(origin)
179 }
180 }
181}
182
183pub struct SignedAccountId32AsNative<Network, RuntimeOrigin>(PhantomData<(Network, RuntimeOrigin)>);
184impl<Network: Get<Option<NetworkId>>, RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin>
185 for SignedAccountId32AsNative<Network, RuntimeOrigin>
186where
187 RuntimeOrigin::AccountId: From<[u8; 32]>,
188{
189 fn convert_origin(
190 origin: impl Into<Location>,
191 kind: OriginKind,
192 ) -> Result<RuntimeOrigin, Location> {
193 let origin = origin.into();
194 tracing::trace!(
195 target: "xcm::origin_conversion",
196 ?origin, ?kind,
197 "SignedAccountId32AsNative",
198 );
199 match (kind, origin.unpack()) {
200 (OriginKind::Native, (0, [Junction::AccountId32 { id, network }]))
201 if matches!(network, None) || *network == Network::get() =>
202 Ok(RuntimeOrigin::signed((*id).into())),
203 _ => Err(origin),
204 }
205 }
206}
207
208pub struct SignedAccountKey20AsNative<Network, RuntimeOrigin>(
209 PhantomData<(Network, RuntimeOrigin)>,
210);
211impl<Network: Get<Option<NetworkId>>, RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin>
212 for SignedAccountKey20AsNative<Network, RuntimeOrigin>
213where
214 RuntimeOrigin::AccountId: From<[u8; 20]>,
215{
216 fn convert_origin(
217 origin: impl Into<Location>,
218 kind: OriginKind,
219 ) -> Result<RuntimeOrigin, Location> {
220 let origin = origin.into();
221 tracing::trace!(
222 target: "xcm::origin_conversion",
223 ?origin, ?kind,
224 "SignedAccountKey20AsNative",
225 );
226 match (kind, origin.unpack()) {
227 (OriginKind::Native, (0, [Junction::AccountKey20 { key, network }]))
228 if (matches!(network, None) || *network == Network::get()) =>
229 Ok(RuntimeOrigin::signed((*key).into())),
230 _ => Err(origin),
231 }
232 }
233}
234
235pub struct EnsureXcmOrigin<RuntimeOrigin, Conversion>(PhantomData<(RuntimeOrigin, Conversion)>);
237impl<RuntimeOrigin: OriginTrait + Clone, Conversion: TryConvert<RuntimeOrigin, Location>>
238 EnsureOrigin<RuntimeOrigin> for EnsureXcmOrigin<RuntimeOrigin, Conversion>
239where
240 RuntimeOrigin::PalletsOrigin: PartialEq,
241{
242 type Success = Location;
243 fn try_origin(o: RuntimeOrigin) -> Result<Self::Success, RuntimeOrigin> {
244 let o = match Conversion::try_convert(o) {
245 Ok(location) => return Ok(location),
246 Err(o) => o,
247 };
248 if o.caller() == RuntimeOrigin::root().caller() {
251 Ok(Here.into())
252 } else {
253 Err(o)
254 }
255 }
256
257 #[cfg(feature = "runtime-benchmarks")]
258 fn try_successful_origin() -> Result<RuntimeOrigin, ()> {
259 Ok(RuntimeOrigin::root())
260 }
261}
262
263pub struct SignedToAccountId32<RuntimeOrigin, AccountId, Network>(
269 PhantomData<(RuntimeOrigin, AccountId, Network)>,
270);
271impl<
272 RuntimeOrigin: OriginTrait + Clone,
273 AccountId: Into<[u8; 32]>,
274 Network: Get<Option<NetworkId>>,
275 > TryConvert<RuntimeOrigin, Location> for SignedToAccountId32<RuntimeOrigin, AccountId, Network>
276where
277 RuntimeOrigin::PalletsOrigin: From<SystemRawOrigin<AccountId>>
278 + TryInto<SystemRawOrigin<AccountId>, Error = RuntimeOrigin::PalletsOrigin>,
279{
280 fn try_convert(o: RuntimeOrigin) -> Result<Location, RuntimeOrigin> {
281 o.try_with_caller(|caller| match caller.try_into() {
282 Ok(SystemRawOrigin::Signed(who)) =>
283 Ok(Junction::AccountId32 { network: Network::get(), id: who.into() }.into()),
284 Ok(other) => Err(other.into()),
285 Err(other) => Err(other),
286 })
287 }
288}
289
290pub struct BackingToPlurality<RuntimeOrigin, COrigin, Body>(
296 PhantomData<(RuntimeOrigin, COrigin, Body)>,
297);
298impl<RuntimeOrigin: OriginTrait + Clone, COrigin: GetBacking, Body: Get<BodyId>>
299 TryConvert<RuntimeOrigin, Location> for BackingToPlurality<RuntimeOrigin, COrigin, Body>
300where
301 RuntimeOrigin::PalletsOrigin:
302 From<COrigin> + TryInto<COrigin, Error = RuntimeOrigin::PalletsOrigin>,
303{
304 fn try_convert(o: RuntimeOrigin) -> Result<Location, RuntimeOrigin> {
305 o.try_with_caller(|caller| match caller.try_into() {
306 Ok(co) => match co.get_backing() {
307 Some(backing) => Ok(Junction::Plurality {
308 id: Body::get(),
309 part: BodyPart::Fraction { nom: backing.approvals, denom: backing.eligible },
310 }
311 .into()),
312 None => Err(co.into()),
313 },
314 Err(other) => Err(other),
315 })
316 }
317}
318
319pub struct OriginToPluralityVoice<RuntimeOrigin, EnsureBodyOrigin, Body>(
322 PhantomData<(RuntimeOrigin, EnsureBodyOrigin, Body)>,
323);
324impl<RuntimeOrigin: Clone, EnsureBodyOrigin: EnsureOrigin<RuntimeOrigin>, Body: Get<BodyId>>
325 TryConvert<RuntimeOrigin, Location>
326 for OriginToPluralityVoice<RuntimeOrigin, EnsureBodyOrigin, Body>
327{
328 fn try_convert(o: RuntimeOrigin) -> Result<Location, RuntimeOrigin> {
329 match EnsureBodyOrigin::try_origin(o) {
330 Ok(_) => Ok(Junction::Plurality { id: Body::get(), part: BodyPart::Voice }.into()),
331 Err(o) => Err(o),
332 }
333 }
334}
335
336pub struct LocationAsSuperuser<WhitelistedSuperuserLocations, RuntimeOrigin>(
339 PhantomData<(WhitelistedSuperuserLocations, RuntimeOrigin)>,
340);
341impl<WhitelistedSuperuserLocations: Contains<Location>, RuntimeOrigin: OriginTrait>
342 ConvertOrigin<RuntimeOrigin>
343 for LocationAsSuperuser<WhitelistedSuperuserLocations, RuntimeOrigin>
344{
345 fn convert_origin(
346 origin: impl Into<Location>,
347 kind: OriginKind,
348 ) -> Result<RuntimeOrigin, Location> {
349 let origin = origin.into();
350 tracing::trace!(
351 target: "xcm::origin_conversion",
352 ?origin, ?kind,
353 "LocationAsSuperuser",
354 );
355 match (kind, &origin) {
356 (OriginKind::Superuser, loc) if WhitelistedSuperuserLocations::contains(loc) =>
357 Ok(RuntimeOrigin::root()),
358 _ => Err(origin),
359 }
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 use frame_support::{construct_runtime, derive_impl, parameter_types, traits::Equals};
367 use xcm::latest::{Junction::*, OriginKind};
368
369 type Block = frame_system::mocking::MockBlock<Test>;
370
371 construct_runtime!(
372 pub enum Test
373 {
374 System: frame_system,
375 }
376 );
377
378 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
379 impl frame_system::Config for Test {
380 type Block = Block;
381 }
382
383 parameter_types! {
384 pub SuperuserLocation: Location = Location::new(0, Parachain(1));
385 }
386
387 #[test]
388 fn superuser_location_works() {
389 let test_conversion = |loc, kind| {
390 LocationAsSuperuser::<Equals<SuperuserLocation>, RuntimeOrigin>::convert_origin(
391 loc, kind,
392 )
393 };
394
395 assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Superuser), Ok(..)));
397 assert!(matches!(
399 test_conversion(Location::new(0, Parachain(1)), OriginKind::Superuser),
400 Ok(..)
401 ));
402
403 assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Native), Err(..)));
405 assert!(matches!(
406 test_conversion(SuperuserLocation::get(), OriginKind::SovereignAccount),
407 Err(..)
408 ));
409 assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Xcm), Err(..)));
410
411 assert!(matches!(
416 test_conversion(Location::new(0, Parachain(2)), OriginKind::Superuser),
417 Err(..)
418 ));
419 assert!(matches!(
421 test_conversion(Location::new(1, Parachain(1)), OriginKind::Superuser),
422 Err(..)
423 ));
424 assert!(matches!(
426 test_conversion(
427 Location::new(1, [Parachain(1), GeneralIndex(0)]),
428 OriginKind::Superuser
429 ),
430 Err(..)
431 ));
432 assert!(matches!(test_conversion(Location::new(0, Here), OriginKind::Superuser), Err(..)));
434 assert!(matches!(test_conversion(Location::new(1, Here), OriginKind::Superuser), Err(..)));
436 assert!(matches!(
438 test_conversion(
439 Location::new(0, AccountId32 { network: None, id: [0u8; 32] }),
440 OriginKind::Superuser
441 ),
442 Err(..)
443 ));
444 assert!(matches!(
446 test_conversion(
447 Location::new(0, [Parachain(1), AccountId32 { network: None, id: [1u8; 32] }]),
448 OriginKind::Superuser
449 ),
450 Err(..)
451 ));
452 }
453}