mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-29 06:47:24 +00:00
XCM Revamp Continued (#2865)
* Introduce plurality XCM locations * Add RelayedFrom * DMP dispatch weight handling. * Add pallet for XCM sending, add routing logic. * Update error types & doc * Fix warnings. * Fixes * Fixes * Fixes * Bump Substrate * Fixes * Docs * Docs * Docs * Fixes * Fixes * Fixes * Update xcm/pallet-xcm/src/lib.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Docs * Fixes * Update lib.rs * Fixes Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Generated
+167
-149
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,7 @@ members = [
|
||||
"xcm",
|
||||
"xcm/xcm-builder",
|
||||
"xcm/xcm-executor",
|
||||
"xcm/pallet-xcm",
|
||||
"node/collation-generation",
|
||||
"node/core/approval-voting",
|
||||
"node/core/av-store",
|
||||
|
||||
@@ -22,21 +22,22 @@ use xcm::opaque::{VersionedXcm, v0::{SendXcm, MultiLocation, Junction, Xcm, Resu
|
||||
use runtime_parachains::{configuration, dmp};
|
||||
|
||||
/// Xcm sender for relay chain. It only sends downward message.
|
||||
pub struct RelayChainXcmSender<T>(PhantomData<T>);
|
||||
pub struct ChildParachainRouter<T>(PhantomData<T>);
|
||||
|
||||
impl<T: configuration::Config + dmp::Config> SendXcm for RelayChainXcmSender<T> {
|
||||
impl<T: configuration::Config + dmp::Config> SendXcm for ChildParachainRouter<T> {
|
||||
fn send_xcm(dest: MultiLocation, msg: Xcm) -> Result {
|
||||
if let MultiLocation::X1(Junction::Parachain { id }) = dest {
|
||||
// Downward message passing.
|
||||
let config = <configuration::Module<T>>::config();
|
||||
<dmp::Module<T>>::queue_downward_message(
|
||||
&config,
|
||||
id.into(),
|
||||
VersionedXcm::from(msg).encode(),
|
||||
).map_err(Into::<Error>::into)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::CannotReachDestination("UnsupportedDestination"))
|
||||
match dest {
|
||||
MultiLocation::X1(Junction::Parachain { id }) => {
|
||||
// Downward message passing.
|
||||
let config = <configuration::Module<T>>::config();
|
||||
<dmp::Module<T>>::queue_downward_message(
|
||||
&config,
|
||||
id.into(),
|
||||
VersionedXcm::from(msg).encode(),
|
||||
).map_err(Into::<Error>::into)?;
|
||||
Ok(())
|
||||
}
|
||||
d => Err(Error::CannotReachDestination(d, msg)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +117,13 @@ impl crate::paras::Config for Test {
|
||||
|
||||
impl crate::dmp::Config for Test { }
|
||||
|
||||
parameter_types! {
|
||||
pub const FirstMessageFactorPercent: u64 = 100;
|
||||
}
|
||||
|
||||
impl crate::ump::Config for Test {
|
||||
type UmpSink = crate::ump::mock_sink::MockUmpSink;
|
||||
type FirstMessageFactorPercent = FirstMessageFactorPercent;
|
||||
}
|
||||
|
||||
impl crate::hrmp::Config for Test {
|
||||
|
||||
@@ -18,9 +18,8 @@ use crate::{
|
||||
configuration::{self, HostConfiguration},
|
||||
initializer,
|
||||
};
|
||||
use sp_std::{fmt, prelude::*};
|
||||
use sp_std::{prelude::*, fmt, marker::PhantomData};
|
||||
use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque};
|
||||
use sp_runtime::traits::Zero;
|
||||
use frame_support::{decl_module, decl_storage, StorageMap, StorageValue, weights::Weight, traits::Get};
|
||||
use primitives::v1::{Id as ParaId, UpwardMessage};
|
||||
|
||||
@@ -42,39 +41,41 @@ const LOG_TARGET: &str = "runtime::ump-sink";
|
||||
/// It is possible that by the time the message is sank the origin parachain was offboarded. It is
|
||||
/// up to the implementer to check that if it cares.
|
||||
pub trait UmpSink {
|
||||
/// Process an incoming upward message and return the amount of weight it consumed.
|
||||
/// Process an incoming upward message and return the amount of weight it consumed, or `None` if
|
||||
/// it did not begin processing a message since it would otherwise exceed `max_weight`.
|
||||
///
|
||||
/// See the trait docs for more details.
|
||||
fn process_upward_message(origin: ParaId, msg: Vec<u8>) -> Weight;
|
||||
fn process_upward_message(origin: ParaId, msg: &[u8], max_weight: Weight) -> Option<Weight>;
|
||||
}
|
||||
|
||||
/// An implementation of a sink that just swallows the message without consuming any weight.
|
||||
/// An implementation of a sink that just swallows the message without consuming any weight. Returns
|
||||
/// `Some(0)` indicating that no messages existed for it to process.
|
||||
impl UmpSink for () {
|
||||
fn process_upward_message(_: ParaId, _: Vec<u8>) -> Weight {
|
||||
0
|
||||
fn process_upward_message(_: ParaId, _: &[u8], _: Weight) -> Option<Weight> {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A specific implementation of a UmpSink where messages are in the XCM format
|
||||
/// and will be forwarded to the XCM Executor.
|
||||
pub struct XcmSink<Config>(sp_std::marker::PhantomData<Config>);
|
||||
pub struct XcmSink<XcmExecutor, Call>(PhantomData<(XcmExecutor, Call)>);
|
||||
|
||||
impl<Config: xcm_executor::Config> UmpSink for XcmSink<Config> {
|
||||
fn process_upward_message(origin: ParaId, msg: Vec<u8>) -> Weight {
|
||||
impl<XcmExecutor: xcm::v0::ExecuteXcm<Call>, Call> UmpSink for XcmSink<XcmExecutor, Call> {
|
||||
fn process_upward_message(origin: ParaId, mut msg: &[u8], max_weight: Weight) -> Option<Weight> {
|
||||
use parity_scale_codec::Decode;
|
||||
use xcm::VersionedXcm;
|
||||
use xcm::v0::{Junction, MultiLocation, ExecuteXcm};
|
||||
use xcm_executor::XcmExecutor;
|
||||
use xcm::v0::{Junction, MultiLocation, Outcome, Error as XcmError};
|
||||
|
||||
// TODO: #2841 #UMPQUEUE Get a proper weight limit here. Probably from Relay Chain Config
|
||||
let weight_limit = Weight::max_value();
|
||||
let weight = if let Ok(versioned_xcm_message) = VersionedXcm::decode(&mut &msg[..]) {
|
||||
if let Ok(versioned_xcm_message) = VersionedXcm::decode(&mut msg) {
|
||||
match versioned_xcm_message {
|
||||
VersionedXcm::V0(xcm_message) => {
|
||||
let xcm_junction: Junction = Junction::Parachain { id: origin.into() };
|
||||
let xcm_location: MultiLocation = xcm_junction.into();
|
||||
let result = XcmExecutor::<Config>::execute_xcm(xcm_location, xcm_message, weight_limit);
|
||||
result.weight_used()
|
||||
match XcmExecutor::execute_xcm(xcm_location, xcm_message, max_weight) {
|
||||
Outcome::Complete(w) | Outcome::Incomplete(w, _) => Some(w),
|
||||
Outcome::Error(XcmError::WeightLimitReached) => None,
|
||||
Outcome::Error(_) => Some(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -82,13 +83,8 @@ impl<Config: xcm_executor::Config> UmpSink for XcmSink<Config> {
|
||||
target: LOG_TARGET,
|
||||
"Failed to decode versioned XCM from upward message.",
|
||||
);
|
||||
Weight::zero()
|
||||
};
|
||||
|
||||
// TODO: #2841 #UMPQUEUE to be sound, this implementation must ensure that returned (and thus consumed)
|
||||
// weight is limited to some small portion of the total block weight (as a ballpark, 1/4, 1/8
|
||||
// or lower).
|
||||
weight
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +144,9 @@ impl fmt::Debug for AcceptanceCheckErr {
|
||||
pub trait Config: frame_system::Config + configuration::Config {
|
||||
/// A place where all received upward messages are funneled.
|
||||
type UmpSink: UmpSink;
|
||||
|
||||
/// The factor by which the weight limit it multiplied for the first UMP message to execute with.
|
||||
type FirstMessageFactorPercent: Get<Weight>;
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
@@ -337,12 +336,21 @@ impl<T: Config> Module<T> {
|
||||
// if so - bail.
|
||||
break;
|
||||
}
|
||||
let max_weight = if used_weight_so_far == 0 {
|
||||
// we increase the amount of weight that we're allowed to use on the first message to try to prevent
|
||||
// the possibility of blockage of the queue.
|
||||
config.preferred_dispatchable_upward_messages_step_weight * T::FirstMessageFactorPercent::get() / 100
|
||||
} else {
|
||||
config.preferred_dispatchable_upward_messages_step_weight - used_weight_so_far
|
||||
};
|
||||
|
||||
// dequeue the next message from the queue of the dispatchee
|
||||
let (upward_message, became_empty) = queue_cache.dequeue::<T>(dispatchee);
|
||||
if let Some(upward_message) = upward_message {
|
||||
used_weight_so_far +=
|
||||
T::UmpSink::process_upward_message(dispatchee, upward_message);
|
||||
match T::UmpSink::process_upward_message(dispatchee, &upward_message[..], max_weight) {
|
||||
None => break,
|
||||
Some(used) => used_weight_so_far += used,
|
||||
}
|
||||
}
|
||||
|
||||
if became_empty {
|
||||
@@ -555,28 +563,25 @@ pub(crate) mod mock_sink {
|
||||
|
||||
pub struct MockUmpSink;
|
||||
impl UmpSink for MockUmpSink {
|
||||
fn process_upward_message(actual_origin: ParaId, actual_msg: Vec<u8>) -> Weight {
|
||||
HOOK.with(|opt_hook| match &mut *opt_hook.borrow_mut() {
|
||||
Some(hook) => {
|
||||
let UmpExpectation {
|
||||
expected_origin,
|
||||
expected_msg,
|
||||
mock_weight,
|
||||
} = match hook.pop_front() {
|
||||
Some(expectation) => expectation,
|
||||
None => {
|
||||
panic!(
|
||||
"The probe is active but didn't expect the message:\n\n\t{:?}.",
|
||||
actual_msg,
|
||||
);
|
||||
}
|
||||
};
|
||||
assert_eq!(expected_origin, actual_origin);
|
||||
assert_eq!(expected_msg, actual_msg);
|
||||
mock_weight
|
||||
}
|
||||
None => 0,
|
||||
})
|
||||
fn process_upward_message(actual_origin: ParaId, actual_msg: &[u8], _max_weight: Weight) -> Option<Weight> {
|
||||
HOOK.with(|opt_hook| opt_hook.borrow_mut().as_mut().map(|hook| {
|
||||
let UmpExpectation {
|
||||
expected_origin,
|
||||
expected_msg,
|
||||
mock_weight,
|
||||
} = match hook.pop_front() {
|
||||
Some(expectation) => expectation,
|
||||
None => {
|
||||
panic!(
|
||||
"The probe is active but didn't expect the message:\n\n\t{:?}.",
|
||||
actual_msg,
|
||||
);
|
||||
}
|
||||
};
|
||||
assert_eq!(expected_origin, actual_origin);
|
||||
assert_eq!(expected_msg, &actual_msg[..]);
|
||||
mock_weight
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch =
|
||||
pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-beefy = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master", default-features = false }
|
||||
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-collective = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
@@ -64,6 +65,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac
|
||||
xcm = { package = "xcm", path = "../../xcm", default-features = false }
|
||||
xcm-executor = { package = "xcm-executor", path = "../../xcm/xcm-executor", default-features = false }
|
||||
xcm-builder = { package = "xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
|
||||
pallet-xcm = { path = "../../xcm/pallet-xcm", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
substrate-wasm-builder = "3.0.0"
|
||||
@@ -81,6 +83,7 @@ std = [
|
||||
"pallet-babe/std",
|
||||
"beefy-primitives/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-collective/std",
|
||||
"pallet-beefy/std",
|
||||
"pallet-grandpa/std",
|
||||
"pallet-sudo/std",
|
||||
@@ -118,6 +121,7 @@ std = [
|
||||
"xcm/std",
|
||||
"xcm-executor/std",
|
||||
"xcm-builder/std",
|
||||
"pallet-xcm/std",
|
||||
"log/std",
|
||||
]
|
||||
# When enabled, the runtime api will not be build.
|
||||
@@ -133,11 +137,14 @@ runtime-benchmarks = [
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"pallet-babe/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
"pallet-collective/runtime-benchmarks",
|
||||
"pallet-grandpa/runtime-benchmarks",
|
||||
"pallet-im-online/runtime-benchmarks",
|
||||
"pallet-indices/runtime-benchmarks",
|
||||
"pallet-staking/runtime-benchmarks",
|
||||
"pallet-timestamp/runtime-benchmarks",
|
||||
"pallet-xcm/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-executive/try-runtime",
|
||||
@@ -147,6 +154,7 @@ try-runtime = [
|
||||
"pallet-authorship/try-runtime",
|
||||
"pallet-babe/try-runtime",
|
||||
"pallet-balances/try-runtime",
|
||||
"pallet-collective/try-runtime",
|
||||
"pallet-grandpa/try-runtime",
|
||||
"pallet-sudo/try-runtime",
|
||||
"pallet-indices/try-runtime",
|
||||
|
||||
@@ -86,6 +86,7 @@ pub use pallet_balances::Call as BalancesCall;
|
||||
|
||||
use polkadot_parachain::primitives::Id as ParaId;
|
||||
use xcm::v0::{MultiLocation, NetworkId};
|
||||
use xcm_executor::XcmExecutor;
|
||||
use xcm_builder::{
|
||||
AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation,
|
||||
CurrencyAdapter as XcmCurrencyAdapter, ChildParachainAsNative,
|
||||
@@ -274,6 +275,9 @@ construct_runtime! {
|
||||
|
||||
Utility: pallet_utility::{Pallet, Call, Event} = 90,
|
||||
Proxy: pallet_proxy::{Pallet, Call, Storage, Event<T>} = 91,
|
||||
|
||||
// Pallet for sending XCM.
|
||||
XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event<T>} = 99,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,10 +623,17 @@ parameter_types! {
|
||||
pub const RocFee: (MultiLocation, u128) = (RocLocation::get(), 1 * CENTS);
|
||||
}
|
||||
|
||||
/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
|
||||
/// individual routers.
|
||||
pub type XcmRouter = (
|
||||
// Only one router so far - use DMP to communicate with child parachains.
|
||||
xcm_sender::ChildParachainRouter<Runtime>,
|
||||
);
|
||||
|
||||
pub struct XcmConfig;
|
||||
impl xcm_executor::Config for XcmConfig {
|
||||
type Call = Call;
|
||||
type XcmSender = xcm_sender::RelayChainXcmSender<Runtime>;
|
||||
type XcmSender = XcmRouter;
|
||||
type AssetTransactor = LocalAssetTransactor;
|
||||
type OriginConverter = LocalOriginConverter;
|
||||
type IsReserve = ();
|
||||
@@ -634,10 +645,29 @@ impl xcm_executor::Config for XcmConfig {
|
||||
type ResponseHandler = ();
|
||||
}
|
||||
|
||||
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location
|
||||
/// of this chain.
|
||||
pub type LocalOriginToLocation = (
|
||||
);
|
||||
|
||||
impl pallet_xcm::Config for Runtime {
|
||||
type Event = Event;
|
||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||
type XcmRouter = XcmRouter;
|
||||
// Right now nobody but root is allowed to dispatch local XCM messages.
|
||||
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, ()>;
|
||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||
}
|
||||
|
||||
impl parachains_session_info::Config for Runtime {}
|
||||
|
||||
parameter_types! {
|
||||
pub const FirstMessageFactorPercent: u64 = 100;
|
||||
}
|
||||
|
||||
impl parachains_ump::Config for Runtime {
|
||||
type UmpSink = crate::parachains_ump::XcmSink<XcmConfig>;
|
||||
type UmpSink = crate::parachains_ump::XcmSink<XcmExecutor<XcmConfig>, Call>;
|
||||
type FirstMessageFactorPercent = FirstMessageFactorPercent;
|
||||
}
|
||||
|
||||
impl parachains_dmp::Config for Runtime {}
|
||||
|
||||
@@ -474,8 +474,13 @@ impl parachains_paras::Config for Runtime {
|
||||
|
||||
impl parachains_dmp::Config for Runtime {}
|
||||
|
||||
parameter_types! {
|
||||
pub const FirstMessageFactorPercent: u64 = 100;
|
||||
}
|
||||
|
||||
impl parachains_ump::Config for Runtime {
|
||||
type UmpSink = ();
|
||||
type FirstMessageFactorPercent = FirstMessageFactorPercent;
|
||||
}
|
||||
|
||||
impl parachains_hrmp::Config for Runtime {
|
||||
|
||||
@@ -6,6 +6,7 @@ description = "The basic XCM datastructures."
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
impl-trait-for-tuples = "0.2.0"
|
||||
parity-scale-codec = { version = "2.0.0", default-features = false, features = [ "derive" ] }
|
||||
derivative = {version = "2.2.0", default-features = false, features = [ "use_core" ] }
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
name = "pallet-xcm"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
|
||||
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
|
||||
xcm = { path = "..", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"serde",
|
||||
"sp-std/std",
|
||||
"sp-runtime/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"xcm/std",
|
||||
]
|
||||
runtime-benchmarks = []
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Cumulus 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.
|
||||
|
||||
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Pallet to handle XCM messages.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::{marker::PhantomData, convert::TryInto, boxed::Box};
|
||||
use codec::{Encode, Decode};
|
||||
use xcm::v0::{BodyId, MultiLocation::{self, X1}, Junction::Plurality};
|
||||
use sp_runtime::{RuntimeDebug, traits::BadOrigin};
|
||||
use frame_support::traits::{EnsureOrigin, OriginTrait, Filter, Get};
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[frame_support::pallet]
|
||||
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};
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::config]
|
||||
/// The module configuration trait.
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
|
||||
/// Required origin for sending XCM messages. If successful, the it resolves to `MultiLocation`
|
||||
/// which exists as an interior location within this chain's XCM context.
|
||||
type SendXcmOrigin: EnsureOrigin<Self::Origin, Success=MultiLocation>;
|
||||
|
||||
/// 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.
|
||||
type ExecuteXcmOrigin: EnsureOrigin<Self::Origin, Success=MultiLocation>;
|
||||
|
||||
/// Something to execute an XCM message.
|
||||
type XcmExecutor: ExecuteXcm<Self::Call>;
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
Attempted(xcm::v0::Outcome),
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
Unreachable,
|
||||
SendFailure,
|
||||
}
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::weight(1_000)]
|
||||
fn send(origin: OriginFor<T>, dest: MultiLocation, message: Xcm<()>) -> DispatchResult {
|
||||
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
|
||||
Self::send_xcm(origin_location, dest, message)
|
||||
.map_err(|e| match e {
|
||||
XcmError::CannotReachDestination(..) => Error::<T>::Unreachable,
|
||||
_ => Error::<T>::SendFailure,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute an XCM message from a local, signed, origin.
|
||||
///
|
||||
/// An event is deposited indicating whether `msg` could be executed completely or only
|
||||
/// partially.
|
||||
///
|
||||
/// No more than `max_weight` will be used in its attempted execution. If this is less than the
|
||||
/// maximum amount of weight that the message could take to be executed, then no execution
|
||||
/// attempt will be made.
|
||||
///
|
||||
/// 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))]
|
||||
fn execute(origin: OriginFor<T>, message: Box<Xcm<T::Call>>, max_weight: Weight)
|
||||
-> DispatchResult
|
||||
{
|
||||
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
||||
let outcome = T::XcmExecutor::execute_xcm(origin_location, *message, max_weight);
|
||||
Self::deposit_event(Event::Attempted(outcome));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Relay an XCM `message` from a given `interior` location in this context to a given `dest`
|
||||
/// location. A null `dest` is not handled.
|
||||
pub fn send_xcm(interior: MultiLocation, dest: MultiLocation, message: Xcm<()>) -> Result<(), XcmError> {
|
||||
let message = match interior {
|
||||
MultiLocation::Null => message,
|
||||
who => Xcm::<()>::RelayedFrom { who, message: Box::new(message) },
|
||||
};
|
||||
T::XcmRouter::send_xcm(dest, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Origin for the parachains module.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
|
||||
pub enum Origin {
|
||||
/// It comes from somewhere in the XCM space.
|
||||
Xcm(MultiLocation),
|
||||
}
|
||||
|
||||
impl From<MultiLocation> for Origin {
|
||||
fn from(location: MultiLocation) -> Origin {
|
||||
Origin::Xcm(location)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin `o` represents a sibling parachain.
|
||||
/// Returns `Ok` with the parachain ID of the sibling or an `Err` otherwise.
|
||||
pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<MultiLocation, BadOrigin>
|
||||
where OuterOrigin: Into<Result<Origin, OuterOrigin>>
|
||||
{
|
||||
match o.into() {
|
||||
Ok(Origin::Xcm(location)) => Ok(location),
|
||||
_ => Err(BadOrigin),
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter for `MultiLocation` to find those which represent a strict majority approval of an identified
|
||||
/// plurality.
|
||||
///
|
||||
/// May reasonably be used with `EnsureXcm`.
|
||||
pub struct IsMajorityOfBody<Body>(PhantomData<Body>);
|
||||
impl<Body: Get<BodyId>> Filter<MultiLocation> for IsMajorityOfBody<Body> {
|
||||
fn filter(l: &MultiLocation) -> bool {
|
||||
matches!(l, X1(Plurality { id, part }) if id == &Body::get() && part.is_majority())
|
||||
}
|
||||
}
|
||||
|
||||
/// `EnsureOrigin` implementation succeeding with a `MultiLocation` value to recognise and filter the
|
||||
/// `Origin::Xcm` item.
|
||||
pub struct EnsureXcm<F>(PhantomData<F>);
|
||||
impl<O: OriginTrait + From<Origin>, F: Filter<MultiLocation>> EnsureOrigin<O> for EnsureXcm<F>
|
||||
where O::PalletsOrigin: From<Origin> + TryInto<Origin, Error=O::PalletsOrigin>
|
||||
{
|
||||
type Success = MultiLocation;
|
||||
|
||||
fn try_origin(outer: O) -> Result<Self::Success, O> {
|
||||
outer.try_with_caller(|caller| caller.try_into()
|
||||
.and_then(|Origin::Xcm(location)|
|
||||
if F::filter(&location) {
|
||||
Ok(location)
|
||||
} else {
|
||||
Err(Origin::Xcm(location).into())
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn successful_origin() -> O {
|
||||
O::from(Origin::Xcm(MultiLocation::Null))
|
||||
}
|
||||
}
|
||||
@@ -61,12 +61,26 @@ pub enum BodyPart {
|
||||
Voice,
|
||||
/// A given number of members of the body.
|
||||
Members { #[codec(compact)] count: u32 },
|
||||
/// A given number of members of the body, out of some larger caucus.
|
||||
Fraction { #[codec(compact)] nom: u32, #[codec(compact)] denom: u32 },
|
||||
/// No less than the given proportion of members of the body.
|
||||
AtLeastProportion { #[codec(compact)] nom: u32, #[codec(compact)] denom: u32 },
|
||||
/// More than than the given proportion of members of the body.
|
||||
MoreThanProportion { #[codec(compact)] nom: u32, #[codec(compact)] denom: u32 },
|
||||
}
|
||||
|
||||
impl BodyPart {
|
||||
/// Returns `true` if the part represents a strict majority (> 50%) of the body in question.
|
||||
pub fn is_majority(&self) -> bool {
|
||||
match self {
|
||||
BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true,
|
||||
BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true,
|
||||
BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single item in a path to describe the relative location of a consensus system.
|
||||
///
|
||||
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
|
||||
|
||||
@@ -27,7 +27,7 @@ mod multi_asset;
|
||||
mod multi_location;
|
||||
mod order;
|
||||
mod traits;
|
||||
pub use junction::{Junction, NetworkId};
|
||||
pub use junction::{Junction, NetworkId, BodyId, BodyPart};
|
||||
pub use multi_asset::{MultiAsset, AssetInstance};
|
||||
pub use multi_location::MultiLocation;
|
||||
pub use order::Order;
|
||||
|
||||
@@ -21,7 +21,7 @@ use parity_scale_codec::{Encode, Decode};
|
||||
|
||||
use super::{MultiLocation, Xcm};
|
||||
|
||||
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Debug)]
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)]
|
||||
pub enum Error {
|
||||
Undefined,
|
||||
Overflow,
|
||||
@@ -33,7 +33,11 @@ pub enum Error {
|
||||
UntrustedReserveLocation,
|
||||
UntrustedTeleportLocation,
|
||||
DestinationBufferOverflow,
|
||||
CannotReachDestination(#[codec(skip)] &'static str),
|
||||
/// The message and destination was recognised as being reachable but the operation could not be completed.
|
||||
/// A human-readable explanation of the specific issue is provided.
|
||||
SendFailed(#[codec(skip)] &'static str),
|
||||
/// The message and destination combination was not recognised as being reachable.
|
||||
CannotReachDestination(MultiLocation, Xcm<()>),
|
||||
MultiLocationFull,
|
||||
FailedToDecode,
|
||||
BadOrigin,
|
||||
@@ -68,9 +72,6 @@ pub enum Error {
|
||||
NotWithdrawable,
|
||||
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
|
||||
LocationCannotHold,
|
||||
/// We attempted to send an XCM to the local consensus system. Execution was not possible probably due to
|
||||
/// no execution weight being assigned.
|
||||
DestinationIsLocal,
|
||||
}
|
||||
|
||||
impl From<()> for Error {
|
||||
@@ -85,7 +86,7 @@ pub type Result = result::Result<(), Error>;
|
||||
pub type Weight = u64;
|
||||
|
||||
/// Outcome of an XCM excution.
|
||||
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug)]
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)]
|
||||
pub enum Outcome {
|
||||
/// Execution completed successfully; given weight was used.
|
||||
Complete(Weight),
|
||||
@@ -121,21 +122,38 @@ impl Outcome {
|
||||
}
|
||||
|
||||
pub trait ExecuteXcm<Call> {
|
||||
type Call;
|
||||
fn execute_xcm(origin: MultiLocation, message: Xcm<Call>, weight_limit: Weight) -> Outcome;
|
||||
}
|
||||
|
||||
impl<C> ExecuteXcm<C> for () {
|
||||
type Call = C;
|
||||
fn execute_xcm(_origin: MultiLocation, _message: Xcm<C>, _weight_limit: Weight) -> Outcome {
|
||||
Outcome::Error(Error::Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility for sending an XCM message.
|
||||
///
|
||||
/// These can be amalgamted in tuples to form sophisticated routing systems.
|
||||
pub trait SendXcm {
|
||||
fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> Result;
|
||||
/// Send an XCM `message` to a given `destination`.
|
||||
///
|
||||
/// If it is not a destination which can be reached with this type but possibly could by others,
|
||||
/// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple implementation to
|
||||
/// exit early without trying other type fields.
|
||||
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result;
|
||||
}
|
||||
|
||||
impl SendXcm for () {
|
||||
fn send_xcm(_dest: MultiLocation, _msg: Xcm<()>) -> Result {
|
||||
Err(Error::Unimplemented)
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl SendXcm for Tuple {
|
||||
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
|
||||
for_tuples!( #(
|
||||
let (destination, message) = match Tuple::send_xcm(destination, message) {
|
||||
Err(Error::CannotReachDestination(d, m)) => (d, m),
|
||||
o @ _ => return o,
|
||||
};
|
||||
)* );
|
||||
Err(Error::CannotReachDestination(destination, message))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@ sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "mas
|
||||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
|
||||
# Polkadot dependencies
|
||||
polkadot-parachain = { path = "../../parachain", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
runtime-benchmarks = []
|
||||
std = [
|
||||
"parity-scale-codec/std",
|
||||
"xcm/std",
|
||||
|
||||
@@ -31,7 +31,7 @@ mod origin_conversion;
|
||||
pub use origin_conversion::{
|
||||
SovereignSignedViaLocation, ParentAsSuperuser, ChildSystemParachainAsSuperuser, SiblingSystemParachainAsSuperuser,
|
||||
ChildParachainAsNative, SiblingParachainAsNative, RelayChainAsNative, SignedAccountId32AsNative,
|
||||
SignedAccountKey20AsNative,
|
||||
SignedAccountKey20AsNative, EnsureXcmOrigin, SignedToAccountId32
|
||||
};
|
||||
|
||||
mod barriers;
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
// 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 frame_support::traits::{Get, OriginTrait};
|
||||
use xcm::v0::{MultiLocation, OriginKind, NetworkId, Junction};
|
||||
use sp_std::{marker::PhantomData, convert::TryInto};
|
||||
use xcm::v0::{MultiLocation, OriginKind, NetworkId, Junction, BodyId, BodyPart};
|
||||
use xcm_executor::traits::{Convert, ConvertOrigin};
|
||||
use frame_support::traits::{EnsureOrigin, Get, OriginTrait, GetBacking};
|
||||
use frame_system::RawOrigin as SystemRawOrigin;
|
||||
use polkadot_parachain::primitives::IsSystem;
|
||||
|
||||
/// Sovereign accounts use the system's `Signed` origin with an account ID derived from the
|
||||
@@ -169,3 +170,87 @@ impl<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// EnsureOrigin barrier to convert from dispatch origin to XCM origin, if one exists.
|
||||
pub struct EnsureXcmOrigin<Origin, Conversion>(PhantomData<(Origin, Conversion)>);
|
||||
impl<
|
||||
Origin: OriginTrait + Clone,
|
||||
Conversion: Convert<Origin, MultiLocation>,
|
||||
> EnsureOrigin<Origin> for EnsureXcmOrigin<Origin, Conversion> where
|
||||
Origin::PalletsOrigin: PartialEq,
|
||||
{
|
||||
type Success = MultiLocation;
|
||||
fn try_origin(o: Origin) -> Result<Self::Success, Origin> {
|
||||
let o = match Conversion::convert(o) {
|
||||
Ok(location) => return Ok(location),
|
||||
Err(o) => o,
|
||||
};
|
||||
// We institute a root fallback so root can always represent the context. This
|
||||
// guarantees that `successful_origin` will work.
|
||||
if o.caller() == Origin::root().caller() {
|
||||
Ok(MultiLocation::Null)
|
||||
} else {
|
||||
Err(o)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn successful_origin() -> Origin {
|
||||
Origin::root()
|
||||
}
|
||||
}
|
||||
|
||||
/// `Convert` implementation to convert from some a `Signed` (system) `Origin` into an `AccountId32`.
|
||||
///
|
||||
/// Typically used when configuring `pallet-xcm` for allowing normal accounts to dispatch an XCM from an `AccountId32`
|
||||
/// origin.
|
||||
pub struct SignedToAccountId32<Origin, AccountId, Network>(
|
||||
PhantomData<(Origin, AccountId, Network)>
|
||||
);
|
||||
impl<
|
||||
Origin: OriginTrait + Clone,
|
||||
AccountId: Into<[u8; 32]>,
|
||||
Network: Get<NetworkId>,
|
||||
> Convert<Origin, MultiLocation> for SignedToAccountId32<Origin, AccountId, Network> where
|
||||
Origin::PalletsOrigin: From<SystemRawOrigin<AccountId>> +
|
||||
TryInto<SystemRawOrigin<AccountId>, Error=Origin::PalletsOrigin>
|
||||
{
|
||||
fn convert(o: Origin) -> Result<MultiLocation, Origin> {
|
||||
o.try_with_caller(|caller| match caller.try_into() {
|
||||
Ok(SystemRawOrigin::Signed(who)) =>
|
||||
Ok(Junction::AccountId32 { network: Network::get(), id: who.into() }.into()),
|
||||
Ok(other) => Err(other.into()),
|
||||
Err(other) => Err(other),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// `Convert` implementation to convert from some an origin which implements `Backing` into a corresponding `Plurality`
|
||||
/// MultiLocation.
|
||||
///
|
||||
/// Typically used when configuring `pallet-xcm` for allowing a collective's Origin to dispatch an XCM from a
|
||||
/// `Plurality` origin.
|
||||
pub struct BackingToPlurality<Origin, COrigin, Body>(
|
||||
PhantomData<(Origin, COrigin, Body)>
|
||||
);
|
||||
impl<
|
||||
Origin: OriginTrait + Clone,
|
||||
COrigin: GetBacking,
|
||||
Body: Get<BodyId>,
|
||||
> Convert<Origin, MultiLocation> for BackingToPlurality<Origin, COrigin, Body> where
|
||||
Origin::PalletsOrigin: From<COrigin> +
|
||||
TryInto<COrigin, Error=Origin::PalletsOrigin>
|
||||
{
|
||||
fn convert(o: Origin) -> Result<MultiLocation, Origin> {
|
||||
o.try_with_caller(|caller| match caller.try_into() {
|
||||
Ok(co) => match co.get_backing() {
|
||||
Some(backing) => Ok(Junction::Plurality {
|
||||
id: Body::get(),
|
||||
part: BodyPart::Fraction { nom: backing.approvals, denom: backing.eligible },
|
||||
}.into()),
|
||||
None => Err(co.into()),
|
||||
}
|
||||
Err(other) => Err(other),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ 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 {
|
||||
// TODO: #2841 #HARDENXCM We should identify recursive bombs here and bail.
|
||||
let mut message = Xcm::<Config::Call>::from(message);
|
||||
|
||||
Reference in New Issue
Block a user