// 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(())); } }