Skip to content
122 changes: 121 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,123 @@ impl<RuntimeOrigin: Clone, EnsureBodyOrigin: EnsureOrigin<RuntimeOrigin>, Body:
}
}
}

/// Implements `ConvertOrigin` to convert a `Location` into `RuntimeOrigin::root()`.
///
/// This is typically used when configuring `pallet-xcm` to allow a remote `Location`
/// to act as the `Root` origin on the local chain.
pub struct LocationAsSuperuser<SuperuserLocation, RuntimeOrigin>(
PhantomData<(SuperuserLocation, RuntimeOrigin)>,
);
impl<SuperuserLocation: Contains<Location>, RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin>
for LocationAsSuperuser<SuperuserLocation, 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 SuperuserLocation::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(..)
));
}
}
Loading