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