parachains_runtimes_test_utils/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use core::marker::PhantomData;
17
18use codec::{Decode, DecodeLimit};
19use cumulus_primitives_core::{
20	relay_chain::Slot, AbridgedHrmpChannel, ParaId, PersistedValidationData,
21};
22use cumulus_primitives_parachain_inherent::ParachainInherentData;
23use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
24use frame_support::{
25	dispatch::{DispatchResult, GetDispatchInfo, RawOrigin},
26	inherent::{InherentData, ProvideInherent},
27	pallet_prelude::Get,
28	traits::{OnFinalize, OnInitialize, OriginTrait, UnfilteredDispatchable},
29	weights::Weight,
30};
31use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor};
32use polkadot_parachain_primitives::primitives::{
33	HeadData, HrmpChannelId, RelayChainBlockNumber, XcmpMessageFormat,
34};
35use sp_consensus_aura::{SlotDuration, AURA_ENGINE_ID};
36use sp_core::{Encode, U256};
37use sp_runtime::{
38	traits::{Dispatchable, Header},
39	BuildStorage, Digest, DigestItem, DispatchError, Either, SaturatedConversion,
40};
41use xcm::{
42	latest::{Asset, Location, XcmContext, XcmHash},
43	prelude::*,
44	VersionedXcm, MAX_XCM_DECODE_DEPTH,
45};
46use xcm_executor::{traits::TransactAsset, AssetsInHolding};
47
48pub mod test_cases;
49
50pub type BalanceOf<Runtime> = <Runtime as pallet_balances::Config>::Balance;
51pub type AccountIdOf<Runtime> = <Runtime as frame_system::Config>::AccountId;
52pub type RuntimeCallOf<Runtime> = <Runtime as frame_system::Config>::RuntimeCall;
53pub type RuntimeOriginOf<Runtime> = <Runtime as frame_system::Config>::RuntimeOrigin;
54pub type ValidatorIdOf<Runtime> = <Runtime as pallet_session::Config>::ValidatorId;
55pub type SessionKeysOf<Runtime> = <Runtime as pallet_session::Config>::Keys;
56
57pub struct CollatorSessionKey<
58	Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config,
59> {
60	collator: AccountIdOf<Runtime>,
61	validator: ValidatorIdOf<Runtime>,
62	key: SessionKeysOf<Runtime>,
63}
64
65pub struct CollatorSessionKeys<
66	Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config,
67> {
68	items: Vec<CollatorSessionKey<Runtime>>,
69}
70
71impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config>
72	CollatorSessionKey<Runtime>
73{
74	pub fn new(
75		collator: AccountIdOf<Runtime>,
76		validator: ValidatorIdOf<Runtime>,
77		key: SessionKeysOf<Runtime>,
78	) -> Self {
79		Self { collator, validator, key }
80	}
81}
82
83impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config> Default
84	for CollatorSessionKeys<Runtime>
85{
86	fn default() -> Self {
87		Self { items: vec![] }
88	}
89}
90
91impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config>
92	CollatorSessionKeys<Runtime>
93{
94	pub fn new(
95		collator: AccountIdOf<Runtime>,
96		validator: ValidatorIdOf<Runtime>,
97		key: SessionKeysOf<Runtime>,
98	) -> Self {
99		Self { items: vec![CollatorSessionKey::new(collator, validator, key)] }
100	}
101
102	pub fn add(mut self, item: CollatorSessionKey<Runtime>) -> Self {
103		self.items.push(item);
104		self
105	}
106
107	pub fn collators(&self) -> Vec<AccountIdOf<Runtime>> {
108		self.items.iter().map(|item| item.collator.clone()).collect::<Vec<_>>()
109	}
110
111	pub fn session_keys(
112		&self,
113	) -> Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)> {
114		self.items
115			.iter()
116			.map(|item| (item.collator.clone(), item.validator.clone(), item.key.clone()))
117			.collect::<Vec<_>>()
118	}
119}
120
121pub struct SlotDurations {
122	pub relay: SlotDuration,
123	pub para: SlotDuration,
124}
125
126/// A set of traits for a minimal parachain runtime, that may be used in conjunction with the
127/// `ExtBuilder` and the `RuntimeHelper`.
128pub trait BasicParachainRuntime:
129	frame_system::Config
130	+ pallet_balances::Config
131	+ pallet_session::Config
132	+ pallet_xcm::Config
133	+ parachain_info::Config
134	+ pallet_collator_selection::Config
135	+ cumulus_pallet_parachain_system::Config
136	+ pallet_timestamp::Config
137{
138}
139
140impl<T> BasicParachainRuntime for T
141where
142	T: frame_system::Config
143		+ pallet_balances::Config
144		+ pallet_session::Config
145		+ pallet_xcm::Config
146		+ parachain_info::Config
147		+ pallet_collator_selection::Config
148		+ cumulus_pallet_parachain_system::Config
149		+ pallet_timestamp::Config,
150	ValidatorIdOf<T>: From<AccountIdOf<T>>,
151{
152}
153
154/// Basic builder based on balances, collators and pallet_session.
155pub struct ExtBuilder<Runtime: BasicParachainRuntime> {
156	// endowed accounts with balances
157	balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
158	// collators to test block prod
159	collators: Vec<AccountIdOf<Runtime>>,
160	// keys added to pallet session
161	keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
162	// safe XCM version for pallet_xcm
163	safe_xcm_version: Option<XcmVersion>,
164	// para id
165	para_id: Option<ParaId>,
166	_runtime: PhantomData<Runtime>,
167}
168
169impl<Runtime: BasicParachainRuntime> Default for ExtBuilder<Runtime> {
170	fn default() -> ExtBuilder<Runtime> {
171		ExtBuilder {
172			balances: vec![],
173			collators: vec![],
174			keys: vec![],
175			safe_xcm_version: None,
176			para_id: None,
177			_runtime: PhantomData,
178		}
179	}
180}
181
182impl<Runtime: BasicParachainRuntime> ExtBuilder<Runtime> {
183	pub fn with_balances(
184		mut self,
185		balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
186	) -> Self {
187		self.balances = balances;
188		self
189	}
190	pub fn with_collators(mut self, collators: Vec<AccountIdOf<Runtime>>) -> Self {
191		self.collators = collators;
192		self
193	}
194
195	pub fn with_session_keys(
196		mut self,
197		keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
198	) -> Self {
199		self.keys = keys;
200		self
201	}
202
203	pub fn with_tracing(self) -> Self {
204		sp_tracing::try_init_simple();
205		self
206	}
207
208	pub fn with_safe_xcm_version(mut self, safe_xcm_version: XcmVersion) -> Self {
209		self.safe_xcm_version = Some(safe_xcm_version);
210		self
211	}
212
213	pub fn with_para_id(mut self, para_id: ParaId) -> Self {
214		self.para_id = Some(para_id);
215		self
216	}
217
218	pub fn build(self) -> sp_io::TestExternalities {
219		let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
220
221		pallet_xcm::GenesisConfig::<Runtime> {
222			safe_xcm_version: self.safe_xcm_version,
223			..Default::default()
224		}
225		.assimilate_storage(&mut t)
226		.unwrap();
227
228		if let Some(para_id) = self.para_id {
229			parachain_info::GenesisConfig::<Runtime> {
230				parachain_id: para_id,
231				..Default::default()
232			}
233			.assimilate_storage(&mut t)
234			.unwrap();
235		}
236
237		pallet_balances::GenesisConfig::<Runtime> { balances: self.balances, ..Default::default() }
238			.assimilate_storage(&mut t)
239			.unwrap();
240
241		pallet_collator_selection::GenesisConfig::<Runtime> {
242			invulnerables: self.collators.clone(),
243			candidacy_bond: Default::default(),
244			desired_candidates: Default::default(),
245		}
246		.assimilate_storage(&mut t)
247		.unwrap();
248
249		pallet_session::GenesisConfig::<Runtime> { keys: self.keys, ..Default::default() }
250			.assimilate_storage(&mut t)
251			.unwrap();
252
253		let mut ext = sp_io::TestExternalities::new(t);
254
255		ext.execute_with(|| {
256			frame_system::Pallet::<Runtime>::set_block_number(1u32.into());
257		});
258
259		ext
260	}
261}
262
263pub struct RuntimeHelper<Runtime, AllPalletsWithoutSystem>(
264	PhantomData<(Runtime, AllPalletsWithoutSystem)>,
265);
266/// Utility function that advances the chain to the desired block number.
267/// If an author is provided, that author information is injected to all the blocks in the meantime.
268impl<
269		Runtime: frame_system::Config + cumulus_pallet_parachain_system::Config + pallet_timestamp::Config,
270		AllPalletsWithoutSystem,
271	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
272where
273	AccountIdOf<Runtime>:
274		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
275	AllPalletsWithoutSystem:
276		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
277{
278	pub fn run_to_block(n: u32, author: AccountIdOf<Runtime>) -> HeaderFor<Runtime> {
279		let mut last_header = None;
280		loop {
281			let block_number = frame_system::Pallet::<Runtime>::block_number();
282			if block_number >= n.into() {
283				break
284			}
285			// Set the new block number and author
286
287			// Inherent is not created at every block, don't finalize parachain
288			// system to avoid panicking.
289			let header = frame_system::Pallet::<Runtime>::finalize();
290
291			let pre_digest =
292				Digest { logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, author.encode())] };
293			frame_system::Pallet::<Runtime>::reset_events();
294
295			let next_block_number = block_number + 1u32.into();
296			frame_system::Pallet::<Runtime>::initialize(
297				&next_block_number,
298				&header.hash(),
299				&pre_digest,
300			);
301			AllPalletsWithoutSystem::on_initialize(next_block_number);
302			last_header = Some(header);
303		}
304		last_header.expect("run_to_block empty block range")
305	}
306
307	pub fn run_to_block_with_finalize(n: u32) -> HeaderFor<Runtime> {
308		let mut last_header = None;
309		loop {
310			let block_number = frame_system::Pallet::<Runtime>::block_number();
311			if block_number >= n.into() {
312				break
313			}
314			// Set the new block number and author
315			let header = frame_system::Pallet::<Runtime>::finalize();
316
317			let pre_digest = Digest {
318				logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, block_number.encode())],
319			};
320			frame_system::Pallet::<Runtime>::reset_events();
321
322			let next_block_number = block_number + 1u32.into();
323			frame_system::Pallet::<Runtime>::initialize(
324				&next_block_number,
325				&header.hash(),
326				&pre_digest,
327			);
328			AllPalletsWithoutSystem::on_initialize(next_block_number);
329
330			let parent_head = HeadData(header.encode());
331			let sproof_builder = RelayStateSproofBuilder {
332				para_id: <Runtime>::SelfParaId::get(),
333				included_para_head: parent_head.clone().into(),
334				..Default::default()
335			};
336
337			let (relay_parent_storage_root, relay_chain_state) =
338				sproof_builder.into_state_root_and_proof();
339			let inherent_data = ParachainInherentData {
340				validation_data: PersistedValidationData {
341					parent_head,
342					relay_parent_number: (block_number.saturated_into::<u32>() * 2 + 1).into(),
343					relay_parent_storage_root,
344					max_pov_size: 100_000_000,
345				},
346				relay_chain_state,
347				downward_messages: Default::default(),
348				horizontal_messages: Default::default(),
349				relay_parent_descendants: Default::default(),
350				collator_peer_id: None,
351			};
352
353			let _ = cumulus_pallet_parachain_system::Pallet::<Runtime>::set_validation_data(
354				Runtime::RuntimeOrigin::none(),
355				inherent_data,
356			);
357			let _ = pallet_timestamp::Pallet::<Runtime>::set(
358				Runtime::RuntimeOrigin::none(),
359				300_u32.into(),
360			);
361			AllPalletsWithoutSystem::on_finalize(next_block_number);
362			let header = frame_system::Pallet::<Runtime>::finalize();
363			last_header = Some(header);
364		}
365		last_header.expect("run_to_block empty block range")
366	}
367
368	pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
369		<Runtime as frame_system::Config>::RuntimeOrigin::root()
370	}
371
372	pub fn block_number() -> U256 {
373		frame_system::Pallet::<Runtime>::block_number().into()
374	}
375
376	pub fn origin_of(
377		account_id: AccountIdOf<Runtime>,
378	) -> <Runtime as frame_system::Config>::RuntimeOrigin {
379		<Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id.into())
380	}
381}
382
383impl<XcmConfig: xcm_executor::Config, AllPalletsWithoutSystem>
384	RuntimeHelper<XcmConfig, AllPalletsWithoutSystem>
385{
386	pub fn do_transfer(
387		from: Location,
388		to: Location,
389		(asset, amount): (Location, u128),
390	) -> Result<AssetsInHolding, XcmError> {
391		<XcmConfig::AssetTransactor as TransactAsset>::transfer_asset(
392			&Asset { id: AssetId(asset), fun: Fungible(amount) },
393			&from,
394			&to,
395			// We aren't able to track the XCM that initiated the fee deposit, so we create a
396			// fake message hash here
397			&XcmContext::with_message_id([0; 32]),
398		)
399	}
400}
401
402impl<
403		Runtime: pallet_xcm::Config + cumulus_pallet_parachain_system::Config,
404		AllPalletsWithoutSystem,
405	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
406{
407	pub fn do_teleport_assets<HrmpChannelOpener>(
408		origin: <Runtime as frame_system::Config>::RuntimeOrigin,
409		dest: Location,
410		beneficiary: Location,
411		(asset, amount): (Location, u128),
412		open_hrmp_channel: Option<(u32, u32)>,
413		included_head: HeaderFor<Runtime>,
414		slot_digest: &[u8],
415		slot_durations: &SlotDurations,
416	) -> DispatchResult
417	where
418		HrmpChannelOpener: frame_support::inherent::ProvideInherent<
419			Call = cumulus_pallet_parachain_system::Call<Runtime>,
420		>,
421	{
422		// open hrmp (if needed)
423		if let Some((source_para_id, target_para_id)) = open_hrmp_channel {
424			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
425				source_para_id.into(),
426				target_para_id.into(),
427				included_head,
428				slot_digest,
429				slot_durations,
430			);
431		}
432
433		// do teleport
434		<pallet_xcm::Pallet<Runtime>>::limited_teleport_assets(
435			origin,
436			Box::new(dest.into()),
437			Box::new(beneficiary.into()),
438			Box::new((AssetId(asset), amount).into()),
439			0,
440			Unlimited,
441		)
442	}
443}
444
445impl<
446		Runtime: cumulus_pallet_parachain_system::Config + pallet_xcm::Config,
447		AllPalletsWithoutSystem,
448	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
449{
450	#[deprecated(
451		note = "Will be removed after Aug 2025; It uses hard-coded `Location::parent()`, \
452		use `execute_as_governance_call` instead."
453	)]
454	pub fn execute_as_governance(call: Vec<u8>) -> Outcome {
455		// prepare xcm as governance will do
456		let xcm = Xcm(vec![
457			UnpaidExecution { weight_limit: Unlimited, check_origin: None },
458			Transact {
459				origin_kind: OriginKind::Superuser,
460				call: call.into(),
461				fallback_max_weight: None,
462			},
463			ExpectTransactStatus(MaybeErrorCode::Success),
464		]);
465
466		// execute xcm as parent origin
467		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
468		<<Runtime as pallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
469			Location::parent(),
470			xcm,
471			&mut hash,
472			Self::xcm_max_weight(XcmReceivedFrom::Parent),
473			Weight::zero(),
474		)
475	}
476
477	pub fn execute_as_governance_call<Call: Dispatchable + Encode>(
478		call: Call,
479		governance_origin: GovernanceOrigin<Call::RuntimeOrigin>,
480	) -> Result<(), Either<DispatchError, InstructionError>> {
481		// execute xcm as governance would send
482		let execute_xcm = |call: Call, governance_location, descend_origin| {
483			// prepare xcm
484			let xcm = if let Some(descend_origin) = descend_origin {
485				Xcm::builder_unsafe().descend_origin(descend_origin)
486			} else {
487				Xcm::builder_unsafe()
488			}
489			.unpaid_execution(Unlimited, None)
490			.transact(OriginKind::Superuser, None, call.encode())
491			.expect_transact_status(MaybeErrorCode::Success)
492			.build();
493
494			let xcm_max_weight = Self::xcm_max_weight_for_location(&governance_location);
495			let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
496
497			<<Runtime as pallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
498				governance_location,
499				xcm,
500				&mut hash,
501				xcm_max_weight,
502				Weight::zero(),
503			)
504		};
505
506		match governance_origin {
507			GovernanceOrigin::Location(location) =>
508				execute_xcm(call, location, None).ensure_complete().map_err(Either::Right),
509			GovernanceOrigin::LocationAndDescendOrigin(location, descend_origin) =>
510				execute_xcm(call, location, Some(descend_origin))
511					.ensure_complete()
512					.map_err(Either::Right),
513			GovernanceOrigin::Origin(origin) =>
514				call.dispatch(origin).map(|_| ()).map_err(|e| Either::Left(e.error)),
515		}
516	}
517
518	pub fn execute_as_origin<Call: GetDispatchInfo + Encode>(
519		(origin, origin_kind): (Location, OriginKind),
520		call: Call,
521		maybe_buy_execution_fee: Option<Asset>,
522	) -> Outcome {
523		let mut instructions = if let Some(buy_execution_fee) = maybe_buy_execution_fee {
524			vec![
525				WithdrawAsset(buy_execution_fee.clone().into()),
526				BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
527			]
528		} else {
529			vec![UnpaidExecution { check_origin: None, weight_limit: Unlimited }]
530		};
531
532		// prepare `Transact` xcm
533		instructions.extend(vec![
534			Transact { origin_kind, call: call.encode().into(), fallback_max_weight: None },
535			ExpectTransactStatus(MaybeErrorCode::Success),
536		]);
537		let xcm = Xcm(instructions);
538		let xcm_max_weight = Self::xcm_max_weight_for_location(&origin);
539
540		// execute xcm as parent origin
541		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
542		<<Runtime as pallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
543			origin,
544			xcm,
545			&mut hash,
546			xcm_max_weight,
547			Weight::zero(),
548		)
549	}
550}
551
552/// Enum representing governance origin/location.
553#[derive(Clone)]
554pub enum GovernanceOrigin<RuntimeOrigin> {
555	Location(Location),
556	LocationAndDescendOrigin(Location, InteriorLocation),
557	Origin(RuntimeOrigin),
558}
559
560pub enum XcmReceivedFrom {
561	Parent,
562	Sibling,
563}
564
565impl<ParachainSystem: cumulus_pallet_parachain_system::Config, AllPalletsWithoutSystem>
566	RuntimeHelper<ParachainSystem, AllPalletsWithoutSystem>
567{
568	pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight {
569		match from {
570			XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(),
571			XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(),
572		}
573	}
574
575	pub fn xcm_max_weight_for_location(location: &Location) -> Weight {
576		Self::xcm_max_weight(if location == &Location::parent() {
577			XcmReceivedFrom::Parent
578		} else {
579			XcmReceivedFrom::Sibling
580		})
581	}
582}
583
584impl<Runtime: frame_system::Config + pallet_xcm::Config, AllPalletsWithoutSystem>
585	RuntimeHelper<Runtime, AllPalletsWithoutSystem>
586{
587	pub fn assert_pallet_xcm_event_outcome(
588		unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
589		assert_outcome: fn(Outcome),
590	) {
591		assert_outcome(Self::get_pallet_xcm_event_outcome(unwrap_pallet_xcm_event));
592	}
593
594	pub fn get_pallet_xcm_event_outcome(
595		unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
596	) -> Outcome {
597		<frame_system::Pallet<Runtime>>::events()
598			.into_iter()
599			.filter_map(|e| unwrap_pallet_xcm_event(e.event.encode()))
600			.find_map(|e| match e {
601				pallet_xcm::Event::Attempted { outcome } => Some(outcome),
602				_ => None,
603			})
604			.expect("No `pallet_xcm::Event::Attempted(outcome)` event found!")
605	}
606}
607
608impl<
609		Runtime: frame_system::Config + cumulus_pallet_xcmp_queue::Config,
610		AllPalletsWithoutSystem,
611	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
612{
613	pub fn xcmp_queue_message_sent(
614		unwrap_xcmp_queue_event: Box<
615			dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
616		>,
617	) -> Option<XcmHash> {
618		<frame_system::Pallet<Runtime>>::events()
619			.into_iter()
620			.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
621			.find_map(|e| match e {
622				cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
623					Some(message_hash),
624				_ => None,
625			})
626	}
627}
628
629pub fn assert_metadata<Fungibles, AccountId>(
630	asset_id: impl Into<Fungibles::AssetId> + Clone,
631	expected_name: &str,
632	expected_symbol: &str,
633	expected_decimals: u8,
634) where
635	Fungibles: frame_support::traits::fungibles::metadata::Inspect<AccountId>
636		+ frame_support::traits::fungibles::Inspect<AccountId>,
637{
638	assert_eq!(Fungibles::name(asset_id.clone().into()), Vec::from(expected_name),);
639	assert_eq!(Fungibles::symbol(asset_id.clone().into()), Vec::from(expected_symbol),);
640	assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals);
641}
642
643pub fn assert_total<Fungibles, AccountId>(
644	asset_id: impl Into<Fungibles::AssetId> + Clone,
645	expected_total_issuance: impl Into<Fungibles::Balance>,
646	expected_active_issuance: impl Into<Fungibles::Balance>,
647) where
648	Fungibles: frame_support::traits::fungibles::metadata::Inspect<AccountId>
649		+ frame_support::traits::fungibles::Inspect<AccountId>,
650{
651	assert_eq!(Fungibles::total_issuance(asset_id.clone().into()), expected_total_issuance.into());
652	assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into());
653}
654
655/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass.
656///
657/// Calls parachain-system's `create_inherent` in case the channel hasn't been opened before, and
658/// thus requires additional parameters for validating it: latest included parachain head and
659/// parachain AuRa-slot.
660///
661/// AuRa consensus hook expects pallets to be initialized, before calling this function make sure to
662/// `run_to_block` at least once.
663pub fn mock_open_hrmp_channel<
664	C: cumulus_pallet_parachain_system::Config,
665	T: ProvideInherent<Call = cumulus_pallet_parachain_system::Call<C>>,
666>(
667	sender: ParaId,
668	recipient: ParaId,
669	included_head: HeaderFor<C>,
670	mut slot_digest: &[u8],
671	slot_durations: &SlotDurations,
672) {
673	let slot = Slot::decode(&mut slot_digest).expect("failed to decode digest");
674	// Convert para slot to relay chain.
675	let timestamp = slot.saturating_mul(slot_durations.para.as_millis());
676	let relay_slot = Slot::from_timestamp(timestamp.into(), slot_durations.relay);
677
678	let n = 1_u32;
679	let mut sproof_builder = RelayStateSproofBuilder {
680		para_id: sender,
681		included_para_head: Some(HeadData(included_head.encode())),
682		hrmp_egress_channel_index: Some(vec![recipient]),
683		current_slot: relay_slot,
684		..Default::default()
685	};
686	sproof_builder.hrmp_channels.insert(
687		HrmpChannelId { sender, recipient },
688		AbridgedHrmpChannel {
689			max_capacity: 10,
690			max_total_size: 10_000_000_u32,
691			max_message_size: 10_000_000_u32,
692			msg_count: 0,
693			total_size: 0_u32,
694			mqc_head: None,
695		},
696	);
697
698	let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof();
699	let vfp = PersistedValidationData {
700		relay_parent_number: n as RelayChainBlockNumber,
701		relay_parent_storage_root,
702		..Default::default()
703	};
704	// It is insufficient to push the validation function params
705	// to storage; they must also be included in the inherent data.
706	let inherent_data = {
707		let mut inherent_data = InherentData::default();
708		let system_inherent_data = ParachainInherentData {
709			validation_data: vfp,
710			relay_chain_state,
711			downward_messages: Default::default(),
712			horizontal_messages: Default::default(),
713			relay_parent_descendants: Default::default(),
714			collator_peer_id: None,
715		};
716		inherent_data
717			.put_data(
718				cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER,
719				&system_inherent_data,
720			)
721			.expect("failed to put VFP inherent");
722		inherent_data
723	};
724
725	// execute the block
726	T::create_inherent(&inherent_data)
727		.expect("got an inherent")
728		.dispatch_bypass_filter(RawOrigin::None.into())
729		.expect("dispatch succeeded");
730}
731
732impl<HrmpChannelSource: cumulus_primitives_core::XcmpMessageSource, AllPalletsWithoutSystem>
733	RuntimeHelper<HrmpChannelSource, AllPalletsWithoutSystem>
734{
735	pub fn take_xcm(sent_to_para_id: ParaId) -> Option<VersionedXcm<()>> {
736		match HrmpChannelSource::take_outbound_messages(10)[..] {
737			[(para_id, ref mut xcm_message_data)] if para_id.eq(&sent_to_para_id.into()) => {
738				let mut xcm_message_data = &xcm_message_data[..];
739				// decode
740				let _ = XcmpMessageFormat::decode(&mut xcm_message_data).expect("valid format");
741				VersionedXcm::<()>::decode_with_depth_limit(
742					MAX_XCM_DECODE_DEPTH,
743					&mut xcm_message_data,
744				)
745				.map(|x| Some(x))
746				.expect("result with xcm")
747			},
748			_ => return None,
749		}
750	}
751}