BridgeHub[Rococo/Wococo] test batched relaying of messages and their dispatch (#2578)

Added some tests that aim to cover the runtime configuration
that is exercised when BH receives relayed complex message.

* checks correct importing of proofs for:
  bridged chain finality, bridged para heads, bridged messages,
* checks relayer extension correctly configured to reward
  submitting relayer,
* checks relayed message is successfully dispatched.

Also moved generic test-utils from
  `asset-test-utils: parachains/runtimes/assets/test-utils`
one level up to new crate
  `parachains-runtimes-test-utils: parachains/runtimes/test-utils`
to be reused by BridgeHubs.

Signed-off-by: acatangiu <adrian@parity.io>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
This commit is contained in:
Adrian Catangiu
2023-05-18 18:10:36 +03:00
committed by GitHub
parent 93cf3908e0
commit 4e4799927a
21 changed files with 1723 additions and 980 deletions
-1
View File
@@ -8,5 +8,4 @@ polkadot_argument_parsing
**/chains/ **/chains/
*.iml *.iml
.env .env
bin
**/._* **/._*
+52
View File
@@ -394,6 +394,7 @@ dependencies = [
"pallet-xcm", "pallet-xcm",
"parachain-info", "parachain-info",
"parachains-common", "parachains-common",
"parachains-runtimes-test-utils",
"parity-scale-codec", "parity-scale-codec",
"polkadot-parachain", "polkadot-parachain",
"sp-consensus-aura", "sp-consensus-aura",
@@ -914,13 +915,18 @@ name = "bp-test-utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bp-header-chain", "bp-header-chain",
"bp-parachains",
"bp-polkadot-core",
"bp-runtime",
"ed25519-dalek", "ed25519-dalek",
"finality-grandpa", "finality-grandpa",
"parity-scale-codec", "parity-scale-codec",
"sp-application-crypto", "sp-application-crypto",
"sp-consensus-grandpa", "sp-consensus-grandpa",
"sp-core",
"sp-runtime", "sp-runtime",
"sp-std", "sp-std",
"sp-trie",
] ]
[[package]] [[package]]
@@ -1126,6 +1132,7 @@ dependencies = [
"sp-core", "sp-core",
"sp-inherents", "sp-inherents",
"sp-io", "sp-io",
"sp-keyring",
"sp-offchain", "sp-offchain",
"sp-runtime", "sp-runtime",
"sp-session", "sp-session",
@@ -1144,28 +1151,40 @@ name = "bridge-hub-test-utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"asset-test-utils", "asset-test-utils",
"bp-bridge-hub-rococo",
"bp-bridge-hub-wococo",
"bp-header-chain", "bp-header-chain",
"bp-messages", "bp-messages",
"bp-parachains",
"bp-polkadot-core", "bp-polkadot-core",
"bp-relayers",
"bp-runtime", "bp-runtime",
"bp-test-utils", "bp-test-utils",
"bridge-runtime-common", "bridge-runtime-common",
"cumulus-pallet-dmp-queue", "cumulus-pallet-dmp-queue",
"cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system",
"cumulus-pallet-xcmp-queue", "cumulus-pallet-xcmp-queue",
"frame-benchmarking",
"frame-executive",
"frame-support", "frame-support",
"frame-system", "frame-system",
"log", "log",
"pallet-balances", "pallet-balances",
"pallet-bridge-grandpa", "pallet-bridge-grandpa",
"pallet-bridge-messages", "pallet-bridge-messages",
"pallet-bridge-parachains",
"pallet-bridge-relayers",
"pallet-collator-selection", "pallet-collator-selection",
"pallet-session", "pallet-session",
"pallet-utility",
"pallet-xcm", "pallet-xcm",
"pallet-xcm-benchmarks", "pallet-xcm-benchmarks",
"parachain-info", "parachain-info",
"parachains-runtimes-test-utils",
"parity-scale-codec", "parity-scale-codec",
"sp-core",
"sp-io", "sp-io",
"sp-keyring",
"sp-runtime", "sp-runtime",
"xcm", "xcm",
"xcm-builder", "xcm-builder",
@@ -8051,6 +8070,39 @@ dependencies = [
"xcm-executor", "xcm-executor",
] ]
[[package]]
name = "parachains-runtimes-test-utils"
version = "1.0.0"
dependencies = [
"assets-common",
"cumulus-pallet-dmp-queue",
"cumulus-pallet-parachain-system",
"cumulus-pallet-xcmp-queue",
"cumulus-primitives-core",
"cumulus-primitives-parachain-inherent",
"cumulus-test-relay-sproof-builder",
"frame-support",
"frame-system",
"hex-literal 0.3.4",
"pallet-assets",
"pallet-balances",
"pallet-collator-selection",
"pallet-session",
"pallet-xcm",
"parachain-info",
"parachains-common",
"parity-scale-codec",
"polkadot-parachain",
"sp-consensus-aura",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
"substrate-wasm-builder",
"xcm",
"xcm-executor",
]
[[package]] [[package]]
name = "parity-db" name = "parity-db"
version = "0.4.8" version = "0.4.8"
@@ -28,12 +28,12 @@ pub mod messages;
pub mod messages_api; pub mod messages_api;
pub mod messages_benchmarking; pub mod messages_benchmarking;
pub mod messages_call_ext; pub mod messages_call_ext;
pub mod messages_generation;
pub mod messages_xcm_extension; pub mod messages_xcm_extension;
pub mod parachains_benchmarking; pub mod parachains_benchmarking;
pub mod priority_calculator; pub mod priority_calculator;
pub mod refund_relayer_extension; pub mod refund_relayer_extension;
mod messages_generation;
mod mock; mod mock;
#[cfg(feature = "integrity-test")] #[cfg(feature = "integrity-test")]
@@ -16,8 +16,6 @@
//! Helpers for generating message storage proofs, that are used by tests and by benchmarks. //! Helpers for generating message storage proofs, that are used by tests and by benchmarks.
#![cfg(any(feature = "runtime-benchmarks", test))]
use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge}; use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge};
use bp_messages::{ use bp_messages::{
@@ -29,19 +27,19 @@ use sp_std::{ops::RangeInclusive, prelude::*};
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
/// Simple and correct message data encode function. /// Simple and correct message data encode function.
pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option<Vec<u8>> { pub fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option<Vec<u8>> {
Some(m.encode()) Some(m.encode())
} }
/// Simple and correct outbound lane data encode function. /// Simple and correct outbound lane data encode function.
pub(crate) fn encode_lane_data(d: &OutboundLaneData) -> Vec<u8> { pub fn encode_lane_data(d: &OutboundLaneData) -> Vec<u8> {
d.encode() d.encode()
} }
/// Prepare storage proof of given messages. /// Prepare storage proof of given messages.
/// ///
/// Returns state trie root and nodes with prepared messages. /// Returns state trie root and nodes with prepared messages.
pub(crate) fn prepare_messages_storage_proof<B>( pub fn prepare_messages_storage_proof<B>(
lane: LaneId, lane: LaneId,
message_nonces: RangeInclusive<MessageNonce>, message_nonces: RangeInclusive<MessageNonce>,
outbound_lane_data: Option<OutboundLaneData>, outbound_lane_data: Option<OutboundLaneData>,
+41 -55
View File
@@ -701,16 +701,17 @@ pub(crate) mod tests {
use crate::mock::{ use crate::mock::{
run_test, test_relay_header, BigParachainHeader, RegularParachainHasher, run_test, test_relay_header, BigParachainHeader, RegularParachainHasher,
RegularParachainHeader, RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, RegularParachainHeader, RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime,
PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, UNTRACKED_PARACHAIN_ID,
}; };
use bp_test_utils::prepare_parachain_heads_proof;
use codec::Encode; use codec::Encode;
use bp_parachains::{ use bp_parachains::{
BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider, BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider,
}; };
use bp_runtime::{ use bp_runtime::{
record_all_trie_keys, BasicOperatingMode, OwnedBridgeModuleError, BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider,
StorageDoubleMapKeyProvider, StorageMapKeyProvider, StorageMapKeyProvider,
}; };
use bp_test_utils::{ use bp_test_utils::{
authority_list, generate_owned_bridge_module_tests, make_default_justification, authority_list, generate_owned_bridge_module_tests, make_default_justification,
@@ -725,7 +726,6 @@ pub(crate) mod tests {
use frame_system::{EventRecord, Pallet as System, Phase}; use frame_system::{EventRecord, Pallet as System, Phase};
use sp_core::Hasher; use sp_core::Hasher;
use sp_runtime::{traits::Header as HeaderT, DispatchError}; use sp_runtime::{traits::Header as HeaderT, DispatchError};
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1;
type WeightInfo = <TestRuntime as Config>::WeightInfo; type WeightInfo = <TestRuntime as Config>::WeightInfo;
@@ -768,32 +768,6 @@ pub(crate) mod tests {
hash hash
} }
pub(crate) fn prepare_parachain_heads_proof(
heads: Vec<(u32, ParaHead)>,
) -> (RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) {
let mut parachains = Vec::with_capacity(heads.len());
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie = TrieDBMutBuilderV1::<RelayBlockHasher>::new(&mut mdb, &mut root).build();
for (parachain, head) in heads {
let storage_key =
parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain));
trie.insert(&storage_key.0, &head.encode())
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in tests");
parachains.push((ParaId(parachain), head.hash()));
}
}
// generate storage proof to be delivered to This chain
let storage_proof = record_all_trie_keys::<LayoutV1<RelayBlockHasher>, _>(&mdb, &root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");
(root, ParaHeadsProof(storage_proof), parachains)
}
fn initial_best_head(parachain: u32) -> ParaInfo { fn initial_best_head(parachain: u32) -> ParaInfo {
ParaInfo { ParaInfo {
best_head_hash: BestParaHeadHash { best_head_hash: BestParaHeadHash {
@@ -875,7 +849,7 @@ pub(crate) mod tests {
#[test] #[test]
fn submit_parachain_heads_checks_operating_mode() { fn submit_parachain_heads_checks_operating_mode() {
let (state_root, proof, parachains) = let (state_root, proof, parachains) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
run_test(|| { run_test(|| {
initialize(state_root); initialize(state_root);
@@ -906,7 +880,10 @@ pub(crate) mod tests {
#[test] #[test]
fn imports_initial_parachain_heads() { fn imports_initial_parachain_heads() {
let (state_root, proof, parachains) = let (state_root, proof, parachains) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
(1, head_data(1, 0)),
(3, head_data(3, 10)),
]);
run_test(|| { run_test(|| {
initialize(state_root); initialize(state_root);
@@ -985,9 +962,9 @@ pub(crate) mod tests {
#[test] #[test]
fn imports_parachain_heads_is_able_to_progress() { fn imports_parachain_heads_is_able_to_progress() {
let (state_root_5, proof_5, parachains_5) = let (state_root_5, proof_5, parachains_5) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
let (state_root_10, proof_10, parachains_10) = let (state_root_10, proof_10, parachains_10) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
run_test(|| { run_test(|| {
// start with relay block #0 and import head#5 of parachain#1 // start with relay block #0 and import head#5 of parachain#1
initialize(state_root_5); initialize(state_root_5);
@@ -1083,11 +1060,12 @@ pub(crate) mod tests {
#[test] #[test]
fn ignores_untracked_parachain() { fn ignores_untracked_parachain() {
let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![ let (state_root, proof, parachains) =
(1, head_data(1, 5)), prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
(UNTRACKED_PARACHAIN_ID, head_data(1, 5)), (1, head_data(1, 5)),
(2, head_data(1, 5)), (UNTRACKED_PARACHAIN_ID, head_data(1, 5)),
]); (2, head_data(1, 5)),
]);
run_test(|| { run_test(|| {
// start with relay block #0 and try to import head#5 of parachain#1 and untracked // start with relay block #0 and try to import head#5 of parachain#1 and untracked
// parachain // parachain
@@ -1160,7 +1138,7 @@ pub(crate) mod tests {
#[test] #[test]
fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { fn does_nothing_when_already_imported_this_head_at_previous_relay_header() {
let (state_root, proof, parachains) = let (state_root, proof, parachains) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
run_test(|| { run_test(|| {
// import head#0 of parachain#1 at relay block#0 // import head#0 of parachain#1 at relay block#0
initialize(state_root); initialize(state_root);
@@ -1220,9 +1198,9 @@ pub(crate) mod tests {
#[test] #[test]
fn does_nothing_when_already_imported_head_at_better_relay_header() { fn does_nothing_when_already_imported_head_at_better_relay_header() {
let (state_root_5, proof_5, parachains_5) = let (state_root_5, proof_5, parachains_5) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
let (state_root_10, proof_10, parachains_10) = let (state_root_10, proof_10, parachains_10) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
run_test(|| { run_test(|| {
// start with relay block #0 // start with relay block #0
initialize(state_root_5); initialize(state_root_5);
@@ -1314,7 +1292,10 @@ pub(crate) mod tests {
#[test] #[test]
fn does_nothing_when_parachain_head_is_too_large() { fn does_nothing_when_parachain_head_is_too_large() {
let (state_root, proof, parachains) = let (state_root, proof, parachains) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 5)), (4, big_head_data(1, 5))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
(1, head_data(1, 5)),
(4, big_head_data(1, 5)),
]);
run_test(|| { run_test(|| {
// start with relay block #0 and try to import head#5 of parachain#1 and big parachain // start with relay block #0 and try to import head#5 of parachain#1 and big parachain
initialize(state_root); initialize(state_root);
@@ -1368,8 +1349,9 @@ pub(crate) mod tests {
// import exactly `HeadsToKeep` headers // import exactly `HeadsToKeep` headers
for i in 0..heads_to_keep { for i in 0..heads_to_keep {
let (state_root, proof, parachains) = let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); RegularParachainHeader,
>(vec![(1, head_data(1, i))]);
if i == 0 { if i == 0 {
initialize(state_root); initialize(state_root);
} else { } else {
@@ -1389,8 +1371,9 @@ pub(crate) mod tests {
} }
// import next relay chain header and next parachain head // import next relay chain header and next parachain head
let (state_root, proof, parachains) = let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); RegularParachainHeader,
>(vec![(1, head_data(1, heads_to_keep))]);
proceed(heads_to_keep, state_root); proceed(heads_to_keep, state_root);
let expected_weight = weight_of_import_parachain_1_head(&proof, true); let expected_weight = weight_of_import_parachain_1_head(&proof, true);
let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof);
@@ -1411,7 +1394,7 @@ pub(crate) mod tests {
#[test] #[test]
fn fails_on_unknown_relay_chain_block() { fn fails_on_unknown_relay_chain_block() {
let (state_root, proof, parachains) = let (state_root, proof, parachains) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
run_test(|| { run_test(|| {
// start with relay block #0 // start with relay block #0
initialize(state_root); initialize(state_root);
@@ -1427,7 +1410,7 @@ pub(crate) mod tests {
#[test] #[test]
fn fails_on_invalid_storage_proof() { fn fails_on_invalid_storage_proof() {
let (_state_root, proof, parachains) = let (_state_root, proof, parachains) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
run_test(|| { run_test(|| {
// start with relay block #0 // start with relay block #0
initialize(Default::default()); initialize(Default::default());
@@ -1445,11 +1428,11 @@ pub(crate) mod tests {
#[test] #[test]
fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() {
let (state_root_5, proof_5, parachains_5) = let (state_root_5, proof_5, parachains_5) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) = let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) =
prepare_parachain_heads_proof(vec![(2, head_data(2, 10))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 10))]);
let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) = let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) =
prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
run_test(|| { run_test(|| {
// we've already imported head#5 of parachain#1 at relay block#10 // we've already imported head#5 of parachain#1 at relay block#10
initialize(state_root_5); initialize(state_root_5);
@@ -1517,7 +1500,8 @@ pub(crate) mod tests {
#[test] #[test]
fn ignores_parachain_head_if_it_is_missing_from_storage_proof() { fn ignores_parachain_head_if_it_is_missing_from_storage_proof() {
let (state_root, proof, _) = prepare_parachain_heads_proof(vec![]); let (state_root, proof, _) =
prepare_parachain_heads_proof::<RegularParachainHeader>(vec![]);
let parachains = vec![(ParaId(2), Default::default())]; let parachains = vec![(ParaId(2), Default::default())];
run_test(|| { run_test(|| {
initialize(state_root); initialize(state_root);
@@ -1542,7 +1526,8 @@ pub(crate) mod tests {
#[test] #[test]
fn ignores_parachain_head_if_parachain_head_hash_is_wrong() { fn ignores_parachain_head_if_parachain_head_hash_is_wrong() {
let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); let (state_root, proof, _) =
prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
let parachains = vec![(ParaId(1), head_data(1, 10).hash())]; let parachains = vec![(ParaId(1), head_data(1, 10).hash())];
run_test(|| { run_test(|| {
initialize(state_root); initialize(state_root);
@@ -1569,7 +1554,8 @@ pub(crate) mod tests {
#[test] #[test]
fn test_bridge_parachain_call_is_correctly_defined() { fn test_bridge_parachain_call_is_correctly_defined() {
let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); let (state_root, proof, _) =
prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
let parachains = vec![(ParaId(2), Default::default())]; let parachains = vec![(ParaId(2), Default::default())];
let relay_header_id = (0, test_relay_header(0, state_root).hash()); let relay_header_id = (0, test_relay_header(0, state_root).hash());
@@ -7,23 +7,30 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies] [dependencies]
bp-header-chain = { path = "../header-chain", default-features = false } bp-header-chain = { path = "../header-chain", default-features = false }
bp-parachains = { path = "../parachains", default-features = false }
bp-polkadot-core = { path = "../polkadot-core", default-features = false }
bp-runtime = { path = "../runtime", default-features = false }
codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false }
ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] } ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] }
finality-grandpa = { version = "0.16.2", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false }
sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[features] [features]
default = ["std"] default = ["std"]
std = [ std = [
"bp-header-chain/std", "bp-header-chain/std",
"bp-polkadot-core/std",
"codec/std", "codec/std",
"ed25519-dalek/std", "ed25519-dalek/std",
"finality-grandpa/std", "finality-grandpa/std",
"sp-application-crypto/std", "sp-application-crypto/std",
"sp-consensus-grandpa/std", "sp-consensus-grandpa/std",
"sp-core/std",
"sp-runtime/std", "sp-runtime/std",
"sp-std/std", "sp-std/std",
] ]
@@ -19,10 +19,14 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification}; use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification};
use bp_parachains::parachain_head_storage_key_at_source;
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
use bp_runtime::record_all_trie_keys;
use codec::Encode; use codec::Encode;
use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId};
use sp_runtime::traits::{Header as HeaderT, One, Zero}; use sp_runtime::traits::{Header as HeaderT, One, Zero};
use sp_std::prelude::*; use sp_std::prelude::*;
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
// Re-export all our test account utilities // Re-export all our test account utilities
pub use keyring::*; pub use keyring::*;
@@ -31,6 +35,7 @@ mod keyring;
pub const TEST_GRANDPA_ROUND: u64 = 1; pub const TEST_GRANDPA_ROUND: u64 = 1;
pub const TEST_GRANDPA_SET_ID: SetId = 1; pub const TEST_GRANDPA_SET_ID: SetId = 1;
pub const PARAS_PALLET_NAME: &str = "Paras";
/// Configuration parameters when generating test GRANDPA justifications. /// Configuration parameters when generating test GRANDPA justifications.
#[derive(Clone)] #[derive(Clone)]
@@ -161,6 +166,33 @@ fn generate_chain<H: HeaderT>(fork_id: u32, depth: u32, ancestor: &H) -> Vec<H>
headers headers
} }
/// Make valid proof for parachain `heads`
pub fn prepare_parachain_heads_proof<H: HeaderT>(
heads: Vec<(u32, ParaHead)>,
) -> (H::Hash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) {
let mut parachains = Vec::with_capacity(heads.len());
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie = TrieDBMutBuilderV1::<H::Hashing>::new(&mut mdb, &mut root).build();
for (parachain, head) in heads {
let storage_key =
parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain));
trie.insert(&storage_key.0, &head.encode())
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in tests");
parachains.push((ParaId(parachain), head.hash()));
}
}
// generate storage proof to be delivered to This chain
let storage_proof = record_all_trie_keys::<LayoutV1<H::Hashing>, _>(&mdb, &root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");
(root, ParaHeadsProof(storage_proof), parachains)
}
/// Create signed precommit with given target. /// Create signed precommit with given target.
pub fn signed_precommit<H: HeaderT>( pub fn signed_precommit<H: HeaderT>(
signer: &Account, signer: &Account,
@@ -207,6 +239,15 @@ pub fn test_header<H: HeaderT>(number: H::Number) -> H {
header header
} }
/// Get a header for testing with given `state_root`.
///
/// The correct parent hash will be used if given a non-zero header.
pub fn test_header_with_root<H: HeaderT>(number: H::Number, state_root: H::Hash) -> H {
let mut header: H = test_header(number);
header.set_state_root(state_root);
header
}
/// Convenience function for generating a Header ID at a given block number. /// Convenience function for generating a Header ID at a given block number.
pub fn header_id<H: HeaderT>(index: u8) -> (H::Hash, H::Number) { pub fn header_id<H: HeaderT>(index: u8) -> (H::Hash, H::Number) {
(test_header::<H>(index.into()).hash(), index.into()) (test_header::<H>(index.into()).hash(), index.into())
@@ -1,4 +1,4 @@
use asset_test_utils::{ExtBuilder, RuntimeHelper}; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use cumulus_primitives_utility::ChargeWeightInFungibles; use cumulus_primitives_utility::ChargeWeightInFungibles;
use frame_support::{ use frame_support::{
@@ -26,6 +26,14 @@ const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32];
type AssetIdForTrustBackedAssetsConvert = type AssetIdForTrustBackedAssetsConvert =
assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>; assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>;
fn collator_session_keys() -> CollatorSessionKeys<Runtime> {
CollatorSessionKeys::new(
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
)
}
#[test] #[test]
fn test_asset_xcm_trader() { fn test_asset_xcm_trader() {
ExtBuilder::<Runtime>::default() ExtBuilder::<Runtime>::default()
@@ -472,11 +480,7 @@ asset_test_utils::include_teleports_for_native_asset_works!(
CheckingAccount, CheckingAccount,
WeightToFee, WeightToFee,
ParachainSystem, ParachainSystem,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -501,11 +505,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!(
ParachainSystem, ParachainSystem,
ForeignCreatorsSovereignAccountOf, ForeignCreatorsSovereignAccountOf,
ForeignAssetsInstance, ForeignAssetsInstance,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -524,11 +524,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!(
asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!(
Runtime, Runtime,
XcmConfig, XcmConfig,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|| { Box::new(|| {
assert!(Assets::asset_ids().collect::<Vec<_>>().is_empty()); assert!(Assets::asset_ids().collect::<Vec<_>>().is_empty());
@@ -547,11 +543,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_
TrustBackedAssetsInstance, TrustBackedAssetsInstance,
AssetIdForTrustBackedAssets, AssetIdForTrustBackedAssets,
AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssetsConvert,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
12345, 12345,
Box::new(|| { Box::new(|| {
@@ -569,11 +561,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_
ForeignAssetsInstance, ForeignAssetsInstance,
MultiLocation, MultiLocation,
JustTry, JustTry,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) }, MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) },
Box::new(|| { Box::new(|| {
@@ -592,11 +580,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p
ForeignAssetsInstance, ForeignAssetsInstance,
MultiLocation, MultiLocation,
JustTry, JustTry,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
AssetDeposit::get(), AssetDeposit::get(),
MetadataDepositBase::get(), MetadataDepositBase::get(),
@@ -1,4 +1,4 @@
use asset_test_utils::{ExtBuilder, RuntimeHelper}; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper};
use codec::Decode; use codec::Decode;
use cumulus_primitives_utility::ChargeWeightInFungibles; use cumulus_primitives_utility::ChargeWeightInFungibles;
use frame_support::{ use frame_support::{
@@ -26,6 +26,14 @@ const ALICE: [u8; 32] = [1u8; 32];
type AssetIdForTrustBackedAssetsConvert = type AssetIdForTrustBackedAssetsConvert =
assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>; assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>;
fn collator_session_keys() -> CollatorSessionKeys<Runtime> {
CollatorSessionKeys::new(
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) },
)
}
#[test] #[test]
fn test_asset_xcm_trader() { fn test_asset_xcm_trader() {
ExtBuilder::<Runtime>::default() ExtBuilder::<Runtime>::default()
@@ -450,11 +458,7 @@ asset_test_utils::include_teleports_for_native_asset_works!(
CheckingAccount, CheckingAccount,
WeightToFee, WeightToFee,
ParachainSystem, ParachainSystem,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -474,11 +478,7 @@ asset_test_utils::include_teleports_for_native_asset_works!(
asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!(
Runtime, Runtime,
XcmConfig, XcmConfig,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|| { Box::new(|| {
assert!(Assets::asset_ids().collect::<Vec<_>>().is_empty()); assert!(Assets::asset_ids().collect::<Vec<_>>().is_empty());
@@ -495,11 +495,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_
TrustBackedAssetsInstance, TrustBackedAssetsInstance,
AssetIdForTrustBackedAssets, AssetIdForTrustBackedAssets,
AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssetsConvert,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
12345, 12345,
Box::new(|| {}), Box::new(|| {}),
@@ -31,6 +31,7 @@ cumulus-primitives-core = { path = "../../../../primitives/core", default-featur
cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false }
cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false }
parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false }
parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false }
# Polkadot # Polkadot
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
@@ -61,6 +62,7 @@ std = [
"assets-common/std", "assets-common/std",
"parachains-common/std", "parachains-common/std",
"parachain-info/std", "parachain-info/std",
"parachains-runtimes-test-utils/std",
"polkadot-parachain/std", "polkadot-parachain/std",
"sp-consensus-aura/std", "sp-consensus-aura/std",
"sp-io/std", "sp-io/std",
@@ -1,411 +1,20 @@
use sp_std::marker::PhantomData; // Copyright Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData}; // Cumulus is free software: you can redistribute it and/or modify
use cumulus_primitives_parachain_inherent::ParachainInherentData; // it under the terms of the GNU General Public License as published by
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; // the Free Software Foundation, either version 3 of the License, or
use frame_support::{ // (at your option) any later version.
dispatch::{DispatchResult, RawOrigin, UnfilteredDispatchable},
inherent::{InherentData, ProvideInherent}, // Cumulus is distributed in the hope that it will be useful,
traits::{GenesisBuild, OriginTrait}, // but WITHOUT ANY WARRANTY; without even the implied warranty of
weights::Weight, // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
}; // GNU General Public License for more details.
use parachains_common::AccountId;
use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber}; // You should have received a copy of the GNU General Public License
use sp_consensus_aura::AURA_ENGINE_ID; // along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
use sp_core::Encode;
use sp_runtime::{Digest, DigestItem}; //! Module contains predefined test-case scenarios for `Runtime` with various assets.
use xcm::{
latest::{MultiAsset, MultiLocation, XcmContext, XcmHash},
prelude::*,
};
use xcm_executor::{traits::TransactAsset, Assets};
pub mod test_cases; pub mod test_cases;
pub use test_cases::CollatorSessionKeys; pub use parachains_runtimes_test_utils::*;
pub type BalanceOf<Runtime> = <Runtime as pallet_balances::Config>::Balance;
pub type AccountIdOf<Runtime> = <Runtime as frame_system::Config>::AccountId;
pub type ValidatorIdOf<Runtime> = <Runtime as pallet_session::Config>::ValidatorId;
pub type SessionKeysOf<Runtime> = <Runtime as pallet_session::Config>::Keys;
// Basic builder based on balances, collators and pallet_sessopm
pub struct ExtBuilder<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> {
// endowed accounts with balances
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
// collators to test block prod
collators: Vec<AccountIdOf<Runtime>>,
// keys added to pallet session
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
// safe xcm version for pallet_xcm
safe_xcm_version: Option<XcmVersion>,
// para id
para_id: Option<ParaId>,
_runtime: PhantomData<Runtime>,
}
impl<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> Default for ExtBuilder<Runtime>
{
fn default() -> ExtBuilder<Runtime> {
ExtBuilder {
balances: vec![],
collators: vec![],
keys: vec![],
safe_xcm_version: None,
para_id: None,
_runtime: PhantomData,
}
}
}
impl<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> ExtBuilder<Runtime>
{
pub fn with_balances(
mut self,
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
) -> Self {
self.balances = balances;
self
}
pub fn with_collators(mut self, collators: Vec<AccountIdOf<Runtime>>) -> Self {
self.collators = collators;
self
}
pub fn with_session_keys(
mut self,
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
) -> Self {
self.keys = keys;
self
}
pub fn with_tracing(self) -> Self {
frame_support::sp_tracing::try_init_simple();
self
}
pub fn with_safe_xcm_version(mut self, safe_xcm_version: XcmVersion) -> Self {
self.safe_xcm_version = Some(safe_xcm_version);
self
}
pub fn with_para_id(mut self, para_id: ParaId) -> Self {
self.para_id = Some(para_id);
self
}
pub fn build(self) -> sp_io::TestExternalities
where
Runtime:
pallet_collator_selection::Config + pallet_balances::Config + pallet_session::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
{
let mut t = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
<pallet_xcm::GenesisConfig as GenesisBuild<Runtime>>::assimilate_storage(
&pallet_xcm::GenesisConfig { safe_xcm_version: self.safe_xcm_version },
&mut t,
)
.unwrap();
if let Some(para_id) = self.para_id {
<parachain_info::GenesisConfig as frame_support::traits::GenesisBuild<Runtime>>::assimilate_storage(
&parachain_info::GenesisConfig { parachain_id: para_id },
&mut t,
)
.unwrap();
}
pallet_balances::GenesisConfig::<Runtime> { balances: self.balances }
.assimilate_storage(&mut t)
.unwrap();
pallet_collator_selection::GenesisConfig::<Runtime> {
invulnerables: self.collators.clone(),
candidacy_bond: Default::default(),
desired_candidates: Default::default(),
}
.assimilate_storage(&mut t)
.unwrap();
pallet_session::GenesisConfig::<Runtime> { keys: self.keys }
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
frame_system::Pallet::<Runtime>::set_block_number(1u32.into());
});
ext
}
}
pub struct RuntimeHelper<Runtime>(PhantomData<Runtime>);
/// Utility function that advances the chain to the desired block number.
/// If an author is provided, that author information is injected to all the blocks in the meantime.
impl<Runtime: frame_system::Config> RuntimeHelper<Runtime>
where
AccountIdOf<Runtime>:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
{
pub fn run_to_block(n: u32, author: Option<AccountId>) {
while frame_system::Pallet::<Runtime>::block_number() < n.into() {
// Set the new block number and author
match author {
Some(ref author) => {
let pre_digest = Digest {
logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, author.encode())],
};
frame_system::Pallet::<Runtime>::reset_events();
frame_system::Pallet::<Runtime>::initialize(
&(frame_system::Pallet::<Runtime>::block_number() + 1u32.into()),
&frame_system::Pallet::<Runtime>::parent_hash(),
&pre_digest,
);
},
None => {
frame_system::Pallet::<Runtime>::set_block_number(
frame_system::Pallet::<Runtime>::block_number() + 1u32.into(),
);
},
}
}
}
pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
<Runtime as frame_system::Config>::RuntimeOrigin::root()
}
pub fn origin_of(
account_id: AccountIdOf<Runtime>,
) -> <Runtime as frame_system::Config>::RuntimeOrigin {
<Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id.into())
}
}
impl<XcmConfig: xcm_executor::Config> RuntimeHelper<XcmConfig> {
pub fn do_transfer(
from: MultiLocation,
to: MultiLocation,
(asset, amount): (MultiLocation, u128),
) -> Result<Assets, XcmError> {
<XcmConfig::AssetTransactor as TransactAsset>::transfer_asset(
&MultiAsset { id: Concrete(asset), fun: Fungible(amount) },
&from,
&to,
// We aren't able to track the XCM that initiated the fee deposit, so we create a
// fake message hash here
&XcmContext::with_message_hash([0; 32]),
)
}
}
impl<Runtime: pallet_xcm::Config + cumulus_pallet_parachain_system::Config> RuntimeHelper<Runtime> {
pub fn do_teleport_assets<HrmpChannelOpener>(
origin: <Runtime as frame_system::Config>::RuntimeOrigin,
dest: MultiLocation,
beneficiary: MultiLocation,
(asset, amount): (MultiLocation, u128),
open_hrmp_channel: Option<(u32, u32)>,
) -> DispatchResult
where
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
{
// open hrmp (if needed)
if let Some((source_para_id, target_para_id)) = open_hrmp_channel {
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
source_para_id.into(),
target_para_id.into(),
);
}
// do teleport
<pallet_xcm::Pallet<Runtime>>::teleport_assets(
origin,
Box::new(dest.into()),
Box::new(beneficiary.into()),
Box::new((Concrete(asset), amount).into()),
0,
)
}
}
impl<Runtime: cumulus_pallet_dmp_queue::Config + cumulus_pallet_parachain_system::Config>
RuntimeHelper<Runtime>
{
pub fn execute_as_governance(call: Vec<u8>, require_weight_at_most: Weight) -> Outcome {
// prepare xcm as governance will do
let xcm = Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
Transact {
origin_kind: OriginKind::Superuser,
require_weight_at_most,
call: call.into(),
},
]);
// execute xcm as parent origin
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
<<Runtime as cumulus_pallet_dmp_queue::Config>::XcmExecutor>::execute_xcm(
MultiLocation::parent(),
xcm,
hash,
Self::xcm_max_weight(XcmReceivedFrom::Parent),
)
}
}
pub enum XcmReceivedFrom {
Parent,
Sibling,
}
impl<ParachainSystem: cumulus_pallet_parachain_system::Config> RuntimeHelper<ParachainSystem> {
pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight {
use frame_support::traits::Get;
match from {
XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(),
XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(),
}
}
}
impl<Runtime: frame_system::Config + pallet_xcm::Config> RuntimeHelper<Runtime> {
pub fn assert_pallet_xcm_event_outcome(
unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
assert_outcome: fn(Outcome),
) {
let outcome = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_pallet_xcm_event(e.event.encode()))
.find_map(|e| match e {
pallet_xcm::Event::Attempted(outcome) => Some(outcome),
_ => None,
})
.expect("No `pallet_xcm::Event::Attempted(outcome)` event found!");
assert_outcome(outcome);
}
}
impl<Runtime: frame_system::Config + cumulus_pallet_xcmp_queue::Config> RuntimeHelper<Runtime> {
pub fn xcmp_queue_message_sent(
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
) -> Option<XcmHash> {
<frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
.find_map(|e| match e {
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => message_hash,
_ => None,
})
}
}
pub fn assert_metadata<Fungibles, AccountId>(
asset_id: impl Into<Fungibles::AssetId> + Copy,
expected_name: &str,
expected_symbol: &str,
expected_decimals: u8,
) where
Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect<AccountId>
+ frame_support::traits::tokens::fungibles::Inspect<AccountId>,
{
assert_eq!(Fungibles::name(asset_id.into()), Vec::from(expected_name),);
assert_eq!(Fungibles::symbol(asset_id.into()), Vec::from(expected_symbol),);
assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals);
}
pub fn assert_total<Fungibles, AccountId>(
asset_id: impl Into<Fungibles::AssetId> + Copy,
expected_total_issuance: impl Into<Fungibles::Balance>,
expected_active_issuance: impl Into<Fungibles::Balance>,
) where
Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect<AccountId>
+ frame_support::traits::tokens::fungibles::Inspect<AccountId>,
{
assert_eq!(Fungibles::total_issuance(asset_id.into()), expected_total_issuance.into());
assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into());
}
/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass
pub fn mock_open_hrmp_channel<
C: cumulus_pallet_parachain_system::Config,
T: ProvideInherent<Call = cumulus_pallet_parachain_system::Call<C>>,
>(
sender: ParaId,
recipient: ParaId,
) {
let n = 1_u32;
let mut sproof_builder = RelayStateSproofBuilder {
para_id: sender,
hrmp_egress_channel_index: Some(vec![recipient]),
..Default::default()
};
sproof_builder.hrmp_channels.insert(
HrmpChannelId { sender, recipient },
AbridgedHrmpChannel {
max_capacity: 10,
max_total_size: 10_000_000_u32,
max_message_size: 10_000_000_u32,
msg_count: 0,
total_size: 0_u32,
mqc_head: None,
},
);
let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof();
let vfp = PersistedValidationData {
relay_parent_number: n as RelayChainBlockNumber,
relay_parent_storage_root,
..Default::default()
};
// It is insufficient to push the validation function params
// to storage; they must also be included in the inherent data.
let inherent_data = {
let mut inherent_data = InherentData::default();
let system_inherent_data = ParachainInherentData {
validation_data: vfp,
relay_chain_state,
downward_messages: Default::default(),
horizontal_messages: Default::default(),
};
inherent_data
.put_data(
cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER,
&system_inherent_data,
)
.expect("failed to put VFP inherent");
inherent_data
};
// execute the block
T::create_inherent(&inherent_data)
.expect("got an inherent")
.dispatch_bypass_filter(RawOrigin::None.into())
.expect("dispatch succeeded");
}
@@ -15,10 +15,6 @@
//! Module contains predefined test-case scenarios for `Runtime` with various assets. //! Module contains predefined test-case scenarios for `Runtime` with various assets.
use crate::{
assert_metadata, assert_total, AccountIdOf, BalanceOf, ExtBuilder, RuntimeHelper,
SessionKeysOf, ValidatorIdOf, XcmReceivedFrom,
};
use codec::Encode; use codec::Encode;
use frame_support::{ use frame_support::{
assert_noop, assert_ok, assert_noop, assert_ok,
@@ -26,6 +22,10 @@ use frame_support::{
weights::Weight, weights::Weight,
}; };
use parachains_common::Balance; use parachains_common::Balance;
use parachains_runtimes_test_utils::{
assert_metadata, assert_total, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder,
RuntimeHelper, ValidatorIdOf, XcmReceivedFrom,
};
use sp_runtime::{ use sp_runtime::{
traits::{StaticLookup, Zero}, traits::{StaticLookup, Zero},
DispatchError, Saturating, DispatchError, Saturating,
@@ -33,35 +33,6 @@ use sp_runtime::{
use xcm::latest::prelude::*; use xcm::latest::prelude::*;
use xcm_executor::{traits::Convert, XcmExecutor}; use xcm_executor::{traits::Convert, XcmExecutor};
pub struct CollatorSessionKeys<
Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config,
> {
collator: AccountIdOf<Runtime>,
validator: ValidatorIdOf<Runtime>,
key: SessionKeysOf<Runtime>,
}
impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config>
CollatorSessionKeys<Runtime>
{
pub fn new(
collator: AccountIdOf<Runtime>,
validator: ValidatorIdOf<Runtime>,
key: SessionKeysOf<Runtime>,
) -> Self {
Self { collator, validator, key }
}
pub fn collators(&self) -> Vec<AccountIdOf<Runtime>> {
vec![self.collator.clone()]
}
pub fn session_keys(
&self,
) -> Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)> {
vec![(self.collator.clone(), self.validator.clone(), self.key.clone())]
}
}
/// Test-case makes sure that `Runtime` can receive native asset from relay chain /// Test-case makes sure that `Runtime` can receive native asset from relay chain
/// and can teleport it back and to the other parachains /// and can teleport it back and to the other parachains
pub fn teleports_for_native_asset_works< pub fn teleports_for_native_asset_works<
@@ -1,4 +1,4 @@
use asset_test_utils::{ExtBuilder, RuntimeHelper, XcmReceivedFrom}; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper, XcmReceivedFrom};
use codec::{Decode, DecodeLimit, Encode}; use codec::{Decode, DecodeLimit, Encode};
use cumulus_primitives_utility::ChargeWeightInFungibles; use cumulus_primitives_utility::ChargeWeightInFungibles;
use frame_support::{ use frame_support::{
@@ -33,6 +33,14 @@ const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32];
type AssetIdForTrustBackedAssetsConvert = type AssetIdForTrustBackedAssetsConvert =
assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>; assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>;
fn collator_session_keys() -> CollatorSessionKeys<Runtime> {
CollatorSessionKeys::new(
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
)
}
#[test] #[test]
fn test_asset_xcm_trader() { fn test_asset_xcm_trader() {
ExtBuilder::<Runtime>::default() ExtBuilder::<Runtime>::default()
@@ -477,11 +485,7 @@ asset_test_utils::include_teleports_for_native_asset_works!(
CheckingAccount, CheckingAccount,
WeightToFee, WeightToFee,
ParachainSystem, ParachainSystem,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -506,11 +510,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!(
ParachainSystem, ParachainSystem,
ForeignCreatorsSovereignAccountOf, ForeignCreatorsSovereignAccountOf,
ForeignAssetsInstance, ForeignAssetsInstance,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -529,11 +529,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!(
asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!(
Runtime, Runtime,
XcmConfig, XcmConfig,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|| { Box::new(|| {
assert!(Assets::asset_ids().collect::<Vec<_>>().is_empty()); assert!(Assets::asset_ids().collect::<Vec<_>>().is_empty());
@@ -552,11 +548,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_
TrustBackedAssetsInstance, TrustBackedAssetsInstance,
AssetIdForTrustBackedAssets, AssetIdForTrustBackedAssets,
AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssetsConvert,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
12345, 12345,
Box::new(|| { Box::new(|| {
@@ -574,11 +566,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_
ForeignAssetsInstance, ForeignAssetsInstance,
MultiLocation, MultiLocation,
JustTry, JustTry,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) }, MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) },
Box::new(|| { Box::new(|| {
@@ -597,11 +585,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p
ForeignAssetsInstance, ForeignAssetsInstance,
MultiLocation, MultiLocation,
JustTry, JustTry,
asset_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
AssetDeposit::get(), AssetDeposit::get(),
MetadataDepositBase::get(), MetadataDepositBase::get(),
@@ -91,6 +91,7 @@ bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", defau
static_assertions = "1.1" static_assertions = "1.1"
bridge-hub-test-utils = { path = "../test-utils"} bridge-hub-test-utils = { path = "../test-utils"}
bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", features = ["integrity-test"] } bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", features = ["integrity-test"] }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features] [features]
default = [ default = [
@@ -119,6 +120,7 @@ std = [
"cumulus-primitives-core/std", "cumulus-primitives-core/std",
"cumulus-primitives-timestamp/std", "cumulus-primitives-timestamp/std",
"cumulus-primitives-utility/std", "cumulus-primitives-utility/std",
"frame-benchmarking/std",
"frame-executive/std", "frame-executive/std",
"frame-support/std", "frame-support/std",
"frame-system-rpc-runtime-api/std", "frame-system-rpc-runtime-api/std",
@@ -122,9 +122,6 @@ pub type SignedExtra = (
pub type UncheckedExtrinsic = pub type UncheckedExtrinsic =
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>; generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, RuntimeCall, SignedExtra>;
/// Executive: handles dispatch to the various modules. /// Executive: handles dispatch to the various modules.
pub type Executive = frame_executive::Executive< pub type Executive = frame_executive::Executive<
Runtime, Runtime,
@@ -14,32 +14,94 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>. // along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
pub use bridge_hub_rococo_runtime::{ #![cfg(test)]
use bp_polkadot_core::Signature;
use bridge_hub_rococo_runtime::{
bridge_hub_rococo_config, bridge_hub_wococo_config,
constants::fee::WeightToFee, constants::fee::WeightToFee,
xcm_config::{RelayNetwork, XcmConfig, XcmRouter}, xcm_config::{RelayNetwork, XcmConfig},
Balances, BridgeGrandpaRococoInstance, BridgeGrandpaWococoInstance, BridgeWococoMessages, BridgeRejectObsoleteHeadersAndMessages, DeliveryRewardInBalance, Executive, ExistentialDeposit,
DeliveryRewardInBalance, ExistentialDeposit, ParachainSystem, PolkadotXcm, ParachainSystem, PolkadotXcm, RequiredStakeForStakeAndSlash, Runtime, RuntimeCall,
RequiredStakeForStakeAndSlash, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, RuntimeEvent, SessionKeys, SignedExtra, UncheckedExtrinsic,
}; };
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use xcm::latest::prelude::*;
use bridge_hub_rococo_runtime::{
bridge_hub_rococo_config, bridge_hub_wococo_config, WithBridgeHubRococoMessagesInstance,
WithBridgeHubWococoMessagesInstance,
};
use frame_support::parameter_types; use frame_support::parameter_types;
use parachains_common::{AccountId, AuraId, Balance}; use parachains_common::{AccountId, AuraId, Balance};
use sp_keyring::AccountKeyring::Alice;
use sp_runtime::{
generic::{Era, SignedPayload},
AccountId32,
};
use xcm::latest::prelude::*;
const ALICE: [u8; 32] = [1u8; 32]; // Para id of sibling chain (Rockmine/Wockmint) used in tests.
pub const SIBLING_PARACHAIN_ID: u32 = 1000;
parameter_types! { parameter_types! {
pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub CheckingAccount: AccountId = PolkadotXcm::check_account();
} }
fn construct_extrinsic(
sender: sp_keyring::AccountKeyring,
call: RuntimeCall,
) -> UncheckedExtrinsic {
let extra: SignedExtra = (
frame_system::CheckNonZeroSender::<Runtime>::new(),
frame_system::CheckSpecVersion::<Runtime>::new(),
frame_system::CheckTxVersion::<Runtime>::new(),
frame_system::CheckGenesis::<Runtime>::new(),
frame_system::CheckEra::<Runtime>::from(Era::immortal()),
frame_system::CheckNonce::<Runtime>::from(0),
frame_system::CheckWeight::<Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
BridgeRejectObsoleteHeadersAndMessages {},
(
bridge_hub_wococo_config::BridgeRefundBridgeHubRococoMessages::default(),
bridge_hub_rococo_config::BridgeRefundBridgeHubWococoMessages::default(),
),
);
let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap();
let signature = payload.using_encoded(|e| sender.sign(e));
UncheckedExtrinsic::new_signed(
call,
AccountId32::from(sender.public()).into(),
Signature::Sr25519(signature.clone()),
extra,
)
}
fn construct_and_apply_extrinsic(
relayer_at_target: sp_keyring::AccountKeyring,
batch: pallet_utility::Call<Runtime>,
) -> sp_runtime::DispatchOutcome {
let batch_call = RuntimeCall::Utility(batch);
let xt = construct_extrinsic(relayer_at_target, batch_call);
let r = Executive::apply_extrinsic(xt);
r.unwrap()
}
fn executive_init_block(header: &<Runtime as frame_system::Config>::Header) {
Executive::initialize_block(header)
}
fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
bridge_hub_test_utils::CollatorSessionKeys::new(
AccountId::from(Alice),
AccountId::from(Alice),
SessionKeys { aura: AuraId::from(Alice.public()) },
)
}
mod bridge_hub_rococo_tests { mod bridge_hub_rococo_tests {
use super::*; use super::*;
use bridge_hub_rococo_config::{
WithBridgeHubWococoMessageBridge, DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO,
};
use bridge_hub_rococo_runtime::{
BridgeGrandpaWococoInstance, BridgeParachainWococoInstance,
WithBridgeHubWococoMessagesInstance,
};
bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!(
Runtime, Runtime,
@@ -47,11 +109,7 @@ mod bridge_hub_rococo_tests {
CheckingAccount, CheckingAccount,
WeightToFee, WeightToFee,
ParachainSystem, ParachainSystem,
bridge_hub_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -68,101 +126,157 @@ mod bridge_hub_rococo_tests {
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID
); );
bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( #[test]
Runtime, fn initialize_bridge_by_governance_works() {
BridgeGrandpaWococoInstance, bridge_hub_test_utils::test_cases::initialize_bridge_by_governance_works::<
bridge_hub_test_utils::CollatorSessionKeys::new( Runtime,
AccountId::from(ALICE), BridgeGrandpaWococoInstance,
AccountId::from(ALICE), >(
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } collator_session_keys(),
), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()),
Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()) )
); }
bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( #[test]
change_delivery_reward_by_governance_works, fn change_delivery_reward_by_governance_works() {
Runtime, bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::<
bridge_hub_test_utils::CollatorSessionKeys::new( Runtime,
AccountId::from(ALICE), DeliveryRewardInBalance,
AccountId::from(ALICE), u64,
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } >(
), collator_session_keys(),
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
Box::new(|call| RuntimeCall::System(call).encode()), Box::new(|call| RuntimeCall::System(call).encode()),
(DeliveryRewardInBalance, u64), || (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()),
|| (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()), |old_value| old_value.checked_mul(2).unwrap(),
|old_value| old_value.checked_mul(2).unwrap() )
); }
bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( #[test]
change_required_stake_by_governance_works, fn change_required_stake_by_governance_works() {
Runtime, bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::<
bridge_hub_test_utils::CollatorSessionKeys::new( Runtime,
AccountId::from(ALICE), RequiredStakeForStakeAndSlash,
AccountId::from(ALICE), Balance,
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } >(
), collator_session_keys(),
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
Box::new(|call| RuntimeCall::System(call).encode()), Box::new(|call| RuntimeCall::System(call).encode()),
(RequiredStakeForStakeAndSlash, Balance), || {
|| (RequiredStakeForStakeAndSlash::key().to_vec(), RequiredStakeForStakeAndSlash::get()), (
|old_value| old_value.checked_mul(2).unwrap() RequiredStakeForStakeAndSlash::key().to_vec(),
); RequiredStakeForStakeAndSlash::get(),
)
},
|old_value| old_value.checked_mul(2).unwrap(),
)
}
bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( #[test]
Runtime, fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() {
XcmConfig, bridge_hub_test_utils::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::<
WithBridgeHubWococoMessagesInstance, Runtime,
bridge_hub_test_utils::CollatorSessionKeys::new( XcmConfig,
AccountId::from(ALICE), WithBridgeHubWococoMessagesInstance,
AccountId::from(ALICE), >(
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } collator_session_keys(),
), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID,
1000, Box::new(|runtime_event_encoded: Vec<u8>| {
Box::new(|runtime_event_encoded: Vec<u8>| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event),
Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event), _ => None,
_ => None, }
} }),
}), || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) },
|| ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO
bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO )
); }
bridge_hub_test_utils::include_message_dispatch_routing_works!( #[test]
Runtime, fn message_dispatch_routing_works() {
XcmConfig, bridge_hub_test_utils::test_cases::message_dispatch_routing_works::<
ParachainSystem, Runtime,
WithBridgeHubWococoMessagesInstance, XcmConfig,
RelayNetwork, ParachainSystem,
bridge_hub_rococo_config::WococoGlobalConsensusNetwork, WithBridgeHubWococoMessagesInstance,
bridge_hub_test_utils::CollatorSessionKeys::new( RelayNetwork,
AccountId::from(ALICE), bridge_hub_rococo_config::WococoGlobalConsensusNetwork,
AccountId::from(ALICE), >(
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } collator_session_keys(),
), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID,
1000, Box::new(|runtime_event_encoded: Vec<u8>| {
Box::new(|runtime_event_encoded: Vec<u8>| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::ParachainSystem(event)) => Some(event),
Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), _ => None,
_ => None, }
} }),
}), Box::new(|runtime_event_encoded: Vec<u8>| {
Box::new(|runtime_event_encoded: Vec<u8>| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), _ => None,
_ => None, }
} }),
}), bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO,
bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO )
); }
#[test]
fn relayed_incoming_message_works() {
bridge_hub_test_utils::test_cases::relayed_incoming_message_works::<
Runtime,
XcmConfig,
ParachainSystem,
BridgeGrandpaWococoInstance,
BridgeParachainWococoInstance,
WithBridgeHubWococoMessagesInstance,
WithBridgeHubWococoMessageBridge,
>(
collator_session_keys(),
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
SIBLING_PARACHAIN_ID,
Rococo,
DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO,
)
}
#[test]
pub fn complex_relay_extrinsic_works() {
bridge_hub_test_utils::test_cases::complex_relay_extrinsic_works::<
Runtime,
XcmConfig,
ParachainSystem,
BridgeGrandpaWococoInstance,
BridgeParachainWococoInstance,
WithBridgeHubWococoMessagesInstance,
WithBridgeHubWococoMessageBridge,
>(
collator_session_keys(),
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
SIBLING_PARACHAIN_ID,
bridge_hub_rococo_config::BridgeHubWococoChainId::get(),
Rococo,
DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO,
ExistentialDeposit::get(),
executive_init_block,
construct_and_apply_extrinsic,
);
}
} }
mod bridge_hub_wococo_tests { mod bridge_hub_wococo_tests {
use super::*; use super::*;
use bridge_hub_rococo_runtime::{
BridgeGrandpaRococoInstance, BridgeParachainRococoInstance,
WithBridgeHubRococoMessagesInstance,
};
use bridge_hub_wococo_config::{
WithBridgeHubRococoMessageBridge, DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO,
};
bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!(
Runtime, Runtime,
@@ -170,11 +284,7 @@ mod bridge_hub_wococo_tests {
CheckingAccount, CheckingAccount,
WeightToFee, WeightToFee,
ParachainSystem, ParachainSystem,
bridge_hub_test_utils::CollatorSessionKeys::new( collator_session_keys(),
AccountId::from(ALICE),
AccountId::from(ALICE),
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }
),
ExistentialDeposit::get(), ExistentialDeposit::get(),
Box::new(|runtime_event_encoded: Vec<u8>| { Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
@@ -191,95 +301,144 @@ mod bridge_hub_wococo_tests {
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID
); );
bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( #[test]
Runtime, fn initialize_bridge_by_governance_works() {
BridgeGrandpaRococoInstance, bridge_hub_test_utils::test_cases::initialize_bridge_by_governance_works::<
bridge_hub_test_utils::CollatorSessionKeys::new( Runtime,
AccountId::from(ALICE), BridgeGrandpaRococoInstance,
AccountId::from(ALICE), >(
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } collator_session_keys(),
), bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()),
Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()) )
); }
bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( #[test]
change_delivery_reward_by_governance_works, fn change_delivery_reward_by_governance_works() {
Runtime, bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::<
bridge_hub_test_utils::CollatorSessionKeys::new( Runtime,
AccountId::from(ALICE), DeliveryRewardInBalance,
AccountId::from(ALICE), u64,
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } >(
), collator_session_keys(),
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
Box::new(|call| RuntimeCall::System(call).encode()), Box::new(|call| RuntimeCall::System(call).encode()),
(DeliveryRewardInBalance, u64), || (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()),
|| (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()), |old_value| old_value.checked_mul(2).unwrap(),
|old_value| old_value.checked_mul(2).unwrap() )
); }
bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( #[test]
change_required_stake_by_governance_works, fn change_required_stake_by_governance_works() {
Runtime, bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::<
bridge_hub_test_utils::CollatorSessionKeys::new( Runtime,
AccountId::from(ALICE), RequiredStakeForStakeAndSlash,
AccountId::from(ALICE), Balance,
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } >(
), collator_session_keys(),
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
Box::new(|call| RuntimeCall::System(call).encode()), Box::new(|call| RuntimeCall::System(call).encode()),
(RequiredStakeForStakeAndSlash, Balance), || {
|| (RequiredStakeForStakeAndSlash::key().to_vec(), RequiredStakeForStakeAndSlash::get()), (
|old_value| old_value.checked_mul(2).unwrap() RequiredStakeForStakeAndSlash::key().to_vec(),
); RequiredStakeForStakeAndSlash::get(),
)
},
|old_value| old_value.checked_mul(2).unwrap(),
)
}
bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( #[test]
Runtime, fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() {
XcmConfig, bridge_hub_test_utils::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::<
WithBridgeHubRococoMessagesInstance, Runtime,
bridge_hub_test_utils::CollatorSessionKeys::new( XcmConfig,
AccountId::from(ALICE), WithBridgeHubRococoMessagesInstance,
AccountId::from(ALICE), >(
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } collator_session_keys(),
), bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID,
1000, Box::new(|runtime_event_encoded: Vec<u8>| {
Box::new(|runtime_event_encoded: Vec<u8>| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::BridgeRococoMessages(event)) => Some(event),
Ok(RuntimeEvent::BridgeRococoMessages(event)) => Some(event), _ => None,
_ => None, }
} }),
}), || ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) },
|| ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO
bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO )
); }
bridge_hub_test_utils::include_message_dispatch_routing_works!( #[test]
Runtime, fn message_dispatch_routing_works() {
XcmConfig, bridge_hub_test_utils::test_cases::message_dispatch_routing_works::<
ParachainSystem, Runtime,
WithBridgeHubRococoMessagesInstance, XcmConfig,
RelayNetwork, ParachainSystem,
bridge_hub_wococo_config::RococoGlobalConsensusNetwork, WithBridgeHubRococoMessagesInstance,
bridge_hub_test_utils::CollatorSessionKeys::new( RelayNetwork,
AccountId::from(ALICE), bridge_hub_wococo_config::RococoGlobalConsensusNetwork,
AccountId::from(ALICE), >(
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } collator_session_keys(),
), bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID,
1000, Box::new(|runtime_event_encoded: Vec<u8>| {
Box::new(|runtime_event_encoded: Vec<u8>| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::ParachainSystem(event)) => Some(event),
Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), _ => None,
_ => None, }
} }),
}), Box::new(|runtime_event_encoded: Vec<u8>| {
Box::new(|runtime_event_encoded: Vec<u8>| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), _ => None,
_ => None, }
} }),
}), bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO,
bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO )
); }
#[test]
fn relayed_incoming_message_works() {
bridge_hub_test_utils::test_cases::relayed_incoming_message_works::<
Runtime,
XcmConfig,
ParachainSystem,
BridgeGrandpaRococoInstance,
BridgeParachainRococoInstance,
WithBridgeHubRococoMessagesInstance,
WithBridgeHubRococoMessageBridge,
>(
collator_session_keys(),
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
SIBLING_PARACHAIN_ID,
Wococo,
DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO,
)
}
#[test]
pub fn complex_relay_extrinsic_works() {
bridge_hub_test_utils::test_cases::complex_relay_extrinsic_works::<
Runtime,
XcmConfig,
ParachainSystem,
BridgeGrandpaRococoInstance,
BridgeParachainRococoInstance,
WithBridgeHubRococoMessagesInstance,
WithBridgeHubRococoMessageBridge,
>(
collator_session_keys(),
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID,
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
SIBLING_PARACHAIN_ID,
bridge_hub_wococo_config::BridgeHubRococoChainId::get(),
Wococo,
DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO,
ExistentialDeposit::get(),
executive_init_block,
construct_and_apply_extrinsic,
);
}
} }
@@ -10,20 +10,26 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =
log = { version = "0.4.17", default-features = false } log = { version = "0.4.17", default-features = false }
# Substrate # Substrate
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-utility = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
# Cumulus # Cumulus
asset-test-utils = { path = "../../assets/test-utils"}
cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false }
pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false }
cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false }
cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false }
pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false }
parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false }
asset-test-utils = { path = "../../assets/test-utils"} parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false }
# Polkadot # Polkadot
pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
@@ -33,13 +39,19 @@ xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features
xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
# Bridges # Bridges
bp-bridge-hub-rococo = { path = "../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false }
bp-bridge-hub-wococo = { path = "../../../../bridges/primitives/chain-bridge-hub-wococo", default-features = false }
bp-header-chain = { path = "../../../../bridges/primitives/header-chain", default-features = false } bp-header-chain = { path = "../../../../bridges/primitives/header-chain", default-features = false }
bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false }
bp-parachains = { path = "../../../../bridges/primitives/parachains", default-features = false }
bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false }
bp-relayers = { path = "../../../../bridges/primitives/relayers", default-features = false }
bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false }
bp-test-utils = { path = "../../../../bridges/primitives/test-utils", default-features = false } bp-test-utils = { path = "../../../../bridges/primitives/test-utils", default-features = false }
pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false } pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false }
pallet-bridge-parachains = { path = "../../../../bridges/modules/parachains", default-features = false }
pallet-bridge-messages = { path = "../../../../bridges/modules/messages", default-features = false } pallet-bridge-messages = { path = "../../../../bridges/modules/messages", default-features = false }
pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", default-features = false }
bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false }
[features] [features]
@@ -47,20 +59,28 @@ default = [ "std" ]
std = [ std = [
"codec/std", "codec/std",
"log/std", "log/std",
"frame-benchmarking/std",
"frame-executive/std",
"frame-support/std", "frame-support/std",
"frame-system/std", "frame-system/std",
"bp-messages/std", "bp-messages/std",
"bp-parachains/std",
"bp-polkadot-core/std", "bp-polkadot-core/std",
"bp-header-chain/std", "bp-header-chain/std",
"bp-relayers/std",
"bp-runtime/std", "bp-runtime/std",
"bp-test-utils/std", "bp-test-utils/std",
"bridge-runtime-common/std", "bridge-runtime-common/std",
"pallet-bridge-grandpa/std", "pallet-bridge-grandpa/std",
"pallet-bridge-parachains/std",
"pallet-bridge-messages/std", "pallet-bridge-messages/std",
"pallet-bridge-relayers/std",
"parachain-info/std", "parachain-info/std",
"parachains-runtimes-test-utils/std",
"cumulus-pallet-parachain-system/std", "cumulus-pallet-parachain-system/std",
"cumulus-pallet-xcmp-queue/std", "cumulus-pallet-xcmp-queue/std",
"pallet-xcm/std", "pallet-xcm/std",
"sp-core/std",
"sp-io/std", "sp-io/std",
"sp-runtime/std", "sp-runtime/std",
"xcm/std", "xcm/std",
@@ -70,4 +90,5 @@ std = [
"cumulus-pallet-dmp-queue/std", "cumulus-pallet-dmp-queue/std",
"pallet-session/std", "pallet-session/std",
"pallet-balances/std", "pallet-balances/std",
"pallet-utility/std",
] ]
@@ -1,4 +1,4 @@
// Copyright 2023 Parity Technologies (UK) Ltd. // Copyright Parity Technologies (UK) Ltd.
// This file is part of Cumulus. // This file is part of Cumulus.
// Cumulus is free software: you can redistribute it and/or modify // Cumulus is free software: you can redistribute it and/or modify
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>. // along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
pub use bp_test_utils::test_header; //! Module contains predefined test-case scenarios for "BridgeHub" `Runtime`s.
pub mod test_cases; pub mod test_cases;
pub use test_cases::CollatorSessionKeys; pub use bp_test_utils::test_header;
pub use parachains_runtimes_test_utils::*;
@@ -16,28 +16,41 @@
//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities. //! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities.
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch, SourceHeaderChain},
LaneId, MessageKey, OutboundLaneData, Weight,
};
use bp_parachains::{BestParaHeadHash, ParaInfo};
use bp_polkadot_core::parachains::{ParaHash, ParaId};
use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
use bp_runtime::{HeaderOf, Parachain, StorageProofSize, UnderlyingChainOf};
use bp_test_utils::{make_default_justification, prepare_parachain_heads_proof};
use bridge_runtime_common::{
messages::{
target::FromBridgedChainMessagesProof, BridgedChain as MessageBridgedChain, MessageBridge,
},
messages_generation::{encode_all_messages, encode_lane_data, prepare_messages_storage_proof},
messages_xcm_extension::{XcmAsPlainPayload, XcmBlobMessageDispatchResult},
};
use codec::Encode; use codec::Encode;
use frame_support::{assert_ok, traits::Get}; use frame_support::{
assert_ok,
traits::{Get, OriginTrait},
};
use pallet_bridge_grandpa::BridgedHeader;
use parachains_runtimes_test_utils::{
mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, RuntimeHelper,
ValidatorIdOf, XcmReceivedFrom,
};
use sp_core::H256;
use sp_keyring::AccountKeyring::*;
use sp_runtime::{traits::Header as HeaderT, AccountId32};
use xcm::latest::prelude::*; use xcm::latest::prelude::*;
use xcm_builder::DispatchBlobError; use xcm_builder::DispatchBlobError;
use xcm_executor::XcmExecutor; use xcm_executor::XcmExecutor;
// Lets re-use this stuff from assets (later we plan to move it outside of assets as `runtimes/test-utils`) // Re-export test_case from assets
use asset_test_utils::{ pub use asset_test_utils::include_teleports_for_native_asset_works;
mock_open_hrmp_channel, AccountIdOf, ExtBuilder, RuntimeHelper, ValidatorIdOf,
};
// Re-export test_cases from assets
pub use asset_test_utils::{
include_teleports_for_native_asset_works, CollatorSessionKeys, XcmReceivedFrom,
};
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
LaneId, MessageKey, OutboundLaneData,
};
use bridge_runtime_common::messages_xcm_extension::{
XcmAsPlainPayload, XcmBlobMessageDispatchResult,
};
/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call
pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>( pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
@@ -99,29 +112,6 @@ pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
}) })
} }
#[macro_export]
macro_rules! include_initialize_bridge_by_governance_works(
(
$runtime:path,
$pallet_bridge_grandpa_instance:path,
$collator_session_key:expr,
$runtime_para_id:expr,
$runtime_call_encode:expr
) => {
#[test]
fn initialize_bridge_by_governance_works() {
$crate::test_cases::initialize_bridge_by_governance_works::<
$runtime,
$pallet_bridge_grandpa_instance,
>(
$collator_session_key,
$runtime_para_id,
$runtime_call_encode
)
}
}
);
/// Test-case makes sure that `Runtime` can change storage constant via governance-like call /// Test-case makes sure that `Runtime` can change storage constant via governance-like call
pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, StorageConstantType>( pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, StorageConstantType>(
collator_session_key: CollatorSessionKeys<Runtime>, collator_session_key: CollatorSessionKeys<Runtime>,
@@ -192,35 +182,6 @@ pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, Sto
}) })
} }
#[macro_export]
macro_rules! include_change_storage_constant_by_governance_works(
(
$test_name:tt,
$runtime:path,
$collator_session_key:expr,
$runtime_para_id:expr,
$runtime_call_encode:expr,
($storage_constant:path, $storage_constant_type:path),
$storage_constant_key_value:expr,
$new_storage_constant_value:expr
) => {
#[test]
fn $test_name() {
$crate::test_cases::change_storage_constant_by_governance_works::<
$runtime,
$storage_constant,
$storage_constant_type,
>(
$collator_session_key,
$runtime_para_id,
$runtime_call_encode,
$storage_constant_key_value,
$new_storage_constant_value,
)
}
}
);
/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`: /// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`:
/// Checks if received XCM messages is correctly added to the message outbound queue for delivery. /// Checks if received XCM messages is correctly added to the message outbound queue for delivery.
/// For SystemParachains we expect unpaid execution. /// For SystemParachains we expect unpaid execution.
@@ -307,37 +268,6 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works<
}) })
} }
#[macro_export]
macro_rules! include_handle_export_message_from_system_parachain_to_outbound_queue_works(
(
$runtime:path,
$xcm_config:path,
$pallet_bridge_messages_instance:path,
$collator_session_key:expr,
$runtime_para_id:expr,
$sibling_parachain_id:expr,
$unwrap_pallet_bridge_messages_event:expr,
$export_message_instruction:expr,
$expected_lane_id:expr
) => {
#[test]
fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() {
$crate::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::<
$runtime,
$xcm_config,
$pallet_bridge_messages_instance
>(
$collator_session_key,
$runtime_para_id,
$sibling_parachain_id,
$unwrap_pallet_bridge_messages_event,
$export_message_instruction,
$expected_lane_id
)
}
}
);
/// Test-case makes sure that Runtime can route XCM messages received in inbound queue, /// Test-case makes sure that Runtime can route XCM messages received in inbound queue,
/// We just test here `MessageDispatch` configuration. /// We just test here `MessageDispatch` configuration.
/// We expect that runtime can route messages: /// We expect that runtime can route messages:
@@ -392,123 +322,573 @@ pub fn message_dispatch_routing_works<
.with_tracing() .with_tracing()
.build() .build()
.execute_with(|| { .execute_with(|| {
// 1. this message is sent from other global consensus with destination of this Runtime relay chain (UMP) // 1. this message is sent from other global consensus with destination of this Runtime relay chain (UMP)
let bridging_message = let bridging_message =
test_data::simulate_message_exporter_on_bridged_chain::<BridgedNetwork, RuntimeNetwork>( test_data::simulate_message_exporter_on_bridged_chain::<BridgedNetwork, RuntimeNetwork>(
(RuntimeNetwork::get(), Here) (RuntimeNetwork::get(), Here)
);
let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
test_data::dispatch_message(expected_lane_id, 1, bridging_message)
); );
assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)); let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
test_data::dispatch_message(expected_lane_id, 1, bridging_message)
);
assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched));
// check events - UpwardMessageSent // check events - UpwardMessageSent
let mut events = <frame_system::Pallet<Runtime>>::events() let mut events = <frame_system::Pallet<Runtime>>::events()
.into_iter() .into_iter()
.filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode())); .filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode()));
assert!( assert!(
events.any(|e| matches!(e, cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. })) events.any(|e| matches!(e, cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }))
);
// 2. this message is sent from other global consensus with destination of this Runtime sibling parachain (HRMP)
let bridging_message =
test_data::simulate_message_exporter_on_bridged_chain::<BridgedNetwork, RuntimeNetwork>(
(RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))),
); );
// 2. this message is sent from other global consensus with destination of this Runtime sibling parachain (HRMP) // 2.1. WITHOUT opened hrmp channel -> RoutingError
let bridging_message = let result =
test_data::simulate_message_exporter_on_bridged_chain::<BridgedNetwork, RuntimeNetwork>( <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
(RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))),
);
// 2.1. WITHOUT opened hrmp channel -> RoutingError
let result =
<<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
DispatchMessage {
key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
data: DispatchMessageData { payload: Ok(bridging_message.clone()) },
}
);
assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError))));
// check events - no XcmpMessageSent
assert_eq!(<frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()))
.count(), 0);
// 2.1. WITH hrmp channel -> Ok
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(runtime_para_id.into(), sibling_parachain_id.into());
let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
DispatchMessage { DispatchMessage {
key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
data: DispatchMessageData { payload: Ok(bridging_message) }, data: DispatchMessageData { payload: Ok(bridging_message.clone()) },
} }
); );
assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)); assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError))));
// check events - XcmpMessageSent // check events - no XcmpMessageSent
let mut events = <frame_system::Pallet<Runtime>>::events() assert_eq!(<frame_system::Pallet<Runtime>>::events()
.into_iter() .into_iter()
.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode())); .filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()))
assert!( .count(), 0);
events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }))
); // 2.1. WITH hrmp channel -> Ok
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(runtime_para_id.into(), sibling_parachain_id.into());
let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
DispatchMessage {
key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
data: DispatchMessageData { payload: Ok(bridging_message) },
}
);
assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched));
// check events - XcmpMessageSent
let mut events = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()));
assert!(
events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }))
);
}) })
} }
#[macro_export] /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
macro_rules! include_message_dispatch_routing_works( /// with proofs (finality, para heads, message) independently submitted.
( pub fn relayed_incoming_message_works<Runtime, XcmConfig, HrmpChannelOpener, GPI, PPI, MPI, MB>(
$runtime:path, collator_session_key: CollatorSessionKeys<Runtime>,
$xcm_config:path, runtime_para_id: u32,
$hrmp_channel_opener:path, bridged_para_id: u32,
$pallet_bridge_messages_instance:path, sibling_parachain_id: u32,
$runtime_network:path, local_relay_chain_id: NetworkId,
$bridged_network:path, lane_id: LaneId,
$collator_session_key:expr, ) where
$runtime_para_id:expr, Runtime: frame_system::Config
$sibling_parachain_id:expr, + pallet_balances::Config
$unwrap_cumulus_pallet_parachain_system_event:expr, + pallet_session::Config
$unwrap_cumulus_pallet_xcmp_queue_event:expr, + pallet_xcm::Config
$expected_lane_id:expr + parachain_info::Config
) => { + pallet_collator_selection::Config
#[test] + cumulus_pallet_dmp_queue::Config
fn message_dispatch_routing_works() { + cumulus_pallet_parachain_system::Config
$crate::test_cases::message_dispatch_routing_works::< + cumulus_pallet_xcmp_queue::Config
$runtime, + pallet_bridge_grandpa::Config<GPI>
$xcm_config, + pallet_bridge_parachains::Config<PPI>
$hrmp_channel_opener, + pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>,
$pallet_bridge_messages_instance, GPI: 'static,
$runtime_network, PPI: 'static,
$bridged_network MPI: 'static,
>( MB: MessageBridge,
$collator_session_key, <MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
$runtime_para_id, UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
$sibling_parachain_id, XcmConfig: xcm_executor::Config,
$unwrap_cumulus_pallet_parachain_system_event, HrmpChannelOpener: frame_support::inherent::ProvideInherent<
$unwrap_cumulus_pallet_xcmp_queue_event, Call = cumulus_pallet_parachain_system::Call<Runtime>,
$expected_lane_id, >,
) ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
} <<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: From<FromBridgedChainMessagesProof<ParaHash>>,
} <<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
); ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
{
assert_ne!(runtime_para_id, sibling_parachain_id);
assert_ne!(runtime_para_id, bridged_para_id);
mod test_data { ExtBuilder::<Runtime>::default()
.with_collators(collator_session_key.collators())
.with_session_keys(collator_session_key.session_keys())
.with_safe_xcm_version(XCM_VERSION)
.with_para_id(runtime_para_id.into())
.with_tracing()
.build()
.execute_with(|| {
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
runtime_para_id.into(),
sibling_parachain_id.into(),
);
// start with bridged chain block#0
let init_data = test_data::initialization_data::<Runtime, GPI>(0);
pallet_bridge_grandpa::Pallet::<Runtime, GPI>::initialize(
RuntimeHelper::<Runtime>::root_origin(),
init_data,
)
.unwrap();
// set up relayer details and proofs
let message_destination =
X2(GlobalConsensus(local_relay_chain_id), Parachain(sibling_parachain_id));
// some random numbers (checked by test)
let message_nonce = 1;
let para_header_number = 5;
let relay_header_number = 1;
let relayer_at_target = Bob;
let relayer_id_on_target: AccountIdOf<Runtime> = relayer_at_target.public().into();
let relayer_at_source = Dave;
let relayer_id_on_source: AccountId32 = relayer_at_source.public().into();
let xcm = vec![xcm::v3::Instruction::<()>::ClearOrigin; 42];
let expected_dispatch = xcm::VersionedXcm::<()>::V3(xcm.clone().into());
// generate bridged relay chain finality, parachain heads and message proofs,
// to be submitted by relayer to this chain.
let (
relay_chain_header,
grandpa_justification,
bridged_para_head,
parachain_heads,
para_heads_proof,
message_proof,
) = test_data::make_complex_relayer_proofs::<BridgedHeader<Runtime, GPI>, MB, ()>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
para_header_number,
relay_header_number,
bridged_para_id,
);
// submit bridged relay chain finality proof
{
let result = pallet_bridge_grandpa::Pallet::<Runtime, GPI>::submit_finality_proof(
RuntimeHelper::<Runtime>::origin_of(relayer_id_on_target.clone()),
Box::new(relay_chain_header.clone()),
grandpa_justification,
);
assert_ok!(result);
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
}
// verify finality proof correctly imported
assert_eq!(
pallet_bridge_grandpa::BestFinalized::<Runtime, GPI>::get().unwrap().1,
relay_chain_header.hash()
);
assert!(pallet_bridge_grandpa::ImportedHeaders::<Runtime, GPI>::contains_key(
relay_chain_header.hash()
));
// submit parachain heads proof
{
let result =
pallet_bridge_parachains::Pallet::<Runtime, PPI>::submit_parachain_heads(
RuntimeHelper::<Runtime>::origin_of(relayer_id_on_target.clone()),
(relay_header_number, relay_chain_header.hash().into()),
parachain_heads,
para_heads_proof,
);
assert_ok!(result);
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
}
// verify parachain head proof correctly imported
assert_eq!(
pallet_bridge_parachains::ParasInfo::<Runtime, PPI>::get(ParaId(bridged_para_id)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: relay_header_number,
head_hash: bridged_para_head.hash()
},
next_imported_hash_position: 1,
})
);
// import message
assert!(RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
sibling_parachain_id.into()
)
.is_none());
assert_eq!(
pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
.last_delivered_nonce(),
0,
);
// submit message proof
{
let result = pallet_bridge_messages::Pallet::<Runtime, MPI>::receive_messages_proof(
RuntimeHelper::<Runtime>::origin_of(relayer_id_on_target),
relayer_id_on_source.into(),
message_proof.into(),
1,
Weight::MAX / 1000,
);
assert_ok!(result);
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
}
// verify message correctly imported and dispatched
assert_eq!(
pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
.last_delivered_nonce(),
1,
);
// verify relayed bridged XCM message is dispatched to destination sibling para
let dispatched = RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
sibling_parachain_id.into(),
)
.unwrap();
assert_eq!(dispatched, expected_dispatch);
})
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, para heads, message) batched together in signed extrinsic.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn complex_relay_extrinsic_works<Runtime, XcmConfig, HrmpChannelOpener, GPI, PPI, MPI, MB>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
bridged_para_id: u32,
sibling_parachain_id: u32,
bridged_chain_id: bp_runtime::ChainId,
local_relay_chain_id: NetworkId,
lane_id: LaneId,
existential_deposit: BalanceOf<Runtime>,
executive_init_block: fn(&<Runtime as frame_system::Config>::Header),
construct_and_apply_extrinsic: fn(
sp_keyring::AccountKeyring,
pallet_utility::Call::<Runtime>
) -> sp_runtime::DispatchOutcome,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_utility::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_dmp_queue::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config
+ pallet_bridge_grandpa::Config<GPI>
+ pallet_bridge_parachains::Config<PPI>
+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
+ pallet_bridge_relayers::Config,
GPI: 'static,
PPI: 'static,
MPI: 'static,
MB: MessageBridge,
<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
XcmConfig: xcm_executor::Config,
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: From<FromBridgedChainMessagesProof<ParaHash>>,
<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
<Runtime as pallet_utility::Config>::RuntimeCall:
From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+ From<pallet_bridge_messages::Call<Runtime, MPI>>
{
assert_ne!(runtime_para_id, sibling_parachain_id);
assert_ne!(runtime_para_id, bridged_para_id);
// Relayer account at local/this BH.
let relayer_at_target = Bob;
let relayer_id_on_target: AccountIdOf<Runtime> = relayer_at_target.public().into();
let relayer_initial_balance = existential_deposit * 100000u32.into();
// Relayer account at remote/bridged BH.
let relayer_at_source = Dave;
let relayer_id_on_source: AccountId32 = relayer_at_source.public().into();
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_key.collators())
.with_session_keys(collator_session_key.session_keys())
.with_safe_xcm_version(XCM_VERSION)
.with_para_id(runtime_para_id.into())
.with_balances(vec![(relayer_id_on_target.clone(), relayer_initial_balance)])
.with_tracing()
.build()
.execute_with(|| {
let zero: <Runtime as frame_system::Config>::BlockNumber = 0u32.into();
let genesis_hash = frame_system::Pallet::<Runtime>::block_hash(zero);
let mut header: <Runtime as frame_system::Config>::Header =
bp_test_utils::test_header(1u32.into());
header.set_parent_hash(genesis_hash);
executive_init_block(&header);
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
runtime_para_id.into(),
sibling_parachain_id.into(),
);
// start with bridged chain block#0
let init_data = test_data::initialization_data::<Runtime, GPI>(0);
pallet_bridge_grandpa::Pallet::<Runtime, GPI>::initialize(
RuntimeHelper::<Runtime>::root_origin(),
init_data,
)
.unwrap();
// set up relayer details and proofs
let message_destination =
X2(GlobalConsensus(local_relay_chain_id), Parachain(sibling_parachain_id));
// some random numbers (checked by test)
let message_nonce = 1;
let para_header_number = 5;
let relay_header_number = 1;
let xcm = vec![xcm::v3::Instruction::<()>::ClearOrigin; 42];
let expected_dispatch = xcm::VersionedXcm::<()>::V3(xcm.clone().into());
// generate bridged relay chain finality, parachain heads and message proofs,
// to be submitted by relayer to this chain.
let (
relay_chain_header,
grandpa_justification,
bridged_para_head,
parachain_heads,
para_heads_proof,
message_proof,
) = test_data::make_complex_relayer_proofs::<BridgedHeader<Runtime, GPI>, MB, ()>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
para_header_number,
relay_header_number,
bridged_para_id,
);
let submit_grandpa =
pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header.clone()),
justification: grandpa_justification,
};
let submit_para_head =
pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
at_relay_block: (relay_header_number, relay_chain_header.hash().into()),
parachains: parachain_heads,
parachain_heads_proof: para_heads_proof,
};
let submit_message =
pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
relayer_id_at_bridged_chain: relayer_id_on_source.into(),
proof: message_proof.into(),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
};
let batch = pallet_utility::Call::<Runtime>::batch_all {
calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()],
};
// sanity checks - before relayer extrinsic
assert!(RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
sibling_parachain_id.into()
)
.is_none());
assert_eq!(
pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
.last_delivered_nonce(),
0,
);
let msg_proofs_rewards_account = RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
);
assert_eq!(
pallet_bridge_relayers::RelayerRewards::<Runtime>::get(
relayer_id_on_target.clone(),
msg_proofs_rewards_account
),
None,
);
// construct and apply extrinsic containing batch calls:
// bridged relay chain finality proof
// + parachain heads proof
// + submit message proof
let dispatch_outcome = construct_and_apply_extrinsic(relayer_at_target, batch);
// verify finality proof correctly imported
assert_ok!(dispatch_outcome);
assert_eq!(
<pallet_bridge_grandpa::BestFinalized<Runtime, GPI>>::get().unwrap().1,
relay_chain_header.hash()
);
assert!(<pallet_bridge_grandpa::ImportedHeaders<Runtime, GPI>>::contains_key(
relay_chain_header.hash()
));
// verify parachain head proof correctly imported
assert_eq!(
pallet_bridge_parachains::ParasInfo::<Runtime, PPI>::get(ParaId(bridged_para_id)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: relay_header_number,
head_hash: bridged_para_head.hash()
},
next_imported_hash_position: 1,
})
);
// verify message correctly imported and dispatched
assert_eq!(
pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
.last_delivered_nonce(),
1,
);
// verify relayer is refunded
assert!(pallet_bridge_relayers::RelayerRewards::<Runtime>::get(
relayer_id_on_target,
msg_proofs_rewards_account
)
.is_some());
// verify relayed bridged XCM message is dispatched to destination sibling para
let dispatched = RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
sibling_parachain_id.into(),
)
.unwrap();
assert_eq!(dispatched, expected_dispatch);
})
}
pub mod test_data {
use super::*; use super::*;
use bp_header_chain::justification::GrandpaJustification;
use bp_messages::MessageNonce; use bp_messages::MessageNonce;
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
use bp_runtime::BasicOperatingMode;
use bp_test_utils::authority_list;
use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter};
use xcm_executor::traits::{validate_export, ExportXcm}; use xcm_executor::traits::{validate_export, ExportXcm};
pub fn prepare_inbound_xcm<InnerXcmRuntimeCall>(
xcm_message: Xcm<InnerXcmRuntimeCall>,
destination: InteriorMultiLocation,
) -> Vec<u8> {
let location = xcm::VersionedInteriorMultiLocation::V3(destination);
let xcm = xcm::VersionedXcm::<InnerXcmRuntimeCall>::V3(xcm_message);
// this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor
// or public fields, so just tuple
// (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed
// to the storage)
(location, xcm).encode().encode()
}
pub fn make_complex_relayer_proofs<BridgedRelayHeader, MB, InnerXcmRuntimeCall>(
lane_id: LaneId,
xcm_message: Xcm<InnerXcmRuntimeCall>,
message_nonce: MessageNonce,
message_destination: Junctions,
para_header_number: u32,
relay_header_number: u32,
bridged_para_id: u32,
) -> (
BridgedRelayHeader,
GrandpaJustification<BridgedRelayHeader>,
ParaHead,
Vec<(ParaId, ParaHash)>,
ParaHeadsProof,
FromBridgedChainMessagesProof<ParaHash>,
)
where
BridgedRelayHeader: HeaderT,
<BridgedRelayHeader as HeaderT>::Hash: From<H256>,
MB: MessageBridge,
<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
{
let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
let message_size = StorageProofSize::Minimal(message_payload.len() as u32);
// prepare para storage proof containing message
let (para_state_root, para_storage_proof) = prepare_messages_storage_proof::<MB>(
lane_id,
message_nonce..=message_nonce,
None,
message_size,
message_payload,
encode_all_messages,
encode_lane_data,
);
let bridged_para_head = ParaHead(
bp_test_utils::test_header_with_root::<HeaderOf<MB::BridgedChain>>(
para_header_number.into(),
para_state_root.into(),
)
.encode(),
);
let (relay_state_root, para_heads_proof, parachain_heads) =
prepare_parachain_heads_proof::<HeaderOf<MB::BridgedChain>>(vec![(
bridged_para_id,
bridged_para_head.clone(),
)]);
assert_eq!(bridged_para_head.hash(), parachain_heads[0].1);
let message_proof = FromBridgedChainMessagesProof {
bridged_header_hash: bridged_para_head.hash(),
storage_proof: para_storage_proof,
lane: lane_id,
nonces_start: message_nonce,
nonces_end: message_nonce,
};
// import bridged relay chain block#1 with state root containing head#5 of bridged parachain
let relay_chain_header: BridgedRelayHeader = bp_test_utils::test_header_with_root(
relay_header_number.into(),
relay_state_root.into(),
);
let justification = make_default_justification(&relay_chain_header);
(
relay_chain_header,
justification,
bridged_para_head,
parachain_heads,
para_heads_proof,
message_proof,
)
}
/// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet /// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet
pub(crate) fn initialization_data< pub fn initialization_data<
Runtime: pallet_bridge_grandpa::Config<GrandpaPalletInstance>, Runtime: pallet_bridge_grandpa::Config<GrandpaPalletInstance>,
GrandpaPalletInstance: 'static, GrandpaPalletInstance: 'static,
>( >(
block_number: u32, block_number: u32,
) -> bp_header_chain::InitializationData< ) -> bp_header_chain::InitializationData<BridgedHeader<Runtime, GrandpaPalletInstance>> {
pallet_bridge_grandpa::BridgedHeader<Runtime, GrandpaPalletInstance>,
> {
bp_header_chain::InitializationData { bp_header_chain::InitializationData {
header: Box::new(bp_test_utils::test_header(block_number.into())), header: Box::new(bp_test_utils::test_header(block_number.into())),
authority_list: Default::default(), authority_list: authority_list(),
set_id: 6, set_id: 1,
operating_mode: bp_runtime::BasicOperatingMode::Normal, operating_mode: BasicOperatingMode::Normal,
} }
} }
@@ -0,0 +1,74 @@
[package]
name = "parachains-runtimes-test-utils"
version = "1.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
description = "Utils for Runtimes testing"
[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] }
# Substrate
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-assets = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-consensus-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
# Cumulus
cumulus-pallet-parachain-system = { path = "../../../pallets/parachain-system", default-features = false }
cumulus-pallet-xcmp-queue = { path = "../../../pallets/xcmp-queue", default-features = false }
cumulus-pallet-dmp-queue = { path = "../../../pallets/dmp-queue", default-features = false }
pallet-collator-selection = { path = "../../../pallets/collator-selection", default-features = false }
parachains-common = { path = "../../common", default-features = false }
assets-common = { path = "../assets/common", default-features = false }
cumulus-primitives-core = { path = "../../../primitives/core", default-features = false }
cumulus-primitives-parachain-inherent = { path = "../../../primitives/parachain-inherent", default-features = false }
cumulus-test-relay-sproof-builder = { path = "../../../test/relay-sproof-builder", default-features = false }
parachain-info = { path = "../../../parachains/pallets/parachain-info", default-features = false }
# Polkadot
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
[dev-dependencies]
hex-literal = "0.3.4"
[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = [ "std" ]
std = [
"cumulus-pallet-parachain-system/std",
"cumulus-primitives-core/std",
"cumulus-test-relay-sproof-builder/std",
"cumulus-primitives-parachain-inherent/std",
"frame-support/std",
"frame-system/std",
"pallet-assets/std",
"pallet-balances/std",
"cumulus-pallet-parachain-system/std",
"pallet-collator-selection/std",
"pallet-session/std",
"assets-common/std",
"parachains-common/std",
"parachain-info/std",
"polkadot-parachain/std",
"sp-consensus-aura/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"xcm/std",
"xcm-executor/std",
"pallet-xcm/std",
"cumulus-pallet-xcmp-queue/std",
"cumulus-pallet-dmp-queue/std",
]
@@ -0,0 +1,479 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_std::marker::PhantomData;
use codec::DecodeLimit;
use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData};
use cumulus_primitives_parachain_inherent::ParachainInherentData;
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
use frame_support::{
dispatch::{DispatchResult, RawOrigin, UnfilteredDispatchable},
inherent::{InherentData, ProvideInherent},
traits::{GenesisBuild, OriginTrait},
weights::Weight,
};
use parachains_common::AccountId;
use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber, XcmpMessageFormat};
use sp_consensus_aura::AURA_ENGINE_ID;
use sp_core::Encode;
use sp_runtime::{Digest, DigestItem};
use xcm::{
latest::{MultiAsset, MultiLocation, XcmContext, XcmHash},
prelude::*,
VersionedXcm, MAX_XCM_DECODE_DEPTH,
};
use xcm_executor::{traits::TransactAsset, Assets};
pub type BalanceOf<Runtime> = <Runtime as pallet_balances::Config>::Balance;
pub type AccountIdOf<Runtime> = <Runtime as frame_system::Config>::AccountId;
pub type ValidatorIdOf<Runtime> = <Runtime as pallet_session::Config>::ValidatorId;
pub type SessionKeysOf<Runtime> = <Runtime as pallet_session::Config>::Keys;
pub struct CollatorSessionKeys<
Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config,
> {
collator: AccountIdOf<Runtime>,
validator: ValidatorIdOf<Runtime>,
key: SessionKeysOf<Runtime>,
}
impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config>
CollatorSessionKeys<Runtime>
{
pub fn new(
collator: AccountIdOf<Runtime>,
validator: ValidatorIdOf<Runtime>,
key: SessionKeysOf<Runtime>,
) -> Self {
Self { collator, validator, key }
}
pub fn collators(&self) -> Vec<AccountIdOf<Runtime>> {
vec![self.collator.clone()]
}
pub fn session_keys(
&self,
) -> Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)> {
vec![(self.collator.clone(), self.validator.clone(), self.key.clone())]
}
}
// Basic builder based on balances, collators and pallet_sessopm
pub struct ExtBuilder<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> {
// endowed accounts with balances
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
// collators to test block prod
collators: Vec<AccountIdOf<Runtime>>,
// keys added to pallet session
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
// safe xcm version for pallet_xcm
safe_xcm_version: Option<XcmVersion>,
// para id
para_id: Option<ParaId>,
_runtime: PhantomData<Runtime>,
}
impl<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> Default for ExtBuilder<Runtime>
{
fn default() -> ExtBuilder<Runtime> {
ExtBuilder {
balances: vec![],
collators: vec![],
keys: vec![],
safe_xcm_version: None,
para_id: None,
_runtime: PhantomData,
}
}
}
impl<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> ExtBuilder<Runtime>
{
pub fn with_balances(
mut self,
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
) -> Self {
self.balances = balances;
self
}
pub fn with_collators(mut self, collators: Vec<AccountIdOf<Runtime>>) -> Self {
self.collators = collators;
self
}
pub fn with_session_keys(
mut self,
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
) -> Self {
self.keys = keys;
self
}
pub fn with_tracing(self) -> Self {
frame_support::sp_tracing::try_init_simple();
self
}
pub fn with_safe_xcm_version(mut self, safe_xcm_version: XcmVersion) -> Self {
self.safe_xcm_version = Some(safe_xcm_version);
self
}
pub fn with_para_id(mut self, para_id: ParaId) -> Self {
self.para_id = Some(para_id);
self
}
pub fn build(self) -> sp_io::TestExternalities
where
Runtime:
pallet_collator_selection::Config + pallet_balances::Config + pallet_session::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
{
let mut t = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
<pallet_xcm::GenesisConfig as GenesisBuild<Runtime>>::assimilate_storage(
&pallet_xcm::GenesisConfig { safe_xcm_version: self.safe_xcm_version },
&mut t,
)
.unwrap();
if let Some(para_id) = self.para_id {
<parachain_info::GenesisConfig as frame_support::traits::GenesisBuild<Runtime>>::assimilate_storage(
&parachain_info::GenesisConfig { parachain_id: para_id },
&mut t,
)
.unwrap();
}
pallet_balances::GenesisConfig::<Runtime> { balances: self.balances }
.assimilate_storage(&mut t)
.unwrap();
pallet_collator_selection::GenesisConfig::<Runtime> {
invulnerables: self.collators.clone(),
candidacy_bond: Default::default(),
desired_candidates: Default::default(),
}
.assimilate_storage(&mut t)
.unwrap();
pallet_session::GenesisConfig::<Runtime> { keys: self.keys }
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
frame_system::Pallet::<Runtime>::set_block_number(1u32.into());
});
ext
}
}
pub struct RuntimeHelper<Runtime>(PhantomData<Runtime>);
/// Utility function that advances the chain to the desired block number.
/// If an author is provided, that author information is injected to all the blocks in the meantime.
impl<Runtime: frame_system::Config> RuntimeHelper<Runtime>
where
AccountIdOf<Runtime>:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
{
pub fn run_to_block(n: u32, author: Option<AccountId>) {
while frame_system::Pallet::<Runtime>::block_number() < n.into() {
// Set the new block number and author
match author {
Some(ref author) => {
let pre_digest = Digest {
logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, author.encode())],
};
frame_system::Pallet::<Runtime>::reset_events();
frame_system::Pallet::<Runtime>::initialize(
&(frame_system::Pallet::<Runtime>::block_number() + 1u32.into()),
&frame_system::Pallet::<Runtime>::parent_hash(),
&pre_digest,
);
},
None => {
frame_system::Pallet::<Runtime>::set_block_number(
frame_system::Pallet::<Runtime>::block_number() + 1u32.into(),
);
},
}
}
}
pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
<Runtime as frame_system::Config>::RuntimeOrigin::root()
}
pub fn origin_of(
account_id: AccountIdOf<Runtime>,
) -> <Runtime as frame_system::Config>::RuntimeOrigin {
<Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id.into())
}
}
impl<XcmConfig: xcm_executor::Config> RuntimeHelper<XcmConfig> {
pub fn do_transfer(
from: MultiLocation,
to: MultiLocation,
(asset, amount): (MultiLocation, u128),
) -> Result<Assets, XcmError> {
<XcmConfig::AssetTransactor as TransactAsset>::transfer_asset(
&MultiAsset { id: Concrete(asset), fun: Fungible(amount) },
&from,
&to,
// We aren't able to track the XCM that initiated the fee deposit, so we create a
// fake message hash here
&XcmContext::with_message_hash([0; 32]),
)
}
}
impl<Runtime: pallet_xcm::Config + cumulus_pallet_parachain_system::Config> RuntimeHelper<Runtime> {
pub fn do_teleport_assets<HrmpChannelOpener>(
origin: <Runtime as frame_system::Config>::RuntimeOrigin,
dest: MultiLocation,
beneficiary: MultiLocation,
(asset, amount): (MultiLocation, u128),
open_hrmp_channel: Option<(u32, u32)>,
) -> DispatchResult
where
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
{
// open hrmp (if needed)
if let Some((source_para_id, target_para_id)) = open_hrmp_channel {
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
source_para_id.into(),
target_para_id.into(),
);
}
// do teleport
<pallet_xcm::Pallet<Runtime>>::teleport_assets(
origin,
Box::new(dest.into()),
Box::new(beneficiary.into()),
Box::new((Concrete(asset), amount).into()),
0,
)
}
}
impl<Runtime: cumulus_pallet_dmp_queue::Config + cumulus_pallet_parachain_system::Config>
RuntimeHelper<Runtime>
{
pub fn execute_as_governance(call: Vec<u8>, require_weight_at_most: Weight) -> Outcome {
// prepare xcm as governance will do
let xcm = Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
Transact {
origin_kind: OriginKind::Superuser,
require_weight_at_most,
call: call.into(),
},
]);
// execute xcm as parent origin
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
<<Runtime as cumulus_pallet_dmp_queue::Config>::XcmExecutor>::execute_xcm(
MultiLocation::parent(),
xcm,
hash,
Self::xcm_max_weight(XcmReceivedFrom::Parent),
)
}
}
pub enum XcmReceivedFrom {
Parent,
Sibling,
}
impl<ParachainSystem: cumulus_pallet_parachain_system::Config> RuntimeHelper<ParachainSystem> {
pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight {
use frame_support::traits::Get;
match from {
XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(),
XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(),
}
}
}
impl<Runtime: frame_system::Config + pallet_xcm::Config> RuntimeHelper<Runtime> {
pub fn assert_pallet_xcm_event_outcome(
unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
assert_outcome: fn(Outcome),
) {
let outcome = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_pallet_xcm_event(e.event.encode()))
.find_map(|e| match e {
pallet_xcm::Event::Attempted(outcome) => Some(outcome),
_ => None,
})
.expect("No `pallet_xcm::Event::Attempted(outcome)` event found!");
assert_outcome(outcome);
}
}
impl<Runtime: frame_system::Config + cumulus_pallet_xcmp_queue::Config> RuntimeHelper<Runtime> {
pub fn xcmp_queue_message_sent(
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
) -> Option<XcmHash> {
<frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
.find_map(|e| match e {
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => message_hash,
_ => None,
})
}
}
pub fn assert_metadata<Fungibles, AccountId>(
asset_id: impl Into<Fungibles::AssetId> + Copy,
expected_name: &str,
expected_symbol: &str,
expected_decimals: u8,
) where
Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect<AccountId>
+ frame_support::traits::tokens::fungibles::Inspect<AccountId>,
{
assert_eq!(Fungibles::name(asset_id.into()), Vec::from(expected_name),);
assert_eq!(Fungibles::symbol(asset_id.into()), Vec::from(expected_symbol),);
assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals);
}
pub fn assert_total<Fungibles, AccountId>(
asset_id: impl Into<Fungibles::AssetId> + Copy,
expected_total_issuance: impl Into<Fungibles::Balance>,
expected_active_issuance: impl Into<Fungibles::Balance>,
) where
Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect<AccountId>
+ frame_support::traits::tokens::fungibles::Inspect<AccountId>,
{
assert_eq!(Fungibles::total_issuance(asset_id.into()), expected_total_issuance.into());
assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into());
}
/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass
pub fn mock_open_hrmp_channel<
C: cumulus_pallet_parachain_system::Config,
T: ProvideInherent<Call = cumulus_pallet_parachain_system::Call<C>>,
>(
sender: ParaId,
recipient: ParaId,
) {
let n = 1_u32;
let mut sproof_builder = RelayStateSproofBuilder {
para_id: sender,
hrmp_egress_channel_index: Some(vec![recipient]),
..Default::default()
};
sproof_builder.hrmp_channels.insert(
HrmpChannelId { sender, recipient },
AbridgedHrmpChannel {
max_capacity: 10,
max_total_size: 10_000_000_u32,
max_message_size: 10_000_000_u32,
msg_count: 0,
total_size: 0_u32,
mqc_head: None,
},
);
let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof();
let vfp = PersistedValidationData {
relay_parent_number: n as RelayChainBlockNumber,
relay_parent_storage_root,
..Default::default()
};
// It is insufficient to push the validation function params
// to storage; they must also be included in the inherent data.
let inherent_data = {
let mut inherent_data = InherentData::default();
let system_inherent_data = ParachainInherentData {
validation_data: vfp,
relay_chain_state,
downward_messages: Default::default(),
horizontal_messages: Default::default(),
};
inherent_data
.put_data(
cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER,
&system_inherent_data,
)
.expect("failed to put VFP inherent");
inherent_data
};
// execute the block
T::create_inherent(&inherent_data)
.expect("got an inherent")
.dispatch_bypass_filter(RawOrigin::None.into())
.expect("dispatch succeeded");
}
impl<HrmpChannelSource: cumulus_primitives_core::XcmpMessageSource>
RuntimeHelper<HrmpChannelSource>
{
pub fn take_xcm(sent_to_para_id: ParaId) -> Option<VersionedXcm<()>> {
match HrmpChannelSource::take_outbound_messages(10)[..] {
[(para_id, ref mut xcm_message_data)] if para_id.eq(&sent_to_para_id.into()) => {
let mut xcm_message_data = &xcm_message_data[..];
// decode
let _ = XcmpMessageFormat::decode_with_depth_limit(
MAX_XCM_DECODE_DEPTH,
&mut xcm_message_data,
)
.expect("valid format");
VersionedXcm::<()>::decode_with_depth_limit(
MAX_XCM_DECODE_DEPTH,
&mut xcm_message_data,
)
.map(|x| Some(x))
.expect("result with xcm")
},
_ => return None,
}
}
}