// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see .
use crate::universal_exports::ensure_is_remote;
use alloc::vec::Vec;
use codec::{Compact, Decode, Encode};
use core::marker::PhantomData;
use pezframe_support::traits::Get;
use pezsp_io::hashing::blake2_256;
use pezsp_runtime::traits::{AccountIdConversion, TrailingZeroInput, TryConvert};
use xcm::latest::prelude::*;
use xcm_executor::traits::ConvertLocation;
/// Means of converting a location into a stable and unique descriptive identifier.
pub trait DescribeLocation {
/// Create a description of the given `location` if possible. No two locations should have the
/// same descriptor.
fn describe_location(location: &Location) -> Option>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl DescribeLocation for Tuple {
fn describe_location(l: &Location) -> Option> {
for_tuples!( #(
match Tuple::describe_location(l) {
Some(result) => return Some(result),
None => {},
}
)* );
None
}
}
pub struct DescribeTerminus;
impl DescribeLocation for DescribeTerminus {
fn describe_location(l: &Location) -> Option> {
match l.unpack() {
(0, []) => Some(Vec::new()),
_ => return None,
}
}
}
pub struct DescribePalletTerminal;
impl DescribeLocation for DescribePalletTerminal {
fn describe_location(l: &Location) -> Option> {
match l.unpack() {
(0, [PalletInstance(i)]) => {
Some((b"Pezpallet", Compact::::from(*i as u32)).encode())
},
_ => return None,
}
}
}
pub struct DescribeAccountId32Terminal;
impl DescribeLocation for DescribeAccountId32Terminal {
fn describe_location(l: &Location) -> Option> {
match l.unpack() {
(0, [AccountId32 { id, .. }]) => Some((b"AccountId32", id).encode()),
_ => return None,
}
}
}
pub struct DescribeAccountKey20Terminal;
impl DescribeLocation for DescribeAccountKey20Terminal {
fn describe_location(l: &Location) -> Option> {
match l.unpack() {
(0, [AccountKey20 { key, .. }]) => Some((b"AccountKey20", key).encode()),
_ => return None,
}
}
}
/// Create a description of the remote treasury `location` if possible. No two locations should have
/// the same descriptor.
pub struct DescribeTreasuryVoiceTerminal;
impl DescribeLocation for DescribeTreasuryVoiceTerminal {
fn describe_location(location: &Location) -> Option> {
match location.unpack() {
(0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]) => {
Some((b"Treasury", b"Voice").encode())
},
_ => None,
}
}
}
pub type DescribeAccountIdTerminal = (DescribeAccountId32Terminal, DescribeAccountKey20Terminal);
pub struct DescribeBodyTerminal;
impl DescribeLocation for DescribeBodyTerminal {
fn describe_location(l: &Location) -> Option> {
match l.unpack() {
(0, [Plurality { id, part }]) => Some((b"Body", id, part).encode()),
_ => return None,
}
}
}
pub type DescribeAllTerminal = (
DescribeTerminus,
DescribePalletTerminal,
DescribeAccountId32Terminal,
DescribeAccountKey20Terminal,
DescribeTreasuryVoiceTerminal,
DescribeBodyTerminal,
);
pub struct DescribeFamily(PhantomData);
impl DescribeLocation for DescribeFamily {
fn describe_location(l: &Location) -> Option> {
match (l.parent_count(), l.first_interior()) {
(0, Some(Teyrchain(index))) => {
let tail = l.clone().split_first_interior().0;
let interior = Suffix::describe_location(&tail.into())?;
Some((b"ChildChain", Compact::::from(*index), interior).encode())
},
(1, Some(Teyrchain(index))) => {
let tail_junctions = l.interior().clone().split_first().0;
let tail = Location::new(0, tail_junctions);
let interior = Suffix::describe_location(&tail)?;
Some((b"SiblingChain", Compact::::from(*index), interior).encode())
},
(1, _) => {
let tail = l.interior().clone().into();
let interior = Suffix::describe_location(&tail)?;
Some((b"ParentChain", interior).encode())
},
_ => return None,
}
}
}
pub struct HashedDescription(PhantomData<(AccountId, Describe)>);
impl + Clone, Describe: DescribeLocation> ConvertLocation
for HashedDescription
{
fn convert_location(value: &Location) -> Option {
Some(blake2_256(&Describe::describe_location(value)?).into())
}
}
/// This is a describer for legacy support of the `ForeignChainAliasAccount` preimage. New chains
/// are recommended to use the more extensible `HashedDescription` type.
pub struct LegacyDescribeForeignChainAccount;
impl DescribeLocation for LegacyDescribeForeignChainAccount {
fn describe_location(location: &Location) -> Option> {
Some(match location.unpack() {
// Used on the relay chain for sending paras that use 32 byte accounts
(0, [Teyrchain(para_id), AccountId32 { id, .. }]) => {
LegacyDescribeForeignChainAccount::from_para_32(para_id, id, 0)
},
// Used on the relay chain for sending paras that use 20 byte accounts
(0, [Teyrchain(para_id), AccountKey20 { key, .. }]) => {
LegacyDescribeForeignChainAccount::from_para_20(para_id, key, 0)
},
// Used on para-chain for sending paras that use 32 byte accounts
(1, [Teyrchain(para_id), AccountId32 { id, .. }]) => {
LegacyDescribeForeignChainAccount::from_para_32(para_id, id, 1)
},
// Used on para-chain for sending paras that use 20 byte accounts
(1, [Teyrchain(para_id), AccountKey20 { key, .. }]) => {
LegacyDescribeForeignChainAccount::from_para_20(para_id, key, 1)
},
// Used on para-chain for sending from the relay chain
(1, [AccountId32 { id, .. }]) => {
LegacyDescribeForeignChainAccount::from_relay_32(id, 1)
},
// No other conversions provided
_ => return None,
})
}
}
/// 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";
impl LegacyDescribeForeignChainAccount {
fn from_para_32(para_id: &u32, id: &[u8; 32], parents: u8) -> Vec {
(FOREIGN_CHAIN_PREFIX_PARA_32, para_id, id, parents).encode()
}
fn from_para_20(para_id: &u32, id: &[u8; 20], parents: u8) -> Vec {
(FOREIGN_CHAIN_PREFIX_PARA_20, para_id, id, parents).encode()
}
fn from_relay_32(id: &[u8; 32], parents: u8) -> Vec {
(FOREIGN_CHAIN_PREFIX_RELAY, id, parents).encode()
}
}
/// This is deprecated in favor of the more modular `HashedDescription` converter. If
/// your chain has previously used this, then you can retain backwards compatibility using
/// `HashedDescription` and a tuple with `LegacyDescribeForeignChainAccount` as the first
/// element. For example:
///
/// ```nocompile
/// pub type LocationToAccount = HashedDescription<
/// // Legacy conversion - MUST BE FIRST!
/// LegacyDescribeForeignChainAccount,
/// // Other conversions
/// DescribeTerminus,
/// DescribePalletTerminal,
/// >;
/// ```
///
/// This type is equivalent to the above but without any other conversions.
///
/// ### Old documentation
///
/// 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.
#[deprecated = "Use `HashedDescription` instead"]
pub type ForeignChainAliasAccount =
HashedDescription;
pub struct Account32Hash(PhantomData<(Network, AccountId)>);
impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
ConvertLocation for Account32Hash
{
fn convert_location(location: &Location) -> Option {
Some(("multiloc", location).using_encoded(blake2_256).into())
}
}
/// A [`Location`] consisting of a single `Parent` [`Junction`] will be converted to the
/// parent `AccountId`.
pub struct ParentIsPreset(PhantomData);
impl ConvertLocation for ParentIsPreset {
fn convert_location(location: &Location) -> Option {
if location.contains_parents_only(1) {
Some(
b"Parent"
.using_encoded(|b| AccountId::decode(&mut TrailingZeroInput::new(b)))
.expect("infinite length input; no invalid inputs for type; qed"),
)
} else {
None
}
}
}
pub struct ChildTeyrchainConvertsVia(PhantomData<(ParaId, AccountId)>);
impl + Into + AccountIdConversion, AccountId: Clone>
ConvertLocation for ChildTeyrchainConvertsVia
{
fn convert_location(location: &Location) -> Option {
match location.unpack() {
(0, [Teyrchain(id)]) => Some(ParaId::from(*id).into_account_truncating()),
_ => None,
}
}
}
pub struct SiblingTeyrchainConvertsVia(PhantomData<(ParaId, AccountId)>);
impl + Into + AccountIdConversion, AccountId: Clone>
ConvertLocation for SiblingTeyrchainConvertsVia
{
fn convert_location(location: &Location) -> Option {
match location.unpack() {
(1, [Teyrchain(id)]) => Some(ParaId::from(*id).into_account_truncating()),
_ => None,
}
}
}
/// 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>
ConvertLocation for AccountId32Aliases
{
fn convert_location(location: &Location) -> Option {
let id = match location.unpack() {
(0, [AccountId32 { id, network: None }]) => id,
(0, [AccountId32 { id, network }]) if *network == Network::get() => id,
_ => return None,
};
Some((*id).into())
}
}
/// Returns specified `TreasuryAccount` as `AccountId32` if passed `location` matches Treasury
/// plurality.
pub struct LocalTreasuryVoiceConvertsVia(
PhantomData<(TreasuryAccount, AccountId)>,
);
impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
ConvertLocation for LocalTreasuryVoiceConvertsVia
{
fn convert_location(location: &Location) -> Option {
match location.unpack() {
(0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]) => {
Some((TreasuryAccount::get().into() as [u8; 32]).into())
},
_ => None,
}
}
}
/// Conversion implementation which converts from a `[u8; 32]`-based `AccountId` into a
/// `Location` 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