Skip to content
6 changes: 3 additions & 3 deletions polkadot/xcm/xcm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ pub use origin_aliases::*;
mod origin_conversion;
pub use origin_conversion::{
BackingToPlurality, ChildParachainAsNative, ChildSystemParachainAsSuperuser, EnsureXcmOrigin,
OriginToPluralityVoice, ParentAsSuperuser, RelayChainAsNative, SiblingParachainAsNative,
SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative, SignedAccountKey20AsNative,
SignedToAccountId32, SovereignSignedViaLocation,
LocationAsSuperuser, OriginToPluralityVoice, ParentAsSuperuser, RelayChainAsNative,
SiblingParachainAsNative, SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative,
SignedAccountKey20AsNative, SignedToAccountId32, SovereignSignedViaLocation,
};

mod pay;
Expand Down
121 changes: 120 additions & 1 deletion polkadot/xcm/xcm-builder/src/origin_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//! Various implementations for `ConvertOrigin`.

use core::marker::PhantomData;
use frame_support::traits::{EnsureOrigin, Get, GetBacking, OriginTrait};
use frame_support::traits::{Contains, EnsureOrigin, Get, GetBacking, OriginTrait};
use frame_system::RawOrigin as SystemRawOrigin;
use polkadot_parachain_primitives::primitives::IsSystem;
use sp_runtime::traits::TryConvert;
Expand Down Expand Up @@ -332,3 +332,122 @@ impl<RuntimeOrigin: Clone, EnsureBodyOrigin: EnsureOrigin<RuntimeOrigin>, Body:
}
}
}

/// Converter that allows specific `Location`s to act as a superuser (`RuntimeOrigin::root()`)
/// if it matches the predefined `WhitelistedSuperuserLocations` filter and `OriginKind::Superuser`.
pub struct LocationAsSuperuser<WhitelistedSuperuserLocations, RuntimeOrigin>(
PhantomData<(WhitelistedSuperuserLocations, RuntimeOrigin)>,
);
impl<WhitelistedSuperuserLocations: Contains<Location>, RuntimeOrigin: OriginTrait>
ConvertOrigin<RuntimeOrigin>
for LocationAsSuperuser<WhitelistedSuperuserLocations, RuntimeOrigin>
{
fn convert_origin(
origin: impl Into<Location>,
kind: OriginKind,
) -> Result<RuntimeOrigin, Location> {
let origin = origin.into();
tracing::trace!(
target: "xcm::origin_conversion",
?origin, ?kind,
"LocationAsSuperuser",
);
match (kind, &origin) {
(OriginKind::Superuser, loc) if WhitelistedSuperuserLocations::contains(loc) =>
Ok(RuntimeOrigin::root()),
_ => Err(origin),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use frame_support::{construct_runtime, derive_impl, parameter_types, traits::Equals};
use xcm::latest::{Junction::*, OriginKind};

type Block = frame_system::mocking::MockBlock<Test>;

construct_runtime!(
pub enum Test
{
System: frame_system,
}
);

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type Block = Block;
}

parameter_types! {
pub SuperuserLocation: Location = Location::new(0, Parachain(1));
}

#[test]
fn superuser_location_works() {
let test_conversion = |loc, kind| {
LocationAsSuperuser::<Equals<SuperuserLocation>, RuntimeOrigin>::convert_origin(
loc, kind,
)
};

// Location that was set as SuperUserLocation should result in success conversion to Root
assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Superuser), Ok(..)));
// Same Location as SuperUserLocation::get()
assert!(matches!(
test_conversion(Location::new(0, Parachain(1)), OriginKind::Superuser),
Ok(..)
));

// Same Location but different origin kind
assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Native), Err(..)));
assert!(matches!(
test_conversion(SuperuserLocation::get(), OriginKind::SovereignAccount),
Err(..)
));
assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Xcm), Err(..)));

// No other location should result in successful conversion to Root
// thus expecting Err in all cases below
//
// Non-matching parachain number
assert!(matches!(
test_conversion(Location::new(0, Parachain(2)), OriginKind::Superuser),
Err(..)
));
// Non-matching parents count
assert!(matches!(
test_conversion(Location::new(1, Parachain(1)), OriginKind::Superuser),
Err(..)
));
// Child location of SuperUserLocation
assert!(matches!(
test_conversion(
Location::new(1, [Parachain(1), GeneralIndex(0)]),
OriginKind::Superuser
),
Err(..)
));
// Here
assert!(matches!(test_conversion(Location::new(0, Here), OriginKind::Superuser), Err(..)));
// Parent
assert!(matches!(test_conversion(Location::new(1, Here), OriginKind::Superuser), Err(..)));
// Some random account
assert!(matches!(
test_conversion(
Location::new(0, AccountId32 { network: None, id: [0u8; 32] }),
OriginKind::Superuser
),
Err(..)
));
// Child location of SuperUserLocation
assert!(matches!(
test_conversion(
Location::new(0, [Parachain(1), AccountId32 { network: None, id: [1u8; 32] }]),
OriginKind::Superuser
),
Err(..)
));
}
}
8 changes: 8 additions & 0 deletions prdoc/pr_8210.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: '[xcm-builder][origin_conversion] LocationAsSuperuser converter introduced'
doc:
- audience: Runtime Dev
description: |-
Introduces a `LocationAsSuperuser` struct that implements `ConvertOrigin` to allow specific `Location`s defined through XCM configuration to act as Root on the local chain. Implementation is generic over `Location` but was created for purposes of allowing AssetHub system chain (by other system chains and relay chains) to execute Root level extrinsics like `authorize_upgrade` on them.
crates:
- name: staging-xcm-builder
bump: minor
Loading