|
17 | 17 | //! Various implementations for `ConvertOrigin`. |
18 | 18 |
|
19 | 19 | use core::marker::PhantomData; |
20 | | -use frame_support::traits::{EnsureOrigin, Get, GetBacking, OriginTrait}; |
| 20 | +use frame_support::traits::{Contains, EnsureOrigin, Get, GetBacking, OriginTrait}; |
21 | 21 | use frame_system::RawOrigin as SystemRawOrigin; |
22 | 22 | use polkadot_parachain_primitives::primitives::IsSystem; |
23 | 23 | use sp_runtime::traits::TryConvert; |
@@ -332,3 +332,122 @@ impl<RuntimeOrigin: Clone, EnsureBodyOrigin: EnsureOrigin<RuntimeOrigin>, Body: |
332 | 332 | } |
333 | 333 | } |
334 | 334 | } |
| 335 | + |
| 336 | +/// Converter that allows specific `Location`s to act as a superuser (`RuntimeOrigin::root()`) |
| 337 | +/// if it matches the predefined `WhitelistedSuperuserLocations` filter and `OriginKind::Superuser`. |
| 338 | +pub struct LocationAsSuperuser<WhitelistedSuperuserLocations, RuntimeOrigin>( |
| 339 | + PhantomData<(WhitelistedSuperuserLocations, RuntimeOrigin)>, |
| 340 | +); |
| 341 | +impl<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)] |
| 364 | +mod 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 | + // Location that was set as SuperUserLocation should result in success conversion to Root |
| 396 | + assert!(matches!(test_conversion(SuperuserLocation::get(), OriginKind::Superuser), Ok(..))); |
| 397 | + // Same Location as SuperUserLocation::get() |
| 398 | + assert!(matches!( |
| 399 | + test_conversion(Location::new(0, Parachain(1)), OriginKind::Superuser), |
| 400 | + Ok(..) |
| 401 | + )); |
| 402 | + |
| 403 | + // Same Location but different origin kind |
| 404 | + 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 | + // No other location should result in successful conversion to Root |
| 412 | + // thus expecting Err in all cases below |
| 413 | + // |
| 414 | + // Non-matching parachain number |
| 415 | + assert!(matches!( |
| 416 | + test_conversion(Location::new(0, Parachain(2)), OriginKind::Superuser), |
| 417 | + Err(..) |
| 418 | + )); |
| 419 | + // Non-matching parents count |
| 420 | + assert!(matches!( |
| 421 | + test_conversion(Location::new(1, Parachain(1)), OriginKind::Superuser), |
| 422 | + Err(..) |
| 423 | + )); |
| 424 | + // Child location of SuperUserLocation |
| 425 | + assert!(matches!( |
| 426 | + test_conversion( |
| 427 | + Location::new(1, [Parachain(1), GeneralIndex(0)]), |
| 428 | + OriginKind::Superuser |
| 429 | + ), |
| 430 | + Err(..) |
| 431 | + )); |
| 432 | + // Here |
| 433 | + assert!(matches!(test_conversion(Location::new(0, Here), OriginKind::Superuser), Err(..))); |
| 434 | + // Parent |
| 435 | + assert!(matches!(test_conversion(Location::new(1, Here), OriginKind::Superuser), Err(..))); |
| 436 | + // Some random account |
| 437 | + assert!(matches!( |
| 438 | + test_conversion( |
| 439 | + Location::new(0, AccountId32 { network: None, id: [0u8; 32] }), |
| 440 | + OriginKind::Superuser |
| 441 | + ), |
| 442 | + Err(..) |
| 443 | + )); |
| 444 | + // Child location of SuperUserLocation |
| 445 | + 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 | +} |
0 commit comments