pallet_bridge_parachains/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Parachains finality module.
18//!
19//! This module needs to be deployed with GRANDPA module, which is syncing relay
20//! chain blocks. The main entry point of this module is `submit_parachain_heads`, which
21//! accepts storage proof of some parachain `Heads` entries from bridged relay chain.
22//! It requires corresponding relay headers to be already synced.
23
24#![warn(missing_docs)]
25#![cfg_attr(not(feature = "std"), no_std)]
26
27pub use weights::WeightInfo;
28pub use weights_ext::WeightInfoExt;
29
30use bp_header_chain::{HeaderChain, HeaderChainError};
31use bp_parachains::{
32	ParaInfo, ParaStoredHeaderData, RelayBlockHash, RelayBlockHasher, RelayBlockNumber,
33	SubmitParachainHeadsInfo,
34};
35use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId};
36use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain};
37use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound};
38use pallet_bridge_grandpa::SubmitFinalityProofHelper;
39use proofs::{ParachainsStorageProofAdapter, StorageProofAdapter};
40use sp_std::{marker::PhantomData, vec::Vec};
41
42#[cfg(feature = "runtime-benchmarks")]
43use bp_parachains::ParaStoredHeaderDataBuilder;
44#[cfg(feature = "runtime-benchmarks")]
45use bp_runtime::HeaderOf;
46#[cfg(feature = "runtime-benchmarks")]
47use codec::Encode;
48
49// Re-export in crate namespace for `construct_runtime!`.
50pub use call_ext::*;
51pub use pallet::*;
52
53pub mod weights;
54pub mod weights_ext;
55
56#[cfg(feature = "runtime-benchmarks")]
57pub mod benchmarking;
58
59mod call_ext;
60#[cfg(test)]
61mod mock;
62mod proofs;
63
64/// The target that will be used when publishing logs related to this pallet.
65pub const LOG_TARGET: &str = "runtime::bridge-parachains";
66
67/// Artifacts of the parachains head update.
68struct UpdateParachainHeadArtifacts {
69	/// New best head of the parachain.
70	pub best_head: ParaInfo,
71	/// If `true`, some old parachain head has been pruned during update.
72	pub prune_happened: bool,
73}
74
75#[frame_support::pallet]
76pub mod pallet {
77	use super::*;
78	use bp_parachains::{
79		BestParaHeadHash, ImportedParaHeadsKeyProvider, ParaStoredHeaderDataBuilder,
80		ParasInfoKeyProvider,
81	};
82	use bp_runtime::{
83		BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider,
84		StorageMapKeyProvider,
85	};
86	use frame_support::pallet_prelude::*;
87	use frame_system::pallet_prelude::*;
88
89	/// Stored parachain head data of given parachains pallet.
90	pub type StoredParaHeadDataOf<T, I> =
91		BoundedStorageValue<<T as Config<I>>::MaxParaHeadDataSize, ParaStoredHeaderData>;
92	/// Weight info of the given parachains pallet.
93	pub type WeightInfoOf<T, I> = <T as Config<I>>::WeightInfo;
94	/// Bridge GRANDPA pallet that is used to verify parachain proofs.
95	pub type GrandpaPalletOf<T, I> =
96		pallet_bridge_grandpa::Pallet<T, <T as Config<I>>::BridgesGrandpaPalletInstance>;
97
98	#[pallet::event]
99	#[pallet::generate_deposit(pub(super) fn deposit_event)]
100	pub enum Event<T: Config<I>, I: 'static = ()> {
101		/// The caller has provided head of parachain that the pallet is not configured to track.
102		UntrackedParachainRejected {
103			/// Identifier of the parachain that is not tracked by the pallet.
104			parachain: ParaId,
105		},
106		/// The caller has declared that he has provided given parachain head, but it is missing
107		/// from the storage proof.
108		MissingParachainHead {
109			/// Identifier of the parachain with missing head.
110			parachain: ParaId,
111		},
112		/// The caller has provided parachain head hash that is not matching the hash read from the
113		/// storage proof.
114		IncorrectParachainHeadHash {
115			/// Identifier of the parachain with incorrect head hast.
116			parachain: ParaId,
117			/// Specified parachain head hash.
118			parachain_head_hash: ParaHash,
119			/// Actual parachain head hash.
120			actual_parachain_head_hash: ParaHash,
121		},
122		/// The caller has provided obsolete parachain head, which is already known to the pallet.
123		RejectedObsoleteParachainHead {
124			/// Identifier of the parachain with obsolete head.
125			parachain: ParaId,
126			/// Obsolete parachain head hash.
127			parachain_head_hash: ParaHash,
128		},
129		/// The caller has provided parachain head that exceeds the maximal configured head size.
130		RejectedLargeParachainHead {
131			/// Identifier of the parachain with rejected head.
132			parachain: ParaId,
133			/// Parachain head hash.
134			parachain_head_hash: ParaHash,
135			/// Parachain head size.
136			parachain_head_size: u32,
137		},
138		/// Parachain head has been updated.
139		UpdatedParachainHead {
140			/// Identifier of the parachain that has been updated.
141			parachain: ParaId,
142			/// Parachain head hash.
143			parachain_head_hash: ParaHash,
144		},
145	}
146
147	#[pallet::error]
148	pub enum Error<T, I = ()> {
149		/// Relay chain block hash is unknown to us.
150		UnknownRelayChainBlock,
151		/// The number of stored relay block is different from what the relayer has provided.
152		InvalidRelayChainBlockNumber,
153		/// Parachain heads storage proof is invalid.
154		HeaderChainStorageProof(HeaderChainError),
155		/// Error generated by the `OwnedBridgeModule` trait.
156		BridgeModule(bp_runtime::OwnedBridgeModuleError),
157	}
158
159	/// Convenience trait for defining `BridgedChain` bounds.
160	pub trait BoundedBridgeGrandpaConfig<I: 'static>:
161		pallet_bridge_grandpa::Config<I, BridgedChain = Self::BridgedRelayChain>
162	{
163		/// Type of the bridged relay chain.
164		type BridgedRelayChain: Chain<
165			BlockNumber = RelayBlockNumber,
166			Hash = RelayBlockHash,
167			Hasher = RelayBlockHasher,
168		>;
169	}
170
171	impl<T, I: 'static> BoundedBridgeGrandpaConfig<I> for T
172	where
173		T: pallet_bridge_grandpa::Config<I>,
174		T::BridgedChain:
175			Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher>,
176	{
177		type BridgedRelayChain = T::BridgedChain;
178	}
179
180	#[pallet::config]
181	#[pallet::disable_frame_system_supertrait_check]
182	pub trait Config<I: 'static = ()>:
183		BoundedBridgeGrandpaConfig<Self::BridgesGrandpaPalletInstance>
184	{
185		/// The overarching event type.
186		type RuntimeEvent: From<Event<Self, I>>
187			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
188		/// Benchmarks results from runtime we're plugged into.
189		type WeightInfo: WeightInfoExt;
190
191		/// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to.
192		///
193		/// The GRANDPA pallet instance must be configured to import headers of relay chain that
194		/// we're interested in.
195		///
196		/// The associated GRANDPA pallet is also used to configure free parachain heads
197		/// submissions. The parachain head submission will be free if:
198		///
199		/// 1) the submission contains exactly one parachain head update that succeeds;
200		///
201		/// 2) the difference between relay chain block numbers, used to prove new parachain head
202		///    and previous best parachain head is larger than the `FreeHeadersInterval`, configured
203		///    at the associated GRANDPA pallet;
204		///
205		/// 3) there are slots for free submissions, remaining at the block. This is also configured
206		///    at the associated GRANDPA pallet using `MaxFreeHeadersPerBlock` parameter.
207		///
208		/// First parachain head submission is also free for the submitted, if free submissions
209		/// are yet accepted to this block.
210		type BridgesGrandpaPalletInstance: 'static;
211
212		/// Name of the original `paras` pallet in the `construct_runtime!()` call at the bridged
213		/// chain.
214		///
215		/// Please keep in mind that this should be the name of the `runtime_parachains::paras`
216		/// pallet from polkadot repository, not the `pallet-bridge-parachains`.
217		#[pallet::constant]
218		type ParasPalletName: Get<&'static str>;
219
220		/// Parachain head data builder.
221		///
222		/// We never store parachain heads here, since they may be too big (e.g. because of large
223		/// digest items). Instead we're using the same approach as `pallet-bridge-grandpa`
224		/// pallet - we are only storing `bp_messages::StoredHeaderData` (number and state root),
225		/// which is enough for our applications. However, we work with different parachains here
226		/// and they can use different primitives (for block numbers and hash). So we can't store
227		/// it directly. Instead, we're storing `bp_messages::StoredHeaderData` in SCALE-encoded
228		/// form, wrapping it into `bp_parachains::ParaStoredHeaderData`.
229		///
230		/// This builder helps to convert from `HeadData` to `bp_parachains::ParaStoredHeaderData`.
231		type ParaStoredHeaderDataBuilder: ParaStoredHeaderDataBuilder;
232
233		/// Maximal number of single parachain heads to keep in the storage.
234		///
235		/// The setting is there to prevent growing the on-chain state indefinitely. Note
236		/// the setting does not relate to parachain block numbers - we will simply keep as much
237		/// items in the storage, so it doesn't guarantee any fixed timeframe for heads.
238		///
239		/// Incautious change of this constant may lead to orphan entries in the runtime storage.
240		#[pallet::constant]
241		type HeadsToKeep: Get<u32>;
242
243		/// Maximal size (in bytes) of the SCALE-encoded parachain head data
244		/// (`bp_parachains::ParaStoredHeaderData`).
245		///
246		/// Keep in mind that the size of any tracked parachain header data must not exceed this
247		/// value. So if you're going to track multiple parachains, one of which is using large
248		/// hashes, you shall choose this maximal value.
249		///
250		/// There's no mandatory headers in this pallet, so it can't stall if there's some header
251		/// that exceeds this bound.
252		#[pallet::constant]
253		type MaxParaHeadDataSize: Get<u32>;
254	}
255
256	/// Optional pallet owner.
257	///
258	/// Pallet owner has a right to halt all pallet operations and then resume them. If it is
259	/// `None`, then there are no direct ways to halt/resume pallet operations, but other
260	/// runtime methods may still be used to do that (i.e. democracy::referendum to update halt
261	/// flag directly or call the `set_operating_mode`).
262	#[pallet::storage]
263	pub type PalletOwner<T: Config<I>, I: 'static = ()> =
264		StorageValue<_, T::AccountId, OptionQuery>;
265
266	/// The current operating mode of the pallet.
267	///
268	/// Depending on the mode either all, or no transactions will be allowed.
269	#[pallet::storage]
270	pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
271		StorageValue<_, BasicOperatingMode, ValueQuery>;
272
273	/// Parachains info.
274	///
275	/// Contains the following info:
276	/// - best parachain head hash
277	/// - the head of the `ImportedParaHashes` ring buffer
278	#[pallet::storage]
279	pub type ParasInfo<T: Config<I>, I: 'static = ()> = StorageMap<
280		Hasher = <ParasInfoKeyProvider as StorageMapKeyProvider>::Hasher,
281		Key = <ParasInfoKeyProvider as StorageMapKeyProvider>::Key,
282		Value = <ParasInfoKeyProvider as StorageMapKeyProvider>::Value,
283		QueryKind = OptionQuery,
284		OnEmpty = GetDefault,
285		MaxValues = MaybeMaxParachains<T, I>,
286	>;
287
288	/// State roots of parachain heads which have been imported into the pallet.
289	#[pallet::storage]
290	pub type ImportedParaHeads<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
291		Hasher1 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Hasher1,
292		Key1 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Key1,
293		Hasher2 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Hasher2,
294		Key2 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Key2,
295		Value = StoredParaHeadDataOf<T, I>,
296		QueryKind = OptionQuery,
297		OnEmpty = GetDefault,
298		MaxValues = MaybeMaxTotalParachainHashes<T, I>,
299	>;
300
301	/// A ring buffer of imported parachain head hashes. Ordered by the insertion time.
302	#[pallet::storage]
303	pub(super) type ImportedParaHashes<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
304		Hasher1 = Blake2_128Concat,
305		Key1 = ParaId,
306		Hasher2 = Twox64Concat,
307		Key2 = u32,
308		Value = ParaHash,
309		QueryKind = OptionQuery,
310		OnEmpty = GetDefault,
311		MaxValues = MaybeMaxTotalParachainHashes<T, I>,
312	>;
313
314	#[pallet::pallet]
315	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
316
317	impl<T: Config<I>, I: 'static> OwnedBridgeModule<T> for Pallet<T, I> {
318		const LOG_TARGET: &'static str = LOG_TARGET;
319		type OwnerStorage = PalletOwner<T, I>;
320		type OperatingMode = BasicOperatingMode;
321		type OperatingModeStorage = PalletOperatingMode<T, I>;
322	}
323
324	#[pallet::call]
325	impl<T: Config<I>, I: 'static> Pallet<T, I> {
326		/// Submit proof of one or several parachain heads.
327		///
328		/// The proof is supposed to be proof of some `Heads` entries from the
329		/// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain.
330		/// The proof is supposed to be crafted at the `relay_header_hash` that must already be
331		/// imported by corresponding GRANDPA pallet at this chain.
332		///
333		/// The call fails if:
334		///
335		/// - the pallet is halted;
336		///
337		/// - the relay chain block `at_relay_block` is not imported by the associated bridge
338		///   GRANDPA pallet.
339		///
340		/// The call may succeed, but some heads may not be updated e.g. because pallet knows
341		/// better head or it isn't tracked by the pallet.
342		#[pallet::call_index(0)]
343		#[pallet::weight(WeightInfoOf::<T, I>::submit_parachain_heads_weight(
344			T::DbWeight::get(),
345			parachain_heads_proof,
346			parachains.len() as _,
347		))]
348		pub fn submit_parachain_heads(
349			origin: OriginFor<T>,
350			at_relay_block: (RelayBlockNumber, RelayBlockHash),
351			parachains: Vec<(ParaId, ParaHash)>,
352			parachain_heads_proof: ParaHeadsProof,
353		) -> DispatchResultWithPostInfo {
354			Self::submit_parachain_heads_ex(
355				origin,
356				at_relay_block,
357				parachains,
358				parachain_heads_proof,
359				false,
360			)
361		}
362
363		/// Change `PalletOwner`.
364		///
365		/// May only be called either by root, or by `PalletOwner`.
366		#[pallet::call_index(1)]
367		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
368		pub fn set_owner(origin: OriginFor<T>, new_owner: Option<T::AccountId>) -> DispatchResult {
369			<Self as OwnedBridgeModule<_>>::set_owner(origin, new_owner)
370		}
371
372		/// Halt or resume all pallet operations.
373		///
374		/// May only be called either by root, or by `PalletOwner`.
375		#[pallet::call_index(2)]
376		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
377		pub fn set_operating_mode(
378			origin: OriginFor<T>,
379			operating_mode: BasicOperatingMode,
380		) -> DispatchResult {
381			<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
382		}
383
384		/// Submit proof of one or several parachain heads.
385		///
386		/// The proof is supposed to be proof of some `Heads` entries from the
387		/// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain.
388		/// The proof is supposed to be crafted at the `relay_header_hash` that must already be
389		/// imported by corresponding GRANDPA pallet at this chain.
390		///
391		/// The call fails if:
392		///
393		/// - the pallet is halted;
394		///
395		/// - the relay chain block `at_relay_block` is not imported by the associated bridge
396		///   GRANDPA pallet.
397		///
398		/// The call may succeed, but some heads may not be updated e.g. because pallet knows
399		/// better head or it isn't tracked by the pallet.
400		///
401		/// The `is_free_execution_expected` parameter is not really used inside the call. It is
402		/// used by the transaction extension, which should be registered at the runtime level. If
403		/// this parameter is `true`, the transaction will be treated as invalid, if the call won't
404		/// be executed for free. If transaction extension is not used by the runtime, this
405		/// parameter is not used at all.
406		#[pallet::call_index(3)]
407		#[pallet::weight(WeightInfoOf::<T, I>::submit_parachain_heads_weight(
408			T::DbWeight::get(),
409			parachain_heads_proof,
410			parachains.len() as _,
411		))]
412		pub fn submit_parachain_heads_ex(
413			origin: OriginFor<T>,
414			at_relay_block: (RelayBlockNumber, RelayBlockHash),
415			parachains: Vec<(ParaId, ParaHash)>,
416			parachain_heads_proof: ParaHeadsProof,
417			_is_free_execution_expected: bool,
418		) -> DispatchResultWithPostInfo {
419			Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
420			ensure_signed(origin)?;
421
422			let total_parachains = parachains.len();
423			let free_headers_interval =
424				T::FreeHeadersInterval::get().unwrap_or(RelayBlockNumber::MAX);
425			// the pallet allows two kind of free submissions
426			// 1) if distance between all parachain heads is gte than the [`T::FreeHeadersInterval`]
427			// 2) if all heads are the first heads of their parachains
428			let mut free_parachain_heads = 0;
429
430			// we'll need relay chain header to verify that parachains heads are always increasing.
431			let (relay_block_number, relay_block_hash) = at_relay_block;
432			let relay_block = pallet_bridge_grandpa::ImportedHeaders::<
433				T,
434				T::BridgesGrandpaPalletInstance,
435			>::get(relay_block_hash)
436			.ok_or(Error::<T, I>::UnknownRelayChainBlock)?;
437			ensure!(
438				relay_block.number == relay_block_number,
439				Error::<T, I>::InvalidRelayChainBlockNumber,
440			);
441
442			// now parse storage proof and read parachain heads
443			let mut actual_weight = WeightInfoOf::<T, I>::submit_parachain_heads_weight(
444				T::DbWeight::get(),
445				&parachain_heads_proof,
446				parachains.len() as _,
447			);
448
449			let mut storage: ParachainsStorageProofAdapter<T, I> =
450				ParachainsStorageProofAdapter::try_new_with_verified_storage_proof(
451					relay_block_hash,
452					parachain_heads_proof.storage_proof,
453				)
454				.map_err(Error::<T, I>::HeaderChainStorageProof)?;
455
456			for (parachain, parachain_head_hash) in parachains {
457				let parachain_head = match storage.read_parachain_head(parachain) {
458					Ok(Some(parachain_head)) => parachain_head,
459					Ok(None) => {
460						log::trace!(
461							target: LOG_TARGET,
462							"The head of parachain {:?} is None. {}",
463							parachain,
464							if ParasInfo::<T, I>::contains_key(parachain) {
465								"Looks like it is not yet registered at the source relay chain"
466							} else {
467								"Looks like it has been deregistered from the source relay chain"
468							},
469						);
470						Self::deposit_event(Event::MissingParachainHead { parachain });
471						continue
472					},
473					Err(e) => {
474						log::trace!(
475							target: LOG_TARGET,
476							"The read of head of parachain {:?} has failed: {:?}",
477							parachain,
478							e,
479						);
480						Self::deposit_event(Event::MissingParachainHead { parachain });
481						continue
482					},
483				};
484
485				// if relayer has specified invalid parachain head hash, ignore the head
486				// (this isn't strictly necessary, but better safe than sorry)
487				let actual_parachain_head_hash = parachain_head.hash();
488				if parachain_head_hash != actual_parachain_head_hash {
489					log::trace!(
490						target: LOG_TARGET,
491						"The submitter has specified invalid parachain {:?} head hash: \
492								{:?} vs {:?}",
493						parachain,
494						parachain_head_hash,
495						actual_parachain_head_hash,
496					);
497					Self::deposit_event(Event::IncorrectParachainHeadHash {
498						parachain,
499						parachain_head_hash,
500						actual_parachain_head_hash,
501					});
502					continue
503				}
504
505				// convert from parachain head into stored parachain head data
506				let parachain_head_size = parachain_head.0.len();
507				let parachain_head_data =
508					match T::ParaStoredHeaderDataBuilder::try_build(parachain, &parachain_head) {
509						Some(parachain_head_data) => parachain_head_data,
510						None => {
511							log::trace!(
512								target: LOG_TARGET,
513								"The head of parachain {:?} has been provided, but it is not tracked by the pallet",
514								parachain,
515							);
516							Self::deposit_event(Event::UntrackedParachainRejected { parachain });
517							continue
518						},
519					};
520
521				let update_result: Result<_, ()> =
522					ParasInfo::<T, I>::try_mutate(parachain, |stored_best_head| {
523						let is_free = parachain_head_size <
524							T::ParaStoredHeaderDataBuilder::max_free_head_size() as usize &&
525							match stored_best_head {
526								Some(ref best_head)
527									if at_relay_block.0.saturating_sub(
528										best_head.best_head_hash.at_relay_block_number,
529									) >= free_headers_interval =>
530									true,
531								Some(_) => false,
532								None => true,
533							};
534						let artifacts = Pallet::<T, I>::update_parachain_head(
535							parachain,
536							stored_best_head.take(),
537							HeaderId(relay_block_number, relay_block_hash),
538							parachain_head_data,
539							parachain_head_hash,
540						)?;
541
542						if is_free {
543							free_parachain_heads = free_parachain_heads + 1;
544						}
545
546						*stored_best_head = Some(artifacts.best_head);
547						Ok(artifacts.prune_happened)
548					});
549
550				// we're refunding weight if update has not happened and if pruning has not happened
551				let is_update_happened = update_result.is_ok();
552				if !is_update_happened {
553					actual_weight = actual_weight.saturating_sub(
554						WeightInfoOf::<T, I>::parachain_head_storage_write_weight(
555							T::DbWeight::get(),
556						),
557					);
558				}
559				let is_prune_happened = matches!(update_result, Ok(true));
560				if !is_prune_happened {
561					actual_weight = actual_weight.saturating_sub(
562						WeightInfoOf::<T, I>::parachain_head_pruning_weight(T::DbWeight::get()),
563					);
564				}
565			}
566
567			// even though we may have accepted some parachain heads, we can't allow relayers to
568			// submit proof with unused trie nodes
569			// => treat this as an error
570			//
571			// (we can throw error here, because now all our calls are transactional)
572			storage.ensure_no_unused_keys().map_err(|e| {
573				Error::<T, I>::HeaderChainStorageProof(HeaderChainError::StorageProof(e))
574			})?;
575
576			// check if we allow this submission for free
577			let is_free = total_parachains == 1
578				&& free_parachain_heads == total_parachains
579				&& SubmitFinalityProofHelper::<T, T::BridgesGrandpaPalletInstance>::has_free_header_slots();
580			let pays_fee = if is_free {
581				log::trace!(target: LOG_TARGET, "Parachain heads update transaction is free");
582				pallet_bridge_grandpa::on_free_header_imported::<T, T::BridgesGrandpaPalletInstance>(
583				);
584				Pays::No
585			} else {
586				log::trace!(target: LOG_TARGET, "Parachain heads update transaction is paid");
587				Pays::Yes
588			};
589
590			Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee })
591		}
592	}
593
594	impl<T: Config<I>, I: 'static> Pallet<T, I> {
595		/// Get stored parachain info.
596		pub fn best_parachain_info(parachain: ParaId) -> Option<ParaInfo> {
597			ParasInfo::<T, I>::get(parachain)
598		}
599
600		/// Get best finalized head data of the given parachain.
601		pub fn best_parachain_head(parachain: ParaId) -> Option<ParaStoredHeaderData> {
602			let best_para_head_hash = ParasInfo::<T, I>::get(parachain)?.best_head_hash.head_hash;
603			ImportedParaHeads::<T, I>::get(parachain, best_para_head_hash).map(|h| h.into_inner())
604		}
605
606		/// Get best finalized head hash of the given parachain.
607		pub fn best_parachain_head_hash(parachain: ParaId) -> Option<ParaHash> {
608			Some(ParasInfo::<T, I>::get(parachain)?.best_head_hash.head_hash)
609		}
610
611		/// Get best finalized head id of the given parachain.
612		pub fn best_parachain_head_id<C: Chain<Hash = ParaHash> + Parachain>(
613		) -> Result<Option<HeaderIdOf<C>>, codec::Error> {
614			let parachain = ParaId(C::PARACHAIN_ID);
615			let best_head_hash = match Self::best_parachain_head_hash(parachain) {
616				Some(best_head_hash) => best_head_hash,
617				None => return Ok(None),
618			};
619			let encoded_head = match Self::parachain_head(parachain, best_head_hash) {
620				Some(encoded_head) => encoded_head,
621				None => return Ok(None),
622			};
623			encoded_head
624				.decode_parachain_head_data::<C>()
625				.map(|data| Some(HeaderId(data.number, best_head_hash)))
626		}
627
628		/// Get parachain head data with given hash.
629		pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option<ParaStoredHeaderData> {
630			ImportedParaHeads::<T, I>::get(parachain, hash).map(|h| h.into_inner())
631		}
632
633		/// Try to update parachain head.
634		pub(super) fn update_parachain_head(
635			parachain: ParaId,
636			stored_best_head: Option<ParaInfo>,
637			new_at_relay_block: HeaderId<RelayBlockHash, RelayBlockNumber>,
638			new_head_data: ParaStoredHeaderData,
639			new_head_hash: ParaHash,
640		) -> Result<UpdateParachainHeadArtifacts, ()> {
641			// check if head has been already updated at better relay chain block. Without this
642			// check, we may import heads in random order
643			let update = SubmitParachainHeadsInfo {
644				at_relay_block: new_at_relay_block,
645				para_id: parachain,
646				para_head_hash: new_head_hash,
647				// doesn't actually matter here
648				is_free_execution_expected: false,
649			};
650			if SubmitParachainHeadsHelper::<T, I>::check_obsolete(&update).is_err() {
651				Self::deposit_event(Event::RejectedObsoleteParachainHead {
652					parachain,
653					parachain_head_hash: new_head_hash,
654				});
655				return Err(())
656			}
657
658			// verify that the parachain head data size is <= `MaxParaHeadDataSize`
659			let updated_head_data =
660				match StoredParaHeadDataOf::<T, I>::try_from_inner(new_head_data) {
661					Ok(updated_head_data) => updated_head_data,
662					Err(e) => {
663						log::trace!(
664							target: LOG_TARGET,
665							"The parachain head can't be updated. The parachain head data size \
666							for {:?} is {}. It exceeds maximal configured size {}.",
667							parachain,
668							e.value_size,
669							e.maximal_size,
670						);
671
672						Self::deposit_event(Event::RejectedLargeParachainHead {
673							parachain,
674							parachain_head_hash: new_head_hash,
675							parachain_head_size: e.value_size as _,
676						});
677
678						return Err(())
679					},
680				};
681
682			let next_imported_hash_position = stored_best_head
683				.map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position);
684
685			// insert updated best parachain head
686			let head_hash_to_prune =
687				ImportedParaHashes::<T, I>::try_get(parachain, next_imported_hash_position);
688			let updated_best_para_head = ParaInfo {
689				best_head_hash: BestParaHeadHash {
690					at_relay_block_number: new_at_relay_block.0,
691					head_hash: new_head_hash,
692				},
693				next_imported_hash_position: (next_imported_hash_position + 1) %
694					T::HeadsToKeep::get(),
695			};
696			ImportedParaHashes::<T, I>::insert(
697				parachain,
698				next_imported_hash_position,
699				new_head_hash,
700			);
701			ImportedParaHeads::<T, I>::insert(parachain, new_head_hash, updated_head_data);
702			log::trace!(
703				target: LOG_TARGET,
704				"Updated head of parachain {:?} to {} at relay block {}",
705				parachain,
706				new_head_hash,
707				new_at_relay_block.0,
708			);
709
710			// remove old head
711			let prune_happened = head_hash_to_prune.is_ok();
712			if let Ok(head_hash_to_prune) = head_hash_to_prune {
713				log::trace!(
714					target: LOG_TARGET,
715					"Pruning old head of parachain {:?}: {}",
716					parachain,
717					head_hash_to_prune,
718				);
719				ImportedParaHeads::<T, I>::remove(parachain, head_hash_to_prune);
720			}
721			Self::deposit_event(Event::UpdatedParachainHead {
722				parachain,
723				parachain_head_hash: new_head_hash,
724			});
725
726			Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened })
727		}
728	}
729
730	#[pallet::genesis_config]
731	#[derive(DefaultNoBound)]
732	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
733		/// Initial pallet operating mode.
734		pub operating_mode: BasicOperatingMode,
735		/// Initial pallet owner.
736		pub owner: Option<T::AccountId>,
737		/// Dummy marker.
738		#[serde(skip)]
739		pub _phantom: sp_std::marker::PhantomData<I>,
740	}
741
742	#[pallet::genesis_build]
743	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
744		fn build(&self) {
745			PalletOperatingMode::<T, I>::put(self.operating_mode);
746			if let Some(ref owner) = self.owner {
747				PalletOwner::<T, I>::put(owner);
748			}
749		}
750	}
751
752	/// Returns maximal number of parachains, supported by the pallet.
753	pub struct MaybeMaxParachains<T, I>(PhantomData<(T, I)>);
754
755	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeMaxParachains<T, I> {
756		fn get() -> Option<u32> {
757			Some(T::ParaStoredHeaderDataBuilder::supported_parachains())
758		}
759	}
760
761	/// Returns total number of all parachains hashes/heads, stored by the pallet.
762	pub struct MaybeMaxTotalParachainHashes<T, I>(PhantomData<(T, I)>);
763
764	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeMaxTotalParachainHashes<T, I> {
765		fn get() -> Option<u32> {
766			Some(
767				T::ParaStoredHeaderDataBuilder::supported_parachains()
768					.saturating_mul(T::HeadsToKeep::get()),
769			)
770		}
771	}
772}
773
774/// Single parachain header chain adapter.
775pub struct ParachainHeaders<T, I, C>(PhantomData<(T, I, C)>);
776
777impl<T: Config<I>, I: 'static, C: Parachain<Hash = ParaHash>> HeaderChain<C>
778	for ParachainHeaders<T, I, C>
779{
780	fn finalized_header_state_root(hash: HashOf<C>) -> Option<HashOf<C>> {
781		Pallet::<T, I>::parachain_head(ParaId(C::PARACHAIN_ID), hash)
782			.and_then(|head| head.decode_parachain_head_data::<C>().ok())
783			.map(|h| h.state_root)
784	}
785}
786
787/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks.
788#[cfg(feature = "runtime-benchmarks")]
789pub fn initialize_for_benchmarks<T: Config<I>, I: 'static, PC: Parachain<Hash = ParaHash>>(
790	header: HeaderOf<PC>,
791) {
792	use bp_polkadot_core::parachains::ParaHead;
793	use bp_runtime::HeaderIdProvider;
794	use sp_runtime::traits::Header;
795
796	let relay_head =
797		pallet_bridge_grandpa::BridgedHeader::<T, T::BridgesGrandpaPalletInstance>::new(
798			0,
799			Default::default(),
800			Default::default(),
801			Default::default(),
802			Default::default(),
803		);
804	let parachain = ParaId(PC::PARACHAIN_ID);
805	let parachain_head = ParaHead(header.encode());
806	let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, &parachain_head)
807		.expect("failed to build stored parachain head in benchmarks");
808	pallet_bridge_grandpa::initialize_for_benchmarks::<T, T::BridgesGrandpaPalletInstance>(
809		relay_head.clone(),
810	);
811	Pallet::<T, I>::update_parachain_head(
812		parachain,
813		None,
814		relay_head.id(),
815		updated_head_data,
816		parachain_head.hash(),
817	)
818	.expect("failed to insert parachain head in benchmarks");
819}
820
821#[cfg(test)]
822pub(crate) mod tests {
823	use super::*;
824	use crate::mock::{
825		run_test, test_relay_header, BigParachain, BigParachainHeader, FreeHeadersInterval,
826		RegularParachainHasher, RegularParachainHeader, RelayBlockHeader,
827		RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, UNTRACKED_PARACHAIN_ID,
828	};
829	use bp_test_utils::prepare_parachain_heads_proof;
830	use codec::Encode;
831
832	use bp_header_chain::{justification::GrandpaJustification, StoredHeaderGrandpaInfo};
833	use bp_parachains::{
834		BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider,
835	};
836	use bp_polkadot_core::parachains::ParaHead;
837	use bp_runtime::{
838		BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider,
839		StorageMapKeyProvider, StorageProofError,
840	};
841	use bp_test_utils::{
842		authority_list, generate_owned_bridge_module_tests, make_default_justification,
843		TEST_GRANDPA_SET_ID,
844	};
845	use frame_support::{
846		assert_noop, assert_ok,
847		dispatch::DispatchResultWithPostInfo,
848		pallet_prelude::Pays,
849		storage::generator::{StorageDoubleMap, StorageMap},
850		traits::Get,
851		weights::Weight,
852	};
853	use frame_system::{EventRecord, Pallet as System, Phase};
854	use sp_core::Hasher;
855	use sp_runtime::{traits::Header as HeaderT, DispatchError};
856
857	type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1;
858	type WeightInfo = <TestRuntime as Config>::WeightInfo;
859	type DbWeight = <TestRuntime as frame_system::Config>::DbWeight;
860
861	pub(crate) fn initialize(state_root: RelayBlockHash) -> RelayBlockHash {
862		pallet_bridge_grandpa::FreeHeadersRemaining::<TestRuntime, BridgesGrandpaPalletInstance>::set(Some(100));
863		pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::initialize(
864			RuntimeOrigin::root(),
865			bp_header_chain::InitializationData {
866				header: Box::new(test_relay_header(0, state_root)),
867				authority_list: authority_list(),
868				set_id: 1,
869				operating_mode: BasicOperatingMode::Normal,
870			},
871		)
872		.unwrap();
873
874		System::<TestRuntime>::set_block_number(1);
875		System::<TestRuntime>::reset_events();
876
877		test_relay_header(0, state_root).hash()
878	}
879
880	fn proceed(
881		num: RelayBlockNumber,
882		state_root: RelayBlockHash,
883	) -> (ParaHash, GrandpaJustification<RelayBlockHeader>) {
884		let header = test_relay_header(num, state_root);
885		let hash = header.hash();
886		let justification = make_default_justification(&header);
887		assert_ok!(
888			pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::submit_finality_proof_ex(
889				RuntimeOrigin::signed(1),
890				Box::new(header),
891				justification.clone(),
892				TEST_GRANDPA_SET_ID,
893				false,
894			)
895		);
896
897		(hash, justification)
898	}
899
900	fn initial_best_head(parachain: u32) -> ParaInfo {
901		ParaInfo {
902			best_head_hash: BestParaHeadHash {
903				at_relay_block_number: 0,
904				head_hash: head_data(parachain, 0).hash(),
905			},
906			next_imported_hash_position: 1,
907		}
908	}
909
910	pub(crate) fn head_data(parachain: u32, head_number: u32) -> ParaHead {
911		ParaHead(
912			RegularParachainHeader::new(
913				head_number as _,
914				Default::default(),
915				RegularParachainHasher::hash(&(parachain, head_number).encode()),
916				Default::default(),
917				Default::default(),
918			)
919			.encode(),
920		)
921	}
922
923	fn stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData {
924		ParaStoredHeaderData(
925			(head_number as u64, RegularParachainHasher::hash(&(parachain, head_number).encode()))
926				.encode(),
927		)
928	}
929
930	fn big_head_data(parachain: u32, head_number: u32) -> ParaHead {
931		ParaHead(
932			BigParachainHeader::new(
933				head_number as _,
934				Default::default(),
935				RegularParachainHasher::hash(&(parachain, head_number).encode()),
936				Default::default(),
937				Default::default(),
938			)
939			.encode(),
940		)
941	}
942
943	fn big_stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData {
944		ParaStoredHeaderData(
945			(head_number as u128, RegularParachainHasher::hash(&(parachain, head_number).encode()))
946				.encode(),
947		)
948	}
949
950	fn head_hash(parachain: u32, head_number: u32) -> ParaHash {
951		head_data(parachain, head_number).hash()
952	}
953
954	fn import_parachain_1_head(
955		relay_chain_block: RelayBlockNumber,
956		relay_state_root: RelayBlockHash,
957		parachains: Vec<(ParaId, ParaHash)>,
958		proof: ParaHeadsProof,
959	) -> DispatchResultWithPostInfo {
960		Pallet::<TestRuntime>::submit_parachain_heads(
961			RuntimeOrigin::signed(1),
962			(relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()),
963			parachains,
964			proof,
965		)
966	}
967
968	fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight {
969		let db_weight = <TestRuntime as frame_system::Config>::DbWeight::get();
970		WeightInfoOf::<TestRuntime, ()>::submit_parachain_heads_weight(db_weight, proof, 1)
971			.saturating_sub(if prune_expected {
972				Weight::zero()
973			} else {
974				WeightInfoOf::<TestRuntime, ()>::parachain_head_pruning_weight(db_weight)
975			})
976	}
977
978	#[test]
979	fn submit_parachain_heads_checks_operating_mode() {
980		let (state_root, proof, parachains) =
981			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
982
983		run_test(|| {
984			initialize(state_root);
985
986			// `submit_parachain_heads()` should fail when the pallet is halted.
987			PalletOperatingMode::<TestRuntime>::put(BasicOperatingMode::Halted);
988			assert_noop!(
989				Pallet::<TestRuntime>::submit_parachain_heads(
990					RuntimeOrigin::signed(1),
991					(0, test_relay_header(0, state_root).hash()),
992					parachains.clone(),
993					proof.clone(),
994				),
995				Error::<TestRuntime>::BridgeModule(OwnedBridgeModuleError::Halted)
996			);
997
998			// `submit_parachain_heads()` should succeed now that the pallet is resumed.
999			PalletOperatingMode::<TestRuntime>::put(BasicOperatingMode::Normal);
1000			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1001				RuntimeOrigin::signed(1),
1002				(0, test_relay_header(0, state_root).hash()),
1003				parachains,
1004				proof,
1005			),);
1006		});
1007	}
1008
1009	#[test]
1010	fn imports_initial_parachain_heads() {
1011		let (state_root, proof, parachains) =
1012			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
1013				(1, head_data(1, 0)),
1014				(3, head_data(3, 10)),
1015			]);
1016		run_test(|| {
1017			initialize(state_root);
1018
1019			// we're trying to update heads of parachains 1 and 3
1020			let expected_weight =
1021				WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2);
1022			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1023				RuntimeOrigin::signed(1),
1024				(0, test_relay_header(0, state_root).hash()),
1025				parachains,
1026				proof,
1027			);
1028			assert_ok!(result);
1029			assert_eq!(result.expect("checked above").pays_fee, Pays::Yes);
1030			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1031
1032			// 1 and 3 are updated, because proof is missing head of parachain#2
1033			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
1034			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(2)), None);
1035			assert_eq!(
1036				ParasInfo::<TestRuntime>::get(ParaId(3)),
1037				Some(ParaInfo {
1038					best_head_hash: BestParaHeadHash {
1039						at_relay_block_number: 0,
1040						head_hash: head_data(3, 10).hash()
1041					},
1042					next_imported_hash_position: 1,
1043				})
1044			);
1045
1046			assert_eq!(
1047				ImportedParaHeads::<TestRuntime>::get(
1048					ParaId(1),
1049					initial_best_head(1).best_head_hash.head_hash
1050				)
1051				.map(|h| h.into_inner()),
1052				Some(stored_head_data(1, 0))
1053			);
1054			assert_eq!(
1055				ImportedParaHeads::<TestRuntime>::get(
1056					ParaId(2),
1057					initial_best_head(2).best_head_hash.head_hash
1058				)
1059				.map(|h| h.into_inner()),
1060				None
1061			);
1062			assert_eq!(
1063				ImportedParaHeads::<TestRuntime>::get(ParaId(3), head_hash(3, 10))
1064					.map(|h| h.into_inner()),
1065				Some(stored_head_data(3, 10))
1066			);
1067
1068			assert_eq!(
1069				System::<TestRuntime>::events(),
1070				vec![
1071					EventRecord {
1072						phase: Phase::Initialization,
1073						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1074							parachain: ParaId(1),
1075							parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1076						}),
1077						topics: vec![],
1078					},
1079					EventRecord {
1080						phase: Phase::Initialization,
1081						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1082							parachain: ParaId(3),
1083							parachain_head_hash: head_data(3, 10).hash(),
1084						}),
1085						topics: vec![],
1086					}
1087				],
1088			);
1089		});
1090	}
1091
1092	#[test]
1093	fn imports_parachain_heads_is_able_to_progress() {
1094		let (state_root_5, proof_5, parachains_5) =
1095			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1096		let (state_root_10, proof_10, parachains_10) =
1097			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
1098		run_test(|| {
1099			// start with relay block #0 and import head#5 of parachain#1
1100			initialize(state_root_5);
1101			let result = import_parachain_1_head(0, state_root_5, parachains_5, proof_5);
1102			// first parachain head is imported for free
1103			assert_eq!(result.unwrap().pays_fee, Pays::No);
1104			assert_eq!(
1105				ParasInfo::<TestRuntime>::get(ParaId(1)),
1106				Some(ParaInfo {
1107					best_head_hash: BestParaHeadHash {
1108						at_relay_block_number: 0,
1109						head_hash: head_data(1, 5).hash()
1110					},
1111					next_imported_hash_position: 1,
1112				})
1113			);
1114			assert_eq!(
1115				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 5).hash())
1116					.map(|h| h.into_inner()),
1117				Some(stored_head_data(1, 5))
1118			);
1119			assert_eq!(
1120				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash())
1121					.map(|h| h.into_inner()),
1122				None
1123			);
1124			assert_eq!(
1125				System::<TestRuntime>::events(),
1126				vec![EventRecord {
1127					phase: Phase::Initialization,
1128					event: TestEvent::Parachains(Event::UpdatedParachainHead {
1129						parachain: ParaId(1),
1130						parachain_head_hash: head_data(1, 5).hash(),
1131					}),
1132					topics: vec![],
1133				}],
1134			);
1135
1136			// import head#10 of parachain#1 at relay block #1
1137			let (relay_1_hash, justification) = proceed(1, state_root_10);
1138			let result = import_parachain_1_head(1, state_root_10, parachains_10, proof_10);
1139			// second parachain head is imported for fee
1140			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1141			assert_eq!(
1142				ParasInfo::<TestRuntime>::get(ParaId(1)),
1143				Some(ParaInfo {
1144					best_head_hash: BestParaHeadHash {
1145						at_relay_block_number: 1,
1146						head_hash: head_data(1, 10).hash()
1147					},
1148					next_imported_hash_position: 2,
1149				})
1150			);
1151			assert_eq!(
1152				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 5).hash())
1153					.map(|h| h.into_inner()),
1154				Some(stored_head_data(1, 5))
1155			);
1156			assert_eq!(
1157				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash())
1158					.map(|h| h.into_inner()),
1159				Some(stored_head_data(1, 10))
1160			);
1161			assert_eq!(
1162				System::<TestRuntime>::events(),
1163				vec![
1164					EventRecord {
1165						phase: Phase::Initialization,
1166						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1167							parachain: ParaId(1),
1168							parachain_head_hash: head_data(1, 5).hash(),
1169						}),
1170						topics: vec![],
1171					},
1172					EventRecord {
1173						phase: Phase::Initialization,
1174						event: TestEvent::Grandpa1(
1175							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1176								number: 1,
1177								hash: relay_1_hash,
1178								grandpa_info: StoredHeaderGrandpaInfo {
1179									finality_proof: justification,
1180									new_verification_context: None,
1181								},
1182							}
1183						),
1184						topics: vec![],
1185					},
1186					EventRecord {
1187						phase: Phase::Initialization,
1188						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1189							parachain: ParaId(1),
1190							parachain_head_hash: head_data(1, 10).hash(),
1191						}),
1192						topics: vec![],
1193					}
1194				],
1195			);
1196		});
1197	}
1198
1199	#[test]
1200	fn ignores_untracked_parachain() {
1201		let (state_root, proof, parachains) =
1202			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
1203				(1, head_data(1, 5)),
1204				(UNTRACKED_PARACHAIN_ID, head_data(1, 5)),
1205				(2, head_data(1, 5)),
1206			]);
1207		run_test(|| {
1208			// start with relay block #0 and try to import head#5 of parachain#1 and untracked
1209			// parachain
1210			let expected_weight =
1211				WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3)
1212					.saturating_sub(WeightInfo::parachain_head_storage_write_weight(
1213						DbWeight::get(),
1214					));
1215			initialize(state_root);
1216			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1217				RuntimeOrigin::signed(1),
1218				(0, test_relay_header(0, state_root).hash()),
1219				parachains,
1220				proof,
1221			);
1222			assert_ok!(result);
1223			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1224			assert_eq!(
1225				ParasInfo::<TestRuntime>::get(ParaId(1)),
1226				Some(ParaInfo {
1227					best_head_hash: BestParaHeadHash {
1228						at_relay_block_number: 0,
1229						head_hash: head_data(1, 5).hash()
1230					},
1231					next_imported_hash_position: 1,
1232				})
1233			);
1234			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,);
1235			assert_eq!(
1236				ParasInfo::<TestRuntime>::get(ParaId(2)),
1237				Some(ParaInfo {
1238					best_head_hash: BestParaHeadHash {
1239						at_relay_block_number: 0,
1240						head_hash: head_data(1, 5).hash()
1241					},
1242					next_imported_hash_position: 1,
1243				})
1244			);
1245			assert_eq!(
1246				System::<TestRuntime>::events(),
1247				vec![
1248					EventRecord {
1249						phase: Phase::Initialization,
1250						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1251							parachain: ParaId(1),
1252							parachain_head_hash: head_data(1, 5).hash(),
1253						}),
1254						topics: vec![],
1255					},
1256					EventRecord {
1257						phase: Phase::Initialization,
1258						event: TestEvent::Parachains(Event::UntrackedParachainRejected {
1259							parachain: ParaId(UNTRACKED_PARACHAIN_ID),
1260						}),
1261						topics: vec![],
1262					},
1263					EventRecord {
1264						phase: Phase::Initialization,
1265						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1266							parachain: ParaId(2),
1267							parachain_head_hash: head_data(1, 5).hash(),
1268						}),
1269						topics: vec![],
1270					}
1271				],
1272			);
1273		});
1274	}
1275
1276	#[test]
1277	fn does_nothing_when_already_imported_this_head_at_previous_relay_header() {
1278		let (state_root, proof, parachains) =
1279			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1280		run_test(|| {
1281			// import head#0 of parachain#1 at relay block#0
1282			initialize(state_root);
1283			assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone()));
1284			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
1285			assert_eq!(
1286				System::<TestRuntime>::events(),
1287				vec![EventRecord {
1288					phase: Phase::Initialization,
1289					event: TestEvent::Parachains(Event::UpdatedParachainHead {
1290						parachain: ParaId(1),
1291						parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1292					}),
1293					topics: vec![],
1294				}],
1295			);
1296
1297			// try to import head#0 of parachain#1 at relay block#1
1298			// => call succeeds, but nothing is changed
1299			let (relay_1_hash, justification) = proceed(1, state_root);
1300			assert_ok!(import_parachain_1_head(1, state_root, parachains, proof));
1301			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
1302			assert_eq!(
1303				System::<TestRuntime>::events(),
1304				vec![
1305					EventRecord {
1306						phase: Phase::Initialization,
1307						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1308							parachain: ParaId(1),
1309							parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1310						}),
1311						topics: vec![],
1312					},
1313					EventRecord {
1314						phase: Phase::Initialization,
1315						event: TestEvent::Grandpa1(
1316							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1317								number: 1,
1318								hash: relay_1_hash,
1319								grandpa_info: StoredHeaderGrandpaInfo {
1320									finality_proof: justification,
1321									new_verification_context: None,
1322								}
1323							}
1324						),
1325						topics: vec![],
1326					},
1327					EventRecord {
1328						phase: Phase::Initialization,
1329						event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead {
1330							parachain: ParaId(1),
1331							parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1332						}),
1333						topics: vec![],
1334					}
1335				],
1336			);
1337		});
1338	}
1339
1340	#[test]
1341	fn does_nothing_when_already_imported_head_at_better_relay_header() {
1342		let (state_root_5, proof_5, parachains_5) =
1343			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1344		let (state_root_10, proof_10, parachains_10) =
1345			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
1346		run_test(|| {
1347			// start with relay block #0
1348			initialize(state_root_5);
1349
1350			// head#10 of parachain#1 at relay block#1
1351			let (relay_1_hash, justification) = proceed(1, state_root_10);
1352			assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10));
1353			assert_eq!(
1354				ParasInfo::<TestRuntime>::get(ParaId(1)),
1355				Some(ParaInfo {
1356					best_head_hash: BestParaHeadHash {
1357						at_relay_block_number: 1,
1358						head_hash: head_data(1, 10).hash()
1359					},
1360					next_imported_hash_position: 1,
1361				})
1362			);
1363			assert_eq!(
1364				System::<TestRuntime>::events(),
1365				vec![
1366					EventRecord {
1367						phase: Phase::Initialization,
1368						event: TestEvent::Grandpa1(
1369							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1370								number: 1,
1371								hash: relay_1_hash,
1372								grandpa_info: StoredHeaderGrandpaInfo {
1373									finality_proof: justification.clone(),
1374									new_verification_context: None,
1375								}
1376							}
1377						),
1378						topics: vec![],
1379					},
1380					EventRecord {
1381						phase: Phase::Initialization,
1382						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1383							parachain: ParaId(1),
1384							parachain_head_hash: head_data(1, 10).hash(),
1385						}),
1386						topics: vec![],
1387					}
1388				],
1389			);
1390
1391			// now try to import head#5 at relay block#0
1392			// => nothing is changed, because better head has already been imported
1393			assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5));
1394			assert_eq!(
1395				ParasInfo::<TestRuntime>::get(ParaId(1)),
1396				Some(ParaInfo {
1397					best_head_hash: BestParaHeadHash {
1398						at_relay_block_number: 1,
1399						head_hash: head_data(1, 10).hash()
1400					},
1401					next_imported_hash_position: 1,
1402				})
1403			);
1404			assert_eq!(
1405				System::<TestRuntime>::events(),
1406				vec![
1407					EventRecord {
1408						phase: Phase::Initialization,
1409						event: TestEvent::Grandpa1(
1410							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1411								number: 1,
1412								hash: relay_1_hash,
1413								grandpa_info: StoredHeaderGrandpaInfo {
1414									finality_proof: justification,
1415									new_verification_context: None,
1416								}
1417							}
1418						),
1419						topics: vec![],
1420					},
1421					EventRecord {
1422						phase: Phase::Initialization,
1423						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1424							parachain: ParaId(1),
1425							parachain_head_hash: head_data(1, 10).hash(),
1426						}),
1427						topics: vec![],
1428					},
1429					EventRecord {
1430						phase: Phase::Initialization,
1431						event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead {
1432							parachain: ParaId(1),
1433							parachain_head_hash: head_data(1, 5).hash(),
1434						}),
1435						topics: vec![],
1436					}
1437				],
1438			);
1439		});
1440	}
1441
1442	#[test]
1443	fn does_nothing_when_parachain_head_is_too_large() {
1444		let (state_root, proof, parachains) =
1445			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
1446				(1, head_data(1, 5)),
1447				(4, big_head_data(1, 5)),
1448			]);
1449		run_test(|| {
1450			// start with relay block #0 and try to import head#5 of parachain#1 and big parachain
1451			initialize(state_root);
1452			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1453				RuntimeOrigin::signed(1),
1454				(0, test_relay_header(0, state_root).hash()),
1455				parachains,
1456				proof,
1457			);
1458			assert_ok!(result);
1459			assert_eq!(
1460				ParasInfo::<TestRuntime>::get(ParaId(1)),
1461				Some(ParaInfo {
1462					best_head_hash: BestParaHeadHash {
1463						at_relay_block_number: 0,
1464						head_hash: head_data(1, 5).hash()
1465					},
1466					next_imported_hash_position: 1,
1467				})
1468			);
1469			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(4)), None);
1470			assert_eq!(
1471				System::<TestRuntime>::events(),
1472				vec![
1473					EventRecord {
1474						phase: Phase::Initialization,
1475						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1476							parachain: ParaId(1),
1477							parachain_head_hash: head_data(1, 5).hash(),
1478						}),
1479						topics: vec![],
1480					},
1481					EventRecord {
1482						phase: Phase::Initialization,
1483						event: TestEvent::Parachains(Event::RejectedLargeParachainHead {
1484							parachain: ParaId(4),
1485							parachain_head_hash: big_head_data(1, 5).hash(),
1486							parachain_head_size: big_stored_head_data(1, 5).encoded_size() as u32,
1487						}),
1488						topics: vec![],
1489					},
1490				],
1491			);
1492		});
1493	}
1494
1495	#[test]
1496	fn prunes_old_heads() {
1497		run_test(|| {
1498			let heads_to_keep = crate::mock::HeadsToKeep::get();
1499
1500			// import exactly `HeadsToKeep` headers
1501			for i in 0..heads_to_keep {
1502				let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1503					RegularParachainHeader,
1504				>(vec![(1, head_data(1, i))]);
1505				if i == 0 {
1506					initialize(state_root);
1507				} else {
1508					proceed(i, state_root);
1509				}
1510
1511				let expected_weight = weight_of_import_parachain_1_head(&proof, false);
1512				let result = import_parachain_1_head(i, state_root, parachains, proof);
1513				assert_ok!(result);
1514				assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1515			}
1516
1517			// nothing is pruned yet
1518			for i in 0..heads_to_keep {
1519				assert!(ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, i).hash())
1520					.is_some());
1521			}
1522
1523			// import next relay chain header and next parachain head
1524			let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1525				RegularParachainHeader,
1526			>(vec![(1, head_data(1, heads_to_keep))]);
1527			proceed(heads_to_keep, state_root);
1528			let expected_weight = weight_of_import_parachain_1_head(&proof, true);
1529			let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof);
1530			assert_ok!(result);
1531			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1532
1533			// and the head#0 is pruned
1534			assert!(
1535				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 0).hash()).is_none()
1536			);
1537			for i in 1..=heads_to_keep {
1538				assert!(ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, i).hash())
1539					.is_some());
1540			}
1541		});
1542	}
1543
1544	#[test]
1545	fn fails_on_unknown_relay_chain_block() {
1546		let (state_root, proof, parachains) =
1547			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1548		run_test(|| {
1549			// start with relay block #0
1550			initialize(state_root);
1551
1552			// try to import head#5 of parachain#1 at unknown relay chain block #1
1553			assert_noop!(
1554				import_parachain_1_head(1, state_root, parachains, proof),
1555				Error::<TestRuntime>::UnknownRelayChainBlock
1556			);
1557		});
1558	}
1559
1560	#[test]
1561	fn fails_on_invalid_storage_proof() {
1562		let (_state_root, proof, parachains) =
1563			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1564		run_test(|| {
1565			// start with relay block #0
1566			initialize(Default::default());
1567
1568			// try to import head#5 of parachain#1 at relay chain block #0
1569			assert_noop!(
1570				import_parachain_1_head(0, Default::default(), parachains, proof),
1571				Error::<TestRuntime>::HeaderChainStorageProof(HeaderChainError::StorageProof(
1572					StorageProofError::StorageRootMismatch
1573				))
1574			);
1575		});
1576	}
1577
1578	#[test]
1579	fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() {
1580		let (state_root_5, proof_5, parachains_5) =
1581			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1582		let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) =
1583			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 10))]);
1584		let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) =
1585			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
1586		run_test(|| {
1587			// we've already imported head#5 of parachain#1 at relay block#10
1588			initialize(state_root_5);
1589			import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok");
1590			assert_eq!(
1591				Pallet::<TestRuntime>::best_parachain_head(ParaId(1)),
1592				Some(stored_head_data(1, 5))
1593			);
1594
1595			// then if someone is pretending to provide updated head#10 of parachain#1 at relay
1596			// block#20, but fails to do that
1597			//
1598			// => we'll leave previous value
1599			proceed(20, state_root_10_at_20);
1600			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1601				RuntimeOrigin::signed(1),
1602				(20, test_relay_header(20, state_root_10_at_20).hash()),
1603				parachains_10_at_20,
1604				proof_10_at_20,
1605			),);
1606			assert_eq!(
1607				Pallet::<TestRuntime>::best_parachain_head(ParaId(1)),
1608				Some(stored_head_data(1, 5))
1609			);
1610
1611			// then if someone is pretending to provide updated head#10 of parachain#1 at relay
1612			// block#30, and actually provides it
1613			//
1614			// => we'll update value
1615			proceed(30, state_root_10_at_30);
1616			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1617				RuntimeOrigin::signed(1),
1618				(30, test_relay_header(30, state_root_10_at_30).hash()),
1619				parachains_10_at_30,
1620				proof_10_at_30,
1621			),);
1622			assert_eq!(
1623				Pallet::<TestRuntime>::best_parachain_head(ParaId(1)),
1624				Some(stored_head_data(1, 10))
1625			);
1626		});
1627	}
1628
1629	#[test]
1630	fn storage_keys_computed_properly() {
1631		assert_eq!(
1632			ParasInfo::<TestRuntime>::storage_map_final_key(ParaId(42)).to_vec(),
1633			ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0
1634		);
1635
1636		assert_eq!(
1637			ImportedParaHeads::<TestRuntime>::storage_double_map_final_key(
1638				ParaId(42),
1639				ParaHash::from([21u8; 32])
1640			)
1641			.to_vec(),
1642			ImportedParaHeadsKeyProvider::final_key(
1643				"Parachains",
1644				&ParaId(42),
1645				&ParaHash::from([21u8; 32])
1646			)
1647			.0,
1648		);
1649	}
1650
1651	#[test]
1652	fn ignores_parachain_head_if_it_is_missing_from_storage_proof() {
1653		let (state_root, proof, _) =
1654			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![]);
1655		let parachains = vec![(ParaId(2), Default::default())];
1656		run_test(|| {
1657			initialize(state_root);
1658			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1659				RuntimeOrigin::signed(1),
1660				(0, test_relay_header(0, state_root).hash()),
1661				parachains,
1662				proof,
1663			));
1664			assert_eq!(
1665				System::<TestRuntime>::events(),
1666				vec![EventRecord {
1667					phase: Phase::Initialization,
1668					event: TestEvent::Parachains(Event::MissingParachainHead {
1669						parachain: ParaId(2),
1670					}),
1671					topics: vec![],
1672				}],
1673			);
1674		});
1675	}
1676
1677	#[test]
1678	fn ignores_parachain_head_if_parachain_head_hash_is_wrong() {
1679		let (state_root, proof, _) =
1680			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1681		let parachains = vec![(ParaId(1), head_data(1, 10).hash())];
1682		run_test(|| {
1683			initialize(state_root);
1684			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1685				RuntimeOrigin::signed(1),
1686				(0, test_relay_header(0, state_root).hash()),
1687				parachains,
1688				proof,
1689			));
1690			assert_eq!(
1691				System::<TestRuntime>::events(),
1692				vec![EventRecord {
1693					phase: Phase::Initialization,
1694					event: TestEvent::Parachains(Event::IncorrectParachainHeadHash {
1695						parachain: ParaId(1),
1696						parachain_head_hash: head_data(1, 10).hash(),
1697						actual_parachain_head_hash: head_data(1, 0).hash(),
1698					}),
1699					topics: vec![],
1700				}],
1701			);
1702		});
1703	}
1704
1705	#[test]
1706	fn test_bridge_parachain_call_is_correctly_defined() {
1707		let (state_root, proof, _) =
1708			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1709		let parachains = vec![(ParaId(2), Default::default())];
1710		let relay_header_id = (0, test_relay_header(0, state_root).hash());
1711
1712		let direct_submit_parachain_heads_call = Call::<TestRuntime>::submit_parachain_heads {
1713			at_relay_block: relay_header_id,
1714			parachains: parachains.clone(),
1715			parachain_heads_proof: proof.clone(),
1716		};
1717		let indirect_submit_parachain_heads_call = BridgeParachainCall::submit_parachain_heads {
1718			at_relay_block: relay_header_id,
1719			parachains,
1720			parachain_heads_proof: proof,
1721		};
1722		assert_eq!(
1723			direct_submit_parachain_heads_call.encode(),
1724			indirect_submit_parachain_heads_call.encode()
1725		);
1726	}
1727
1728	generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
1729
1730	#[test]
1731	fn maybe_max_parachains_returns_correct_value() {
1732		assert_eq!(MaybeMaxParachains::<TestRuntime, ()>::get(), Some(mock::TOTAL_PARACHAINS));
1733	}
1734
1735	#[test]
1736	fn maybe_max_total_parachain_hashes_returns_correct_value() {
1737		assert_eq!(
1738			MaybeMaxTotalParachainHashes::<TestRuntime, ()>::get(),
1739			Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()),
1740		);
1741	}
1742
1743	#[test]
1744	fn submit_finality_proof_requires_signed_origin() {
1745		run_test(|| {
1746			let (state_root, proof, parachains) =
1747				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1748
1749			initialize(state_root);
1750
1751			// `submit_parachain_heads()` should fail when the pallet is halted.
1752			assert_noop!(
1753				Pallet::<TestRuntime>::submit_parachain_heads(
1754					RuntimeOrigin::root(),
1755					(0, test_relay_header(0, state_root).hash()),
1756					parachains,
1757					proof,
1758				),
1759				DispatchError::BadOrigin
1760			);
1761		})
1762	}
1763
1764	#[test]
1765	fn may_be_free_for_submitting_filtered_heads() {
1766		run_test(|| {
1767			let (state_root, proof, parachains) =
1768				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 5))]);
1769			// start with relay block #0 and import head#5 of parachain#2
1770			initialize(state_root);
1771			// first submission is free
1772			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1773				RuntimeOrigin::signed(1),
1774				(0, test_relay_header(0, state_root).hash()),
1775				parachains.clone(),
1776				proof.clone(),
1777			);
1778			assert_eq!(result.unwrap().pays_fee, Pays::No);
1779			// next submission is NOT free, because we haven't updated anything
1780			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1781				RuntimeOrigin::signed(1),
1782				(0, test_relay_header(0, state_root).hash()),
1783				parachains,
1784				proof,
1785			);
1786			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1787			// then we submit new head, proved at relay block `FreeHeadersInterval - 1` => Pays::Yes
1788			let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1789				RegularParachainHeader,
1790			>(vec![(2, head_data(2, 50))]);
1791			let relay_block_number = FreeHeadersInterval::get() - 1;
1792			proceed(relay_block_number, state_root);
1793			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1794				RuntimeOrigin::signed(1),
1795				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1796				parachains,
1797				proof,
1798			);
1799			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1800			// then we submit new head, proved after `FreeHeadersInterval` => Pays::No
1801			let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1802				RegularParachainHeader,
1803			>(vec![(2, head_data(2, 100))]);
1804			let relay_block_number = relay_block_number + FreeHeadersInterval::get();
1805			proceed(relay_block_number, state_root);
1806			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1807				RuntimeOrigin::signed(1),
1808				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1809				parachains,
1810				proof,
1811			);
1812			assert_eq!(result.unwrap().pays_fee, Pays::No);
1813			// then we submit new BIG head, proved after `FreeHeadersInterval` => Pays::Yes
1814			// then we submit new head, proved after `FreeHeadersInterval` => Pays::No
1815			let mut large_head = head_data(2, 100);
1816			large_head.0.extend(&[42u8; BigParachain::MAX_HEADER_SIZE as _]);
1817			let (state_root, proof, parachains) =
1818				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, large_head)]);
1819			let relay_block_number = relay_block_number + FreeHeadersInterval::get();
1820			proceed(relay_block_number, state_root);
1821			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1822				RuntimeOrigin::signed(1),
1823				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1824				parachains,
1825				proof,
1826			);
1827			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1828		})
1829	}
1830
1831	#[test]
1832	fn grandpa_and_parachain_pallets_share_free_headers_counter() {
1833		run_test(|| {
1834			initialize(Default::default());
1835			// set free headers limit to `4`
1836			let mut free_headers_remaining = 4;
1837			pallet_bridge_grandpa::FreeHeadersRemaining::<TestRuntime, BridgesGrandpaPalletInstance>::set(
1838				Some(free_headers_remaining),
1839			);
1840			// import free GRANDPA and parachain headers
1841			let mut relay_block_number = 0;
1842			for i in 0..2 {
1843				// import free GRANDPA header
1844				let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1845					RegularParachainHeader,
1846				>(vec![(2, head_data(2, 5 + i))]);
1847				relay_block_number = relay_block_number + FreeHeadersInterval::get();
1848				proceed(relay_block_number, state_root);
1849				assert_eq!(
1850					pallet_bridge_grandpa::FreeHeadersRemaining::<
1851						TestRuntime,
1852						BridgesGrandpaPalletInstance,
1853					>::get(),
1854					Some(free_headers_remaining - 1),
1855				);
1856				free_headers_remaining = free_headers_remaining - 1;
1857				// import free parachain header
1858				assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1859					RuntimeOrigin::signed(1),
1860					(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1861					parachains,
1862					proof,
1863				),);
1864				assert_eq!(
1865					pallet_bridge_grandpa::FreeHeadersRemaining::<
1866						TestRuntime,
1867						BridgesGrandpaPalletInstance,
1868					>::get(),
1869					Some(free_headers_remaining - 1),
1870				);
1871				free_headers_remaining = free_headers_remaining - 1;
1872			}
1873			// try to import free GRANDPA header => non-free execution
1874			let (state_root, proof, parachains) =
1875				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 7))]);
1876			relay_block_number = relay_block_number + FreeHeadersInterval::get();
1877			let result = pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::submit_finality_proof_ex(
1878				RuntimeOrigin::signed(1),
1879				Box::new(test_relay_header(relay_block_number, state_root)),
1880				make_default_justification(&test_relay_header(relay_block_number, state_root)),
1881				TEST_GRANDPA_SET_ID,
1882				false,
1883			);
1884			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1885			// try to import free parachain header => non-free execution
1886			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1887				RuntimeOrigin::signed(1),
1888				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1889				parachains,
1890				proof,
1891			);
1892			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1893			assert_eq!(
1894				pallet_bridge_grandpa::FreeHeadersRemaining::<
1895					TestRuntime,
1896					BridgesGrandpaPalletInstance,
1897				>::get(),
1898				Some(0),
1899			);
1900		});
1901	}
1902}