diff --git a/.gitignore b/.gitignore index 58abcc32cc5..225be857745 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ polkadot_argument_parsing **/chains/ *.iml .env -bin **/._* diff --git a/Cargo.lock b/Cargo.lock index 9958eeccdbf..212366da60a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -394,6 +394,7 @@ dependencies = [ "pallet-xcm", "parachain-info", "parachains-common", + "parachains-runtimes-test-utils", "parity-scale-codec", "polkadot-parachain", "sp-consensus-aura", @@ -914,13 +915,18 @@ name = "bp-test-utils" version = "0.1.0" dependencies = [ "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", "ed25519-dalek", "finality-grandpa", "parity-scale-codec", "sp-application-crypto", "sp-consensus-grandpa", + "sp-core", "sp-runtime", "sp-std", + "sp-trie", ] [[package]] @@ -1126,6 +1132,7 @@ dependencies = [ "sp-core", "sp-inherents", "sp-io", + "sp-keyring", "sp-offchain", "sp-runtime", "sp-session", @@ -1144,28 +1151,40 @@ name = "bridge-hub-test-utils" version = "0.1.0" dependencies = [ "asset-test-utils", + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", "bp-header-chain", "bp-messages", + "bp-parachains", "bp-polkadot-core", + "bp-relayers", "bp-runtime", "bp-test-utils", "bridge-runtime-common", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", + "frame-benchmarking", + "frame-executive", "frame-support", "frame-system", "log", "pallet-balances", "pallet-bridge-grandpa", "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", "pallet-collator-selection", "pallet-session", + "pallet-utility", "pallet-xcm", "pallet-xcm-benchmarks", "parachain-info", + "parachains-runtimes-test-utils", "parity-scale-codec", + "sp-core", "sp-io", + "sp-keyring", "sp-runtime", "xcm", "xcm-builder", @@ -8051,6 +8070,39 @@ dependencies = [ "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]] name = "parity-db" version = "0.4.8" diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index 12b096492cd..546d4388471 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -28,12 +28,12 @@ pub mod messages; pub mod messages_api; pub mod messages_benchmarking; pub mod messages_call_ext; +pub mod messages_generation; pub mod messages_xcm_extension; pub mod parachains_benchmarking; pub mod priority_calculator; pub mod refund_relayer_extension; -mod messages_generation; mod mock; #[cfg(feature = "integrity-test")] diff --git a/bridges/bin/runtime-common/src/messages_generation.rs b/bridges/bin/runtime-common/src/messages_generation.rs index 29a869a5c87..8dbf3abd683 100644 --- a/bridges/bin/runtime-common/src/messages_generation.rs +++ b/bridges/bin/runtime-common/src/messages_generation.rs @@ -16,8 +16,6 @@ //! 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 bp_messages::{ @@ -29,19 +27,19 @@ use sp_std::{ops::RangeInclusive, prelude::*}; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; /// Simple and correct message data encode function. -pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { +pub fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { Some(m.encode()) } /// Simple and correct outbound lane data encode function. -pub(crate) fn encode_lane_data(d: &OutboundLaneData) -> Vec { +pub fn encode_lane_data(d: &OutboundLaneData) -> Vec { d.encode() } /// Prepare storage proof of given messages. /// /// Returns state trie root and nodes with prepared messages. -pub(crate) fn prepare_messages_storage_proof( +pub fn prepare_messages_storage_proof( lane: LaneId, message_nonces: RangeInclusive, outbound_lane_data: Option, diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 5a393af7cc4..c2052e3d4eb 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -701,16 +701,17 @@ pub(crate) mod tests { use crate::mock::{ run_test, test_relay_header, BigParachainHeader, RegularParachainHasher, 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 bp_parachains::{ BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider, }; use bp_runtime::{ - record_all_trie_keys, BasicOperatingMode, OwnedBridgeModuleError, - StorageDoubleMapKeyProvider, StorageMapKeyProvider, + BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, }; use bp_test_utils::{ 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 sp_core::Hasher; use sp_runtime::{traits::Header as HeaderT, DispatchError}; - use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type WeightInfo = ::WeightInfo; @@ -768,32 +768,6 @@ pub(crate) mod tests { 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::::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::, _>(&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 { ParaInfo { best_head_hash: BestParaHeadHash { @@ -875,7 +849,7 @@ pub(crate) mod tests { #[test] fn submit_parachain_heads_checks_operating_mode() { let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); run_test(|| { initialize(state_root); @@ -906,7 +880,10 @@ pub(crate) mod tests { #[test] fn imports_initial_parachain_heads() { let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); + prepare_parachain_heads_proof::(vec![ + (1, head_data(1, 0)), + (3, head_data(3, 10)), + ]); run_test(|| { initialize(state_root); @@ -985,9 +962,9 @@ pub(crate) mod tests { #[test] fn imports_parachain_heads_is_able_to_progress() { let (state_root_5, proof_5, parachains_5) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); let (state_root_10, proof_10, parachains_10) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 and import head#5 of parachain#1 initialize(state_root_5); @@ -1083,11 +1060,12 @@ pub(crate) mod tests { #[test] fn ignores_untracked_parachain() { - let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![ - (1, head_data(1, 5)), - (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), - (2, head_data(1, 5)), - ]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof::(vec![ + (1, head_data(1, 5)), + (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), + (2, head_data(1, 5)), + ]); run_test(|| { // start with relay block #0 and try to import head#5 of parachain#1 and untracked // parachain @@ -1160,7 +1138,7 @@ pub(crate) mod tests { #[test] fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); run_test(|| { // import head#0 of parachain#1 at relay block#0 initialize(state_root); @@ -1220,9 +1198,9 @@ pub(crate) mod tests { #[test] fn does_nothing_when_already_imported_head_at_better_relay_header() { let (state_root_5, proof_5, parachains_5) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); let (state_root_10, proof_10, parachains_10) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 initialize(state_root_5); @@ -1314,7 +1292,10 @@ pub(crate) mod tests { #[test] fn does_nothing_when_parachain_head_is_too_large() { 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::(vec![ + (1, head_data(1, 5)), + (4, big_head_data(1, 5)), + ]); run_test(|| { // start with relay block #0 and try to import head#5 of parachain#1 and big parachain initialize(state_root); @@ -1368,8 +1349,9 @@ pub(crate) mod tests { // import exactly `HeadsToKeep` headers for i in 0..heads_to_keep { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); + let (state_root, proof, parachains) = prepare_parachain_heads_proof::< + RegularParachainHeader, + >(vec![(1, head_data(1, i))]); if i == 0 { initialize(state_root); } else { @@ -1389,8 +1371,9 @@ pub(crate) mod tests { } // import next relay chain header and next parachain head - let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); + let (state_root, proof, parachains) = prepare_parachain_heads_proof::< + RegularParachainHeader, + >(vec![(1, head_data(1, heads_to_keep))]); proceed(heads_to_keep, state_root); let expected_weight = weight_of_import_parachain_1_head(&proof, true); let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); @@ -1411,7 +1394,7 @@ pub(crate) mod tests { #[test] fn fails_on_unknown_relay_chain_block() { let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(state_root); @@ -1427,7 +1410,7 @@ pub(crate) mod tests { #[test] fn fails_on_invalid_storage_proof() { let (_state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(Default::default()); @@ -1445,11 +1428,11 @@ pub(crate) mod tests { #[test] fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { let (state_root_5, proof_5, parachains_5) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); 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::(vec![(2, head_data(2, 10))]); 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::(vec![(1, head_data(1, 10))]); run_test(|| { // we've already imported head#5 of parachain#1 at relay block#10 initialize(state_root_5); @@ -1517,7 +1500,8 @@ pub(crate) mod tests { #[test] 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::(vec![]); let parachains = vec![(ParaId(2), Default::default())]; run_test(|| { initialize(state_root); @@ -1542,7 +1526,8 @@ pub(crate) mod tests { #[test] 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::(vec![(1, head_data(1, 0))]); let parachains = vec![(ParaId(1), head_data(1, 10).hash())]; run_test(|| { initialize(state_root); @@ -1569,7 +1554,8 @@ pub(crate) mod tests { #[test] 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::(vec![(1, head_data(1, 0))]); let parachains = vec![(ParaId(2), Default::default())]; let relay_header_id = (0, test_relay_header(0, state_root).hash()); diff --git a/bridges/primitives/test-utils/Cargo.toml b/bridges/primitives/test-utils/Cargo.toml index 5ed835857d1..2e2af99332e 100644 --- a/bridges/primitives/test-utils/Cargo.toml +++ b/bridges/primitives/test-utils/Cargo.toml @@ -7,23 +7,30 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] 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 } ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] } finality-grandpa = { version = "0.16.2", 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-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] default = ["std"] std = [ "bp-header-chain/std", + "bp-polkadot-core/std", "codec/std", "ed25519-dalek/std", "finality-grandpa/std", "sp-application-crypto/std", "sp-consensus-grandpa/std", + "sp-core/std", "sp-runtime/std", "sp-std/std", ] diff --git a/bridges/primitives/test-utils/src/lib.rs b/bridges/primitives/test-utils/src/lib.rs index 6bb4adbf450..5a7d0cca279 100644 --- a/bridges/primitives/test-utils/src/lib.rs +++ b/bridges/primitives/test-utils/src/lib.rs @@ -19,10 +19,14 @@ #![cfg_attr(not(feature = "std"), no_std)] 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 sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; use sp_runtime::traits::{Header as HeaderT, One, Zero}; use sp_std::prelude::*; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; // Re-export all our test account utilities pub use keyring::*; @@ -31,6 +35,7 @@ mod keyring; pub const TEST_GRANDPA_ROUND: u64 = 1; pub const TEST_GRANDPA_SET_ID: SetId = 1; +pub const PARAS_PALLET_NAME: &str = "Paras"; /// Configuration parameters when generating test GRANDPA justifications. #[derive(Clone)] @@ -161,6 +166,33 @@ fn generate_chain(fork_id: u32, depth: u32, ancestor: &H) -> Vec headers } +/// Make valid proof for parachain `heads` +pub fn prepare_parachain_heads_proof( + 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::::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::, _>(&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. pub fn signed_precommit( signer: &Account, @@ -207,6 +239,15 @@ pub fn test_header(number: H::Number) -> H { 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(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. pub fn header_id(index: u8) -> (H::Hash, H::Number) { (test_header::(index.into()).hash(), index.into()) diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index 57cd502857c..c25d09837b6 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -1,4 +1,4 @@ -use asset_test_utils::{ExtBuilder, RuntimeHelper}; +use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper}; use codec::{Decode, Encode}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ @@ -26,6 +26,14 @@ const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = assets_common::AssetIdForTrustBackedAssetsConvert; +fn collator_session_keys() -> CollatorSessionKeys { + CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + ) +} + #[test] fn test_asset_xcm_trader() { ExtBuilder::::default() @@ -472,11 +480,7 @@ asset_test_utils::include_teleports_for_native_asset_works!( CheckingAccount, WeightToFee, ParachainSystem, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { @@ -501,11 +505,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!( ParachainSystem, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { 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!( Runtime, XcmConfig, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|| { assert!(Assets::asset_ids().collect::>().is_empty()); @@ -547,11 +543,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ TrustBackedAssetsInstance, AssetIdForTrustBackedAssets, AssetIdForTrustBackedAssetsConvert, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), 12345, Box::new(|| { @@ -569,11 +561,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ ForeignAssetsInstance, MultiLocation, JustTry, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) }, Box::new(|| { @@ -592,11 +580,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p ForeignAssetsInstance, MultiLocation, JustTry, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), MetadataDepositBase::get(), diff --git a/parachains/runtimes/assets/statemint/tests/tests.rs b/parachains/runtimes/assets/statemint/tests/tests.rs index 87242682b15..82491fa27f5 100644 --- a/parachains/runtimes/assets/statemint/tests/tests.rs +++ b/parachains/runtimes/assets/statemint/tests/tests.rs @@ -1,4 +1,4 @@ -use asset_test_utils::{ExtBuilder, RuntimeHelper}; +use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper}; use codec::Decode; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ @@ -26,6 +26,14 @@ const ALICE: [u8; 32] = [1u8; 32]; type AssetIdForTrustBackedAssetsConvert = assets_common::AssetIdForTrustBackedAssetsConvert; +fn collator_session_keys() -> CollatorSessionKeys { + CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) }, + ) +} + #[test] fn test_asset_xcm_trader() { ExtBuilder::::default() @@ -450,11 +458,7 @@ asset_test_utils::include_teleports_for_native_asset_works!( CheckingAccount, WeightToFee, ParachainSystem, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { 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!( Runtime, XcmConfig, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|| { assert!(Assets::asset_ids().collect::>().is_empty()); @@ -495,11 +495,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ TrustBackedAssetsInstance, AssetIdForTrustBackedAssets, AssetIdForTrustBackedAssetsConvert, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), 12345, Box::new(|| {}), diff --git a/parachains/runtimes/assets/test-utils/Cargo.toml b/parachains/runtimes/assets/test-utils/Cargo.toml index 8a1ce0e6e3c..d4e7922c576 100644 --- a/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/parachains/runtimes/assets/test-utils/Cargo.toml @@ -31,6 +31,7 @@ cumulus-primitives-core = { path = "../../../../primitives/core", default-featur 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 } +parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false } # Polkadot xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -61,6 +62,7 @@ std = [ "assets-common/std", "parachains-common/std", "parachain-info/std", + "parachains-runtimes-test-utils/std", "polkadot-parachain/std", "sp-consensus-aura/std", "sp-io/std", diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index 4a67e661322..9a24867592e 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -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}; -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}; -use sp_consensus_aura::AURA_ENGINE_ID; -use sp_core::Encode; -use sp_runtime::{Digest, DigestItem}; -use xcm::{ - latest::{MultiAsset, MultiLocation, XcmContext, XcmHash}, - prelude::*, -}; -use xcm_executor::{traits::TransactAsset, Assets}; +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. -pub mod test_cases; -pub use test_cases::CollatorSessionKeys; - -pub type BalanceOf = ::Balance; -pub type AccountIdOf = ::AccountId; -pub type ValidatorIdOf = ::ValidatorId; -pub type SessionKeysOf = ::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, BalanceOf)>, - // collators to test block prod - collators: Vec>, - // keys added to pallet session - keys: Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)>, - // safe xcm version for pallet_xcm - safe_xcm_version: Option, - // para id - para_id: Option, - _runtime: PhantomData, -} - -impl< - Runtime: frame_system::Config - + pallet_balances::Config - + pallet_session::Config - + pallet_xcm::Config - + parachain_info::Config, - > Default for ExtBuilder -{ - fn default() -> ExtBuilder { - 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 -{ - pub fn with_balances( - mut self, - balances: Vec<(AccountIdOf, BalanceOf)>, - ) -> Self { - self.balances = balances; - self - } - pub fn with_collators(mut self, collators: Vec>) -> Self { - self.collators = collators; - self - } - - pub fn with_session_keys( - mut self, - keys: Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)>, - ) -> 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: From>, - { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { safe_xcm_version: self.safe_xcm_version }, - &mut t, - ) - .unwrap(); - - if let Some(para_id) = self.para_id { - >::assimilate_storage( - ¶chain_info::GenesisConfig { parachain_id: para_id }, - &mut t, - ) - .unwrap(); - } - - pallet_balances::GenesisConfig:: { balances: self.balances } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_collator_selection::GenesisConfig:: { - invulnerables: self.collators.clone(), - candidacy_bond: Default::default(), - desired_candidates: Default::default(), - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_session::GenesisConfig:: { keys: self.keys } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - - ext.execute_with(|| { - frame_system::Pallet::::set_block_number(1u32.into()); - }); +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. - ext - } -} +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . -pub struct RuntimeHelper(PhantomData); -/// 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 RuntimeHelper -where - AccountIdOf: - Into<<::RuntimeOrigin as OriginTrait>::AccountId>, -{ - pub fn run_to_block(n: u32, author: Option) { - while frame_system::Pallet::::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::::reset_events(); - frame_system::Pallet::::initialize( - &(frame_system::Pallet::::block_number() + 1u32.into()), - &frame_system::Pallet::::parent_hash(), - &pre_digest, - ); - }, - None => { - frame_system::Pallet::::set_block_number( - frame_system::Pallet::::block_number() + 1u32.into(), - ); - }, - } - } - } +//! Module contains predefined test-case scenarios for `Runtime` with various assets. - pub fn root_origin() -> ::RuntimeOrigin { - ::RuntimeOrigin::root() - } - - pub fn origin_of( - account_id: AccountIdOf, - ) -> ::RuntimeOrigin { - ::RuntimeOrigin::signed(account_id.into()) - } -} - -impl RuntimeHelper { - pub fn do_transfer( - from: MultiLocation, - to: MultiLocation, - (asset, amount): (MultiLocation, u128), - ) -> Result { - ::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 RuntimeHelper { - pub fn do_teleport_assets( - origin: ::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, - >, - { - // open hrmp (if needed) - if let Some((source_para_id, target_para_id)) = open_hrmp_channel { - mock_open_hrmp_channel::( - source_para_id.into(), - target_para_id.into(), - ); - } - - // do teleport - >::teleport_assets( - origin, - Box::new(dest.into()), - Box::new(beneficiary.into()), - Box::new((Concrete(asset), amount).into()), - 0, - ) - } -} - -impl - RuntimeHelper -{ - pub fn execute_as_governance(call: Vec, 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); - <::XcmExecutor>::execute_xcm( - MultiLocation::parent(), - xcm, - hash, - Self::xcm_max_weight(XcmReceivedFrom::Parent), - ) - } -} - -pub enum XcmReceivedFrom { - Parent, - Sibling, -} - -impl RuntimeHelper { - 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 RuntimeHelper { - pub fn assert_pallet_xcm_event_outcome( - unwrap_pallet_xcm_event: &Box) -> Option>>, - assert_outcome: fn(Outcome), - ) { - let outcome = >::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 RuntimeHelper { - pub fn xcmp_queue_message_sent( - unwrap_xcmp_queue_event: Box< - dyn Fn(Vec) -> Option>, - >, - ) -> Option { - >::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( - asset_id: impl Into + Copy, - expected_name: &str, - expected_symbol: &str, - expected_decimals: u8, -) where - Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect - + frame_support::traits::tokens::fungibles::Inspect, -{ - 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( - asset_id: impl Into + Copy, - expected_total_issuance: impl Into, - expected_active_issuance: impl Into, -) where - Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect - + frame_support::traits::tokens::fungibles::Inspect, -{ - 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>, ->( - 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"); -} +pub mod test_cases; +pub use parachains_runtimes_test_utils::*; diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index a599db82713..366c1922cda 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -15,10 +15,6 @@ //! 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 frame_support::{ assert_noop, assert_ok, @@ -26,6 +22,10 @@ use frame_support::{ weights::Weight, }; use parachains_common::Balance; +use parachains_runtimes_test_utils::{ + assert_metadata, assert_total, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, + RuntimeHelper, ValidatorIdOf, XcmReceivedFrom, +}; use sp_runtime::{ traits::{StaticLookup, Zero}, DispatchError, Saturating, @@ -33,35 +33,6 @@ use sp_runtime::{ use xcm::latest::prelude::*; use xcm_executor::{traits::Convert, XcmExecutor}; -pub struct CollatorSessionKeys< - Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config, -> { - collator: AccountIdOf, - validator: ValidatorIdOf, - key: SessionKeysOf, -} - -impl - CollatorSessionKeys -{ - pub fn new( - collator: AccountIdOf, - validator: ValidatorIdOf, - key: SessionKeysOf, - ) -> Self { - Self { collator, validator, key } - } - pub fn collators(&self) -> Vec> { - vec![self.collator.clone()] - } - - pub fn session_keys( - &self, - ) -> Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)> { - vec![(self.collator.clone(), self.validator.clone(), self.key.clone())] - } -} - /// Test-case makes sure that `Runtime` can receive native asset from relay chain /// and can teleport it back and to the other parachains pub fn teleports_for_native_asset_works< diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index 740edc0c20b..e378e7b1e52 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -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 cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ @@ -33,6 +33,14 @@ const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = assets_common::AssetIdForTrustBackedAssetsConvert; +fn collator_session_keys() -> CollatorSessionKeys { + CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + ) +} + #[test] fn test_asset_xcm_trader() { ExtBuilder::::default() @@ -477,11 +485,7 @@ asset_test_utils::include_teleports_for_native_asset_works!( CheckingAccount, WeightToFee, ParachainSystem, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { @@ -506,11 +510,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!( ParachainSystem, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { 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!( Runtime, XcmConfig, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|| { assert!(Assets::asset_ids().collect::>().is_empty()); @@ -552,11 +548,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ TrustBackedAssetsInstance, AssetIdForTrustBackedAssets, AssetIdForTrustBackedAssetsConvert, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), 12345, Box::new(|| { @@ -574,11 +566,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ ForeignAssetsInstance, MultiLocation, JustTry, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) }, Box::new(|| { @@ -597,11 +585,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p ForeignAssetsInstance, MultiLocation, JustTry, - asset_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), MetadataDepositBase::get(), diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 2ab55ae8ca4..758474da6c2 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -91,6 +91,7 @@ bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", defau static_assertions = "1.1" bridge-hub-test-utils = { path = "../test-utils"} bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", features = ["integrity-test"] } +sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } [features] default = [ @@ -119,6 +120,7 @@ std = [ "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", "cumulus-primitives-utility/std", + "frame-benchmarking/std", "frame-executive/std", "frame-support/std", "frame-system-rpc-runtime-api/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index d94aef2cb8e..e7b3630171d 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -122,9 +122,6 @@ pub type SignedExtra = ( pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; - /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 64477b9ddc4..6a1ec2793f2 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -14,32 +14,94 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -pub use bridge_hub_rococo_runtime::{ - constants::fee::WeightToFee, - xcm_config::{RelayNetwork, XcmConfig, XcmRouter}, - Balances, BridgeGrandpaRococoInstance, BridgeGrandpaWococoInstance, BridgeWococoMessages, - DeliveryRewardInBalance, ExistentialDeposit, ParachainSystem, PolkadotXcm, - RequiredStakeForStakeAndSlash, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, -}; -use codec::{Decode, Encode}; -use xcm::latest::prelude::*; +#![cfg(test)] +use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_hub_rococo_config, bridge_hub_wococo_config, WithBridgeHubRococoMessagesInstance, - WithBridgeHubWococoMessagesInstance, + bridge_hub_rococo_config, bridge_hub_wococo_config, + constants::fee::WeightToFee, + xcm_config::{RelayNetwork, XcmConfig}, + BridgeRejectObsoleteHeadersAndMessages, DeliveryRewardInBalance, Executive, ExistentialDeposit, + ParachainSystem, PolkadotXcm, RequiredStakeForStakeAndSlash, Runtime, RuntimeCall, + RuntimeEvent, SessionKeys, SignedExtra, UncheckedExtrinsic, }; - +use codec::{Decode, Encode}; use frame_support::parameter_types; 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! { pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } +fn construct_extrinsic( + sender: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> UncheckedExtrinsic { + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(Era::immortal()), + frame_system::CheckNonce::::from(0), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::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, +) -> 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: &::Header) { + Executive::initialize_block(header) +} + +fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(Alice), + AccountId::from(Alice), + SessionKeys { aura: AuraId::from(Alice.public()) }, + ) +} + mod bridge_hub_rococo_tests { 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!( Runtime, @@ -47,11 +109,7 @@ mod bridge_hub_rococo_tests { CheckingAccount, WeightToFee, ParachainSystem, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { 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 ); - bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( - Runtime, - BridgeGrandpaWococoInstance, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()) - ); + #[test] + fn initialize_bridge_by_governance_works() { + bridge_hub_test_utils::test_cases::initialize_bridge_by_governance_works::< + Runtime, + BridgeGrandpaWococoInstance, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()), + ) + } - bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( - change_delivery_reward_by_governance_works, - Runtime, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::System(call).encode()), - (DeliveryRewardInBalance, u64), - || (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()), - |old_value| old_value.checked_mul(2).unwrap() - ); + #[test] + fn change_delivery_reward_by_governance_works() { + bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + DeliveryRewardInBalance, + u64, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()), + |old_value| old_value.checked_mul(2).unwrap(), + ) + } - bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( - change_required_stake_by_governance_works, - Runtime, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::System(call).encode()), - (RequiredStakeForStakeAndSlash, Balance), - || (RequiredStakeForStakeAndSlash::key().to_vec(), RequiredStakeForStakeAndSlash::get()), - |old_value| old_value.checked_mul(2).unwrap() - ); + #[test] + fn change_required_stake_by_governance_works() { + bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + RequiredStakeForStakeAndSlash, + Balance, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + 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!( - Runtime, - XcmConfig, - WithBridgeHubWococoMessagesInstance, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - 1000, - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event), - _ => None, - } - }), - || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, - bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO - ); + #[test] + fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { + bridge_hub_test_utils::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::< + Runtime, + XcmConfig, + WithBridgeHubWococoMessagesInstance, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event), + _ => None, + } + }), + || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, + bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO + ) + } - bridge_hub_test_utils::include_message_dispatch_routing_works!( - Runtime, - XcmConfig, - ParachainSystem, - WithBridgeHubWococoMessagesInstance, - RelayNetwork, - bridge_hub_rococo_config::WococoGlobalConsensusNetwork, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - 1000, - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), - _ => None, - } - }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), - bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO - ); + #[test] + fn message_dispatch_routing_works() { + bridge_hub_test_utils::test_cases::message_dispatch_routing_works::< + Runtime, + XcmConfig, + ParachainSystem, + WithBridgeHubWococoMessagesInstance, + RelayNetwork, + bridge_hub_rococo_config::WococoGlobalConsensusNetwork, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + 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 { 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!( Runtime, @@ -170,11 +284,7 @@ mod bridge_hub_wococo_tests { CheckingAccount, WeightToFee, ParachainSystem, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), + collator_session_keys(), ExistentialDeposit::get(), Box::new(|runtime_event_encoded: Vec| { 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 ); - bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( - Runtime, - BridgeGrandpaRococoInstance, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()) - ); + #[test] + fn initialize_bridge_by_governance_works() { + bridge_hub_test_utils::test_cases::initialize_bridge_by_governance_works::< + Runtime, + BridgeGrandpaRococoInstance, + >( + collator_session_keys(), + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()), + ) + } - bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( - change_delivery_reward_by_governance_works, - Runtime, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::System(call).encode()), - (DeliveryRewardInBalance, u64), - || (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()), - |old_value| old_value.checked_mul(2).unwrap() - ); + #[test] + fn change_delivery_reward_by_governance_works() { + bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + DeliveryRewardInBalance, + u64, + >( + collator_session_keys(), + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || (DeliveryRewardInBalance::key().to_vec(), DeliveryRewardInBalance::get()), + |old_value| old_value.checked_mul(2).unwrap(), + ) + } - bridge_hub_test_utils::include_change_storage_constant_by_governance_works!( - change_required_stake_by_governance_works, - Runtime, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::System(call).encode()), - (RequiredStakeForStakeAndSlash, Balance), - || (RequiredStakeForStakeAndSlash::key().to_vec(), RequiredStakeForStakeAndSlash::get()), - |old_value| old_value.checked_mul(2).unwrap() - ); + #[test] + fn change_required_stake_by_governance_works() { + bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + RequiredStakeForStakeAndSlash, + Balance, + >( + collator_session_keys(), + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + 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!( - Runtime, - XcmConfig, - WithBridgeHubRococoMessagesInstance, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, - 1000, - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::BridgeRococoMessages(event)) => Some(event), - _ => None, - } - }), - || ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, - bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO - ); + #[test] + fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { + bridge_hub_test_utils::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::< + Runtime, + XcmConfig, + WithBridgeHubRococoMessagesInstance, + >( + collator_session_keys(), + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeRococoMessages(event)) => Some(event), + _ => None, + } + }), + || ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, + bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO + ) + } - bridge_hub_test_utils::include_message_dispatch_routing_works!( - Runtime, - XcmConfig, - ParachainSystem, - WithBridgeHubRococoMessagesInstance, - RelayNetwork, - bridge_hub_wococo_config::RococoGlobalConsensusNetwork, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, - 1000, - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), - _ => None, - } - }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), - bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO - ); + #[test] + fn message_dispatch_routing_works() { + bridge_hub_test_utils::test_cases::message_dispatch_routing_works::< + Runtime, + XcmConfig, + ParachainSystem, + WithBridgeHubRococoMessagesInstance, + RelayNetwork, + bridge_hub_wococo_config::RococoGlobalConsensusNetwork, + >( + collator_session_keys(), + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + 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, + ); + } } diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 096c777901a..d9410a1abf0 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -10,20 +10,26 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = log = { version = "0.4.17", default-features = false } # 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-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-keyring = { git = "https://github.com/paritytech/substrate", 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-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 +asset-test-utils = { path = "../../assets/test-utils"} 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-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 } -asset-test-utils = { path = "../../assets/test-utils"} +parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false } # Polkadot 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" } # 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-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-relayers = { path = "../../../../bridges/primitives/relayers", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", 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-parachains = { path = "../../../../bridges/modules/parachains", 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 } [features] @@ -47,20 +59,28 @@ default = [ "std" ] std = [ "codec/std", "log/std", + "frame-benchmarking/std", + "frame-executive/std", "frame-support/std", "frame-system/std", "bp-messages/std", + "bp-parachains/std", "bp-polkadot-core/std", "bp-header-chain/std", + "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", "bridge-runtime-common/std", "pallet-bridge-grandpa/std", + "pallet-bridge-parachains/std", "pallet-bridge-messages/std", + "pallet-bridge-relayers/std", "parachain-info/std", + "parachains-runtimes-test-utils/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-xcmp-queue/std", "pallet-xcm/std", + "sp-core/std", "sp-io/std", "sp-runtime/std", "xcm/std", @@ -70,4 +90,5 @@ std = [ "cumulus-pallet-dmp-queue/std", "pallet-session/std", "pallet-balances/std", + "pallet-utility/std", ] diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index b65b25c525d..289d3f5b4d3 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // 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 // along with Cumulus. If not, see . -pub use bp_test_utils::test_header; +//! Module contains predefined test-case scenarios for "BridgeHub" `Runtime`s. + pub mod test_cases; -pub use test_cases::CollatorSessionKeys; +pub use bp_test_utils::test_header; +pub use parachains_runtimes_test_utils::*; diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index bc428c2791c..7d26b266dc2 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -16,28 +16,41 @@ //! 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 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_builder::DispatchBlobError; use xcm_executor::XcmExecutor; -// Lets re-use this stuff from assets (later we plan to move it outside of assets as `runtimes/test-utils`) -use asset_test_utils::{ - 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, -}; +// Re-export test_case from assets +pub use asset_test_utils::include_teleports_for_native_asset_works; /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call pub fn initialize_bridge_by_governance_works( @@ -99,29 +112,6 @@ pub fn initialize_bridge_by_governance_works( }) } -#[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 pub fn change_storage_constant_by_governance_works( collator_session_key: CollatorSessionKeys, @@ -192,35 +182,6 @@ pub fn change_storage_constant_by_governance_works { - #[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`: /// Checks if received XCM messages is correctly added to the message outbound queue for delivery. /// 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, /// We just test here `MessageDispatch` configuration. /// We expect that runtime can route messages: @@ -392,123 +322,573 @@ pub fn message_dispatch_routing_works< .with_tracing() .build() .execute_with(|| { - // 1. this message is sent from other global consensus with destination of this Runtime relay chain (UMP) - let bridging_message = - test_data::simulate_message_exporter_on_bridged_chain::( - (RuntimeNetwork::get(), Here) - ); - let result = <>::MessageDispatch>::dispatch( - test_data::dispatch_message(expected_lane_id, 1, bridging_message) + // 1. this message is sent from other global consensus with destination of this Runtime relay chain (UMP) + let bridging_message = + test_data::simulate_message_exporter_on_bridged_chain::( + (RuntimeNetwork::get(), Here) ); - assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)); - - // check events - UpwardMessageSent - let mut events = >::events() - .into_iter() - .filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode())); - assert!( - 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::( - (RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))), - ); + let result = <>::MessageDispatch>::dispatch( + test_data::dispatch_message(expected_lane_id, 1, bridging_message) + ); + assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)); - // 2.1. WITHOUT opened hrmp channel -> RoutingError - let result = - <>::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 - UpwardMessageSent + let mut events = >::events() + .into_iter() + .filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode())); + assert!( + events.any(|e| matches!(e, cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. })) + ); - // check events - no XcmpMessageSent - assert_eq!(>::events() - .into_iter() - .filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode())) - .count(), 0); + // 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::( + (RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))), + ); - // 2.1. WITH hrmp channel -> Ok - mock_open_hrmp_channel::(runtime_para_id.into(), sibling_parachain_id.into()); - let result = <>::MessageDispatch>::dispatch( + // 2.1. WITHOUT opened hrmp channel -> RoutingError + let result = + <>::MessageDispatch>::dispatch( DispatchMessage { 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)); - - // check events - XcmpMessageSent - let mut events = >::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 { .. })) + assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError)))); + + // check events - no XcmpMessageSent + assert_eq!(>::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_para_id.into(), sibling_parachain_id.into()); + let result = <>::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 = >::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 { .. })) + ); + }) +} + +/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer, +/// with proofs (finality, para heads, message) independently submitted. +pub fn relayed_incoming_message_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + bridged_para_id: u32, + sibling_parachain_id: u32, + local_relay_chain_id: NetworkId, + lane_id: LaneId, +) where + Runtime: frame_system::Config + + pallet_balances::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 + + pallet_bridge_parachains::Config + + pallet_bridge_messages::Config, + GPI: 'static, + PPI: 'static, + MPI: 'static, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + UnderlyingChainOf>: bp_runtime::Chain + Parachain, + XcmConfig: xcm_executor::Config, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + ValidatorIdOf: From>, + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: From>, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, + ParaHash: From<<>::BridgedChain as bp_runtime::Chain>::Hash>, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + AccountIdOf: From, + >::InboundRelayer: From, +{ + assert_ne!(runtime_para_id, sibling_parachain_id); + assert_ne!(runtime_para_id, bridged_para_id); + + ExtBuilder::::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_para_id.into(), + sibling_parachain_id.into(), + ); + + // start with bridged chain block#0 + let init_data = test_data::initialization_data::(0); + pallet_bridge_grandpa::Pallet::::initialize( + RuntimeHelper::::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 = 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::, 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::::submit_finality_proof( + RuntimeHelper::::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::::get().unwrap().1, + relay_chain_header.hash() + ); + assert!(pallet_bridge_grandpa::ImportedHeaders::::contains_key( + relay_chain_header.hash() + )); + + // submit parachain heads proof + { + let result = + pallet_bridge_parachains::Pallet::::submit_parachain_heads( + RuntimeHelper::::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::::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::>::take_xcm( + sibling_parachain_id.into() + ) + .is_none()); + assert_eq!( + pallet_bridge_messages::InboundLanes::::get(lane_id) + .last_delivered_nonce(), + 0, + ); + // submit message proof + { + let result = pallet_bridge_messages::Pallet::::receive_messages_proof( + RuntimeHelper::::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::::get(lane_id) + .last_delivered_nonce(), + 1, + ); + // verify relayed bridged XCM message is dispatched to destination sibling para + let dispatched = RuntimeHelper::>::take_xcm( + sibling_parachain_id.into(), + ) + .unwrap(); + assert_eq!(dispatched, expected_dispatch); }) } -#[macro_export] -macro_rules! include_message_dispatch_routing_works( - ( - $runtime:path, - $xcm_config:path, - $hrmp_channel_opener:path, - $pallet_bridge_messages_instance:path, - $runtime_network:path, - $bridged_network:path, - $collator_session_key:expr, - $runtime_para_id:expr, - $sibling_parachain_id:expr, - $unwrap_cumulus_pallet_parachain_system_event:expr, - $unwrap_cumulus_pallet_xcmp_queue_event:expr, - $expected_lane_id:expr - ) => { - #[test] - fn message_dispatch_routing_works() { - $crate::test_cases::message_dispatch_routing_works::< - $runtime, - $xcm_config, - $hrmp_channel_opener, - $pallet_bridge_messages_instance, - $runtime_network, - $bridged_network - >( - $collator_session_key, - $runtime_para_id, - $sibling_parachain_id, - $unwrap_cumulus_pallet_parachain_system_event, - $unwrap_cumulus_pallet_xcmp_queue_event, - $expected_lane_id, +/// 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( + collator_session_key: CollatorSessionKeys, + 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, + executive_init_block: fn(&::Header), + construct_and_apply_extrinsic: fn( + sp_keyring::AccountKeyring, + pallet_utility::Call:: + ) -> 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 + + pallet_bridge_parachains::Config + + pallet_bridge_messages::Config + + pallet_bridge_relayers::Config, + GPI: 'static, + PPI: 'static, + MPI: 'static, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + UnderlyingChainOf>: bp_runtime::Chain + Parachain, + XcmConfig: xcm_executor::Config, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + ValidatorIdOf: From>, + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: From>, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, + ParaHash: From<<>::BridgedChain as bp_runtime::Chain>::Hash>, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + AccountIdOf: From, + >::InboundRelayer: From, + ::RuntimeCall: + From> + + From> + + From> +{ + 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 = 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::::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: ::BlockNumber = 0u32.into(); + let genesis_hash = frame_system::Pallet::::block_hash(zero); + let mut header: ::Header = + bp_test_utils::test_header(1u32.into()); + header.set_parent_hash(genesis_hash); + executive_init_block(&header); + + mock_open_hrmp_channel::( + runtime_para_id.into(), + sibling_parachain_id.into(), + ); + + // start with bridged chain block#0 + let init_data = test_data::initialization_data::(0); + pallet_bridge_grandpa::Pallet::::initialize( + RuntimeHelper::::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::, MB, ()>( + lane_id, + xcm.into(), + message_nonce, + message_destination, + para_header_number, + relay_header_number, + bridged_para_id, + ); -mod test_data { + let submit_grandpa = + pallet_bridge_grandpa::Call::::submit_finality_proof { + finality_target: Box::new(relay_chain_header.clone()), + justification: grandpa_justification, + }; + let submit_para_head = + pallet_bridge_parachains::Call::::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::::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::::batch_all { + calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()], + }; + + // sanity checks - before relayer extrinsic + assert!(RuntimeHelper::>::take_xcm( + sibling_parachain_id.into() + ) + .is_none()); + assert_eq!( + pallet_bridge_messages::InboundLanes::::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::::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!( + >::get().unwrap().1, + relay_chain_header.hash() + ); + assert!(>::contains_key( + relay_chain_header.hash() + )); + // verify parachain head proof correctly imported + assert_eq!( + pallet_bridge_parachains::ParasInfo::::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::::get(lane_id) + .last_delivered_nonce(), + 1, + ); + // verify relayer is refunded + assert!(pallet_bridge_relayers::RelayerRewards::::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::>::take_xcm( + sibling_parachain_id.into(), + ) + .unwrap(); + assert_eq!(dispatched, expected_dispatch); + }) +} + +pub mod test_data { use super::*; + use bp_header_chain::justification::GrandpaJustification; 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_executor::traits::{validate_export, ExportXcm}; + pub fn prepare_inbound_xcm( + xcm_message: Xcm, + destination: InteriorMultiLocation, + ) -> Vec { + let location = xcm::VersionedInteriorMultiLocation::V3(destination); + let xcm = xcm::VersionedXcm::::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( + lane_id: LaneId, + xcm_message: Xcm, + message_nonce: MessageNonce, + message_destination: Junctions, + para_header_number: u32, + relay_header_number: u32, + bridged_para_id: u32, + ) -> ( + BridgedRelayHeader, + GrandpaJustification, + ParaHead, + Vec<(ParaId, ParaHash)>, + ParaHeadsProof, + FromBridgedChainMessagesProof, + ) + where + BridgedRelayHeader: HeaderT, + ::Hash: From, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + UnderlyingChainOf>: bp_runtime::Chain + 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::( + 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::>( + para_header_number.into(), + para_state_root.into(), + ) + .encode(), + ); + let (relay_state_root, para_heads_proof, parachain_heads) = + prepare_parachain_heads_proof::>(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 - pub(crate) fn initialization_data< + pub fn initialization_data< Runtime: pallet_bridge_grandpa::Config, GrandpaPalletInstance: 'static, >( block_number: u32, - ) -> bp_header_chain::InitializationData< - pallet_bridge_grandpa::BridgedHeader, - > { + ) -> bp_header_chain::InitializationData> { bp_header_chain::InitializationData { header: Box::new(bp_test_utils::test_header(block_number.into())), - authority_list: Default::default(), - set_id: 6, - operating_mode: bp_runtime::BasicOperatingMode::Normal, + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, } } diff --git a/parachains/runtimes/test-utils/Cargo.toml b/parachains/runtimes/test-utils/Cargo.toml new file mode 100644 index 00000000000..2b8ee2fb322 --- /dev/null +++ b/parachains/runtimes/test-utils/Cargo.toml @@ -0,0 +1,74 @@ +[package] +name = "parachains-runtimes-test-utils" +version = "1.0.0" +authors = ["Parity Technologies "] +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", +] diff --git a/parachains/runtimes/test-utils/src/lib.rs b/parachains/runtimes/test-utils/src/lib.rs new file mode 100644 index 00000000000..7a59650db51 --- /dev/null +++ b/parachains/runtimes/test-utils/src/lib.rs @@ -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 = ::Balance; +pub type AccountIdOf = ::AccountId; +pub type ValidatorIdOf = ::ValidatorId; +pub type SessionKeysOf = ::Keys; + +pub struct CollatorSessionKeys< + Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config, +> { + collator: AccountIdOf, + validator: ValidatorIdOf, + key: SessionKeysOf, +} + +impl + CollatorSessionKeys +{ + pub fn new( + collator: AccountIdOf, + validator: ValidatorIdOf, + key: SessionKeysOf, + ) -> Self { + Self { collator, validator, key } + } + pub fn collators(&self) -> Vec> { + vec![self.collator.clone()] + } + + pub fn session_keys( + &self, + ) -> Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)> { + 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, BalanceOf)>, + // collators to test block prod + collators: Vec>, + // keys added to pallet session + keys: Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)>, + // safe xcm version for pallet_xcm + safe_xcm_version: Option, + // para id + para_id: Option, + _runtime: PhantomData, +} + +impl< + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config, + > Default for ExtBuilder +{ + fn default() -> ExtBuilder { + 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 +{ + pub fn with_balances( + mut self, + balances: Vec<(AccountIdOf, BalanceOf)>, + ) -> Self { + self.balances = balances; + self + } + pub fn with_collators(mut self, collators: Vec>) -> Self { + self.collators = collators; + self + } + + pub fn with_session_keys( + mut self, + keys: Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)>, + ) -> 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: From>, + { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + >::assimilate_storage( + &pallet_xcm::GenesisConfig { safe_xcm_version: self.safe_xcm_version }, + &mut t, + ) + .unwrap(); + + if let Some(para_id) = self.para_id { + >::assimilate_storage( + ¶chain_info::GenesisConfig { parachain_id: para_id }, + &mut t, + ) + .unwrap(); + } + + pallet_balances::GenesisConfig:: { balances: self.balances } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_collator_selection::GenesisConfig:: { + invulnerables: self.collators.clone(), + candidacy_bond: Default::default(), + desired_candidates: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_session::GenesisConfig:: { keys: self.keys } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + + ext.execute_with(|| { + frame_system::Pallet::::set_block_number(1u32.into()); + }); + + ext + } +} + +pub struct RuntimeHelper(PhantomData); +/// 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 RuntimeHelper +where + AccountIdOf: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, +{ + pub fn run_to_block(n: u32, author: Option) { + while frame_system::Pallet::::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::::reset_events(); + frame_system::Pallet::::initialize( + &(frame_system::Pallet::::block_number() + 1u32.into()), + &frame_system::Pallet::::parent_hash(), + &pre_digest, + ); + }, + None => { + frame_system::Pallet::::set_block_number( + frame_system::Pallet::::block_number() + 1u32.into(), + ); + }, + } + } + } + + pub fn root_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::root() + } + + pub fn origin_of( + account_id: AccountIdOf, + ) -> ::RuntimeOrigin { + ::RuntimeOrigin::signed(account_id.into()) + } +} + +impl RuntimeHelper { + pub fn do_transfer( + from: MultiLocation, + to: MultiLocation, + (asset, amount): (MultiLocation, u128), + ) -> Result { + ::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 RuntimeHelper { + pub fn do_teleport_assets( + origin: ::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, + >, + { + // open hrmp (if needed) + if let Some((source_para_id, target_para_id)) = open_hrmp_channel { + mock_open_hrmp_channel::( + source_para_id.into(), + target_para_id.into(), + ); + } + + // do teleport + >::teleport_assets( + origin, + Box::new(dest.into()), + Box::new(beneficiary.into()), + Box::new((Concrete(asset), amount).into()), + 0, + ) + } +} + +impl + RuntimeHelper +{ + pub fn execute_as_governance(call: Vec, 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); + <::XcmExecutor>::execute_xcm( + MultiLocation::parent(), + xcm, + hash, + Self::xcm_max_weight(XcmReceivedFrom::Parent), + ) + } +} + +pub enum XcmReceivedFrom { + Parent, + Sibling, +} + +impl RuntimeHelper { + 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 RuntimeHelper { + pub fn assert_pallet_xcm_event_outcome( + unwrap_pallet_xcm_event: &Box) -> Option>>, + assert_outcome: fn(Outcome), + ) { + let outcome = >::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 RuntimeHelper { + pub fn xcmp_queue_message_sent( + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, + ) -> Option { + >::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( + asset_id: impl Into + Copy, + expected_name: &str, + expected_symbol: &str, + expected_decimals: u8, +) where + Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect + + frame_support::traits::tokens::fungibles::Inspect, +{ + 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( + asset_id: impl Into + Copy, + expected_total_issuance: impl Into, + expected_active_issuance: impl Into, +) where + Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect + + frame_support::traits::tokens::fungibles::Inspect, +{ + 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>, +>( + 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 + RuntimeHelper +{ + pub fn take_xcm(sent_to_para_id: ParaId) -> Option> { + 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, + } + } +}