From 73482bff3359f53ef52c3d954543f389f4db680d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 16 May 2024 02:01:49 +0200 Subject: [PATCH] XCM Cookbook (#2633) # Context XCM docs are currently an md book hosted with github pages: https://paritytech.github.io/xcm-docs/. While that's fine, it's not in line with the work being done in the polkadot-sdk docs. # Main addition This PR aims to fix that by bringing the docs back to this repo. This does not have all the information currently present in the mdbook xcm-docs but aims to be a good chunk of it and fully replace it over time. I also added the sections `guides` and `cookbook` which will be very useful for users wanting to get into XCM. For now I only added one example to the cookbook, but have ideas for guides and more examples. Having this docs be in rust docs is very useful for the cookbook. # TODO - [x] Use `FungibleAdapter` - [x] Improve and relocate mock message queue - [x] Fix license issue. Why does docs/sdk/ not have this problem? (Just added the licenses) # Next steps - More examples in the cookbook - End-to-end XCM guide with zombienet testing --------- Co-authored-by: command-bot <> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- Cargo.lock | 62 +++++- Cargo.toml | 1 + polkadot/xcm/docs/Cargo.toml | 39 ++++ .../xcm/docs/mermaid/location_hierarchy.mmd | 9 + polkadot/xcm/docs/mermaid/structure.mmd | 4 + .../xcm/docs/mermaid/transport_protocols.mmd | 6 + .../xcm/docs/mermaid/universal_location.mmd | 3 + polkadot/xcm/docs/mermaid/usdt_location.mmd | 6 + polkadot/xcm/docs/src/cookbook/mod.rs | 27 +++ .../cookbook/relay_token_transactor/mod.rs | 51 +++++ .../relay_token_transactor/network.rs | 90 +++++++++ .../relay_token_transactor/parachain/mod.rs | 56 ++++++ .../parachain/xcm_config.rs | 189 ++++++++++++++++++ .../relay_token_transactor/relay_chain/mod.rs | 103 ++++++++++ .../relay_chain/xcm_config.rs | 163 +++++++++++++++ .../cookbook/relay_token_transactor/tests.rs | 128 ++++++++++++ polkadot/xcm/docs/src/fundamentals.rs | 177 ++++++++++++++++ polkadot/xcm/docs/src/glossary.rs | 123 ++++++++++++ polkadot/xcm/docs/src/guides/mod.rs | 25 +++ polkadot/xcm/docs/src/lib.rs | 63 ++++++ polkadot/xcm/xcm-simulator/Cargo.toml | 4 + .../example/src/parachain/mod.rs | 6 +- .../src/parachain/xcm_config/constants.rs | 5 +- .../xcm/xcm-simulator/example/src/tests.rs | 6 +- polkadot/xcm/xcm-simulator/src/lib.rs | 4 + .../mock_message_queue.rs} | 79 ++++---- 26 files changed, 1381 insertions(+), 48 deletions(-) create mode 100644 polkadot/xcm/docs/Cargo.toml create mode 100644 polkadot/xcm/docs/mermaid/location_hierarchy.mmd create mode 100644 polkadot/xcm/docs/mermaid/structure.mmd create mode 100644 polkadot/xcm/docs/mermaid/transport_protocols.mmd create mode 100644 polkadot/xcm/docs/mermaid/universal_location.mmd create mode 100644 polkadot/xcm/docs/mermaid/usdt_location.mmd create mode 100644 polkadot/xcm/docs/src/cookbook/mod.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs create mode 100644 polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs create mode 100644 polkadot/xcm/docs/src/fundamentals.rs create mode 100644 polkadot/xcm/docs/src/glossary.rs create mode 100644 polkadot/xcm/docs/src/guides/mod.rs create mode 100644 polkadot/xcm/docs/src/lib.rs rename polkadot/xcm/xcm-simulator/{example/src/parachain/mock_msg_queue.rs => src/mock_message_queue.rs} (72%) diff --git a/Cargo.lock b/Cargo.lock index ded2cc5329334..642fe88db006f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8409,7 +8409,7 @@ dependencies = [ "pallet-minimal-template", "polkadot-sdk-docs", "polkadot-sdk-frame", - "simple-mermaid", + "simple-mermaid 0.1.1", ] [[package]] @@ -13900,7 +13900,7 @@ dependencies = [ "sc-rpc-api", "sc-service", "scale-info", - "simple-mermaid", + "simple-mermaid 0.1.1", "sp-api", "sp-arithmetic", "sp-core", @@ -18353,6 +18353,11 @@ dependencies = [ "bitflags 2.4.0", ] +[[package]] +name = "simple-mermaid" +version = "0.1.0" +source = "git+https://github.com/kianenigma/simple-mermaid.git?branch=main#e48b187bcfd5cc75111acd9d241f1bd36604344b" + [[package]] name = "simple-mermaid" version = "0.1.1" @@ -19645,7 +19650,7 @@ dependencies = [ "scale-info", "serde", "serde_json", - "simple-mermaid", + "simple-mermaid 0.1.1", "sp-api", "sp-application-crypto", "sp-arithmetic", @@ -21068,6 +21073,28 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger 0.11.3", + "test-log-macros", + "tracing-subscriber 0.3.18", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2 1.0.82", + "quote 1.0.35", + "syn 2.0.61", +] + [[package]] name = "test-parachain-adder" version = "1.0.0" @@ -23281,6 +23308,31 @@ dependencies = [ "libc", ] +[[package]] +name = "xcm-docs" +version = "0.1.0" +dependencies = [ + "docify", + "pallet-balances", + "pallet-message-queue", + "pallet-xcm", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "polkadot-sdk-frame", + "scale-info", + "simple-mermaid 0.1.0", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "test-log", + "xcm-simulator", +] + [[package]] name = "xcm-emulator" version = "0.5.0" @@ -23379,12 +23431,16 @@ name = "xcm-simulator" version = "7.0.0" dependencies = [ "frame-support", + "frame-system", "parity-scale-codec", "paste", "polkadot-core-primitives", "polkadot-parachain-primitives", + "polkadot-primitives", "polkadot-runtime-parachains", + "scale-info", "sp-io", + "sp-runtime", "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", diff --git a/Cargo.toml b/Cargo.toml index dcf410daa1f00..1d02b701d2316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -218,6 +218,7 @@ members = [ "polkadot/utils/generate-bags", "polkadot/utils/remote-ext-tests/bags-list", "polkadot/xcm", + "polkadot/xcm/docs", "polkadot/xcm/pallet-xcm", "polkadot/xcm/pallet-xcm-benchmarks", "polkadot/xcm/procedural", diff --git a/polkadot/xcm/docs/Cargo.toml b/polkadot/xcm/docs/Cargo.toml new file mode 100644 index 0000000000000..9820bd36dc0b1 --- /dev/null +++ b/polkadot/xcm/docs/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "xcm-docs" +description = "Documentation and guides for XCM" +version = "0.1.0" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository.workspace = true +authors.workspace = true +edition.workspace = true +publish = false + +[dependencies] +# For XCM stuff +xcm = { path = "../../xcm", package = "staging-xcm" } +xcm-executor = { path = "../../xcm/xcm-executor", package = "staging-xcm-executor" } +xcm-builder = { path = "../../xcm/xcm-builder", package = "staging-xcm-builder" } +xcm-simulator = { path = "../../xcm/xcm-simulator" } +pallet-xcm = { path = "../../xcm/pallet-xcm" } + +# For building FRAME runtimes +frame = { package = "polkadot-sdk-frame", path = "../../../substrate/frame", features = ["experimental", "runtime"] } +codec = { package = "parity-scale-codec", version = "3.6.9" } +scale-info = { version = "2.6.0", default-features = false } +polkadot-parachain-primitives = { path = "../../../polkadot/parachain" } +polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains" } +polkadot-primitives = { path = "../../../polkadot/primitives" } +sp-runtime = { path = "../../../substrate/primitives/runtime" } +sp-std = { path = "../../../substrate/primitives/std" } +sp-io = { path = "../../../substrate/primitives/io" } + +# Some pallets +pallet-message-queue = { path = "../../../substrate/frame/message-queue" } +pallet-balances = { path = "../../../substrate/frame/balances" } + +# For building docs +simple-mermaid = { git = "https://github.com/kianenigma/simple-mermaid.git", branch = "main" } +docify = "0.2.6" + +[dev-dependencies] +test-log = "0.2.14" diff --git a/polkadot/xcm/docs/mermaid/location_hierarchy.mmd b/polkadot/xcm/docs/mermaid/location_hierarchy.mmd new file mode 100644 index 0000000000000..54fcfc8072a9a --- /dev/null +++ b/polkadot/xcm/docs/mermaid/location_hierarchy.mmd @@ -0,0 +1,9 @@ +flowchart + relay[Relaychain] --> paraA["Parachain(1000)"] + relay --> paraB["Parachain(2000)"] + + paraA --> pallet[Pallet] + pallet --> indexA[Index 1] + pallet --> indexB[Index 2] + + paraA --> account[Account] diff --git a/polkadot/xcm/docs/mermaid/structure.mmd b/polkadot/xcm/docs/mermaid/structure.mmd new file mode 100644 index 0000000000000..17f60467241a3 --- /dev/null +++ b/polkadot/xcm/docs/mermaid/structure.mmd @@ -0,0 +1,4 @@ +flowchart + docs[xcm_docs] --> fundamentals + docs --> guides + docs --> cookbook diff --git a/polkadot/xcm/docs/mermaid/transport_protocols.mmd b/polkadot/xcm/docs/mermaid/transport_protocols.mmd new file mode 100644 index 0000000000000..c0340db0651a3 --- /dev/null +++ b/polkadot/xcm/docs/mermaid/transport_protocols.mmd @@ -0,0 +1,6 @@ +flowchart + relay[Relaychain] --"DMP"--> paraA["Parachain(2000)"] + relay --"DMP"--> paraB["Parachain(2001)"] + + paraA --"UMP"--> relay + paraB --"UMP"--> relay diff --git a/polkadot/xcm/docs/mermaid/universal_location.mmd b/polkadot/xcm/docs/mermaid/universal_location.mmd new file mode 100644 index 0000000000000..97bfa747319db --- /dev/null +++ b/polkadot/xcm/docs/mermaid/universal_location.mmd @@ -0,0 +1,3 @@ +flowchart + universe[Universal Location] --> polkadot[Polkadot] + universe --> ethereum[Ethereum] diff --git a/polkadot/xcm/docs/mermaid/usdt_location.mmd b/polkadot/xcm/docs/mermaid/usdt_location.mmd new file mode 100644 index 0000000000000..5e9222f6098ec --- /dev/null +++ b/polkadot/xcm/docs/mermaid/usdt_location.mmd @@ -0,0 +1,6 @@ +flowchart + relay[Polkadot] --> assetHub["Asset Hub"] + relay --> anotherPara["Another parachain"] + + assetHub --> assetsPallet["Assets Pallet"] + assetsPallet --> usdt[1984] diff --git a/polkadot/xcm/docs/src/cookbook/mod.rs b/polkadot/xcm/docs/src/cookbook/mod.rs new file mode 100644 index 0000000000000..1c69bf0ead6f8 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/mod.rs @@ -0,0 +1,27 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # XCM Cookbook +//! +//! A collection of XCM recipes. +//! +//! Each recipe is tested and explains all the code necessary to run it -- they're not just snippets +//! to copy and paste. + +/// Configuring a parachain that only uses the Relay Chain native token. +/// In the case of Polkadot, this recipe will show you how to launch a parachain with no native +/// token -- dealing only on DOT. +pub mod relay_token_transactor; diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs new file mode 100644 index 0000000000000..279dd71a35f74 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs @@ -0,0 +1,51 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # Relay Asset Transactor +//! +//! This example shows how to configure a parachain to only deal with the Relay Chain token. +//! +//! The first step is using the [`xcm_builder::FungibleAdapter`] to create an `AssetTransactor` that +//! can handle the relay chain token. +#![doc = docify::embed!("src/cookbook/relay_token_transactor/parachain/xcm_config.rs", asset_transactor)] +//! +//! The second step is to configure `IsReserve` to recognize the relay chain as a reserve for its +//! own asset. +//! With this, you'll be able to easily mint a derivative asset, backed one-to-one from the Relay +//! Chain, by using the xcm pallet's `transfer_assets` extrinsic. +//! +//! The `IsReserve` type takes a type that implements `ContainsPair`. +//! In this case, we want a type that contains the pair `(relay_chain_native_token, relay_chain)`. +#![doc = docify::embed!("src/cookbook/relay_token_transactor/parachain/xcm_config.rs", is_reserve)] +//! +//! With this setup, we are able to do a reserve asset transfer to and from the parachain and relay +//! chain. +#![doc = docify::embed!("src/cookbook/relay_token_transactor/tests.rs", reserve_asset_transfers_work)] +//! +//! For the rest of the code, be sure to check the contents of this module. + +/// The parachain runtime for this example +pub mod parachain; + +/// The relay chain runtime for this example. +pub mod relay_chain; + +/// The network for this example. +pub mod network; + +/// Tests for this example. +#[cfg(test)] +pub mod tests; diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs new file mode 100644 index 0000000000000..46ac0e5df6372 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs @@ -0,0 +1,90 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Mock network + +use frame::deps::{ + frame_system, + sp_io::TestExternalities, + sp_runtime::{AccountId32, BuildStorage}, +}; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; + +use super::{parachain, relay_chain}; + +pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); +pub const BOB: AccountId32 = AccountId32::new([1u8; 32]); +pub const UNITS: u64 = 10_000_000_000; +pub const CENTS: u64 = 100_000_000; +pub const INITIAL_BALANCE: u64 = UNITS; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MessageQueue, + DmpMessageHandler = parachain::MessageQueue, + new_ext = para_ext(), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (2222, ParaA), + ], + } +} + +pub fn para_ext() -> TestExternalities { + use parachain::{MessageQueue, Runtime, System}; + + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = frame::deps::sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MessageQueue::set_para_id(2222.into()); + }); + ext +} + +pub fn relay_ext() -> TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE)] } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs new file mode 100644 index 0000000000000..e3fdda2e73337 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs @@ -0,0 +1,56 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # Runtime + +use frame::{deps::frame_system, prelude::*, runtime::prelude::*, traits::IdentityLookup}; +use xcm_executor::XcmExecutor; +use xcm_simulator::mock_message_queue; + +mod xcm_config; +use xcm_config::XcmConfig; + +pub type Block = frame_system::mocking::MockBlock; +pub type AccountId = frame::deps::sp_runtime::AccountId32; +pub type Balance = u64; + +construct_runtime! { + pub struct Runtime { + System: frame_system, + MessageQueue: mock_message_queue, + Balances: pallet_balances, + XcmPallet: pallet_xcm, + } +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; +} + +impl mock_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type AccountStore = System; +} diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs new file mode 100644 index 0000000000000..99f17693093e7 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs @@ -0,0 +1,189 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # XCM Configuration + +use frame::{ + deps::frame_system, + runtime::prelude::*, + traits::{Everything, Nothing}, +}; +use xcm::v4::prelude::*; +use xcm_builder::{ + AccountId32Aliases, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, + FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + SignedToAccountId32, +}; +use xcm_executor::XcmExecutor; + +use super::{AccountId, Balances, MessageQueue, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; + +parameter_types! { + pub RelayLocation: Location = Location::parent(); + pub ThisNetwork: NetworkId = NetworkId::Polkadot; +} + +pub type LocationToAccountId = ( + HashedDescription>, + AccountId32Aliases, +); + +/// Configuration related to asset transactors +#[docify::export] +mod asset_transactor { + use super::*; + + parameter_types! { + pub ParentRelayLocation: Location = Location::parent(); + } + + /// AssetTransactor for handling the relay chain token + pub type FungibleTransactor = FungibleAdapter< + // Use this implementation of the `fungible::*` traits. + // `Balances` is the name given to the balances pallet in this particular recipe. + // Any implementation of the traits would suffice. + Balances, + // This transactor deals with the native token of the Relay Chain. + // This token is referenced by the Location of the Relay Chain relative to this chain + // -- Location::parent(). + IsConcrete, + // How to convert an XCM Location into a local account id. + // This is also something that's configured in the XCM executor. + LocationToAccountId, + // The type for account ids, only needed because `fungible` is generic over it. + AccountId, + // Not tracking teleports. + // This recipe only uses reserve asset transfers to handle the Relay Chain token. + (), + >; + + /// Actual configuration item that'll be set in the XCM config. + /// A tuple could be used here to have multiple transactors, each (potentially) handling + /// different assets. + /// In this recipe, we only have one. + pub type AssetTransactor = FungibleTransactor; +} + +/// Configuration related to token reserves +#[docify::export] +mod is_reserve { + use super::*; + + parameter_types! { + /// Reserves are specified using a pair `(AssetFilter, Location)`. + /// Each pair means that the specified Location is a reserve for all the assets in AssetsFilter. + /// Here, we are specifying that the Relay Chain is the reserve location for its native token. + pub RelayTokenForRelay: (AssetFilter, Location) = + (Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible }), Parent.into()); + } + + /// The wrapper type xcm_builder::Case is needed in order to use this in the configuration. + pub type IsReserve = xcm_builder::Case; +} + +mod weigher { + use super::*; + use xcm_builder::FixedWeightBounds; + + parameter_types! { + pub const WeightPerInstruction: Weight = Weight::from_parts(1, 1); + pub const MaxInstructions: u32 = 100; + } + + pub type Weigher = FixedWeightBounds; +} + +parameter_types! { + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Polkadot), Parachain(2222)].into(); +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = (); + // The declaration of which Locations are reserves for which Assets. + type IsReserve = is_reserve::IsReserve; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + // This is not safe, you should use `xcm_builder::AllowTopLevelPaidExecutionFrom` in a + // production chain + type Barrier = xcm_builder::AllowUnpaidExecutionFrom; + type Weigher = weigher::Weigher; + type Trader = (); + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = frame::traits::ConstU32<1>; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + // We turn off sending for these tests + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = super::super::network::ParachainXcmRouter; // Provided by xcm-simulator + // Anyone can execute XCM programs + type ExecuteXcmOrigin = EnsureXcmOrigin; + // We execute any type of program + type XcmExecuteFilter = Everything; + // How we execute programs + type XcmExecutor = XcmExecutor; + // We don't allow teleports + type XcmTeleportFilter = Nothing; + // We allow all reserve transfers + type XcmReserveTransferFilter = Everything; + // Same weigher executor uses to weigh XCM programs + type Weigher = weigher::Weigher; + // Same universal location + type UniversalLocation = UniversalLocation; + // No version discovery needed + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; + type AdvertisedXcmVersion = frame::traits::ConstU32<3>; + type AdminOrigin = frame_system::EnsureRoot; + // No locking + type TrustedLockers = (); + type MaxLockers = frame::traits::ConstU32<0>; + type MaxRemoteLockConsumers = frame::traits::ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // How to turn locations into accounts + type SovereignAccountOf = LocationToAccountId; + // A currency to pay for things and its matcher, we are using the relay token + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + // Pallet benchmarks, no need for this recipe + type WeightInfo = pallet_xcm::TestWeightInfo; + // Runtime types + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs new file mode 100644 index 0000000000000..25c35dd4aaa83 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs @@ -0,0 +1,103 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Relay chain runtime mock. + +use frame::{ + deps::{frame_support::weights::WeightMeter, sp_runtime::AccountId32}, + prelude::*, + runtime::prelude::*, + traits::{IdentityLookup, ProcessMessage, ProcessMessageError}, +}; +use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId}; +use xcm::v4::prelude::*; + +mod xcm_config; +pub use xcm_config::LocationToAccountId; +use xcm_config::XcmConfig; + +pub type AccountId = AccountId32; +pub type Balance = u64; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Runtime { + type AccountStore = System; +} + +type Block = frame_system::mocking::MockBlock; + +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter, id) + } +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type QueuePausedQuery = (); + type WeightInfo = (); + type IdleMaxServiceWeight = MessageQueueServiceWeight; +} + +construct_runtime! { + pub struct Runtime { + System: frame_system, + Balances: pallet_balances, + MessageQueue: pallet_message_queue, + XcmPallet: pallet_xcm, + } +} diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs new file mode 100644 index 0000000000000..987bb3f9ab664 --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs @@ -0,0 +1,163 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Relay chain XCM configuration + +use frame::{ + deps::frame_system, + runtime::prelude::*, + traits::{Everything, Nothing}, +}; +use xcm::v4::prelude::*; +use xcm_builder::{ + AccountId32Aliases, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, + FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + SignedToAccountId32, +}; +use xcm_executor::XcmExecutor; + +use super::{AccountId, Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; + +parameter_types! { + pub HereLocation: Location = Location::here(); + pub ThisNetwork: NetworkId = NetworkId::Polkadot; +} + +/// Converter from XCM Locations to accounts. +/// This generates sovereign accounts for Locations and converts +/// local AccountId32 junctions to local accounts. +pub type LocationToAccountId = ( + HashedDescription>, + AccountId32Aliases, +); + +mod asset_transactor { + use super::*; + + /// AssetTransactor for handling the Relay Chain token. + pub type FungibleTransactor = FungibleAdapter< + // Use this `fungible` implementation. + Balances, + // This transactor handles the native token. + IsConcrete, + // How to convert an XCM Location into a local account id. + // Whenever assets are handled, the location is turned into an account. + // This account is the one where balances are withdrawn/deposited. + LocationToAccountId, + // The account id type, needed because `fungible` is generic over it. + AccountId, + // Not tracking teleports. + (), + >; + + /// All asset transactors, in this case only one + pub type AssetTransactor = FungibleTransactor; +} + +mod weigher { + use super::*; + use xcm_builder::FixedWeightBounds; + + parameter_types! { + pub const WeightPerInstruction: Weight = Weight::from_parts(1, 1); + pub const MaxInstructions: u32 = 100; + } + + pub type Weigher = FixedWeightBounds; +} + +parameter_types! { + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Polkadot)].into(); +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = (); + // We don't need to recognize anyone as a reserve + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + // This is not safe, you should use `xcm_builder::AllowTopLevelPaidExecutionFrom` in a + // production chain + type Barrier = xcm_builder::AllowUnpaidExecutionFrom; + type Weigher = weigher::Weigher; + type Trader = (); + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = frame::traits::ConstU32<1>; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + // No one can call `send` + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = super::super::network::RelayChainXcmRouter; // Provided by xcm-simulator + // Anyone can execute XCM programs + type ExecuteXcmOrigin = EnsureXcmOrigin; + // We execute any type of program + type XcmExecuteFilter = Everything; + // How we execute programs + type XcmExecutor = XcmExecutor; + // We don't allow teleports + type XcmTeleportFilter = Nothing; + // We allow all reserve transfers. + // This is so it can act as a reserve for its native token. + type XcmReserveTransferFilter = Everything; + // Same weigher executor uses to weigh XCM programs + type Weigher = weigher::Weigher; + // Same universal location + type UniversalLocation = UniversalLocation; + // No version discovery needed + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; + type AdvertisedXcmVersion = frame::traits::ConstU32<3>; + type AdminOrigin = frame_system::EnsureRoot; + // No locking + type TrustedLockers = (); + type MaxLockers = frame::traits::ConstU32<0>; + type MaxRemoteLockConsumers = frame::traits::ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // How to turn locations into accounts + type SovereignAccountOf = LocationToAccountId; + // A currency to pay for things and its matcher, we are using the relay token + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + // Pallet benchmarks, no need for this example + type WeightInfo = pallet_xcm::TestWeightInfo; + // Runtime types + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs new file mode 100644 index 0000000000000..792cf6149e7cb --- /dev/null +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs @@ -0,0 +1,128 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use frame::testing_prelude::*; +use test_log::test; +use xcm::prelude::*; +use xcm_executor::traits::ConvertLocation; +use xcm_simulator::TestExt; + +use super::{ + network::{MockNet, ParaA, Relay, ALICE, BOB, CENTS, INITIAL_BALANCE}, + parachain, relay_chain, +}; + +#[docify::export] +#[test] +fn reserve_asset_transfers_work() { + // Scenario: + // ALICE on the relay chain holds some of Relay Chain's native tokens. + // She transfers them to BOB's account on the parachain using a reserve transfer. + // BOB receives Relay Chain native token derivatives on the parachain, + // which are backed one-to-one with the real tokens on the Relay Chain. + // + // NOTE: We could've used ALICE on both chains because it's a different account, + // but using ALICE and BOB makes it clearer. + + // We restart the mock network. + MockNet::reset(); + + // ALICE starts with INITIAL_BALANCE on the relay chain + Relay::execute_with(|| { + assert_eq!(relay_chain::Balances::free_balance(&ALICE), INITIAL_BALANCE); + }); + + // BOB starts with 0 on the parachain + ParaA::execute_with(|| { + assert_eq!(parachain::Balances::free_balance(&BOB), 0); + }); + + // ALICE on the Relay Chain sends some Relay Chain native tokens to BOB on the parachain. + // The transfer is done with the `transfer_assets` extrinsic in the XCM pallet. + // The extrinsic figures out it should do a reserve asset transfer + // with the local chain as reserve. + Relay::execute_with(|| { + // The parachain id is specified in the network.rs file in this recipe. + let destination: Location = Parachain(2222).into(); + let beneficiary: Location = + AccountId32 { id: BOB.clone().into(), network: Some(NetworkId::Polkadot) }.into(); + // We need to use `u128` here for the conversion to work properly. + // If we don't specify anything, it will be a `u64`, which the conversion + // will turn into a non-fungible token instead of a fungible one. + let assets: Assets = (Here, 50u128 * CENTS as u128).into(); + assert_ok!(relay_chain::XcmPallet::transfer_assets( + relay_chain::RuntimeOrigin::signed(ALICE), + Box::new(VersionedLocation::V4(destination.clone())), + Box::new(VersionedLocation::V4(beneficiary)), + Box::new(VersionedAssets::V4(assets)), + 0, + WeightLimit::Unlimited, + )); + + // ALICE now has less Relay Chain tokens. + assert_eq!(relay_chain::Balances::free_balance(&ALICE), INITIAL_BALANCE - 50 * CENTS); + + // The funds of the sovereign account of the parachain increase by 50 cents, + // the ones transferred over to BOB. + // The funds in this sovereign account represent how many Relay Chain tokens + // have been sent to this parachain. + // If the parachain wants to send those assets somewhere else they have to go + // via the reserve, and this balance is updated accordingly. + // This is why the derivatives are backed one-to-one. + let parachains_sovereign_account = + relay_chain::LocationToAccountId::convert_location(&destination).unwrap(); + assert_eq!(relay_chain::Balances::free_balance(parachains_sovereign_account), 50 * CENTS); + }); + + ParaA::execute_with(|| { + // On the parachain, BOB has received the derivative tokens + assert_eq!(parachain::Balances::free_balance(&BOB), 50 * CENTS); + + // BOB gives back half to ALICE in the relay chain + let destination: Location = Parent.into(); + let beneficiary: Location = + AccountId32 { id: ALICE.clone().into(), network: Some(NetworkId::Polkadot) }.into(); + // We specify `Parent` because we are referencing the Relay Chain token. + // This chain doesn't have a token of its own, so we always refer to this token, + // and we do so by the Location of the Relay Chain. + let assets: Assets = (Parent, 25u128 * CENTS as u128).into(); + assert_ok!(parachain::XcmPallet::transfer_assets( + parachain::RuntimeOrigin::signed(BOB), + Box::new(VersionedLocation::V4(destination)), + Box::new(VersionedLocation::V4(beneficiary)), + Box::new(VersionedAssets::V4(assets)), + 0, + WeightLimit::Unlimited, + )); + + // BOB's balance decreased + assert_eq!(parachain::Balances::free_balance(&BOB), 25 * CENTS); + }); + + Relay::execute_with(|| { + // ALICE's balance increases + assert_eq!( + relay_chain::Balances::free_balance(&ALICE), + INITIAL_BALANCE - 50 * CENTS + 25 * CENTS + ); + + // The funds in the parachain's sovereign account decrease. + let parachain: Location = Parachain(2222).into(); + let parachains_sovereign_account = + relay_chain::LocationToAccountId::convert_location(¶chain).unwrap(); + assert_eq!(relay_chain::Balances::free_balance(parachains_sovereign_account), 25 * CENTS); + }); +} diff --git a/polkadot/xcm/docs/src/fundamentals.rs b/polkadot/xcm/docs/src/fundamentals.rs new file mode 100644 index 0000000000000..28899df801aa4 --- /dev/null +++ b/polkadot/xcm/docs/src/fundamentals.rs @@ -0,0 +1,177 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # XCM Fundamentals +//! +//! XCM standardizes usual actions users take in consensus systems, for example +//! dealing with assets locally, on other chains, and locking them. +//! XCM programs can both be executed locally or sent to a different consensus system. +//! Examples of consensus systems are blockchains and smart contracts. +//! +//! The goal of XCM is to allow multi-chain ecosystems to thrive via specialization. +//! Very specific functionalities can be abstracted away and standardized in this common language. +//! Then, every member of the ecosystem can implement the subset of the language that makes sense +//! for them. +//! +//! The language evolves over time to accomodate the needs of the community +//! via the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md). +//! +//! XCM is the language, it deals with interpreting and executing programs. +//! It does not deal with actually **sending** these programs from one consensus system to another. +//! This responsibility falls to a transport protocol. +//! XCM can even be interpreted on the local system, with no need of a transport protocol. +//! However, automatic and composable workflows can be achieved via the use of one. +//! +//! At the core of XCM lies the XCVM, the Cross-Consensus Virtual Machine. +//! It's the virtual machine that executes XCM programs. +//! It is a specification that comes with the language. +//! +//! For these docs, we'll use a Rust implementation of XCM and the XCVM, consisting of the following +//! parts: +//! - [`XCM`](xcm): Holds the definition of an XCM program, the instructions and main concepts. +//! - [`Executor`](xcm_executor): Implements the XCVM, capable of executing XCMs. Highly +//! configurable. +//! - [`Builder`](xcm_builder): A collection of types used to configure the executor. +//! - [`XCM Pallet`](pallet_xcm): A FRAME pallet for interacting with the executor. +//! - [`Simulator`](xcm_simulator): A playground to tinker with different XCM programs and executor +//! configurations. +//! +//! XCM programs are composed of Instructions, which reference Locations and Assets. +//! +//! ## Locations +//! +//! Locations are XCM's vocabulary of places we want to talk about in our XCM programs. +//! They are used to reference things like 32-byte accounts, governance bodies, smart contracts, +//! blockchains and more. +//! +//! Locations are hierarchical. +//! This means some places in consensus are wholly encapsulated in other places. +//! Say we have two systems A and B. +//! If any change in A's state implies a change in B's state, then we say A is interior to B. +#![doc = simple_mermaid::mermaid!("../mermaid/location_hierarchy.mmd")] +//! +//! Parachains are interior to their Relay Chain, since a change in their state implies a change in +//! the Relay Chain's state. +//! +//! Because of this hierarchy, the way we represent locations is with both a number of **parents**, +//! times we move __up__ the hierarchy, and a sequence of **junctions**, the steps we take __down__ +//! the hierarchy after going up the specified number of parents. +//! +//! In Rust, this is specified with the following datatype: +//! ```ignore +//! pub struct Location { +//! parents: u8, +//! interior: Junctions, +//! } +//! ``` +//! +//! Many junctions are available; parachains, pallets, 32 and 20 byte accounts, governance bodies, +//! and arbitrary indices are the most common. +//! A full list of available junctions can be found in the [format](https://github.com/paritytech/xcm-format#interior-locations--junctions) +//! and [Junction enum](xcm::v4::prelude::Junction). +//! +//! We'll use a file system notation to represent locations, and start with relative locations. +//! In the diagram, the location of parachain 1000 as seen from all other locations is as follows: +//! - From the relaychain: `Parachain(1000)` +//! - From parachain 1000 itself: `Here` +//! - From parachain 2000: `../Parachain(1000)` +//! +//! Relative locations are interpreted by the system that is executing an XCM program, which is the +//! receiver of a message in the case where it's sent. +//! +//! Locations can also be absolute. +//! Keeping in line with our filesystem analogy, we can imagine the root of our filesystem to exist. +//! This would be a location with no parents, that is also the parent of all systems that derive +//! their own consensus, say Polkadot or Ethereum or Bitcoin. +//! Such a location does not exist concretely, but we can still use this definition for it. +//! This is the **universal location**. +//! We need the universal location to be able to describe locations in an absolute way. +#![doc = simple_mermaid::mermaid!("../mermaid/universal_location.mmd")] +//! +//! Here, the absolute location of parachain 1000 would be +//! `GlobalConsensus(Polkadot)/Parachain(1000)`. +//! +//! ## Assets +//! +//! We want to be able to reference assets in our XCM programs, if only to be able to pay for fees. +//! Assets are represented using locations. +//! +//! The native asset of a chain is represented by the location of that chain. +//! For example, DOT is represented by the location of the Polkadot relaychain. +//! If the interpreting chain has its own asset, it would be represented by `Here`. +//! +//! How do we represent other assets? +//! The asset hub system parachain in Polkadot, for example, holds a lot of assets. +//! To represent each of them, it uses the indices we mentioned, and it makes them interior to the +//! assets pallet instance it uses. +//! USDT, an example asset that lives on asset hub, is identified by the location +//! `Parachain(1000)/PalletInstance(53)/GeneralIndex(1984)`, when seen from the Polkadot relaychain. +#![doc = simple_mermaid::mermaid!("../mermaid/usdt_location.mmd")] +//! +//! Asset Hub also has another type of assets called `ForeignAssets`. +//! These assets are identified by the XCM Location to their origin. +//! Two such assets are a Parachain asset, like Moonbeam's GLMR, and KSM, from the cousin Kusama +//! network. These are represented as `../Parachain(2004)/PalletInstance(10)` and +//! `../../GlobalConsensus(Kusama)` respectively. +//! +//! The whole type can be seen in the [format](https://github.com/paritytech/xcm-format#6-universal-asset-identifiers) +//! and [rust docs](xcm::v4::prelude::Asset). +//! +//! ## Instructions +//! +//! Given the vocabulary to talk about both locations -- chains and accounts -- and assets, we now +//! need a way to express what we want the consensus system to do when executing our programs. +//! We need a way of writing our programs. +//! +//! XCM programs are composed of a sequence of instructions. +//! +//! All available instructions can be seen in the [format](https://github.com/paritytech/xcm-format#5-the-xcvm-instruction-set) +//! and the [Instruction enum](xcm::v4::prelude::Instruction). +//! +//! A very simple example is the following: +//! +//! ```ignore +//! let message = Xcm(vec![ +//! TransferAsset { assets, beneficiary }, +//! ]); +//! ``` +//! +//! This instruction is enough to transfer `assets` from the account of the **origin** of a message +//! to the `beneficiary` account. However, because of XCM's generality, fees need to be paid +//! explicitly. This next example sheds more light on this: +//! +//! ```ignore +//! let message = Xcm(vec![ +//! WithdrawAsset(assets), +//! BuyExecution { fees: assets, weight_limit }, +//! DepositAsset { assets: AssetFilter(Wild(All)), beneficiary }, +//! ]); +//! ``` +//! +//! Here we see the process of transferring assets was broken down into smaller instructions, and we +//! add the explicit fee payment step in the middle. +//! `WithdrawAsset` withdraws assets from the account of the **origin** of the message for usage +//! inside this message's execution. `BuyExecution` explicitly buys execution for this program using +//! the assets specified in `fees`, with a sanity check of `weight_limit`. `DepositAsset` uses a +//! wildcard, specifying all remaining `assets` after subtracting the fees and a `beneficiary` +//! account. +//! +//! ## Next steps +//! +//! Continue with the [guides](crate::guides) for step-by-step tutorials on XCM, +//! or jump to the [cookbook](crate::cookbook) to see examples. +//! +//! The [glossary](crate::glossary) can be useful if some of the terms are confusing. diff --git a/polkadot/xcm/docs/src/glossary.rs b/polkadot/xcm/docs/src/glossary.rs new file mode 100644 index 0000000000000..6035888ab733b --- /dev/null +++ b/polkadot/xcm/docs/src/glossary.rs @@ -0,0 +1,123 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # Glossary +//! +//! ## XCM (Cross-Consensus Messaging) +//! +//! A messaging format meant to communicate intentions between consensus systems. +//! XCM could also refer to a single message. +//! +//! ## Instructions +//! +//! XCMs are composed of a sequence of instructions. +//! Each instruction aims to convey a particular intention. +//! There are instructions for transferring and locking assets, handling fees, calling arbitrary +//! blobs, and more. +//! +//! ## Consensus system +//! +//! A system that can reach any kind of consensus. +//! For example, relay chains, parachains, smart contracts. +//! Most messaging between consensus systems has to be done asynchronously, for this, XCM is used. +//! Between two smart contracts on the same parachain, however, communication can be done +//! synchronously. +//! +//! ## [`Location`](xcm::v4::prelude::Location) +//! +//! A way of addressing consensus systems. +//! These could be relative or absolute. +//! +//! ## [`Junction`](xcm::v4::prelude::Junction) +//! +//! The different ways of descending down a [`Location`](xcm::v4::prelude::Location) hierarchy. +//! A junction can be a Parachain, an Account, or more. +//! +//! ## [`Asset`](xcm::v4::prelude::Asset) +//! +//! A way of identifying assets in the same or another consensus system, by using a +//! [`Location`](xcm::v4::prelude::Location). +//! +//! ## Sovereign account +//! +//! An account in a consensus system that is controlled by an account in another consensus system. +//! +//! Runtimes use a converter between a [`Location`](xcm::v4::prelude::Location) and an account. +//! These converters implement the [`ConvertLocation`](xcm_executor::traits::ConvertLocation) trait. +//! +//! ## Teleport +//! +//! A way of transferring assets between two consensus systems without the need of a third party. +//! It consists of the sender system burning the asset that wants to be sent over and the recipient +//! minting an equivalent amount of that asset. It requires a lot of trust between the two systems, +//! since failure to mint or burn will reduce or increase the total issuance of the token. +//! +//! ## Reserve asset transfer +//! +//! A way of transferring assets between two consensus systems that don't trust each other, by using +//! a third system they both trust, called the reserve. The real asset only exists on the reserve, +//! both sender and recipient only deal with derivatives. It consists of the sender burning a +//! certain amount of derivatives, telling the reserve to move real assets from its sovereign +//! account to the destination's sovereign account, and then telling the recipient to mint the right +//! amount of derivatives. +//! In practice, the reserve chain can also be one of the source or destination. +//! +//! ## XCVM +//! +//! The virtual machine behind XCM. +//! Every XCM is an XCVM programme. +//! Holds state in registers. +//! +//! An implementation of the virtual machine is the [`xcm-executor`](xcm_executor::XcmExecutor). +//! +//! ## Holding register +//! +//! An XCVM register used to hold arbitrary `Asset`s during the execution of an XCVM programme. +//! +//! ## Barrier +//! +//! An XCM executor configuration item that works as a firewall for incoming XCMs. +//! All XCMs have to pass the barrier to be executed, else they are dropped. +//! It can be used for whitelisting only certain types or messages or messages from certain senders. +//! +//! Lots of barrier definitions exist in [`xcm-builder`](xcm_builder). +//! +//! ## VMP (Vertical Message Passing) +//! +//! Umbrella term for both UMP (Upward Message Passing) and DMP (Downward Message Passing). +//! +//! The following diagram shows the uses of both protocols: +#![doc = simple_mermaid::mermaid!("../mermaid/transport_protocols.mmd")] +//! +//! ## UMP (Upward Message Passing) +//! +//! Transport-layer protocol that allows parachains to send messages upwards to their relay chain. +//! +//! ## DMP (Downward Message Passing) +//! +//! Transport-layer protocol that allows the relay chain to send messages downwards to one of their +//! parachains. +//! +//! ## XCMP (Cross-Consensus Message Passing) +//! +//! Transport-layer protocol that allows parachains to send messages between themselves, without +//! going through the relay chain. +//! +//! ## HRMP (Horizontal Message Passing) +//! +//! Transport-layer protocol that allows a parachain to send messages to a sibling parachain going +//! through the relay chain. It's a precursor to XCMP, also known as XCMP-lite. +//! It uses a mixture of UMP and DMP. diff --git a/polkadot/xcm/docs/src/guides/mod.rs b/polkadot/xcm/docs/src/guides/mod.rs new file mode 100644 index 0000000000000..5af89428d9a4c --- /dev/null +++ b/polkadot/xcm/docs/src/guides/mod.rs @@ -0,0 +1,25 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # XCM Guides +//! +//! These guides aim to get you up and running with XCM. +//! +//! Coming soon. +//! +//! ## Next steps +//! +//! Jump to the [cookbook](crate::cookbook) for different examples. diff --git a/polkadot/xcm/docs/src/lib.rs b/polkadot/xcm/docs/src/lib.rs new file mode 100644 index 0000000000000..287c97140c91b --- /dev/null +++ b/polkadot/xcm/docs/src/lib.rs @@ -0,0 +1,63 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! # XCM Docs +//! +//! Documentation and guides for XCM +//! +//! Welcome to the Cross-Consensus Messaging documentation! +//! +//! XCM is a **language** for communicating **intentions** between **consensus systems**. +//! Whether you're a developer, a blockchain enthusiast, or just interested in Polkadot, this guide +//! aims to provide you with an easy-to-understand and comprehensive introduction to XCM. +//! +//! ## Getting started +//! +//! Head over to the [fundamentals](fundamentals) section. +//! Then, go to the [guides](guides), to learn about how to do things with XCM. +//! +//! ## Cookbook +//! +//! There's also the [cookbook](cookbook) for useful recipes for XCM. +//! +//! ## Glossary +//! +//! There's a [glossary](glossary) with common terms used throughout the docs. +//! +//! ## Contribute +//! +//! To contribute to the format, check out the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md). +//! To contribute to these docs, [make a PR](https://github.com/paritytech/polkadot-sdk). +//! +//! ## Why Rust Docs? +//! +//! Rust Docs allow docs to be as close to the source as possible. +//! They're also available offline automatically for anyone who has the `polkadot-sdk` repo locally. +//! +//! ## Docs structure +#![doc = simple_mermaid::mermaid!("../mermaid/structure.mmd")] + +/// Fundamentals of the XCM language. The virtual machine, instructions, locations and assets. +pub mod fundamentals; + +/// Step-by-step guides to set up an XCM environment and start hacking. +pub mod guides; + +/// Useful recipes for programs and configurations. +pub mod cookbook; + +/// Glossary +pub mod glossary; diff --git a/polkadot/xcm/xcm-simulator/Cargo.toml b/polkadot/xcm/xcm-simulator/Cargo.toml index 9324359d365dc..fc09b5e31861c 100644 --- a/polkadot/xcm/xcm-simulator/Cargo.toml +++ b/polkadot/xcm/xcm-simulator/Cargo.toml @@ -11,15 +11,19 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.12" } +scale-info = { version = "2.6.0", default-features = false } paste = "1.0.7" frame-support = { path = "../../../substrate/frame/support" } +frame-system = { path = "../../../substrate/frame/system" } sp-io = { path = "../../../substrate/primitives/io" } sp-std = { path = "../../../substrate/primitives/std" } +sp-runtime = { path = "../../../substrate/primitives/runtime" } xcm = { package = "staging-xcm", path = ".." } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor" } xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder" } +polkadot-primitives = { path = "../../primitives" } polkadot-core-primitives = { path = "../../core-primitives" } polkadot-parachain-primitives = { path = "../../parachain" } polkadot-runtime-parachains = { path = "../../runtime/parachains" } diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs index 8021f95516580..93c8302757cb0 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs @@ -16,7 +16,6 @@ //! Parachain runtime mock. -mod mock_msg_queue; mod xcm_config; pub use xcm_config::*; @@ -36,6 +35,7 @@ use sp_std::prelude::*; use xcm::latest::prelude::*; use xcm_builder::{EnsureXcmOrigin, SignedToAccountId32}; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; +use xcm_simulator::mock_message_queue; pub type AccountId = AccountId32; pub type Balance = u128; @@ -121,7 +121,7 @@ parameter_types! { pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); } -impl mock_msg_queue::Config for Runtime { +impl mock_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } @@ -175,7 +175,7 @@ construct_runtime!( pub struct Runtime { System: frame_system, Balances: pallet_balances, - MsgQueue: mock_msg_queue, + MsgQueue: mock_message_queue, PolkadotXcm: pallet_xcm, ForeignUniques: pallet_uniques, } diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs index f6d0174def8f4..0769507ec37b7 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::parachain::MsgQueue; +use crate::parachain::Runtime; use frame_support::parameter_types; use xcm::latest::prelude::*; +use xcm_simulator::mock_message_queue::ParachainId; parameter_types! { pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1); @@ -26,5 +27,5 @@ parameter_types! { parameter_types! { pub const KsmLocation: Location = Location::parent(); pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainId::::get().into())].into(); } diff --git a/polkadot/xcm/xcm-simulator/example/src/tests.rs b/polkadot/xcm/xcm-simulator/example/src/tests.rs index 6486a849af363..34c1feb6e9468 100644 --- a/polkadot/xcm/xcm-simulator/example/src/tests.rs +++ b/polkadot/xcm/xcm-simulator/example/src/tests.rs @@ -19,7 +19,7 @@ use crate::*; use codec::Encode; use frame_support::{assert_ok, weights::Weight}; use xcm::latest::QueryResponseInfo; -use xcm_simulator::TestExt; +use xcm_simulator::{mock_message_queue::ReceivedDmp, TestExt}; // Helper function for forming buy execution message fn buy_execution(fees: impl Into) -> Instruction { @@ -171,7 +171,7 @@ fn remote_locking_and_unlocking() { ParaA::execute_with(|| { assert_eq!( - parachain::MsgQueue::received_dmp(), + ReceivedDmp::::get(), vec![Xcm(vec![NoteUnlockable { owner: (Parent, Parachain(2)).into(), asset: (Parent, locked_amount).into() @@ -501,7 +501,7 @@ fn query_holding() { // Check that QueryResponse message was received ParaA::execute_with(|| { assert_eq!( - parachain::MsgQueue::received_dmp(), + ReceivedDmp::::get(), vec![Xcm(vec![QueryResponse { query_id: query_id_set, response: Response::Assets(Assets::new()), diff --git a/polkadot/xcm/xcm-simulator/src/lib.rs b/polkadot/xcm/xcm-simulator/src/lib.rs index 7efbc658bbfb8..a6747a4789edf 100644 --- a/polkadot/xcm/xcm-simulator/src/lib.rs +++ b/polkadot/xcm/xcm-simulator/src/lib.rs @@ -16,6 +16,10 @@ //! Test kit to simulate cross-chain message passing and XCM execution. +/// Implementation of a simple message queue. +/// Used for sending messages. +pub mod mock_message_queue; + pub use codec::Encode; pub use paste; diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs b/polkadot/xcm/xcm-simulator/src/mock_message_queue.rs similarity index 72% rename from polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs rename to polkadot/xcm/xcm-simulator/src/mock_message_queue.rs index 17cde921f3e20..96b47999fe952 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs +++ b/polkadot/xcm/xcm-simulator/src/mock_message_queue.rs @@ -1,4 +1,4 @@ -// Copyright (C) Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify @@ -14,14 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -pub use pallet::*; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +//! Simple mock message queue. + +use codec::{Decode, Encode}; + use polkadot_parachain_primitives::primitives::{ DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler, }; +use polkadot_primitives::BlockNumber as RelayBlockNumber; use sp_runtime::traits::{Get, Hash}; + +use sp_std::prelude::*; use xcm::{latest::prelude::*, VersionedXcm}; +pub use pallet::*; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -41,15 +48,15 @@ pub mod pallet { pub struct Pallet(_); #[pallet::storage] - pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + pub type ParachainId = StorageValue<_, ParaId, ValueQuery>; #[pallet::storage] /// A queue of received DMP messages - pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; + pub type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; impl Get for Pallet { fn get() -> ParaId { - Self::parachain_id() + ParachainId::::get() } } @@ -60,45 +67,34 @@ pub mod pallet { pub enum Event { // XCMP /// Some XCM was executed OK. - Success(Option), + Success { message_id: Option }, /// Some XCM failed. - Fail(Option, XcmError), + Fail { message_id: Option, error: XcmError }, /// Bad XCM version used. - BadVersion(Option), + BadVersion { message_id: Option }, /// Bad XCM format used. - BadFormat(Option), + BadFormat { message_id: Option }, // DMP /// Downward message is invalid XCM. - InvalidFormat(MessageId), + InvalidFormat { message_id: MessageId }, /// Downward message is unsupported version of XCM. - UnsupportedVersion(MessageId), + UnsupportedVersion { message_id: MessageId }, /// Downward message executed with the given outcome. - ExecutedDownward(MessageId, Outcome), + ExecutedDownward { message_id: MessageId, outcome: Outcome }, } impl Pallet { - /// Get the Parachain Id. - pub fn parachain_id() -> ParaId { - ParachainId::::get() - } - - /// Set the Parachain Id. pub fn set_para_id(para_id: ParaId) { ParachainId::::put(para_id); } - /// Get the queue of receieved DMP messages. - pub fn received_dmp() -> Vec> { - ReceivedDmp::::get() - } - fn handle_xcmp_message( sender: ParaId, _sent_at: RelayBlockNumber, xcm: VersionedXcm, - max_weight: Weight, - ) -> Result { + max_weight: xcm::latest::Weight, + ) -> Result { let hash = Encode::using_encoded(&xcm, T::Hashing::hash); let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); let (result, event) = match Xcm::::try_from(xcm) { @@ -111,15 +107,20 @@ pub mod pallet { max_weight, Weight::zero(), ) { - Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)), - Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + Outcome::Error { error } => + (Err(error), Event::Fail { message_id: Some(hash), error }), + Outcome::Complete { used } => + (Ok(used), Event::Success { message_id: Some(hash) }), // As far as the caller is concerned, this was dispatched without error, so // we just report the weight used. Outcome::Incomplete { used, error } => - (Ok(used), Event::Fail(Some(hash), error)), + (Ok(used), Event::Fail { message_id: Some(hash), error }), } }, - Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion { message_id: Some(hash) }, + ), }; Self::deposit_event(event); result @@ -129,8 +130,8 @@ pub mod pallet { impl XcmpMessageHandler for Pallet { fn handle_xcmp_messages<'a, I: Iterator>( iter: I, - max_weight: Weight, - ) -> Weight { + max_weight: xcm::latest::Weight, + ) -> xcm::latest::Weight { for (sender, sent_at, data) in iter { let mut data_ref = data; let _ = XcmpMessageFormat::decode(&mut data_ref) @@ -156,15 +157,16 @@ pub mod pallet { iter: impl Iterator)>, limit: Weight, ) -> Weight { - for (_i, (_sent_at, data)) in iter.enumerate() { + for (_sent_at, data) in iter { let mut id = sp_io::hashing::blake2_256(&data[..]); let maybe_versioned = VersionedXcm::::decode(&mut &data[..]); match maybe_versioned { Err(_) => { - Self::deposit_event(Event::InvalidFormat(id)); + Self::deposit_event(Event::InvalidFormat { message_id: id }); }, Ok(versioned) => match Xcm::try_from(versioned) { - Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), + Err(()) => + Self::deposit_event(Event::UnsupportedVersion { message_id: id }), Ok(x) => { let outcome = T::XcmExecutor::prepare_and_execute( Parent, @@ -173,8 +175,11 @@ pub mod pallet { limit, Weight::zero(), ); - >::append(x); - Self::deposit_event(Event::ExecutedDownward(id, outcome)); + ReceivedDmp::::append(x); + Self::deposit_event(Event::ExecutedDownward { + message_id: id, + outcome, + }); }, }, }