// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . //! Various implementations for `ConvertOrigin`. use frame_support::traits::{EnsureOrigin, Get, GetBacking, OriginTrait}; use frame_system::RawOrigin as SystemRawOrigin; use polkadot_parachain::primitives::IsSystem; use sp_std::marker::PhantomData; use xcm::latest::{BodyId, BodyPart, Junction, Junctions::*, MultiLocation, NetworkId, OriginKind}; use xcm_executor::traits::{Convert, ConvertOrigin}; /// Sovereign accounts use the system's `Signed` origin with an account ID derived from the `LocationConverter`. pub struct SovereignSignedViaLocation( PhantomData<(LocationConverter, RuntimeOrigin)>, ); impl< LocationConverter: Convert, RuntimeOrigin: OriginTrait, > ConvertOrigin for SovereignSignedViaLocation where RuntimeOrigin::AccountId: Clone, { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!( target: "xcm::origin_conversion", "SovereignSignedViaLocation origin: {:?}, kind: {:?}", origin, kind, ); if let OriginKind::SovereignAccount = kind { let location = LocationConverter::convert(origin)?; Ok(RuntimeOrigin::signed(location).into()) } else { Err(origin) } } } pub struct ParentAsSuperuser(PhantomData); impl ConvertOrigin for ParentAsSuperuser { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!(target: "xcm::origin_conversion", "ParentAsSuperuser origin: {:?}, kind: {:?}", origin, kind); if kind == OriginKind::Superuser && origin.contains_parents_only(1) { Ok(RuntimeOrigin::root()) } else { Err(origin) } } } pub struct ChildSystemParachainAsSuperuser( PhantomData<(ParaId, RuntimeOrigin)>, ); impl, RuntimeOrigin: OriginTrait> ConvertOrigin for ChildSystemParachainAsSuperuser { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!(target: "xcm::origin_conversion", "ChildSystemParachainAsSuperuser origin: {:?}, kind: {:?}", origin, kind); match (kind, origin) { ( OriginKind::Superuser, MultiLocation { parents: 0, interior: X1(Junction::Parachain(id)) }, ) if ParaId::from(id).is_system() => Ok(RuntimeOrigin::root()), (_, origin) => Err(origin), } } } pub struct SiblingSystemParachainAsSuperuser( PhantomData<(ParaId, RuntimeOrigin)>, ); impl, RuntimeOrigin: OriginTrait> ConvertOrigin for SiblingSystemParachainAsSuperuser { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!( target: "xcm::origin_conversion", "SiblingSystemParachainAsSuperuser origin: {:?}, kind: {:?}", origin, kind, ); match (kind, origin) { ( OriginKind::Superuser, MultiLocation { parents: 1, interior: X1(Junction::Parachain(id)) }, ) if ParaId::from(id).is_system() => Ok(RuntimeOrigin::root()), (_, origin) => Err(origin), } } } pub struct ChildParachainAsNative( PhantomData<(ParachainOrigin, RuntimeOrigin)>, ); impl, RuntimeOrigin: From> ConvertOrigin for ChildParachainAsNative { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!(target: "xcm::origin_conversion", "ChildParachainAsNative origin: {:?}, kind: {:?}", origin, kind); match (kind, origin) { ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::Parachain(id)) }, ) => Ok(RuntimeOrigin::from(ParachainOrigin::from(id))), (_, origin) => Err(origin), } } } pub struct SiblingParachainAsNative( PhantomData<(ParachainOrigin, RuntimeOrigin)>, ); impl, RuntimeOrigin: From> ConvertOrigin for SiblingParachainAsNative { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!( target: "xcm::origin_conversion", "SiblingParachainAsNative origin: {:?}, kind: {:?}", origin, kind, ); match (kind, origin) { ( OriginKind::Native, MultiLocation { parents: 1, interior: X1(Junction::Parachain(id)) }, ) => Ok(RuntimeOrigin::from(ParachainOrigin::from(id))), (_, origin) => Err(origin), } } } // Our Relay-chain has a native origin given by the `Get`ter. pub struct RelayChainAsNative( PhantomData<(RelayOrigin, RuntimeOrigin)>, ); impl, RuntimeOrigin> ConvertOrigin for RelayChainAsNative { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!(target: "xcm::origin_conversion", "RelayChainAsNative origin: {:?}, kind: {:?}", origin, kind); if kind == OriginKind::Native && origin.contains_parents_only(1) { Ok(RelayOrigin::get()) } else { Err(origin) } } } pub struct SignedAccountId32AsNative(PhantomData<(Network, RuntimeOrigin)>); impl, RuntimeOrigin: OriginTrait> ConvertOrigin for SignedAccountId32AsNative where RuntimeOrigin::AccountId: From<[u8; 32]>, { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!( target: "xcm::origin_conversion", "SignedAccountId32AsNative origin: {:?}, kind: {:?}", origin, kind, ); match (kind, origin) { ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::AccountId32 { id, network }) }, ) if matches!(network, NetworkId::Any) || network == Network::get() => Ok(RuntimeOrigin::signed(id.into())), (_, origin) => Err(origin), } } } pub struct SignedAccountKey20AsNative( PhantomData<(Network, RuntimeOrigin)>, ); impl, RuntimeOrigin: OriginTrait> ConvertOrigin for SignedAccountKey20AsNative where RuntimeOrigin::AccountId: From<[u8; 20]>, { fn convert_origin( origin: impl Into, kind: OriginKind, ) -> Result { let origin = origin.into(); log::trace!( target: "xcm::origin_conversion", "SignedAccountKey20AsNative origin: {:?}, kind: {:?}", origin, kind, ); match (kind, origin) { ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::AccountKey20 { key, network }) }, ) if (matches!(network, NetworkId::Any) || network == Network::get()) => Ok(RuntimeOrigin::signed(key.into())), (_, origin) => Err(origin), } } } /// `EnsureOrigin` barrier to convert from dispatch origin to XCM origin, if one exists. pub struct EnsureXcmOrigin(PhantomData<(RuntimeOrigin, Conversion)>); impl> EnsureOrigin for EnsureXcmOrigin where RuntimeOrigin::PalletsOrigin: PartialEq, { type Success = MultiLocation; fn try_origin(o: RuntimeOrigin) -> Result { let o = match Conversion::convert(o) { Ok(location) => return Ok(location), Err(o) => o, }; // We institute a root fallback so root can always represent the context. This // guarantees that `successful_origin` will work. if o.caller() == RuntimeOrigin::root().caller() { Ok(Here.into()) } else { Err(o) } } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { Ok(RuntimeOrigin::root()) } } /// `Convert` implementation to convert from some a `Signed` (system) `Origin` into an `AccountId32`. /// /// Typically used when configuring `pallet-xcm` for allowing normal accounts to dispatch an XCM from an `AccountId32` /// origin. pub struct SignedToAccountId32( PhantomData<(RuntimeOrigin, AccountId, Network)>, ); impl, Network: Get> Convert for SignedToAccountId32 where RuntimeOrigin::PalletsOrigin: From> + TryInto, Error = RuntimeOrigin::PalletsOrigin>, { fn convert(o: RuntimeOrigin) -> Result { o.try_with_caller(|caller| match caller.try_into() { Ok(SystemRawOrigin::Signed(who)) => Ok(Junction::AccountId32 { network: Network::get(), id: who.into() }.into()), Ok(other) => Err(other.into()), Err(other) => Err(other), }) } } /// `Convert` implementation to convert from some an origin which implements `Backing` into a corresponding `Plurality` /// `MultiLocation`. /// /// Typically used when configuring `pallet-xcm` for allowing a collective's Origin to dispatch an XCM from a /// `Plurality` origin. pub struct BackingToPlurality( PhantomData<(RuntimeOrigin, COrigin, Body)>, ); impl> Convert for BackingToPlurality where RuntimeOrigin::PalletsOrigin: From + TryInto, { fn convert(o: RuntimeOrigin) -> Result { o.try_with_caller(|caller| match caller.try_into() { Ok(co) => match co.get_backing() { Some(backing) => Ok(Junction::Plurality { id: Body::get(), part: BodyPart::Fraction { nom: backing.approvals, denom: backing.eligible }, } .into()), None => Err(co.into()), }, Err(other) => Err(other), }) } }