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