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			};
350
351			let _ = cumulus_pallet_parachain_system::Pallet::<Runtime>::set_validation_data(
352				Runtime::RuntimeOrigin::none(),
353				inherent_data,
354			);
355			let _ = pallet_timestamp::Pallet::<Runtime>::set(
356				Runtime::RuntimeOrigin::none(),
357				300_u32.into(),
358			);
359			AllPalletsWithoutSystem::on_finalize(next_block_number);
360			let header = frame_system::Pallet::<Runtime>::finalize();
361			last_header = Some(header);
362		}
363		last_header.expect("run_to_block empty block range")
364	}
365
366	pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
367		<Runtime as frame_system::Config>::RuntimeOrigin::root()
368	}
369
370	pub fn block_number() -> U256 {
371		frame_system::Pallet::<Runtime>::block_number().into()
372	}
373
374	pub fn origin_of(
375		account_id: AccountIdOf<Runtime>,
376	) -> <Runtime as frame_system::Config>::RuntimeOrigin {
377		<Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id.into())
378	}
379}
380
381impl<XcmConfig: xcm_executor::Config, AllPalletsWithoutSystem>
382	RuntimeHelper<XcmConfig, AllPalletsWithoutSystem>
383{
384	pub fn do_transfer(
385		from: Location,
386		to: Location,
387		(asset, amount): (Location, u128),
388	) -> Result<AssetsInHolding, XcmError> {
389		<XcmConfig::AssetTransactor as TransactAsset>::transfer_asset(
390			&Asset { id: AssetId(asset), fun: Fungible(amount) },
391			&from,
392			&to,
393			// We aren't able to track the XCM that initiated the fee deposit, so we create a
394			// fake message hash here
395			&XcmContext::with_message_id([0; 32]),
396		)
397	}
398}
399
400impl<
401		Runtime: pallet_xcm::Config + cumulus_pallet_parachain_system::Config,
402		AllPalletsWithoutSystem,
403	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
404{
405	pub fn do_teleport_assets<HrmpChannelOpener>(
406		origin: <Runtime as frame_system::Config>::RuntimeOrigin,
407		dest: Location,
408		beneficiary: Location,
409		(asset, amount): (Location, u128),
410		open_hrmp_channel: Option<(u32, u32)>,
411		included_head: HeaderFor<Runtime>,
412		slot_digest: &[u8],
413		slot_durations: &SlotDurations,
414	) -> DispatchResult
415	where
416		HrmpChannelOpener: frame_support::inherent::ProvideInherent<
417			Call = cumulus_pallet_parachain_system::Call<Runtime>,
418		>,
419	{
420		// open hrmp (if needed)
421		if let Some((source_para_id, target_para_id)) = open_hrmp_channel {
422			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
423				source_para_id.into(),
424				target_para_id.into(),
425				included_head,
426				slot_digest,
427				slot_durations,
428			);
429		}
430
431		// do teleport
432		<pallet_xcm::Pallet<Runtime>>::limited_teleport_assets(
433			origin,
434			Box::new(dest.into()),
435			Box::new(beneficiary.into()),
436			Box::new((AssetId(asset), amount).into()),
437			0,
438			Unlimited,
439		)
440	}
441}
442
443impl<
444		Runtime: cumulus_pallet_parachain_system::Config + pallet_xcm::Config,
445		AllPalletsWithoutSystem,
446	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
447{
448	#[deprecated(
449		note = "Will be removed after Aug 2025; It uses hard-coded `Location::parent()`, \
450		use `execute_as_governance_call` instead."
451	)]
452	pub fn execute_as_governance(call: Vec<u8>) -> Outcome {
453		// prepare xcm as governance will do
454		let xcm = Xcm(vec![
455			UnpaidExecution { weight_limit: Unlimited, check_origin: None },
456			Transact {
457				origin_kind: OriginKind::Superuser,
458				call: call.into(),
459				fallback_max_weight: None,
460			},
461			ExpectTransactStatus(MaybeErrorCode::Success),
462		]);
463
464		// execute xcm as parent origin
465		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
466		<<Runtime as pallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
467			Location::parent(),
468			xcm,
469			&mut hash,
470			Self::xcm_max_weight(XcmReceivedFrom::Parent),
471			Weight::zero(),
472		)
473	}
474
475	pub fn execute_as_governance_call<Call: Dispatchable + Encode>(
476		call: Call,
477		governance_origin: GovernanceOrigin<Call::RuntimeOrigin>,
478	) -> Result<(), Either<DispatchError, XcmError>> {
479		// execute xcm as governance would send
480		let execute_xcm = |call: Call, governance_location, descend_origin| {
481			// prepare xcm
482			let xcm = if let Some(descend_origin) = descend_origin {
483				Xcm::builder_unsafe().descend_origin(descend_origin)
484			} else {
485				Xcm::builder_unsafe()
486			}
487			.unpaid_execution(Unlimited, None)
488			.transact(OriginKind::Superuser, None, call.encode())
489			.expect_transact_status(MaybeErrorCode::Success)
490			.build();
491
492			let xcm_max_weight = Self::xcm_max_weight_for_location(&governance_location);
493			let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
494
495			<<Runtime as pallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
496				governance_location,
497				xcm,
498				&mut hash,
499				xcm_max_weight,
500				Weight::zero(),
501			)
502		};
503
504		match governance_origin {
505			GovernanceOrigin::Location(location) =>
506				execute_xcm(call, location, None).ensure_complete().map_err(Either::Right),
507			GovernanceOrigin::LocationAndDescendOrigin(location, descend_origin) =>
508				execute_xcm(call, location, Some(descend_origin))
509					.ensure_complete()
510					.map_err(Either::Right),
511			GovernanceOrigin::Origin(origin) =>
512				call.dispatch(origin).map(|_| ()).map_err(|e| Either::Left(e.error)),
513		}
514	}
515
516	pub fn execute_as_origin<Call: GetDispatchInfo + Encode>(
517		(origin, origin_kind): (Location, OriginKind),
518		call: Call,
519		maybe_buy_execution_fee: Option<Asset>,
520	) -> Outcome {
521		let mut instructions = if let Some(buy_execution_fee) = maybe_buy_execution_fee {
522			vec![
523				WithdrawAsset(buy_execution_fee.clone().into()),
524				BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
525			]
526		} else {
527			vec![UnpaidExecution { check_origin: None, weight_limit: Unlimited }]
528		};
529
530		// prepare `Transact` xcm
531		instructions.extend(vec![
532			Transact { origin_kind, call: call.encode().into(), fallback_max_weight: None },
533			ExpectTransactStatus(MaybeErrorCode::Success),
534		]);
535		let xcm = Xcm(instructions);
536		let xcm_max_weight = Self::xcm_max_weight_for_location(&origin);
537
538		// execute xcm as parent origin
539		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
540		<<Runtime as pallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
541			origin,
542			xcm,
543			&mut hash,
544			xcm_max_weight,
545			Weight::zero(),
546		)
547	}
548}
549
550/// Enum representing governance origin/location.
551#[derive(Clone)]
552pub enum GovernanceOrigin<RuntimeOrigin> {
553	Location(Location),
554	LocationAndDescendOrigin(Location, InteriorLocation),
555	Origin(RuntimeOrigin),
556}
557
558pub enum XcmReceivedFrom {
559	Parent,
560	Sibling,
561}
562
563impl<ParachainSystem: cumulus_pallet_parachain_system::Config, AllPalletsWithoutSystem>
564	RuntimeHelper<ParachainSystem, AllPalletsWithoutSystem>
565{
566	pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight {
567		match from {
568			XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(),
569			XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(),
570		}
571	}
572
573	pub fn xcm_max_weight_for_location(location: &Location) -> Weight {
574		Self::xcm_max_weight(if location == &Location::parent() {
575			XcmReceivedFrom::Parent
576		} else {
577			XcmReceivedFrom::Sibling
578		})
579	}
580}
581
582impl<Runtime: frame_system::Config + pallet_xcm::Config, AllPalletsWithoutSystem>
583	RuntimeHelper<Runtime, AllPalletsWithoutSystem>
584{
585	pub fn assert_pallet_xcm_event_outcome(
586		unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
587		assert_outcome: fn(Outcome),
588	) {
589		assert_outcome(Self::get_pallet_xcm_event_outcome(unwrap_pallet_xcm_event));
590	}
591
592	pub fn get_pallet_xcm_event_outcome(
593		unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
594	) -> Outcome {
595		<frame_system::Pallet<Runtime>>::events()
596			.into_iter()
597			.filter_map(|e| unwrap_pallet_xcm_event(e.event.encode()))
598			.find_map(|e| match e {
599				pallet_xcm::Event::Attempted { outcome } => Some(outcome),
600				_ => None,
601			})
602			.expect("No `pallet_xcm::Event::Attempted(outcome)` event found!")
603	}
604}
605
606impl<
607		Runtime: frame_system::Config + cumulus_pallet_xcmp_queue::Config,
608		AllPalletsWithoutSystem,
609	> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
610{
611	pub fn xcmp_queue_message_sent(
612		unwrap_xcmp_queue_event: Box<
613			dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
614		>,
615	) -> Option<XcmHash> {
616		<frame_system::Pallet<Runtime>>::events()
617			.into_iter()
618			.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
619			.find_map(|e| match e {
620				cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
621					Some(message_hash),
622				_ => None,
623			})
624	}
625}
626
627pub fn assert_metadata<Fungibles, AccountId>(
628	asset_id: impl Into<Fungibles::AssetId> + Clone,
629	expected_name: &str,
630	expected_symbol: &str,
631	expected_decimals: u8,
632) where
633	Fungibles: frame_support::traits::fungibles::metadata::Inspect<AccountId>
634		+ frame_support::traits::fungibles::Inspect<AccountId>,
635{
636	assert_eq!(Fungibles::name(asset_id.clone().into()), Vec::from(expected_name),);
637	assert_eq!(Fungibles::symbol(asset_id.clone().into()), Vec::from(expected_symbol),);
638	assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals);
639}
640
641pub fn assert_total<Fungibles, AccountId>(
642	asset_id: impl Into<Fungibles::AssetId> + Clone,
643	expected_total_issuance: impl Into<Fungibles::Balance>,
644	expected_active_issuance: impl Into<Fungibles::Balance>,
645) where
646	Fungibles: frame_support::traits::fungibles::metadata::Inspect<AccountId>
647		+ frame_support::traits::fungibles::Inspect<AccountId>,
648{
649	assert_eq!(Fungibles::total_issuance(asset_id.clone().into()), expected_total_issuance.into());
650	assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into());
651}
652
653/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass.
654///
655/// Calls parachain-system's `create_inherent` in case the channel hasn't been opened before, and
656/// thus requires additional parameters for validating it: latest included parachain head and
657/// parachain AuRa-slot.
658///
659/// AuRa consensus hook expects pallets to be initialized, before calling this function make sure to
660/// `run_to_block` at least once.
661pub fn mock_open_hrmp_channel<
662	C: cumulus_pallet_parachain_system::Config,
663	T: ProvideInherent<Call = cumulus_pallet_parachain_system::Call<C>>,
664>(
665	sender: ParaId,
666	recipient: ParaId,
667	included_head: HeaderFor<C>,
668	mut slot_digest: &[u8],
669	slot_durations: &SlotDurations,
670) {
671	let slot = Slot::decode(&mut slot_digest).expect("failed to decode digest");
672	// Convert para slot to relay chain.
673	let timestamp = slot.saturating_mul(slot_durations.para.as_millis());
674	let relay_slot = Slot::from_timestamp(timestamp.into(), slot_durations.relay);
675
676	let n = 1_u32;
677	let mut sproof_builder = RelayStateSproofBuilder {
678		para_id: sender,
679		included_para_head: Some(HeadData(included_head.encode())),
680		hrmp_egress_channel_index: Some(vec![recipient]),
681		current_slot: relay_slot,
682		..Default::default()
683	};
684	sproof_builder.hrmp_channels.insert(
685		HrmpChannelId { sender, recipient },
686		AbridgedHrmpChannel {
687			max_capacity: 10,
688			max_total_size: 10_000_000_u32,
689			max_message_size: 10_000_000_u32,
690			msg_count: 0,
691			total_size: 0_u32,
692			mqc_head: None,
693		},
694	);
695
696	let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof();
697	let vfp = PersistedValidationData {
698		relay_parent_number: n as RelayChainBlockNumber,
699		relay_parent_storage_root,
700		..Default::default()
701	};
702	// It is insufficient to push the validation function params
703	// to storage; they must also be included in the inherent data.
704	let inherent_data = {
705		let mut inherent_data = InherentData::default();
706		let system_inherent_data = ParachainInherentData {
707			validation_data: vfp,
708			relay_chain_state,
709			downward_messages: Default::default(),
710			horizontal_messages: Default::default(),
711		};
712		inherent_data
713			.put_data(
714				cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER,
715				&system_inherent_data,
716			)
717			.expect("failed to put VFP inherent");
718		inherent_data
719	};
720
721	// execute the block
722	T::create_inherent(&inherent_data)
723		.expect("got an inherent")
724		.dispatch_bypass_filter(RawOrigin::None.into())
725		.expect("dispatch succeeded");
726}
727
728impl<HrmpChannelSource: cumulus_primitives_core::XcmpMessageSource, AllPalletsWithoutSystem>
729	RuntimeHelper<HrmpChannelSource, AllPalletsWithoutSystem>
730{
731	pub fn take_xcm(sent_to_para_id: ParaId) -> Option<VersionedXcm<()>> {
732		match HrmpChannelSource::take_outbound_messages(10)[..] {
733			[(para_id, ref mut xcm_message_data)] if para_id.eq(&sent_to_para_id.into()) => {
734				let mut xcm_message_data = &xcm_message_data[..];
735				// decode
736				let _ = XcmpMessageFormat::decode(&mut xcm_message_data).expect("valid format");
737				VersionedXcm::<()>::decode_with_depth_limit(
738					MAX_XCM_DECODE_DEPTH,
739					&mut xcm_message_data,
740				)
741				.map(|x| Some(x))
742				.expect("result with xcm")
743			},
744			_ => return None,
745		}
746	}
747}