Dispatchable for teleporting assets (#2995)

* Dispatchable for teleporting assets

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Update node/network/protocol/src/peer_set.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Update xcm/src/v0/traits.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Gavin Wood
2021-05-10 16:13:46 +02:00
committed by GitHub
parent 6c0cb7f8d0
commit 36b9d23923
10 changed files with 290 additions and 168 deletions
+149 -149
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -52,7 +52,7 @@ use runtime_parachains::scheduler as parachains_scheduler;
use runtime_parachains::reward_points as parachains_reward_points; use runtime_parachains::reward_points as parachains_reward_points;
use runtime_parachains::runtime_api_impl::v1 as parachains_runtime_api_impl; use runtime_parachains::runtime_api_impl::v1 as parachains_runtime_api_impl;
use xcm::v0::{MultiLocation, NetworkId, BodyId, Xcm}; use xcm::v0::{MultiLocation, NetworkId, BodyId, Xcm, MultiAsset};
use xcm_builder::{ use xcm_builder::{
AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter, AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter,
ChildParachainAsNative, SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, LocationInverter, ChildParachainAsNative, SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, LocationInverter,
@@ -1217,6 +1217,8 @@ impl pallet_xcm::Config for Runtime {
// ...but they must match our filter, which requires them to be a simple withdraw + teleport. // ...but they must match our filter, which requires them to be a simple withdraw + teleport.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
type XcmExecutor = XcmExecutor<XcmConfig>; type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
} }
construct_runtime! { construct_runtime! {
+3 -1
View File
@@ -110,7 +110,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("rococo"), spec_name: create_runtime_str!("rococo"),
impl_name: create_runtime_str!("parity-rococo-v1.5"), impl_name: create_runtime_str!("parity-rococo-v1.5"),
authoring_version: 0, authoring_version: 0,
spec_version: 9000, spec_version: 9001,
impl_version: 0, impl_version: 0,
#[cfg(not(feature = "disable-runtime-api"))] #[cfg(not(feature = "disable-runtime-api"))]
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
@@ -714,6 +714,8 @@ impl pallet_xcm::Config for Runtime {
// ...but they must match our filter, which requires them to be a simple withdraw + teleport. // ...but they must match our filter, which requires them to be a simple withdraw + teleport.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
type XcmExecutor = XcmExecutor<XcmConfig>; type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
} }
impl parachains_session_info::Config for Runtime {} impl parachains_session_info::Config for Runtime {}
+3 -1
View File
@@ -53,7 +53,7 @@ use runtime_parachains::scheduler as parachains_scheduler;
use runtime_parachains::reward_points as parachains_reward_points; use runtime_parachains::reward_points as parachains_reward_points;
use runtime_parachains::runtime_api_impl::v1 as parachains_runtime_api_impl; use runtime_parachains::runtime_api_impl::v1 as parachains_runtime_api_impl;
use xcm::v0::{MultiLocation, NetworkId, Xcm}; use xcm::v0::{MultiLocation, NetworkId, Xcm, MultiAsset};
use xcm_executor::XcmExecutor; use xcm_executor::XcmExecutor;
use xcm_builder::{ use xcm_builder::{
AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter, AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter,
@@ -922,6 +922,8 @@ impl pallet_xcm::Config for Runtime {
// ...but they must match our filter, which requires them to be a simple withdraw + teleport. // ...but they must match our filter, which requires them to be a simple withdraw + teleport.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
type XcmExecutor = XcmExecutor<XcmConfig>; type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
} }
construct_runtime! { construct_runtime! {
+76 -7
View File
@@ -18,9 +18,9 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use sp_std::{marker::PhantomData, convert::TryInto, boxed::Box}; use sp_std::{prelude::*, marker::PhantomData, convert::TryInto, boxed::Box, vec};
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use xcm::v0::{BodyId, OriginKind, MultiLocation, Junction::Plurality}; use xcm::v0::prelude::*;
use xcm_executor::traits::ConvertOrigin; use xcm_executor::traits::ConvertOrigin;
use sp_runtime::{RuntimeDebug, traits::BadOrigin}; use sp_runtime::{RuntimeDebug, traits::BadOrigin};
use frame_support::traits::{EnsureOrigin, OriginTrait, Filter, Get, Contains}; use frame_support::traits::{EnsureOrigin, OriginTrait, Filter, Get, Contains};
@@ -32,7 +32,7 @@ pub mod pallet {
use super::*; use super::*;
use frame_support::pallet_prelude::*; use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*; use frame_system::pallet_prelude::*;
use xcm::v0::{Xcm, MultiLocation, Error as XcmError, SendXcm, ExecuteXcm}; use xcm_executor::traits::WeightBounds;
#[pallet::pallet] #[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)] #[pallet::generate_store(pub(super) trait Store)]
@@ -51,8 +51,9 @@ pub mod pallet {
/// The type used to actually dispatch an XCM to its destination. /// The type used to actually dispatch an XCM to its destination.
type XcmRouter: SendXcm; type XcmRouter: SendXcm;
/// Required origin for executing XCM messages. If successful, the it resolves to `MultiLocation` /// Required origin for executing XCM messages, includng the teleport functionality. If successful,
/// which exists as an interior location within this chain's XCM context. /// the it resolves to `MultiLocation` which exists as an interior location within this chain's XCM
/// context.
type ExecuteXcmOrigin: EnsureOrigin<Self::Origin, Success=MultiLocation>; type ExecuteXcmOrigin: EnsureOrigin<Self::Origin, Success=MultiLocation>;
/// Our XCM filter which messages to be executed using `XcmExecutor` must pass. /// Our XCM filter which messages to be executed using `XcmExecutor` must pass.
@@ -60,6 +61,12 @@ pub mod pallet {
/// Something to execute an XCM message. /// Something to execute an XCM message.
type XcmExecutor: ExecuteXcm<Self::Call>; type XcmExecutor: ExecuteXcm<Self::Call>;
/// Our XCM filter which messages to be executed using `XcmExecutor` must pass.
type XcmTeleportFilter: Contains<(MultiLocation, Vec<MultiAsset>)>;
/// Means of measuring the weight consumed by an XCM message locally.
type Weigher: WeightBounds<Self::Call>;
} }
#[pallet::event] #[pallet::event]
@@ -75,6 +82,8 @@ pub mod pallet {
SendFailure, SendFailure,
/// The message execution fails the filter. /// The message execution fails the filter.
Filtered, Filtered,
/// The message's weight could not be determined.
UnweighableMessage,
} }
#[pallet::hooks] #[pallet::hooks]
@@ -82,7 +91,7 @@ pub mod pallet {
#[pallet::call] #[pallet::call]
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
#[pallet::weight(1_000)] #[pallet::weight(100_000_000)]
fn send(origin: OriginFor<T>, dest: MultiLocation, message: Xcm<()>) -> DispatchResult { fn send(origin: OriginFor<T>, dest: MultiLocation, message: Xcm<()>) -> DispatchResult {
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
Self::send_xcm(origin_location.clone(), dest.clone(), message.clone()) Self::send_xcm(origin_location.clone(), dest.clone(), message.clone())
@@ -94,6 +103,66 @@ pub mod pallet {
Ok(()) Ok(())
} }
/// Teleport some assets from the local chain to some destination chain.
///
/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
/// - `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send
/// from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain.
/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be
/// an `AccountId32` value.
/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the
/// `dest` side.
/// - `dest_weight`: Equal to the total weight on `dest` of the XCM message
/// `Teleport { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`.
#[pallet::weight({
let mut message = Xcm::WithdrawAsset {
assets: assets.clone(),
effects: sp_std::vec![ InitiateTeleport {
assets: sp_std::vec![ All ],
dest: dest.clone(),
effects: sp_std::vec![],
} ]
};
T::Weigher::weight(&mut message).map_or(Weight::max_value(), |w| 100_000_000 + w)
})]
fn teleport_assets(
origin: OriginFor<T>,
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
dest_weight: Weight,
) -> DispatchResult {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
let value = (origin_location, assets);
ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, assets) = value;
let mut message = Xcm::WithdrawAsset {
assets,
effects: vec![
InitiateTeleport {
assets: vec![ All ],
dest,
effects: vec![
BuyExecution {
fees: All,
// Zero weight for additional XCM (since there are none to execute)
weight: 0,
debt: dest_weight,
halt_on_error: false,
xcm: vec![],
},
DepositAsset { assets: vec![ All ], dest: beneficiary },
],
},
],
};
let weight = T::Weigher::weight(&mut message)
.map_err(|()| Error::<T>::UnweighableMessage)?;
let outcome = T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight);
Self::deposit_event(Event::Attempted(outcome));
Ok(())
}
/// Execute an XCM message from a local, signed, origin. /// Execute an XCM message from a local, signed, origin.
/// ///
/// An event is deposited indicating whether `msg` could be executed completely or only /// An event is deposited indicating whether `msg` could be executed completely or only
@@ -105,7 +174,7 @@ pub mod pallet {
/// ///
/// NOTE: A successful return to this does *not* imply that the `msg` was executed successfully /// NOTE: A successful return to this does *not* imply that the `msg` was executed successfully
/// to completion; only that *some* of it was executed. /// to completion; only that *some* of it was executed.
#[pallet::weight(max_weight.saturating_add(1_000u64))] #[pallet::weight(max_weight.saturating_add(100_000_000u64))]
fn execute(origin: OriginFor<T>, message: Box<Xcm<T::Call>>, max_weight: Weight) fn execute(origin: OriginFor<T>, message: Box<Xcm<T::Call>>, max_weight: Weight)
-> DispatchResult -> DispatchResult
{ {
+10
View File
@@ -33,6 +33,16 @@ pub use multi_location::MultiLocation;
pub use order::Order; pub use order::Order;
pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::junction::{Junction::*, NetworkId, BodyId, BodyPart};
pub use super::multi_asset::{MultiAsset::{self, *}, AssetInstance::{self, *}};
pub use super::multi_location::MultiLocation::{self, *};
pub use super::order::Order::{self, *};
pub use super::traits::{Error as XcmError, Result as XcmResult, SendXcm, ExecuteXcm, Outcome};
pub use super::{Xcm::{self, *}, OriginKind};
}
// TODO: #2841 #XCMENCODE Efficient encodings for Vec<MultiAsset>, Vec<Order>, using initial byte values 128+ to encode // TODO: #2841 #XCMENCODE Efficient encodings for Vec<MultiAsset>, Vec<Order>, using initial byte values 128+ to encode
// the number of items in the vector. // the number of items in the vector.
+24 -4
View File
@@ -126,14 +126,34 @@ impl Outcome {
} }
} }
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> { pub trait ExecuteXcm<Call> {
type Call; /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is
fn execute_xcm(origin: MultiLocation, message: Xcm<Call>, weight_limit: Weight) -> Outcome; /// a basic hard-limit and the implementation may place further restrictions or requirements on weight and
/// other aspects.
fn execute_xcm(origin: MultiLocation, message: Xcm<Call>, weight_limit: Weight) -> Outcome {
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
}
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight.
///
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
/// execution without associated payment.
fn execute_xcm_in_credit(
origin: MultiLocation,
message: Xcm<Call>,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome;
} }
impl<C> ExecuteXcm<C> for () { impl<C> ExecuteXcm<C> for () {
type Call = C; fn execute_xcm_in_credit(
fn execute_xcm(_origin: MultiLocation, _message: Xcm<C>, _weight_limit: Weight) -> Outcome { _origin: MultiLocation,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented) Outcome::Error(Error::Unimplemented)
} }
} }
+2 -1
View File
@@ -32,7 +32,8 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
Xcm::RelayedFrom { ref mut message, .. } => T::get() + Self::shallow(message.as_mut())?, Xcm::RelayedFrom { ref mut message, .. } => T::get() + Self::shallow(message.as_mut())?,
Xcm::WithdrawAsset { effects, .. } Xcm::WithdrawAsset { effects, .. }
| Xcm::ReserveAssetDeposit { effects, .. } | Xcm::ReserveAssetDeposit { effects, .. }
| Xcm::TeleportAsset { effects, .. } => { | Xcm::TeleportAsset { effects, .. }
=> {
let inner: Weight = effects.iter_mut() let inner: Weight = effects.iter_mut()
.map(|effect| match effect { .map(|effect| match effect {
Order::BuyExecution { .. } => { Order::BuyExecution { .. } => {
+7 -3
View File
@@ -40,8 +40,12 @@ pub use config::Config;
pub struct XcmExecutor<Config>(PhantomData<Config>); pub struct XcmExecutor<Config>(PhantomData<Config>);
impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> { impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
type Call = Config::Call; fn execute_xcm_in_credit(
fn execute_xcm(origin: MultiLocation, message: Xcm<Config::Call>, weight_limit: Weight) -> Outcome { origin: MultiLocation,
message: Xcm<Config::Call>,
weight_limit: Weight,
mut weight_credit: Weight,
) -> Outcome {
// TODO: #2841 #HARDENXCM We should identify recursive bombs here and bail. // TODO: #2841 #HARDENXCM We should identify recursive bombs here and bail.
let mut message = Xcm::<Config::Call>::from(message); let mut message = Xcm::<Config::Call>::from(message);
let shallow_weight = match Config::Weigher::shallow(&mut message) { let shallow_weight = match Config::Weigher::shallow(&mut message) {
@@ -60,7 +64,7 @@ impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
return Outcome::Error(XcmError::WeightLimitReached(maximum_weight)); return Outcome::Error(XcmError::WeightLimitReached(maximum_weight));
} }
let mut trader = Config::Trader::new(); let mut trader = Config::Trader::new();
let result = Self::do_execute_xcm(origin, true, message, &mut 0, Some(shallow_weight), &mut trader); let result = Self::do_execute_xcm(origin, true, message, &mut weight_credit, Some(shallow_weight), &mut trader);
drop(trader); drop(trader);
match result { match result {
Ok(surplus) => Outcome::Complete(maximum_weight.saturating_sub(surplus)), Ok(surplus) => Outcome::Complete(maximum_weight.saturating_sub(surplus)),
+13 -1
View File
@@ -15,7 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use sp_std::result::Result; use sp_std::result::Result;
use xcm::v0::{Xcm, MultiAsset, Error}; use xcm::v0::{Xcm, MultiAsset, MultiLocation, Error};
use frame_support::weights::Weight; use frame_support::weights::Weight;
use crate::Assets; use crate::Assets;
@@ -43,6 +43,18 @@ pub trait WeightBounds<Call> {
/// any internal effects. Inner XCM messages may be executed by: /// any internal effects. Inner XCM messages may be executed by:
/// - Order::BuyExecution /// - Order::BuyExecution
fn deep(message: &mut Xcm<Call>) -> Result<Weight, ()>; fn deep(message: &mut Xcm<Call>) -> Result<Weight, ()>;
/// Return the total weight for executing `message`.
fn weight(message: &mut Xcm<Call>) -> Result<Weight, ()> {
Self::shallow(message)?.checked_add(Self::deep(message)?).ok_or(())
}
}
/// A means of getting approximate weight consumption for a given destination message executor and a
/// message.
pub trait UniversalWeigher {
/// Get the upper limit of weight required for `dest` to execute `message`.
fn weigh(dest: MultiLocation, message: Xcm<()>) -> Result<Weight, ()>;
} }
/// Charge for weight in order to execute XCM. /// Charge for weight in order to execute XCM.