From ec83613a386e35d8790a3660fe4ab2821a4710d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Aug 2021 23:45:52 +0200 Subject: [PATCH 01/11] Bump libc from 0.2.99 to 0.2.100 (#3703) Bumps [libc](https://github.com/rust-lang/libc) from 0.2.99 to 0.2.100. - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.99...0.2.100) --- updated-dependencies: - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- node/core/pvf/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3f1d83495b4..05fffd2056bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3295,9 +3295,9 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "libc" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" [[package]] name = "libloading" diff --git a/node/core/pvf/Cargo.toml b/node/core/pvf/Cargo.toml index 809d8269ac2e..8bd7c8168276 100644 --- a/node/core/pvf/Cargo.toml +++ b/node/core/pvf/Cargo.toml @@ -15,7 +15,7 @@ async-process = "1.1.0" assert_matches = "1.4.0" futures = "0.3.15" futures-timer = "3.0.2" -libc = "0.2.99" +libc = "0.2.100" slotmap = "1.0" tracing = "0.1.26" pin-project = "1.0.8" From 1e6cddb06db287f51eff0bfd0be16a8dcfe80f38 Mon Sep 17 00:00:00 2001 From: Robert Klotzner Date: Wed, 25 Aug 2021 09:25:45 +0200 Subject: [PATCH 02/11] Don't err on deactivated leaf during valiation. (#3708) * Don't err on deactivated leaf during valiation. * Fix error level + formatting of unrelated code. --- node/core/backing/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/node/core/backing/src/lib.rs b/node/core/backing/src/lib.rs index 5ed80017e7d9..338f333efbfa 100644 --- a/node/core/backing/src/lib.rs +++ b/node/core/backing/src/lib.rs @@ -92,7 +92,7 @@ pub enum Error { #[error(transparent)] ValidationFailed(#[from] ValidationFailed), #[error(transparent)] - Mpsc(#[from] mpsc::SendError), + BackgroundValidationMpsc(#[from] mpsc::SendError), #[error(transparent)] UtilError(#[from] util::Error), } @@ -444,7 +444,7 @@ async fn validate_and_make_available( tx_command .send(ValidatedCandidateCommand::AttestNoPoV(candidate.hash())) .await - .map_err(Error::Mpsc)?; + .map_err(Error::BackgroundValidationMpsc)?; return Ok(()) }, Err(err) => return Err(err), @@ -650,11 +650,19 @@ impl CandidateBackingJob { // spawn background task. let bg = async move { if let Err(e) = validate_and_make_available(params).await { - tracing::error!( - target: LOG_TARGET, - "Failed to validate and make available: {:?}", - e - ); + if let Error::BackgroundValidationMpsc(error) = e { + tracing::debug!( + target: LOG_TARGET, + ?error, + "Mpsc background validation mpsc died during validation- leaf no longer active?" + ); + } else { + tracing::error!( + target: LOG_TARGET, + "Failed to validate and make available: {:?}", + e + ); + } } }; sender From de4beb8eb53207e7c34d92cf584f5ba9b4b9e461 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Aug 2021 09:40:42 +0200 Subject: [PATCH 03/11] Bump trybuild from 1.0.43 to 1.0.45 (#3713) Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.43 to 1.0.45. - [Release notes](https://github.com/dtolnay/trybuild/releases) - [Commits](https://github.com/dtolnay/trybuild/compare/1.0.43...1.0.45) --- updated-dependencies: - dependency-name: trybuild dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- node/overseer/all-subsystems-gen/Cargo.toml | 2 +- node/overseer/overseer-gen/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05fffd2056bc..e214f62099dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10943,9 +10943,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.43" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02c413315329fc96167f922b46fd0caa3a43f4697b7a7896b183c7142635832" +checksum = "5bdaf2a1d317f3d58b44b31c7f6436b9b9acafe7bddfeace50897c2b804d7792" dependencies = [ "glob", "lazy_static", diff --git a/node/overseer/all-subsystems-gen/Cargo.toml b/node/overseer/all-subsystems-gen/Cargo.toml index e8578f9cd1ed..2c227c308b41 100644 --- a/node/overseer/all-subsystems-gen/Cargo.toml +++ b/node/overseer/all-subsystems-gen/Cargo.toml @@ -15,4 +15,4 @@ proc-macro2 = "1.0.24" assert_matches = "1.5.0" [dev-dependencies] -trybuild = "1.0.42" +trybuild = "1.0.45" diff --git a/node/overseer/overseer-gen/Cargo.toml b/node/overseer/overseer-gen/Cargo.toml index b18f0e5bb4ec..90c96672b1b3 100644 --- a/node/overseer/overseer-gen/Cargo.toml +++ b/node/overseer/overseer-gen/Cargo.toml @@ -19,4 +19,4 @@ futures-timer = "3.0.2" pin-project = "1.0" [dev-dependencies] -trybuild = "1.0.41" +trybuild = "1.0.45" From 6b7e328e54b813dce9fafdeaa7a2537fc92d925c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Aug 2021 08:14:43 +0000 Subject: [PATCH 04/11] Bump tokio from 1.10.0 to 1.10.1 (#3717) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.10.0 to 1.10.1. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.10.0...tokio-1.10.1) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e214f62099dc..6a322e9bf581 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10679,9 +10679,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b" +checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5" dependencies = [ "autocfg", "bytes 1.0.1", From 38598678bdb6649384542a29ca45d54d6e1b009e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Aug 2021 11:54:35 +0200 Subject: [PATCH 05/11] Bump itertools from 0.10.0 to 0.10.1 (#3719) Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.0 to 0.10.1. - [Release notes](https://github.com/rust-itertools/itertools/releases) - [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.0...v0.10.1) --- updated-dependencies: - dependency-name: itertools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a322e9bf581..b2cabffc2682 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1283,7 +1283,7 @@ dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "itertools 0.10.0", + "itertools 0.10.1", "log", "serde", "smallvec", @@ -2878,9 +2878,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] @@ -6424,7 +6424,7 @@ dependencies = [ "env_logger 0.9.0", "futures 0.3.16", "futures-timer 3.0.2", - "itertools 0.10.0", + "itertools 0.10.1", "log", "lru", "metered-channel", @@ -7385,7 +7385,7 @@ checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" dependencies = [ "bytes 1.0.1", "heck", - "itertools 0.10.0", + "itertools 0.10.1", "log", "multimap", "petgraph", @@ -7415,7 +7415,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" dependencies = [ "anyhow", - "itertools 0.10.0", + "itertools 0.10.1", "proc-macro2", "quote", "syn", @@ -7717,7 +7717,7 @@ checksum = "3bd8f48b2066e9f69ab192797d66da804d1935bf22763204ed3675740cb0f221" dependencies = [ "derive_more", "fs-err", - "itertools 0.10.0", + "itertools 0.10.1", "static_init", "thiserror", ] From 4315bad8a7d93774e0744bfff0c7ea7d5079ed49 Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Wed, 25 Aug 2021 21:42:07 +0200 Subject: [PATCH 06/11] allow some overhead in MERKLE_NODE_MAX_SIZE (#3724) * allow overhead in MERKLE_NODE_MAX_SIZE * Update node/primitives/src/lib.rs Co-authored-by: cheme Co-authored-by: cheme --- node/primitives/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index ae982708c8cb..6aabb93d763c 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -52,8 +52,10 @@ pub use disputes::{ SignedDisputeStatement, UncheckedDisputeMessage, ValidDisputeVote, }; -// For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node. -const MERKLE_NODE_MAX_SIZE: usize = 512; +// For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node +// plus some overhead: +// header 1 + bitmap 2 + max partial_key 8 + children 16 * (32 + len 1) + value 32 + value len 1 +const MERKLE_NODE_MAX_SIZE: usize = 512 + 100; // 16-ary Merkle Prefix Trie for 32-bit ValidatorIndex has depth at most 8. const MERKLE_PROOF_MAX_DEPTH: usize = 8; From 43603f683f50ff396e5b7b78023edeb939996f26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Aug 2021 12:19:22 +0200 Subject: [PATCH 07/11] Bump async-trait from 0.1.50 to 0.1.51 (#3721) Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.50 to 0.1.51. - [Release notes](https://github.com/dtolnay/async-trait/releases) - [Commits](https://github.com/dtolnay/async-trait/compare/0.1.50...0.1.51) --- updated-dependencies: - dependency-name: async-trait dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- node/core/candidate-validation/Cargo.toml | 2 +- node/core/parachains-inherent/Cargo.toml | 2 +- node/malus/Cargo.toml | 2 +- node/metrics/Cargo.toml | 2 +- node/network/bridge/Cargo.toml | 2 +- node/network/dispute-distribution/Cargo.toml | 2 +- node/network/protocol/Cargo.toml | 2 +- node/overseer/Cargo.toml | 2 +- node/service/Cargo.toml | 2 +- node/subsystem-test-helpers/Cargo.toml | 2 +- node/subsystem-types/Cargo.toml | 4 ++-- node/subsystem-util/Cargo.toml | 4 ++-- 13 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2cabffc2682..a6a662a029f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,9 +339,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" [[package]] name = "async-trait" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" dependencies = [ "proc-macro2", "quote", diff --git a/node/core/candidate-validation/Cargo.toml b/node/core/candidate-validation/Cargo.toml index 6e388a99466d..f43fb36120a9 100644 --- a/node/core/candidate-validation/Cargo.toml +++ b/node/core/candidate-validation/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" futures = "0.3.15" tracing = "0.1.26" diff --git a/node/core/parachains-inherent/Cargo.toml b/node/core/parachains-inherent/Cargo.toml index 8fc07eb86316..fab4b3b10b08 100644 --- a/node/core/parachains-inherent/Cargo.toml +++ b/node/core/parachains-inherent/Cargo.toml @@ -9,7 +9,7 @@ futures = "0.3.15" futures-timer = "3.0.2" tracing = "0.1.26" thiserror = "1.0.26" -async-trait = "0.1.47" +async-trait = "0.1.51" polkadot-node-subsystem = { path = "../../subsystem" } polkadot-primitives = { path = "../../../primitives" } sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/malus/Cargo.toml b/node/malus/Cargo.toml index 7e5dbb429168..7a860e149cae 100644 --- a/node/malus/Cargo.toml +++ b/node/malus/Cargo.toml @@ -26,4 +26,4 @@ parity-util-mem = { version = "0.10.0", default-features = false, features = ["j color-eyre = { version = "0.5.11", default-features = false } assert_matches = "1.5" structopt = "0.3.21" -async-trait = "0.1.50" +async-trait = "0.1.51" diff --git a/node/metrics/Cargo.toml b/node/metrics/Cargo.toml index 9a51284f828e..adc7fa362244 100644 --- a/node/metrics/Cargo.toml +++ b/node/metrics/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" description = "Subsystem traits and message definitions" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" futures = "0.3.15" futures-timer = "3.0.2" diff --git a/node/network/bridge/Cargo.toml b/node/network/bridge/Cargo.toml index 098b9aefe83e..07ea1f80d81a 100644 --- a/node/network/bridge/Cargo.toml +++ b/node/network/bridge/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" futures = "0.3.15" tracing = "0.1.26" polkadot-primitives = { path = "../../../primitives" } diff --git a/node/network/dispute-distribution/Cargo.toml b/node/network/dispute-distribution/Cargo.toml index 12e3dc8d72d6..e2401e8b2cb1 100644 --- a/node/network/dispute-distribution/Cargo.toml +++ b/node/network/dispute-distribution/Cargo.toml @@ -25,7 +25,7 @@ rand = "0.8.3" lru = "0.6.6" [dev-dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" polkadot-subsystem-testhelpers = { package = "polkadot-node-subsystem-test-helpers", path = "../../subsystem-test-helpers" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["std"] } sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/network/protocol/Cargo.toml b/node/network/protocol/Cargo.toml index 2e4c3ee03019..8384e3e40652 100644 --- a/node/network/protocol/Cargo.toml +++ b/node/network/protocol/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" description = "Primitives types for the Node-side" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" polkadot-primitives = { path = "../../../primitives" } polkadot-node-primitives = { path = "../../primitives" } polkadot-node-jaeger = { path = "../../jaeger" } diff --git a/node/overseer/Cargo.toml b/node/overseer/Cargo.toml index d2ccd40cb0d2..8547e87ceddb 100644 --- a/node/overseer/Cargo.toml +++ b/node/overseer/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" client = { package = "sc-client-api", git = "https://github.com/paritytech/substrate", branch = "master" } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } futures = "0.3.15" diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index fabe12b87d35..c86de99a581c 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -67,7 +67,7 @@ serde = { version = "1.0.123", features = ["derive"] } thiserror = "1.0.26" kvdb = "0.10.0" kvdb-rocksdb = { version = "0.14.0", optional = true } -async-trait = "0.1.42" +async-trait = "0.1.51" # Polkadot polkadot-node-core-parachains-inherent = { path = "../core/parachains-inherent" } diff --git a/node/subsystem-test-helpers/Cargo.toml b/node/subsystem-test-helpers/Cargo.toml index 2686976ae9a1..333037d76cf2 100644 --- a/node/subsystem-test-helpers/Cargo.toml +++ b/node/subsystem-test-helpers/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" description = "Subsystem traits and message definitions" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" futures = "0.3.15" futures-timer = "3.0.2" tracing = "0.1.26" diff --git a/node/subsystem-types/Cargo.toml b/node/subsystem-types/Cargo.toml index 13ea766f0ff6..be1bb1ab8e0f 100644 --- a/node/subsystem-types/Cargo.toml +++ b/node/subsystem-types/Cargo.toml @@ -7,7 +7,7 @@ description = "Subsystem traits and message definitions" [dependencies] async-std = "1.8.0" -async-trait = "0.1.42" +async-trait = "0.1.51" derive_more = "0.99.11" futures = "0.3.12" futures-timer = "3.0.2" @@ -32,6 +32,6 @@ log = "0.4.13" [dev-dependencies] assert_matches = "1.4.0" -async-trait = "0.1.42" +async-trait = "0.1.51" futures = { version = "0.3.12", features = ["thread-pool"] } polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } diff --git a/node/subsystem-util/Cargo.toml b/node/subsystem-util/Cargo.toml index 9b018de0bf3e..ea4deb1ad391 100644 --- a/node/subsystem-util/Cargo.toml +++ b/node/subsystem-util/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" description = "Subsystem traits and message definitions" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.51" futures = "0.3.15" futures-timer = "3.0.2" itertools = "0.10" @@ -36,7 +36,7 @@ substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate [dev-dependencies] assert_matches = "1.4.0" -async-trait = "0.1.42" +async-trait = "0.1.51" env_logger = "0.9.0" futures = { version = "0.3.15", features = ["thread-pool"] } log = "0.4.13" From d8d5ce285287a185c3e62ae058bbbbf5c636ea8a Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Thu, 26 Aug 2021 12:41:16 +0200 Subject: [PATCH 08/11] XCM v2: Scripting, Query responses, Exception handling and Error reporting (#3629) * Intoduce XCM v2 Also some minor fix for v0/v1 * Minor version cleanup * Minor version cleanup * Introduce SendError for XcmSend trait to avoid cycles with having Outcome in Xcm * comment * Corrent type * Docs * Fix build * Fixes * Introduce the basic impl * Docs * Add function * Basic implementation * Weighed responses and on_report * Make XCM more script-like * Remove BuyExecution::orders * Fixes * Fixes * Fixes * Formatting * Initial draft and make pallet-xcm build * fix XCM tests * Formatting * Fixes * Formatting * spelling * Fixes * Fixes * spelling * tests for translation * extra fields to XCM pallet * Formatting * Fixes * spelling * first integration test * Another integration test * Formatting * fix tests * all tests * Fixes * Fixes * Formatting * Fixes * Fixes * Formatting * Bump * Remove unneeded structuring * add instruction * Fixes * spelling * Fixes * Fixes * Formatting * Fixes * Fixes * Formatting * Introduce and use VersionedResponse * Introduce versioning to dispatchables' params * Fixes * Formatting * Rest of merge * more work * Formatting * Basic logic * Fixes * Fixes * Add test * Fixes * Formatting * Fixes * Fixes * Fixes * Nits * Simplify * Spelling * Formatting * Return weight of unexecuted instructions in case of error as surplus * Formatting * Fixes * Test for instruction count limiting * Formatting * Docs --- node/test/service/src/lib.rs | 6 +- runtime/common/src/xcm_sender.rs | 12 +- runtime/kusama/src/lib.rs | 9 +- runtime/parachains/src/dmp.rs | 6 +- runtime/parachains/src/hrmp.rs | 18 +- runtime/rococo/src/lib.rs | 7 +- runtime/test-runtime/src/lib.rs | 92 ++- runtime/test-runtime/src/xcm_config.rs | 12 +- runtime/westend/src/lib.rs | 7 +- scripts/gitlab/lingua.dic | 5 + xcm/pallet-xcm/src/lib.rs | 479 +++++++++-- xcm/pallet-xcm/src/mock.rs | 138 +++- xcm/pallet-xcm/src/tests.rs | 182 ++++- xcm/src/lib.rs | 120 ++- xcm/src/v0/mod.rs | 6 +- xcm/src/v1/mod.rs | 150 +++- xcm/src/v1/multilocation.rs | 35 +- xcm/src/v1/order.rs | 84 +- xcm/src/v2/mod.rs | 761 ++++++++++++++++++ xcm/src/v2/traits.rs | 308 +++++++ xcm/xcm-builder/src/barriers.rs | 73 +- xcm/xcm-builder/src/mock.rs | 8 +- xcm/xcm-builder/src/tests.rs | 313 ++++--- xcm/xcm-builder/src/weight.rs | 103 +-- xcm/xcm-builder/tests/mock/mod.rs | 11 +- xcm/xcm-builder/tests/scenarios.rs | 209 +++-- xcm/xcm-executor/integration-tests/src/lib.rs | 184 ++++- xcm/xcm-executor/src/config.rs | 4 +- xcm/xcm-executor/src/lib.rs | 485 ++++++----- xcm/xcm-executor/src/traits/on_response.rs | 14 +- xcm/xcm-executor/src/traits/should_execute.rs | 31 +- xcm/xcm-executor/src/traits/transact_asset.rs | 6 +- xcm/xcm-executor/src/traits/weight.rs | 38 +- xcm/xcm-simulator/example/src/lib.rs | 86 +- xcm/xcm-simulator/example/src/parachain.rs | 7 +- xcm/xcm-simulator/example/src/relay_chain.rs | 9 +- xcm/xcm-simulator/src/lib.rs | 12 +- 37 files changed, 3067 insertions(+), 963 deletions(-) create mode 100644 xcm/src/v2/mod.rs create mode 100644 xcm/src/v2/traits.rs diff --git a/node/test/service/src/lib.rs b/node/test/service/src/lib.rs index bf5193469fa3..f8207829852b 100644 --- a/node/test/service/src/lib.rs +++ b/node/test/service/src/lib.rs @@ -276,7 +276,7 @@ impl PolkadotTestNode { function: impl Into, caller: Sr25519Keyring, ) -> Result { - let extrinsic = construct_extrinsic(&*self.client, function, caller); + let extrinsic = construct_extrinsic(&*self.client, function, caller, 0); self.rpc_handlers.send_transaction(extrinsic.into()).await } @@ -332,12 +332,12 @@ pub fn construct_extrinsic( client: &Client, function: impl Into, caller: Sr25519Keyring, + nonce: u32, ) -> UncheckedExtrinsic { let function = function.into(); let current_block_hash = client.info().best_hash; let current_block = client.info().best_number.saturated_into(); let genesis_block = client.hash(0).unwrap().unwrap(); - let nonce = 0; let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let tip = 0; @@ -384,5 +384,5 @@ pub fn construct_transfer_extrinsic( value, )); - construct_extrinsic(client, function, origin) + construct_extrinsic(client, function, origin, 0) } diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 602f45bbadf6..af1f783cfa9a 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -19,7 +19,7 @@ use parity_scale_codec::Encode; use runtime_parachains::{configuration, dmp}; use sp_std::marker::PhantomData; -use xcm::opaque::latest::*; +use xcm::latest::prelude::*; /// XCM sender for relay chain. It only sends downward message. pub struct ChildParachainRouter(PhantomData<(T, W)>); @@ -27,22 +27,22 @@ pub struct ChildParachainRouter(PhantomData<(T, W)>); impl SendXcm for ChildParachainRouter { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> Result { + fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> SendResult { match dest { - MultiLocation { parents: 0, interior: Junctions::X1(Junction::Parachain(id)) } => { + MultiLocation { parents: 0, interior: X1(Parachain(id)) } => { // Downward message passing. let versioned_xcm = - W::wrap_version(&dest, msg).map_err(|()| Error::DestinationUnsupported)?; + W::wrap_version(&dest, msg).map_err(|()| SendError::DestinationUnsupported)?; let config = >::config(); >::queue_downward_message( &config, id.into(), versioned_xcm.encode(), ) - .map_err(Into::::into)?; + .map_err(Into::::into)?; Ok(()) }, - dest => Err(Error::CannotReachDestination(dest, msg)), + dest => Err(SendError::CannotReachDestination(dest, msg)), } } } diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index ede28280b21a..3d7eac08e775 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1255,6 +1255,9 @@ type LocalOriginConverter = ( parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. pub const BaseXcmWeight: Weight = 1_000_000_000; + /// Maximum number of instructions in a single XCM fragment. A sanity check against weight + /// calculations getting too crazy. + pub const MaxInstructions: u32 = 100; } /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our @@ -1290,7 +1293,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = TrustedTeleporters; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. type Trader = UsingComponents>; type ResponseHandler = (); @@ -1325,8 +1328,10 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; } parameter_types! { diff --git a/runtime/parachains/src/dmp.rs b/runtime/parachains/src/dmp.rs index 6ca7f09fc773..08def77f9c35 100644 --- a/runtime/parachains/src/dmp.rs +++ b/runtime/parachains/src/dmp.rs @@ -22,7 +22,7 @@ use frame_support::pallet_prelude::*; use primitives::v1::{DownwardMessage, Hash, Id as ParaId, InboundDownwardMessage}; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; use sp_std::{fmt, prelude::*}; -use xcm::latest::Error as XcmError; +use xcm::latest::SendError; pub use pallet::*; @@ -33,10 +33,10 @@ pub enum QueueDownwardMessageError { ExceedsMaxMessageSize, } -impl From for XcmError { +impl From for SendError { fn from(err: QueueDownwardMessageError) -> Self { match err { - QueueDownwardMessageError::ExceedsMaxMessageSize => XcmError::ExceedsMaxMessageSize, + QueueDownwardMessageError::ExceedsMaxMessageSize => SendError::ExceedsMaxMessageSize, } } } diff --git a/runtime/parachains/src/hrmp.rs b/runtime/parachains/src/hrmp.rs index 1a85cbe9451e..c31f658faf36 100644 --- a/runtime/parachains/src/hrmp.rs +++ b/runtime/parachains/src/hrmp.rs @@ -1007,13 +1007,13 @@ impl Pallet { let notification_bytes = { use parity_scale_codec::Encode as _; - use xcm::opaque::{v1::Xcm, VersionedXcm}; + use xcm::opaque::{latest::prelude::*, VersionedXcm}; - VersionedXcm::from(Xcm::HrmpNewChannelOpenRequest { + VersionedXcm::from(Xcm(vec![HrmpNewChannelOpenRequest { sender: u32::from(origin), max_capacity: proposed_max_capacity, max_message_size: proposed_max_message_size, - }) + }])) .encode() }; if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) = @@ -1066,9 +1066,9 @@ impl Pallet { let notification_bytes = { use parity_scale_codec::Encode as _; - use xcm::opaque::{v1::Xcm, VersionedXcm}; - - VersionedXcm::from(Xcm::HrmpChannelAccepted { recipient: u32::from(origin) }).encode() + use xcm::opaque::{latest::prelude::*, VersionedXcm}; + let xcm = Xcm(vec![HrmpChannelAccepted { recipient: u32::from(origin) }]); + VersionedXcm::from(xcm).encode() }; if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) = >::queue_downward_message(&config, sender, notification_bytes) @@ -1106,13 +1106,13 @@ impl Pallet { let config = >::config(); let notification_bytes = { use parity_scale_codec::Encode as _; - use xcm::opaque::{v1::Xcm, VersionedXcm}; + use xcm::opaque::{latest::prelude::*, VersionedXcm}; - VersionedXcm::from(Xcm::HrmpChannelClosing { + VersionedXcm::from(Xcm(vec![HrmpChannelClosing { initiator: u32::from(origin), sender: u32::from(channel_id.sender), recipient: u32::from(channel_id.recipient), - }) + }])) .encode() }; let opposite_party = diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 35d78487eca9..1ae220330b1c 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -631,6 +631,7 @@ parameter_types! { pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(110).into()); pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(120).into()); pub const RococoForStatemint: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(1001).into()); + pub const MaxInstructions: u32 = 100; } pub type TrustedTeleporters = ( xcm_builder::Case, @@ -666,7 +667,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = TrustedTeleporters; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = UsingComponents>; type ResponseHandler = (); } @@ -696,8 +697,10 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; } impl parachains_session_info::Config for Runtime {} diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 1ca04fbf1184..529a44f53d09 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -495,6 +495,7 @@ impl parachains_ump::Config for Runtime { parameter_types! { pub const BaseXcmWeight: frame_support::weights::Weight = 1_000; pub const AnyNetwork: xcm::latest::NetworkId = xcm::latest::NetworkId::Any; + pub const MaxInstructions: u32 = 100; } pub type LocalOriginToLocation = xcm_builder::SignedToAccountId32; @@ -506,12 +507,14 @@ impl pallet_xcm::Config for Runtime { type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type LocationInverter = xcm_config::InvertNothing; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; - type Weigher = xcm_builder::FixedWeightBounds; + type Weigher = xcm_builder::FixedWeightBounds; type XcmRouter = xcm_config::DoNothingRouter; type XcmExecuteFilter = Everything; type XcmExecutor = xcm_executor::XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; + type Origin = Origin; + type Call = Call; } impl parachains_hrmp::Config for Runtime { @@ -524,6 +527,91 @@ impl parachains_scheduler::Config for Runtime {} impl paras_sudo_wrapper::Config for Runtime {} +impl pallet_test_notifier::Config for Runtime { + type Event = Event; + type Origin = Origin; + type Call = Call; +} + +#[frame_support::pallet] +pub mod pallet_test_notifier { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use pallet_xcm::{ensure_response, QueryId}; + use sp_runtime::DispatchResult; + use xcm::latest::prelude::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_xcm::Config { + type Event: IsType<::Event> + From>; + type Origin: IsType<::Origin> + + Into::Origin>>; + type Call: IsType<::Call> + From>; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + QueryPrepared(QueryId), + NotifyQueryPrepared(QueryId), + ResponseReceived(MultiLocation, QueryId, Response), + } + + #[pallet::error] + pub enum Error { + UnexpectedId, + BadAccountFormat, + } + + #[pallet::call] + impl Pallet { + #[pallet::weight(1_000_000)] + pub fn prepare_new_query(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let id = who + .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) + .map_err(|_| Error::::BadAccountFormat)?; + let qid = pallet_xcm::Pallet::::new_query( + Junction::AccountId32 { network: Any, id }.into(), + 100u32.into(), + ); + Self::deposit_event(Event::::QueryPrepared(qid)); + Ok(()) + } + + #[pallet::weight(1_000_000)] + pub fn prepare_new_notify_query(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let id = who + .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) + .map_err(|_| Error::::BadAccountFormat)?; + let call = Call::::notification_received(0, Default::default()); + let qid = pallet_xcm::Pallet::::new_notify_query( + Junction::AccountId32 { network: Any, id }.into(), + ::Call::from(call), + 100u32.into(), + ); + Self::deposit_event(Event::::NotifyQueryPrepared(qid)); + Ok(()) + } + + #[pallet::weight(1_000_000)] + pub fn notification_received( + origin: OriginFor, + query_id: QueryId, + response: Response, + ) -> DispatchResult { + let responder = ensure_response(::Origin::from(origin))?; + Self::deposit_event(Event::::ResponseReceived(responder, query_id, response)); + Ok(()) + } + } +} + construct_runtime! { pub enum Runtime where Block = Block, @@ -573,6 +661,8 @@ construct_runtime! { ParasDisputes: parachains_disputes::{Pallet, Storage, Event}, Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event}, + + TestNotifier: pallet_test_notifier::{Pallet, Call, Event}, } } diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 7748d55e1875..3af079e92f43 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -15,10 +15,7 @@ // along with Polkadot. If not, see . use frame_support::{parameter_types, traits::Everything, weights::Weight}; -use xcm::latest::{ - Error as XcmError, Junctions::Here, MultiAsset, MultiLocation, NetworkId, Parent, - Result as XcmResult, SendXcm, Xcm, -}; +use xcm::latest::prelude::*; use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, SignedToAccountId32}; use xcm_executor::{ traits::{InvertLocation, TransactAsset, WeightTrader}, @@ -27,6 +24,7 @@ use xcm_executor::{ parameter_types! { pub const OurNetwork: NetworkId = NetworkId::Polkadot; + pub const MaxInstructions: u32 = 100; } /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location @@ -38,7 +36,7 @@ pub type LocalOriginToLocation = ( pub struct DoNothingRouter; impl SendXcm for DoNothingRouter { - fn send_xcm(_dest: MultiLocation, _msg: Xcm<()>) -> XcmResult { + fn send_xcm(_dest: MultiLocation, _msg: Xcm<()>) -> SendResult { Ok(()) } } @@ -85,7 +83,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = (); type LocationInverter = InvertNothing; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = DummyWeightTrader; - type ResponseHandler = (); + type ResponseHandler = super::Xcm; } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 8910e306d54c..0be1568f0fc3 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -912,6 +912,7 @@ pub type XcmRouter = ( parameter_types! { pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) = (Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), Parachain(1000).into()); + pub const MaxInstructions: u32 = 100; } pub type TrustedTeleporters = (xcm_builder::Case,); @@ -935,7 +936,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = TrustedTeleporters; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = UsingComponents>; type ResponseHandler = (); } @@ -958,8 +959,10 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; } construct_runtime! { diff --git a/scripts/gitlab/lingua.dic b/scripts/gitlab/lingua.dic index 6e0ffd5c6c39..e7b4348cbaec 100644 --- a/scripts/gitlab/lingua.dic +++ b/scripts/gitlab/lingua.dic @@ -108,6 +108,7 @@ inverter/MS io IP/S isn +isolatable isolate/BG iterable jaeger/MS @@ -189,6 +190,7 @@ prometheus/MS provisioner/MS proxy/DMSG proxy/G +proxying PRs PVF/S README/MS @@ -200,6 +202,7 @@ responder/SM retriability reverify roundtrip/MS +routable rpc RPC/MS runtime/MS @@ -231,6 +234,7 @@ taskmanager/MS TCP teleport/D teleport/RG +teleports teleportation/SM teleporter/SM teleporters @@ -251,6 +255,7 @@ unordered unreceived unreserve unreserving +unroutable unservable/B untrusted untyped diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index c6772cb6df37..acc5a6269f23 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -33,7 +33,10 @@ use sp_std::{ prelude::*, vec, }; -use xcm::{latest::prelude::*, VersionedMultiAssets, VersionedMultiLocation, VersionedXcm}; +use xcm::{ + latest::prelude::*, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, + VersionedXcm, +}; use xcm_executor::traits::ConvertOrigin; use frame_support::PalletId; @@ -42,10 +45,13 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use sp_runtime::traits::AccountIdConversion; - use xcm_executor::traits::{InvertLocation, WeightBounds}; + use frame_support::{ + dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + }; + use frame_system::{pallet_prelude::*, Config as SysConfig}; + use sp_runtime::traits::{AccountIdConversion, BlockNumberProvider}; + use xcm_executor::traits::{InvertLocation, OnResponse, WeightBounds}; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -59,7 +65,7 @@ pub mod pallet { /// Required origin for sending XCM messages. If successful, the it resolves to `MultiLocation` /// which exists as an interior location within this chain's XCM context. - type SendXcmOrigin: EnsureOrigin; + type SendXcmOrigin: EnsureOrigin<::Origin, Success = MultiLocation>; /// The type used to actually dispatch an XCM to its destination. type XcmRouter: SendXcm; @@ -67,13 +73,13 @@ pub mod pallet { /// Required origin for executing XCM messages, including the teleport functionality. If successful, /// then it resolves to `MultiLocation` which exists as an interior location within this chain's XCM /// context. - type ExecuteXcmOrigin: EnsureOrigin; + type ExecuteXcmOrigin: EnsureOrigin<::Origin, Success = MultiLocation>; /// Our XCM filter which messages to be executed using `XcmExecutor` must pass. - type XcmExecuteFilter: Contains<(MultiLocation, Xcm)>; + type XcmExecuteFilter: Contains<(MultiLocation, Xcm<::Call>)>; /// Something to execute an XCM message. - type XcmExecutor: ExecuteXcm; + type XcmExecutor: ExecuteXcm<::Call>; /// Our XCM filter which messages to be teleported using the dedicated extrinsic must pass. type XcmTeleportFilter: Contains<(MultiLocation, Vec)>; @@ -82,10 +88,19 @@ pub mod pallet { type XcmReserveTransferFilter: Contains<(MultiLocation, Vec)>; /// Means of measuring the weight consumed by an XCM message locally. - type Weigher: WeightBounds; + type Weigher: WeightBounds<::Call>; /// Means of inverting a location. type LocationInverter: InvertLocation; + + /// The outer `Origin` type. + type Origin: From + From<::Origin>; + + /// The outer `Call` type. + type Call: Parameter + + GetDispatchInfo + + IsType<::Call> + + Dispatchable::Origin, PostInfo = PostDispatchInfo>; } /// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic. @@ -94,13 +109,90 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// Execution of an XCM message was attempted. + /// + /// \[ outcome \] Attempted(xcm::latest::Outcome), + /// A XCM message was sent. + /// + /// \[ origin, destination, message \] Sent(MultiLocation, MultiLocation, Xcm<()>), + /// Query response received which does not match a registered query. This may be because a + /// matching query was never registered, it may be because it is a duplicate response, or + /// because the query timed out. + /// + /// \[ origin location, id \] + UnexpectedResponse(MultiLocation, QueryId), + /// Query response has been received and is ready for taking with `take_response`. There is + /// no registered notification call. + /// + /// \[ id, response \] + ResponseReady(QueryId, Response), + /// Query response has been received and query is removed. The registered notification has + /// been dispatched and executed successfully. + /// + /// \[ id, pallet index, call index \] + Notified(QueryId, u8, u8), + /// Query response has been received and query is removed. The registered notification could + /// not be dispatched because the dispatch weight is greater than the maximum weight + /// originally budgeted by this runtime for the query result. + /// + /// \[ id, pallet index, call index, actual weight, max budgeted weight \] + NotifyOverweight(QueryId, u8, u8, Weight, Weight), + /// Query response has been received and query is removed. There was a general error with + /// dispatching the notification call. + /// + /// \[ id, pallet index, call index \] + NotifyDispatchError(QueryId, u8, u8), + /// Query response has been received and query is removed. The dispatch was unable to be + /// decoded into a `Call`; this might be due to dispatch function having a signature which + /// is not `(origin, QueryId, Response)`. + /// + /// \[ id, pallet index, call index \] + NotifyDecodeFailed(QueryId, u8, u8), + /// Expected query response has been received but the origin location of the response does + /// not match that expected. The query remains registered for a later, valid, response to + /// be received and acted upon. + /// + /// \[ origin location, id, expected location \] + InvalidResponder(MultiLocation, QueryId, MultiLocation), + /// Expected query response has been received but the expected origin location placed in + /// storate by this runtime previously cannot be decoded. The query remains registered. + /// + /// This is unexpected (since a location placed in storage in a previously executing + /// runtime should be readable prior to query timeout) and dangerous since the possibly + /// valid response will be dropped. Manual governance intervention is probably going to be + /// needed. + /// + /// \[ origin location, id \] + InvalidResponderVersion(MultiLocation, QueryId), + /// Received query response has been read and removed. + /// + /// \[ id \] + ResponseTaken(QueryId), + } + + #[pallet::origin] + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] + pub enum Origin { + /// It comes from somewhere in the XCM space wanting to transact. + Xcm(MultiLocation), + /// It comes as an expected response from an XCM location. + Response(MultiLocation), + } + impl From for Origin { + fn from(location: MultiLocation) -> Origin { + Origin::Xcm(location) + } } #[pallet::error] pub enum Error { + /// The desired destination was unreachable, generally because there is a no way of routing + /// to it. Unreachable, + /// There was some other issue (i.e. not to do with routing) in sending the message. Perhaps + /// a lack of space for buffering the message. SendFailure, /// The message execution fails the filter. Filtered, @@ -118,6 +210,32 @@ pub mod pallet { BadVersion, } + /// The status of a query. + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] + pub enum QueryStatus { + /// The query was sent but no response has yet been received. + Pending { + responder: VersionedMultiLocation, + maybe_notify: Option<(u8, u8)>, + timeout: BlockNumber, + }, + /// A response has been received. + Ready { response: VersionedResponse, at: BlockNumber }, + } + + /// Value of a query, must be unique for each query. + pub type QueryId = u64; + + /// The latest available query index. + #[pallet::storage] + pub(super) type QueryCount = StorageValue<_, QueryId, ValueQuery>; + + /// The ongoing queries. + #[pallet::storage] + #[pallet::getter(fn query)] + pub(super) type Queries = + StorageMap<_, Blake2_128Concat, QueryId, QueryStatus, OptionQuery>; + #[pallet::hooks] impl Hooks> for Pallet {} @@ -136,7 +254,7 @@ pub mod pallet { let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; Self::send_xcm(interior, dest.clone(), message.clone()).map_err(|e| match e { - XcmError::CannotReachDestination(..) => Error::::Unreachable, + SendError::CannotReachDestination(..) => Error::::Unreachable, _ => Error::::SendFailure, })?; Self::deposit_event(Event::Sent(origin_location, dest, message)); @@ -161,14 +279,11 @@ pub mod pallet { let maybe_dest: Result = (*dest.clone()).try_into(); match (maybe_assets, maybe_dest) { (Ok(assets), Ok(dest)) => { - let mut message = Xcm::WithdrawAsset { - assets, - effects: sp_std::vec![ InitiateTeleport { - assets: Wild(All), - dest, - effects: sp_std::vec![], - } ] - }; + use sp_std::vec; + let mut message = Xcm(vec![ + WithdrawAsset(assets), + InitiateTeleport { assets: Wild(All), dest, xcm: Xcm(vec![]) }, + ]); T::Weigher::weight(&mut message).map_or(Weight::max_value(), |w| 100_000_000 + w) }, _ => Weight::max_value(), @@ -180,7 +295,6 @@ pub mod pallet { beneficiary: Box, assets: Box, fee_asset_item: u32, - dest_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; let dest = MultiLocation::try_from(*dest).map_err(|()| Error::::BadVersion)?; @@ -201,24 +315,17 @@ pub mod pallet { .map_err(|_| Error::::CannotReanchor)?; let max_assets = assets.len() as u32; let assets = assets.into(); - let mut message = Xcm::WithdrawAsset { - assets, - effects: vec![InitiateTeleport { + let mut message = Xcm(vec![ + WithdrawAsset(assets), + InitiateTeleport { assets: Wild(All), dest, - effects: vec![ - BuyExecution { - fees, - // Zero weight for additional XCM (since there are none to execute) - weight: 0, - debt: dest_weight, - halt_on_error: false, - instructions: vec![], - }, + xcm: Xcm(vec![ + BuyExecution { fees, weight_limit: Unlimited }, DepositAsset { assets: Wild(All), max_assets, beneficiary }, - ], - }], - }; + ]), + }, + ]); let weight = T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; let outcome = @@ -239,13 +346,15 @@ pub mod pallet { /// an `AccountId32` value. /// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the /// `dest` side. - /// - `dest_weight`: Equal to the total weight on `dest` of the XCM message - /// `ReserveAssetDeposited { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`. + /// - `fee_asset_item`: The index into `assets` of the item which should be used to pay + /// fees. #[pallet::weight({ match ((*assets.clone()).try_into(), (*dest.clone()).try_into()) { (Ok(assets), Ok(dest)) => { - let effects = sp_std::vec![]; - let mut message = Xcm::TransferReserveAsset { assets, dest, effects }; + use sp_std::vec; + let mut message = Xcm(vec![ + TransferReserveAsset { assets, dest, xcm: Xcm(vec![]) } + ]); T::Weigher::weight(&mut message).map_or(Weight::max_value(), |w| 100_000_000 + w) }, _ => Weight::max_value(), @@ -257,7 +366,6 @@ pub mod pallet { beneficiary: Box, assets: Box, fee_asset_item: u32, - dest_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; let dest = (*dest).try_into().map_err(|()| Error::::BadVersion)?; @@ -277,21 +385,14 @@ pub mod pallet { .map_err(|_| Error::::CannotReanchor)?; let max_assets = assets.len() as u32; let assets = assets.into(); - let mut message = Xcm::TransferReserveAsset { + let mut message = Xcm(vec![TransferReserveAsset { assets, dest, - effects: vec![ - BuyExecution { - fees, - // Zero weight for additional instructions/orders (since there are none to execute) - weight: 0, - debt: dest_weight, // covers this, `TransferReserveAsset` xcm, and `DepositAsset` order. - halt_on_error: false, - instructions: vec![], - }, + xcm: Xcm(vec![ + BuyExecution { fees, weight_limit: Unlimited }, DepositAsset { assets: Wild(All), max_assets, beneficiary }, - ], - }; + ]), + }]); let weight = T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; let outcome = @@ -314,7 +415,7 @@ pub mod pallet { #[pallet::weight(max_weight.saturating_add(100_000_000u64))] pub fn execute( origin: OriginFor, - message: Box>, + message: Box::Call>>, max_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; @@ -334,12 +435,10 @@ pub mod pallet { pub fn send_xcm( interior: Junctions, dest: MultiLocation, - message: Xcm<()>, - ) -> Result<(), XcmError> { - let message = if let Junctions::Here = interior { - message - } else { - Xcm::<()>::RelayedFrom { who: interior, message: Box::new(message) } + mut message: Xcm<()>, + ) -> Result<(), SendError> { + if interior != Junctions::Here { + message.0.insert(0, DescendOrigin(interior)) }; log::trace!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message); T::XcmRouter::send_xcm(dest, message) @@ -349,25 +448,215 @@ pub mod pallet { const ID: PalletId = PalletId(*b"py/xcmch"); AccountIdConversion::::into_account(&ID) } - } - /// Origin for the parachains module. - #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] - #[pallet::origin] - pub enum Origin { - /// It comes from somewhere in the XCM space. - Xcm(MultiLocation), + fn do_new_query( + responder: MultiLocation, + maybe_notify: Option<(u8, u8)>, + timeout: T::BlockNumber, + ) -> u64 { + QueryCount::::mutate(|q| { + let r = *q; + *q += 1; + Queries::::insert( + r, + QueryStatus::Pending { responder: responder.into(), maybe_notify, timeout }, + ); + r + }) + } + + /// Consume `message` and return another which is equivalent to it except that it reports + /// back the outcome. + /// + /// - `message`: The message whose outcome should be reported. + /// - `responder`: The origin from which a response should be expected. + /// - `timeout`: The block number after which it is permissible for `notify` not to be + /// called even if a response is received. + /// + /// To check the status of the query, use `fn query()` passing the resultant `QueryId` + /// value. + pub fn report_outcome( + message: &mut Xcm<()>, + responder: MultiLocation, + timeout: T::BlockNumber, + ) -> QueryId { + let dest = T::LocationInverter::invert_location(&responder); + let query_id = Self::new_query(responder, timeout); + let report_error = Xcm(vec![ReportError { dest, query_id, max_response_weight: 0 }]); + message.0.insert(0, SetAppendix(report_error)); + query_id + } + + /// Consume `message` and return another which is equivalent to it except that it reports + /// back the outcome and dispatches `notify` on this chain. + /// + /// - `message`: The message whose outcome should be reported. + /// - `responder`: The origin from which a response should be expected. + /// - `notify`: A dispatchable function which will be called once the outcome of `message` + /// is known. It may be a dispatchable in any pallet of the local chain, but other than + /// the usual origin, it must accept exactly two arguments: `query_id: QueryId` and + /// `outcome: Response`, and in that order. It should expect that the origin is + /// `Origin::Response` and will contain the responder's location. + /// - `timeout`: The block number after which it is permissible for `notify` not to be + /// called even if a response is received. + /// + /// NOTE: `notify` gets called as part of handling an incoming message, so it should be + /// lightweight. Its weight is estimated during this function and stored ready for + /// weighing `ReportOutcome` on the way back. If it turns out to be heavier once it returns + /// then reporting the outcome will fail. Futhermore if the estimate is too high, then it + /// may be put in the overweight queue and need to be manually executed. + pub fn report_outcome_notify( + message: &mut Xcm<()>, + responder: MultiLocation, + notify: impl Into<::Call>, + timeout: T::BlockNumber, + ) { + let dest = T::LocationInverter::invert_location(&responder); + let notify: ::Call = notify.into(); + let max_response_weight = notify.get_dispatch_info().weight; + let query_id = Self::new_notify_query(responder, notify, timeout); + let report_error = Xcm(vec![ReportError { dest, query_id, max_response_weight }]); + message.0.insert(0, SetAppendix(report_error)); + } + + /// Attempt to create a new query ID and register it as a query that is yet to respond. + pub fn new_query(responder: MultiLocation, timeout: T::BlockNumber) -> u64 { + Self::do_new_query(responder, None, timeout) + } + + /// Attempt to create a new query ID and register it as a query that is yet to respond, and + /// which will call a dispatchable when a response happens. + pub fn new_notify_query( + responder: MultiLocation, + notify: impl Into<::Call>, + timeout: T::BlockNumber, + ) -> u64 { + let notify = + notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect( + "decode input is output of Call encode; Call guaranteed to have two enums; qed", + ); + Self::do_new_query(responder, Some(notify), timeout) + } + + /// Attempt to remove and return the response of query with ID `query_id`. + /// + /// Returns `None` if the response is not (yet) available. + pub fn take_response(query_id: QueryId) -> Option<(Response, T::BlockNumber)> { + if let Some(QueryStatus::Ready { response, at }) = Queries::::get(query_id) { + let response = response.try_into().ok()?; + Queries::::remove(query_id); + Self::deposit_event(Event::ResponseTaken(query_id)); + Some((response, at)) + } else { + None + } + } } - impl From for Origin { - fn from(location: MultiLocation) -> Origin { - Origin::Xcm(location) + impl OnResponse for Pallet { + /// Returns `true` if we are expecting a response from `origin` for query `query_id`. + fn expecting_response(origin: &MultiLocation, query_id: QueryId) -> bool { + if let Some(QueryStatus::Pending { responder, .. }) = Queries::::get(query_id) { + return MultiLocation::try_from(responder).map_or(false, |r| origin == &r) + } + false + } + + /// Handler for receiving a `response` from `origin` relating to `query_id`. + fn on_response( + origin: &MultiLocation, + query_id: QueryId, + response: Response, + max_weight: Weight, + ) -> Weight { + if let Some(QueryStatus::Pending { responder, maybe_notify, .. }) = + Queries::::get(query_id) + { + if let Ok(responder) = MultiLocation::try_from(responder) { + if origin == &responder { + return match maybe_notify { + Some((pallet_index, call_index)) => { + // This is a bit horrible, but we happen to know that the `Call` will + // be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`. + // So we just encode that and then re-encode to a real Call. + let bare = (pallet_index, call_index, query_id, response); + if let Ok(call) = bare.using_encoded(|mut bytes| { + ::Call::decode(&mut bytes) + }) { + Queries::::remove(query_id); + let weight = call.get_dispatch_info().weight; + if weight > max_weight { + let e = Event::NotifyOverweight( + query_id, + pallet_index, + call_index, + weight, + max_weight, + ); + Self::deposit_event(e); + return 0 + } + let dispatch_origin = Origin::Response(origin.clone()).into(); + match call.dispatch(dispatch_origin) { + Ok(post_info) => { + let e = + Event::Notified(query_id, pallet_index, call_index); + Self::deposit_event(e); + post_info.actual_weight + }, + Err(error_and_info) => { + let e = Event::NotifyDispatchError( + query_id, + pallet_index, + call_index, + ); + Self::deposit_event(e); + // Not much to do with the result as it is. It's up to the parachain to ensure that the + // message makes sense. + error_and_info.post_info.actual_weight + }, + } + .unwrap_or(weight) + } else { + let e = Event::NotifyDecodeFailed( + query_id, + pallet_index, + call_index, + ); + Self::deposit_event(e); + 0 + } + }, + None => { + let e = Event::ResponseReady(query_id, response.clone()); + Self::deposit_event(e); + let at = frame_system::Pallet::::current_block_number(); + let response = response.into(); + Queries::::insert(query_id, QueryStatus::Ready { response, at }); + 0 + }, + } + } else { + Self::deposit_event(Event::InvalidResponder( + origin.clone(), + query_id, + responder, + )); + } + } else { + Self::deposit_event(Event::InvalidResponderVersion(origin.clone(), query_id)); + } + } else { + Self::deposit_event(Event::UnexpectedResponse(origin.clone(), query_id)); + } + 0 } } } -/// Ensure that the origin `o` represents a sibling parachain. -/// Returns `Ok` with the parachain ID of the sibling or an `Err` otherwise. +/// Ensure that the origin `o` represents an XCM (`Transact`) origin. +/// +/// Returns `Ok` with the location of the XCM sender or an `Err` otherwise. pub fn ensure_xcm(o: OuterOrigin) -> Result where OuterOrigin: Into>, @@ -378,6 +667,19 @@ where } } +/// Ensure that the origin `o` represents an XCM response origin. +/// +/// Returns `Ok` with the location of the responder or an `Err` otherwise. +pub fn ensure_response(o: OuterOrigin) -> Result +where + OuterOrigin: Into>, +{ + match o.into() { + Ok(Origin::Response(location)) => Ok(location), + _ => Err(BadOrigin), + } +} + /// Filter for `MultiLocation` to find those which represent a strict majority approval of an identified /// plurality. /// @@ -403,12 +705,10 @@ where fn try_origin(outer: O) -> Result { outer.try_with_caller(|caller| { - caller.try_into().and_then(|Origin::Xcm(location)| { - if F::contains(&location) { - Ok(location) - } else { - Err(Origin::Xcm(location).into()) - } + caller.try_into().and_then(|o| match o { + Origin::Xcm(location) if F::contains(&location) => Ok(location), + Origin::Xcm(location) => Err(Origin::Xcm(location).into()), + o => Err(o.into()), }) }) } @@ -419,6 +719,31 @@ where } } +/// `EnsureOrigin` implementation succeeding with a `MultiLocation` value to recognize and filter +/// the `Origin::Response` item. +pub struct EnsureResponse(PhantomData); +impl, F: Contains> EnsureOrigin + for EnsureResponse +where + O::PalletsOrigin: From + TryInto, +{ + type Success = MultiLocation; + + fn try_origin(outer: O) -> Result { + outer.try_with_caller(|caller| { + caller.try_into().and_then(|o| match o { + Origin::Response(responder) => Ok(responder), + o => Err(o.into()), + }) + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + O::from(Origin::Response(Here.into())) + } +} + /// A simple passthrough where we reuse the `MultiLocation`-typed XCM origin as the inner value of /// this crate's `Origin::Xcm` value. pub struct XcmPassthrough(PhantomData); diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index ac612a0641f1..66c427b9ff3c 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -20,13 +20,10 @@ use polkadot_runtime_parachains::origin; use sp_core::H256; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData}; -use xcm::{ - latest::prelude::*, - opaque::latest::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm}, -}; +use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, - ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, + AccountId32Aliases, AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, Case, + ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, @@ -40,6 +37,85 @@ pub type Balance = u128; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +#[frame_support::pallet] +pub mod pallet_test_notifier { + use crate::{ensure_response, QueryId}; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_runtime::DispatchResult; + use xcm::latest::prelude::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + crate::Config { + type Event: IsType<::Event> + From>; + type Origin: IsType<::Origin> + + Into::Origin>>; + type Call: IsType<::Call> + From>; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + QueryPrepared(QueryId), + NotifyQueryPrepared(QueryId), + ResponseReceived(MultiLocation, QueryId, Response), + } + + #[pallet::error] + pub enum Error { + UnexpectedId, + BadAccountFormat, + } + + #[pallet::call] + impl Pallet { + #[pallet::weight(1_000_000)] + pub fn prepare_new_query(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let id = who + .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) + .map_err(|_| Error::::BadAccountFormat)?; + let qid = crate::Pallet::::new_query( + Junction::AccountId32 { network: Any, id }.into(), + 100u32.into(), + ); + Self::deposit_event(Event::::QueryPrepared(qid)); + Ok(()) + } + + #[pallet::weight(1_000_000)] + pub fn prepare_new_notify_query(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let id = who + .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) + .map_err(|_| Error::::BadAccountFormat)?; + let call = Call::::notification_received(0, Default::default()); + let qid = crate::Pallet::::new_notify_query( + Junction::AccountId32 { network: Any, id }.into(), + ::Call::from(call), + 100u32.into(), + ); + Self::deposit_event(Event::::NotifyQueryPrepared(qid)); + Ok(()) + } + + #[pallet::weight(1_000_000)] + pub fn notification_received( + origin: OriginFor, + query_id: QueryId, + response: Response, + ) -> DispatchResult { + let responder = ensure_response(::Origin::from(origin))?; + Self::deposit_event(Event::::ResponseReceived(responder, query_id, response)); + Ok(()) + } + } +} + construct_runtime!( pub enum Test where Block = Block, @@ -49,20 +125,21 @@ construct_runtime!( System: frame_system::{Pallet, Call, Storage, Config, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParasOrigin: origin::{Pallet, Origin}, - XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, + TestNotifier: pallet_test_notifier::{Pallet, Call, Event}, } ); thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); } -pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { +pub fn sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } /// Sender that never returns error, always sends pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { + fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> SendResult { SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); Ok(()) } @@ -70,9 +147,9 @@ impl SendXcm for TestSendXcm { /// Sender that returns error if `X8` junction and stops routing pub struct TestSendXcmErrX8; impl SendXcm for TestSendXcmErrX8 { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { + fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> SendResult { if dest.len() == 8 { - Err(XcmError::Undefined) + Err(SendError::Transport("Destination location full")) } else { SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); Ok(()) @@ -152,9 +229,14 @@ parameter_types! { pub const BaseXcmWeight: Weight = 1_000; pub CurrencyPerSecond: (AssetId, u128) = (Concrete(RelayLocation::get()), 1); pub TrustedAssets: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into()); + pub const MaxInstructions: u32 = 100; } -pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom); +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowKnownQueryResponses, +); pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { @@ -166,9 +248,9 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = Case; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; - type ResponseHandler = (); + type ResponseHandler = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; @@ -182,25 +264,31 @@ impl pallet_xcm::Config for Test { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; } impl origin::Config for Test {} +impl pallet_test_notifier::Config for Test { + type Event = Event; + type Origin = Origin; + type Call = Call; +} + pub(crate) fn last_event() -> Event { System::events().pop().expect("Event expected").event } -pub(crate) fn buy_execution(fees: impl Into, debt: Weight) -> Order { - use xcm::opaque::latest::prelude::*; - Order::BuyExecution { - fees: fees.into(), - weight: 0, - debt, - halt_on_error: false, - instructions: vec![], - } +pub(crate) fn last_events(n: usize) -> Vec { + System::events().into_iter().map(|e| e.event).rev().take(n).rev().collect() +} + +pub(crate) fn buy_execution(fees: impl Into) -> Instruction { + use xcm::latest::prelude::*; + BuyExecution { fees: fees.into(), weight_limit: Unlimited } } pub(crate) fn new_test_ext_with_balances( diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index a790cf7374ce..540ee153a64e 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -14,11 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::mock::*; +use crate::{mock::*, QueryStatus}; use frame_support::{assert_noop, assert_ok, traits::Currency}; use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId}; use std::convert::TryInto; -use xcm::{v1::prelude::*, VersionedXcm}; +use xcm::{latest::prelude::*, VersionedXcm}; +use xcm_executor::XcmExecutor; const ALICE: AccountId = AccountId::new([0u8; 32]); const BOB: AccountId = AccountId::new([1u8; 32]); @@ -26,6 +27,111 @@ const PARA_ID: u32 = 2000; const INITIAL_BALANCE: u128 = 100; const SEND_AMOUNT: u128 = 10; +#[test] +fn report_outcome_notify_works() { + let balances = + vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; + let sender = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + let mut message = Xcm(vec![TransferAsset { + assets: (Here, SEND_AMOUNT).into(), + beneficiary: sender.clone(), + }]); + let call = pallet_test_notifier::Call::notification_received(0, Default::default()); + let notify = Call::TestNotifier(call); + new_test_ext_with_balances(balances).execute_with(|| { + XcmPallet::report_outcome_notify(&mut message, Parachain(PARA_ID).into(), notify, 100); + assert_eq!( + message, + Xcm(vec![ + SetAppendix(Xcm(vec![ReportError { + query_id: 0, + dest: Parent.into(), + max_response_weight: 1_000_000 + },])), + TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone() }, + ]) + ); + let status = QueryStatus::Pending { + responder: MultiLocation::from(Parachain(PARA_ID)).into(), + maybe_notify: Some((4, 2)), + timeout: 100, + }; + assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); + + let r = XcmExecutor::::execute_xcm( + Parachain(PARA_ID).into(), + Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(Ok(())), + max_weight: 1_000_000, + }]), + 1_000_000_000, + ); + assert_eq!(r, Outcome::Complete(1_000)); + assert_eq!( + last_events(2), + vec![ + Event::TestNotifier(pallet_test_notifier::Event::ResponseReceived( + Parachain(PARA_ID).into(), + 0, + Response::ExecutionResult(Ok(())), + )), + Event::XcmPallet(crate::Event::Notified(0, 4, 2)), + ] + ); + assert_eq!(crate::Queries::::iter().collect::>(), vec![]); + }); +} + +#[test] +fn report_outcome_works() { + let balances = + vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; + let sender = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + let mut message = Xcm(vec![TransferAsset { + assets: (Here, SEND_AMOUNT).into(), + beneficiary: sender.clone(), + }]); + new_test_ext_with_balances(balances).execute_with(|| { + XcmPallet::report_outcome(&mut message, Parachain(PARA_ID).into(), 100); + assert_eq!( + message, + Xcm(vec![ + SetAppendix(Xcm(vec![ReportError { + query_id: 0, + dest: Parent.into(), + max_response_weight: 0 + },])), + TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone() }, + ]) + ); + let status = QueryStatus::Pending { + responder: MultiLocation::from(Parachain(PARA_ID)).into(), + maybe_notify: None, + timeout: 100, + }; + assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); + + let r = XcmExecutor::::execute_xcm( + Parachain(PARA_ID).into(), + Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(Ok(())), + max_weight: 0, + }]), + 1_000_000_000, + ); + assert_eq!(r, Outcome::Complete(1_000)); + assert_eq!( + last_event(), + Event::XcmPallet(crate::Event::ResponseReady(0, Response::ExecutionResult(Ok(())),)) + ); + + let response = Some((Response::ExecutionResult(Ok(())), 1)); + assert_eq!(XcmPallet::take_response(0), response); + }); +} + /// Test sending an `XCM` message (`XCM::ReserveAssetDeposit`) /// /// Asserts that the expected message is sent and the event is emitted @@ -34,30 +140,26 @@ fn send_works() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = 2 * BaseXcmWeight::get(); let sender: MultiLocation = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); - let message = Xcm::ReserveAssetDeposited { - assets: (Parent, SEND_AMOUNT).into(), - effects: vec![ - buy_execution((Parent, SEND_AMOUNT), weight), - DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, - ], - }; - assert_ok!(XcmPallet::send( - Origin::signed(ALICE), - Box::new(RelayLocation::get().into()), - Box::new(VersionedXcm::from(message.clone())) - )); + let message = Xcm(vec![ + ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), + ClearOrigin, + buy_execution((Parent, SEND_AMOUNT)), + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, + ]); + let versioned_dest = Box::new(RelayLocation::get().into()); + let versioned_message = Box::new(VersionedXcm::from(message.clone())); + assert_ok!(XcmPallet::send(Origin::signed(ALICE), versioned_dest, versioned_message)); assert_eq!( sent_xcm(), vec![( Here.into(), - RelayedFrom { - who: sender.clone().try_into().unwrap(), - message: Box::new(message.clone()), - } - )] + Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap())) + .into_iter() + .chain(message.0.clone().into_iter()) + .collect()) + )], ); assert_eq!( last_event(), @@ -75,16 +177,13 @@ fn send_fails_when_xcm_router_blocks() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = 2 * BaseXcmWeight::get(); let sender: MultiLocation = Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); - let message = Xcm::ReserveAssetDeposited { - assets: (Parent, SEND_AMOUNT).into(), - effects: vec![ - buy_execution((Parent, SEND_AMOUNT), weight), - DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, - ], - }; + let message = Xcm(vec![ + ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), + buy_execution((Parent, SEND_AMOUNT)), + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, + ]); assert_noop!( XcmPallet::send( Origin::signed(ALICE), @@ -113,7 +212,6 @@ fn teleport_assets_works() { Box::new(AccountId32 { network: Any, id: BOB.into() }.into().into()), Box::new((Here, SEND_AMOUNT).into()), 0, - weight, )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); assert_eq!( @@ -142,7 +240,6 @@ fn reserve_transfer_assets_works() { Box::new(dest.clone().into()), Box::new((Here, SEND_AMOUNT).into()), 0, - weight )); // Alice spent amount assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -153,13 +250,12 @@ fn reserve_transfer_assets_works() { sent_xcm(), vec![( Parachain(PARA_ID).into(), - Xcm::ReserveAssetDeposited { - assets: (Parent, SEND_AMOUNT).into(), - effects: vec![ - buy_execution((Parent, SEND_AMOUNT), weight), - DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }, - ] - } + Xcm(vec![ + ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), + ClearOrigin, + buy_execution((Parent, SEND_AMOUNT)), + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }, + ]), )] ); assert_eq!( @@ -184,13 +280,11 @@ fn execute_withdraw_to_deposit_works() { assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::execute( Origin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm::WithdrawAsset { - assets: (Here, SEND_AMOUNT).into(), - effects: vec![ - buy_execution((Here, SEND_AMOUNT), weight), - DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest } - ], - })), + Box::new(VersionedXcm::from(Xcm(vec![ + WithdrawAsset((Here, SEND_AMOUNT).into()), + buy_execution((Here, SEND_AMOUNT)), + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }, + ]))), weight )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 7e460e792f31..af53b7bddcf2 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -33,9 +33,10 @@ use parity_scale_codec::{Decode, Encode, Error as CodecError, Input}; pub mod v0; pub mod v1; +pub mod v2; pub mod latest { - pub use super::v1::*; + pub use super::v2::*; } mod double_encoded; @@ -97,6 +98,71 @@ impl TryFrom for v1::MultiLocation { } } +/// A single `Response` value, together with its version code. +#[derive(Derivative, Encode, Decode)] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] +#[codec(encode_bound())] +#[codec(decode_bound())] +pub enum VersionedResponse { + V0(v0::Response), + V1(v1::Response), + V2(v2::Response), +} + +impl From for VersionedResponse { + fn from(x: v0::Response) -> Self { + VersionedResponse::V0(x) + } +} + +impl From for VersionedResponse { + fn from(x: v1::Response) -> Self { + VersionedResponse::V1(x) + } +} + +impl> From for VersionedResponse { + fn from(x: T) -> Self { + VersionedResponse::V2(x.into()) + } +} + +impl TryFrom for v0::Response { + type Error = (); + fn try_from(x: VersionedResponse) -> Result { + use VersionedResponse::*; + match x { + V0(x) => Ok(x), + V1(x) => x.try_into(), + V2(x) => VersionedResponse::V1(x.try_into()?).try_into(), + } + } +} + +impl TryFrom for v1::Response { + type Error = (); + fn try_from(x: VersionedResponse) -> Result { + use VersionedResponse::*; + match x { + V0(x) => x.try_into(), + V1(x) => Ok(x), + V2(x) => x.try_into(), + } + } +} + +impl TryFrom for v2::Response { + type Error = (); + fn try_from(x: VersionedResponse) -> Result { + use VersionedResponse::*; + match x { + V0(x) => VersionedResponse::V1(x.try_into()?).try_into(), + V1(x) => x.try_into(), + V2(x) => Ok(x), + } + } +} + /// A single `MultiAsset` value, together with its version code. #[derive(Derivative, Encode, Decode)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] @@ -142,8 +208,6 @@ impl TryFrom for v1::MultiAsset { } /// A single `MultiAssets` value, together with its version code. -/// -/// NOTE: For XCM v0, this was `Vec`. #[derive(Derivative, Encode, Decode)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] @@ -195,6 +259,7 @@ impl TryFrom for v1::MultiAssets { pub enum VersionedXcm { V0(v0::Xcm), V1(v1::Xcm), + V2(v2::Xcm), } impl From> for VersionedXcm { @@ -209,12 +274,20 @@ impl From> for VersionedXcm { } } +impl From> for VersionedXcm { + fn from(x: v2::Xcm) -> Self { + VersionedXcm::V2(x) + } +} + impl TryFrom> for v0::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { + use VersionedXcm::*; match x { - VersionedXcm::V0(x) => Ok(x), - VersionedXcm::V1(x) => x.try_into(), + V0(x) => Ok(x), + V1(x) => x.try_into(), + V2(x) => V1(x.try_into()?).try_into(), } } } @@ -222,9 +295,23 @@ impl TryFrom> for v0::Xcm { impl TryFrom> for v1::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { + use VersionedXcm::*; + match x { + V0(x) => x.try_into(), + V1(x) => Ok(x), + V2(x) => x.try_into(), + } + } +} + +impl TryFrom> for v2::Xcm { + type Error = (); + fn try_from(x: VersionedXcm) -> Result { + use VersionedXcm::*; match x { - VersionedXcm::V0(x) => x.try_into(), - VersionedXcm::V1(x) => Ok(x), + V0(x) => V1(x.try_into()?).try_into(), + V1(x) => x.try_into(), + V2(x) => Ok(x), } } } @@ -269,6 +356,17 @@ impl WrapVersion for AlwaysV1 { } } +/// `WrapVersion` implementation which attempts to always convert the XCM to version 1 before wrapping it. +pub struct AlwaysV2; +impl WrapVersion for AlwaysV2 { + fn wrap_version( + _: &latest::MultiLocation, + xcm: impl Into>, + ) -> Result, ()> { + Ok(VersionedXcm::::V2(xcm.into().try_into()?)) + } +} + /// `WrapVersion` implementation which attempts to always convert the XCM to the latest version before wrapping it. pub type AlwaysLatest = AlwaysV1; @@ -288,9 +386,15 @@ pub mod opaque { // Then override with the opaque types in v1 pub use crate::v1::opaque::{Order, Xcm}; } + pub mod v2 { + // Everything from v1 + pub use crate::v2::*; + // Then override with the opaque types in v2 + pub use crate::v2::opaque::{Instruction, Xcm}; + } pub mod latest { - pub use super::v1::*; + pub use super::v2::*; } /// The basic `VersionedXcm` type which just uses the `Vec` as an encoded call. diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index d58147c172db..dd043554ef8b 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -295,7 +295,7 @@ impl Xcm { }, TeleportAsset { assets, effects } => TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response }, + QueryResponse { query_id, response } => QueryResponse { query_id, response }, TransferAsset { assets, dest } => TransferAsset { assets, dest }, TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { assets, dest, effects }, @@ -356,8 +356,8 @@ impl TryFrom> for Xcm { .map(Order::try_from) .collect::>()?, }, - Xcm1::QueryResponse { query_id: u64, response } => - QueryResponse { query_id: u64, response: response.try_into()? }, + Xcm1::QueryResponse { query_id, response } => + QueryResponse { query_id, response: response.try_into()? }, Xcm1::TransferAsset { assets, beneficiary } => TransferAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? }, Xcm1::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index f06472498e2d..dc0f6a251a98 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -16,7 +16,10 @@ //! Version 1 of the Cross-Consensus Message format data structures. -use super::v0::{Response as Response0, Xcm as Xcm0}; +use super::{ + v0::{Response as OldResponse, Xcm as OldXcm}, + v2::{Instruction, Response as NewResponse, Xcm as NewXcm}, +}; use crate::DoubleEncoded; use alloc::vec::Vec; use core::{ @@ -28,7 +31,7 @@ use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; mod junction; -pub mod multiasset; +mod multiasset; mod multilocation; mod order; mod traits; // the new multiasset. @@ -38,7 +41,9 @@ pub use multiasset::{ AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, WildFungibility, WildMultiAsset, }; -pub use multilocation::{Ancestor, AncestorThen, Junctions, MultiLocation, Parent, ParentThen}; +pub use multilocation::{ + Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen, +}; pub use order::Order; pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm}; @@ -48,30 +53,23 @@ pub use super::v0::{BodyId, BodyPart, NetworkId, OriginKind}; /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { pub use super::{ - super::v0::{ - BodyId, BodyPart, - NetworkId::{self, *}, - }, junction::Junction::{self, *}, - multiasset::{ - AssetId::{self, *}, - AssetInstance::{self, *}, - Fungibility::{self, *}, - MultiAsset, - MultiAssetFilter::{self, *}, - MultiAssets, - WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, - WildMultiAsset::{self, *}, - }, - multilocation::{ - Ancestor, AncestorThen, - Junctions::{self, *}, - MultiLocation, Parent, ParentThen, - }, opaque, order::Order::{self, *}, - traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm}, - OriginKind, Response, + Ancestor, AncestorThen, + AssetId::{self, *}, + AssetInstance::{self, *}, + BodyId, BodyPart, Error as XcmError, ExecuteXcm, + Fungibility::{self, *}, + InteriorMultiLocation, + Junctions::{self, *}, + MultiAsset, + MultiAssetFilter::{self, *}, + MultiAssets, MultiLocation, + NetworkId::{self, *}, + OriginKind, Outcome, Parent, ParentThen, Response, Result as XcmResult, SendXcm, + WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, + WildMultiAsset::{self, *}, Xcm::{self, *}, }; } @@ -271,7 +269,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 10)] - RelayedFrom { who: Junctions, message: alloc::boxed::Box> }, + RelayedFrom { who: InteriorMultiLocation, message: alloc::boxed::Box> }, } impl Xcm { @@ -291,7 +289,7 @@ impl Xcm { assets, effects: effects.into_iter().map(Order::into).collect(), }, - QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response }, + QueryResponse { query_id, response } => QueryResponse { query_id, response }, TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary }, TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { assets, dest, effects }, @@ -317,46 +315,46 @@ pub mod opaque { } // Convert from a v0 response to a v1 response -impl TryFrom for Response { +impl TryFrom for Response { type Error = (); - fn try_from(old_response: Response0) -> result::Result { + fn try_from(old_response: OldResponse) -> result::Result { match old_response { - Response0::Assets(assets) => Ok(Self::Assets(assets.try_into()?)), + OldResponse::Assets(assets) => Ok(Self::Assets(assets.try_into()?)), } } } -impl TryFrom> for Xcm { +impl TryFrom> for Xcm { type Error = (); - fn try_from(old: Xcm0) -> result::Result, ()> { + fn try_from(old: OldXcm) -> result::Result, ()> { use Xcm::*; Ok(match old { - Xcm0::WithdrawAsset { assets, effects } => WithdrawAsset { + OldXcm::WithdrawAsset { assets, effects } => WithdrawAsset { assets: assets.try_into()?, effects: effects .into_iter() .map(Order::try_from) .collect::>()?, }, - Xcm0::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited { + OldXcm::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited { assets: assets.try_into()?, effects: effects .into_iter() .map(Order::try_from) .collect::>()?, }, - Xcm0::TeleportAsset { assets, effects } => ReceiveTeleportedAsset { + OldXcm::TeleportAsset { assets, effects } => ReceiveTeleportedAsset { assets: assets.try_into()?, effects: effects .into_iter() .map(Order::try_from) .collect::>()?, }, - Xcm0::QueryResponse { query_id: u64, response } => - QueryResponse { query_id: u64, response: response.try_into()? }, - Xcm0::TransferAsset { assets, dest } => + OldXcm::QueryResponse { query_id, response } => + QueryResponse { query_id, response: response.try_into()? }, + OldXcm::TransferAsset { assets, dest } => TransferAsset { assets: assets.try_into()?, beneficiary: dest.try_into()? }, - Xcm0::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { + OldXcm::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { assets: assets.try_into()?, dest: dest.try_into()?, effects: effects @@ -364,17 +362,83 @@ impl TryFrom> for Xcm { .map(Order::try_from) .collect::>()?, }, - Xcm0::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - Xcm0::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, - Xcm0::HrmpChannelClosing { initiator, sender, recipient } => + OldXcm::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, + OldXcm::HrmpChannelClosing { initiator, sender, recipient } => HrmpChannelClosing { initiator, sender, recipient }, - Xcm0::Transact { origin_type, require_weight_at_most, call } => + OldXcm::Transact { origin_type, require_weight_at_most, call } => Transact { origin_type, require_weight_at_most, call: call.into() }, - Xcm0::RelayedFrom { who, message } => RelayedFrom { + OldXcm::RelayedFrom { who, message } => RelayedFrom { who: MultiLocation::try_from(who)?.try_into()?, message: alloc::boxed::Box::new((*message).try_into()?), }, }) } } + +impl TryFrom> for Xcm { + type Error = (); + fn try_from(old: NewXcm) -> result::Result, ()> { + use Xcm::*; + let mut iter = old.0.into_iter(); + let instruction = iter.next().ok_or(())?; + Ok(match instruction { + Instruction::WithdrawAsset(assets) => { + let effects = iter.map(Order::try_from).collect::>()?; + WithdrawAsset { assets, effects } + }, + Instruction::ReserveAssetDeposited(assets) => { + if !matches!(iter.next(), Some(Instruction::ClearOrigin)) { + return Err(()) + } + let effects = iter.map(Order::try_from).collect::>()?; + ReserveAssetDeposited { assets, effects } + }, + Instruction::ReceiveTeleportedAsset(assets) => { + if !matches!(iter.next(), Some(Instruction::ClearOrigin)) { + return Err(()) + } + let effects = iter.map(Order::try_from).collect::>()?; + ReceiveTeleportedAsset { assets, effects } + }, + Instruction::QueryResponse { query_id, response, max_weight } => { + // Cannot handle special response weights. + if max_weight > 0 { + return Err(()) + } + QueryResponse { query_id, response: response.try_into()? } + }, + Instruction::TransferAsset { assets, beneficiary } => + TransferAsset { assets, beneficiary }, + Instruction::TransferReserveAsset { assets, dest, xcm } => TransferReserveAsset { + assets, + dest, + effects: xcm + .0 + .into_iter() + .map(Order::try_from) + .collect::>()?, + }, + Instruction::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + Instruction::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, + Instruction::HrmpChannelClosing { initiator, sender, recipient } => + HrmpChannelClosing { initiator, sender, recipient }, + Instruction::Transact { origin_type, require_weight_at_most, call } => + Transact { origin_type, require_weight_at_most, call }, + _ => return Err(()), + }) + } +} + +// Convert from a v1 response to a v2 response +impl TryFrom for Response { + type Error = (); + fn try_from(response: NewResponse) -> result::Result { + match response { + NewResponse::Assets(assets) => Ok(Self::Assets(assets)), + _ => Err(()), + } + } +} diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index 87c963187672..f2c255bd1125 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -22,26 +22,30 @@ use parity_scale_codec::{Decode, Encode}; /// A relative path between state-bearing consensus systems. /// -/// A location in a consensus system is defined as an *isolatable state machine* held within global consensus. The -/// location in question need not have a sophisticated consensus algorithm of its own; a single account within -/// Ethereum, for example, could be considered a location. +/// A location in a consensus system is defined as an *isolatable state machine* held within global +/// consensus. The location in question need not have a sophisticated consensus algorithm of its +/// own; a single account within Ethereum, for example, could be considered a location. /// /// A very-much non-exhaustive list of types of location include: /// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain. /// - A layer-0 super-chain, e.g. the Polkadot Relay chain. /// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum. -/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based Substrate chain. +/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based +/// Substrate chain. /// - An account. /// -/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the relative path -/// between two locations, and cannot generally be used to refer to a location universally. It is comprised of a -/// number of *junctions*, each morphing the previous location, either diving down into one of its internal locations, -/// called a *sub-consensus*, or going up into its parent location. +/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the +/// relative path between two locations, and cannot generally be used to refer to a location +/// universally. It is comprised of an integer number of parents specifying the number of times to +/// "escape" upwards into the containing consensus system and then a number of *junctions*, each +/// diving down and specifying some interior portion of state (which may be considered a +/// "sub-consensus" system). /// -/// The `parents` field of this struct indicates the number of parent junctions that exist at the -/// beginning of this `MultiLocation`. A corollary of such a property is that no parent junctions -/// can be added in the middle or at the end of a `MultiLocation`, thus ensuring well-formedness -/// of each and every `MultiLocation` that can be constructed. +/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum` +/// in order to make pattern matching easier. There are occasions where it is important to ensure +/// that a value is strictly an interior location, in those cases, `Junctions` may be used. +/// +/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system. #[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct MultiLocation { /// The number of parent junctions at the beginning of this `MultiLocation`. @@ -50,6 +54,11 @@ pub struct MultiLocation { pub interior: Junctions, } +/// A relative location which is constrained to be an interior location of the context. +/// +/// See also `MultiLocation`. +pub type InteriorMultiLocation = Junctions; + impl Default for MultiLocation { fn default() -> Self { Self::here() @@ -724,7 +733,7 @@ impl Junctions { /// /// # Example /// ```rust - /// # use xcm::latest::{Junctions::*, Junction::*}; + /// # use xcm::v1::{Junctions::*, Junction::*}; /// # fn main() { /// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild); /// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild)); diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index 8911c7f5ac13..9b8a7591f183 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -16,10 +16,9 @@ //! Version 1 of the Cross-Consensus Message format data structures. -use super::{ - super::v0::Order as Order0, MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm, -}; -use alloc::vec::Vec; +use super::{MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm}; +use crate::{v0::Order as OldOrder, v2::Instruction}; +use alloc::{vec, vec::Vec}; use core::{ convert::{TryFrom, TryInto}, result, @@ -184,18 +183,18 @@ impl Order { } } -impl TryFrom> for Order { +impl TryFrom> for Order { type Error = (); - fn try_from(old: Order0) -> result::Result, ()> { + fn try_from(old: OldOrder) -> result::Result, ()> { use Order::*; Ok(match old { - Order0::Null => Noop, - Order0::DepositAsset { assets, dest } => DepositAsset { + OldOrder::Null => Noop, + OldOrder::DepositAsset { assets, dest } => DepositAsset { assets: assets.try_into()?, max_assets: 1, beneficiary: dest.try_into()?, }, - Order0::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset { + OldOrder::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset { assets: assets.try_into()?, max_assets: 1, dest: dest.try_into()?, @@ -204,9 +203,9 @@ impl TryFrom> for Order { .map(Order::<()>::try_from) .collect::>()?, }, - Order0::ExchangeAsset { give, receive } => + OldOrder::ExchangeAsset { give, receive } => ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? }, - Order0::InitiateReserveWithdraw { assets, reserve, effects } => + OldOrder::InitiateReserveWithdraw { assets, reserve, effects } => InitiateReserveWithdraw { assets: assets.try_into()?, reserve: reserve.try_into()?, @@ -215,7 +214,7 @@ impl TryFrom> for Order { .map(Order::<()>::try_from) .collect::>()?, }, - Order0::InitiateTeleport { assets, dest, effects } => InitiateTeleport { + OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport { assets: assets.try_into()?, dest: dest.try_into()?, effects: effects @@ -223,9 +222,9 @@ impl TryFrom> for Order { .map(Order::<()>::try_from) .collect::>()?, }, - Order0::QueryHolding { query_id, dest, assets } => + OldOrder::QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? }, - Order0::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { + OldOrder::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { let instructions = xcm.into_iter().map(Xcm::::try_from).collect::>()?; BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, instructions } @@ -233,3 +232,60 @@ impl TryFrom> for Order { }) } } + +impl TryFrom> for Order { + type Error = (); + fn try_from(old: Instruction) -> result::Result, ()> { + use Order::*; + Ok(match old { + Instruction::DepositAsset { assets, max_assets, beneficiary } => + DepositAsset { assets, max_assets, beneficiary }, + Instruction::DepositReserveAsset { assets, max_assets, dest, xcm } => + DepositReserveAsset { + assets, + max_assets, + dest, + effects: xcm + .0 + .into_iter() + .map(Order::<()>::try_from) + .collect::>()?, + }, + Instruction::ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, + Instruction::InitiateReserveWithdraw { assets, reserve, xcm } => + InitiateReserveWithdraw { + assets, + reserve, + effects: xcm + .0 + .into_iter() + .map(Order::<()>::try_from) + .collect::>()?, + }, + Instruction::InitiateTeleport { assets, dest, xcm } => InitiateTeleport { + assets, + dest, + effects: xcm + .0 + .into_iter() + .map(Order::<()>::try_from) + .collect::>()?, + }, + Instruction::QueryHolding { query_id, dest, assets, max_response_weight } => { + // Cannot handle special response weights. + if max_response_weight > 0 { + return Err(()) + } + QueryHolding { query_id, dest, assets } + }, + Instruction::BuyExecution { fees, weight_limit } => { + let instructions = vec![]; + let halt_on_error = true; + let weight = 0; + let debt = Option::::from(weight_limit).ok_or(())?; + BuyExecution { fees, weight, debt, halt_on_error, instructions } + }, + _ => return Err(()), + }) + } +} diff --git a/xcm/src/v2/mod.rs b/xcm/src/v2/mod.rs new file mode 100644 index 000000000000..42d0283652d8 --- /dev/null +++ b/xcm/src/v2/mod.rs @@ -0,0 +1,761 @@ +// Copyright 2020 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 . + +//! Version 1 of the Cross-Consensus Message format data structures. + +use super::v1::{Order as OldOrder, Response as OldResponse, Xcm as OldXcm}; +use crate::DoubleEncoded; +use alloc::{vec, vec::Vec}; +use core::{ + convert::{TryFrom, TryInto}, + fmt::Debug, + result, +}; +use derivative::Derivative; +use parity_scale_codec::{self, Decode, Encode}; + +mod traits; + +pub use traits::{Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm}; +// These parts of XCM v1 have been unchanged in XCM v2, and are re-imported here. +pub use super::v1::{ + Ancestor, AncestorThen, AssetId, AssetInstance, BodyId, BodyPart, Fungibility, + InteriorMultiLocation, Junction, Junctions, MultiAsset, MultiAssetFilter, MultiAssets, + MultiLocation, NetworkId, OriginKind, Parent, ParentThen, WildFungibility, WildMultiAsset, +}; + +#[derive(Derivative, Default, Encode, Decode)] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] +#[codec(encode_bound())] +#[codec(decode_bound())] +pub struct Xcm(pub Vec>); + +impl Xcm { + /// Create an empty instance. + pub fn new() -> Self { + Self(vec![]) + } + + /// Return `true` if no instructions are held in `self`. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Return the number of instructions held in `self`. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Consume and either return `self` if it contains some instructions, or if it's empty, then + /// instead return the result of `f`. + pub fn or_else(self, f: impl FnOnce() -> Self) -> Self { + if self.0.is_empty() { + f() + } else { + self + } + } + + /// Return the first instruction, if any. + pub fn first(&self) -> Option<&Instruction> { + self.0.first() + } + + /// Return the last instruction, if any. + pub fn last(&self) -> Option<&Instruction> { + self.0.last() + } + + /// Return the only instruction, contained in `Self`, iff only one exists (`None` otherwise). + pub fn only(&self) -> Option<&Instruction> { + if self.0.len() == 1 { + self.0.first() + } else { + None + } + } + + /// Return the only instruction, contained in `Self`, iff only one exists (returns `self` + /// otherwise). + pub fn into_only(mut self) -> core::result::Result, Self> { + if self.0.len() == 1 { + self.0.pop().ok_or(self) + } else { + Err(self) + } + } +} + +/// A prelude for importing all types typically used when interacting with XCM messages. +pub mod prelude { + mod contents { + pub use super::super::{ + Ancestor, AncestorThen, + AssetId::{self, *}, + AssetInstance::{self, *}, + BodyId, BodyPart, Error as XcmError, ExecuteXcm, + Fungibility::{self, *}, + Instruction::*, + InteriorMultiLocation, + Junction::{self, *}, + Junctions::{self, *}, + MultiAsset, + MultiAssetFilter::{self, *}, + MultiAssets, MultiLocation, + NetworkId::{self, *}, + OriginKind, Outcome, Parent, ParentThen, Response, Result as XcmResult, SendError, + SendResult, SendXcm, + WeightLimit::{self, *}, + WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, + WildMultiAsset::{self, *}, + }; + } + pub use super::{Instruction, Xcm}; + pub use contents::*; + pub mod opaque { + pub use super::{ + super::opaque::{Instruction, Xcm}, + contents::*, + }; + } +} + +/// Response data to a query. +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] +pub enum Response { + /// No response. Serves as a neutral default. + Null, + /// Some assets. + Assets(MultiAssets), + /// The outcome of an XCM instruction. + ExecutionResult(result::Result<(), (u32, Error)>), +} + +impl Default for Response { + fn default() -> Self { + Self::Null + } +} + +/// An optional weight limit. +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] +pub enum WeightLimit { + /// No weight limit imposed. + Unlimited, + /// Weight limit imposed of the inner value. + Limited(#[codec(compact)] u64), +} + +impl From> for WeightLimit { + fn from(x: Option) -> Self { + match x { + Some(w) => WeightLimit::Limited(w), + None => WeightLimit::Unlimited, + } + } +} + +impl From for Option { + fn from(x: WeightLimit) -> Self { + match x { + WeightLimit::Limited(w) => Some(w), + WeightLimit::Unlimited => None, + } + } +} + +/// Cross-Consensus Message: A message from one consensus system to another. +/// +/// Consensus systems that may send and receive messages include blockchains and smart contracts. +/// +/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`. +/// +/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer +/// XCM format, known as `VersionedXcm`. +#[derive(Derivative, Encode, Decode)] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] +#[codec(encode_bound())] +#[codec(decode_bound())] +pub enum Instruction { + /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into the Holding + /// Register. + /// + /// - `assets`: The asset(s) to be withdrawn into holding. + /// + /// Kind: *Instruction*. + /// + /// Errors: + WithdrawAsset(MultiAssets), + + /// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` + /// system and equivalent derivatives should be placed into the Holding Register. + /// + /// - `assets`: The asset(s) that are minted into holding. + /// + /// Safety: `origin` must be trusted to have received and be storing `assets` such that they + /// may later be withdrawn should this system send a corresponding message. + /// + /// Kind: *Trusted Indication*. + /// + /// Errors: + ReserveAssetDeposited(MultiAssets), + + /// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should + /// be created and placed into the Holding Register. + /// + /// - `assets`: The asset(s) that are minted into the Holding Register. + /// + /// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` + /// prior as a consequence of sending this message. + /// + /// Kind: *Trusted Indication*. + /// + /// Errors: + ReceiveTeleportedAsset(MultiAssets), + + /// Respond with information that the local system is expecting. + /// + /// - `query_id`: The identifier of the query that resulted in this message being sent. + /// - `response`: The message content. + /// - `max_weight`: The maximum weight that handling this response should take. + /// + /// Safety: No concerns. + /// + /// Kind: *Information*. + /// + /// Errors: + QueryResponse { + #[codec(compact)] + query_id: u64, + response: Response, + #[codec(compact)] + max_weight: u64, + }, + + /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets + /// under the ownership of `beneficiary`. + /// + /// - `assets`: The asset(s) to be withdrawn. + /// - `beneficiary`: The new owner for the assets. + /// + /// Safety: No concerns. + /// + /// Kind: *Instruction*. + /// + /// Errors: + TransferAsset { assets: MultiAssets, beneficiary: MultiLocation }, + + /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets + /// under the ownership of `dest` within this consensus system (i.e. its sovereign account). + /// + /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given + /// `xcm`. + /// + /// - `assets`: The asset(s) to be withdrawn. + /// - `dest`: The location whose sovereign account will own the assets and thus the effective + /// beneficiary for the assets and the notification target for the reserve asset deposit + /// message. + /// - `xcm`: The instructions that should follow the `ReserveAssetDeposited` + /// instruction, which is sent onwards to `dest`. + /// + /// Safety: No concerns. + /// + /// Kind: *Instruction*. + /// + /// Errors: + TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, xcm: Xcm<()> }, + + /// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed + /// by the kind of origin `origin_type`. + /// + /// - `origin_type`: The means of expressing the message origin as a dispatch origin. + /// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight + /// and will be used in the weight determination arithmetic. + /// - `call`: The encoded transaction to be applied. + /// + /// Safety: No concerns. + /// + /// Kind: *Instruction*. + /// + /// Errors: + Transact { origin_type: OriginKind, require_weight_at_most: u64, call: DoubleEncoded }, + + /// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the + /// relay-chain to a para. + /// + /// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening. + /// - `max_message_size`: The maximum size of a message proposed by the sender. + /// - `max_capacity`: The maximum number of messages that can be queued in the channel. + /// + /// Safety: The message should originate directly from the relay-chain. + /// + /// Kind: *System Notification* + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: u32, + #[codec(compact)] + max_message_size: u32, + #[codec(compact)] + max_capacity: u32, + }, + + /// A message to notify about that a previously sent open channel request has been accepted by + /// the recipient. That means that the channel will be opened during the next relay-chain session + /// change. This message is meant to be sent by the relay-chain to a para. + /// + /// Safety: The message should originate directly from the relay-chain. + /// + /// Kind: *System Notification* + /// + /// Errors: + HrmpChannelAccepted { + // NOTE: We keep this as a structured item to a) keep it consistent with the other Hrmp + // items; and b) because the field's meaning is not obvious/mentioned from the item name. + #[codec(compact)] + recipient: u32, + }, + + /// A message to notify that the other party in an open channel decided to close it. In particular, + /// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close + /// will be enacted at the next relay-chain session change. This message is meant to be sent by + /// the relay-chain to a para. + /// + /// Safety: The message should originate directly from the relay-chain. + /// + /// Kind: *System Notification* + /// + /// Errors: + HrmpChannelClosing { + #[codec(compact)] + initiator: u32, + #[codec(compact)] + sender: u32, + #[codec(compact)] + recipient: u32, + }, + + /// Clear the origin. + /// + /// This may be used by the XCM author to ensure that later instructions cannot command the + /// authority of the origin (e.g. if they are being relayed from an untrusted source, as often + /// the case with `ReserveAssetDeposited`). + /// + /// Safety: No concerns. + /// + /// Kind: *Instruction*. + /// + /// Errors: + ClearOrigin, + + /// Mutate the origin to some interior location. + /// + /// Kind: *Instruction* + /// + /// Errors: + DescendOrigin(InteriorMultiLocation), + + /// Immediately report the contents of the Error Register to the given destination via XCM. + /// + /// A `QueryResponse` message of type `ExecutionOutcome` is sent to `dest` with the given + /// `query_id` and the outcome of the XCM. + /// + /// Kind: *Instruction* + /// + /// Errors: + ReportError { + #[codec(compact)] + query_id: u64, + dest: MultiLocation, + #[codec(compact)] + max_response_weight: u64, + }, + + /// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under + /// the ownership of `beneficiary` within this consensus system. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. + /// Only the first `max_assets` assets/instances of those matched by `assets` will be removed, + /// prioritized under standard asset ordering. Any others will remain in holding. + /// - `beneficiary`: The new owner for the assets. + /// + /// Errors: + DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation }, + + /// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under + /// the ownership of `dest` within this consensus system (i.e. deposit them into its sovereign + /// account). + /// + /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. + /// Only the first `max_assets` assets/instances of those matched by `assets` will be removed, + /// prioritized under standard asset ordering. Any others will remain in holding. + /// - `dest`: The location whose sovereign account will own the assets and thus the effective + /// beneficiary for the assets and the notification target for the reserve asset deposit + /// message. + /// - `xcm`: The orders that should follow the `ReserveAssetDeposited` instruction + /// which is sent onwards to `dest`. + /// + /// Errors: + DepositReserveAsset { + assets: MultiAssetFilter, + max_assets: u32, + dest: MultiLocation, + xcm: Xcm<()>, + }, + + /// Remove the asset(s) (`give`) from the Holding Register and replace them with alternative + /// assets. + /// + /// The minimum amount of assets to be received into the Holding Register for the order not to + /// fail may be stated. + /// + /// - `give`: The asset(s) to remove from holding. + /// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. + /// + /// Errors: + ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets }, + + /// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a + /// reserve location. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The + /// sovereign account of this consensus system *on the reserve location* will have appropriate + /// assets withdrawn and `effects` will be executed on them. There will typically be only one + /// valid location on any given asset/chain combination. + /// - `xcm`: The instructions to execute on the assets once withdrawn *on the reserve + /// location*. + /// + /// Errors: + InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, xcm: Xcm<()> }, + + /// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message + /// to a `dest` location. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `dest`: A valid location that respects teleports coming from this location. + /// - `xcm`: The instructions to execute on the assets once arrived *on the destination + /// location*. + /// + /// NOTE: The `dest` location *MUST* respect this origin as a valid teleportation origin for all + /// `assets`. If it does not, then the assets may be lost. + /// + /// Errors: + InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, xcm: Xcm<()> }, + + /// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a + /// portion thereof. + /// + /// - `query_id`: An identifier that will be replicated into the returned XCM message. + /// - `dest`: A valid destination for the returned XCM message. This may be limited to the + /// current origin. + /// - `assets`: A filter for the assets that should be reported back. The assets reported back + /// will be, asset-wise, *the lesser of this value and the holding register*. No wildcards + /// will be used when reporting assets back. + /// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which + /// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the + /// response may not execute at all. + /// + /// Errors: + QueryHolding { + #[codec(compact)] + query_id: u64, + dest: MultiLocation, + assets: MultiAssetFilter, + #[codec(compact)] + max_response_weight: u64, + }, + + /// Pay for the execution of some XCM `xcm` and `orders` with up to `weight` + /// picoseconds of execution time, paying for this with up to `fees` from the Holding Register. + /// + /// - `fees`: The asset(s) to remove from the Holding Register to pay for fees. + /// - `weight_limit`: The maximum amount of weight to purchase; this must be at least the + /// expected maximum weight of the total XCM to be executed for the + /// `AllowTopLevelPaidExecutionFrom` barrier to allow the XCM be executed. + /// + /// Errors: + BuyExecution { fees: MultiAsset, weight_limit: WeightLimit }, + + /// Refund any surplus weight previously bought with `BuyExecution`. + RefundSurplus, + + /// Set code that should be called in the case of an error happening. + /// + /// An error occurring within execution of this code will _NOT_ result in the error register + /// being set, nor will an error handler be called due to it. The error handler and appendix + /// may each still be set. + /// + /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing + /// weight however includes only the difference between the previous handler and the new + /// handler, which can reasonably be negative, which would result in a surplus. + SetErrorHandler(Xcm), + + /// Set code that should be called after code execution (including the error handler if any) + /// is finished. This will be called regardless of whether an error occurred. + /// + /// Any error occurring due to execution of this code will result in the error register being + /// set, and the error handler (if set) firing. + /// + /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing + /// weight however includes only the difference between the previous appendix and the new + /// appendix, which can reasonably be negative, which would result in a surplus. + SetAppendix(Xcm), + + /// Clear the error register. + ClearError, +} + +impl Xcm { + pub fn into(self) -> Xcm { + Xcm::from(self) + } + pub fn from(xcm: Xcm) -> Self { + Self(xcm.0.into_iter().map(Instruction::::from).collect()) + } +} + +impl Instruction { + pub fn into(self) -> Instruction { + Instruction::from(self) + } + pub fn from(xcm: Instruction) -> Self { + use Instruction::*; + match xcm { + WithdrawAsset(assets) => WithdrawAsset(assets), + ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets), + ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets), + QueryResponse { query_id, response, max_weight } => + QueryResponse { query_id, response, max_weight }, + TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary }, + TransferReserveAsset { assets, dest, xcm } => + TransferReserveAsset { assets, dest, xcm }, + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, + HrmpChannelClosing { initiator, sender, recipient } => + HrmpChannelClosing { initiator, sender, recipient }, + Transact { origin_type, require_weight_at_most, call } => + Transact { origin_type, require_weight_at_most, call: call.into() }, + ReportError { query_id, dest, max_response_weight } => + ReportError { query_id, dest, max_response_weight }, + DepositAsset { assets, max_assets, beneficiary } => + DepositAsset { assets, max_assets, beneficiary }, + DepositReserveAsset { assets, max_assets, dest, xcm } => + DepositReserveAsset { assets, max_assets, dest, xcm }, + ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, + InitiateReserveWithdraw { assets, reserve, xcm } => + InitiateReserveWithdraw { assets, reserve, xcm }, + InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm }, + QueryHolding { query_id, dest, assets, max_response_weight } => + QueryHolding { query_id, dest, assets, max_response_weight }, + BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit }, + ClearOrigin => ClearOrigin, + DescendOrigin(who) => DescendOrigin(who), + RefundSurplus => RefundSurplus, + SetErrorHandler(xcm) => SetErrorHandler(xcm.into()), + SetAppendix(xcm) => SetAppendix(xcm.into()), + ClearError => ClearError, + } + } +} + +pub mod opaque { + /// The basic concrete type of `Xcm`, which doesn't make any assumptions about the + /// format of a call other than it is pre-encoded. + pub type Xcm = super::Xcm<()>; + + /// The basic concrete type of `Instruction`, which doesn't make any assumptions about the + /// format of a call other than it is pre-encoded. + pub type Instruction = super::Instruction<()>; +} + +// Convert from a v1 response to a v2 response +impl TryFrom for Response { + type Error = (); + fn try_from(old_response: OldResponse) -> result::Result { + match old_response { + OldResponse::Assets(assets) => Ok(Self::Assets(assets)), + } + } +} + +impl TryFrom> for Xcm { + type Error = (); + fn try_from(old: OldXcm) -> result::Result, ()> { + use Instruction::*; + Ok(Xcm(match old { + OldXcm::WithdrawAsset { assets, effects } => Some(Ok(WithdrawAsset(assets))) + .into_iter() + .chain(effects.into_iter().map(Instruction::try_from)) + .collect::, _>>()?, + OldXcm::ReserveAssetDeposited { assets, effects } => + Some(Ok(ReserveAssetDeposited(assets))) + .into_iter() + .chain(Some(Ok(ClearOrigin)).into_iter()) + .chain(effects.into_iter().map(Instruction::try_from)) + .collect::, _>>()?, + OldXcm::ReceiveTeleportedAsset { assets, effects } => + Some(Ok(ReceiveTeleportedAsset(assets))) + .into_iter() + .chain(Some(Ok(ClearOrigin)).into_iter()) + .chain(effects.into_iter().map(Instruction::try_from)) + .collect::, _>>()?, + OldXcm::QueryResponse { query_id, response } => vec![QueryResponse { + query_id, + response: response.try_into()?, + max_weight: 50_000_000, + }], + OldXcm::TransferAsset { assets, beneficiary } => + vec![TransferAsset { assets, beneficiary }], + OldXcm::TransferReserveAsset { assets, dest, effects } => vec![TransferReserveAsset { + assets, + dest, + xcm: Xcm(effects + .into_iter() + .map(Instruction::<()>::try_from) + .collect::>()?), + }], + OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + vec![HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }], + OldXcm::HrmpChannelAccepted { recipient } => vec![HrmpChannelAccepted { recipient }], + OldXcm::HrmpChannelClosing { initiator, sender, recipient } => + vec![HrmpChannelClosing { initiator, sender, recipient }], + OldXcm::Transact { origin_type, require_weight_at_most, call } => + vec![Transact { origin_type, require_weight_at_most, call }], + // We don't handle this one at all due to nested XCM. + OldXcm::RelayedFrom { .. } => return Err(()), + })) + } +} + +impl TryFrom> for Instruction { + type Error = (); + fn try_from(old: OldOrder) -> result::Result, ()> { + use Instruction::*; + Ok(match old { + OldOrder::Noop => return Err(()), + OldOrder::DepositAsset { assets, max_assets, beneficiary } => + DepositAsset { assets, max_assets, beneficiary }, + OldOrder::DepositReserveAsset { assets, max_assets, dest, effects } => + DepositReserveAsset { + assets, + max_assets, + dest, + xcm: Xcm(effects + .into_iter() + .map(Instruction::<()>::try_from) + .collect::>()?), + }, + OldOrder::ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, + OldOrder::InitiateReserveWithdraw { assets, reserve, effects } => + InitiateReserveWithdraw { + assets, + reserve, + xcm: Xcm(effects + .into_iter() + .map(Instruction::<()>::try_from) + .collect::>()?), + }, + OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport { + assets, + dest, + xcm: Xcm(effects + .into_iter() + .map(Instruction::<()>::try_from) + .collect::>()?), + }, + OldOrder::QueryHolding { query_id, dest, assets } => + QueryHolding { query_id, dest, assets, max_response_weight: 0 }, + OldOrder::BuyExecution { fees, debt, instructions, .. } => { + // We don't handle nested XCM. + if !instructions.is_empty() { + return Err(()) + } + BuyExecution { fees, weight_limit: WeightLimit::Limited(debt) } + }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::{prelude::*, *}; + + #[test] + fn basic_roundtrip_works() { + let xcm = + Xcm::<()>(vec![TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() }]); + let old_xcm = + OldXcm::<()>::TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() }; + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn teleport_roundtrip_works() { + let xcm = Xcm::<()>(vec![ + ReceiveTeleportedAsset((Here, 1).into()), + ClearOrigin, + DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Here.into() }, + ]); + let old_xcm: OldXcm<()> = OldXcm::<()>::ReceiveTeleportedAsset { + assets: (Here, 1).into(), + effects: vec![OldOrder::DepositAsset { + assets: Wild(All), + max_assets: 1, + beneficiary: Here.into(), + }], + }; + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn reserve_deposit_roundtrip_works() { + let xcm = Xcm::<()>(vec![ + ReserveAssetDeposited((Here, 1).into()), + ClearOrigin, + BuyExecution { fees: (Here, 1).into(), weight_limit: Some(1).into() }, + DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Here.into() }, + ]); + let old_xcm: OldXcm<()> = OldXcm::<()>::ReserveAssetDeposited { + assets: (Here, 1).into(), + effects: vec![ + OldOrder::BuyExecution { + fees: (Here, 1).into(), + debt: 1, + weight: 0, + instructions: vec![], + halt_on_error: true, + }, + OldOrder::DepositAsset { + assets: Wild(All), + max_assets: 1, + beneficiary: Here.into(), + }, + ], + }; + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } +} diff --git a/xcm/src/v2/traits.rs b/xcm/src/v2/traits.rs new file mode 100644 index 000000000000..facbdd089263 --- /dev/null +++ b/xcm/src/v2/traits.rs @@ -0,0 +1,308 @@ +// Copyright 2020 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 . + +//! Cross-Consensus Message format data structures. + +use core::result; +use parity_scale_codec::{Decode, Encode}; + +use super::{MultiLocation, Xcm}; + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug)] +pub enum Error { + Undefined, + /// An arithmetic overflow happened. + Overflow, + /// The operation is intentionally unsupported. + Unimplemented, + UnhandledXcmVersion, + /// The implementation does not handle a given XCM. + UnhandledXcmMessage, + /// The implementation does not handle an effect present in an XCM. + UnhandledEffect, + EscalationOfPrivilege, + UntrustedReserveLocation, + UntrustedTeleportLocation, + DestinationBufferOverflow, + MultiLocationFull, + FailedToDecode, + BadOrigin, + ExceedsMaxMessageSize, + /// An asset transaction (like withdraw or deposit) failed. + /// See implementers of the `TransactAsset` trait for sources. + /// Causes can include type conversion failures between id or balance types. + FailedToTransactAsset(#[codec(skip)] &'static str), + /// Execution of the XCM would potentially result in a greater weight used than the pre-specified + /// weight limit. The amount that is potentially required is the parameter. + WeightLimitReached(Weight), + /// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a + /// `WithdrawAsset` XCM). + Wildcard, + /// The case where an XCM message has specified a optional weight limit and the weight required for + /// processing is too great. + /// + /// Used by: + /// - `Transact` + TooMuchWeightRequired, + /// The fees specified by the XCM message were not found in the holding register. + /// + /// Used by: + /// - `BuyExecution` + NotHoldingFees, + /// The weight of an XCM message is not computable ahead of execution. This generally means at least part + /// of the message is invalid, which could be due to it containing overly nested structures or an invalid + /// nested data segment (e.g. for the call in `Transact`). + WeightNotComputable, + /// The XCM did not pass the barrier condition for execution. The barrier condition differs on different + /// chains and in different circumstances, but generally it means that the conditions surrounding the message + /// were not such that the chain considers the message worth spending time executing. Since most chains + /// lift the barrier to execution on appropriate payment, presentation of an NFT voucher, or based on the + /// message origin, it means that none of those were the case. + Barrier, + /// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its + /// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a + /// lock, hold, freeze or is otherwise unavailable. + NotWithdrawable, + /// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location. + LocationCannotHold, + /// The assets given to purchase weight is are insufficient for the weight desired. + TooExpensive, + /// The given asset is not handled. + AssetNotFound, + /// The given message cannot be translated into a format that the destination can be expected to interpret. + DestinationUnsupported, + /// `execute_xcm` has been called too many times recursively. + RecursionLimitReached, + /// Destination is routable, but there is some issue with the transport mechanism. + /// + /// A human-readable explanation of the specific issue is provided. + Transport(#[codec(skip)] &'static str), + /// Destination is known to be unroutable. + Unroutable, + /// The weight required was not specified when it should have been. + UnknownWeightRequired, +} + +impl From<()> for Error { + fn from(_: ()) -> Self { + Self::Undefined + } +} + +impl From for Error { + fn from(e: SendError) -> Self { + match e { + SendError::CannotReachDestination(..) | SendError::Unroutable => Error::Unroutable, + SendError::Transport(s) => Error::Transport(s), + SendError::DestinationUnsupported => Error::DestinationUnsupported, + SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, + } + } +} + +pub type Result = result::Result<(), Error>; + +/// Local weight type; execution time in picoseconds. +pub type Weight = u64; + +/// Outcome of an XCM execution. +#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)] +pub enum Outcome { + /// Execution completed successfully; given weight was used. + Complete(Weight), + /// Execution started, but did not complete successfully due to the given error; given weight was used. + Incomplete(Weight, Error), + /// Execution did not start due to the given error. + Error(Error), +} + +impl Outcome { + pub fn ensure_complete(self) -> Result { + match self { + Outcome::Complete(_) => Ok(()), + Outcome::Incomplete(_, e) => Err(e), + Outcome::Error(e) => Err(e), + } + } + pub fn ensure_execution(self) -> result::Result { + match self { + Outcome::Complete(w) => Ok(w), + Outcome::Incomplete(w, _) => Ok(w), + Outcome::Error(e) => Err(e), + } + } + /// How much weight was used by the XCM execution attempt. + pub fn weight_used(&self) -> Weight { + match self { + Outcome::Complete(w) => *w, + Outcome::Incomplete(w, _) => *w, + Outcome::Error(_) => 0, + } + } +} + +/// Type of XCM message executor. +pub trait ExecuteXcm { + /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is + /// a basic hard-limit and the implementation may place further restrictions or requirements on weight and + /// other aspects. + fn execute_xcm(origin: MultiLocation, message: Xcm, weight_limit: Weight) -> Outcome { + log::debug!( + target: "xcm::execute_xcm", + "origin: {:?}, message: {:?}, weight_limit: {:?}", + origin, + message, + weight_limit, + ); + Self::execute_xcm_in_credit(origin, message, weight_limit, 0) + } + + /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. + /// + /// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow + /// execution without associated payment. + fn execute_xcm_in_credit( + origin: MultiLocation, + message: Xcm, + weight_limit: Weight, + weight_credit: Weight, + ) -> Outcome; +} + +impl ExecuteXcm for () { + fn execute_xcm_in_credit( + _origin: MultiLocation, + _message: Xcm, + _weight_limit: Weight, + _weight_credit: Weight, + ) -> Outcome { + Outcome::Error(Error::Unimplemented) + } +} + +/// Error result value when attempting to send an XCM message. +#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)] +pub enum SendError { + /// The message and destination combination was not recognized as being reachable. + /// + /// This is not considered fatal: if there are alternative transport routes available, then + /// they may be attempted. For this reason, the destination and message are contained. + CannotReachDestination(MultiLocation, Xcm<()>), + /// Destination is routable, but there is some issue with the transport mechanism. This is + /// considered fatal. + /// A human-readable explanation of the specific issue is provided. + Transport(#[codec(skip)] &'static str), + /// Destination is known to be unroutable. This is considered fatal. + Unroutable, + /// The given message cannot be translated into a format that the destination can be expected + /// to interpret. + DestinationUnsupported, + /// Message could not be sent due to its size exceeding the maximum allowed by the transport + /// layer. + ExceedsMaxMessageSize, +} + +/// Result value when attempting to send an XCM message. +pub type SendResult = result::Result<(), SendError>; + +/// Utility for sending an XCM message. +/// +/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return +/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination` +/// might alter the destination and the XCM message for to the next router. +/// +/// +/// # Example +/// ```rust +/// # use xcm::v2::prelude::*; +/// # use parity_scale_codec::Encode; +/// +/// /// A sender that only passes the message through and does nothing. +/// struct Sender1; +/// impl SendXcm for Sender1 { +/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> SendResult { +/// return Err(SendError::CannotReachDestination(destination, message)) +/// } +/// } +/// +/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing. +/// struct Sender2; +/// impl SendXcm for Sender2 { +/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> SendResult { +/// if let MultiLocation { parents: 0, interior: X2(j1, j2) } = destination { +/// Ok(()) +/// } else { +/// Err(SendError::Unroutable) +/// } +/// } +/// } +/// +/// /// A sender that accepts a message from a parent, passing through otherwise. +/// struct Sender3; +/// impl SendXcm for Sender3 { +/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> SendResult { +/// match destination { +/// MultiLocation { parents: 1, interior: Here } => Ok(()), +/// _ => Err(SendError::CannotReachDestination(destination, message)), +/// } +/// } +/// } +/// +/// // A call to send via XCM. We don't really care about this. +/// # fn main() { +/// let call: Vec = ().encode(); +/// let message = Xcm(vec![Instruction::Transact { +/// origin_type: OriginKind::Superuser, +/// require_weight_at_most: 0, +/// call: call.into(), +/// }]); +/// let destination = MultiLocation::parent(); +/// +/// assert!( +/// // Sender2 will block this. +/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone()) +/// .is_err() +/// ); +/// +/// assert!( +/// // Sender3 will catch this. +/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone()) +/// .is_ok() +/// ); +/// # } +/// ``` +pub trait SendXcm { + /// Send an XCM `message` to a given `destination`. + /// + /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST* + /// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without + /// trying other type fields. + fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> SendResult; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl SendXcm for Tuple { + fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> SendResult { + for_tuples!( #( + // we shadow `destination` and `message` in each expansion for the next one. + let (destination, message) = match Tuple::send_xcm(destination, message) { + Err(SendError::CannotReachDestination(d, m)) => (d, m), + o @ _ => return o, + }; + )* ); + Err(SendError::CannotReachDestination(destination, message)) + } +} diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index ca1f399a9c45..4d82380a03c1 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -19,10 +19,10 @@ use frame_support::{ensure, traits::Contains, weights::Weight}; use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; -use xcm::latest::{Junction, Junctions, MultiLocation, Order, Xcm}; +use xcm::latest::{Instruction::*, Junction, Junctions, MultiLocation, WeightLimit::*, Xcm}; use xcm_executor::traits::{OnResponse, ShouldExecute}; -/// Execution barrier that just takes `shallow_weight` from `weight_credit`. +/// Execution barrier that just takes `max_weight` from `weight_credit`. /// /// Useful to allow XCM execution by local chain users via extrinsics. /// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset @@ -30,42 +30,53 @@ use xcm_executor::traits::{OnResponse, ShouldExecute}; pub struct TakeWeightCredit; impl ShouldExecute for TakeWeightCredit { fn should_execute( - _origin: &MultiLocation, + _origin: &Option, _top_level: bool, - _message: &Xcm, - shallow_weight: Weight, + _message: &mut Xcm, + max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { - *weight_credit = weight_credit.checked_sub(shallow_weight).ok_or(())?; + *weight_credit = weight_credit.checked_sub(max_weight).ok_or(())?; Ok(()) } } -/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking payments into -/// account. +/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking +/// payments into account. /// -/// Only allows for `TeleportAsset`, `WithdrawAsset` and `ReserveAssetDeposit` XCMs because they are the only ones -/// that place assets in the Holding Register to pay for execution. +/// Only allows for `TeleportAsset`, `WithdrawAsset` and `ReserveAssetDeposit` XCMs because they are +/// the only ones that place assets in the Holding Register to pay for execution. pub struct AllowTopLevelPaidExecutionFrom(PhantomData); impl> ShouldExecute for AllowTopLevelPaidExecutionFrom { fn should_execute( - origin: &MultiLocation, + origin: &Option, top_level: bool, - message: &Xcm, - shallow_weight: Weight, + message: &mut Xcm, + max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { + let origin = origin.as_ref().ok_or(())?; ensure!(T::contains(origin), ()); ensure!(top_level, ()); - match message { - Xcm::ReceiveTeleportedAsset { effects, .. } | - Xcm::WithdrawAsset { effects, .. } | - Xcm::ReserveAssetDeposited { effects, .. } - if matches!( - effects.first(), - Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight - ) => - Ok(()), + let mut iter = message.0.iter_mut(); + let i = iter.next().ok_or(())?; + match i { + ReceiveTeleportedAsset(..) | WithdrawAsset(..) | ReserveAssetDeposited(..) => (), + _ => return Err(()), + } + let mut i = iter.next().ok_or(())?; + while let ClearOrigin = i { + i = iter.next().ok_or(())?; + } + match i { + BuyExecution { weight_limit: Limited(ref mut weight), .. } if *weight >= max_weight => { + *weight = max_weight; + Ok(()) + }, + BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => { + *weight_limit = Limited(max_weight); + Ok(()) + }, _ => Err(()), } } @@ -76,12 +87,13 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro pub struct AllowUnpaidExecutionFrom(PhantomData); impl> ShouldExecute for AllowUnpaidExecutionFrom { fn should_execute( - origin: &MultiLocation, + origin: &Option, _top_level: bool, - _message: &Xcm, - _shallow_weight: Weight, + _message: &mut Xcm, + _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { + let origin = origin.as_ref().ok_or(())?; ensure!(T::contains(origin), ()); Ok(()) } @@ -103,14 +115,15 @@ impl> Contains for IsChildSystemPara pub struct AllowKnownQueryResponses(PhantomData); impl ShouldExecute for AllowKnownQueryResponses { fn should_execute( - origin: &MultiLocation, + origin: &Option, _top_level: bool, - message: &Xcm, - _shallow_weight: Weight, + message: &mut Xcm, + _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { - match message { - Xcm::QueryResponse { query_id, .. } + let origin = origin.as_ref().ok_or(())?; + match message.0.first() { + Some(QueryResponse { query_id, .. }) if ResponseHandler::expecting_response(origin, *query_id) => Ok(()), _ => Err(()), diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 3e96e7980210..21cb0c8650c4 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -107,7 +107,7 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { } pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> XcmResult { + fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> SendResult { SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); Ok(()) } @@ -222,9 +222,10 @@ impl OnResponse for TestResponseHandler { }) } fn on_response( - _origin: MultiLocation, + _origin: &MultiLocation, query_id: u64, response: xcm::latest::Response, + _max_weight: Weight, ) -> Weight { QUERIES.with(|q| { q.borrow_mut().entry(query_id).and_modify(|v| { @@ -258,6 +259,7 @@ parameter_types! { pub static AllowPaidFrom: Vec = vec![]; // 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight. pub static WeightPrice: (AssetId, u128) = (From::from(Here), 1_000_000_000_000); + pub static MaxInstructions: u32 = 100; } pub type TestBarrier = ( @@ -277,7 +279,7 @@ impl Config for TestConfig { type IsTeleporter = TestIsTeleporter; type LocationInverter = LocationInverter; type Barrier = TestBarrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; type ResponseHandler = TestResponseHandler; } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 25ab06718fcb..22f72041c364 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -44,31 +44,21 @@ fn basic_setup_works() { #[test] fn weigher_should_work() { - let mut message = opaque::Xcm::ReserveAssetDeposited { - assets: (Parent, 100).into(), - effects: vec![ - Order::BuyExecution { - fees: (Parent, 1).into(), - weight: 0, - debt: 30, - halt_on_error: true, - instructions: vec![], - }, - Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, - ], - } - .into(); - assert_eq!(::Weigher::shallow(&mut message), Ok(30)); + let mut message = Xcm(vec![ + ReserveAssetDeposited((Parent, 100).into()), + BuyExecution { fees: (Parent, 1).into(), weight_limit: Limited(30) }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, + ]); + assert_eq!(::Weigher::weight(&mut message), Ok(30)); } #[test] fn take_weight_credit_barrier_should_work() { let mut message = - opaque::Xcm::TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }; - + Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]); let mut weight_credit = 10; let r = TakeWeightCredit::should_execute( - &Parent.into(), + &Some(Parent.into()), true, &mut message, 10, @@ -78,7 +68,7 @@ fn take_weight_credit_barrier_should_work() { assert_eq!(weight_credit, 0); let r = TakeWeightCredit::should_execute( - &Parent.into(), + &Some(Parent.into()), true, &mut message, 10, @@ -91,12 +81,12 @@ fn take_weight_credit_barrier_should_work() { #[test] fn allow_unpaid_should_work() { let mut message = - opaque::Xcm::TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }; + Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]); AllowUnpaidFrom::set(vec![Parent.into()]); let r = AllowUnpaidExecutionFrom::>::should_execute( - &Parachain(1).into(), + &Some(Parachain(1).into()), true, &mut message, 10, @@ -105,7 +95,7 @@ fn allow_unpaid_should_work() { assert_eq!(r, Err(())); let r = AllowUnpaidExecutionFrom::>::should_execute( - &Parent.into(), + &Some(Parent.into()), true, &mut message, 10, @@ -119,10 +109,10 @@ fn allow_paid_should_work() { AllowPaidFrom::set(vec![Parent.into()]); let mut message = - opaque::Xcm::TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }; + Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]); let r = AllowTopLevelPaidExecutionFrom::>::should_execute( - &Parachain(1).into(), + &Some(Parachain(1).into()), true, &mut message, 10, @@ -131,22 +121,14 @@ fn allow_paid_should_work() { assert_eq!(r, Err(())); let fees = (Parent, 1).into(); - let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited { - assets: (Parent, 100).into(), - effects: vec![ - Order::BuyExecution { - fees, - weight: 0, - debt: 20, - halt_on_error: true, - instructions: vec![], - }, - Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, - ], - }; + let mut underpaying_message = Xcm::<()>(vec![ + ReserveAssetDeposited((Parent, 100).into()), + BuyExecution { fees, weight_limit: Limited(20) }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, + ]); let r = AllowTopLevelPaidExecutionFrom::>::should_execute( - &Parent.into(), + &Some(Parent.into()), true, &mut underpaying_message, 30, @@ -155,22 +137,14 @@ fn allow_paid_should_work() { assert_eq!(r, Err(())); let fees = (Parent, 1).into(); - let mut paying_message = opaque::Xcm::ReserveAssetDeposited { - assets: (Parent, 100).into(), - effects: vec![ - Order::BuyExecution { - fees, - weight: 0, - debt: 30, - halt_on_error: true, - instructions: vec![], - }, - Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, - ], - }; + let mut paying_message = Xcm::<()>(vec![ + ReserveAssetDeposited((Parent, 100).into()), + BuyExecution { fees, weight_limit: Limited(30) }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, + ]); let r = AllowTopLevelPaidExecutionFrom::>::should_execute( - &Parachain(1).into(), + &Some(Parachain(1).into()), true, &mut paying_message, 30, @@ -179,7 +153,7 @@ fn allow_paid_should_work() { assert_eq!(r, Err(())); let r = AllowTopLevelPaidExecutionFrom::>::should_execute( - &Parent.into(), + &Some(Parent.into()), true, &mut paying_message, 30, @@ -196,23 +170,11 @@ fn paying_reserve_deposit_should_work() { let origin = Parent.into(); let fees = (Parent, 30).into(); - let message = Xcm::::ReserveAssetDeposited { - assets: (Parent, 100).into(), - effects: vec![ - Order::::BuyExecution { - fees, - weight: 0, - debt: 30, - halt_on_error: true, - instructions: vec![], - }, - Order::::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: Here.into(), - }, - ], - }; + let message = Xcm(vec![ + ReserveAssetDeposited((Parent, 100).into()), + BuyExecution { fees, weight_limit: Limited(30) }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() }, + ]); let weight_limit = 50; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(30)); @@ -228,10 +190,10 @@ fn transfer_should_work() { // They want to transfer 100 of them to their sibling parachain #2 let r = XcmExecutor::::execute_xcm( Parachain(1).into(), - Xcm::TransferAsset { + Xcm(vec![TransferAsset { assets: (Here, 100).into(), beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), - }, + }]), 50, ); assert_eq!(r, Outcome::Complete(10)); @@ -240,6 +202,132 @@ fn transfer_should_work() { assert_eq!(sent_xcm(), vec![]); } +#[test] +fn errors_should_return_unused_weight() { + // we'll let them have message execution for free. + AllowUnpaidFrom::set(vec![Here.into()]); + // We own 1000 of our tokens. + add_asset(3000, (Here, 11)); + let mut message = Xcm(vec![ + // First xfer results in an error on the last message only + TransferAsset { + assets: (Here, 1).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }, + // Second xfer results in error third message and after + TransferAsset { + assets: (Here, 2).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }, + // Third xfer results in error second message and after + TransferAsset { + assets: (Here, 4).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }, + ]); + // Weight limit of 70 is needed. + let limit = ::Weigher::weight(&mut message).unwrap(); + assert_eq!(limit, 30); + + let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + assert_eq!(r, Outcome::Complete(30)); + assert_eq!(assets(3), vec![(Here, 7).into()]); + assert_eq!(assets(3000), vec![(Here, 4).into()]); + assert_eq!(sent_xcm(), vec![]); + + let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + assert_eq!(r, Outcome::Incomplete(30, XcmError::NotWithdrawable)); + assert_eq!(assets(3), vec![(Here, 10).into()]); + assert_eq!(assets(3000), vec![(Here, 1).into()]); + assert_eq!(sent_xcm(), vec![]); + + let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + assert_eq!(r, Outcome::Incomplete(20, XcmError::NotWithdrawable)); + assert_eq!(assets(3), vec![(Here, 11).into()]); + assert_eq!(assets(3000), vec![]); + assert_eq!(sent_xcm(), vec![]); + + let r = XcmExecutor::::execute_xcm(Here.into(), message, limit); + assert_eq!(r, Outcome::Incomplete(10, XcmError::NotWithdrawable)); + assert_eq!(assets(3), vec![(Here, 11).into()]); + assert_eq!(assets(3000), vec![]); + assert_eq!(sent_xcm(), vec![]); +} + +#[test] +fn weight_bounds_should_respect_instructions_limit() { + MaxInstructions::set(3); + let mut message = Xcm(vec![ClearOrigin; 4]); + // 4 instructions are too many. + assert_eq!(::Weigher::weight(&mut message), Err(())); + + let mut message = + Xcm(vec![SetErrorHandler(Xcm(vec![ClearOrigin])), SetAppendix(Xcm(vec![ClearOrigin]))]); + // 4 instructions are too many, even when hidden within 2. + assert_eq!(::Weigher::weight(&mut message), Err(())); + + let mut message = + Xcm(vec![SetErrorHandler(Xcm(vec![SetErrorHandler(Xcm(vec![SetErrorHandler(Xcm( + vec![ClearOrigin], + ))]))]))]); + // 4 instructions are too many, even when it's just one that's 3 levels deep. + assert_eq!(::Weigher::weight(&mut message), Err(())); + + let mut message = + Xcm(vec![SetErrorHandler(Xcm(vec![SetErrorHandler(Xcm(vec![ClearOrigin]))]))]); + // 3 instructions are OK. + assert_eq!(::Weigher::weight(&mut message), Ok(30)); +} + +#[test] +fn code_registers_should_work() { + // we'll let them have message execution for free. + AllowUnpaidFrom::set(vec![Here.into()]); + // We own 1000 of our tokens. + add_asset(3000, (Here, 21)); + let mut message = Xcm(vec![ + // Set our error handler - this will fire only on the second message, when there's an error + SetErrorHandler(Xcm(vec![ + TransferAsset { + assets: (Here, 2).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }, + // It was handled fine. + ClearError, + ])), + // Set the appendix - this will always fire. + SetAppendix(Xcm(vec![TransferAsset { + assets: (Here, 4).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }])), + // First xfer always works ok + TransferAsset { + assets: (Here, 1).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }, + // Second xfer results in error on the second message - our error handler will fire. + TransferAsset { + assets: (Here, 8).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + }, + ]); + // Weight limit of 70 is needed. + let limit = ::Weigher::weight(&mut message).unwrap(); + assert_eq!(limit, 70); + + let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + assert_eq!(r, Outcome::Complete(50)); // We don't pay the 20 weight for the error handler. + assert_eq!(assets(3), vec![(Here, 13).into()]); + assert_eq!(assets(3000), vec![(Here, 8).into()]); + assert_eq!(sent_xcm(), vec![]); + + let r = XcmExecutor::::execute_xcm(Here.into(), message, limit); + assert_eq!(r, Outcome::Complete(70)); // We pay the full weight here. + assert_eq!(assets(3), vec![(Here, 20).into()]); + assert_eq!(assets(3000), vec![(Here, 1).into()]); + assert_eq!(sent_xcm(), vec![]); +} + #[test] fn reserve_transfer_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); @@ -252,15 +340,15 @@ fn reserve_transfer_should_work() { // and let them know to hand it to account #3. let r = XcmExecutor::::execute_xcm( Parachain(1).into(), - Xcm::TransferReserveAsset { + Xcm(vec![TransferReserveAsset { assets: (Here, 100).into(), dest: Parachain(2).into(), - effects: vec![Order::DepositAsset { + xcm: Xcm::<()>(vec![DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three.clone(), - }], - }, + }]), + }]), 50, ); assert_eq!(r, Outcome::Complete(10)); @@ -270,14 +358,11 @@ fn reserve_transfer_should_work() { sent_xcm(), vec![( Parachain(2).into(), - Xcm::ReserveAssetDeposited { - assets: (Parent, 100).into(), - effects: vec![Order::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: three - }], - } + Xcm::<()>(vec![ + ReserveAssetDeposited((Parent, 100).into()), + ClearOrigin, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three }, + ]), )] ); } @@ -287,11 +372,11 @@ fn transacting_should_work() { AllowUnpaidFrom::set(vec![Parent.into()]); let origin = Parent.into(); - let message = Xcm::::Transact { + let message = Xcm::(vec![Transact { origin_type: OriginKind::Native, require_weight_at_most: 50, call: TestCall::Any(50, None).encode().into(), - }; + }]); let weight_limit = 60; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(60)); @@ -302,14 +387,14 @@ fn transacting_should_respect_max_weight_requirement() { AllowUnpaidFrom::set(vec![Parent.into()]); let origin = Parent.into(); - let message = Xcm::::Transact { + let message = Xcm::(vec![Transact { origin_type: OriginKind::Native, require_weight_at_most: 40, call: TestCall::Any(50, None).encode().into(), - }; + }]); let weight_limit = 60; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); - assert_eq!(r, Outcome::Incomplete(60, XcmError::TooMuchWeightRequired)); + assert_eq!(r, Outcome::Incomplete(50, XcmError::TooMuchWeightRequired)); } #[test] @@ -317,11 +402,11 @@ fn transacting_should_refund_weight() { AllowUnpaidFrom::set(vec![Parent.into()]); let origin = Parent.into(); - let message = Xcm::::Transact { + let message = Xcm::(vec![Transact { origin_type: OriginKind::Native, require_weight_at_most: 50, call: TestCall::Any(50, Some(30)).encode().into(), - }; + }]); let weight_limit = 60; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(40)); @@ -336,32 +421,22 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { let origin = one.clone(); let fees = (Parent, 100).into(); - let message = Xcm::::WithdrawAsset { - assets: (Parent, 100).into(), // enough for 100 units of weight. - effects: vec![ - Order::::BuyExecution { - fees, - weight: 70, - debt: 30, - halt_on_error: true, - instructions: vec![Xcm::::Transact { - origin_type: OriginKind::Native, - require_weight_at_most: 60, - // call estimated at 70 but only takes 10. - call: TestCall::Any(60, Some(10)).encode().into(), - }], - }, - Order::::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: one.clone(), - }, - ], - }; + let message = Xcm::(vec![ + WithdrawAsset((Parent, 100).into()), // enough for 100 units of weight. + BuyExecution { fees, weight_limit: Limited(100) }, + Transact { + origin_type: OriginKind::Native, + require_weight_at_most: 50, + // call estimated at 50 but only takes 10. + call: TestCall::Any(50, Some(10)).encode().into(), + }, + RefundSurplus, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: one.clone() }, + ]); let weight_limit = 100; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); - assert_eq!(r, Outcome::Complete(50)); - assert_eq!(assets(1), vec![(Parent, 50).into()]); + assert_eq!(r, Outcome::Complete(60)); + assert_eq!(assets(1), vec![(Parent, 40).into()]); } #[test] @@ -372,7 +447,11 @@ fn prepaid_result_of_query_should_get_free_execution() { expect_response(query_id, origin.clone()); let the_response = Response::Assets((Parent, 100).into()); - let message = Xcm::::QueryResponse { query_id, response: the_response.clone() }; + let message = Xcm::(vec![QueryResponse { + query_id, + response: the_response.clone(), + max_weight: 10, + }]); let weight_limit = 10; // First time the response gets through since we're expecting it... @@ -382,7 +461,7 @@ fn prepaid_result_of_query_should_get_free_execution() { // Second time it doesn't, since we're not. let r = XcmExecutor::::execute_xcm(origin.clone(), message.clone(), weight_limit); - assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier)); + assert_eq!(r, Outcome::Error(XcmError::Barrier)); } fn fungible_multi_asset(location: MultiLocation, amount: u128) -> MultiAsset { diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 515d7dbf22c4..c438ae4edbd4 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -21,76 +21,43 @@ use frame_support::{ use parity_scale_codec::Decode; use sp_runtime::traits::{SaturatedConversion, Saturating, Zero}; use sp_std::{convert::TryInto, marker::PhantomData, result::Result}; -use xcm::latest::{AssetId, AssetId::Concrete, Error, MultiAsset, MultiLocation, Order, Xcm}; +use xcm::latest::prelude::*; use xcm_executor::{ traits::{WeightBounds, WeightTrader}, Assets, }; -pub struct FixedWeightBounds(PhantomData<(T, C)>); -impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeightBounds { - fn shallow(message: &mut Xcm) -> Result { - Ok(match message { - Xcm::Transact { call, .. } => - call.ensure_decoded()?.get_dispatch_info().weight.saturating_add(T::get()), - Xcm::RelayedFrom { ref mut message, .. } => - T::get().saturating_add(Self::shallow(message.as_mut())?), - Xcm::WithdrawAsset { effects, .. } | - Xcm::ReserveAssetDeposited { effects, .. } | - Xcm::ReceiveTeleportedAsset { effects, .. } => { - let mut extra = T::get(); - for order in effects.iter_mut() { - extra.saturating_accrue(Self::shallow_order(order)?); - } - extra - }, - _ => T::get(), - }) +pub struct FixedWeightBounds(PhantomData<(T, C, M)>); +impl, C: Decode + GetDispatchInfo, M: Get> WeightBounds + for FixedWeightBounds +{ + fn weight(message: &mut Xcm) -> Result { + let mut instructions_left = M::get(); + Self::weight_with_limit(message, &mut instructions_left) } - fn deep(message: &mut Xcm) -> Result { - Ok(match message { - Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?, - Xcm::WithdrawAsset { effects, .. } | - Xcm::ReserveAssetDeposited { effects, .. } | - Xcm::ReceiveTeleportedAsset { effects, .. } => { - let mut extra = 0; - for order in effects.iter_mut() { - extra.saturating_accrue(Self::deep_order(order)?); - } - extra - }, - _ => 0, - }) + fn instr_weight(message: &Instruction) -> Result { + Self::instr_weight_with_limit(message, &mut u32::max_value()) } } -impl, C: Decode + GetDispatchInfo> FixedWeightBounds { - fn shallow_order(order: &mut Order) -> Result { - Ok(match order { - Order::BuyExecution { .. } => { - // On success, execution of this will result in more weight being consumed but - // we don't count it here since this is only the *shallow*, non-negotiable weight - // spend and doesn't count weight placed behind a `BuyExecution` since it will not - // be definitely consumed from any existing weight credit if execution of the message - // is attempted. - T::get() - }, - _ => T::get(), - }) - } - fn deep_order(order: &mut Order) -> Result { - Ok(match order { - Order::BuyExecution { instructions, .. } => { - let mut extra = 0; - for instruction in instructions.iter_mut() { - extra.saturating_accrue( - Self::shallow(instruction)?.saturating_add(Self::deep(instruction)?), - ); - } - extra - }, +impl, C: Decode + GetDispatchInfo, M> FixedWeightBounds { + fn weight_with_limit(message: &Xcm, instrs_limit: &mut u32) -> Result { + let mut r = 0; + *instrs_limit = instrs_limit.checked_sub(message.0.len() as u32).ok_or(())?; + for m in message.0.iter() { + r += Self::instr_weight_with_limit(m, instrs_limit)?; + } + Ok(r) + } + fn instr_weight_with_limit( + message: &Instruction, + instrs_limit: &mut u32, + ) -> Result { + Ok(T::get().saturating_add(match message { + Transact { require_weight_at_most, .. } => *require_weight_at_most, + SetErrorHandler(xcm) | SetAppendix(xcm) => Self::weight_with_limit(xcm, instrs_limit)?, _ => 0, - }) + })) } } @@ -124,10 +91,11 @@ impl, R: TakeRevenue> WeightTrader Self(0, 0, PhantomData) } - fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let (id, units_per_second) = T::get(); let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128); - let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?; + let unused = + payment.checked_sub((id, amount).into()).map_err(|_| XcmError::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); Ok(unused) @@ -169,13 +137,14 @@ impl, R: TakeRevenue> WeightTrader for FixedRateOfFungib Self(0, 0, PhantomData) } - fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let (id, units_per_second) = T::get(); let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128); if amount == 0 { return Ok(payment) } - let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?; + let unused = + payment.checked_sub((id, amount).into()).map_err(|_| XcmError::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); Ok(unused) @@ -228,11 +197,11 @@ impl< Self(0, Zero::zero(), PhantomData) } - fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let amount = WeightToFee::calc(&weight); - let u128_amount: u128 = amount.try_into().map_err(|_| Error::Overflow)?; + let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?; let required = (Concrete(AssetId::get()), u128_amount).into(); - let unused = payment.checked_sub(required).map_err(|_| Error::TooExpensive)?; + let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); Ok(unused) diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index fa40c9e231fc..ed64ce9470da 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -47,7 +47,7 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { } pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> XcmResult { + fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> SendResult { SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); Ok(()) } @@ -150,6 +150,7 @@ pub type Barrier = ( parameter_types! { pub const KusamaForStatemint: (MultiAssetFilter, MultiLocation) = (MultiAssetFilter::Wild(WildMultiAsset::AllOf { id: Concrete(MultiLocation::here()), fun: WildFungible }), X1(Parachain(1000)).into()); + pub const MaxInstructions: u32 = 100; } pub type TrustedTeleporters = (xcm_builder::Case,); @@ -163,7 +164,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = TrustedTeleporters; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; type ResponseHandler = (); } @@ -181,7 +182,9 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; + type Call = Call; + type Origin = Origin; } impl origin::Config for Runtime {} @@ -198,7 +201,7 @@ construct_runtime!( System: frame_system::{Pallet, Call, Storage, Config, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParasOrigin: origin::{Pallet, Origin}, - XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, } ); diff --git a/xcm/xcm-builder/tests/scenarios.rs b/xcm/xcm-builder/tests/scenarios.rs index bb3186fd36d0..077a6590d576 100644 --- a/xcm/xcm-builder/tests/scenarios.rs +++ b/xcm/xcm-builder/tests/scenarios.rs @@ -16,7 +16,6 @@ mod mock; -use frame_support::weights::Weight; use mock::{ kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, XcmConfig, CENTS, }; @@ -31,15 +30,8 @@ pub const INITIAL_BALANCE: u128 = 100_000_000_000; pub const REGISTER_AMOUNT: Balance = 10 * CENTS; // Construct a `BuyExecution` order. -fn buy_execution(debt: Weight) -> Order { - use xcm::latest::prelude::*; - Order::BuyExecution { - fees: (Here, REGISTER_AMOUNT).into(), - weight: 0, - debt, - halt_on_error: false, - instructions: vec![], - } +fn buy_execution() -> Instruction { + BuyExecution { fees: (Here, REGISTER_AMOUNT).into(), weight_limit: Unlimited } } /// Scenario: @@ -56,17 +48,15 @@ fn withdraw_and_deposit_works() { let weight = 3 * BaseXcmWeight::get(); let r = XcmExecutor::::execute_xcm( Parachain(PARA_ID).into(), - Xcm::WithdrawAsset { - assets: vec![(Here, amount).into()].into(), - effects: vec![ - buy_execution(weight), - Order::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: Parachain(other_para_id).into(), - }, - ], - }, + Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Parachain(other_para_id).into(), + }, + ]), weight, ); assert_eq!(r, Outcome::Complete(weight)); @@ -94,31 +84,31 @@ fn query_holding_works() { let amount = REGISTER_AMOUNT; let query_id = 1234; let weight = 4 * BaseXcmWeight::get(); + let max_response_weight = 1_000_000_000; let r = XcmExecutor::::execute_xcm( Parachain(PARA_ID).into(), - Xcm::WithdrawAsset { - assets: vec![(Here, amount).into()].into(), - effects: vec![ - buy_execution(weight), - Order::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: OnlyChild.into(), // invalid destination - }, - // is not triggered becasue the deposit fails - Order::QueryHolding { - query_id, - dest: Parachain(PARA_ID).into(), - assets: All.into(), - }, - ], - }, + Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: OnlyChild.into(), // invalid destination + }, + // is not triggered becasue the deposit fails + QueryHolding { + query_id, + dest: Parachain(PARA_ID).into(), + assets: All.into(), + max_response_weight, + }, + ]), weight, ); assert_eq!( r, Outcome::Incomplete( - weight, + weight - BaseXcmWeight::get(), XcmError::FailedToTransactAsset("AccountIdConversionFailed") ) ); @@ -129,23 +119,22 @@ fn query_holding_works() { // now do a successful transfer let r = XcmExecutor::::execute_xcm( Parachain(PARA_ID).into(), - Xcm::WithdrawAsset { - assets: vec![(Here, amount).into()].into(), - effects: vec![ - buy_execution(weight), - Order::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: Parachain(other_para_id).into(), - }, - // used to get a notification in case of success - Order::QueryHolding { - query_id, - dest: Parachain(PARA_ID).into(), - assets: All.into(), - }, - ], - }, + Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Parachain(other_para_id).into(), + }, + // used to get a notification in case of success + QueryHolding { + query_id, + dest: Parachain(PARA_ID).into(), + assets: All.into(), + max_response_weight: 1_000_000_000, + }, + ]), weight, ); assert_eq!(r, Outcome::Complete(weight)); @@ -156,7 +145,11 @@ fn query_holding_works() { mock::sent_xcm(), vec![( Parachain(PARA_ID).into(), - Xcm::QueryResponse { query_id, response: Response::Assets(vec![].into()) } + Xcm(vec![QueryResponse { + query_id, + response: Response::Assets(vec![].into()), + max_weight: 1_000_000_000, + }]), )] ); }); @@ -180,8 +173,8 @@ fn teleport_to_statemine_works() { let other_para_id = 3000; let amount = REGISTER_AMOUNT; let teleport_effects = vec![ - buy_execution(5), // unchecked mock value - Order::DepositAsset { + buy_execution(), // unchecked mock value + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: (1, Parachain(PARA_ID)).into(), @@ -192,17 +185,15 @@ fn teleport_to_statemine_works() { // teleports are allowed to community chains, even in the absence of trust from their side. let r = XcmExecutor::::execute_xcm( Parachain(PARA_ID).into(), - Xcm::WithdrawAsset { - assets: vec![(Here, amount).into()].into(), - effects: vec![ - buy_execution(weight), - Order::InitiateTeleport { - assets: All.into(), - dest: Parachain(other_para_id).into(), - effects: teleport_effects.clone(), - }, - ], - }, + Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + InitiateTeleport { + assets: All.into(), + dest: Parachain(other_para_id).into(), + xcm: Xcm(teleport_effects.clone()), + }, + ]), weight, ); assert_eq!(r, Outcome::Complete(weight)); @@ -210,27 +201,25 @@ fn teleport_to_statemine_works() { mock::sent_xcm(), vec![( Parachain(other_para_id).into(), - Xcm::ReceiveTeleportedAsset { - assets: vec![(Parent, amount).into()].into(), - effects: teleport_effects.clone(), - } + Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,] + .into_iter() + .chain(teleport_effects.clone().into_iter()) + .collect()) )] ); // teleports are allowed from statemine to kusama. let r = XcmExecutor::::execute_xcm( Parachain(PARA_ID).into(), - Xcm::WithdrawAsset { - assets: vec![(Here, amount).into()].into(), - effects: vec![ - buy_execution(weight), - Order::InitiateTeleport { - assets: All.into(), - dest: Parachain(statemine_id).into(), - effects: teleport_effects.clone(), - }, - ], - }, + Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + InitiateTeleport { + assets: All.into(), + dest: Parachain(statemine_id).into(), + xcm: Xcm(teleport_effects.clone()), + }, + ]), weight, ); assert_eq!(r, Outcome::Complete(weight)); @@ -241,17 +230,17 @@ fn teleport_to_statemine_works() { vec![ ( Parachain(other_para_id).into(), - Xcm::ReceiveTeleportedAsset { - assets: vec![(Parent, amount).into()].into(), - effects: teleport_effects.clone(), - } + Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,] + .into_iter() + .chain(teleport_effects.clone().into_iter()) + .collect()), ), ( Parachain(statemine_id).into(), - Xcm::ReceiveTeleportedAsset { - assets: vec![(Parent, amount).into()].into(), - effects: teleport_effects, - } + Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,] + .into_iter() + .chain(teleport_effects.clone().into_iter()) + .collect()), ) ] ); @@ -273,8 +262,8 @@ fn reserve_based_transfer_works() { let other_para_id = 3000; let amount = REGISTER_AMOUNT; let transfer_effects = vec![ - buy_execution(5), // unchecked mock value - Order::DepositAsset { + buy_execution(), // unchecked mock value + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: (1, Parachain(PARA_ID)).into(), @@ -283,18 +272,16 @@ fn reserve_based_transfer_works() { let weight = 3 * BaseXcmWeight::get(); let r = XcmExecutor::::execute_xcm( Parachain(PARA_ID).into(), - Xcm::WithdrawAsset { - assets: vec![(Here, amount).into()].into(), - effects: vec![ - buy_execution(weight), - Order::DepositReserveAsset { - assets: All.into(), - max_assets: 1, - dest: Parachain(other_para_id).into(), - effects: transfer_effects.clone(), - }, - ], - }, + Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositReserveAsset { + assets: All.into(), + max_assets: 1, + dest: Parachain(other_para_id).into(), + xcm: Xcm(transfer_effects.clone()), + }, + ]), weight, ); assert_eq!(r, Outcome::Complete(weight)); @@ -303,10 +290,10 @@ fn reserve_based_transfer_works() { mock::sent_xcm(), vec![( Parachain(other_para_id).into(), - Xcm::ReserveAssetDeposited { - assets: vec![(Parent, amount).into()].into(), - effects: transfer_effects, - } + Xcm(vec![ReserveAssetDeposited((Parent, amount).into()), ClearOrigin,] + .into_iter() + .chain(transfer_effects.into_iter()) + .collect()) )] ); }); diff --git a/xcm/xcm-executor/integration-tests/src/lib.rs b/xcm/xcm-executor/integration-tests/src/lib.rs index 78b03dd82ccd..72aa7b15d07b 100644 --- a/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/xcm/xcm-executor/integration-tests/src/lib.rs @@ -21,46 +21,35 @@ use polkadot_test_client::{ BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, ExecutionStrategy, InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt, }; +use polkadot_test_runtime::pallet_test_notifier; use polkadot_test_service::construct_extrinsic; use sp_runtime::{generic::BlockId, traits::Block}; use sp_state_machine::InspectState; -use xcm::{latest::prelude::*, VersionedXcm}; -use xcm_executor::MAX_RECURSION_LIMIT; - -// This is the inflection point where the test should either fail or pass. -const MAX_RECURSION_CHECK: u32 = MAX_RECURSION_LIMIT / 2; +use xcm::{latest::prelude::*, VersionedResponse, VersionedXcm}; #[test] -fn execute_within_recursion_limit() { +fn basic_buy_fees_message_executes() { sp_tracing::try_init_simple(); let mut client = TestClientBuilder::new() .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); - let mut msg = WithdrawAsset { assets: (Parent, 100).into(), effects: vec![] }; - for _ in 0..MAX_RECURSION_CHECK { - msg = WithdrawAsset { - assets: (Parent, 100).into(), - effects: vec![Order::BuyExecution { - fees: (Parent, 1).into(), - weight: 0, - debt: 0, - halt_on_error: true, - // nest `msg` into itself on each iteration. - instructions: vec![msg], - }], - }; - } + let msg = Xcm(vec![ + WithdrawAsset((Parent, 100).into()), + BuyExecution { fees: (Parent, 1).into(), weight_limit: Unlimited }, + DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Parent.into() }, + ]); let mut block_builder = client.init_polkadot_block_builder(); let execute = construct_extrinsic( &client, polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute( - Box::new(VersionedXcm::from(msg.clone())), + Box::new(VersionedXcm::from(msg)), 1_000_000_000, )), sp_keyring::Sr25519Keyring::Alice, + 0, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); @@ -79,42 +68,153 @@ fn execute_within_recursion_limit() { r.event, polkadot_test_runtime::Event::Xcm(pallet_xcm::Event::Attempted(Outcome::Complete( _ - )),), + ))), ))); }); } #[test] -fn exceed_recursion_limit() { +fn query_response_fires() { + use pallet_test_notifier::Event::*; + use pallet_xcm::QueryStatus; + use polkadot_test_runtime::Event::TestNotifier; + sp_tracing::try_init_simple(); let mut client = TestClientBuilder::new() .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); - let mut msg = WithdrawAsset { assets: (Parent, 100).into(), effects: vec![] }; - for _ in 0..(MAX_RECURSION_CHECK + 1) { - msg = WithdrawAsset { - assets: (Parent, 100).into(), - effects: vec![Order::BuyExecution { - fees: (Parent, 1).into(), - weight: 0, - debt: 0, - halt_on_error: true, - // nest `msg` into itself on each iteration. - instructions: vec![msg], - }], - }; - } + let mut block_builder = client.init_polkadot_block_builder(); + + let execute = construct_extrinsic( + &client, + polkadot_test_runtime::Call::TestNotifier(pallet_test_notifier::Call::prepare_new_query()), + sp_keyring::Sr25519Keyring::Alice, + 0, + ); + + block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); + + let block = block_builder.build().expect("Finalizes the block").block; + let block_hash = block.hash(); + + futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) + .expect("imports the block"); + + let mut query_id = None; + client + .state_at(&BlockId::Hash(block_hash)) + .expect("state should exist") + .inspect_state(|| { + for r in polkadot_test_runtime::System::events().iter() { + match r.event { + TestNotifier(QueryPrepared(q)) => query_id = Some(q), + _ => (), + } + } + }); + let query_id = query_id.unwrap(); let mut block_builder = client.init_polkadot_block_builder(); + let response = Response::ExecutionResult(Ok(())); + let max_weight = 1_000_000; + let msg = Xcm(vec![QueryResponse { query_id, response, max_weight }]); + let msg = Box::new(VersionedXcm::from(msg)); + + let execute = construct_extrinsic( + &client, + polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute(msg, 1_000_000_000)), + sp_keyring::Sr25519Keyring::Alice, + 1, + ); + + block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); + + let block = block_builder.build().expect("Finalizes the block").block; + let block_hash = block.hash(); + + futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) + .expect("imports the block"); + + client + .state_at(&BlockId::Hash(block_hash)) + .expect("state should exist") + .inspect_state(|| { + assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( + r.event, + polkadot_test_runtime::Event::Xcm(pallet_xcm::Event::ResponseReady( + q, + Response::ExecutionResult(Ok(())), + )) if q == query_id, + ))); + assert_eq!( + polkadot_test_runtime::Xcm::query(query_id), + Some(QueryStatus::Ready { + response: VersionedResponse::V2(Response::ExecutionResult(Ok(()))), + at: 2u32.into() + }), + ) + }); +} + +#[test] +fn query_response_elicits_handler() { + use pallet_test_notifier::Event::*; + use polkadot_test_runtime::Event::TestNotifier; + + sp_tracing::try_init_simple(); + let mut client = TestClientBuilder::new() + .set_execution_strategy(ExecutionStrategy::AlwaysWasm) + .build(); + + let mut block_builder = client.init_polkadot_block_builder(); + + let execute = construct_extrinsic( + &client, + polkadot_test_runtime::Call::TestNotifier( + pallet_test_notifier::Call::prepare_new_notify_query(), + ), + sp_keyring::Sr25519Keyring::Alice, + 0, + ); + + block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); + + let block = block_builder.build().expect("Finalizes the block").block; + let block_hash = block.hash(); + + futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) + .expect("imports the block"); + + let mut query_id = None; + client + .state_at(&BlockId::Hash(block_hash)) + .expect("state should exist") + .inspect_state(|| { + for r in polkadot_test_runtime::System::events().iter() { + match r.event { + TestNotifier(NotifyQueryPrepared(q)) => query_id = Some(q), + _ => (), + } + } + }); + let query_id = query_id.unwrap(); + + let mut block_builder = client.init_polkadot_block_builder(); + + let response = Response::ExecutionResult(Ok(())); + let max_weight = 1_000_000; + let msg = Xcm(vec![QueryResponse { query_id, response, max_weight }]); + let execute = construct_extrinsic( &client, polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute( - Box::new(VersionedXcm::from(msg.clone())), + Box::new(VersionedXcm::from(msg)), 1_000_000_000, )), sp_keyring::Sr25519Keyring::Alice, + 1, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); @@ -131,9 +231,11 @@ fn exceed_recursion_limit() { .inspect_state(|| { assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( r.event, - polkadot_test_runtime::Event::Xcm(pallet_xcm::Event::Attempted( - Outcome::Incomplete(_, XcmError::RecursionLimitReached), - )), + TestNotifier(ResponseReceived( + MultiLocation { parents: 0, interior: X1(Junction::AccountId32 { .. }) }, + q, + Response::ExecutionResult(Ok(())), + )) if q == query_id, ))); }); } diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index 7f4bf08f5fa0..abb241c49274 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -38,10 +38,10 @@ pub trait Config { /// How to get a call origin from a `OriginKind` value. type OriginConverter: ConvertOrigin<::Origin>; - /// Combinations of (Location, Asset) pairs which we unilateral trust as reserves. + /// Combinations of (Location, Asset) pairs which we trust as reserves. type IsReserve: FilterAssetLocation; - /// Combinations of (Location, Asset) pairs which we bilateral trust as teleporters. + /// Combinations of (Location, Asset) pairs which we trust as teleporters. type IsTeleporter: FilterAssetLocation; /// Means of inverting a location. diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index e85e211a18d0..5484121d5ebc 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -21,10 +21,12 @@ use frame_support::{ ensure, weights::GetDispatchInfo, }; +use sp_runtime::traits::Saturating; use sp_std::{marker::PhantomData, prelude::*}; use xcm::latest::{ - Error as XcmError, ExecuteXcm, MultiAssets, MultiLocation, Order, Outcome, Response, SendXcm, - Xcm, + Error as XcmError, ExecuteXcm, + Instruction::{self, *}, + MultiAssets, MultiLocation, Outcome, Response, SendXcm, Xcm, }; pub mod traits; @@ -39,7 +41,25 @@ mod config; pub use config::Config; /// The XCM executor. -pub struct XcmExecutor(PhantomData); +pub struct XcmExecutor { + holding: Assets, + origin: Option, + trader: Config::Trader, + /// The most recent error result and instruction index into the fragment in which it occured, + /// if any. + error: Option<(u32, XcmError)>, + /// The surplus weight, defined as the amount by which `max_weight` is + /// an over-estimate of the actual weight consumed. We do it this way to avoid needing the + /// execution engine to keep track of all instructions' weights (it only needs to care about + /// the weight of dynamically determined instructions such as `Transact`). + total_surplus: u64, + total_refunded: u64, + error_handler: Xcm, + error_handler_weight: u64, + appendix: Xcm, + appendix_weight: u64, + _config: PhantomData, +} /// The maximum recursion limit for `execute_xcm` and `execute_effects`. pub const MAX_RECURSION_LIMIT: u32 = 8; @@ -47,7 +67,7 @@ pub const MAX_RECURSION_LIMIT: u32 = 8; impl ExecuteXcm for XcmExecutor { fn execute_xcm_in_credit( origin: MultiLocation, - message: Xcm, + mut message: Xcm, weight_limit: Weight, mut weight_credit: Weight, ) -> Outcome { @@ -59,143 +79,186 @@ impl ExecuteXcm for XcmExecutor { weight_limit, weight_credit, ); - let mut message = Xcm::::from(message); - let shallow_weight = match Config::Weigher::shallow(&mut message) { - Ok(x) => x, - Err(()) => return Outcome::Error(XcmError::WeightNotComputable), - }; - let deep_weight = match Config::Weigher::deep(&mut message) { + let xcm_weight = match Config::Weigher::weight(&mut message) { Ok(x) => x, Err(()) => return Outcome::Error(XcmError::WeightNotComputable), }; - let maximum_weight = match shallow_weight.checked_add(deep_weight) { - Some(x) => x, - None => return Outcome::Error(XcmError::Overflow), - }; - if maximum_weight > weight_limit { - return Outcome::Error(XcmError::WeightLimitReached(maximum_weight)) + if xcm_weight > weight_limit { + return Outcome::Error(XcmError::WeightLimitReached(xcm_weight)) } - let mut trader = Config::Trader::new(); - let result = Self::do_execute_xcm( - origin, + let origin = Some(origin); + + if let Err(_) = Config::Barrier::should_execute( + &origin, true, - message, + &mut message, + xcm_weight, &mut weight_credit, - Some(shallow_weight), - &mut trader, - 0, - ); - drop(trader); - log::trace!(target: "xcm::execute_xcm", "result: {:?}", &result); - match result { - Ok(surplus) => Outcome::Complete(maximum_weight.saturating_sub(surplus)), - // TODO: #2841 #REALWEIGHT We can do better than returning `maximum_weight` here, and we should otherwise - // we'll needlessly be disregarding block execution time. - Err(e) => Outcome::Incomplete(maximum_weight, e), + ) { + return Outcome::Error(XcmError::Barrier) + } + + let mut vm = Self::new(origin); + + while !message.0.is_empty() { + let result = vm.execute(message); + log::trace!(target: "xcm::execute_xcm_in_credit", "result: {:?}", result); + message = if let Err((i, e, w)) = result { + vm.total_surplus.saturating_accrue(w); + vm.error = Some((i, e)); + vm.take_error_handler().or_else(|| vm.take_appendix()) + } else { + vm.drop_error_handler(); + vm.take_appendix() + } + } + + vm.refund_surplus(); + drop(vm.trader); + + // TODO #2841: Do something with holding? (Fail-safe AssetTrap?) + + let weight_used = xcm_weight.saturating_sub(vm.total_surplus); + match vm.error { + None => Outcome::Complete(weight_used), + // TODO: #2841 #REALWEIGHT We should deduct the cost of any instructions following + // the error which didn't end up being executed. + Some((_, e)) => Outcome::Incomplete(weight_used, e), } } } impl XcmExecutor { - fn reanchored(mut assets: Assets, dest: &MultiLocation) -> MultiAssets { - let inv_dest = Config::LocationInverter::invert_location(&dest); - assets.prepend_location(&inv_dest); - assets.into_assets_iter().collect::>().into() + fn new(origin: Option) -> Self { + Self { + holding: Assets::new(), + origin, + trader: Config::Trader::new(), + error: None, + total_surplus: 0, + total_refunded: 0, + error_handler: Xcm(vec![]), + error_handler_weight: 0, + appendix: Xcm(vec![]), + appendix_weight: 0, + _config: PhantomData, + } } - /// Execute the XCM and return the portion of weight of `shallow_weight + deep_weight` that `message` did not use. - /// - /// NOTE: The amount returned must be less than `shallow_weight + deep_weight` of `message`. - fn do_execute_xcm( - origin: MultiLocation, - top_level: bool, - mut message: Xcm, - weight_credit: &mut Weight, - maybe_shallow_weight: Option, - trader: &mut Config::Trader, - num_recursions: u32, - ) -> Result { + /// Execute the XCM program fragment and report back the error and which instruction caused it, + /// or `Ok` if there was no error. + fn execute(&mut self, xcm: Xcm) -> Result<(), (u32, XcmError, u64)> { log::trace!( - target: "xcm::do_execute_xcm", - "origin: {:?}, top_level: {:?}, message: {:?}, weight_credit: {:?}, maybe_shallow_weight: {:?}, recursion: {:?}", - origin, - top_level, - message, - weight_credit, - maybe_shallow_weight, - num_recursions, + target: "xcm::execute", + "origin: {:?}, total_surplus/refunded: {:?}/{:?}, error_handler_weight: {:?}", + self.origin, + self.total_surplus, + self.total_refunded, + self.error_handler_weight, ); - - if num_recursions > MAX_RECURSION_LIMIT { - return Err(XcmError::RecursionLimitReached) + let mut result = Ok(()); + for (i, instr) in xcm.0.into_iter().enumerate() { + match &mut result { + r @ Ok(()) => + if let Err(e) = self.process_instruction(instr) { + *r = Err((i as u32, e, 0)); + }, + Err((_, _, ref mut w)) => + if let Ok(x) = Config::Weigher::instr_weight(&instr) { + w.saturating_accrue(x) + }, + } } + result + } - // This is the weight of everything that cannot be paid for. This basically means all computation - // except any XCM which is behind an Order::BuyExecution. - let shallow_weight = maybe_shallow_weight - .or_else(|| Config::Weigher::shallow(&mut message).ok()) - .ok_or(XcmError::WeightNotComputable)?; + /// Remove the registered error handler and return it. Do not refund its weight. + fn take_error_handler(&mut self) -> Xcm { + let mut r = Xcm::(vec![]); + sp_std::mem::swap(&mut self.error_handler, &mut r); + self.error_handler_weight = 0; + r + } - Config::Barrier::should_execute( - &origin, - top_level, - &message, - shallow_weight, - weight_credit, - ) - .map_err(|()| XcmError::Barrier)?; + /// Drop the registered error handler and refund its weight. + fn drop_error_handler(&mut self) { + self.error_handler = Xcm::(vec![]); + self.total_surplus = self.total_surplus.saturating_add(self.error_handler_weight); + self.error_handler_weight = 0; + } + + /// Remove the registered appendix and return it. + fn take_appendix(&mut self) -> Xcm { + let mut r = Xcm::(vec![]); + sp_std::mem::swap(&mut self.appendix, &mut r); + self.appendix_weight = 0; + r + } - // The surplus weight, defined as the amount by which `shallow_weight` plus all nested - // `shallow_weight` values (ensuring no double-counting and also known as `deep_weight`) is an - // over-estimate of the actual weight consumed. - let mut total_surplus: Weight = 0; + /// Refund any unused weight. + fn refund_surplus(&mut self) { + let current_surplus = self.total_surplus.saturating_sub(self.total_refunded); + if current_surplus > 0 { + self.total_refunded = self.total_refunded.saturating_add(current_surplus); + if let Some(w) = self.trader.refund_weight(current_surplus) { + self.holding.subsume(w); + } + } + } - let maybe_holding_effects = match (origin.clone(), message) { - (origin, Xcm::WithdrawAsset { assets, effects }) => { + /// Process a single XCM instruction, mutating the state of the XCM virtual machine. + fn process_instruction(&mut self, instr: Instruction) -> Result<(), XcmError> { + match instr { + WithdrawAsset(assets) => { // Take `assets` from the origin account (on-chain) and place in holding. - let mut holding = Assets::default(); - for asset in assets.inner() { - let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?; - holding.subsume_assets(withdrawn); + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + for asset in assets.drain().into_iter() { + Config::AssetTransactor::withdraw_asset(&asset, origin)?; + self.holding.subsume(asset); } - Some((holding, effects)) + Ok(()) }, - (origin, Xcm::ReserveAssetDeposited { assets, effects }) => { + ReserveAssetDeposited(assets) => { // check whether we trust origin to be our reserve location for this asset. - for asset in assets.inner() { - // We only trust the origin to send us assets that they identify as their - // sovereign assets. + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + for asset in assets.drain().into_iter() { + // Must ensure that we recognise the asset as being managed by the origin. ensure!( - Config::IsReserve::filter_asset_location(asset, &origin), + Config::IsReserve::filter_asset_location(&asset, origin), XcmError::UntrustedReserveLocation ); + self.holding.subsume(asset); } - Some((assets.into(), effects)) + Ok(()) }, - (origin, Xcm::TransferAsset { assets, beneficiary }) => { + TransferAsset { assets, beneficiary } => { // Take `assets` from the origin account (on-chain) and place into dest account. + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; for asset in assets.inner() { - Config::AssetTransactor::beam_asset(&asset, &origin, &beneficiary)?; + Config::AssetTransactor::beam_asset(&asset, origin, &beneficiary)?; } - None + Ok(()) }, - (origin, Xcm::TransferReserveAsset { mut assets, dest, effects }) => { + TransferReserveAsset { mut assets, dest, xcm } => { + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; // Take `assets` from the origin account (on-chain) and place into dest account. let inv_dest = Config::LocationInverter::invert_location(&dest); for asset in assets.inner() { - Config::AssetTransactor::beam_asset(asset, &origin, &dest)?; + Config::AssetTransactor::beam_asset(asset, origin, &dest)?; } assets.reanchor(&inv_dest)?; - Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; - None + let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) }, - (origin, Xcm::ReceiveTeleportedAsset { assets, effects }) => { + ReceiveTeleportedAsset(assets) => { + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; // check whether we trust origin to teleport this asset to us via config trait. for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their // sovereign assets. ensure!( - Config::IsTeleporter::filter_asset_location(asset, &origin), + Config::IsTeleporter::filter_asset_location(asset, origin), XcmError::UntrustedTeleportLocation ); // We should check that the asset can actually be teleported in (for this to be in error, there @@ -203,13 +266,15 @@ impl XcmExecutor { // don't want to punish a possibly innocent chain/user). Config::AssetTransactor::can_check_in(&origin, asset)?; } - for asset in assets.inner() { - Config::AssetTransactor::check_in(&origin, asset); + for asset in assets.drain().into_iter() { + Config::AssetTransactor::check_in(origin, &asset); + self.holding.subsume(asset); } - Some((Assets::from(assets), effects)) + Ok(()) }, - (origin, Xcm::Transact { origin_type, require_weight_at_most, mut call }) => { + Transact { origin_type, require_weight_at_most, mut call } => { // We assume that the Relay-chain is allowed to use transact on this parachain. + let origin = self.origin.clone().ok_or(XcmError::BadOrigin)?; // TODO: #2841 #TRANSACTFILTER allow the trait to issue filters for the relay-chain let message_call = call.take_decoded().map_err(|_| XcmError::FailedToDecode)?; @@ -227,142 +292,130 @@ impl XcmExecutor { } .unwrap_or(weight); let surplus = weight.saturating_sub(actual_weight); - // Credit any surplus weight that we bought. This should be safe since it's work we - // didn't realise that we didn't have to do. - // It works because we assume that the `Config::Weigher` will always count the `call`'s - // `get_dispatch_info` weight into its `shallow` estimate. - *weight_credit = weight_credit.saturating_add(surplus); - // Do the same for the total surplus, which is reported to the caller and eventually makes its way - // back up the stack to be subtracted from the deep-weight. - total_surplus = total_surplus.saturating_add(surplus); - // Return the overestimated amount so we can adjust our expectations on how much this entire - // execution has taken. - None + // We assume that the `Config::Weigher` will counts the `require_weight_at_most` + // for the estimate of how much weight this instruction will take. Now that we know + // that it's less, we credit it. + // + // We make the adjustment for the total surplus, which is used eventually + // reported back to the caller and this ensures that they account for the total + // weight consumed correctly (potentially allowing them to do more operations in a + // block than they otherwise would). + self.total_surplus = self.total_surplus.saturating_add(surplus); + Ok(()) }, - (origin, Xcm::QueryResponse { query_id, response }) => { - Config::ResponseHandler::on_response(origin, query_id, response); - None + QueryResponse { query_id, response, max_weight } => { + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + Config::ResponseHandler::on_response(origin, query_id, response, max_weight); + Ok(()) }, - (origin, Xcm::RelayedFrom { who, message }) => { - let mut origin = origin; - origin.append_with(who).map_err(|_| XcmError::MultiLocationFull)?; - let surplus = Self::do_execute_xcm( - origin, - top_level, - *message, - weight_credit, - None, - trader, - num_recursions + 1, - )?; - total_surplus = total_surplus.saturating_add(surplus); - None + DescendOrigin(who) => self + .origin + .as_mut() + .ok_or(XcmError::BadOrigin)? + .append_with(who) + .map_err(|_| XcmError::MultiLocationFull), + ClearOrigin => { + self.origin = None; + Ok(()) }, - _ => Err(XcmError::UnhandledXcmMessage)?, // Unhandled XCM message. - }; - - if let Some((mut holding, effects)) = maybe_holding_effects { - for effect in effects.into_iter() { - total_surplus += Self::execute_orders( - &origin, - &mut holding, - effect, - trader, - num_recursions + 1, - )?; - } - } - - Ok(total_surplus) - } - - fn execute_orders( - origin: &MultiLocation, - holding: &mut Assets, - order: Order, - trader: &mut Config::Trader, - num_recursions: u32, - ) -> Result { - log::trace!( - target: "xcm::execute_orders", - "origin: {:?}, holding: {:?}, order: {:?}, recursion: {:?}", - origin, - holding, - order, - num_recursions, - ); - - if num_recursions > MAX_RECURSION_LIMIT { - return Err(XcmError::RecursionLimitReached) - } - - let mut total_surplus = 0; - match order { - Order::DepositAsset { assets, max_assets, beneficiary } => { - let deposited = holding.limited_saturating_take(assets, max_assets as usize); + ReportError { query_id, dest, max_response_weight: max_weight } => { + // Report the given result by sending a QueryResponse XCM to a previously given outcome + // destination if one was registered. + let response = Response::ExecutionResult(match self.error { + None => Ok(()), + Some(e) => Err(e), + }); + let message = QueryResponse { query_id, response, max_weight }; + Config::XcmSender::send_xcm(dest, Xcm(vec![message]))?; + Ok(()) + }, + DepositAsset { assets, max_assets, beneficiary } => { + let deposited = self.holding.limited_saturating_take(assets, max_assets as usize); for asset in deposited.into_assets_iter() { Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?; } + Ok(()) }, - Order::DepositReserveAsset { assets, max_assets, dest, effects } => { - let deposited = holding.limited_saturating_take(assets, max_assets as usize); + DepositReserveAsset { assets, max_assets, dest, xcm } => { + let deposited = self.holding.limited_saturating_take(assets, max_assets as usize); for asset in deposited.assets_iter() { Config::AssetTransactor::deposit_asset(&asset, &dest)?; } let assets = Self::reanchored(deposited, &dest); - Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; + let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) }, - Order::InitiateReserveWithdraw { assets, reserve, effects } => { - let assets = Self::reanchored(holding.saturating_take(assets), &reserve); - Config::XcmSender::send_xcm(reserve, Xcm::WithdrawAsset { assets, effects })?; + InitiateReserveWithdraw { assets, reserve, xcm } => { + let assets = Self::reanchored(self.holding.saturating_take(assets), &reserve); + let mut message = vec![WithdrawAsset(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + Config::XcmSender::send_xcm(reserve, Xcm(message)).map_err(Into::into) }, - Order::InitiateTeleport { assets, dest, effects } => { + InitiateTeleport { assets, dest, xcm } => { // We must do this first in order to resolve wildcards. - let assets = holding.saturating_take(assets); + let assets = self.holding.saturating_take(assets); for asset in assets.assets_iter() { - Config::AssetTransactor::check_out(&origin, &asset); + Config::AssetTransactor::check_out(&dest, &asset); } let assets = Self::reanchored(assets, &dest); - Config::XcmSender::send_xcm(dest, Xcm::ReceiveTeleportedAsset { assets, effects })?; + let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) }, - Order::QueryHolding { query_id, dest, assets } => { - let assets = Self::reanchored(holding.min(&assets), &dest); - Config::XcmSender::send_xcm( - dest, - Xcm::QueryResponse { query_id, response: Response::Assets(assets) }, - )?; + QueryHolding { query_id, dest, assets, max_response_weight } => { + let assets = Self::reanchored(self.holding.min(&assets), &dest); + let max_weight = max_response_weight; + let response = Response::Assets(assets); + let instruction = QueryResponse { query_id, response, max_weight }; + Config::XcmSender::send_xcm(dest, Xcm(vec![instruction])).map_err(Into::into) }, - Order::BuyExecution { fees, weight, debt, halt_on_error, instructions } => { - // pay for `weight` using up to `fees` of the holding register. - let purchasing_weight = - Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); - let max_fee = - holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; - let unspent = trader.buy_weight(purchasing_weight, max_fee)?; - holding.subsume_assets(unspent); - - let mut remaining_weight = weight; - for instruction in instructions.into_iter() { - match Self::do_execute_xcm( - origin.clone(), - false, - instruction, - &mut remaining_weight, - None, - trader, - num_recursions + 1, - ) { - Err(e) if halt_on_error => return Err(e), - Err(_) => {}, - Ok(surplus) => total_surplus += surplus, - } - } - if let Some(w) = trader.refund_weight(remaining_weight) { - holding.subsume(w); + BuyExecution { fees, weight_limit } => { + // There is no need to buy any weight is `weight_limit` is `Unlimited` since it + // would indicate that `AllowTopLevelPaidExecutionFrom` was unused for execution + // and thus there is some other reason why it has been determined that this XCM + // should be executed. + if let Some(weight) = Option::::from(weight_limit) { + // pay for `weight` using up to `fees` of the holding register. + let max_fee = + self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; + let unspent = self.trader.buy_weight(weight, max_fee)?; + self.holding.subsume_assets(unspent); } + Ok(()) + }, + RefundSurplus => { + self.refund_surplus(); + Ok(()) }, - _ => return Err(XcmError::UnhandledEffect)?, + SetErrorHandler(mut handler) => { + let handler_weight = Config::Weigher::weight(&mut handler)?; + self.total_surplus = self.total_surplus.saturating_add(self.error_handler_weight); + self.error_handler = handler; + self.error_handler_weight = handler_weight; + Ok(()) + }, + SetAppendix(mut appendix) => { + let appendix_weight = Config::Weigher::weight(&mut appendix)?; + self.total_surplus = self.total_surplus.saturating_add(self.appendix_weight); + self.appendix = appendix; + self.appendix_weight = appendix_weight; + Ok(()) + }, + ClearError => { + self.error = None; + Ok(()) + }, + ExchangeAsset { .. } => Err(XcmError::Unimplemented), + HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented), + HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented), + HrmpChannelClosing { .. } => Err(XcmError::Unimplemented), } - Ok(total_surplus) + } + + fn reanchored(mut assets: Assets, dest: &MultiLocation) -> MultiAssets { + let inv_dest = Config::LocationInverter::invert_location(&dest); + assets.prepend_location(&inv_dest); + assets.into_assets_iter().collect::>().into() } } diff --git a/xcm/xcm-executor/src/traits/on_response.rs b/xcm/xcm-executor/src/traits/on_response.rs index 8af238e7cef2..158e448161e2 100644 --- a/xcm/xcm-executor/src/traits/on_response.rs +++ b/xcm/xcm-executor/src/traits/on_response.rs @@ -22,13 +22,23 @@ pub trait OnResponse { /// Returns `true` if we are expecting a response from `origin` for query `query_id`. fn expecting_response(origin: &MultiLocation, query_id: u64) -> bool; /// Handler for receiving a `response` from `origin` relating to `query_id`. - fn on_response(origin: MultiLocation, query_id: u64, response: Response) -> Weight; + fn on_response( + origin: &MultiLocation, + query_id: u64, + response: Response, + max_weight: Weight, + ) -> Weight; } impl OnResponse for () { fn expecting_response(_origin: &MultiLocation, _query_id: u64) -> bool { false } - fn on_response(_origin: MultiLocation, _query_id: u64, _response: Response) -> Weight { + fn on_response( + _origin: &MultiLocation, + _query_id: u64, + _response: Response, + _max_weight: Weight, + ) -> Weight { 0 } } diff --git a/xcm/xcm-executor/src/traits/should_execute.rs b/xcm/xcm-executor/src/traits/should_execute.rs index a6f7c0207b1a..53600779fc54 100644 --- a/xcm/xcm-executor/src/traits/should_execute.rs +++ b/xcm/xcm-executor/src/traits/should_execute.rs @@ -26,19 +26,18 @@ pub trait ShouldExecute { /// Returns `true` if the given `message` may be executed. /// /// - `origin`: The origin (sender) of the message. - /// - `top_level`: `true` indicates the initial XCM coming from the `origin`, `false` indicates an embedded XCM - /// executed internally as part of another message or an `Order`. + /// - `top_level`: `true` indicates the initial XCM coming from the `origin`, `false` indicates + /// an embedded XCM executed internally as part of another message or an `Order`. /// - `message`: The message itself. - /// - `shallow_weight`: The weight of the non-negotiable execution of the message. This does not include any - /// embedded XCMs sat behind mechanisms like `BuyExecution` which would need to answer for their own weight. - /// - `weight_credit`: The pre-established amount of weight that the system has determined this message may utilize - /// in its execution. Typically non-zero only because of prior fee payment, but could in principle be due to other - /// factors. + /// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message. + /// - `weight_credit`: The pre-established amount of weight that the system has determined this + /// message may utilize in its execution. Typically non-zero only because of prior fee + /// payment, but could in principle be due to other factors. fn should_execute( - origin: &MultiLocation, + origin: &Option, top_level: bool, - message: &Xcm, - shallow_weight: Weight, + message: &mut Xcm, + max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()>; } @@ -46,25 +45,25 @@ pub trait ShouldExecute { #[impl_trait_for_tuples::impl_for_tuples(30)] impl ShouldExecute for Tuple { fn should_execute( - origin: &MultiLocation, + origin: &Option, top_level: bool, - message: &Xcm, - shallow_weight: Weight, + message: &mut Xcm, + max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { for_tuples!( #( - match Tuple::should_execute(origin, top_level, message, shallow_weight, weight_credit) { + match Tuple::should_execute(origin, top_level, message, max_weight, weight_credit) { Ok(()) => return Ok(()), _ => (), } )* ); log::trace!( target: "xcm::should_execute", - "did not pass barrier: origin: {:?}, top_level: {:?}, message: {:?}, shallow_weight: {:?}, weight_credit: {:?}", + "did not pass barrier: origin: {:?}, top_level: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", origin, top_level, message, - shallow_weight, + max_weight, weight_credit, ); Err(()) diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index 80ee2de660c3..78e2180ed7a4 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -58,7 +58,7 @@ pub trait TransactAsset { /// /// When composed as a tuple, all type-items are called. It is up to the implementer that there exists no /// value for `_what` which can cause side-effects for more than one of the type-items. - fn check_out(_origin: &MultiLocation, _what: &MultiAsset) {} + fn check_out(_dest: &MultiLocation, _what: &MultiAsset) {} /// Deposit the `what` asset into the account of `who`. /// @@ -67,8 +67,8 @@ pub trait TransactAsset { Err(XcmError::Unimplemented) } - /// Withdraw the given asset from the consensus system. Return the actual asset(s) withdrawn. In - /// the case of `what` being a wildcard, this may be something more specific. + /// Withdraw the given asset from the consensus system. Return the actual asset(s) withdrawn, + /// which should always be equal to `_what`. /// /// Implementations should return `XcmError::FailedToTransactAsset` if withdraw failed. fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result { diff --git a/xcm/xcm-executor/src/traits/weight.rs b/xcm/xcm-executor/src/traits/weight.rs index 8d962c88e5ec..5d3af4f4c141 100644 --- a/xcm/xcm-executor/src/traits/weight.rs +++ b/xcm/xcm-executor/src/traits/weight.rs @@ -17,37 +17,17 @@ use crate::Assets; use frame_support::weights::Weight; use sp_std::result::Result; -use xcm::latest::{Error, MultiAsset, MultiLocation, Xcm}; +use xcm::latest::prelude::*; /// Determine the weight of an XCM message. pub trait WeightBounds { - /// Return the minimum amount of weight that an attempted execution of this message would definitely + /// Return the maximum amount of weight that an attempted execution of this message could /// consume. - /// - /// This is useful to gauge how many fees should be paid up front to begin execution of the message. - /// It is not useful for determining whether execution should begin lest it result in surpassing weight - /// limits - in that case `deep` is the function to use. - fn shallow(message: &mut Xcm) -> Result; + fn weight(message: &mut Xcm) -> Result; - /// Return the deep amount of weight, over `shallow` that complete, successful and worst-case execution of - /// `message` would incur. - /// - /// This is perhaps overly pessimistic for determining how many fees should be paid for up-front since - /// fee payment (or any other way of offsetting the execution costs such as an voucher-style NFT) may - /// happen in stages throughout execution of the XCM. - /// - /// A reminder: if it is possible that `message` may have alternative means of successful completion - /// (perhaps a conditional path), then the *worst case* weight must be reported. - /// - /// This is guaranteed equal to the eventual sum of all `shallow` XCM messages that get executed through - /// any internal effects. Inner XCM messages may be executed by: - /// - `Order::BuyExecution` - fn deep(message: &mut Xcm) -> Result; - - /// Return the total weight for executing `message`. - fn weight(message: &mut Xcm) -> Result { - Self::shallow(message)?.checked_add(Self::deep(message)?).ok_or(()) - } + /// Return the maximum amount of weight that an attempted execution of this instruction could + /// consume. + fn instr_weight(instruction: &Instruction) -> Result; } /// A means of getting approximate weight consumption for a given destination message executor and a @@ -70,7 +50,7 @@ pub trait WeightTrader: Sized { /// Purchase execution weight credit in return for up to a given `fee`. If less of the fee is required /// then the surplus is returned. If the `fee` cannot be used to pay for the `weight`, then an error is /// returned. - fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result; + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result; /// Attempt a refund of `weight` into some asset. The caller does not guarantee that the weight was /// purchased using `buy_weight`. @@ -87,7 +67,7 @@ impl WeightTrader for Tuple { for_tuples!( ( #( Tuple::new() ),* ) ) } - fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let mut last_error = None; for_tuples!( #( match Tuple.buy_weight(weight, payment.clone()) { @@ -95,7 +75,7 @@ impl WeightTrader for Tuple { Err(e) => { last_error = Some(e) } } )* ); - let last_error = last_error.unwrap_or(Error::TooExpensive); + let last_error = last_error.unwrap_or(XcmError::TooExpensive); log::trace!(target: "xcm::buy_weight", "last_error: {:?}", last_error); Err(last_error) } diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 964046d6f2f0..e93393e54d7f 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -105,19 +105,13 @@ mod tests { use super::*; use codec::Encode; - use frame_support::{assert_ok, weights::Weight}; + use frame_support::assert_ok; use xcm::latest::prelude::*; use xcm_simulator::TestExt; // Helper function for forming buy execution message - fn buy_execution(fees: impl Into, debt: Weight) -> Order { - Order::BuyExecution { - fees: fees.into(), - weight: 0, - debt, - halt_on_error: false, - instructions: vec![], - } + fn buy_execution(fees: impl Into) -> Instruction { + BuyExecution { fees: fees.into(), weight_limit: Unlimited } } #[test] @@ -131,11 +125,11 @@ mod tests { assert_ok!(RelayChainPalletXcm::send_xcm( Here, Parachain(1).into(), - Transact { + Xcm(vec![Transact { origin_type: OriginKind::SovereignAccount, require_weight_at_most: INITIAL_BALANCE as u64, call: remark.encode().into(), - }, + }]), )); }); @@ -158,11 +152,11 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm( Here, Parent.into(), - Transact { + Xcm(vec![Transact { origin_type: OriginKind::SovereignAccount, require_weight_at_most: INITIAL_BALANCE as u64, call: remark.encode().into(), - }, + }]), )); }); @@ -185,11 +179,11 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm( Here, MultiLocation::new(1, X1(Parachain(2))), - Transact { + Xcm(vec![Transact { origin_type: OriginKind::SovereignAccount, require_weight_at_most: INITIAL_BALANCE as u64, call: remark.encode().into(), - }, + }]), )); }); @@ -206,7 +200,6 @@ mod tests { MockNet::reset(); let withdraw_amount = 123; - let max_weight_for_execution = 3; Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( @@ -215,7 +208,6 @@ mod tests { Box::new(X1(AccountId32 { network: Any, id: ALICE.into() }).into().into()), Box::new((Here, withdraw_amount).into()), 0, - max_weight_for_execution, )); assert_eq!( parachain::Balances::free_balance(¶_account_id(1)), @@ -241,20 +233,17 @@ mod tests { MockNet::reset(); let send_amount = 10; - let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); ParaA::execute_with(|| { - let message = WithdrawAsset { - assets: (Here, send_amount).into(), - effects: vec![ - buy_execution((Here, send_amount), weight_for_execution), - Order::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: Parachain(2).into(), - }, - ], - }; + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Parachain(2).into(), + }, + ]); // Send withdraw and deposit assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent.into(), message.clone())); }); @@ -278,27 +267,25 @@ mod tests { MockNet::reset(); let send_amount = 10; - let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); let query_id_set = 1234; // Send a message which fully succeeds on the relay chain ParaA::execute_with(|| { - let message = WithdrawAsset { - assets: (Here, send_amount).into(), - effects: vec![ - buy_execution((Here, send_amount), weight_for_execution), - Order::DepositAsset { - assets: All.into(), - max_assets: 1, - beneficiary: Parachain(2).into(), - }, - Order::QueryHolding { - query_id: query_id_set, - dest: Parachain(1).into(), - assets: All.into(), - }, - ], - }; + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Parachain(2).into(), + }, + QueryHolding { + query_id: query_id_set, + dest: Parachain(1).into(), + assets: All.into(), + max_response_weight: 1_000_000_000, + }, + ]); // Send withdraw and deposit with query holding assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent.into(), message.clone(),)); }); @@ -318,10 +305,11 @@ mod tests { ParaA::execute_with(|| { assert_eq!( parachain::MsgQueue::received_dmp(), - vec![QueryResponse { + vec![Xcm(vec![QueryResponse { query_id: query_id_set, - response: Response::Assets(MultiAssets::new()) - }] + response: Response::Assets(MultiAssets::new()), + max_weight: 1_000_000_000, + }])], ); }); } diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 4b08b45d4dc4..8f714a30cb52 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -121,6 +121,7 @@ pub type XcmOriginToCallOrigin = ( parameter_types! { pub const UnitWeightCost: Weight = 1; pub KsmPerSecond: (AssetId, u128) = (Concrete(Parent.into()), 1); + pub const MaxInstructions: u32 = 100; } pub type LocalAssetTransactor = @@ -139,7 +140,7 @@ impl Config for XcmConfig { type IsTeleporter = (); type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; type ResponseHandler = (); } @@ -298,8 +299,10 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Nothing; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index a5816447e6f7..fa2218786d2c 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -114,6 +114,7 @@ type LocalOriginConverter = ( parameter_types! { pub const BaseXcmWeight: Weight = 1_000; pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1); + pub const MaxInstructions: u32 = 100; } pub type XcmRouter = super::RelayChainXcmRouter; @@ -129,7 +130,7 @@ impl Config for XcmConfig { type IsTeleporter = (); type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; type ResponseHandler = (); } @@ -146,8 +147,10 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; } parameter_types! { @@ -175,6 +178,6 @@ construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParasOrigin: origin::{Pallet, Origin}, ParasUmp: ump::{Pallet, Call, Storage, Event}, - XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, } ); diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 25cd15cc9e49..f932fc50deb8 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -260,7 +260,7 @@ macro_rules! decl_test_network { }, )* _ => { - return Err($crate::XcmError::CannotReachDestination(destination, message)); + return Err($crate::XcmError::Unroutable); } } } @@ -285,7 +285,7 @@ macro_rules! decl_test_network { ); }, )* - _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), + _ => return Err($crate::XcmError::Transport("Only sends to children parachain.")), } } @@ -296,7 +296,7 @@ macro_rules! decl_test_network { pub struct ParachainXcmRouter($crate::PhantomData); impl> $crate::SendXcm for ParachainXcmRouter { - fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { + fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::SendResult { use $crate::{UmpSink, XcmpMessageHandlerT}; match destination.interior() { @@ -312,7 +312,7 @@ macro_rules! decl_test_network { Ok(()) }, )* - _ => Err($crate::XcmError::CannotReachDestination(destination, message)), + _ => Err($crate::SendError::CannotReachDestination(destination, message)), } } } @@ -320,7 +320,7 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; impl $crate::SendXcm for RelayChainXcmRouter { - fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { + fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::SendResult { use $crate::DmpMessageHandlerT; match destination.interior() { @@ -331,7 +331,7 @@ macro_rules! decl_test_network { Ok(()) }, )* - _ => Err($crate::XcmError::SendFailed("Only sends to children parachain.")), + _ => Err($crate::SendError::Unroutable), } } } From 732348c063064637bdeafe099dd8b96e71ab4b92 Mon Sep 17 00:00:00 2001 From: ferrell-code Date: Thu, 26 Aug 2021 07:36:07 -0400 Subject: [PATCH 09/11] Fix Try-Runtime (#3725) * compile try-runtime * cargo fmt lol --- cli/src/command.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index c641a29f8239..64a10c9a1fd9 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -420,8 +420,10 @@ pub fn run() -> Result<()> { if chain_spec.is_kusama() { return runner.async_run(|config| { Ok(( - cmd.run::(config) - .map_err(Error::SubstrateCli), + cmd.run::( + config, + ) + .map_err(Error::SubstrateCli), task_manager, )) }) @@ -431,7 +433,7 @@ pub fn run() -> Result<()> { if chain_spec.is_westend() { return runner.async_run(|config| { Ok(( - cmd.run::( + cmd.run::( config, ) .map_err(Error::SubstrateCli), @@ -442,8 +444,10 @@ pub fn run() -> Result<()> { // else we assume it is polkadot. runner.async_run(|config| { Ok(( - cmd.run::(config) - .map_err(Error::SubstrateCli), + cmd.run::( + config, + ) + .map_err(Error::SubstrateCli), task_manager, )) }) From ccfd5b911acbbf9ee3bcf75735bf2c44e24a60c6 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Thu, 26 Aug 2021 10:34:45 -0400 Subject: [PATCH 10/11] Companion for 9619 (Private Events) (#3712) * update runtime api * use storage_prefix * update read_events_no_consensus * update Substrate Co-authored-by: parity-processbot <> --- Cargo.lock | 306 +++++++++--------- runtime/parachains/src/runtime_api_impl/v1.rs | 2 +- utils/staking-miner/src/monitor.rs | 2 +- 3 files changed, 155 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6a662a029f3..1dc406ccad2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1926,7 +1926,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", ] @@ -1944,7 +1944,7 @@ dependencies = [ [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -1963,7 +1963,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "Inflector", "chrono", @@ -1989,7 +1989,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -2002,7 +2002,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -2017,7 +2017,7 @@ dependencies = [ [[package]] name = "frame-metadata" version = "14.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "serde", @@ -2028,7 +2028,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "bitflags", "frame-metadata", @@ -2054,7 +2054,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "Inflector", "frame-support-procedural-tools", @@ -2066,7 +2066,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 1.0.0", @@ -2078,7 +2078,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro2", "quote", @@ -2088,7 +2088,7 @@ dependencies = [ [[package]] name = "frame-support-test" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-metadata", "frame-support", @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "frame-support-test-pallet" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -2119,7 +2119,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "log", @@ -2135,7 +2135,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -2149,7 +2149,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sp-api", @@ -2158,7 +2158,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "sp-api", @@ -4546,7 +4546,7 @@ checksum = "13370dae44474229701bb69b90b4f4dca6404cb0357a2d50d635f1171dc3aa7b" [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -4561,7 +4561,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -4575,7 +4575,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4598,7 +4598,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4653,7 +4653,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4731,7 +4731,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4747,7 +4747,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4762,7 +4762,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -4785,7 +4785,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4802,7 +4802,7 @@ dependencies = [ [[package]] name = "pallet-gilt" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4816,7 +4816,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4838,7 +4838,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "enumflags2", "frame-benchmarking", @@ -4853,7 +4853,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4872,7 +4872,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4888,7 +4888,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4903,7 +4903,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "ckb-merkle-mountain-range", "frame-benchmarking", @@ -4920,7 +4920,7 @@ dependencies = [ [[package]] name = "pallet-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -4936,7 +4936,7 @@ dependencies = [ [[package]] name = "pallet-mmr-rpc" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -4953,7 +4953,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -4967,7 +4967,7 @@ dependencies = [ [[package]] name = "pallet-nicks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -4980,7 +4980,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -4996,7 +4996,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5018,7 +5018,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5032,7 +5032,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -5045,7 +5045,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5060,7 +5060,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -5080,7 +5080,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5096,7 +5096,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -5109,7 +5109,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5131,7 +5131,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", @@ -5142,7 +5142,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "sp-arithmetic", @@ -5151,7 +5151,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -5164,7 +5164,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5181,7 +5181,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5196,7 +5196,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-support", "frame-system", @@ -5212,7 +5212,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -5229,7 +5229,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -5240,7 +5240,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5256,7 +5256,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -5271,7 +5271,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-benchmarking", "frame-support", @@ -7797,7 +7797,7 @@ dependencies = [ [[package]] name = "remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "env_logger 0.9.0", "jsonrpsee-proc-macros", @@ -8068,7 +8068,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "sp-core", @@ -8079,7 +8079,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "derive_more", @@ -8106,7 +8106,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "futures-timer 3.0.2", @@ -8129,7 +8129,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -8145,7 +8145,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -8161,7 +8161,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", @@ -8172,7 +8172,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "chrono", "fdlimit", @@ -8210,7 +8210,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "fnv", "futures 0.3.16", @@ -8238,7 +8238,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "hash-db", "kvdb", @@ -8263,7 +8263,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "futures 0.3.16", @@ -8287,7 +8287,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "derive_more", @@ -8330,7 +8330,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "derive_more", "futures 0.3.16", @@ -8354,7 +8354,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "fork-tree", "parity-scale-codec", @@ -8367,7 +8367,7 @@ dependencies = [ [[package]] name = "sc-consensus-manual-seal" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "assert_matches", "async-trait", @@ -8401,7 +8401,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "futures 0.3.16", @@ -8427,7 +8427,7 @@ dependencies = [ [[package]] name = "sc-consensus-uncles" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "sc-client-api", "sp-authorship", @@ -8438,7 +8438,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "lazy_static", "libsecp256k1 0.6.0", @@ -8464,7 +8464,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "derive_more", "parity-scale-codec", @@ -8481,7 +8481,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "parity-scale-codec", @@ -8497,7 +8497,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "cfg-if 1.0.0", "libc", @@ -8516,7 +8516,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "derive_more", @@ -8553,7 +8553,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "derive_more", "finality-grandpa", @@ -8577,7 +8577,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "ansi_term 0.12.1", "futures 0.3.16", @@ -8594,7 +8594,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "derive_more", @@ -8609,7 +8609,7 @@ dependencies = [ [[package]] name = "sc-light" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "hash-db", "parity-scale-codec", @@ -8627,7 +8627,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-std", "async-trait", @@ -8678,7 +8678,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "futures-timer 3.0.2", @@ -8694,7 +8694,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "bytes 1.0.1", "fnv", @@ -8721,7 +8721,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "libp2p", @@ -8734,7 +8734,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.9.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -8743,7 +8743,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "hash-db", @@ -8774,7 +8774,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "derive_more", "futures 0.3.16", @@ -8799,7 +8799,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "jsonrpc-core", @@ -8815,7 +8815,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "directories", @@ -8879,7 +8879,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "parity-scale-codec", @@ -8893,7 +8893,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -8915,7 +8915,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "chrono", "futures 0.3.16", @@ -8933,7 +8933,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "ansi_term 0.12.1", "atty", @@ -8964,7 +8964,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", @@ -8975,7 +8975,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "intervalier", @@ -9002,7 +9002,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "derive_more", "futures 0.3.16", @@ -9436,7 +9436,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "hash-db", "log", @@ -9453,7 +9453,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "blake2-rfc", "proc-macro-crate 1.0.0", @@ -9465,7 +9465,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "serde", @@ -9477,7 +9477,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "integer-sqrt", "num-traits", @@ -9491,7 +9491,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sp-api", @@ -9503,7 +9503,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "parity-scale-codec", @@ -9515,7 +9515,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sp-api", @@ -9527,7 +9527,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "log", @@ -9545,7 +9545,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "futures 0.3.16", @@ -9564,7 +9564,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "merlin", @@ -9586,7 +9586,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sp-arithmetic", @@ -9596,7 +9596,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "schnorrkel", @@ -9608,7 +9608,7 @@ dependencies = [ [[package]] name = "sp-core" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "base58", "blake2-rfc", @@ -9652,7 +9652,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "kvdb", "parking_lot 0.11.1", @@ -9661,7 +9661,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro2", "quote", @@ -9671,7 +9671,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "environmental", "parity-scale-codec", @@ -9682,7 +9682,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "finality-grandpa", "log", @@ -9699,7 +9699,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -9713,7 +9713,7 @@ dependencies = [ [[package]] name = "sp-io" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "hash-db", @@ -9738,7 +9738,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "lazy_static", "sp-core", @@ -9749,7 +9749,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "derive_more", @@ -9766,7 +9766,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "ruzstd", "zstd", @@ -9775,7 +9775,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "serde", @@ -9788,7 +9788,7 @@ dependencies = [ [[package]] name = "sp-npos-elections-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", @@ -9799,7 +9799,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "sp-api", "sp-core", @@ -9809,7 +9809,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "backtrace", ] @@ -9817,7 +9817,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "rustc-hash", "serde", @@ -9827,7 +9827,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "either", "hash256-std-hasher", @@ -9848,7 +9848,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -9865,7 +9865,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "Inflector", "proc-macro-crate 1.0.0", @@ -9877,7 +9877,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "serde", "serde_json", @@ -9886,7 +9886,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sp-api", @@ -9899,7 +9899,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "sp-runtime", @@ -9909,7 +9909,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "hash-db", "log", @@ -9932,12 +9932,12 @@ dependencies = [ [[package]] name = "sp-std" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" [[package]] name = "sp-storage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9950,7 +9950,7 @@ dependencies = [ [[package]] name = "sp-tasks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "sp-core", @@ -9963,7 +9963,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "futures-timer 3.0.2", @@ -9979,7 +9979,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "erased-serde", "log", @@ -9997,7 +9997,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "sp-api", "sp-runtime", @@ -10006,7 +10006,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "log", @@ -10021,7 +10021,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "hash-db", "memory-db", @@ -10035,7 +10035,7 @@ dependencies = [ [[package]] name = "sp-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "futures-timer 3.0.2", @@ -10046,7 +10046,7 @@ dependencies = [ [[package]] name = "sp-version" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "impl-serde", "parity-scale-codec", @@ -10061,7 +10061,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -10072,7 +10072,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -10270,7 +10270,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "platforms", ] @@ -10278,7 +10278,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-system-rpc-runtime-api", "futures 0.3.16", @@ -10300,7 +10300,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.9.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-std", "derive_more", @@ -10314,7 +10314,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "async-trait", "futures 0.3.16", @@ -10341,7 +10341,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "futures 0.3.16", "substrate-test-utils-derive", @@ -10351,7 +10351,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", @@ -10362,7 +10362,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "ansi_term 0.12.1", "build-helper", @@ -10517,7 +10517,7 @@ dependencies = [ [[package]] name = "test-runner" version = "0.9.0" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "frame-system", "futures 0.3.16", @@ -10924,7 +10924,7 @@ checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#b7a1a2cda5c860f18a7802051e881c5062828042" +source = "git+https://github.com/paritytech/substrate?branch=master#69e5b50896ff743f4725cb9747750c4093963a66" dependencies = [ "log", "parity-scale-codec", diff --git a/runtime/parachains/src/runtime_api_impl/v1.rs b/runtime/parachains/src/runtime_api_impl/v1.rs index 3dd0be084886..58afabc83d9c 100644 --- a/runtime/parachains/src/runtime_api_impl/v1.rs +++ b/runtime/parachains/src/runtime_api_impl/v1.rs @@ -289,7 +289,7 @@ where { use inclusion::Event as RawEvent; - >::events() + >::read_events_no_consensus() .into_iter() .filter_map(|record| extract_event(record.event)) .map(|event| match event { diff --git a/utils/staking-miner/src/monitor.rs b/utils/staking-miner/src/monitor.rs index f14054426f80..d6ea12401d82 100644 --- a/utils/staking-miner/src/monitor.rs +++ b/utils/staking-miner/src/monitor.rs @@ -148,7 +148,7 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! { TransactionStatus::Ready | TransactionStatus::Broadcast(_) | TransactionStatus::Future => continue, TransactionStatus::InBlock(hash) => { log::info!(target: LOG_TARGET, "included at {:?}", hash); - let key = sp_core::storage::StorageKey(frame_system::Events::::hashed_key().to_vec()); + let key = frame_support::storage::storage_prefix(b"System", b"Events"); let events =get_storage::< Vec::Hash>> >(client, params!{ key, hash }).await?.unwrap_or_default(); From 948e0f1e3e9b307f7f778b0d08f6354d4604e871 Mon Sep 17 00:00:00 2001 From: Chevdor Date: Fri, 27 Aug 2021 11:29:17 +0200 Subject: [PATCH 11/11] staking-miner: remove need of a file to pass the seed (#3680) * staking-miner: remove need of a file to pass the seed * cleanup * linting round * fix linting * fix naming and remove unused field --- .gitignore | 1 + utils/staking-miner/src/dry_run.rs | 6 ++++-- utils/staking-miner/src/main.rs | 16 ++++++++-------- utils/staking-miner/src/prelude.rs | 4 ++-- utils/staking-miner/src/rpc_helpers.rs | 4 ++-- utils/staking-miner/src/signer.rs | 24 +++++++++--------------- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index a654e51fd7c8..5ea0458ddfc8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ polkadot.* !.rpm/* .DS_Store .cargo +.env diff --git a/utils/staking-miner/src/dry_run.rs b/utils/staking-miner/src/dry_run.rs index 79190793f9e9..236d63ce4cc1 100644 --- a/utils/staking-miner/src/dry_run.rs +++ b/utils/staking-miner/src/dry_run.rs @@ -61,7 +61,7 @@ async fn print_info( let snapshot_size = >::snapshot_metadata().expect("snapshot must exist by now; qed."); - let deposit = EPM::Pallet::::deposit_for(&raw_solution, snapshot_size); + let deposit = EPM::Pallet::::deposit_for(raw_solution, snapshot_size); log::info!( target: LOG_TARGET, "solution score {:?} / deposit {:?} / length {:?}", @@ -80,7 +80,9 @@ async fn print_info( log::info!( target: LOG_TARGET, "payment_queryInfo: (fee = {}) {:?}", - info.as_ref().map(|d| Token::from(d.partial_fee)).unwrap_or(Token::from(0)), + info.as_ref() + .map(|d| Token::from(d.partial_fee)) + .unwrap_or_else(|_| Token::from(0)), info, ); } diff --git a/utils/staking-miner/src/main.rs b/utils/staking-miner/src/main.rs index 2aabe722150d..1769382e62af 100644 --- a/utils/staking-miner/src/main.rs +++ b/utils/staking-miner/src/main.rs @@ -276,15 +276,15 @@ struct DryRunConfig { #[derive(Debug, Clone, StructOpt)] struct SharedConfig { /// The `ws` node to connect to. - #[structopt(long, short, default_value = DEFAULT_URI)] + #[structopt(long, short, default_value = DEFAULT_URI, env = "URI")] uri: String, - /// The file from which we read the account seed. + /// The seed of a funded account in hex. /// - /// WARNING: don't use an account with a large stash for this. Based on how the bot is - /// configured, it might re-try lose funds through transaction fees/deposits. - #[structopt(long, short)] - account_seed: std::path::PathBuf, + /// WARNING: Don't use an account with a large stash for this. Based on how the bot is + /// configured, it might re-try and lose funds through transaction fees/deposits. + #[structopt(long, short, env = "SEED")] + seed: String, } #[derive(Debug, Clone, StructOpt)] @@ -354,7 +354,7 @@ fn mine_dpos(ext: &mut Ext) -> Result<(), Error> { let desired_targets = EPM::DesiredTargets::::get().unwrap(); let mut candidates_and_backing = BTreeMap::::new(); voters.into_iter().for_each(|(who, stake, targets)| { - if targets.len() == 0 { + if targets.is_empty() { println!("target = {:?}", (who, stake, targets)); return } @@ -491,7 +491,7 @@ async fn main() { }; let signer_account = any_runtime! { - signer::read_signer_uri::<_, Runtime>(&shared.account_seed, &client) + signer::signer_uri_from_string::(&shared.seed, &client) .await .expect("Provided account is invalid, terminating.") }; diff --git a/utils/staking-miner/src/prelude.rs b/utils/staking-miner/src/prelude.rs index e3c25887af2d..db9ce4e2c1e2 100644 --- a/utils/staking-miner/src/prelude.rs +++ b/utils/staking-miner/src/prelude.rs @@ -34,9 +34,9 @@ pub type Hash = core_primitives::Hash; pub use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; /// Default URI to connect to. -pub const DEFAULT_URI: &'static str = "wss://rpc.polkadot.io"; +pub const DEFAULT_URI: &str = "wss://rpc.polkadot.io"; /// The logging target. -pub const LOG_TARGET: &'static str = "staking-miner"; +pub const LOG_TARGET: &str = "staking-miner"; /// The election provider pallet. pub use pallet_election_provider_multi_phase as EPM; diff --git a/utils/staking-miner/src/rpc_helpers.rs b/utils/staking-miner/src/rpc_helpers.rs index e759917adee6..cc086b1235b1 100644 --- a/utils/staking-miner/src/rpc_helpers.rs +++ b/utils/staking-miner/src/rpc_helpers.rs @@ -82,7 +82,7 @@ where Hash: serde::Serialize, { let key = >::hashed_key(); - get_storage::(&client, params! { key, maybe_at }).await + get_storage::(client, params! { key, maybe_at }).await } #[allow(unused)] @@ -103,5 +103,5 @@ where Hash: serde::Serialize, { let key = >::hashed_key_for(key); - get_storage::(&client, params! { key, maybe_at }).await + get_storage::(client, params! { key, maybe_at }).await } diff --git a/utils/staking-miner/src/signer.rs b/utils/staking-miner/src/signer.rs index 9ced41a93e72..8dd2696b3309 100644 --- a/utils/staking-miner/src/signer.rs +++ b/utils/staking-miner/src/signer.rs @@ -18,9 +18,8 @@ use crate::{prelude::*, rpc_helpers, AccountId, Error, Index, Pair, WsClient, LOG_TARGET}; use sp_core::crypto::Pair as _; -use std::path::Path; -pub(crate) const SIGNER_ACCOUNT_WILL_EXIST: &'static str = +pub(crate) const SIGNER_ACCOUNT_WILL_EXIST: &str = "signer account is checked to exist upon startup; it can only die if it transfers funds out \ of it, or get slashed. If it does not exist at this point, it is likely due to a bug, or the \ signer got slashed. Terminating."; @@ -30,10 +29,9 @@ pub(crate) const SIGNER_ACCOUNT_WILL_EXIST: &'static str = pub(crate) struct Signer { /// The account id. pub(crate) account: AccountId, + /// The full crypto key-pair. pub(crate) pair: Pair, - /// The raw URI read from file. - pub(crate) uri: String, } pub(crate) async fn get_account_info( @@ -51,26 +49,22 @@ pub(crate) async fn get_account_info( .await } -/// Read the signer account's URI from the given `path`. -pub(crate) async fn read_signer_uri< - P: AsRef, +/// Read the signer account's URI +pub(crate) async fn signer_uri_from_string< T: frame_system::Config< AccountId = AccountId, Index = Index, AccountData = pallet_balances::AccountData, >, >( - path: P, + seed: &str, client: &WsClient, ) -> Result { - let uri = std::fs::read_to_string(path)?; - - // trim any trailing garbage. - let uri = uri.trim_end(); + let seed = seed.trim(); - let pair = Pair::from_string(&uri, None)?; + let pair = Pair::from_string(seed, None)?; let account = T::AccountId::from(pair.public()); - let _info = get_account_info::(&client, &account, None) + let _info = get_account_info::(client, &account, None) .await? .ok_or(Error::AccountDoesNotExists)?; log::info!( @@ -80,5 +74,5 @@ pub(crate) async fn read_signer_uri< Token::from(_info.data.free), _info ); - Ok(Signer { account, pair, uri: uri.to_string() }) + Ok(Signer { account, pair }) }