// 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 sp_std::{marker::PhantomData, convert::TryInto};
use xcm::v0::{MultiLocation, OriginKind, NetworkId, Junction, BodyId, BodyPart};
use xcm_executor::traits::{Convert, ConvertOrigin};
use frame_support::traits::{EnsureOrigin, Get, OriginTrait, GetBacking};
use frame_system::RawOrigin as SystemRawOrigin;
use polkadot_parachain::primitives::IsSystem;
/// Sovereign accounts use the system's `Signed` origin with an account ID derived from the `LocationConverter`.
pub struct SovereignSignedViaLocation(
PhantomData<(LocationConverter, Origin)>
);
impl<
LocationConverter: Convert,
Origin: OriginTrait,
> ConvertOrigin for SovereignSignedViaLocation where Origin::AccountId: Clone {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
if let OriginKind::SovereignAccount = kind {
let location = LocationConverter::convert(origin)?;
Ok(Origin::signed(location).into())
} else {
Err(origin)
}
}
}
pub struct ParentAsSuperuser(PhantomData);
impl<
Origin: OriginTrait,
> ConvertOrigin for ParentAsSuperuser {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Superuser, MultiLocation::X1(Junction::Parent)) =>
Ok(Origin::root()),
(_, origin) => Err(origin),
}
}
}
pub struct ChildSystemParachainAsSuperuser(PhantomData<(ParaId, Origin)>);
impl<
ParaId: IsSystem + From,
Origin: OriginTrait,
> ConvertOrigin for ChildSystemParachainAsSuperuser {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Superuser, MultiLocation::X1(Junction::Parachain(id)))
if ParaId::from(id).is_system() =>
Ok(Origin::root()),
(_, origin) => Err(origin),
}
}
}
pub struct SiblingSystemParachainAsSuperuser(PhantomData<(ParaId, Origin)>);
impl<
ParaId: IsSystem + From,
Origin: OriginTrait
> ConvertOrigin for SiblingSystemParachainAsSuperuser {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Superuser, MultiLocation::X2(Junction::Parent, Junction::Parachain(id)))
if ParaId::from(id).is_system() =>
Ok(Origin::root()),
(_, origin) => Err(origin),
}
}
}
pub struct ChildParachainAsNative(
PhantomData<(ParachainOrigin, Origin)>
);
impl<
ParachainOrigin: From,
Origin: From,
> ConvertOrigin for ChildParachainAsNative {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Native, MultiLocation::X1(Junction::Parachain(id)))
=> Ok(Origin::from(ParachainOrigin::from(id))),
(_, origin) => Err(origin),
}
}
}
pub struct SiblingParachainAsNative(
PhantomData<(ParachainOrigin, Origin)>
);
impl<
ParachainOrigin: From,
Origin: From,
> ConvertOrigin for SiblingParachainAsNative {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Native, MultiLocation::X2(Junction::Parent, Junction::Parachain(id)))
=> Ok(Origin::from(ParachainOrigin::from(id))),
(_, origin) => Err(origin),
}
}
}
// Our Relay-chain has a native origin given by the `Get`ter.
pub struct RelayChainAsNative(
PhantomData<(RelayOrigin, Origin)>
);
impl<
RelayOrigin: Get,
Origin,
> ConvertOrigin for RelayChainAsNative {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Native, MultiLocation::X1(Junction::Parent)) => Ok(RelayOrigin::get()),
(_, origin) => Err(origin),
}
}
}
pub struct SignedAccountId32AsNative(
PhantomData<(Network, Origin)>
);
impl<
Network: Get,
Origin: OriginTrait,
> ConvertOrigin for SignedAccountId32AsNative where
Origin::AccountId: From<[u8; 32]>,
{
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Native, MultiLocation::X1(Junction::AccountId32 { id, network }))
if matches!(network, NetworkId::Any) || network == Network::get()
=> Ok(Origin::signed(id.into())),
(_, origin) => Err(origin),
}
}
}
pub struct SignedAccountKey20AsNative(
PhantomData<(Network, Origin)>
);
impl<
Network: Get,
Origin: OriginTrait
> ConvertOrigin for SignedAccountKey20AsNative where
Origin::AccountId: From<[u8; 20]>,
{
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result {
match (kind, origin) {
(OriginKind::Native, MultiLocation::X1(Junction::AccountKey20 { key, network }))
if matches!(network, NetworkId::Any) || network == Network::get() =>
{
Ok(Origin::signed(key.into()))
}
(_, origin) => Err(origin),
}
}
}
/// EnsureOrigin barrier to convert from dispatch origin to XCM origin, if one exists.
pub struct EnsureXcmOrigin(PhantomData<(Origin, Conversion)>);
impl<
Origin: OriginTrait + Clone,
Conversion: Convert,
> EnsureOrigin for EnsureXcmOrigin where
Origin::PalletsOrigin: PartialEq,
{
type Success = MultiLocation;
fn try_origin(o: Origin) -> 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() == Origin::root().caller() {
Ok(MultiLocation::Null)
} else {
Err(o)
}
}
#[cfg(feature = "runtime-benchmarks")]
fn successful_origin() -> Origin {
Origin::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<(Origin, AccountId, Network)>
);
impl<
Origin: OriginTrait + Clone,
AccountId: Into<[u8; 32]>,
Network: Get,
> Convert for SignedToAccountId32 where
Origin::PalletsOrigin: From> +
TryInto, Error=Origin::PalletsOrigin>
{
fn convert(o: Origin) -> 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<(Origin, COrigin, Body)>
);
impl<
Origin: OriginTrait + Clone,
COrigin: GetBacking,
Body: Get,
> Convert for BackingToPlurality where
Origin::PalletsOrigin: From +
TryInto
{
fn convert(o: Origin) -> 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),
})
}
}