Unverified Commit 5851517d authored by Gavin Wood's avatar Gavin Wood Committed by GitHub
Browse files

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's avatarShawn Tabrizi <shawntabrizi@gmail.com>

* Update xcm/src/v0/traits.rs
Co-authored-by: Shawn Tabrizi's avatarShawn Tabrizi <shawntabrizi@gmail.com>
parent 78b8cb25
Pipeline #137616 passed with stages
in 39 minutes and 43 seconds
This diff is collapsed.
......@@ -52,7 +52,7 @@ use runtime_parachains::scheduler as parachains_scheduler;
use runtime_parachains::reward_points as parachains_reward_points;
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::{
AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter,
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.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
}
construct_runtime! {
......
......@@ -110,7 +110,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("rococo"),
impl_name: create_runtime_str!("parity-rococo-v1.5"),
authoring_version: 0,
spec_version: 9000,
spec_version: 9001,
impl_version: 0,
#[cfg(not(feature = "disable-runtime-api"))]
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.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
}
impl parachains_session_info::Config for Runtime {}
......
......@@ -53,7 +53,7 @@ use runtime_parachains::scheduler as parachains_scheduler;
use runtime_parachains::reward_points as parachains_reward_points;
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_builder::{
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.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
}
construct_runtime! {
......
......@@ -18,9 +18,9 @@
#![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 xcm::v0::{BodyId, OriginKind, MultiLocation, Junction::Plurality};
use xcm::v0::prelude::*;
use xcm_executor::traits::ConvertOrigin;
use sp_runtime::{RuntimeDebug, traits::BadOrigin};
use frame_support::traits::{EnsureOrigin, OriginTrait, Filter, Get, Contains};
......@@ -32,7 +32,7 @@ pub mod pallet {
use super::*;
use frame_support::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::generate_store(pub(super) trait Store)]
......@@ -51,8 +51,9 @@ pub mod pallet {
/// The type used to actually dispatch an XCM to its destination.
type XcmRouter: SendXcm;
/// Required origin for executing XCM messages. If successful, the it resolves to `MultiLocation`
/// which exists as an interior location within this chain's XCM context.
/// Required origin for executing XCM messages, includng the teleport functionality. If successful,
/// the it resolves to `MultiLocation` which exists as an interior location within this chain's XCM
/// context.
type ExecuteXcmOrigin: EnsureOrigin<Self::Origin, Success=MultiLocation>;
/// 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.
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]
......@@ -75,6 +82,8 @@ pub mod pallet {
SendFailure,
/// The message execution fails the filter.
Filtered,
/// The message's weight could not be determined.
UnweighableMessage,
}
#[pallet::hooks]
......@@ -82,7 +91,7 @@ pub mod pallet {
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(1_000)]
#[pallet::weight(100_000_000)]
fn send(origin: OriginFor<T>, dest: MultiLocation, message: Xcm<()>) -> DispatchResult {
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
Self::send_xcm(origin_location.clone(), dest.clone(), message.clone())
......@@ -94,6 +103,66 @@ pub mod pallet {
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.
///
/// 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
/// 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)
-> DispatchResult
{
......
......@@ -33,6 +33,16 @@ pub use multi_location::MultiLocation;
pub use order::Order;
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
// the number of items in the vector.
......
......@@ -126,14 +126,34 @@ impl Outcome {
}
}
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> {
type Call;
fn execute_xcm(origin: MultiLocation, message: Xcm<Call>, weight_limit: Weight) -> Outcome;
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is
/// 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 () {
type Call = C;
fn execute_xcm(_origin: MultiLocation, _message: Xcm<C>, _weight_limit: Weight) -> Outcome {
fn execute_xcm_in_credit(
_origin: MultiLocation,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented)
}
}
......
......@@ -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::WithdrawAsset { effects, .. }
| Xcm::ReserveAssetDeposit { effects, .. }
| Xcm::TeleportAsset { effects, .. } => {
| Xcm::TeleportAsset { effects, .. }
=> {
let inner: Weight = effects.iter_mut()
.map(|effect| match effect {
Order::BuyExecution { .. } => {
......
......@@ -40,8 +40,12 @@ pub use config::Config;
pub struct XcmExecutor<Config>(PhantomData<Config>);
impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
type Call = Config::Call;
fn execute_xcm(origin: MultiLocation, message: Xcm<Config::Call>, weight_limit: Weight) -> Outcome {
fn execute_xcm_in_credit(
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.
let mut message = Xcm::<Config::Call>::from(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));
}
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);
match result {
Ok(surplus) => Outcome::Complete(maximum_weight.saturating_sub(surplus)),
......
......@@ -15,7 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use sp_std::result::Result;
use xcm::v0::{Xcm, MultiAsset, Error};
use xcm::v0::{Xcm, MultiAsset, MultiLocation, Error};
use frame_support::weights::Weight;
use crate::Assets;
......@@ -43,6 +43,18 @@ pub trait WeightBounds<Call> {
/// any internal effects. Inner XCM messages may be executed by:
/// - Order::BuyExecution
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.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment