Skip to content
Snippets Groups Projects
Commit 86f9a991 authored by Gavin Wood's avatar Gavin Wood Committed by GitHub
Browse files

XCM revamp & Ping pallet (#391)


* Add spambot

* Fixes

* Add some extra functions to spambot, bump version

* Lock..

* Aggregate HRMP (XCMP/HMP) messages. Payloads for spambot.

* Fix tests, bump Polkadot.

* Fix HMP tests

* Rename Hrmp -> Xcmp for handler/sender

* Use master branch

* Test Xcm message passing & rename away from HMP

* Docs

* Introduce fee payment mechanics into XCM.

* Rename spambot -> ping

* Lock

* 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

* Should process any remaining XCM messages when we're not doing anything else.

* Update API usage and preparation for the big build.

* Some build fixes

* Build fixes

* xcm-handler builds

* Fix warnings

* Docs

* Parachains system builds

* Parachain runtime building

* Fix build

* Introduce transfer_asset specialisation.

* Fixes

* Two-stage upgrade for parachains.

* Fixes

* Fixes

* Updates for message sending.

* Repotting/renaming. Add primitives/utility.

* Remove real-overseer and bump refs

* Configure & document Rococo XCM runtime.

* Add shell runtime, some companion changes for #8589

* Bumps & fixes

* Fix test

* Build fix

* Update pallets/xcmp-queue/src/lib.rs

Co-authored-by: default avatarAmar Singh <asinghchrony@protonmail.com>

* Make tests compile

* Apply suggestions from code review

Co-authored-by: default avatarBastian Köcher <bkchr@users.noreply.github.com>

* remove unused

* remove unused event stuff

* Adds proper validation-worker to make integration tests work

* Apply suggestions from code review

Co-authored-by: default avatarBastian Köcher <bkchr@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: default avatarBastian Köcher <bkchr@users.noreply.github.com>

* import saturating

* remove panic test

Co-authored-by: default avatarRobert Habermeier <rphmeier@gmail.com>
Co-authored-by: default avatarBastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: default avatarAmar Singh <asinghchrony@protonmail.com>
Co-authored-by: default avatarShawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: default avatarBastian Köcher <info@kchr.de>
parent 2516e069
Branches
No related merge requests found
Showing
with 2057 additions and 1065 deletions
This diff is collapsed.
......@@ -6,17 +6,22 @@ members = [
"client/network",
"client/service",
"pallets/parachain-system",
"pallets/xcm-handler",
"pallets/xcm",
"pallets/xcmp-queue",
"primitives/core",
"primitives/parachain-inherent",
"primitives/utility",
"rococo-parachains/",
"rococo-parachains/pallets/parachain-info",
"rococo-parachains/pallets/ping",
"rococo-parachains/primitives",
"rococo-parachains/runtime",
"rococo-parachains/shell-runtime",
"test/runtime",
"test/client",
"test/service",
"test/relay-sproof-builder",
"test/relay-validation-worker-provider",
]
[profile.release]
......
......@@ -82,16 +82,16 @@ chain, and from the relay chain to its destination parachain.
git clone https://github.com/paritytech/polkadot
git fetch
git checkout rococo-v1
cargo build --release --features=real-overseer
cargo build --release
# Generate a raw chain spec
./target/release/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > rococo-local-cfde-real-overseer.json
./target/release/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > rococo-local-cfde.json
# Alice
./target/release/polkadot --chain rococo-local-cfde-real-overseer.json --alice --tmp
./target/release/polkadot --chain rococo-local-cfde.json --alice --tmp
# Bob (In a separate terminal)
./target/release/polkadot --chain rococo-local-cfde-real-overseer.json --bob --tmp --port 30334
./target/release/polkadot --chain rococo-local-cfde.json --bob --tmp --port 30334
```
### Launch the Parachain
......@@ -111,13 +111,13 @@ cargo build --release
./target/release/rococo-collator export-genesis-wasm > genesis-wasm
# Collator1
./target/release/rococo-collator --collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40335 --ws-port 9946 -- --execution wasm --chain ../polkadot/rococo-local-cfde-real-overseer.json --port 30335
./target/release/rococo-collator --collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40335 --ws-port 9946 -- --execution wasm --chain ../polkadot/rococo-local-cfde.json --port 30335
# Collator2
./target/release/rococo-collator --collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40336 --ws-port 9947 -- --execution wasm --chain ../polkadot/rococo-local-cfde-real-overseer.json --port 30336
./target/release/rococo-collator --collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40336 --ws-port 9947 -- --execution wasm --chain ../polkadot/rococo-local-cfde.json --port 30336
# Parachain Full Node 1
./target/release/rococo-collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40337 --ws-port 9948 -- --execution wasm --chain ../polkadot/rococo-local-cfde-real-overseer.json --port 30337
./target/release/rococo-collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40337 --ws-port 9948 -- --execution wasm --chain ../polkadot/rococo-local-cfde.json --port 30337
```
### Register the parachain
![image](https://user-images.githubusercontent.com/2915325/99548884-1be13580-2987-11eb-9a8b-20be658d34f9.png)
......@@ -400,10 +400,15 @@ mod tests {
use cumulus_test_runtime::{Block, Header};
use futures::{channel::mpsc, executor::block_on, StreamExt};
use polkadot_node_subsystem_test_helpers::ForwardSubsystem;
use polkadot_overseer::{AllSubsystems, Overseer};
use polkadot_overseer::{AllSubsystems, Overseer, HeadSupportsParachains};
use sp_consensus::BlockOrigin;
use sp_core::{testing::TaskExecutor, Pair};
struct AlwaysSupportsParachains;
impl HeadSupportsParachains for AlwaysSupportsParachains {
fn head_supports_parachains(&self, _head: &PHash) -> bool { true }
}
#[derive(Clone)]
struct DummyParachainConsensus {
client: Arc<Client>,
......@@ -454,8 +459,13 @@ mod tests {
let all_subsystems =
AllSubsystems::<()>::dummy().replace_collation_generation(ForwardSubsystem(sub_tx));
let (overseer, handler) = Overseer::new(Vec::new(), all_subsystems, None, spawner.clone())
.expect("Creates overseer");
let (overseer, handler) = Overseer::new(
Vec::new(),
all_subsystems,
None,
AlwaysSupportsParachains,
spawner.clone(),
).expect("Creates overseer");
spawner.spawn("overseer", overseer.run().then(|_| async { () }).boxed());
......
......@@ -18,7 +18,7 @@ sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "mas
substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot dependencies
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master", features = [ "real-overseer" ] }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# Cumulus dependencies
cumulus-client-consensus-common = { path = "../common" }
......
......@@ -38,7 +38,6 @@ cumulus-primitives-core = { path = "../../primitives/core" }
# Polkadot deps
polkadot-test-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# substrate deps
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
......
......@@ -15,7 +15,7 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use cumulus_primitives_core::ParaId;
use cumulus_test_service::{initial_head_data, Keyring::*};
use cumulus_test_service::{initial_head_data, run_relay_chain_validator_node, Keyring::*};
use futures::join;
use sc_service::TaskExecutor;
......@@ -28,16 +28,11 @@ async fn sync_blocks_from_tip_without_being_connected_to_a_collator(task_executo
let para_id = ParaId::from(100);
// start alice
let alice =
polkadot_test_service::run_validator_node(task_executor.clone(), Alice, || {}, vec![]);
let alice = run_relay_chain_validator_node(task_executor.clone(), Alice, || {}, vec![]);
// start bob
let bob = polkadot_test_service::run_validator_node(
task_executor.clone(),
Bob,
|| {},
vec![alice.addr.clone()],
);
let bob =
run_relay_chain_validator_node(task_executor.clone(), Bob, || {}, vec![alice.addr.clone()]);
// register parachain
alice
......
......@@ -12,6 +12,7 @@ cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inh
# Polkadot dependencies
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, features = [ "wasm-api" ], branch = "master" }
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
# Substrate dependencies
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
......@@ -72,4 +73,5 @@ std = [
"cumulus-primitives-core/std",
"cumulus-primitives-parachain-inherent/std",
"environmental/std",
"xcm/std"
]
This diff is collapsed.
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate 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.
// Substrate 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/>.
//! A pallet which implements the message handling APIs for handling incoming XCM:
//! * `DownwardMessageHandler`
//! * `HrmpMessageHandler`
//!
//! Also provides an implementation of `SendXcm` to handle outgoing XCM.
#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, Encode};
use cumulus_primitives_core::{
DownwardMessageHandler, HrmpMessageHandler, HrmpMessageSender, InboundDownwardMessage,
InboundHrmpMessage, OutboundHrmpMessage, ParaId, UpwardMessageSender,
};
use frame_support::{decl_error, decl_event, decl_module, dispatch::DispatchResult, sp_runtime::traits::Hash, traits::EnsureOrigin};
use sp_std::convert::{TryFrom, TryInto};
use xcm::{
v0::{Error as XcmError, ExecuteXcm, Junction, MultiLocation, SendXcm, Xcm},
VersionedXcm,
};
use xcm_executor::traits::LocationConversion;
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
/// Something to execute an XCM message.
type XcmExecutor: ExecuteXcm;
/// Something to send an upward message.
type UpwardMessageSender: UpwardMessageSender;
/// Something to send an HRMP message.
type HrmpMessageSender: HrmpMessageSender;
/// Required origin for sending XCM messages. Typically Root or parachain
/// council majority.
type SendXcmOrigin: EnsureOrigin<Self::Origin>;
/// Utility for converting from the signed origin (of type `Self::AccountId`) into a sensible
/// `MultiLocation` ready for passing to the XCM interpreter.
type AccountIdConverter: LocationConversion<Self::AccountId>;
}
decl_event! {
pub enum Event<T> where Hash = <T as frame_system::Config>::Hash {
/// Some XCM was executed ok.
Success(Hash),
/// Some XCM failed.
Fail(Hash, XcmError),
/// Bad XCM version used.
BadVersion(Hash),
/// Bad XCM format used.
BadFormat(Hash),
/// An upward message was sent to the relay chain.
UpwardMessageSent(Hash),
/// An HRMP message was sent to a sibling parachain.
HrmpMessageSent(Hash),
}
}
decl_error! {
pub enum Error for Module<T: Config> {
/// Failed to send XCM message.
FailedToSend,
/// Bad XCM origin.
BadXcmOrigin,
}
}
decl_module! {
pub struct Module<T: Config> for enum Call where origin: T::Origin {
type Error = Error<T>;
fn deposit_event() = default;
#[weight = 1_000]
fn send_xcm(origin, dest: MultiLocation, message: Xcm) {
T::SendXcmOrigin::ensure_origin(origin)?;
<Self as SendXcm>::send_xcm(dest, message).map_err(|_| Error::<T>::FailedToSend)?;
}
#[weight = 1_000]
fn send_upward_xcm(origin, message: VersionedXcm) {
T::SendXcmOrigin::ensure_origin(origin)?;
let data = message.encode();
T::UpwardMessageSender::send_upward_message(data).map_err(|_| Error::<T>::FailedToSend)?;
}
#[weight = 1_000]
fn send_hrmp_xcm(origin, recipient: ParaId, message: VersionedXcm) {
T::SendXcmOrigin::ensure_origin(origin)?;
let data = message.encode();
let outbound_message = OutboundHrmpMessage {
recipient,
data,
};
T::HrmpMessageSender::send_hrmp_message(outbound_message).map_err(|_| Error::<T>::FailedToSend)?;
}
}
}
impl<T: Config> Module<T> {
/// Execute an XCM message locally. Returns `DispatchError` if failed.
pub fn execute_xcm(origin: T::AccountId, xcm: Xcm) -> DispatchResult {
let xcm_origin = T::AccountIdConverter::try_into_location(origin)
.map_err(|_| Error::<T>::BadXcmOrigin)?;
let hash = T::Hashing::hash(&xcm.encode());
let event = match T::XcmExecutor::execute_xcm(xcm_origin, xcm) {
Ok(_) => Event::<T>::Success(hash),
Err(e) => Event::<T>::Fail(hash, e),
};
Self::deposit_event(event);
Ok(())
}
}
impl<T: Config> DownwardMessageHandler for Module<T> {
fn handle_downward_message(msg: InboundDownwardMessage) {
let hash = msg.using_encoded(T::Hashing::hash);
log::debug!("Processing Downward XCM: {:?}", &hash);
let event = match VersionedXcm::decode(&mut &msg.msg[..]).map(Xcm::try_from) {
Ok(Ok(xcm)) => {
match T::XcmExecutor::execute_xcm(Junction::Parent.into(), xcm) {
Ok(..) => RawEvent::Success(hash),
Err(e) => RawEvent::Fail(hash, e),
}
}
Ok(Err(..)) => RawEvent::BadVersion(hash),
Err(..) => RawEvent::BadFormat(hash),
};
Self::deposit_event(event);
}
}
impl<T: Config> HrmpMessageHandler for Module<T> {
fn handle_hrmp_message(sender: ParaId, msg: InboundHrmpMessage) {
let hash = msg.using_encoded(T::Hashing::hash);
log::debug!("Processing HRMP XCM: {:?}", &hash);
let event = match VersionedXcm::decode(&mut &msg.data[..]).map(Xcm::try_from) {
Ok(Ok(xcm)) => {
let location = (
Junction::Parent,
Junction::Parachain { id: sender.into() },
);
match T::XcmExecutor::execute_xcm(location.into(), xcm) {
Ok(..) => RawEvent::Success(hash),
Err(e) => RawEvent::Fail(hash, e),
}
}
Ok(Err(..)) => RawEvent::BadVersion(hash),
Err(..) => RawEvent::BadFormat(hash),
};
Self::deposit_event(event);
}
}
impl<T: Config> SendXcm for Module<T> {
fn send_xcm(dest: MultiLocation, msg: Xcm) -> Result<(), XcmError> {
let msg: VersionedXcm = msg.into();
match dest.first() {
// A message for us. Execute directly.
None => {
let msg = msg.try_into().map_err(|_| XcmError::UnhandledXcmVersion)?;
let res = T::XcmExecutor::execute_xcm(MultiLocation::Null, msg);
res
}
// An upward message for the relay chain.
Some(Junction::Parent) if dest.len() == 1 => {
let data = msg.encode();
let hash = T::Hashing::hash(&data);
T::UpwardMessageSender::send_upward_message(data)
.map_err(|_| XcmError::CannotReachDestination)?;
Self::deposit_event(RawEvent::UpwardMessageSent(hash));
Ok(())
}
// An HRMP message for a sibling parachain.
Some(Junction::Parent) if dest.len() == 2 => {
if let Some(Junction::Parachain { id }) = dest.at(1) {
let data = msg.encode();
let hash = T::Hashing::hash(&data);
let message = OutboundHrmpMessage {
recipient: (*id).into(),
data,
};
T::HrmpMessageSender::send_hrmp_message(message)
.map_err(|_| XcmError::CannotReachDestination)?;
Self::deposit_event(RawEvent::HrmpMessageSent(hash));
Ok(())
} else {
Err(XcmError::UnhandledXcmMessage)
}
}
_ => {
/* TODO: Handle other cases, like downward message */
Err(XcmError::UnhandledXcmMessage)
}
}
}
}
/// Origin for the parachains module.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Origin {
/// It comes from the (parent) relay chain.
Relay,
/// It comes from a (sibling) parachain.
SiblingParachain(ParaId),
}
impl From<ParaId> for Origin {
fn from(id: ParaId) -> Origin {
Origin::SiblingParachain(id)
}
}
impl From<u32> for Origin {
fn from(id: u32) -> Origin {
Origin::SiblingParachain(id.into())
}
}
[package]
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
name = "cumulus-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 = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
cumulus-primitives-core = { path = "../../primitives/core", default-features = false }
[features]
default = ["std"]
std = [
"codec/std",
"serde",
"cumulus-primitives-core/std",
"sp-std/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
]
// 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 for stuff specific to parachains' usage of XCM. Right now that's just the origin
//! used by parachains when receiving `Transact` messages from other parachains or the Relay chain
//! which must be natively represented.
#![cfg_attr(not(feature = "std"), no_std)]
use cumulus_primitives_core::ParaId;
use codec::{Encode, Decode};
use sp_runtime::traits::BadOrigin;
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
/// The module configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::error]
pub enum Error<T> {}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
}
/// Origin for the parachains module.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Origin {
/// It comes from the (parent) relay chain.
Relay,
/// It comes from a (sibling) parachain.
SiblingParachain(ParaId),
}
impl From<ParaId> for Origin {
fn from(id: ParaId) -> Origin {
Origin::SiblingParachain(id)
}
}
impl From<u32> for Origin {
fn from(id: u32) -> Origin {
Origin::SiblingParachain(id.into())
}
}
/// 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_sibling_para<OuterOrigin>(o: OuterOrigin) -> Result<ParaId, BadOrigin>
where OuterOrigin: Into<Result<Origin, OuterOrigin>>
{
match o.into() {
Ok(Origin::SiblingParachain(id)) => Ok(id),
_ => Err(BadOrigin),
}
}
/// Ensure that the origin `o` represents is the relay chain.
/// Returns `Ok` if it does or an `Err` otherwise.
pub fn ensure_relay<OuterOrigin>(o: OuterOrigin) -> Result<(), BadOrigin>
where OuterOrigin: Into<Result<Origin, OuterOrigin>>
{
match o.into() {
Ok(Origin::Relay) => Ok(()),
_ => Err(BadOrigin),
}
}
[package]
name = "cumulus-pallet-xcm-handler"
name = "cumulus-pallet-xcmp-queue"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
......@@ -8,9 +8,12 @@ edition = "2018"
# Other dependencies
codec = { package = "parity-scale-codec", version = "2.0.0", features = [ "derive" ], default-features = false }
log = { version = "0.4.14", default-features = false }
rand = { version = "0.8.3", default-features = false }
rand_chacha = { version = "0.3.0", default-features = false }
# Substrate Dependencies
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" }
......@@ -27,6 +30,7 @@ std = [
"codec/std",
"log/std",
"sp-std/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"cumulus-primitives-core/std",
......
This diff is collapsed.
......@@ -9,16 +9,19 @@ edition = "2018"
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" }
sp-trie = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
# Polkadot dependencies
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
# Other dependencies
impl-trait-for-tuples = "0.2.1"
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive" ] }
[features]
default = [ "std" ]
std = [
......@@ -29,4 +32,5 @@ std = [
"polkadot-core-primitives/std",
"sp-runtime/std",
"sp-trie/std",
"frame-support/std",
]
......@@ -18,7 +18,10 @@
#![cfg_attr(not(feature = "std"), no_std)]
use sp_runtime::traits::Block as BlockT;
use sp_std::prelude::*;
use codec::{Encode, Decode};
use sp_runtime::{RuntimeDebug, traits::Block as BlockT};
use frame_support::weights::Weight;
pub use polkadot_core_primitives::InboundDownwardMessage;
pub use polkadot_parachain::primitives::{Id as ParaId, UpwardMessage, ValidationParams};
......@@ -32,6 +35,7 @@ pub mod relay_chain {
pub use polkadot_primitives::v1;
pub use polkadot_primitives::v1::well_known_keys;
}
use relay_chain::BlockNumber as RelayBlockNumber;
/// An inbound HRMP message.
pub type InboundHrmpMessage = polkadot_primitives::v1::InboundHrmpMessage<relay_chain::BlockNumber>;
......@@ -39,6 +43,52 @@ pub type InboundHrmpMessage = polkadot_primitives::v1::InboundHrmpMessage<relay_
/// And outbound HRMP message
pub type OutboundHrmpMessage = polkadot_primitives::v1::OutboundHrmpMessage<ParaId>;
/// Error description of a message send failure.
#[derive(Eq, PartialEq, Copy, Clone, RuntimeDebug, Encode, Decode)]
pub enum MessageSendError {
/// The dispatch queue is full.
QueueFull,
/// There does not exist a channel for sending the message.
NoChannel,
/// The message is too big to ever fit in a channel.
TooBig,
/// Some other error.
Other,
}
impl From<MessageSendError> for &'static str {
fn from(e: MessageSendError) -> Self {
use MessageSendError::*;
match e {
QueueFull => "QueueFull",
NoChannel => "NoChannel",
TooBig => "TooBig",
Other => "Other",
}
}
}
/// Information about an XCMP channel.
pub struct ChannelInfo {
/// The maximum number of messages that can be pending in the channel at once.
pub max_capacity: u32,
/// The maximum total size of the messages that can be pending in the channel at once.
pub max_total_size: u32,
/// The maximum message size that could be put into the channel.
pub max_message_size: u32,
/// The current number of messages pending in the channel.
/// Invariant: should be less or equal to `max_capacity`.s`.
pub msg_count: u32,
/// The total size in bytes of all message payloads in the channel.
/// Invariant: should be less or equal to `max_total_size`.
pub total_size: u32,
}
pub trait GetChannelInfo {
fn get_channel_status(id: ParaId) -> ChannelStatus;
fn get_channel_max(id: ParaId) -> Option<usize>;
}
/// Well known keys for values in the storage.
pub mod well_known_keys {
/// The storage key for the upward messages.
......@@ -68,29 +118,81 @@ pub mod well_known_keys {
}
/// Something that should be called when a downward message is received.
#[impl_trait_for_tuples::impl_for_tuples(30)]
pub trait DownwardMessageHandler {
/// Handle the given downward message.
fn handle_downward_message(msg: InboundDownwardMessage);
fn handle_downward_message(msg: InboundDownwardMessage) -> Weight;
}
impl DownwardMessageHandler for () {
fn handle_downward_message(_msg: InboundDownwardMessage) -> Weight { 0 }
}
/// Something that should be called when an HRMP message is received.
#[impl_trait_for_tuples::impl_for_tuples(30)]
pub trait HrmpMessageHandler {
/// Handle the given HRMP message.
fn handle_hrmp_message(sender: ParaId, msg: InboundHrmpMessage);
/// Something that should be called for each batch of messages received over XCMP.
pub trait XcmpMessageHandler {
/// Handle some incoming XCMP messages (note these are the big one-per-block aggregate
/// messages).
///
/// Also, process messages up to some `max_weight`.
fn handle_xcmp_messages<'a, I: Iterator<Item=(ParaId, RelayBlockNumber, &'a [u8])>>(
iter: I,
max_weight: Weight,
) -> Weight;
}
impl XcmpMessageHandler for () {
fn handle_xcmp_messages<'a, I: Iterator<Item=(ParaId, RelayBlockNumber, &'a [u8])>>(
iter: I,
_max_weight: Weight,
) -> Weight { for _ in iter {} 0 }
}
/// Something that should be called when sending an upward message.
pub trait UpwardMessageSender {
/// Send the given upward message.
fn send_upward_message(msg: UpwardMessage) -> Result<(), ()>;
/// Send the given UMP message; return the expected number of blocks before the message will
/// be dispatched or an error if the message cannot be sent.
fn send_upward_message(msg: UpwardMessage) -> Result<u32, MessageSendError>;
}
impl UpwardMessageSender for () {
fn send_upward_message(_msg: UpwardMessage) -> Result<u32, MessageSendError> {
Err(MessageSendError::NoChannel)
}
}
/// The status of a channel.
pub enum ChannelStatus {
/// Channel doesn't exist/has been closed.
Closed,
/// Channel is completely full right now.
Full,
/// Channel is ready for sending; the two parameters are the maximum size a valid message may
/// have right now, and the maximum size a message may ever have (this will generally have been
/// available during message construction, but it's possible the channel parameters changed in
/// the meantime).
Ready(usize, usize),
}
/// A means of figuring out what outbound XCMP messages should be being sent.
pub trait XcmpMessageSource {
/// Take a single XCMP message from the queue for the given `dest`, if one exists.
fn take_outbound_messages(
maximum_channels: usize,
) -> Vec<(ParaId, Vec<u8>)>;
}
impl XcmpMessageSource for () {
fn take_outbound_messages(
_maximum_channels: usize,
) -> Vec<(ParaId, Vec<u8>)> { vec![] }
}
/// Something that should be called when sending an HRMP message.
pub trait HrmpMessageSender {
/// Send the given HRMP message.
fn send_hrmp_message(msg: OutboundHrmpMessage) -> Result<(), ()>;
/// The "quality of service" considerations for message sending.
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug)]
pub enum ServiceQuality {
/// Ensure that this message is dispatched in the same relative order as any other messages that
/// were also sent with `Ordered`. This only guarantees message ordering on the dispatch side,
/// and not necessarily on the execution side.
Ordered,
/// Ensure that the message is dispatched as soon as possible, which could result in it being
/// dispatched before other messages which are larger and/or rely on relative ordering.
Fast,
}
/// A trait which is called when the validation data is set.
......
[package]
name = "cumulus-primitives-utility"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
# Substrate dependencies
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" }
sp-trie = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
# Polkadot dependencies
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
cumulus-primitives-core = { path = "../core", default-features = false }
# Other dependencies
impl-trait-for-tuples = "0.2.1"
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive" ] }
[features]
default = [ "std" ]
std = [
"codec/std",
"sp-std/std",
"polkadot-primitives/std",
"polkadot-parachain/std",
"polkadot-core-primitives/std",
"sp-runtime/std",
"sp-trie/std",
"frame-support/std",
"cumulus-primitives-core/std",
]
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate 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.
// Substrate 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/>.
//! Helper datatypes for cumulus. This includes the [`ParentAsUmp`] routing type which will route
//! messages into an [`UpwardMessageSender`] if the destination is `Parent`.
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::{marker::PhantomData, convert::TryFrom};
use codec::{Encode, Decode};
use cumulus_primitives_core::{UpwardMessageSender, DownwardMessageHandler, InboundDownwardMessage};
use xcm::{VersionedXcm, v0::{Xcm, MultiLocation, Junction, SendXcm, Error as XcmError, ExecuteXcm}};
use frame_support::{traits::Get, dispatch::Weight};
/// Xcm router which recognises the `Parent` destination and handles it by sending the message into
/// the given UMP `UpwardMessageSender` implementation. Thus this essentially adapts an
/// `UpwardMessageSender` trait impl into a `SendXcm` trait impl.
///
/// NOTE: This is a pretty dumb "just send it" router; we will probably want to introduce queuing
/// to UMP eventually and when we do, the pallet which implements the queuing will be responsible
/// for the `SendXcm` implementation.
pub struct ParentAsUmp<T>(PhantomData<T>);
impl<T: UpwardMessageSender> SendXcm for ParentAsUmp<T> {
fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> Result<(), XcmError> {
match &dest {
// An upward message for the relay chain.
MultiLocation::X1(Junction::Parent) => {
let data = VersionedXcm::<()>::from(msg).encode();
T::send_upward_message(data)
.map_err(|e| XcmError::SendFailed(e.into()))?;
Ok(())
}
// Anything else is unhandled. This includes a message this is meant for us.
_ => Err(XcmError::CannotReachDestination(dest, msg)),
}
}
}
/// For an incoming downward message, this just adapts an XCM executor and executes DMP messages
/// immediately up until some `MaxWeight` at which point it errors. Their origin is asserted to be
/// the Parent location.
pub struct UnqueuedDmpAsParent<MaxWeight, XcmExecutor, Call>(
PhantomData<(MaxWeight, XcmExecutor, Call)>
);
impl<
MaxWeight: Get<Weight>,
XcmExecutor: ExecuteXcm<Call>,
Call,
> DownwardMessageHandler for UnqueuedDmpAsParent<MaxWeight, XcmExecutor, Call> {
fn handle_downward_message(msg: InboundDownwardMessage) -> Weight {
let msg = VersionedXcm::<Call>::decode(&mut &msg.msg[..])
.map(Xcm::<Call>::try_from);
match msg {
Ok(Ok(x)) => {
let weight_limit = MaxWeight::get();
XcmExecutor::execute_xcm(Junction::Parent.into(), x, weight_limit).weight_used()
}
Ok(Err(..)) => 0,
Err(..) => 0,
}
}
}
......@@ -23,6 +23,7 @@ hex-literal = "0.2.1"
# Parachain dependencies
parachain-runtime = { package = "cumulus-test-parachain-runtime", path = "runtime" }
shell-runtime = { package = "cumulus-shell-runtime", path = "shell-runtime" }
rococo-parachain-primitives = { path = "primitives" }
# Substrate dependencies
......@@ -67,7 +68,6 @@ cumulus-primitives-core = { path = "../primitives/core" }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" }
[build-dependencies]
......
This diff is collapsed.
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