// 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 sp_std::{prelude::*, result::Result, borrow::Borrow, convert::TryFrom}; use parity_scale_codec::{Encode, Decode}; use xcm::v0::{MultiLocation, OriginKind}; /// Generic third-party conversion trait. Use this when you don't want to force the user to use default /// implementations of `From` and `Into` for the types you wish to convert between. /// /// One of `convert`/`convert_ref` and `reverse`/`reverse_ref` MUST be implemented. If possible, implement /// `convert_ref`, since this will never result in a clone. Use `convert` when you definitely need to consume /// the source value. /// /// Can be amalgamated into tuples. If any of the tuple elements converts into `Ok(_)` it short circuits. Otherwise returns /// the `Err(_)` of the last failing conversion (or `Err(())` for ref conversions). pub trait Convert { /// Convert from `value` (of type `A`) into an equivalent value of type `B`, `Err` if not possible. fn convert(value: A) -> Result { Self::convert_ref(&value).map_err(|_| value) } fn convert_ref(value: impl Borrow) -> Result { Self::convert(value.borrow().clone()).map_err(|_| ()) } /// Convert from `value` (of type `B`) into an equivalent value of type `A`, `Err` if not possible. fn reverse(value: B) -> Result { Self::reverse_ref(&value).map_err(|_| value) } fn reverse_ref(value: impl Borrow) -> Result { Self::reverse(value.borrow().clone()).map_err(|_| ()) } } #[impl_trait_for_tuples::impl_for_tuples(30)] impl Convert for Tuple { fn convert(value: A) -> Result { for_tuples!( #( let value = match Tuple::convert(value) { Ok(result) => return Ok(result), Err(v) => v, }; )* ); Err(value) } fn reverse(value: B) -> Result { for_tuples!( #( let value = match Tuple::reverse(value) { Ok(result) => return Ok(result), Err(v) => v, }; )* ); Err(value) } fn convert_ref(value: impl Borrow) -> Result { let value = value.borrow(); for_tuples!( #( match Tuple::convert_ref(value) { Ok(result) => return Ok(result), Err(_) => (), } )* ); Err(()) } fn reverse_ref(value: impl Borrow) -> Result { let value = value.borrow(); for_tuples!( #( match Tuple::reverse_ref(value.clone()) { Ok(result) => return Ok(result), Err(_) => (), } )* ); Err(()) } } /// Simple pass-through which implements `BytesConversion` while not doing any conversion. pub struct Identity; impl Convert for Identity { fn convert(value: T) -> Result { Ok(value) } fn reverse(value: T) -> Result { Ok(value) } } /// Implementation of `Convert` trait using `TryFrom`. pub struct JustTry; impl + Clone, Dest: TryFrom + Clone> Convert for JustTry { fn convert(value: Source) -> Result { Dest::try_from(value.clone()).map_err(|_| value) } fn reverse(value: Dest) -> Result { Source::try_from(value.clone()).map_err(|_| value) } } /// Implementation of `Convert<_, Vec>` using the parity scale codec. pub struct Encoded; impl Convert> for Encoded { fn convert_ref(value: impl Borrow) -> Result, ()> { Ok(value.borrow().encode()) } fn reverse_ref(bytes: impl Borrow>) -> Result { T::decode(&mut &bytes.borrow()[..]).map_err(|_| ()) } } /// Implementation of `Convert, _>` using the parity scale codec. pub struct Decoded; impl Convert, T> for Decoded { fn convert_ref(bytes: impl Borrow>) -> Result { T::decode(&mut &bytes.borrow()[..]).map_err(|_| ()) } fn reverse_ref(value: impl Borrow) -> Result, ()> { Ok(value.borrow().encode()) } } /// A converter `trait` for origin types. /// /// Can be amalgamated into tuples. If any of the tuple elements returns `Ok(_)`, it short circuits. Else, the `Err(_)` /// of the last tuple item is returned. Each intermediate `Err(_)` might return a different `origin` of type `Origin` /// which is passed to the next convert item. /// /// ```rust /// # use xcm::v0::{MultiLocation, Junction, OriginKind}; /// # use xcm_executor::traits::ConvertOrigin; /// // A convertor that will bump the para id and pass it to the next one. /// struct BumpParaId; /// impl ConvertOrigin for BumpParaId { /// fn convert_origin(origin: MultiLocation, _: OriginKind) -> Result { /// match origin { /// MultiLocation::X1(Junction::Parachain(id)) => { /// Err(MultiLocation::X1(Junction::Parachain(id + 1))) /// } /// _ => unreachable!() /// } /// } /// } /// /// struct AcceptPara7; /// impl ConvertOrigin for AcceptPara7 { /// fn convert_origin(origin: MultiLocation, _: OriginKind) -> Result { /// match origin { /// MultiLocation::X1(Junction::Parachain(id)) if id == 7 => { /// Ok(7) /// } /// _ => Err(origin) /// } /// } /// } /// # fn main() { /// let origin = MultiLocation::X1(Junction::Parachain(6)); /// assert!( /// <(BumpParaId, AcceptPara7) as ConvertOrigin>::convert_origin(origin, OriginKind::Native) /// .is_ok() /// ); /// # } /// ``` pub trait ConvertOrigin { /// Attempt to convert `origin` to the generic `Origin` whilst consuming it. fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl ConvertOrigin for Tuple { fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result { for_tuples!( #( let origin = match Tuple::convert_origin(origin, kind) { Err(o) => o, r => return r }; )* ); log::trace!( target: "xcm::convert_origin", "could not convert: origin: {:?}, kind: {:?}", origin, kind, ); Err(origin) } } /// Means of inverting a location: given a location which describes a `target` interpreted from the /// `source`, this will provide the corresponding location which describes the `source`. pub trait InvertLocation { fn invert_location(l: &MultiLocation) -> MultiLocation; }