referrerpolicy=no-referrer-when-downgrade

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						tracing::trace!(
465							target: LOG_TARGET,
466							?parachain,
467							"The head of parachain is None. {}",
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						tracing::trace!(
479							target: LOG_TARGET,
480							error=?e,
481							?parachain,
482							"The read of head of parachain has failed"
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					tracing::trace!(
494						target: LOG_TARGET,
495						?parachain,
496						?parachain_head_hash,
497						?actual_parachain_head_hash,
498						"The submitter has specified invalid parachain head hash"
499					);
500					Self::deposit_event(Event::IncorrectParachainHeadHash {
501						parachain,
502						parachain_head_hash,
503						actual_parachain_head_hash,
504					});
505					continue
506				}
507
508				// convert from parachain head into stored parachain head data
509				let parachain_head_size = parachain_head.0.len();
510				let parachain_head_data =
511					match T::ParaStoredHeaderDataBuilder::try_build(parachain, &parachain_head) {
512						Some(parachain_head_data) => parachain_head_data,
513						None => {
514							tracing::trace!(
515								target: LOG_TARGET,
516								?parachain,
517								"The head of parachain has been provided, but it is not tracked by the pallet"
518							);
519							Self::deposit_event(Event::UntrackedParachainRejected { parachain });
520							continue
521						},
522					};
523
524				let update_result: Result<_, ()> =
525					ParasInfo::<T, I>::try_mutate(parachain, |stored_best_head| {
526						let is_free = parachain_head_size <
527							T::ParaStoredHeaderDataBuilder::max_free_head_size() as usize &&
528							match stored_best_head {
529								Some(ref best_head)
530									if at_relay_block.0.saturating_sub(
531										best_head.best_head_hash.at_relay_block_number,
532									) >= free_headers_interval =>
533									true,
534								Some(_) => false,
535								None => true,
536							};
537						let artifacts = Pallet::<T, I>::update_parachain_head(
538							parachain,
539							stored_best_head.take(),
540							HeaderId(relay_block_number, relay_block_hash),
541							parachain_head_data,
542							parachain_head_hash,
543							parachain_head,
544						)?;
545
546						if is_free {
547							free_parachain_heads = free_parachain_heads + 1;
548						}
549
550						*stored_best_head = Some(artifacts.best_head);
551						Ok(artifacts.prune_happened)
552					});
553
554				// we're refunding weight if update has not happened and if pruning has not happened
555				let is_update_happened = update_result.is_ok();
556				if !is_update_happened {
557					actual_weight = actual_weight.saturating_sub(
558						WeightInfoOf::<T, I>::parachain_head_storage_write_weight(
559							T::DbWeight::get(),
560						),
561					);
562				}
563				let is_prune_happened = matches!(update_result, Ok(true));
564				if !is_prune_happened {
565					actual_weight = actual_weight.saturating_sub(
566						WeightInfoOf::<T, I>::parachain_head_pruning_weight(T::DbWeight::get()),
567					);
568				}
569			}
570
571			// even though we may have accepted some parachain heads, we can't allow relayers to
572			// submit proof with unused trie nodes
573			// => treat this as an error
574			//
575			// (we can throw error here, because now all our calls are transactional)
576			storage.ensure_no_unused_keys().map_err(|e| {
577				Error::<T, I>::HeaderChainStorageProof(HeaderChainError::StorageProof(e))
578			})?;
579
580			// check if we allow this submission for free
581			let is_free = total_parachains == 1
582				&& free_parachain_heads == total_parachains
583				&& SubmitFinalityProofHelper::<T, T::BridgesGrandpaPalletInstance>::has_free_header_slots();
584			let pays_fee = if is_free {
585				tracing::trace!(target: LOG_TARGET, "Parachain heads update transaction is free");
586				pallet_bridge_grandpa::on_free_header_imported::<T, T::BridgesGrandpaPalletInstance>(
587				);
588				Pays::No
589			} else {
590				tracing::trace!(target: LOG_TARGET, "Parachain heads update transaction is paid");
591				Pays::Yes
592			};
593
594			Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee })
595		}
596	}
597
598	impl<T: Config<I>, I: 'static> Pallet<T, I> {
599		/// Get stored parachain info.
600		pub fn best_parachain_info(parachain: ParaId) -> Option<ParaInfo> {
601			ParasInfo::<T, I>::get(parachain)
602		}
603
604		/// Get best finalized head data of the given parachain.
605		pub fn best_parachain_head(parachain: ParaId) -> Option<ParaStoredHeaderData> {
606			let best_para_head_hash = ParasInfo::<T, I>::get(parachain)?.best_head_hash.head_hash;
607			ImportedParaHeads::<T, I>::get(parachain, best_para_head_hash).map(|h| h.into_inner())
608		}
609
610		/// Get best finalized head hash of the given parachain.
611		pub fn best_parachain_head_hash(parachain: ParaId) -> Option<ParaHash> {
612			Some(ParasInfo::<T, I>::get(parachain)?.best_head_hash.head_hash)
613		}
614
615		/// Get best finalized head id of the given parachain.
616		pub fn best_parachain_head_id<C: Chain<Hash = ParaHash> + Parachain>(
617		) -> Result<Option<HeaderIdOf<C>>, codec::Error> {
618			let parachain = ParaId(C::PARACHAIN_ID);
619			let best_head_hash = match Self::best_parachain_head_hash(parachain) {
620				Some(best_head_hash) => best_head_hash,
621				None => return Ok(None),
622			};
623			let encoded_head = match Self::parachain_head(parachain, best_head_hash) {
624				Some(encoded_head) => encoded_head,
625				None => return Ok(None),
626			};
627			encoded_head
628				.decode_parachain_head_data::<C>()
629				.map(|data| Some(HeaderId(data.number, best_head_hash)))
630		}
631
632		/// Get parachain head data with given hash.
633		pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option<ParaStoredHeaderData> {
634			ImportedParaHeads::<T, I>::get(parachain, hash).map(|h| h.into_inner())
635		}
636
637		/// Try to update parachain head.
638		pub(super) fn update_parachain_head(
639			parachain: ParaId,
640			stored_best_head: Option<ParaInfo>,
641			new_at_relay_block: HeaderId<RelayBlockHash, RelayBlockNumber>,
642			new_head_data: ParaStoredHeaderData,
643			new_head_hash: ParaHash,
644			new_head: ParaHead,
645		) -> Result<UpdateParachainHeadArtifacts, ()> {
646			// check if head has been already updated at better relay chain block. Without this
647			// check, we may import heads in random order
648			let update = SubmitParachainHeadsInfo {
649				at_relay_block: new_at_relay_block,
650				para_id: parachain,
651				para_head_hash: new_head_hash,
652				// doesn't actually matter here
653				is_free_execution_expected: false,
654			};
655			if SubmitParachainHeadsHelper::<T, I>::check_obsolete(&update).is_err() {
656				Self::deposit_event(Event::RejectedObsoleteParachainHead {
657					parachain,
658					parachain_head_hash: new_head_hash,
659				});
660				return Err(())
661			}
662
663			// verify that the parachain head data size is <= `MaxParaHeadDataSize`
664			let updated_head_data = match StoredParaHeadDataOf::<T, I>::try_from_inner(
665				new_head_data,
666			) {
667				Ok(updated_head_data) => updated_head_data,
668				Err(e) => {
669					tracing::trace!(
670						target: LOG_TARGET,
671						error=?e,
672						?parachain,
673						"The parachain head can't be updated. The parachain head data size exceeds maximal configured size."
674					);
675
676					Self::deposit_event(Event::RejectedLargeParachainHead {
677						parachain,
678						parachain_head_hash: new_head_hash,
679						parachain_head_size: e.value_size as _,
680					});
681
682					return Err(())
683				},
684			};
685
686			let next_imported_hash_position = stored_best_head
687				.map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position);
688
689			// insert updated best parachain head
690			let head_hash_to_prune =
691				ImportedParaHashes::<T, I>::try_get(parachain, next_imported_hash_position);
692			let updated_best_para_head = ParaInfo {
693				best_head_hash: BestParaHeadHash {
694					at_relay_block_number: new_at_relay_block.0,
695					head_hash: new_head_hash,
696				},
697				next_imported_hash_position: (next_imported_hash_position + 1) %
698					T::HeadsToKeep::get(),
699			};
700			ImportedParaHashes::<T, I>::insert(
701				parachain,
702				next_imported_hash_position,
703				new_head_hash,
704			);
705			ImportedParaHeads::<T, I>::insert(parachain, new_head_hash, &updated_head_data);
706			tracing::trace!(
707				target: LOG_TARGET,
708				?parachain,
709				%new_head_hash,
710				at_relay_block=%new_at_relay_block.0,
711				"Updated head of parachain"
712			);
713
714			// trigger callback
715			T::OnNewHead::on_new_head(parachain, &new_head);
716
717			// remove old head
718			let prune_happened = head_hash_to_prune.is_ok();
719			if let Ok(head_hash_to_prune) = head_hash_to_prune {
720				tracing::trace!(
721					target: LOG_TARGET,
722					?parachain,
723					%head_hash_to_prune,
724					"Pruning old head of parachain"
725				);
726				ImportedParaHeads::<T, I>::remove(parachain, head_hash_to_prune);
727			}
728			Self::deposit_event(Event::UpdatedParachainHead {
729				parachain,
730				parachain_head_hash: new_head_hash,
731			});
732
733			Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened })
734		}
735	}
736
737	#[pallet::genesis_config]
738	#[derive(DefaultNoBound)]
739	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
740		/// Initial pallet operating mode.
741		pub operating_mode: BasicOperatingMode,
742		/// Initial pallet owner.
743		pub owner: Option<T::AccountId>,
744		/// Dummy marker.
745		#[serde(skip)]
746		pub _phantom: sp_std::marker::PhantomData<I>,
747	}
748
749	#[pallet::genesis_build]
750	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
751		fn build(&self) {
752			PalletOperatingMode::<T, I>::put(self.operating_mode);
753			if let Some(ref owner) = self.owner {
754				PalletOwner::<T, I>::put(owner);
755			}
756		}
757	}
758
759	/// Returns maximal number of parachains, supported by the pallet.
760	pub struct MaybeMaxParachains<T, I>(PhantomData<(T, I)>);
761
762	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeMaxParachains<T, I> {
763		fn get() -> Option<u32> {
764			Some(T::ParaStoredHeaderDataBuilder::supported_parachains())
765		}
766	}
767
768	/// Returns total number of all parachains hashes/heads, stored by the pallet.
769	pub struct MaybeMaxTotalParachainHashes<T, I>(PhantomData<(T, I)>);
770
771	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeMaxTotalParachainHashes<T, I> {
772		fn get() -> Option<u32> {
773			Some(
774				T::ParaStoredHeaderDataBuilder::supported_parachains()
775					.saturating_mul(T::HeadsToKeep::get()),
776			)
777		}
778	}
779}
780
781/// Single parachain header chain adapter.
782pub struct ParachainHeaders<T, I, C>(PhantomData<(T, I, C)>);
783
784impl<T: Config<I>, I: 'static, C: Parachain<Hash = ParaHash>> HeaderChain<C>
785	for ParachainHeaders<T, I, C>
786{
787	fn finalized_header_state_root(hash: HashOf<C>) -> Option<HashOf<C>> {
788		Pallet::<T, I>::parachain_head(ParaId(C::PARACHAIN_ID), hash)
789			.and_then(|head| head.decode_parachain_head_data::<C>().ok())
790			.map(|h| h.state_root)
791	}
792}
793
794/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks.
795#[cfg(feature = "runtime-benchmarks")]
796pub fn initialize_for_benchmarks<T: Config<I>, I: 'static, PC: Parachain<Hash = ParaHash>>(
797	header: HeaderOf<PC>,
798) {
799	use bp_polkadot_core::parachains::ParaHead;
800	use bp_runtime::HeaderIdProvider;
801	use sp_runtime::traits::Header;
802
803	let relay_head =
804		pallet_bridge_grandpa::BridgedHeader::<T, T::BridgesGrandpaPalletInstance>::new(
805			0,
806			Default::default(),
807			Default::default(),
808			Default::default(),
809			Default::default(),
810		);
811	let parachain = ParaId(PC::PARACHAIN_ID);
812	let parachain_head = ParaHead(header.encode());
813	let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, &parachain_head)
814		.expect("failed to build stored parachain head in benchmarks");
815	pallet_bridge_grandpa::initialize_for_benchmarks::<T, T::BridgesGrandpaPalletInstance>(
816		relay_head.clone(),
817	);
818	Pallet::<T, I>::update_parachain_head(
819		parachain,
820		None,
821		relay_head.id(),
822		updated_head_data,
823		parachain_head.hash(),
824		parachain_head,
825	)
826	.expect("failed to insert parachain head in benchmarks");
827}
828
829#[cfg(test)]
830pub(crate) mod tests {
831	use super::*;
832	use crate::mock::{
833		run_test, test_relay_header, BigParachain, BigParachainHeader, FreeHeadersInterval,
834		RegularParachainHasher, RegularParachainHeader, RelayBlockHeader,
835		RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, UNTRACKED_PARACHAIN_ID,
836	};
837	use bp_test_utils::prepare_parachain_heads_proof;
838	use codec::Encode;
839
840	use bp_header_chain::{justification::GrandpaJustification, StoredHeaderGrandpaInfo};
841	use bp_parachains::{
842		BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider,
843	};
844	use bp_polkadot_core::parachains::ParaHead;
845	use bp_runtime::{
846		BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider,
847		StorageMapKeyProvider, StorageProofError,
848	};
849	use bp_test_utils::{
850		authority_list, generate_owned_bridge_module_tests, make_default_justification,
851		TEST_GRANDPA_SET_ID,
852	};
853	use frame_support::{
854		assert_noop, assert_ok,
855		dispatch::DispatchResultWithPostInfo,
856		pallet_prelude::Pays,
857		storage::generator::{StorageDoubleMap, StorageMap},
858		traits::Get,
859		weights::Weight,
860	};
861	use frame_system::{EventRecord, Pallet as System, Phase};
862	use sp_core::Hasher;
863	use sp_runtime::{traits::Header as HeaderT, DispatchError};
864
865	type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1;
866	type WeightInfo = <TestRuntime as Config>::WeightInfo;
867	type DbWeight = <TestRuntime as frame_system::Config>::DbWeight;
868
869	pub(crate) fn initialize(state_root: RelayBlockHash) -> RelayBlockHash {
870		pallet_bridge_grandpa::FreeHeadersRemaining::<TestRuntime, BridgesGrandpaPalletInstance>::set(Some(100));
871		pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::initialize(
872			RuntimeOrigin::root(),
873			bp_header_chain::InitializationData {
874				header: Box::new(test_relay_header(0, state_root)),
875				authority_list: authority_list(),
876				set_id: 1,
877				operating_mode: BasicOperatingMode::Normal,
878			},
879		)
880		.unwrap();
881
882		System::<TestRuntime>::set_block_number(1);
883		System::<TestRuntime>::reset_events();
884
885		test_relay_header(0, state_root).hash()
886	}
887
888	fn proceed(
889		num: RelayBlockNumber,
890		state_root: RelayBlockHash,
891	) -> (ParaHash, GrandpaJustification<RelayBlockHeader>) {
892		let header = test_relay_header(num, state_root);
893		let hash = header.hash();
894		let justification = make_default_justification(&header);
895		assert_ok!(
896			pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::submit_finality_proof_ex(
897				RuntimeOrigin::signed(1),
898				Box::new(header),
899				justification.clone(),
900				TEST_GRANDPA_SET_ID,
901				false,
902			)
903		);
904
905		(hash, justification)
906	}
907
908	fn initial_best_head(parachain: u32) -> ParaInfo {
909		ParaInfo {
910			best_head_hash: BestParaHeadHash {
911				at_relay_block_number: 0,
912				head_hash: head_data(parachain, 0).hash(),
913			},
914			next_imported_hash_position: 1,
915		}
916	}
917
918	pub(crate) fn head_data(parachain: u32, head_number: u32) -> ParaHead {
919		ParaHead(
920			RegularParachainHeader::new(
921				head_number as _,
922				Default::default(),
923				RegularParachainHasher::hash(&(parachain, head_number).encode()),
924				Default::default(),
925				Default::default(),
926			)
927			.encode(),
928		)
929	}
930
931	fn stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData {
932		ParaStoredHeaderData(
933			(head_number as u64, RegularParachainHasher::hash(&(parachain, head_number).encode()))
934				.encode(),
935		)
936	}
937
938	fn big_head_data(parachain: u32, head_number: u32) -> ParaHead {
939		ParaHead(
940			BigParachainHeader::new(
941				head_number as _,
942				Default::default(),
943				RegularParachainHasher::hash(&(parachain, head_number).encode()),
944				Default::default(),
945				Default::default(),
946			)
947			.encode(),
948		)
949	}
950
951	fn big_stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData {
952		ParaStoredHeaderData(
953			(head_number as u128, RegularParachainHasher::hash(&(parachain, head_number).encode()))
954				.encode(),
955		)
956	}
957
958	fn head_hash(parachain: u32, head_number: u32) -> ParaHash {
959		head_data(parachain, head_number).hash()
960	}
961
962	fn import_parachain_1_head(
963		relay_chain_block: RelayBlockNumber,
964		relay_state_root: RelayBlockHash,
965		parachains: Vec<(ParaId, ParaHash)>,
966		proof: ParaHeadsProof,
967	) -> DispatchResultWithPostInfo {
968		Pallet::<TestRuntime>::submit_parachain_heads(
969			RuntimeOrigin::signed(1),
970			(relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()),
971			parachains,
972			proof,
973		)
974	}
975
976	fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight {
977		let db_weight = <TestRuntime as frame_system::Config>::DbWeight::get();
978		WeightInfoOf::<TestRuntime, ()>::submit_parachain_heads_weight(db_weight, proof, 1)
979			.saturating_sub(if prune_expected {
980				Weight::zero()
981			} else {
982				WeightInfoOf::<TestRuntime, ()>::parachain_head_pruning_weight(db_weight)
983			})
984	}
985
986	#[test]
987	fn submit_parachain_heads_checks_operating_mode() {
988		let (state_root, proof, parachains) =
989			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
990
991		run_test(|| {
992			initialize(state_root);
993
994			// `submit_parachain_heads()` should fail when the pallet is halted.
995			PalletOperatingMode::<TestRuntime>::put(BasicOperatingMode::Halted);
996			assert_noop!(
997				Pallet::<TestRuntime>::submit_parachain_heads(
998					RuntimeOrigin::signed(1),
999					(0, test_relay_header(0, state_root).hash()),
1000					parachains.clone(),
1001					proof.clone(),
1002				),
1003				Error::<TestRuntime>::BridgeModule(OwnedBridgeModuleError::Halted)
1004			);
1005
1006			// `submit_parachain_heads()` should succeed now that the pallet is resumed.
1007			PalletOperatingMode::<TestRuntime>::put(BasicOperatingMode::Normal);
1008			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1009				RuntimeOrigin::signed(1),
1010				(0, test_relay_header(0, state_root).hash()),
1011				parachains,
1012				proof,
1013			),);
1014		});
1015	}
1016
1017	#[test]
1018	fn imports_initial_parachain_heads() {
1019		let (state_root, proof, parachains) =
1020			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
1021				(1, head_data(1, 0)),
1022				(3, head_data(3, 10)),
1023			]);
1024		run_test(|| {
1025			initialize(state_root);
1026
1027			// we're trying to update heads of parachains 1 and 3
1028			let expected_weight =
1029				WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2);
1030			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1031				RuntimeOrigin::signed(1),
1032				(0, test_relay_header(0, state_root).hash()),
1033				parachains,
1034				proof,
1035			);
1036			assert_ok!(result);
1037			assert_eq!(result.expect("checked above").pays_fee, Pays::Yes);
1038			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1039
1040			// 1 and 3 are updated, because proof is missing head of parachain#2
1041			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
1042			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(2)), None);
1043			assert_eq!(
1044				ParasInfo::<TestRuntime>::get(ParaId(3)),
1045				Some(ParaInfo {
1046					best_head_hash: BestParaHeadHash {
1047						at_relay_block_number: 0,
1048						head_hash: head_data(3, 10).hash()
1049					},
1050					next_imported_hash_position: 1,
1051				})
1052			);
1053
1054			assert_eq!(
1055				ImportedParaHeads::<TestRuntime>::get(
1056					ParaId(1),
1057					initial_best_head(1).best_head_hash.head_hash
1058				)
1059				.map(|h| h.into_inner()),
1060				Some(stored_head_data(1, 0))
1061			);
1062			assert_eq!(
1063				ImportedParaHeads::<TestRuntime>::get(
1064					ParaId(2),
1065					initial_best_head(2).best_head_hash.head_hash
1066				)
1067				.map(|h| h.into_inner()),
1068				None
1069			);
1070			assert_eq!(
1071				ImportedParaHeads::<TestRuntime>::get(ParaId(3), head_hash(3, 10))
1072					.map(|h| h.into_inner()),
1073				Some(stored_head_data(3, 10))
1074			);
1075
1076			assert_eq!(
1077				System::<TestRuntime>::events(),
1078				vec![
1079					EventRecord {
1080						phase: Phase::Initialization,
1081						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1082							parachain: ParaId(1),
1083							parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1084						}),
1085						topics: vec![],
1086					},
1087					EventRecord {
1088						phase: Phase::Initialization,
1089						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1090							parachain: ParaId(3),
1091							parachain_head_hash: head_data(3, 10).hash(),
1092						}),
1093						topics: vec![],
1094					}
1095				],
1096			);
1097		});
1098	}
1099
1100	#[test]
1101	fn imports_parachain_heads_is_able_to_progress() {
1102		let (state_root_5, proof_5, parachains_5) =
1103			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1104		let (state_root_10, proof_10, parachains_10) =
1105			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
1106		run_test(|| {
1107			// start with relay block #0 and import head#5 of parachain#1
1108			initialize(state_root_5);
1109			let result = import_parachain_1_head(0, state_root_5, parachains_5, proof_5);
1110			// first parachain head is imported for free
1111			assert_eq!(result.unwrap().pays_fee, Pays::No);
1112			assert_eq!(
1113				ParasInfo::<TestRuntime>::get(ParaId(1)),
1114				Some(ParaInfo {
1115					best_head_hash: BestParaHeadHash {
1116						at_relay_block_number: 0,
1117						head_hash: head_data(1, 5).hash()
1118					},
1119					next_imported_hash_position: 1,
1120				})
1121			);
1122			assert_eq!(
1123				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 5).hash())
1124					.map(|h| h.into_inner()),
1125				Some(stored_head_data(1, 5))
1126			);
1127			assert_eq!(
1128				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash())
1129					.map(|h| h.into_inner()),
1130				None
1131			);
1132			assert_eq!(
1133				System::<TestRuntime>::events(),
1134				vec![EventRecord {
1135					phase: Phase::Initialization,
1136					event: TestEvent::Parachains(Event::UpdatedParachainHead {
1137						parachain: ParaId(1),
1138						parachain_head_hash: head_data(1, 5).hash(),
1139					}),
1140					topics: vec![],
1141				}],
1142			);
1143
1144			// import head#10 of parachain#1 at relay block #1
1145			let (relay_1_hash, justification) = proceed(1, state_root_10);
1146			let result = import_parachain_1_head(1, state_root_10, parachains_10, proof_10);
1147			// second parachain head is imported for fee
1148			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1149			assert_eq!(
1150				ParasInfo::<TestRuntime>::get(ParaId(1)),
1151				Some(ParaInfo {
1152					best_head_hash: BestParaHeadHash {
1153						at_relay_block_number: 1,
1154						head_hash: head_data(1, 10).hash()
1155					},
1156					next_imported_hash_position: 2,
1157				})
1158			);
1159			assert_eq!(
1160				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 5).hash())
1161					.map(|h| h.into_inner()),
1162				Some(stored_head_data(1, 5))
1163			);
1164			assert_eq!(
1165				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash())
1166					.map(|h| h.into_inner()),
1167				Some(stored_head_data(1, 10))
1168			);
1169			assert_eq!(
1170				System::<TestRuntime>::events(),
1171				vec![
1172					EventRecord {
1173						phase: Phase::Initialization,
1174						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1175							parachain: ParaId(1),
1176							parachain_head_hash: head_data(1, 5).hash(),
1177						}),
1178						topics: vec![],
1179					},
1180					EventRecord {
1181						phase: Phase::Initialization,
1182						event: TestEvent::Grandpa1(
1183							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1184								number: 1,
1185								hash: relay_1_hash,
1186								grandpa_info: StoredHeaderGrandpaInfo {
1187									finality_proof: justification,
1188									new_verification_context: None,
1189								},
1190							}
1191						),
1192						topics: vec![],
1193					},
1194					EventRecord {
1195						phase: Phase::Initialization,
1196						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1197							parachain: ParaId(1),
1198							parachain_head_hash: head_data(1, 10).hash(),
1199						}),
1200						topics: vec![],
1201					}
1202				],
1203			);
1204		});
1205	}
1206
1207	#[test]
1208	fn ignores_untracked_parachain() {
1209		let (state_root, proof, parachains) =
1210			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
1211				(1, head_data(1, 5)),
1212				(UNTRACKED_PARACHAIN_ID, head_data(1, 5)),
1213				(2, head_data(1, 5)),
1214			]);
1215		run_test(|| {
1216			// start with relay block #0 and try to import head#5 of parachain#1 and untracked
1217			// parachain
1218			let expected_weight =
1219				WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3)
1220					.saturating_sub(WeightInfo::parachain_head_storage_write_weight(
1221						DbWeight::get(),
1222					));
1223			initialize(state_root);
1224			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1225				RuntimeOrigin::signed(1),
1226				(0, test_relay_header(0, state_root).hash()),
1227				parachains,
1228				proof,
1229			);
1230			assert_ok!(result);
1231			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1232			assert_eq!(
1233				ParasInfo::<TestRuntime>::get(ParaId(1)),
1234				Some(ParaInfo {
1235					best_head_hash: BestParaHeadHash {
1236						at_relay_block_number: 0,
1237						head_hash: head_data(1, 5).hash()
1238					},
1239					next_imported_hash_position: 1,
1240				})
1241			);
1242			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,);
1243			assert_eq!(
1244				ParasInfo::<TestRuntime>::get(ParaId(2)),
1245				Some(ParaInfo {
1246					best_head_hash: BestParaHeadHash {
1247						at_relay_block_number: 0,
1248						head_hash: head_data(1, 5).hash()
1249					},
1250					next_imported_hash_position: 1,
1251				})
1252			);
1253			assert_eq!(
1254				System::<TestRuntime>::events(),
1255				vec![
1256					EventRecord {
1257						phase: Phase::Initialization,
1258						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1259							parachain: ParaId(1),
1260							parachain_head_hash: head_data(1, 5).hash(),
1261						}),
1262						topics: vec![],
1263					},
1264					EventRecord {
1265						phase: Phase::Initialization,
1266						event: TestEvent::Parachains(Event::UntrackedParachainRejected {
1267							parachain: ParaId(UNTRACKED_PARACHAIN_ID),
1268						}),
1269						topics: vec![],
1270					},
1271					EventRecord {
1272						phase: Phase::Initialization,
1273						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1274							parachain: ParaId(2),
1275							parachain_head_hash: head_data(1, 5).hash(),
1276						}),
1277						topics: vec![],
1278					}
1279				],
1280			);
1281		});
1282	}
1283
1284	#[test]
1285	fn does_nothing_when_already_imported_this_head_at_previous_relay_header() {
1286		let (state_root, proof, parachains) =
1287			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1288		run_test(|| {
1289			// import head#0 of parachain#1 at relay block#0
1290			initialize(state_root);
1291			assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone()));
1292			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
1293			assert_eq!(
1294				System::<TestRuntime>::events(),
1295				vec![EventRecord {
1296					phase: Phase::Initialization,
1297					event: TestEvent::Parachains(Event::UpdatedParachainHead {
1298						parachain: ParaId(1),
1299						parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1300					}),
1301					topics: vec![],
1302				}],
1303			);
1304
1305			// try to import head#0 of parachain#1 at relay block#1
1306			// => call succeeds, but nothing is changed
1307			let (relay_1_hash, justification) = proceed(1, state_root);
1308			assert_ok!(import_parachain_1_head(1, state_root, parachains, proof));
1309			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
1310			assert_eq!(
1311				System::<TestRuntime>::events(),
1312				vec![
1313					EventRecord {
1314						phase: Phase::Initialization,
1315						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1316							parachain: ParaId(1),
1317							parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1318						}),
1319						topics: vec![],
1320					},
1321					EventRecord {
1322						phase: Phase::Initialization,
1323						event: TestEvent::Grandpa1(
1324							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1325								number: 1,
1326								hash: relay_1_hash,
1327								grandpa_info: StoredHeaderGrandpaInfo {
1328									finality_proof: justification,
1329									new_verification_context: None,
1330								}
1331							}
1332						),
1333						topics: vec![],
1334					},
1335					EventRecord {
1336						phase: Phase::Initialization,
1337						event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead {
1338							parachain: ParaId(1),
1339							parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
1340						}),
1341						topics: vec![],
1342					}
1343				],
1344			);
1345		});
1346	}
1347
1348	#[test]
1349	fn does_nothing_when_already_imported_head_at_better_relay_header() {
1350		let (state_root_5, proof_5, parachains_5) =
1351			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1352		let (state_root_10, proof_10, parachains_10) =
1353			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
1354		run_test(|| {
1355			// start with relay block #0
1356			initialize(state_root_5);
1357
1358			// head#10 of parachain#1 at relay block#1
1359			let (relay_1_hash, justification) = proceed(1, state_root_10);
1360			assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10));
1361			assert_eq!(
1362				ParasInfo::<TestRuntime>::get(ParaId(1)),
1363				Some(ParaInfo {
1364					best_head_hash: BestParaHeadHash {
1365						at_relay_block_number: 1,
1366						head_hash: head_data(1, 10).hash()
1367					},
1368					next_imported_hash_position: 1,
1369				})
1370			);
1371			assert_eq!(
1372				System::<TestRuntime>::events(),
1373				vec![
1374					EventRecord {
1375						phase: Phase::Initialization,
1376						event: TestEvent::Grandpa1(
1377							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1378								number: 1,
1379								hash: relay_1_hash,
1380								grandpa_info: StoredHeaderGrandpaInfo {
1381									finality_proof: justification.clone(),
1382									new_verification_context: None,
1383								}
1384							}
1385						),
1386						topics: vec![],
1387					},
1388					EventRecord {
1389						phase: Phase::Initialization,
1390						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1391							parachain: ParaId(1),
1392							parachain_head_hash: head_data(1, 10).hash(),
1393						}),
1394						topics: vec![],
1395					}
1396				],
1397			);
1398
1399			// now try to import head#5 at relay block#0
1400			// => nothing is changed, because better head has already been imported
1401			assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5));
1402			assert_eq!(
1403				ParasInfo::<TestRuntime>::get(ParaId(1)),
1404				Some(ParaInfo {
1405					best_head_hash: BestParaHeadHash {
1406						at_relay_block_number: 1,
1407						head_hash: head_data(1, 10).hash()
1408					},
1409					next_imported_hash_position: 1,
1410				})
1411			);
1412			assert_eq!(
1413				System::<TestRuntime>::events(),
1414				vec![
1415					EventRecord {
1416						phase: Phase::Initialization,
1417						event: TestEvent::Grandpa1(
1418							pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader {
1419								number: 1,
1420								hash: relay_1_hash,
1421								grandpa_info: StoredHeaderGrandpaInfo {
1422									finality_proof: justification,
1423									new_verification_context: None,
1424								}
1425							}
1426						),
1427						topics: vec![],
1428					},
1429					EventRecord {
1430						phase: Phase::Initialization,
1431						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1432							parachain: ParaId(1),
1433							parachain_head_hash: head_data(1, 10).hash(),
1434						}),
1435						topics: vec![],
1436					},
1437					EventRecord {
1438						phase: Phase::Initialization,
1439						event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead {
1440							parachain: ParaId(1),
1441							parachain_head_hash: head_data(1, 5).hash(),
1442						}),
1443						topics: vec![],
1444					}
1445				],
1446			);
1447		});
1448	}
1449
1450	#[test]
1451	fn does_nothing_when_parachain_head_is_too_large() {
1452		let (state_root, proof, parachains) =
1453			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![
1454				(1, head_data(1, 5)),
1455				(4, big_head_data(1, 5)),
1456			]);
1457		run_test(|| {
1458			// start with relay block #0 and try to import head#5 of parachain#1 and big parachain
1459			initialize(state_root);
1460			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1461				RuntimeOrigin::signed(1),
1462				(0, test_relay_header(0, state_root).hash()),
1463				parachains,
1464				proof,
1465			);
1466			assert_ok!(result);
1467			assert_eq!(
1468				ParasInfo::<TestRuntime>::get(ParaId(1)),
1469				Some(ParaInfo {
1470					best_head_hash: BestParaHeadHash {
1471						at_relay_block_number: 0,
1472						head_hash: head_data(1, 5).hash()
1473					},
1474					next_imported_hash_position: 1,
1475				})
1476			);
1477			assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(4)), None);
1478			assert_eq!(
1479				System::<TestRuntime>::events(),
1480				vec![
1481					EventRecord {
1482						phase: Phase::Initialization,
1483						event: TestEvent::Parachains(Event::UpdatedParachainHead {
1484							parachain: ParaId(1),
1485							parachain_head_hash: head_data(1, 5).hash(),
1486						}),
1487						topics: vec![],
1488					},
1489					EventRecord {
1490						phase: Phase::Initialization,
1491						event: TestEvent::Parachains(Event::RejectedLargeParachainHead {
1492							parachain: ParaId(4),
1493							parachain_head_hash: big_head_data(1, 5).hash(),
1494							parachain_head_size: big_stored_head_data(1, 5).encoded_size() as u32,
1495						}),
1496						topics: vec![],
1497					},
1498				],
1499			);
1500		});
1501	}
1502
1503	#[test]
1504	fn prunes_old_heads() {
1505		run_test(|| {
1506			let heads_to_keep = crate::mock::HeadsToKeep::get();
1507
1508			// import exactly `HeadsToKeep` headers
1509			for i in 0..heads_to_keep {
1510				let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1511					RegularParachainHeader,
1512				>(vec![(1, head_data(1, i))]);
1513				if i == 0 {
1514					initialize(state_root);
1515				} else {
1516					proceed(i, state_root);
1517				}
1518
1519				let expected_weight = weight_of_import_parachain_1_head(&proof, false);
1520				let result = import_parachain_1_head(i, state_root, parachains, proof);
1521				assert_ok!(result);
1522				assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1523			}
1524
1525			// nothing is pruned yet
1526			for i in 0..heads_to_keep {
1527				assert!(ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, i).hash())
1528					.is_some());
1529			}
1530
1531			// import next relay chain header and next parachain head
1532			let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1533				RegularParachainHeader,
1534			>(vec![(1, head_data(1, heads_to_keep))]);
1535			proceed(heads_to_keep, state_root);
1536			let expected_weight = weight_of_import_parachain_1_head(&proof, true);
1537			let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof);
1538			assert_ok!(result);
1539			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
1540
1541			// and the head#0 is pruned
1542			assert!(
1543				ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 0).hash()).is_none()
1544			);
1545			for i in 1..=heads_to_keep {
1546				assert!(ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, i).hash())
1547					.is_some());
1548			}
1549		});
1550	}
1551
1552	#[test]
1553	fn fails_on_unknown_relay_chain_block() {
1554		let (state_root, proof, parachains) =
1555			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1556		run_test(|| {
1557			// start with relay block #0
1558			initialize(state_root);
1559
1560			// try to import head#5 of parachain#1 at unknown relay chain block #1
1561			assert_noop!(
1562				import_parachain_1_head(1, state_root, parachains, proof),
1563				Error::<TestRuntime>::UnknownRelayChainBlock
1564			);
1565		});
1566	}
1567
1568	#[test]
1569	fn fails_on_invalid_storage_proof() {
1570		let (_state_root, proof, parachains) =
1571			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1572		run_test(|| {
1573			// start with relay block #0
1574			initialize(Default::default());
1575
1576			// try to import head#5 of parachain#1 at relay chain block #0
1577			assert_noop!(
1578				import_parachain_1_head(0, Default::default(), parachains, proof),
1579				Error::<TestRuntime>::HeaderChainStorageProof(HeaderChainError::StorageProof(
1580					StorageProofError::StorageRootMismatch
1581				))
1582			);
1583		});
1584	}
1585
1586	#[test]
1587	fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() {
1588		let (state_root_5, proof_5, parachains_5) =
1589			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 5))]);
1590		let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) =
1591			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 10))]);
1592		let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) =
1593			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 10))]);
1594		run_test(|| {
1595			// we've already imported head#5 of parachain#1 at relay block#10
1596			initialize(state_root_5);
1597			import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok");
1598			assert_eq!(
1599				Pallet::<TestRuntime>::best_parachain_head(ParaId(1)),
1600				Some(stored_head_data(1, 5))
1601			);
1602
1603			// then if someone is pretending to provide updated head#10 of parachain#1 at relay
1604			// block#20, but fails to do that
1605			//
1606			// => we'll leave previous value
1607			proceed(20, state_root_10_at_20);
1608			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1609				RuntimeOrigin::signed(1),
1610				(20, test_relay_header(20, state_root_10_at_20).hash()),
1611				parachains_10_at_20,
1612				proof_10_at_20,
1613			),);
1614			assert_eq!(
1615				Pallet::<TestRuntime>::best_parachain_head(ParaId(1)),
1616				Some(stored_head_data(1, 5))
1617			);
1618
1619			// then if someone is pretending to provide updated head#10 of parachain#1 at relay
1620			// block#30, and actually provides it
1621			//
1622			// => we'll update value
1623			proceed(30, state_root_10_at_30);
1624			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1625				RuntimeOrigin::signed(1),
1626				(30, test_relay_header(30, state_root_10_at_30).hash()),
1627				parachains_10_at_30,
1628				proof_10_at_30,
1629			),);
1630			assert_eq!(
1631				Pallet::<TestRuntime>::best_parachain_head(ParaId(1)),
1632				Some(stored_head_data(1, 10))
1633			);
1634		});
1635	}
1636
1637	#[test]
1638	fn storage_keys_computed_properly() {
1639		assert_eq!(
1640			ParasInfo::<TestRuntime>::storage_map_final_key(ParaId(42)).to_vec(),
1641			ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0
1642		);
1643
1644		assert_eq!(
1645			ImportedParaHeads::<TestRuntime>::storage_double_map_final_key(
1646				ParaId(42),
1647				ParaHash::from([21u8; 32])
1648			)
1649			.to_vec(),
1650			ImportedParaHeadsKeyProvider::final_key(
1651				"Parachains",
1652				&ParaId(42),
1653				&ParaHash::from([21u8; 32])
1654			)
1655			.0,
1656		);
1657	}
1658
1659	#[test]
1660	fn ignores_parachain_head_if_it_is_missing_from_storage_proof() {
1661		let (state_root, proof, _) =
1662			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![]);
1663		let parachains = vec![(ParaId(2), Default::default())];
1664		run_test(|| {
1665			initialize(state_root);
1666			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1667				RuntimeOrigin::signed(1),
1668				(0, test_relay_header(0, state_root).hash()),
1669				parachains,
1670				proof,
1671			));
1672			assert_eq!(
1673				System::<TestRuntime>::events(),
1674				vec![EventRecord {
1675					phase: Phase::Initialization,
1676					event: TestEvent::Parachains(Event::MissingParachainHead {
1677						parachain: ParaId(2),
1678					}),
1679					topics: vec![],
1680				}],
1681			);
1682		});
1683	}
1684
1685	#[test]
1686	fn ignores_parachain_head_if_parachain_head_hash_is_wrong() {
1687		let (state_root, proof, _) =
1688			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1689		let parachains = vec![(ParaId(1), head_data(1, 10).hash())];
1690		run_test(|| {
1691			initialize(state_root);
1692			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1693				RuntimeOrigin::signed(1),
1694				(0, test_relay_header(0, state_root).hash()),
1695				parachains,
1696				proof,
1697			));
1698			assert_eq!(
1699				System::<TestRuntime>::events(),
1700				vec![EventRecord {
1701					phase: Phase::Initialization,
1702					event: TestEvent::Parachains(Event::IncorrectParachainHeadHash {
1703						parachain: ParaId(1),
1704						parachain_head_hash: head_data(1, 10).hash(),
1705						actual_parachain_head_hash: head_data(1, 0).hash(),
1706					}),
1707					topics: vec![],
1708				}],
1709			);
1710		});
1711	}
1712
1713	#[test]
1714	fn test_bridge_parachain_call_is_correctly_defined() {
1715		let (state_root, proof, _) =
1716			prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1717		let parachains = vec![(ParaId(2), Default::default())];
1718		let relay_header_id = (0, test_relay_header(0, state_root).hash());
1719
1720		let direct_submit_parachain_heads_call = Call::<TestRuntime>::submit_parachain_heads {
1721			at_relay_block: relay_header_id,
1722			parachains: parachains.clone(),
1723			parachain_heads_proof: proof.clone(),
1724		};
1725		let indirect_submit_parachain_heads_call = BridgeParachainCall::submit_parachain_heads {
1726			at_relay_block: relay_header_id,
1727			parachains,
1728			parachain_heads_proof: proof,
1729		};
1730		assert_eq!(
1731			direct_submit_parachain_heads_call.encode(),
1732			indirect_submit_parachain_heads_call.encode()
1733		);
1734	}
1735
1736	generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
1737
1738	#[test]
1739	fn maybe_max_parachains_returns_correct_value() {
1740		assert_eq!(MaybeMaxParachains::<TestRuntime, ()>::get(), Some(mock::TOTAL_PARACHAINS));
1741	}
1742
1743	#[test]
1744	fn maybe_max_total_parachain_hashes_returns_correct_value() {
1745		assert_eq!(
1746			MaybeMaxTotalParachainHashes::<TestRuntime, ()>::get(),
1747			Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()),
1748		);
1749	}
1750
1751	#[test]
1752	fn submit_finality_proof_requires_signed_origin() {
1753		run_test(|| {
1754			let (state_root, proof, parachains) =
1755				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(1, head_data(1, 0))]);
1756
1757			initialize(state_root);
1758
1759			// `submit_parachain_heads()` should fail when the pallet is halted.
1760			assert_noop!(
1761				Pallet::<TestRuntime>::submit_parachain_heads(
1762					RuntimeOrigin::root(),
1763					(0, test_relay_header(0, state_root).hash()),
1764					parachains,
1765					proof,
1766				),
1767				DispatchError::BadOrigin
1768			);
1769		})
1770	}
1771
1772	#[test]
1773	fn may_be_free_for_submitting_filtered_heads() {
1774		run_test(|| {
1775			let (state_root, proof, parachains) =
1776				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 5))]);
1777			// start with relay block #0 and import head#5 of parachain#2
1778			initialize(state_root);
1779			// first submission is free
1780			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1781				RuntimeOrigin::signed(1),
1782				(0, test_relay_header(0, state_root).hash()),
1783				parachains.clone(),
1784				proof.clone(),
1785			);
1786			assert_eq!(result.unwrap().pays_fee, Pays::No);
1787			// next submission is NOT free, because we haven't updated anything
1788			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1789				RuntimeOrigin::signed(1),
1790				(0, test_relay_header(0, state_root).hash()),
1791				parachains,
1792				proof,
1793			);
1794			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1795			// then we submit new head, proved at relay block `FreeHeadersInterval - 1` => Pays::Yes
1796			let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1797				RegularParachainHeader,
1798			>(vec![(2, head_data(2, 50))]);
1799			let relay_block_number = FreeHeadersInterval::get() - 1;
1800			proceed(relay_block_number, state_root);
1801			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1802				RuntimeOrigin::signed(1),
1803				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1804				parachains,
1805				proof,
1806			);
1807			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1808			// then we submit new head, proved after `FreeHeadersInterval` => Pays::No
1809			let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1810				RegularParachainHeader,
1811			>(vec![(2, head_data(2, 100))]);
1812			let relay_block_number = relay_block_number + FreeHeadersInterval::get();
1813			proceed(relay_block_number, state_root);
1814			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1815				RuntimeOrigin::signed(1),
1816				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1817				parachains,
1818				proof,
1819			);
1820			assert_eq!(result.unwrap().pays_fee, Pays::No);
1821			// then we submit new BIG head, proved after `FreeHeadersInterval` => Pays::Yes
1822			// then we submit new head, proved after `FreeHeadersInterval` => Pays::No
1823			let mut large_head = head_data(2, 100);
1824			large_head.0.extend(&[42u8; BigParachain::MAX_HEADER_SIZE as _]);
1825			let (state_root, proof, parachains) =
1826				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, large_head)]);
1827			let relay_block_number = relay_block_number + FreeHeadersInterval::get();
1828			proceed(relay_block_number, state_root);
1829			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1830				RuntimeOrigin::signed(1),
1831				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1832				parachains,
1833				proof,
1834			);
1835			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1836		})
1837	}
1838
1839	#[test]
1840	fn grandpa_and_parachain_pallets_share_free_headers_counter() {
1841		run_test(|| {
1842			initialize(Default::default());
1843			// set free headers limit to `4`
1844			let mut free_headers_remaining = 4;
1845			pallet_bridge_grandpa::FreeHeadersRemaining::<TestRuntime, BridgesGrandpaPalletInstance>::set(
1846				Some(free_headers_remaining),
1847			);
1848			// import free GRANDPA and parachain headers
1849			let mut relay_block_number = 0;
1850			for i in 0..2 {
1851				// import free GRANDPA header
1852				let (state_root, proof, parachains) = prepare_parachain_heads_proof::<
1853					RegularParachainHeader,
1854				>(vec![(2, head_data(2, 5 + i))]);
1855				relay_block_number = relay_block_number + FreeHeadersInterval::get();
1856				proceed(relay_block_number, state_root);
1857				assert_eq!(
1858					pallet_bridge_grandpa::FreeHeadersRemaining::<
1859						TestRuntime,
1860						BridgesGrandpaPalletInstance,
1861					>::get(),
1862					Some(free_headers_remaining - 1),
1863				);
1864				free_headers_remaining = free_headers_remaining - 1;
1865				// import free parachain header
1866				assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
1867					RuntimeOrigin::signed(1),
1868					(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1869					parachains,
1870					proof,
1871				),);
1872				assert_eq!(
1873					pallet_bridge_grandpa::FreeHeadersRemaining::<
1874						TestRuntime,
1875						BridgesGrandpaPalletInstance,
1876					>::get(),
1877					Some(free_headers_remaining - 1),
1878				);
1879				free_headers_remaining = free_headers_remaining - 1;
1880			}
1881			// try to import free GRANDPA header => non-free execution
1882			let (state_root, proof, parachains) =
1883				prepare_parachain_heads_proof::<RegularParachainHeader>(vec![(2, head_data(2, 7))]);
1884			relay_block_number = relay_block_number + FreeHeadersInterval::get();
1885			let result = pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::submit_finality_proof_ex(
1886				RuntimeOrigin::signed(1),
1887				Box::new(test_relay_header(relay_block_number, state_root)),
1888				make_default_justification(&test_relay_header(relay_block_number, state_root)),
1889				TEST_GRANDPA_SET_ID,
1890				false,
1891			);
1892			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1893			// try to import free parachain header => non-free execution
1894			let result = Pallet::<TestRuntime>::submit_parachain_heads(
1895				RuntimeOrigin::signed(1),
1896				(relay_block_number, test_relay_header(relay_block_number, state_root).hash()),
1897				parachains,
1898				proof,
1899			);
1900			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1901			assert_eq!(
1902				pallet_bridge_grandpa::FreeHeadersRemaining::<
1903					TestRuntime,
1904					BridgesGrandpaPalletInstance,
1905				>::get(),
1906				Some(0),
1907			);
1908		});
1909	}
1910}