// Copyright (C) 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 .
use crate::universal_exports::ensure_is_remote;
use frame_support::traits::Get;
use parity_scale_codec::{Decode, Encode};
use sp_io::hashing::blake2_256;
use sp_runtime::traits::{AccountIdConversion, TrailingZeroInput};
use sp_std::{borrow::Borrow, marker::PhantomData};
use xcm::latest::prelude::*;
use xcm_executor::traits::Convert;
/// Prefix for generating alias account for accounts coming
/// from chains that use 32 byte long representations.
pub const FOREIGN_CHAIN_PREFIX_PARA_32: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para32";
/// Prefix for generating alias account for accounts coming
/// from chains that use 20 byte long representations.
pub const FOREIGN_CHAIN_PREFIX_PARA_20: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para20";
/// Prefix for generating alias account for accounts coming
/// from the relay chain using 32 byte long representations.
pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPrefix_Relay";
/// This converter will for a given `AccountId32`/`AccountKey20`
/// always generate the same "remote" account for a specific
/// sending chain.
/// I.e. the user gets the same remote account
/// on every consuming para-chain and relay chain.
///
/// Can be used as a converter in `SovereignSignedViaLocation`
///
/// ## Example
/// Assuming the following network layout.
///
/// ```notrust
/// R
/// / \
/// / \
/// P1 P2
/// / \ / \
/// / \ / \
/// P1.1 P1.2 P2.1 P2.2
/// ```
/// Then a given account A will have the same alias accounts in the
/// same plane. So, it is important which chain account A acts from.
/// E.g.
/// * From P1.2 A will act as
/// * hash(ParaPrefix, A, 1, 1) on P1.2
/// * hash(ParaPrefix, A, 1, 0) on P1
/// * From P1 A will act as
/// * hash(RelayPrefix, A, 1) on P1.2 & P1.1
/// * hash(ParaPrefix, A, 1, 1) on P2
/// * hash(ParaPrefix, A, 1, 0) on R
///
/// Note that the alias accounts have overlaps but never on the same
/// chain when the sender comes from different chains.
pub struct ForeignChainAliasAccount(PhantomData);
impl + Clone> Convert
for ForeignChainAliasAccount
{
fn convert_ref(location: impl Borrow) -> Result {
let entropy = match location.borrow() {
// Used on the relay chain for sending paras that use 32 byte accounts
MultiLocation {
parents: 0,
interior: X2(Parachain(para_id), AccountId32 { id, .. }),
} => ForeignChainAliasAccount::::from_para_32(para_id, id, 0),
// Used on the relay chain for sending paras that use 20 byte accounts
MultiLocation {
parents: 0,
interior: X2(Parachain(para_id), AccountKey20 { key, .. }),
} => ForeignChainAliasAccount::::from_para_20(para_id, key, 0),
// Used on para-chain for sending paras that use 32 byte accounts
MultiLocation {
parents: 1,
interior: X2(Parachain(para_id), AccountId32 { id, .. }),
} => ForeignChainAliasAccount::::from_para_32(para_id, id, 1),
// Used on para-chain for sending paras that use 20 byte accounts
MultiLocation {
parents: 1,
interior: X2(Parachain(para_id), AccountKey20 { key, .. }),
} => ForeignChainAliasAccount::::from_para_20(para_id, key, 1),
// Used on para-chain for sending from the relay chain
MultiLocation { parents: 1, interior: X1(AccountId32 { id, .. }) } =>
ForeignChainAliasAccount::::from_relay_32(id, 1),
// No other conversions provided
_ => return Err(()),
};
Ok(entropy.into())
}
fn reverse_ref(_: impl Borrow) -> Result {
Err(())
}
}
impl ForeignChainAliasAccount {
fn from_para_32(para_id: &u32, id: &[u8; 32], parents: u8) -> [u8; 32] {
(FOREIGN_CHAIN_PREFIX_PARA_32, para_id, id, parents).using_encoded(blake2_256)
}
fn from_para_20(para_id: &u32, id: &[u8; 20], parents: u8) -> [u8; 32] {
(FOREIGN_CHAIN_PREFIX_PARA_20, para_id, id, parents).using_encoded(blake2_256)
}
fn from_relay_32(id: &[u8; 32], parents: u8) -> [u8; 32] {
(FOREIGN_CHAIN_PREFIX_RELAY, id, parents).using_encoded(blake2_256)
}
}
pub struct Account32Hash(PhantomData<(Network, AccountId)>);
impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
Convert for Account32Hash
{
fn convert_ref(location: impl Borrow) -> Result {
Ok(("multiloc", location.borrow()).using_encoded(blake2_256).into())
}
fn reverse_ref(_: impl Borrow) -> Result {
Err(())
}
}
/// A [`MultiLocation`] consisting of a single `Parent` [`Junction`] will be converted to the
/// parent `AccountId`.
pub struct ParentIsPreset(PhantomData);
impl Convert
for ParentIsPreset
{
fn convert_ref(location: impl Borrow) -> Result {
if location.borrow().contains_parents_only(1) {
Ok(b"Parent"
.using_encoded(|b| AccountId::decode(&mut TrailingZeroInput::new(b)))
.expect("infinite length input; no invalid inputs for type; qed"))
} else {
Err(())
}
}
fn reverse_ref(who: impl Borrow) -> Result {
let parent_account = b"Parent"
.using_encoded(|b| AccountId::decode(&mut TrailingZeroInput::new(b)))
.expect("infinite length input; no invalid inputs for type; qed");
if who.borrow() == &parent_account {
Ok(Parent.into())
} else {
Err(())
}
}
}
pub struct ChildParachainConvertsVia(PhantomData<(ParaId, AccountId)>);
impl + Into + AccountIdConversion, AccountId: Clone>
Convert for ChildParachainConvertsVia
{
fn convert_ref(location: impl Borrow) -> Result {
match location.borrow() {
MultiLocation { parents: 0, interior: X1(Parachain(id)) } =>
Ok(ParaId::from(*id).into_account_truncating()),
_ => Err(()),
}
}
fn reverse_ref(who: impl Borrow) -> Result {
if let Some(id) = ParaId::try_from_account(who.borrow()) {
Ok(Parachain(id.into()).into())
} else {
Err(())
}
}
}
pub struct SiblingParachainConvertsVia(PhantomData<(ParaId, AccountId)>);
impl + Into + AccountIdConversion, AccountId: Clone>
Convert for SiblingParachainConvertsVia
{
fn convert_ref(location: impl Borrow) -> Result {
match location.borrow() {
MultiLocation { parents: 1, interior: X1(Parachain(id)) } =>
Ok(ParaId::from(*id).into_account_truncating()),
_ => Err(()),
}
}
fn reverse_ref(who: impl Borrow) -> Result {
if let Some(id) = ParaId::try_from_account(who.borrow()) {
Ok(MultiLocation::new(1, X1(Parachain(id.into()))))
} else {
Err(())
}
}
}
/// Extracts the `AccountId32` from the passed `location` if the network matches.
pub struct AccountId32Aliases(PhantomData<(Network, AccountId)>);
impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
Convert for AccountId32Aliases
{
fn convert(location: MultiLocation) -> Result {
let id = match location {
MultiLocation { parents: 0, interior: X1(AccountId32 { id, network: None }) } => id,
MultiLocation { parents: 0, interior: X1(AccountId32 { id, network }) }
if network == Network::get() =>
id,
_ => return Err(location),
};
Ok(id.into())
}
fn reverse(who: AccountId) -> Result {
Ok(AccountId32 { id: who.into(), network: Network::get() }.into())
}
}
/// Conversion implementation which converts from a `[u8; 32]`-based `AccountId` into a
/// `MultiLocation` consisting solely of a `AccountId32` junction with a fixed value for its
/// network (provided by `Network`) and the `AccountId`'s `[u8; 32]` datum for the `id`.
pub struct AliasesIntoAccountId32(PhantomData<(Network, AccountId)>);
impl<'a, Network: Get