mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 10:31:03 +00:00
XCM revamp (#2836)
* Remove unused relaying XCM * Aggregate HRMP (XCMP/HMP) messages. Payloads for spambot. * Revert lock * Fix * Broken example * Introduce fee payment mechanics into XCM. * Weight limitations on XCM execution * Mock environment for tests and the first test * Tests for XCM and a few refactors. * Remove code that's not ready * Fix for an XCM and an additional test * Query response system * XCMP message dispatch system reimagining - Moved most of the logic into xcm-handler pallet - Altered the outgoing XCMP API from push to pull - Changed underlying outgoing queue data structures to avoid multi-page read/writes - Introduced queuing for incoming messages - Introduced signal messages as a flow-control sub-stream - Introduced flow-control with basic threshold back-pressure - Introduced overall weight limitation on messages executed - Additonal alterations to XCM APIs for the new system * Some build fixes * Remove the Encode bounds sprayed around * More faff * Fix bounds amek use latest scale codec. * remove println * fixes * Fix XcmExecutor Tests * Fix XCM bounds using derivative crate * Refactor names of XcmGeneric &c into Xcm * Repot the xcm-executor into xcm-builder * Docs * Docs * Fixes * Update xcm/src/lib.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Fixes * Docs * Update runtime/parachains/src/ump.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Docs * Fixes * Fixes * Fixes * Docs * Fixes * Fixes * Introduce transfer_asset specialisation. * Fixes * Fixes Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::{result::Result, marker::PhantomData};
|
||||
use xcm::v0::{Xcm, Order, MultiLocation};
|
||||
use frame_support::{ensure, traits::Contains, weights::Weight};
|
||||
use xcm_executor::traits::{OnResponse, ShouldExecute};
|
||||
|
||||
pub struct TakeWeightCredit;
|
||||
impl ShouldExecute for TakeWeightCredit {
|
||||
fn should_execute<Call>(
|
||||
_origin: &MultiLocation,
|
||||
_top_level: bool,
|
||||
_message: &Xcm<Call>,
|
||||
shallow_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
*weight_credit = weight_credit.checked_sub(shallow_weight).ok_or(())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
|
||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
top_level: bool,
|
||||
message: &Xcm<Call>,
|
||||
shallow_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
ensure!(T::contains(origin), ());
|
||||
ensure!(top_level, ());
|
||||
match message {
|
||||
Xcm::TeleportAsset { effects, .. }
|
||||
| Xcm::WithdrawAsset { effects, ..}
|
||||
| Xcm::ReserveAssetDeposit { effects, ..}
|
||||
if matches!(
|
||||
effects.first(),
|
||||
Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight
|
||||
)
|
||||
=> Ok(()),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
|
||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
_top_level: bool,
|
||||
_message: &Xcm<Call>,
|
||||
_shallow_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
ensure!(T::contains(origin), ());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
|
||||
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
_top_level: bool,
|
||||
message: &Xcm<Call>,
|
||||
_shallow_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
match message {
|
||||
Xcm::QueryResponse { query_id, .. } if ResponseHandler::expecting_response(origin, *query_id)
|
||||
=> Ok(()),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,8 @@ use sp_std::{result, convert::TryInto, marker::PhantomData};
|
||||
use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation};
|
||||
use sp_runtime::traits::SaturatedConversion;
|
||||
use frame_support::traits::{ExistenceRequirement::AllowDeath, WithdrawReasons};
|
||||
use xcm_executor::traits::{MatchesFungible, LocationConversion, TransactAsset};
|
||||
use xcm_executor::traits::{MatchesFungible, Convert, TransactAsset};
|
||||
use xcm_executor::Assets;
|
||||
|
||||
/// Asset transaction errors.
|
||||
enum Error {
|
||||
@@ -48,9 +49,9 @@ pub struct CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId>(
|
||||
|
||||
impl<
|
||||
Matcher: MatchesFungible<Currency::Balance>,
|
||||
AccountIdConverter: LocationConversion<AccountId>,
|
||||
AccountIdConverter: Convert<MultiLocation, AccountId>,
|
||||
Currency: frame_support::traits::Currency<AccountId>,
|
||||
AccountId, // can't get away without it since Currency is generic over it.
|
||||
AccountId: Clone, // can't get away without it since Currency is generic over it.
|
||||
> TransactAsset for CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId> {
|
||||
|
||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
|
||||
@@ -58,8 +59,8 @@ impl<
|
||||
let amount: u128 = Matcher::matches_fungible(&what)
|
||||
.ok_or(Error::AssetNotFound)?
|
||||
.saturated_into();
|
||||
let who = AccountIdConverter::from_location(who)
|
||||
.ok_or(Error::AccountIdConversionFailed)?;
|
||||
let who = AccountIdConverter::convert_ref(who)
|
||||
.map_err(|()| Error::AccountIdConversionFailed)?;
|
||||
let balance_amount = amount
|
||||
.try_into()
|
||||
.map_err(|_| Error::AmountToBalanceConversionFailed)?;
|
||||
@@ -70,18 +71,18 @@ impl<
|
||||
fn withdraw_asset(
|
||||
what: &MultiAsset,
|
||||
who: &MultiLocation
|
||||
) -> result::Result<MultiAsset, XcmError> {
|
||||
) -> result::Result<Assets, XcmError> {
|
||||
// Check we handle this asset.
|
||||
let amount: u128 = Matcher::matches_fungible(what)
|
||||
.ok_or(Error::AssetNotFound)?
|
||||
.saturated_into();
|
||||
let who = AccountIdConverter::from_location(who)
|
||||
.ok_or(Error::AccountIdConversionFailed)?;
|
||||
let who = AccountIdConverter::convert_ref(who)
|
||||
.map_err(|()| Error::AccountIdConversionFailed)?;
|
||||
let balance_amount = amount
|
||||
.try_into()
|
||||
.map_err(|_| Error::AmountToBalanceConversionFailed)?;
|
||||
Currency::withdraw(&who, balance_amount, WithdrawReasons::TRANSFER, AllowDeath)
|
||||
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
|
||||
Ok(what.clone())
|
||||
Ok(what.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::marker::PhantomData;
|
||||
use xcm::v0::{MultiAsset, MultiLocation};
|
||||
use frame_support::traits::Get;
|
||||
use xcm_executor::traits::FilterAssetLocation;
|
||||
|
||||
pub struct NativeAsset;
|
||||
impl FilterAssetLocation for NativeAsset {
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
matches!(asset, MultiAsset::ConcreteFungible { ref id, .. } if id == origin)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Case<T>(PhantomData<T>);
|
||||
impl<T: Get<(MultiAsset, MultiLocation)>> FilterAssetLocation for Case<T> {
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
let (a, o) = T::get();
|
||||
&a == asset && &o == origin
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::{prelude::*, result, convert::TryFrom, marker::PhantomData, borrow::Borrow};
|
||||
use sp_std::{prelude::*, result, marker::PhantomData, borrow::Borrow};
|
||||
use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation, Junction};
|
||||
use frame_support::traits::{Get, tokens::fungibles::Mutate as Fungibles};
|
||||
use xcm_executor::traits::{LocationConversion, TransactAsset};
|
||||
use frame_support::traits::{Get, tokens::fungibles};
|
||||
use xcm_executor::traits::{TransactAsset, Convert};
|
||||
|
||||
/// Asset transaction errors.
|
||||
pub enum Error {
|
||||
@@ -45,104 +45,6 @@ impl From<Error> for XcmError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic third-party conversion trait. Use this when you don't want to force the user to use default
|
||||
/// impls 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.
|
||||
pub trait Convert<A: Clone, B: Clone> {
|
||||
/// Convert from `value` (of type `A`) into an equivalent value of type `B`, `Err` if not possible.
|
||||
fn convert(value: A) -> result::Result<B, A> { Self::convert_ref(&value).map_err(|_| value) }
|
||||
fn convert_ref(value: impl Borrow<A>) -> result::Result<B, ()> {
|
||||
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::Result<A, B> { Self::reverse_ref(&value).map_err(|_| value) }
|
||||
fn reverse_ref(value: impl Borrow<B>) -> result::Result<A, ()> {
|
||||
Self::reverse(value.borrow().clone()).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl<A: Clone, B: Clone> Convert<A, B> for Tuple {
|
||||
fn convert(value: A) -> result::Result<B, A> {
|
||||
for_tuples!( #(
|
||||
let value = match Tuple::convert(value) {
|
||||
Ok(result) => return Ok(result),
|
||||
Err(v) => v,
|
||||
};
|
||||
)* );
|
||||
Err(value)
|
||||
}
|
||||
fn reverse(value: B) -> result::Result<A, B> {
|
||||
for_tuples!( #(
|
||||
let value = match Tuple::reverse(value) {
|
||||
Ok(result) => return Ok(result),
|
||||
Err(v) => v,
|
||||
};
|
||||
)* );
|
||||
Err(value)
|
||||
}
|
||||
fn convert_ref(value: impl Borrow<A>) -> result::Result<B, ()> {
|
||||
let value = value.borrow();
|
||||
for_tuples!( #(
|
||||
match Tuple::convert_ref(value) {
|
||||
Ok(result) => return Ok(result),
|
||||
Err(_) => (),
|
||||
}
|
||||
)* );
|
||||
Err(())
|
||||
}
|
||||
fn reverse_ref(value: impl Borrow<B>) -> result::Result<A, ()> {
|
||||
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<T: Clone> Convert<T, T> for Identity {
|
||||
fn convert(value: T) -> result::Result<T, T> { Ok(value) }
|
||||
fn reverse(value: T) -> result::Result<T, T> { Ok(value) }
|
||||
}
|
||||
|
||||
/// Implementation of `Convert` trait using `TryFrom`.
|
||||
pub struct JustTry;
|
||||
impl<Source: TryFrom<Dest> + Clone, Dest: TryFrom<Source> + Clone> Convert<Source, Dest> for JustTry {
|
||||
fn convert(value: Source) -> result::Result<Dest, Source> {
|
||||
Dest::try_from(value.clone()).map_err(|_| value)
|
||||
}
|
||||
fn reverse(value: Dest) -> result::Result<Source, Dest> {
|
||||
Source::try_from(value.clone()).map_err(|_| value)
|
||||
}
|
||||
}
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
/// Implementation of `Convert<_, Vec<u8>>` using the parity scale codec.
|
||||
pub struct Encoded;
|
||||
impl<T: Clone + Encode + Decode> Convert<T, Vec<u8>> for Encoded {
|
||||
fn convert_ref(value: impl Borrow<T>) -> result::Result<Vec<u8>, ()> { Ok(value.borrow().encode()) }
|
||||
fn reverse_ref(bytes: impl Borrow<Vec<u8>>) -> result::Result<T, ()> {
|
||||
T::decode(&mut &bytes.borrow()[..]).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `Convert<Vec<u8>, _>` using the parity scale codec.
|
||||
pub struct Decoded;
|
||||
impl<T: Clone + Encode + Decode> Convert<Vec<u8>, T> for Decoded {
|
||||
fn convert_ref(bytes: impl Borrow<Vec<u8>>) -> result::Result<T, ()> {
|
||||
T::decode(&mut &bytes.borrow()[..]).map_err(|_| ())
|
||||
}
|
||||
fn reverse_ref(value: impl Borrow<T>) -> result::Result<Vec<u8>, ()> { Ok(value.borrow().encode()) }
|
||||
}
|
||||
|
||||
/// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be TryFrom/TryInto<u128>)
|
||||
/// into a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will
|
||||
/// typically be a `PalletInstance` junction.
|
||||
@@ -232,22 +134,47 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FungiblesAdapter<Assets, Matcher, AccountIdConverter, AccountId>(
|
||||
pub struct FungiblesTransferAdapter<Assets, Matcher, AccountIdConverter, AccountId>(
|
||||
PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>
|
||||
);
|
||||
|
||||
impl<
|
||||
Assets: Fungibles<AccountId>,
|
||||
Assets: fungibles::Transfer<AccountId>,
|
||||
Matcher: MatchesFungibles<Assets::AssetId, Assets::Balance>,
|
||||
AccountIdConverter: LocationConversion<AccountId>,
|
||||
AccountId, // can't get away without it since Currency is generic over it.
|
||||
> TransactAsset for FungiblesAdapter<Assets, Matcher, AccountIdConverter, AccountId> {
|
||||
AccountIdConverter: Convert<MultiLocation, AccountId>,
|
||||
AccountId: Clone, // can't get away without it since Currency is generic over it.
|
||||
> TransactAsset for FungiblesTransferAdapter<Assets, Matcher, AccountIdConverter, AccountId> {
|
||||
fn transfer_asset(
|
||||
what: &MultiAsset,
|
||||
from: &MultiLocation,
|
||||
to: &MultiLocation,
|
||||
) -> result::Result<xcm_executor::Assets, XcmError> {
|
||||
// Check we handle this asset.
|
||||
let (asset_id, amount) = Matcher::matches_fungibles(what)?;
|
||||
let source = AccountIdConverter::convert_ref(from)
|
||||
.map_err(|()| Error::AccountIdConversionFailed)?;
|
||||
let dest = AccountIdConverter::convert_ref(to)
|
||||
.map_err(|()| Error::AccountIdConversionFailed)?;
|
||||
Assets::transfer(asset_id, &source, &dest, amount, true)
|
||||
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
|
||||
Ok(what.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FungiblesMutateAdapter<Assets, Matcher, AccountIdConverter, AccountId>(
|
||||
PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>
|
||||
);
|
||||
impl<
|
||||
Assets: fungibles::Mutate<AccountId>,
|
||||
Matcher: MatchesFungibles<Assets::AssetId, Assets::Balance>,
|
||||
AccountIdConverter: Convert<MultiLocation, AccountId>,
|
||||
AccountId: Clone, // can't get away without it since Currency is generic over it.
|
||||
> TransactAsset for FungiblesMutateAdapter<Assets, Matcher, AccountIdConverter, AccountId> {
|
||||
|
||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
|
||||
// Check we handle this asset.
|
||||
let (asset_id, amount) = Matcher::matches_fungibles(what)?;
|
||||
let who = AccountIdConverter::from_location(who)
|
||||
.ok_or(Error::AccountIdConversionFailed)?;
|
||||
let who = AccountIdConverter::convert_ref(who)
|
||||
.map_err(|()| Error::AccountIdConversionFailed)?;
|
||||
Assets::mint_into(asset_id, &who, amount)
|
||||
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))
|
||||
}
|
||||
@@ -255,13 +182,43 @@ impl<
|
||||
fn withdraw_asset(
|
||||
what: &MultiAsset,
|
||||
who: &MultiLocation
|
||||
) -> result::Result<MultiAsset, XcmError> {
|
||||
) -> result::Result<xcm_executor::Assets, XcmError> {
|
||||
// Check we handle this asset.
|
||||
let (asset_id, amount) = Matcher::matches_fungibles(what)?;
|
||||
let who = AccountIdConverter::from_location(who)
|
||||
.ok_or(Error::AccountIdConversionFailed)?;
|
||||
let who = AccountIdConverter::convert_ref(who)
|
||||
.map_err(|()| Error::AccountIdConversionFailed)?;
|
||||
Assets::burn_from(asset_id, &who, amount)
|
||||
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
|
||||
Ok(what.clone())
|
||||
Ok(what.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FungiblesAdapter<Assets, Matcher, AccountIdConverter, AccountId>(
|
||||
PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>
|
||||
);
|
||||
impl<
|
||||
Assets: fungibles::Mutate<AccountId> + fungibles::Transfer<AccountId>,
|
||||
Matcher: MatchesFungibles<Assets::AssetId, Assets::Balance>,
|
||||
AccountIdConverter: Convert<MultiLocation, AccountId>,
|
||||
AccountId: Clone, // can't get away without it since Currency is generic over it.
|
||||
> TransactAsset for FungiblesAdapter<Assets, Matcher, AccountIdConverter, AccountId> {
|
||||
|
||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
|
||||
FungiblesMutateAdapter::<Assets, Matcher, AccountIdConverter, AccountId>::deposit_asset(what, who)
|
||||
}
|
||||
|
||||
fn withdraw_asset(
|
||||
what: &MultiAsset,
|
||||
who: &MultiLocation
|
||||
) -> result::Result<xcm_executor::Assets, XcmError> {
|
||||
FungiblesMutateAdapter::<Assets, Matcher, AccountIdConverter, AccountId>::withdraw_asset(what, who)
|
||||
}
|
||||
|
||||
fn transfer_asset(
|
||||
what: &MultiAsset,
|
||||
from: &MultiLocation,
|
||||
to: &MultiLocation,
|
||||
) -> result::Result<xcm_executor::Assets, XcmError> {
|
||||
FungiblesTransferAdapter::<Assets, Matcher, AccountIdConverter, AccountId>::transfer_asset(what, from, to)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,43 +16,40 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod location_conversion;
|
||||
pub use location_conversion::{
|
||||
Account32Hash, ParentIsDefault, ChildParachainConvertsVia, SiblingParachainConvertsVia, AccountId32Aliases, AccountKey20Aliases,
|
||||
Account32Hash, ParentIsDefault, ChildParachainConvertsVia, SiblingParachainConvertsVia, AccountId32Aliases,
|
||||
AccountKey20Aliases, LocationInverter,
|
||||
};
|
||||
|
||||
mod origin_conversion;
|
||||
pub use origin_conversion::{
|
||||
SovereignSignedViaLocation, ParentAsSuperuser, ChildSystemParachainAsSuperuser, SiblingSystemParachainAsSuperuser,
|
||||
ChildParachainAsNative, SiblingParachainAsNative, RelayChainAsNative, SignedAccountId32AsNative, SignedAccountKey20AsNative,
|
||||
ChildParachainAsNative, SiblingParachainAsNative, RelayChainAsNative, SignedAccountId32AsNative,
|
||||
SignedAccountKey20AsNative,
|
||||
};
|
||||
|
||||
mod barriers;
|
||||
pub use barriers::{
|
||||
TakeWeightCredit, AllowUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, AllowKnownQueryResponses,
|
||||
};
|
||||
|
||||
mod currency_adapter;
|
||||
mod fungibles_adapter;
|
||||
pub use currency_adapter::CurrencyAdapter;
|
||||
|
||||
mod fungibles_adapter;
|
||||
pub use fungibles_adapter::FungiblesAdapter;
|
||||
|
||||
use sp_std::marker::PhantomData;
|
||||
use xcm_executor::traits::InvertLocation;
|
||||
use xcm::v0::{MultiLocation, Junction};
|
||||
use frame_support::traits::Get;
|
||||
mod weight;
|
||||
pub use weight::{FixedRateOfConcreteFungible, FixedWeightBounds};
|
||||
|
||||
/// Simple location inverter; give it this location's ancestry and it'll
|
||||
pub struct LocationInverter<Ancestry>(PhantomData<Ancestry>);
|
||||
mod matches_fungible;
|
||||
pub use matches_fungible::{IsAbstract, IsConcrete};
|
||||
|
||||
impl<Ancestry: Get<MultiLocation>> InvertLocation for LocationInverter<Ancestry> {
|
||||
fn invert_location(location: &MultiLocation) -> MultiLocation {
|
||||
let mut ancestry = Ancestry::get();
|
||||
let mut result = location.clone();
|
||||
for (i, j) in location.iter_rev()
|
||||
.map(|j| match j {
|
||||
Junction::Parent => ancestry.take_first().unwrap_or(Junction::OnlyChild),
|
||||
_ => Junction::Parent,
|
||||
})
|
||||
.enumerate()
|
||||
{
|
||||
*result.at_mut(i).expect("location and result begin equal; same size; qed") = j;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
mod filter_asset_location;
|
||||
pub use filter_asset_location::{Case, NativeAsset};
|
||||
|
||||
@@ -14,70 +14,68 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::marker::PhantomData;
|
||||
use sp_std::{marker::PhantomData, borrow::Borrow};
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::traits::AccountIdConversion;
|
||||
use frame_support::traits::Get;
|
||||
use parity_scale_codec::Encode;
|
||||
use xcm::v0::{MultiLocation, NetworkId, Junction};
|
||||
use xcm_executor::traits::LocationConversion;
|
||||
use xcm_executor::traits::{InvertLocation, Convert};
|
||||
|
||||
pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>);
|
||||
|
||||
impl<
|
||||
Network: Get<NetworkId>,
|
||||
AccountId: From<[u8; 32]> + Into<[u8; 32]>,
|
||||
> LocationConversion<AccountId> for Account32Hash<Network, AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
Some(("multiloc", location).using_encoded(blake2_256).into())
|
||||
AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone,
|
||||
> Convert<MultiLocation, AccountId> for Account32Hash<Network, AccountId> {
|
||||
fn convert_ref(location: impl Borrow<MultiLocation>) -> Result<AccountId, ()> {
|
||||
Ok(("multiloc", location.borrow()).using_encoded(blake2_256).into())
|
||||
}
|
||||
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
Err(who)
|
||||
fn reverse_ref(_: impl Borrow<AccountId>) -> Result<MultiLocation, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ParentIsDefault<AccountId>(PhantomData<AccountId>);
|
||||
|
||||
impl<
|
||||
AccountId: Default + Eq,
|
||||
> LocationConversion<AccountId> for ParentIsDefault<AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
if let MultiLocation::X1(Junction::Parent) = location {
|
||||
Some(AccountId::default())
|
||||
AccountId: Default + Eq + Clone,
|
||||
> Convert<MultiLocation, AccountId> for ParentIsDefault<AccountId> {
|
||||
fn convert_ref(location: impl Borrow<MultiLocation>) -> Result<AccountId, ()> {
|
||||
if let &MultiLocation::X1(Junction::Parent) = location.borrow() {
|
||||
Ok(AccountId::default())
|
||||
} else {
|
||||
None
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
if who == AccountId::default() {
|
||||
fn reverse_ref(who: impl Borrow<AccountId>) -> Result<MultiLocation, ()> {
|
||||
if who.borrow() == &AccountId::default() {
|
||||
Ok(Junction::Parent.into())
|
||||
} else {
|
||||
Err(who)
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChildParachainConvertsVia<ParaId, AccountId>(PhantomData<(ParaId, AccountId)>);
|
||||
|
||||
impl<
|
||||
ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>,
|
||||
AccountId,
|
||||
> LocationConversion<AccountId> for ChildParachainConvertsVia<ParaId, AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
if let MultiLocation::X1(Junction::Parachain { id }) = location {
|
||||
Some(ParaId::from(*id).into_account())
|
||||
AccountId: Clone,
|
||||
> Convert<MultiLocation, AccountId> for ChildParachainConvertsVia<ParaId, AccountId> {
|
||||
fn convert_ref(location: impl Borrow<MultiLocation>) -> Result<AccountId, ()> {
|
||||
if let &MultiLocation::X1(Junction::Parachain { id }) = location.borrow() {
|
||||
Ok(ParaId::from(id).into_account())
|
||||
} else {
|
||||
None
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
if let Some(id) = ParaId::try_from_account(&who) {
|
||||
fn reverse_ref(who: impl Borrow<AccountId>) -> Result<MultiLocation, ()> {
|
||||
if let Some(id) = ParaId::try_from_account(who.borrow()) {
|
||||
Ok(Junction::Parachain { id: id.into() }.into())
|
||||
} else {
|
||||
Err(who)
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,65 +84,79 @@ pub struct SiblingParachainConvertsVia<ParaId, AccountId>(PhantomData<(ParaId, A
|
||||
|
||||
impl<
|
||||
ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>,
|
||||
AccountId,
|
||||
> LocationConversion<AccountId> for SiblingParachainConvertsVia<ParaId, AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
if let MultiLocation::X2(Junction::Parent, Junction::Parachain { id }) = location {
|
||||
Some(ParaId::from(*id).into_account())
|
||||
AccountId: Clone,
|
||||
> Convert<MultiLocation, AccountId> for SiblingParachainConvertsVia<ParaId, AccountId> {
|
||||
fn convert_ref(location: impl Borrow<MultiLocation>) -> Result<AccountId, ()> {
|
||||
if let &MultiLocation::X2(Junction::Parent, Junction::Parachain { id }) = location.borrow() {
|
||||
Ok(ParaId::from(id).into_account())
|
||||
} else {
|
||||
None
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
if let Some(id) = ParaId::try_from_account(&who) {
|
||||
fn reverse_ref(who: impl Borrow<AccountId>) -> Result<MultiLocation, ()> {
|
||||
if let Some(id) = ParaId::try_from_account(who.borrow()) {
|
||||
Ok([Junction::Parent, Junction::Parachain { id: id.into() }].into())
|
||||
} else {
|
||||
Err(who)
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccountId32Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
|
||||
|
||||
impl<
|
||||
Network: Get<NetworkId>,
|
||||
AccountId: From<[u8; 32]> + Into<[u8; 32]>,
|
||||
> LocationConversion<AccountId> for AccountId32Aliases<Network, AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
if let MultiLocation::X1(Junction::AccountId32 { id, network }) = location {
|
||||
if matches!(network, NetworkId::Any) || network == &Network::get() {
|
||||
return Some((*id).into())
|
||||
}
|
||||
}
|
||||
None
|
||||
AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone,
|
||||
> Convert<MultiLocation, AccountId> for AccountId32Aliases<Network, AccountId> {
|
||||
fn convert(location: MultiLocation) -> Result<AccountId, MultiLocation> {
|
||||
let id = match location {
|
||||
MultiLocation::X1(Junction::AccountId32 { id, network: NetworkId::Any }) => id,
|
||||
MultiLocation::X1(Junction::AccountId32 { id, network }) if &network == &Network::get() => id,
|
||||
l => return Err(l),
|
||||
};
|
||||
Ok(id.into())
|
||||
}
|
||||
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
fn reverse(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
Ok(Junction::AccountId32 { id: who.into(), network: Network::get() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccountKey20Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
|
||||
|
||||
impl<
|
||||
Network: Get<NetworkId>,
|
||||
AccountId: From<[u8; 20]> + Into<[u8; 20]>
|
||||
> LocationConversion<AccountId> for AccountKey20Aliases<Network, AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
if let MultiLocation::X1(Junction::AccountKey20 { key, network }) = location {
|
||||
if matches!(network, NetworkId::Any) || network == &Network::get() {
|
||||
return Some((*key).into());
|
||||
}
|
||||
}
|
||||
None
|
||||
AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone,
|
||||
> Convert<MultiLocation, AccountId> for AccountKey20Aliases<Network, AccountId> {
|
||||
fn convert(location: MultiLocation) -> Result<AccountId, MultiLocation> {
|
||||
let key = match location {
|
||||
MultiLocation::X1(Junction::AccountKey20 { key, network: NetworkId::Any }) => key,
|
||||
MultiLocation::X1(Junction::AccountKey20 { key, network }) if &network == &Network::get() => key,
|
||||
l => return Err(l),
|
||||
};
|
||||
Ok(key.into())
|
||||
}
|
||||
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
Ok(Junction::AccountKey20 {
|
||||
key: who.into(),
|
||||
network: Network::get(),
|
||||
}
|
||||
.into())
|
||||
fn reverse(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
let j = Junction::AccountKey20 { key: who.into(), network: Network::get() };
|
||||
Ok(j.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple location inverter; give it this location's ancestry and it'll figure out the inverted location.
|
||||
pub struct LocationInverter<Ancestry>(PhantomData<Ancestry>);
|
||||
impl<Ancestry: Get<MultiLocation>> InvertLocation for LocationInverter<Ancestry> {
|
||||
fn invert_location(location: &MultiLocation) -> MultiLocation {
|
||||
let mut ancestry = Ancestry::get();
|
||||
let mut result = location.clone();
|
||||
for (i, j) in location.iter_rev()
|
||||
.map(|j| match j {
|
||||
Junction::Parent => ancestry.take_first().unwrap_or(Junction::OnlyChild),
|
||||
_ => Junction::Parent,
|
||||
})
|
||||
.enumerate()
|
||||
{
|
||||
*result.at_mut(i).expect("location and result begin equal; same size; qed") = j;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::{marker::PhantomData, convert::TryFrom};
|
||||
use sp_runtime::traits::CheckedConversion;
|
||||
use xcm::v0::{MultiAsset, MultiLocation};
|
||||
use frame_support::traits::Get;
|
||||
use xcm_executor::traits::MatchesFungible;
|
||||
|
||||
pub struct IsConcrete<T>(PhantomData<T>);
|
||||
impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> {
|
||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||
match a {
|
||||
MultiAsset::ConcreteFungible { id, amount } if id == &T::get() =>
|
||||
CheckedConversion::checked_from(*amount),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct IsAbstract<T>(PhantomData<T>);
|
||||
impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> {
|
||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||
match a {
|
||||
MultiAsset::AbstractFungible { id, amount } if &id[..] == T::get() =>
|
||||
CheckedConversion::checked_from(*amount),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use sp_std::{fmt::Debug, marker::PhantomData, cell::RefCell};
|
||||
pub use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
|
||||
pub use parity_scale_codec::{Encode, Decode};
|
||||
pub use xcm::v0::{
|
||||
SendXcm, MultiLocation::*, Junction::*, MultiAsset, Xcm, Order, Result as XcmResult, Error as XcmError,
|
||||
OriginKind, MultiLocation, Junction, opaque,
|
||||
};
|
||||
pub use frame_support::{
|
||||
ensure, parameter_types,
|
||||
dispatch::{Dispatchable, Parameter, Weight, DispatchError, DispatchResultWithPostInfo, DispatchInfo},
|
||||
weights::{PostDispatchInfo, GetDispatchInfo},
|
||||
sp_runtime::DispatchErrorWithPostInfo,
|
||||
traits::{Get, Contains},
|
||||
};
|
||||
pub use xcm_executor::{
|
||||
Assets, Config, traits::{TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse}
|
||||
};
|
||||
pub use crate::{
|
||||
TakeWeightCredit, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, FixedWeightBounds,
|
||||
FixedRateOfConcreteFungible, AllowKnownQueryResponses, LocationInverter,
|
||||
};
|
||||
|
||||
pub enum TestOrigin { Root, Relay, Signed(u64), Parachain(u32) }
|
||||
|
||||
#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum TestCall {
|
||||
OnlyRoot(Weight, Option<Weight>),
|
||||
OnlyParachain(Weight, Option<Weight>, Option<u32>),
|
||||
OnlySigned(Weight, Option<Weight>, Option<u64>),
|
||||
Any(Weight, Option<Weight>),
|
||||
}
|
||||
impl Dispatchable for TestCall {
|
||||
type Origin = TestOrigin;
|
||||
type Config = ();
|
||||
type Info = ();
|
||||
type PostInfo = PostDispatchInfo;
|
||||
fn dispatch(self, origin: Self::Origin) -> DispatchResultWithPostInfo {
|
||||
let mut post_info = PostDispatchInfo::default();
|
||||
post_info.actual_weight = match self {
|
||||
TestCall::OnlyRoot(_, maybe_actual)
|
||||
| TestCall::OnlySigned(_, maybe_actual, _)
|
||||
| TestCall::OnlyParachain(_, maybe_actual, _)
|
||||
| TestCall::Any(_, maybe_actual)
|
||||
=> maybe_actual,
|
||||
};
|
||||
if match (&origin, &self) {
|
||||
(TestOrigin::Parachain(i), TestCall::OnlyParachain(_, _, Some(j)))
|
||||
=> i == j,
|
||||
(TestOrigin::Signed(i), TestCall::OnlySigned(_, _, Some(j)))
|
||||
=> i == j,
|
||||
|
||||
(TestOrigin::Root, TestCall::OnlyRoot(..))
|
||||
| (TestOrigin::Parachain(_), TestCall::OnlyParachain(_, _, None))
|
||||
| (TestOrigin::Signed(_), TestCall::OnlySigned(_, _, None))
|
||||
| (_, TestCall::Any(..))
|
||||
=> true,
|
||||
|
||||
_ => false,
|
||||
} {
|
||||
Ok(post_info)
|
||||
} else {
|
||||
Err(DispatchErrorWithPostInfo { error: DispatchError::BadOrigin, post_info })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetDispatchInfo for TestCall {
|
||||
fn get_dispatch_info(&self) -> DispatchInfo {
|
||||
let weight = *match self {
|
||||
TestCall::OnlyRoot(estimate, ..)
|
||||
| TestCall::OnlyParachain(estimate, ..)
|
||||
| TestCall::OnlySigned(estimate, ..)
|
||||
| TestCall::Any(estimate, ..)
|
||||
=> estimate,
|
||||
};
|
||||
DispatchInfo { weight, .. Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static SENT_XCM: RefCell<Vec<(MultiLocation, opaque::Xcm)>> = RefCell::new(Vec::new());
|
||||
}
|
||||
pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> {
|
||||
SENT_XCM.with(|q| (*q.borrow()).clone())
|
||||
}
|
||||
pub struct TestSendXcm;
|
||||
impl SendXcm for TestSendXcm {
|
||||
fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> XcmResult {
|
||||
SENT_XCM.with(|q| q.borrow_mut().push((dest, msg)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static ASSETS: RefCell<BTreeMap<u64, Assets>> = RefCell::new(BTreeMap::new());
|
||||
}
|
||||
pub fn assets(who: u64) -> Vec<MultiAsset> {
|
||||
ASSETS.with(|a| a.borrow().get(&who).map_or(vec![], |a| a.clone().into()))
|
||||
}
|
||||
pub fn add_asset(who: u64, what: MultiAsset) {
|
||||
ASSETS.with(|a| a.borrow_mut()
|
||||
.entry(who)
|
||||
.or_insert(Assets::new())
|
||||
.saturating_subsume(what)
|
||||
);
|
||||
}
|
||||
|
||||
pub struct TestAssetTransactor;
|
||||
impl TransactAsset for TestAssetTransactor {
|
||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result<(), XcmError> {
|
||||
let who = to_account(who.clone()).map_err(|_| XcmError::LocationCannotHold)?;
|
||||
add_asset(who, what.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result<Assets, XcmError> {
|
||||
let who = to_account(who.clone()).map_err(|_| XcmError::LocationCannotHold)?;
|
||||
ASSETS.with(|a| a.borrow_mut()
|
||||
.get_mut(&who)
|
||||
.ok_or(XcmError::NotWithdrawable)?
|
||||
.try_take(what.clone())
|
||||
.map_err(|()| XcmError::NotWithdrawable)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn to_account(l: MultiLocation) -> Result<u64, MultiLocation> {
|
||||
Ok(match l {
|
||||
// Siblings at 2000+id
|
||||
X2(Parent, Parachain { id }) => 2000 + id as u64,
|
||||
// Accounts are their number
|
||||
X1(AccountIndex64 { index, .. }) => index,
|
||||
// Children at 1000+id
|
||||
X1(Parachain { id }) => 1000 + id as u64,
|
||||
// Self at 3000
|
||||
Null => 3000,
|
||||
// Parent at 3000
|
||||
X1(Parent) => 3001,
|
||||
l => return Err(l),
|
||||
})
|
||||
}
|
||||
|
||||
pub struct TestOriginConverter;
|
||||
impl ConvertOrigin<TestOrigin> for TestOriginConverter {
|
||||
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<TestOrigin, MultiLocation> {
|
||||
use {OriginKind::*};
|
||||
match (kind, origin) {
|
||||
(Superuser, _) => Ok(TestOrigin::Root),
|
||||
(SovereignAccount, l) => Ok(TestOrigin::Signed(to_account(l)?)),
|
||||
(Native, X1(Parachain { id })) => Ok(TestOrigin::Parachain(id)),
|
||||
(Native, X1(Parent)) => Ok(TestOrigin::Relay),
|
||||
(Native, X1(AccountIndex64 {index, ..})) => Ok(TestOrigin::Signed(index)),
|
||||
(_, origin) => Err(origin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static IS_RESERVE: RefCell<BTreeMap<MultiLocation, Vec<MultiAsset>>> = RefCell::new(BTreeMap::new());
|
||||
pub static IS_TELEPORTER: RefCell<BTreeMap<MultiLocation, Vec<MultiAsset>>> = RefCell::new(BTreeMap::new());
|
||||
}
|
||||
pub fn add_reserve(from: MultiLocation, asset: MultiAsset) {
|
||||
IS_RESERVE.with(|r| r.borrow_mut().entry(from).or_default().push(asset));
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn add_teleporter(from: MultiLocation, asset: MultiAsset) {
|
||||
IS_TELEPORTER.with(|r| r.borrow_mut().entry(from).or_default().push(asset));
|
||||
}
|
||||
pub struct TestIsReserve;
|
||||
impl FilterAssetLocation for TestIsReserve {
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
IS_RESERVE.with(|r| r.borrow().get(origin)
|
||||
.map_or(false, |v| v.iter().any(|a| a.contains(asset)))
|
||||
)
|
||||
}
|
||||
}
|
||||
pub struct TestIsTeleporter;
|
||||
impl FilterAssetLocation for TestIsTeleporter {
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
IS_TELEPORTER.with(|r| r.borrow().get(origin)
|
||||
.map_or(false, |v| v.iter().any(|a| a.contains(asset)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use xcm::v0::Response;
|
||||
pub enum ResponseSlot {
|
||||
Expecting(MultiLocation),
|
||||
Received(Response),
|
||||
}
|
||||
thread_local! {
|
||||
pub static QUERIES: RefCell<BTreeMap<u64, ResponseSlot>> = RefCell::new(BTreeMap::new());
|
||||
}
|
||||
pub struct TestResponseHandler;
|
||||
impl OnResponse for TestResponseHandler {
|
||||
fn expecting_response(origin: &MultiLocation, query_id: u64) -> bool {
|
||||
QUERIES.with(|q| match q.borrow().get(&query_id) {
|
||||
Some(ResponseSlot::Expecting(ref l)) => l == origin,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
fn on_response(_origin: MultiLocation, query_id: u64, response: xcm::v0::Response) -> Weight {
|
||||
QUERIES.with(|q| {
|
||||
q.borrow_mut()
|
||||
.entry(query_id)
|
||||
.and_modify(|v| if matches!(*v, ResponseSlot::Expecting(..)) {
|
||||
*v = ResponseSlot::Received(response);
|
||||
});
|
||||
});
|
||||
10
|
||||
}
|
||||
}
|
||||
pub fn expect_response(query_id: u64, from: MultiLocation) {
|
||||
QUERIES.with(|q| q.borrow_mut()
|
||||
.insert(query_id, ResponseSlot::Expecting(from))
|
||||
);
|
||||
}
|
||||
pub fn response(query_id: u64) -> Option<Response> {
|
||||
QUERIES.with(|q| q.borrow()
|
||||
.get(&query_id)
|
||||
.and_then(|v| match v {
|
||||
ResponseSlot::Received(r) => Some(r.clone()),
|
||||
_ => None,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub TestAncestry: MultiLocation = X1(Parachain{id: 42});
|
||||
pub UnitWeightCost: Weight = 10;
|
||||
}
|
||||
parameter_types! {
|
||||
// Nothing is allowed to be paid/unpaid by default.
|
||||
pub static AllowUnpaidFrom: Vec<MultiLocation> = vec![];
|
||||
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
|
||||
// 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight.
|
||||
pub static WeightPrice: (MultiLocation, u128) = (Null, 1_000_000_000_000);
|
||||
}
|
||||
|
||||
pub struct IsInVec<T>(PhantomData<T>);
|
||||
impl<X: Ord + PartialOrd, T: Get<Vec<X>>> Contains<X> for IsInVec<T> {
|
||||
fn sorted_members() -> Vec<X> { let mut r = T::get(); r.sort(); r }
|
||||
}
|
||||
|
||||
pub type TestBarrier = (
|
||||
TakeWeightCredit,
|
||||
AllowKnownQueryResponses<TestResponseHandler>,
|
||||
AllowTopLevelPaidExecutionFrom<IsInVec<AllowPaidFrom>>,
|
||||
AllowUnpaidExecutionFrom<IsInVec<AllowUnpaidFrom>>,
|
||||
);
|
||||
|
||||
pub struct TestConfig;
|
||||
impl Config for TestConfig {
|
||||
type Call = TestCall;
|
||||
type XcmSender = TestSendXcm;
|
||||
type AssetTransactor = TestAssetTransactor;
|
||||
type OriginConverter = TestOriginConverter;
|
||||
type IsReserve = TestIsReserve;
|
||||
type IsTeleporter = TestIsTeleporter;
|
||||
type LocationInverter = LocationInverter<TestAncestry>;
|
||||
type Barrier = TestBarrier;
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall>;
|
||||
type Trader = FixedRateOfConcreteFungible<WeightPrice>;
|
||||
type ResponseHandler = TestResponseHandler;
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
use sp_std::marker::PhantomData;
|
||||
use frame_support::traits::{Get, OriginTrait};
|
||||
use xcm::v0::{MultiLocation, OriginKind, NetworkId, Junction};
|
||||
use xcm_executor::traits::{LocationConversion, ConvertOrigin};
|
||||
use xcm_executor::traits::{Convert, ConvertOrigin};
|
||||
use polkadot_parachain::primitives::IsSystem;
|
||||
|
||||
/// Sovereign accounts use the system's `Signed` origin with an account ID derived from the
|
||||
@@ -26,12 +26,12 @@ pub struct SovereignSignedViaLocation<LocationConverter, Origin>(
|
||||
PhantomData<(LocationConverter, Origin)>
|
||||
);
|
||||
impl<
|
||||
LocationConverter: LocationConversion<Origin::AccountId>,
|
||||
LocationConverter: Convert<MultiLocation, Origin::AccountId>,
|
||||
Origin: OriginTrait,
|
||||
> ConvertOrigin<Origin> for SovereignSignedViaLocation<LocationConverter, Origin> {
|
||||
> ConvertOrigin<Origin> for SovereignSignedViaLocation<LocationConverter, Origin> where Origin::AccountId: Clone {
|
||||
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<Origin, MultiLocation> {
|
||||
if let OriginKind::SovereignAccount = kind {
|
||||
let location = LocationConverter::from_location(&origin).ok_or(origin)?;
|
||||
let location = LocationConverter::convert(origin)?;
|
||||
Ok(Origin::signed(location).into())
|
||||
} else {
|
||||
Err(origin)
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use super::mock::*;
|
||||
use {MultiAsset::*, Option::None};
|
||||
use xcm::v0::{Order, NetworkId::Any, Outcome, Response, ExecuteXcm};
|
||||
use xcm_executor::{XcmExecutor, Config, traits::*};
|
||||
|
||||
#[test]
|
||||
fn basic_setup_works() {
|
||||
add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) });
|
||||
assert!(<TestConfig as Config>::IsReserve::filter_asset_location(
|
||||
&ConcreteFungible { id: X1(Parent), amount: 100 },
|
||||
&X1(Parent),
|
||||
));
|
||||
|
||||
assert_eq!(to_account(X1(Parachain{id:1})), Ok(1001));
|
||||
assert_eq!(to_account(X1(Parachain{id:50})), Ok(1050));
|
||||
assert_eq!(to_account(X2(Parent, Parachain{id:1})), Ok(2001));
|
||||
assert_eq!(to_account(X2(Parent, Parachain{id:50})), Ok(2050));
|
||||
assert_eq!(to_account(X1(AccountIndex64{index:1, network:Any})), Ok(1));
|
||||
assert_eq!(to_account(X1(AccountIndex64{index:42, network:Any})), Ok(42));
|
||||
assert_eq!(to_account(Null), Ok(3000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weigher_should_work() {
|
||||
let mut message = opaque::Xcm::ReserveAssetDeposit {
|
||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
||||
effects: vec![
|
||||
Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] },
|
||||
Order::DepositAsset { assets: vec![All], dest: Null },
|
||||
],
|
||||
}.into();
|
||||
assert_eq!(<TestConfig as Config>::Weigher::shallow(&mut message), Ok(30));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_weight_credit_barrier_should_work() {
|
||||
let mut message = opaque::Xcm::TransferAsset {
|
||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
||||
dest: Null,
|
||||
};
|
||||
|
||||
let mut weight_credit = 10;
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&X1(Parent),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
&mut weight_credit,
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
assert_eq!(weight_credit, 0);
|
||||
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&X1(Parent),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
&mut weight_credit,
|
||||
);
|
||||
assert_eq!(r, Err(()));
|
||||
assert_eq!(weight_credit, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_unpaid_should_work() {
|
||||
let mut message = opaque::Xcm::TransferAsset {
|
||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
||||
dest: Null,
|
||||
};
|
||||
|
||||
AllowUnpaidFrom::set(vec![ X1(Parent) ]);
|
||||
|
||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||
&X1(Parachain { id: 1 }),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
&mut 0,
|
||||
);
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||
&X1(Parent),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
&mut 0,
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_paid_should_work() {
|
||||
AllowPaidFrom::set(vec![ X1(Parent) ]);
|
||||
|
||||
let mut message = opaque::Xcm::TransferAsset {
|
||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
||||
dest: Null,
|
||||
};
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&X1(Parachain { id: 1 }),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
&mut 0,
|
||||
);
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let mut underpaying_message = opaque::Xcm::ReserveAssetDeposit {
|
||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
||||
effects: vec![
|
||||
Order::BuyExecution { fees: All, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] },
|
||||
Order::DepositAsset { assets: vec![All], dest: Null },
|
||||
],
|
||||
};
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&X1(Parent),
|
||||
true,
|
||||
&mut underpaying_message,
|
||||
30,
|
||||
&mut 0,
|
||||
);
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let mut paying_message = opaque::Xcm::ReserveAssetDeposit {
|
||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
||||
effects: vec![
|
||||
Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] },
|
||||
Order::DepositAsset { assets: vec![All], dest: Null },
|
||||
],
|
||||
};
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&X1(Parachain { id: 1 }),
|
||||
true,
|
||||
&mut paying_message,
|
||||
30,
|
||||
&mut 0,
|
||||
);
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&X1(Parent),
|
||||
true,
|
||||
&mut paying_message,
|
||||
30,
|
||||
&mut 0,
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paying_reserve_deposit_should_work() {
|
||||
AllowPaidFrom::set(vec![ X1(Parent) ]);
|
||||
add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) });
|
||||
WeightPrice::set((X1(Parent), 1_000_000_000_000));
|
||||
|
||||
let origin = X1(Parent);
|
||||
let message = Xcm::<TestCall>::ReserveAssetDeposit {
|
||||
assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ],
|
||||
effects: vec![
|
||||
Order::<TestCall>::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] },
|
||||
Order::<TestCall>::DepositAsset { assets: vec![ All ], dest: Null },
|
||||
],
|
||||
};
|
||||
let weight_limit = 50;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(30));
|
||||
assert_eq!(assets(3000), vec![ ConcreteFungible { id: X1(Parent), amount: 70 } ]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_should_work() {
|
||||
// we'll let them have message execution for free.
|
||||
AllowUnpaidFrom::set(vec![ X1(Parachain{id:1}) ]);
|
||||
// Child parachain #1 owns 1000 tokens held by us in reserve.
|
||||
add_asset(1001, ConcreteFungible { id: Null, amount: 1000 });
|
||||
// They want to transfer 100 of them to their sibling parachain #2
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
X1(Parachain{id:1}),
|
||||
Xcm::TransferAsset {
|
||||
assets: vec![ ConcreteFungible { id: Null, amount: 100 } ],
|
||||
dest: X1(AccountIndex64{index:3, network:Any}),
|
||||
},
|
||||
50,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(10));
|
||||
assert_eq!(assets(3), vec![ ConcreteFungible { id: Null, amount: 100 } ]);
|
||||
assert_eq!(assets(1001), vec![ ConcreteFungible { id: Null, amount: 900 } ]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserve_transfer_should_work() {
|
||||
AllowUnpaidFrom::set(vec![ X1(Parachain{id:1}) ]);
|
||||
// Child parachain #1 owns 1000 tokens held by us in reserve.
|
||||
add_asset(1001, ConcreteFungible { id: Null, amount: 1000 });
|
||||
// The remote account owned by gav.
|
||||
let three = X1(AccountIndex64{index:3, network:Any});
|
||||
|
||||
// They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2
|
||||
// and let them know to hand it to account #3.
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
X1(Parachain{id:1}),
|
||||
Xcm::TransferReserveAsset {
|
||||
assets: vec![ ConcreteFungible { id: Null, amount: 100 } ],
|
||||
dest: X1(Parachain{id:2}),
|
||||
effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three.clone() } ],
|
||||
},
|
||||
50,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(10));
|
||||
|
||||
assert_eq!(assets(1002), vec![ ConcreteFungible { id: Null, amount: 100 } ]);
|
||||
assert_eq!(sent_xcm(), vec![(
|
||||
X1(Parachain { id: 2 }),
|
||||
Xcm::ReserveAssetDeposit {
|
||||
assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ],
|
||||
effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three } ],
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transacting_should_work() {
|
||||
AllowUnpaidFrom::set(vec![ X1(Parent) ]);
|
||||
|
||||
let origin = X1(Parent);
|
||||
let message = Xcm::<TestCall>::Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 50,
|
||||
call: TestCall::Any(50, None).encode().into(),
|
||||
};
|
||||
let weight_limit = 60;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(60));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transacting_should_respect_max_weight_requirement() {
|
||||
AllowUnpaidFrom::set(vec![ X1(Parent) ]);
|
||||
|
||||
let origin = X1(Parent);
|
||||
let message = Xcm::<TestCall>::Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 40,
|
||||
call: TestCall::Any(50, None).encode().into(),
|
||||
};
|
||||
let weight_limit = 60;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Incomplete(60, XcmError::TooMuchWeightRequired));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transacting_should_refund_weight() {
|
||||
AllowUnpaidFrom::set(vec![ X1(Parent) ]);
|
||||
|
||||
let origin = X1(Parent);
|
||||
let message = Xcm::<TestCall>::Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 50,
|
||||
call: TestCall::Any(50, Some(30)).encode().into(),
|
||||
};
|
||||
let weight_limit = 60;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(40));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paid_transacting_should_refund_payment_for_unused_weight() {
|
||||
let one = X1(AccountIndex64{index:1, network:Any});
|
||||
AllowPaidFrom::set(vec![ one.clone() ]);
|
||||
add_asset(1, ConcreteFungible { id: X1(Parent), amount: 100 });
|
||||
WeightPrice::set((X1(Parent), 1_000_000_000_000));
|
||||
|
||||
let origin = one.clone();
|
||||
let message = Xcm::<TestCall>::WithdrawAsset {
|
||||
assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], // enough for 100 units of weight.
|
||||
effects: vec![
|
||||
Order::<TestCall>::BuyExecution { fees: All, weight: 70, debt: 30, halt_on_error: true, xcm: vec![
|
||||
Xcm::<TestCall>::Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 60,
|
||||
// call estimated at 70 but only takes 10.
|
||||
call: TestCall::Any(60, Some(10)).encode().into(),
|
||||
}
|
||||
] },
|
||||
Order::<TestCall>::DepositAsset { assets: vec![ All ], dest: one.clone() },
|
||||
],
|
||||
};
|
||||
let weight_limit = 100;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(50));
|
||||
assert_eq!(assets(1), vec![ ConcreteFungible { id: X1(Parent), amount: 50 } ]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepaid_result_of_query_should_get_free_execution() {
|
||||
let query_id = 33;
|
||||
let origin = X1(Parent);
|
||||
// We put this in manually here, but normally this would be done at the point of crafting the message.
|
||||
expect_response(query_id, origin.clone());
|
||||
|
||||
let the_response = Response::Assets(vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ]);
|
||||
let message = Xcm::<TestCall>::QueryResponse {
|
||||
query_id,
|
||||
response: the_response.clone(),
|
||||
};
|
||||
let weight_limit = 10;
|
||||
|
||||
// First time the response gets through since we're expecting it...
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin.clone(), message.clone(), weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(10));
|
||||
assert_eq!(response(query_id).unwrap(), the_response);
|
||||
|
||||
// Second time it doesn't, since we're not.
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin.clone(), message.clone(), weight_limit);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier));
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::{result::Result, marker::PhantomData};
|
||||
use parity_scale_codec::Decode;
|
||||
use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation};
|
||||
use frame_support::{traits::Get, weights::{Weight, GetDispatchInfo}};
|
||||
use xcm_executor::{Assets, traits::{WeightBounds, WeightTrader}};
|
||||
|
||||
pub struct FixedWeightBounds<T, C>(PhantomData<(T, C)>);
|
||||
impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeightBounds<T, C> {
|
||||
fn shallow(message: &mut Xcm<C>) -> Result<Weight, ()> {
|
||||
let min = match message {
|
||||
Xcm::Transact { call, .. } => {
|
||||
call.ensure_decoded()?.get_dispatch_info().weight + T::get()
|
||||
}
|
||||
Xcm::WithdrawAsset { effects, .. }
|
||||
| Xcm::ReserveAssetDeposit { effects, .. }
|
||||
| Xcm::TeleportAsset { effects, .. } => {
|
||||
let inner: Weight = effects.iter_mut()
|
||||
.map(|effect| match effect {
|
||||
Order::BuyExecution { .. } => {
|
||||
// On success, execution of this will result in more weight being consumed but
|
||||
// we don't count it here since this is only the *shallow*, non-negotiable weight
|
||||
// spend and doesn't count weight placed behind a `BuyExecution` since it will not
|
||||
// be definitely consumed from any existing weight credit if execution of the message
|
||||
// is attempted.
|
||||
T::get()
|
||||
},
|
||||
_ => T::get(),
|
||||
}).sum();
|
||||
T::get() + inner
|
||||
}
|
||||
_ => T::get(),
|
||||
};
|
||||
Ok(min)
|
||||
}
|
||||
fn deep(message: &mut Xcm<C>) -> Result<Weight, ()> {
|
||||
let mut extra = 0;
|
||||
match message {
|
||||
Xcm::Transact { .. } => {}
|
||||
Xcm::WithdrawAsset { effects, .. }
|
||||
| Xcm::ReserveAssetDeposit { effects, .. }
|
||||
| Xcm::TeleportAsset { effects, .. } => {
|
||||
for effect in effects.iter_mut() {
|
||||
match effect {
|
||||
Order::BuyExecution { xcm, .. } => {
|
||||
for message in xcm.iter_mut() {
|
||||
extra += Self::shallow(message)? + Self::deep(message)?;
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(extra)
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple fee calculator that requires payment in a single concrete fungible at a fixed rate.
|
||||
///
|
||||
/// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for
|
||||
/// one second of weight.
|
||||
pub struct FixedRateOfConcreteFungible<T>(Weight, PhantomData<T>);
|
||||
impl<T: Get<(MultiLocation, u128)>> WeightTrader for FixedRateOfConcreteFungible<T> {
|
||||
fn new() -> Self { Self(0, PhantomData) }
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, ()> {
|
||||
let (id, units_per_second) = T::get();
|
||||
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
|
||||
let required = MultiAsset::ConcreteFungible { amount, id };
|
||||
let (used, _) = payment.less(required).map_err(|_| ())?;
|
||||
self.0 = self.0.saturating_add(weight);
|
||||
Ok(used)
|
||||
}
|
||||
fn refund_weight(&mut self, weight: Weight) -> MultiAsset {
|
||||
let weight = weight.min(self.0);
|
||||
self.0 -= weight;
|
||||
let (id, units_per_second) = T::get();
|
||||
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
|
||||
let result = MultiAsset::ConcreteFungible { amount, id };
|
||||
result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user