// 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 .
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;
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())
}
}
pub struct AccountKey20Aliases(PhantomData<(Network, AccountId)>);
impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone>
Convert for AccountKey20Aliases
{
fn convert(location: MultiLocation) -> Result {
let key = match location {
MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network: None }) } => key,
MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network }) }
if network == Network::get() =>
key,
_ => return Err(location),
};
Ok(key.into())
}
fn reverse(who: AccountId) -> Result {
let j = AccountKey20 { key: who.into(), network: Network::get() };
Ok(j.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::parameter_types;
use xcm::latest::Junction;
fn account20() -> Junction {
AccountKey20 { network: None, key: Default::default() }
}
fn account32() -> Junction {
AccountId32 { network: None, id: Default::default() }
}
// Network Topology
// v Source
// Relay -> Para 1 -> SmartContract -> Account
// -> Para 2 -> Account
// ^ Target
//
// Inputs and outputs written as file paths:
//
// input location (source to target): ../../../para_2/account32_default
// context (root to source): para_1/account20_default/account20_default
// =>
// output (target to source): ../../para_1/account20_default/account20_default
#[test]
fn inverter_works_in_tree() {
parameter_types! {
pub UniversalLocation: InteriorMultiLocation = X3(Parachain(1), account20(), account20());
}
let input = MultiLocation::new(3, X2(Parachain(2), account32()));
let inverted = UniversalLocation::get().invert_target(&input).unwrap();
assert_eq!(inverted, MultiLocation::new(2, X3(Parachain(1), account20(), account20())));
}
// Network Topology
// v Source
// Relay -> Para 1 -> SmartContract -> Account
// ^ Target
#[test]
fn inverter_uses_context_as_inverted_location() {
parameter_types! {
pub UniversalLocation: InteriorMultiLocation = X2(account20(), account20());
}
let input = MultiLocation::grandparent();
let inverted = UniversalLocation::get().invert_target(&input).unwrap();
assert_eq!(inverted, X2(account20(), account20()).into());
}
// Network Topology
// v Source
// Relay -> Para 1 -> CollectivePallet -> Plurality
// ^ Target
#[test]
fn inverter_uses_only_child_on_missing_context() {
parameter_types! {
pub UniversalLocation: InteriorMultiLocation = PalletInstance(5).into();
}
let input = MultiLocation::grandparent();
let inverted = UniversalLocation::get().invert_target(&input).unwrap();
assert_eq!(inverted, (OnlyChild, PalletInstance(5)).into());
}
#[test]
fn inverter_errors_when_location_is_too_large() {
parameter_types! {
pub UniversalLocation: InteriorMultiLocation = Here;
}
let input = MultiLocation { parents: 99, interior: X1(Parachain(88)) };
let inverted = UniversalLocation::get().invert_target(&input);
assert_eq!(inverted, Err(()));
}
}