referrerpolicy=no-referrer-when-downgrade

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