referrerpolicy=no-referrer-when-downgrade

pallet_bridge_grandpa/
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//! Substrate GRANDPA Pallet
18//!
19//! This pallet is an on-chain GRANDPA light client for Substrate based chains.
20//!
21//! This pallet achieves this by trustlessly verifying GRANDPA finality proofs on-chain. Once
22//! verified, finalized headers are stored in the pallet, thereby creating a sparse header chain.
23//! This sparse header chain can be used as a source of truth for other higher-level applications.
24//!
25//! The pallet is responsible for tracking GRANDPA validator set hand-offs. We only import headers
26//! with justifications signed by the current validator set we know of. The header is inspected for
27//! a `ScheduledChanges` digest item, which is then used to update to next validator set.
28//!
29//! Since this pallet only tracks finalized headers it does not deal with forks. Forks can only
30//! occur if the GRANDPA validator set on the bridged chain is either colluding or there is a severe
31//! bug causing resulting in an equivocation. Such events are outside the scope of this pallet.
32//! Shall the fork occur on the bridged chain governance intervention will be required to
33//! re-initialize the bridge and track the right fork.
34
35#![warn(missing_docs)]
36#![cfg_attr(not(feature = "std"), no_std)]
37
38pub use storage_types::StoredAuthoritySet;
39
40use bp_header_chain::{
41	justification::GrandpaJustification, AuthoritySet, ChainWithGrandpa, GrandpaConsensusLogReader,
42	HeaderChain, InitializationData, StoredHeaderData, StoredHeaderDataBuilder,
43	StoredHeaderGrandpaInfo,
44};
45use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule};
46use frame_support::{dispatch::PostDispatchInfo, ensure, DefaultNoBound};
47use sp_consensus_grandpa::{AuthorityList, SetId};
48use sp_runtime::{
49	traits::{Header as HeaderT, Zero},
50	SaturatedConversion,
51};
52use sp_std::{boxed::Box, prelude::*};
53
54mod call_ext;
55#[cfg(test)]
56mod mock;
57mod storage_types;
58
59/// Module, containing weights for this pallet.
60pub mod weights;
61pub mod weights_ext;
62
63#[cfg(feature = "runtime-benchmarks")]
64pub mod benchmarking;
65
66// Re-export in crate namespace for `construct_runtime!`
67pub use call_ext::*;
68pub use pallet::*;
69pub use weights::WeightInfo;
70pub use weights_ext::WeightInfoExt;
71
72/// The target that will be used when publishing logs related to this pallet.
73pub const LOG_TARGET: &str = "runtime::bridge-grandpa";
74
75/// Bridged chain from the pallet configuration.
76pub type BridgedChain<T, I> = <T as Config<I>>::BridgedChain;
77/// Block number of the bridged chain.
78pub type BridgedBlockNumber<T, I> = BlockNumberOf<<T as Config<I>>::BridgedChain>;
79/// Block hash of the bridged chain.
80pub type BridgedBlockHash<T, I> = HashOf<<T as Config<I>>::BridgedChain>;
81/// Block id of the bridged chain.
82pub type BridgedBlockId<T, I> = HeaderId<BridgedBlockHash<T, I>, BridgedBlockNumber<T, I>>;
83/// Hasher of the bridged chain.
84pub type BridgedBlockHasher<T, I> = HasherOf<<T as Config<I>>::BridgedChain>;
85/// Header of the bridged chain.
86pub type BridgedHeader<T, I> = HeaderOf<<T as Config<I>>::BridgedChain>;
87/// Header data of the bridged chain that is stored at this chain by this pallet.
88pub type BridgedStoredHeaderData<T, I> =
89	StoredHeaderData<BridgedBlockNumber<T, I>, BridgedBlockHash<T, I>>;
90
91#[frame_support::pallet]
92pub mod pallet {
93	use super::*;
94	use bp_runtime::BasicOperatingMode;
95	use frame_support::pallet_prelude::*;
96	use frame_system::pallet_prelude::*;
97
98	#[pallet::config]
99	pub trait Config<I: 'static = ()>: frame_system::Config {
100		/// The overarching event type.
101		#[allow(deprecated)]
102		type RuntimeEvent: From<Event<Self, I>>
103			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
104
105		/// The chain we are bridging to here.
106		type BridgedChain: ChainWithGrandpa;
107
108		/// Maximal number of "free" header transactions per block.
109		///
110		/// To be able to track the bridged chain, the pallet requires all headers that are
111		/// changing GRANDPA authorities set at the bridged chain (we call them mandatory).
112		/// So it is a common good deed to submit mandatory headers to the pallet.
113		///
114		/// The pallet may be configured (see `[Self::FreeHeadersInterval]`) to import some
115		/// non-mandatory headers for free as well. It also may be treated as a common good
116		/// deed, because it may help to reduce bridge fees - this cost may be deducted from
117		/// bridge fees, paid by message senders.
118		///
119		/// However, if the bridged chain gets compromised, its validators may generate as many
120		/// "free" headers as they want. And they may fill the whole block (at this chain) for
121		/// free. This constant limits number of calls that we may refund in a single block.
122		/// All calls above this limit are accepted, but are not refunded.
123		#[pallet::constant]
124		type MaxFreeHeadersPerBlock: Get<u32>;
125
126		/// The distance between bridged chain headers, that may be submitted for free. The
127		/// first free header is header number zero, the next one is header number
128		/// `FreeHeadersInterval::get()` or any of its descendant if that header has not
129		/// been submitted. In other words, interval between free headers should be at least
130		/// `FreeHeadersInterval`.
131		#[pallet::constant]
132		type FreeHeadersInterval: Get<Option<u32>>;
133
134		/// Maximal number of finalized headers to keep in the storage.
135		///
136		/// The setting is there to prevent growing the on-chain state indefinitely. Note
137		/// the setting does not relate to block numbers - we will simply keep as much items
138		/// in the storage, so it doesn't guarantee any fixed timeframe for finality headers.
139		///
140		/// Incautious change of this constant may lead to orphan entries in the runtime storage.
141		#[pallet::constant]
142		type HeadersToKeep: Get<u32>;
143
144		/// Weights gathered through benchmarking.
145		type WeightInfo: WeightInfoExt;
146	}
147
148	#[pallet::pallet]
149	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
150
151	#[pallet::hooks]
152	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
153		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
154			FreeHeadersRemaining::<T, I>::put(T::MaxFreeHeadersPerBlock::get());
155			Weight::zero()
156		}
157
158		fn on_finalize(_n: BlockNumberFor<T>) {
159			FreeHeadersRemaining::<T, I>::kill();
160		}
161	}
162
163	impl<T: Config<I>, I: 'static> OwnedBridgeModule<T> for Pallet<T, I> {
164		const LOG_TARGET: &'static str = LOG_TARGET;
165		type OwnerStorage = PalletOwner<T, I>;
166		type OperatingMode = BasicOperatingMode;
167		type OperatingModeStorage = PalletOperatingMode<T, I>;
168	}
169
170	#[pallet::call]
171	impl<T: Config<I>, I: 'static> Pallet<T, I> {
172		/// This call is deprecated and will be removed around May 2024. Use the
173		/// `submit_finality_proof_ex` instead. Semantically, this call is an equivalent of the
174		/// `submit_finality_proof_ex` call without current authority set id check.
175		#[pallet::call_index(0)]
176		#[pallet::weight(T::WeightInfo::submit_finality_proof_weight(
177			justification.commit.precommits.len().saturated_into(),
178			justification.votes_ancestries.len().saturated_into(),
179		))]
180		#[allow(deprecated)]
181		#[deprecated(
182			note = "`submit_finality_proof` will be removed in May 2024. Use `submit_finality_proof_ex` instead."
183		)]
184		pub fn submit_finality_proof(
185			origin: OriginFor<T>,
186			finality_target: Box<BridgedHeader<T, I>>,
187			justification: GrandpaJustification<BridgedHeader<T, I>>,
188		) -> DispatchResultWithPostInfo {
189			Self::submit_finality_proof_ex(
190				origin,
191				finality_target,
192				justification,
193				// the `submit_finality_proof_ex` also reads this value, but it is done from the
194				// cache, so we don't treat it as an additional db access
195				<CurrentAuthoritySet<T, I>>::get().set_id,
196				// cannot enforce free execution using this call
197				false,
198			)
199		}
200
201		/// Bootstrap the bridge pallet with an initial header and authority set from which to sync.
202		///
203		/// The initial configuration provided does not need to be the genesis header of the bridged
204		/// chain, it can be any arbitrary header. You can also provide the next scheduled set
205		/// change if it is already know.
206		///
207		/// This function is only allowed to be called from a trusted origin and writes to storage
208		/// with practically no checks in terms of the validity of the data. It is important that
209		/// you ensure that valid data is being passed in.
210		#[pallet::call_index(1)]
211		#[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))]
212		pub fn initialize(
213			origin: OriginFor<T>,
214			init_data: super::InitializationData<BridgedHeader<T, I>>,
215		) -> DispatchResultWithPostInfo {
216			Self::ensure_owner_or_root(origin)?;
217
218			let init_allowed = !<BestFinalized<T, I>>::exists();
219			ensure!(init_allowed, <Error<T, I>>::AlreadyInitialized);
220			initialize_bridge::<T, I>(init_data.clone())?;
221
222			tracing::info!(
223				target: LOG_TARGET,
224				parameters=?init_data,
225				"Pallet has been initialized"
226			);
227
228			Ok(().into())
229		}
230
231		/// Change `PalletOwner`.
232		///
233		/// May only be called either by root, or by `PalletOwner`.
234		#[pallet::call_index(2)]
235		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
236		pub fn set_owner(origin: OriginFor<T>, new_owner: Option<T::AccountId>) -> DispatchResult {
237			<Self as OwnedBridgeModule<_>>::set_owner(origin, new_owner)
238		}
239
240		/// Halt or resume all pallet operations.
241		///
242		/// May only be called either by root, or by `PalletOwner`.
243		#[pallet::call_index(3)]
244		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
245		pub fn set_operating_mode(
246			origin: OriginFor<T>,
247			operating_mode: BasicOperatingMode,
248		) -> DispatchResult {
249			<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
250		}
251
252		/// Verify a target header is finalized according to the given finality proof. The proof
253		/// is assumed to be signed by GRANDPA authorities set with `current_set_id` id.
254		///
255		/// It will use the underlying storage pallet to fetch information about the current
256		/// authorities and best finalized header in order to verify that the header is finalized.
257		///
258		/// If successful in verification, it will write the target header to the underlying storage
259		/// pallet.
260		///
261		/// The call fails if:
262		///
263		/// - the pallet is halted;
264		///
265		/// - the pallet knows better header than the `finality_target`;
266		///
267		/// - the id of best GRANDPA authority set, known to the pallet is not equal to the
268		///   `current_set_id`;
269		///
270		/// - verification is not optimized or invalid;
271		///
272		/// - header contains forced authorities set change or change with non-zero delay.
273		///
274		/// The `is_free_execution_expected` parameter is not really used inside the call. It is
275		/// used by the transaction extension, which should be registered at the runtime level. If
276		/// this parameter is `true`, the transaction will be treated as invalid, if the call won't
277		/// be executed for free. If transaction extension is not used by the runtime, this
278		/// parameter is not used at all.
279		#[pallet::call_index(4)]
280		#[pallet::weight(T::WeightInfo::submit_finality_proof_weight(
281			justification.commit.precommits.len().saturated_into(),
282			justification.votes_ancestries.len().saturated_into(),
283		))]
284		pub fn submit_finality_proof_ex(
285			origin: OriginFor<T>,
286			finality_target: Box<BridgedHeader<T, I>>,
287			justification: GrandpaJustification<BridgedHeader<T, I>>,
288			current_set_id: sp_consensus_grandpa::SetId,
289			_is_free_execution_expected: bool,
290		) -> DispatchResultWithPostInfo {
291			Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
292			ensure_signed(origin)?;
293
294			let (hash, number) = (finality_target.hash(), *finality_target.number());
295			tracing::trace!(
296				target: LOG_TARGET,
297				header=?finality_target,
298				"Going to try and finalize header"
299			);
300
301			// it checks whether the `number` is better than the current best block number
302			// and whether the `current_set_id` matches the best known set id
303			let improved_by =
304				SubmitFinalityProofHelper::<T, I>::check_obsolete(number, Some(current_set_id))?;
305
306			let authority_set = <CurrentAuthoritySet<T, I>>::get();
307			let unused_proof_size = authority_set.unused_proof_size();
308			let set_id = authority_set.set_id;
309			let authority_set: AuthoritySet = authority_set.into();
310			verify_justification::<T, I>(&justification, hash, number, authority_set)?;
311
312			let maybe_new_authority_set =
313				try_enact_authority_change::<T, I>(&finality_target, set_id)?;
314			let may_refund_call_fee = may_refund_call_fee::<T, I>(
315				&finality_target,
316				&justification,
317				current_set_id,
318				improved_by,
319			);
320			if may_refund_call_fee {
321				on_free_header_imported::<T, I>();
322			}
323			insert_header::<T, I>(*finality_target, hash);
324
325			// mandatory header is a header that changes authorities set. The pallet can't go
326			// further without importing this header. So every bridge MUST import mandatory headers.
327			//
328			// We don't want to charge extra costs for mandatory operations. So relayer is not
329			// paying fee for mandatory headers import transactions.
330			//
331			// If size/weight of the call is exceeds our estimated limits, the relayer still needs
332			// to pay for the transaction.
333			let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes };
334
335			tracing::info!(
336				target: LOG_TARGET,
337				?hash,
338				?pays_fee,
339				"Successfully imported finalized header!"
340			);
341
342			// the proof size component of the call weight assumes that there are
343			// `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen`
344			// estimation). But if their number is lower, then we may "refund" some `proof_size`,
345			// making proof smaller and leaving block space to other useful transactions
346			let pre_dispatch_weight = T::WeightInfo::submit_finality_proof(
347				justification.commit.precommits.len().saturated_into(),
348				justification.votes_ancestries.len().saturated_into(),
349			);
350			let actual_weight = pre_dispatch_weight
351				.set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size));
352
353			Self::deposit_event(Event::UpdatedBestFinalizedHeader {
354				number,
355				hash,
356				grandpa_info: StoredHeaderGrandpaInfo {
357					finality_proof: justification,
358					new_verification_context: maybe_new_authority_set,
359				},
360			});
361
362			Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee })
363		}
364
365		/// Set current authorities set and best finalized bridged header to given values
366		/// (almost) without any checks. This call can fail only if:
367		///
368		/// - the call origin is not a root or a pallet owner;
369		///
370		/// - there are too many authorities in the new set.
371		///
372		/// No other checks are made. Previously imported headers stay in the storage and
373		/// are still accessible after the call.
374		#[pallet::call_index(5)]
375		#[pallet::weight(T::WeightInfo::force_set_pallet_state())]
376		pub fn force_set_pallet_state(
377			origin: OriginFor<T>,
378			new_current_set_id: SetId,
379			new_authorities: AuthorityList,
380			new_best_header: Box<BridgedHeader<T, I>>,
381		) -> DispatchResult {
382			Self::ensure_owner_or_root(origin)?;
383
384			// save new authorities set. It only fails if there are too many authorities
385			// in the new set
386			save_authorities_set::<T, I>(
387				CurrentAuthoritySet::<T, I>::get().set_id,
388				new_current_set_id,
389				new_authorities,
390			)?;
391
392			// save new best header. It may be older than the best header that is already
393			// known to the pallet - it changes nothing (except for the fact that previously
394			// imported headers may still be used to prove something)
395			let new_best_header_hash = new_best_header.hash();
396			insert_header::<T, I>(*new_best_header, new_best_header_hash);
397
398			Ok(())
399		}
400	}
401
402	/// Number of free header submissions that we may yet accept in the current block.
403	///
404	/// If the `FreeHeadersRemaining` hits zero, all following mandatory headers in the
405	/// current block are accepted with fee (`Pays::Yes` is returned).
406	///
407	/// The `FreeHeadersRemaining` is an ephemeral value that is set to
408	/// `MaxFreeHeadersPerBlock` at each block initialization and is killed on block
409	/// finalization. So it never ends up in the storage trie.
410	#[pallet::storage]
411	#[pallet::whitelist_storage]
412	pub type FreeHeadersRemaining<T: Config<I>, I: 'static = ()> =
413		StorageValue<_, u32, OptionQuery>;
414
415	/// Hash of the header used to bootstrap the pallet.
416	#[pallet::storage]
417	pub(super) type InitialHash<T: Config<I>, I: 'static = ()> =
418		StorageValue<_, BridgedBlockHash<T, I>, ValueQuery>;
419
420	/// Hash of the best finalized header.
421	#[pallet::storage]
422	pub type BestFinalized<T: Config<I>, I: 'static = ()> =
423		StorageValue<_, BridgedBlockId<T, I>, OptionQuery>;
424
425	/// A ring buffer of imported hashes. Ordered by the insertion time.
426	#[pallet::storage]
427	pub(super) type ImportedHashes<T: Config<I>, I: 'static = ()> = StorageMap<
428		Hasher = Identity,
429		Key = u32,
430		Value = BridgedBlockHash<T, I>,
431		QueryKind = OptionQuery,
432		OnEmpty = GetDefault,
433		MaxValues = MaybeHeadersToKeep<T, I>,
434	>;
435
436	/// Current ring buffer position.
437	#[pallet::storage]
438	pub(super) type ImportedHashesPointer<T: Config<I>, I: 'static = ()> =
439		StorageValue<_, u32, ValueQuery>;
440
441	/// Relevant fields of imported headers.
442	#[pallet::storage]
443	pub type ImportedHeaders<T: Config<I>, I: 'static = ()> = StorageMap<
444		Hasher = Identity,
445		Key = BridgedBlockHash<T, I>,
446		Value = BridgedStoredHeaderData<T, I>,
447		QueryKind = OptionQuery,
448		OnEmpty = GetDefault,
449		MaxValues = MaybeHeadersToKeep<T, I>,
450	>;
451
452	/// The current GRANDPA Authority set.
453	#[pallet::storage]
454	pub type CurrentAuthoritySet<T: Config<I>, I: 'static = ()> =
455		StorageValue<_, StoredAuthoritySet<T, I>, ValueQuery>;
456
457	/// Optional pallet owner.
458	///
459	/// Pallet owner has a right to halt all pallet operations and then resume it. If it is
460	/// `None`, then there are no direct ways to halt/resume pallet operations, but other
461	/// runtime methods may still be used to do that (i.e. democracy::referendum to update halt
462	/// flag directly or call the `set_operating_mode`).
463	#[pallet::storage]
464	pub type PalletOwner<T: Config<I>, I: 'static = ()> =
465		StorageValue<_, T::AccountId, OptionQuery>;
466
467	/// The current operating mode of the pallet.
468	///
469	/// Depending on the mode either all, or no transactions will be allowed.
470	#[pallet::storage]
471	pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
472		StorageValue<_, BasicOperatingMode, ValueQuery>;
473
474	#[pallet::genesis_config]
475	#[derive(DefaultNoBound)]
476	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
477		/// Optional module owner account.
478		pub owner: Option<T::AccountId>,
479		/// Optional module initialization data.
480		pub init_data: Option<super::InitializationData<BridgedHeader<T, I>>>,
481	}
482
483	#[pallet::genesis_build]
484	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
485		fn build(&self) {
486			if let Some(ref owner) = self.owner {
487				<PalletOwner<T, I>>::put(owner);
488			}
489
490			if let Some(init_data) = self.init_data.clone() {
491				initialize_bridge::<T, I>(init_data).expect("genesis config is correct; qed");
492			} else {
493				// Since the bridge hasn't been initialized we shouldn't allow anyone to perform
494				// transactions.
495				<PalletOperatingMode<T, I>>::put(BasicOperatingMode::Halted);
496			}
497		}
498	}
499
500	#[pallet::event]
501	#[pallet::generate_deposit(pub(super) fn deposit_event)]
502	pub enum Event<T: Config<I>, I: 'static = ()> {
503		/// Best finalized chain header has been updated to the header with given number and hash.
504		UpdatedBestFinalizedHeader {
505			/// Number of the new best finalized header.
506			number: BridgedBlockNumber<T, I>,
507			/// Hash of the new best finalized header.
508			hash: BridgedBlockHash<T, I>,
509			/// The Grandpa info associated to the new best finalized header.
510			grandpa_info: StoredHeaderGrandpaInfo<BridgedHeader<T, I>>,
511		},
512	}
513
514	#[pallet::error]
515	pub enum Error<T, I = ()> {
516		/// The given justification is invalid for the given header.
517		InvalidJustification,
518		/// The authority set from the underlying header chain is invalid.
519		InvalidAuthoritySet,
520		/// The header being imported is older than the best finalized header known to the pallet.
521		OldHeader,
522		/// The scheduled authority set change found in the header is unsupported by the pallet.
523		///
524		/// This is the case for non-standard (e.g forced) authority set changes.
525		UnsupportedScheduledChange,
526		/// The pallet is not yet initialized.
527		NotInitialized,
528		/// The pallet has already been initialized.
529		AlreadyInitialized,
530		/// Too many authorities in the set.
531		TooManyAuthoritiesInSet,
532		/// Error generated by the `OwnedBridgeModule` trait.
533		BridgeModule(bp_runtime::OwnedBridgeModuleError),
534		/// The `current_set_id` argument of the `submit_finality_proof_ex` doesn't match
535		/// the id of the current set, known to the pallet.
536		InvalidAuthoritySetId,
537		/// The submitter wanted free execution, but we can't fit more free transactions
538		/// to the block.
539		FreeHeadersLimitExceded,
540		/// The submitter wanted free execution, but the difference between best known and
541		/// bundled header numbers is below the `FreeHeadersInterval`.
542		BelowFreeHeaderInterval,
543		/// The header (and its finality) submission overflows hardcoded chain limits: size
544		/// and/or weight are larger than expected.
545		HeaderOverflowLimits,
546	}
547
548	/// Called when new free header is imported.
549	pub fn on_free_header_imported<T: Config<I>, I: 'static>() {
550		FreeHeadersRemaining::<T, I>::mutate(|count| {
551			*count = match *count {
552				None => None,
553				// the signed extension expects that `None` means outside of block
554				// execution - i.e. when transaction is validated from the transaction pool,
555				// so use `saturating_sub` and don't go from `Some(0)`->`None`
556				Some(count) => Some(count.saturating_sub(1)),
557			}
558		});
559	}
560
561	/// Return true if we may refund transaction cost to the submitter. In other words,
562	/// this transaction is considered as common good deed w.r.t to pallet configuration.
563	fn may_refund_call_fee<T: Config<I>, I: 'static>(
564		finality_target: &BridgedHeader<T, I>,
565		justification: &GrandpaJustification<BridgedHeader<T, I>>,
566		current_set_id: SetId,
567		improved_by: BridgedBlockNumber<T, I>,
568	) -> bool {
569		// if we have refunded too much at this block => not refunding
570		if FreeHeadersRemaining::<T, I>::get().unwrap_or(0) == 0 {
571			return false;
572		}
573
574		// if size/weight of call is larger than expected => not refunding
575		let call_info = submit_finality_proof_info_from_args::<T, I>(
576			&finality_target,
577			&justification,
578			Some(current_set_id),
579			// this function is called from the transaction body and we do not want
580			// to do MAY-be-free-executed checks here - they had to be done in the
581			// transaction extension before
582			false,
583		);
584		if !call_info.fits_limits() {
585			return false;
586		}
587
588		// if that's a mandatory header => refund
589		if call_info.is_mandatory {
590			return true;
591		}
592
593		// if configuration allows free non-mandatory headers and the header
594		// matches criteria => refund
595		if let Some(free_headers_interval) = T::FreeHeadersInterval::get() {
596			if improved_by >= free_headers_interval.into() {
597				return true;
598			}
599		}
600
601		false
602	}
603
604	/// Check the given header for a GRANDPA scheduled authority set change. If a change
605	/// is found it will be enacted immediately.
606	///
607	/// This function does not support forced changes, or scheduled changes with delays
608	/// since these types of changes are indicative of abnormal behavior from GRANDPA.
609	///
610	/// Returned value will indicate if a change was enacted or not.
611	pub(crate) fn try_enact_authority_change<T: Config<I>, I: 'static>(
612		header: &BridgedHeader<T, I>,
613		current_set_id: sp_consensus_grandpa::SetId,
614	) -> Result<Option<AuthoritySet>, DispatchError> {
615		// We don't support forced changes - at that point governance intervention is required.
616		ensure!(
617			GrandpaConsensusLogReader::<BridgedBlockNumber<T, I>>::find_forced_change(
618				header.digest()
619			)
620			.is_none(),
621			<Error<T, I>>::UnsupportedScheduledChange
622		);
623
624		if let Some(change) =
625			GrandpaConsensusLogReader::<BridgedBlockNumber<T, I>>::find_scheduled_change(
626				header.digest(),
627			) {
628			// GRANDPA only includes a `delay` for forced changes, so this isn't valid.
629			ensure!(change.delay == Zero::zero(), <Error<T, I>>::UnsupportedScheduledChange);
630
631			// Since our header schedules a change and we know the delay is 0, it must also enact
632			// the change.
633			// TODO [#788]: Stop manually increasing the `set_id` here.
634			return save_authorities_set::<T, I>(
635				current_set_id,
636				current_set_id + 1,
637				change.next_authorities,
638			);
639		};
640
641		Ok(None)
642	}
643
644	/// Save new authorities set.
645	pub(crate) fn save_authorities_set<T: Config<I>, I: 'static>(
646		old_current_set_id: SetId,
647		new_current_set_id: SetId,
648		new_authorities: AuthorityList,
649	) -> Result<Option<AuthoritySet>, DispatchError> {
650		let next_authorities = StoredAuthoritySet::<T, I> {
651			authorities: new_authorities
652				.try_into()
653				.map_err(|_| Error::<T, I>::TooManyAuthoritiesInSet)?,
654			set_id: new_current_set_id,
655		};
656
657		<CurrentAuthoritySet<T, I>>::put(&next_authorities);
658
659		tracing::info!(
660			target: LOG_TARGET,
661			%old_current_set_id,
662			%new_current_set_id,
663			?next_authorities,
664			"Transitioned from authority set!"
665		);
666
667		Ok(Some(next_authorities.into()))
668	}
669
670	/// Verify a GRANDPA justification (finality proof) for a given header.
671	///
672	/// Will use the GRANDPA current authorities known to the pallet.
673	///
674	/// If successful it returns the decoded GRANDPA justification so we can refund any weight which
675	/// was overcharged in the initial call.
676	pub(crate) fn verify_justification<T: Config<I>, I: 'static>(
677		justification: &GrandpaJustification<BridgedHeader<T, I>>,
678		hash: BridgedBlockHash<T, I>,
679		number: BridgedBlockNumber<T, I>,
680		authority_set: bp_header_chain::AuthoritySet,
681	) -> Result<(), sp_runtime::DispatchError> {
682		use bp_header_chain::justification::verify_justification;
683
684		Ok(verify_justification::<BridgedHeader<T, I>>(
685			(hash, number),
686			&authority_set.try_into().map_err(|_| <Error<T, I>>::InvalidAuthoritySet)?,
687			justification,
688		)
689		.map_err(|e| {
690			tracing::error!(
691				target: LOG_TARGET,
692				error=?e,
693				?hash,
694				"Received invalid justification"
695			);
696			<Error<T, I>>::InvalidJustification
697		})?)
698	}
699
700	/// Import a previously verified header to the storage.
701	///
702	/// Note this function solely takes care of updating the storage and pruning old entries,
703	/// but does not verify the validity of such import.
704	pub(crate) fn insert_header<T: Config<I>, I: 'static>(
705		header: BridgedHeader<T, I>,
706		hash: BridgedBlockHash<T, I>,
707	) {
708		let index = <ImportedHashesPointer<T, I>>::get();
709		let pruning = <ImportedHashes<T, I>>::try_get(index);
710		<BestFinalized<T, I>>::put(HeaderId(*header.number(), hash));
711		<ImportedHeaders<T, I>>::insert(hash, header.build());
712		<ImportedHashes<T, I>>::insert(index, hash);
713
714		// Update ring buffer pointer and remove old header.
715		<ImportedHashesPointer<T, I>>::put((index + 1) % T::HeadersToKeep::get());
716		if let Ok(hash) = pruning {
717			tracing::debug!(target: LOG_TARGET, ?hash, "Pruning old header.");
718			<ImportedHeaders<T, I>>::remove(hash);
719		}
720	}
721
722	/// Since this writes to storage with no real checks this should only be used in functions that
723	/// were called by a trusted origin.
724	pub(crate) fn initialize_bridge<T: Config<I>, I: 'static>(
725		init_params: super::InitializationData<BridgedHeader<T, I>>,
726	) -> Result<(), Error<T, I>> {
727		let super::InitializationData { header, authority_list, set_id, operating_mode } =
728			init_params;
729		let authority_set_length = authority_list.len();
730		let authority_set = StoredAuthoritySet::<T, I>::try_new(authority_list, set_id)
731			.inspect_err(|_| {
732				tracing::error!(
733					target: LOG_TARGET,
734					%authority_set_length,
735					max_count=%T::BridgedChain::MAX_AUTHORITIES_COUNT,
736					"Failed to initialize bridge. Number of authorities in the set is larger than the configured value"
737				);
738			})?;
739		let initial_hash = header.hash();
740
741		<InitialHash<T, I>>::put(initial_hash);
742		<ImportedHashesPointer<T, I>>::put(0);
743		insert_header::<T, I>(*header, initial_hash);
744
745		<CurrentAuthoritySet<T, I>>::put(authority_set);
746
747		<PalletOperatingMode<T, I>>::put(operating_mode);
748
749		Ok(())
750	}
751
752	/// Adapter for using `Config::HeadersToKeep` as `MaxValues` bound in our storage maps.
753	pub struct MaybeHeadersToKeep<T, I>(PhantomData<(T, I)>);
754
755	// this implementation is required to use the struct as `MaxValues`
756	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeHeadersToKeep<T, I> {
757		fn get() -> Option<u32> {
758			Some(T::HeadersToKeep::get())
759		}
760	}
761
762	/// Initialize pallet so that it is ready for inserting new header.
763	///
764	/// The function makes sure that the new insertion will cause the pruning of some old header.
765	///
766	/// Returns parent header for the new header.
767	#[cfg(feature = "runtime-benchmarks")]
768	pub(crate) fn bootstrap_bridge<T: Config<I>, I: 'static>(
769		init_params: super::InitializationData<BridgedHeader<T, I>>,
770	) -> BridgedHeader<T, I> {
771		let start_header = init_params.header.clone();
772		initialize_bridge::<T, I>(init_params).expect("benchmarks are correct");
773
774		// the most obvious way to cause pruning during next insertion would be to insert
775		// `HeadersToKeep` headers. But it'll make our benchmarks slow. So we will just play with
776		// our pruning ring-buffer.
777		assert_eq!(ImportedHashesPointer::<T, I>::get(), 1);
778		ImportedHashesPointer::<T, I>::put(0);
779
780		*start_header
781	}
782}
783
784impl<T: Config<I>, I: 'static> Pallet<T, I>
785where
786	<T as frame_system::Config>::RuntimeEvent: TryInto<Event<T, I>>,
787{
788	/// Get the GRANDPA justifications accepted in the current block.
789	pub fn synced_headers_grandpa_info() -> Vec<StoredHeaderGrandpaInfo<BridgedHeader<T, I>>> {
790		frame_system::Pallet::<T>::read_events_no_consensus()
791			.filter_map(|event| {
792				let Event::<T, I>::UpdatedBestFinalizedHeader { grandpa_info, .. } =
793					event.event.try_into().ok()?;
794				Some(grandpa_info)
795			})
796			.collect()
797	}
798}
799
800/// Bridge GRANDPA pallet as header chain.
801pub type GrandpaChainHeaders<T, I> = Pallet<T, I>;
802
803impl<T: Config<I>, I: 'static> HeaderChain<BridgedChain<T, I>> for GrandpaChainHeaders<T, I> {
804	fn finalized_header_state_root(
805		header_hash: HashOf<BridgedChain<T, I>>,
806	) -> Option<HashOf<BridgedChain<T, I>>> {
807		ImportedHeaders::<T, I>::get(header_hash).map(|h| h.state_root)
808	}
809}
810
811/// (Re)initialize bridge with given header for using it in `pallet-bridge-messages` benchmarks.
812#[cfg(feature = "runtime-benchmarks")]
813pub fn initialize_for_benchmarks<T: Config<I>, I: 'static>(header: BridgedHeader<T, I>) {
814	initialize_bridge::<T, I>(InitializationData {
815		header: Box::new(header),
816		authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external
817		                                          * benchmarks */
818		set_id: 0,
819		operating_mode: bp_runtime::BasicOperatingMode::Normal,
820	})
821	.expect("only used from benchmarks; benchmarks are correct; qed");
822}
823
824impl<T: Config<I>, I: 'static> Pallet<T, I> {
825	/// Returns the hash of the best finalized header.
826	pub fn best_finalized() -> Option<BridgedBlockId<T, I>> {
827		BestFinalized::<T, I>::get()
828	}
829}
830
831#[cfg(test)]
832mod tests {
833	use super::*;
834	use crate::mock::{
835		run_test, test_header, FreeHeadersInterval, RuntimeEvent as TestEvent, RuntimeOrigin,
836		System, TestBridgedChain, TestHeader, TestNumber, TestRuntime, MAX_BRIDGED_AUTHORITIES,
837	};
838	use bp_header_chain::BridgeGrandpaCall;
839	use bp_runtime::BasicOperatingMode;
840	use bp_test_utils::{
841		authority_list, generate_owned_bridge_module_tests, make_default_justification,
842		make_justification_for_header, JustificationGeneratorParams, ALICE, BOB,
843		TEST_GRANDPA_SET_ID,
844	};
845	use codec::Encode;
846	use frame_support::{
847		assert_err, assert_noop, assert_ok,
848		dispatch::{Pays, PostDispatchInfo},
849		storage::generator::StorageValue,
850	};
851	use frame_system::{EventRecord, Phase};
852	use sp_consensus_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID};
853	use sp_core::Get;
854	use sp_runtime::{Digest, DigestItem, DispatchError};
855
856	fn initialize_substrate_bridge() {
857		System::set_block_number(1);
858		System::reset_events();
859
860		assert_ok!(init_with_origin(RuntimeOrigin::root()));
861	}
862
863	fn init_with_origin(
864		origin: RuntimeOrigin,
865	) -> Result<
866		InitializationData<TestHeader>,
867		sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>,
868	> {
869		let genesis = test_header(0);
870
871		let init_data = InitializationData {
872			header: Box::new(genesis),
873			authority_list: authority_list(),
874			set_id: TEST_GRANDPA_SET_ID,
875			operating_mode: BasicOperatingMode::Normal,
876		};
877
878		Pallet::<TestRuntime>::initialize(origin, init_data.clone()).map(|_| init_data)
879	}
880
881	fn submit_finality_proof(header: u8) -> frame_support::dispatch::DispatchResultWithPostInfo {
882		let header = test_header(header.into());
883		let justification = make_default_justification(&header);
884
885		Pallet::<TestRuntime>::submit_finality_proof_ex(
886			RuntimeOrigin::signed(1),
887			Box::new(header),
888			justification,
889			TEST_GRANDPA_SET_ID,
890			false,
891		)
892	}
893
894	fn submit_finality_proof_with_set_id(
895		header: u8,
896		set_id: u64,
897	) -> frame_support::dispatch::DispatchResultWithPostInfo {
898		let header = test_header(header.into());
899		let justification = make_justification_for_header(JustificationGeneratorParams {
900			header: header.clone(),
901			set_id,
902			..Default::default()
903		});
904
905		Pallet::<TestRuntime>::submit_finality_proof_ex(
906			RuntimeOrigin::signed(1),
907			Box::new(header),
908			justification,
909			set_id,
910			false,
911		)
912	}
913
914	fn submit_mandatory_finality_proof(
915		number: u8,
916		set_id: u64,
917	) -> frame_support::dispatch::DispatchResultWithPostInfo {
918		let mut header = test_header(number.into());
919		// to ease tests that are using `submit_mandatory_finality_proof`, we'll be using the
920		// same set for all sessions
921		let consensus_log =
922			ConsensusLog::<TestNumber>::ScheduledChange(sp_consensus_grandpa::ScheduledChange {
923				next_authorities: authority_list(),
924				delay: 0,
925			});
926		header.digest =
927			Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] };
928		let justification = make_justification_for_header(JustificationGeneratorParams {
929			header: header.clone(),
930			set_id,
931			..Default::default()
932		});
933
934		Pallet::<TestRuntime>::submit_finality_proof_ex(
935			RuntimeOrigin::signed(1),
936			Box::new(header),
937			justification,
938			set_id,
939			false,
940		)
941	}
942
943	fn next_block() {
944		use frame_support::traits::OnInitialize;
945
946		let current_number = frame_system::Pallet::<TestRuntime>::block_number();
947		frame_system::Pallet::<TestRuntime>::set_block_number(current_number + 1);
948		let _ = Pallet::<TestRuntime>::on_initialize(current_number);
949	}
950
951	fn change_log(delay: u64) -> Digest {
952		let consensus_log =
953			ConsensusLog::<TestNumber>::ScheduledChange(sp_consensus_grandpa::ScheduledChange {
954				next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)],
955				delay,
956			});
957
958		Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }
959	}
960
961	fn forced_change_log(delay: u64) -> Digest {
962		let consensus_log = ConsensusLog::<TestNumber>::ForcedChange(
963			delay,
964			sp_consensus_grandpa::ScheduledChange {
965				next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)],
966				delay,
967			},
968		);
969
970		Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }
971	}
972
973	fn many_authorities_log() -> Digest {
974		let consensus_log =
975			ConsensusLog::<TestNumber>::ScheduledChange(sp_consensus_grandpa::ScheduledChange {
976				next_authorities: std::iter::repeat((ALICE.into(), 1))
977					.take(MAX_BRIDGED_AUTHORITIES as usize + 1)
978					.collect(),
979				delay: 0,
980			});
981
982		Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }
983	}
984
985	#[test]
986	fn init_root_or_owner_origin_can_initialize_pallet() {
987		run_test(|| {
988			assert_noop!(init_with_origin(RuntimeOrigin::signed(1)), DispatchError::BadOrigin);
989			assert_ok!(init_with_origin(RuntimeOrigin::root()));
990
991			// Reset storage so we can initialize the pallet again
992			BestFinalized::<TestRuntime>::kill();
993			PalletOwner::<TestRuntime>::put(2);
994			assert_ok!(init_with_origin(RuntimeOrigin::signed(2)));
995		})
996	}
997
998	#[test]
999	fn init_storage_entries_are_correctly_initialized() {
1000		run_test(|| {
1001			assert_eq!(BestFinalized::<TestRuntime>::get(), None,);
1002			assert_eq!(Pallet::<TestRuntime>::best_finalized(), None);
1003			assert_eq!(PalletOperatingMode::<TestRuntime>::try_get(), Err(()));
1004
1005			let init_data = init_with_origin(RuntimeOrigin::root()).unwrap();
1006
1007			assert!(<ImportedHeaders<TestRuntime>>::contains_key(init_data.header.hash()));
1008			assert_eq!(BestFinalized::<TestRuntime>::get().unwrap().1, init_data.header.hash());
1009			assert_eq!(
1010				CurrentAuthoritySet::<TestRuntime>::get().authorities,
1011				init_data.authority_list
1012			);
1013			assert_eq!(
1014				PalletOperatingMode::<TestRuntime>::try_get(),
1015				Ok(BasicOperatingMode::Normal)
1016			);
1017		})
1018	}
1019
1020	#[test]
1021	fn init_can_only_initialize_pallet_once() {
1022		run_test(|| {
1023			initialize_substrate_bridge();
1024			assert_noop!(
1025				init_with_origin(RuntimeOrigin::root()),
1026				<Error<TestRuntime>>::AlreadyInitialized
1027			);
1028		})
1029	}
1030
1031	#[test]
1032	fn init_fails_if_there_are_too_many_authorities_in_the_set() {
1033		run_test(|| {
1034			let genesis = test_header(0);
1035			let init_data = InitializationData {
1036				header: Box::new(genesis),
1037				authority_list: std::iter::repeat(authority_list().remove(0))
1038					.take(MAX_BRIDGED_AUTHORITIES as usize + 1)
1039					.collect(),
1040				set_id: 1,
1041				operating_mode: BasicOperatingMode::Normal,
1042			};
1043
1044			assert_noop!(
1045				Pallet::<TestRuntime>::initialize(RuntimeOrigin::root(), init_data),
1046				Error::<TestRuntime>::TooManyAuthoritiesInSet,
1047			);
1048		});
1049	}
1050
1051	#[test]
1052	fn pallet_rejects_transactions_if_halted() {
1053		run_test(|| {
1054			initialize_substrate_bridge();
1055
1056			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
1057				RuntimeOrigin::root(),
1058				BasicOperatingMode::Halted
1059			));
1060			assert_noop!(
1061				submit_finality_proof(1),
1062				Error::<TestRuntime>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted)
1063			);
1064
1065			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
1066				RuntimeOrigin::root(),
1067				BasicOperatingMode::Normal
1068			));
1069			assert_ok!(submit_finality_proof(1));
1070		})
1071	}
1072
1073	#[test]
1074	fn pallet_rejects_header_if_not_initialized_yet() {
1075		run_test(|| {
1076			assert_noop!(submit_finality_proof(1), Error::<TestRuntime>::NotInitialized);
1077		});
1078	}
1079
1080	#[test]
1081	fn successfully_imports_header_with_valid_finality() {
1082		run_test(|| {
1083			initialize_substrate_bridge();
1084
1085			let header_number = 1;
1086			let header = test_header(header_number.into());
1087			let justification = make_default_justification(&header);
1088
1089			let pre_dispatch_weight = <TestRuntime as Config>::WeightInfo::submit_finality_proof(
1090				justification.commit.precommits.len().try_into().unwrap_or(u32::MAX),
1091				justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX),
1092			);
1093
1094			let result = submit_finality_proof(header_number);
1095			assert_ok!(result);
1096			assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
1097			// our test config assumes 2048 max authorities and we are just using couple
1098			let pre_dispatch_proof_size = pre_dispatch_weight.proof_size();
1099			let actual_proof_size = result.unwrap().actual_weight.unwrap().proof_size();
1100			assert!(actual_proof_size > 0);
1101			assert!(
1102				actual_proof_size < pre_dispatch_proof_size,
1103				"Actual proof size {actual_proof_size} must be less than the pre-dispatch {pre_dispatch_proof_size}",
1104			);
1105
1106			let header = test_header(1);
1107			assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
1108			assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
1109
1110			assert_eq!(
1111				System::events(),
1112				vec![EventRecord {
1113					phase: Phase::Initialization,
1114					event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader {
1115						number: *header.number(),
1116						hash: header.hash(),
1117						grandpa_info: StoredHeaderGrandpaInfo {
1118							finality_proof: justification.clone(),
1119							new_verification_context: None,
1120						},
1121					}),
1122					topics: vec![],
1123				}],
1124			);
1125			assert_eq!(
1126				Pallet::<TestRuntime>::synced_headers_grandpa_info(),
1127				vec![StoredHeaderGrandpaInfo {
1128					finality_proof: justification,
1129					new_verification_context: None
1130				}]
1131			);
1132		})
1133	}
1134
1135	#[test]
1136	fn rejects_justification_that_skips_authority_set_transition() {
1137		run_test(|| {
1138			initialize_substrate_bridge();
1139
1140			let header = test_header(1);
1141
1142			let next_set_id = 2;
1143			let params = JustificationGeneratorParams::<TestHeader> {
1144				set_id: next_set_id,
1145				..Default::default()
1146			};
1147			let justification = make_justification_for_header(params);
1148
1149			assert_err!(
1150				Pallet::<TestRuntime>::submit_finality_proof_ex(
1151					RuntimeOrigin::signed(1),
1152					Box::new(header.clone()),
1153					justification.clone(),
1154					TEST_GRANDPA_SET_ID,
1155					false,
1156				),
1157				<Error<TestRuntime>>::InvalidJustification
1158			);
1159			assert_err!(
1160				Pallet::<TestRuntime>::submit_finality_proof_ex(
1161					RuntimeOrigin::signed(1),
1162					Box::new(header),
1163					justification,
1164					next_set_id,
1165					false,
1166				),
1167				<Error<TestRuntime>>::InvalidAuthoritySetId
1168			);
1169		})
1170	}
1171
1172	#[test]
1173	fn does_not_import_header_with_invalid_finality_proof() {
1174		run_test(|| {
1175			initialize_substrate_bridge();
1176
1177			let header = test_header(1);
1178			let mut justification = make_default_justification(&header);
1179			justification.round = 42;
1180
1181			assert_err!(
1182				Pallet::<TestRuntime>::submit_finality_proof_ex(
1183					RuntimeOrigin::signed(1),
1184					Box::new(header),
1185					justification,
1186					TEST_GRANDPA_SET_ID,
1187					false,
1188				),
1189				<Error<TestRuntime>>::InvalidJustification
1190			);
1191		})
1192	}
1193
1194	#[test]
1195	fn disallows_invalid_authority_set() {
1196		run_test(|| {
1197			let genesis = test_header(0);
1198
1199			let invalid_authority_list = vec![(ALICE.into(), u64::MAX), (BOB.into(), u64::MAX)];
1200			let init_data = InitializationData {
1201				header: Box::new(genesis),
1202				authority_list: invalid_authority_list,
1203				set_id: 1,
1204				operating_mode: BasicOperatingMode::Normal,
1205			};
1206
1207			assert_ok!(Pallet::<TestRuntime>::initialize(RuntimeOrigin::root(), init_data));
1208
1209			let header = test_header(1);
1210			let justification = make_default_justification(&header);
1211
1212			assert_err!(
1213				Pallet::<TestRuntime>::submit_finality_proof_ex(
1214					RuntimeOrigin::signed(1),
1215					Box::new(header),
1216					justification,
1217					TEST_GRANDPA_SET_ID,
1218					false,
1219				),
1220				<Error<TestRuntime>>::InvalidAuthoritySet
1221			);
1222		})
1223	}
1224
1225	#[test]
1226	fn importing_header_ensures_that_chain_is_extended() {
1227		run_test(|| {
1228			initialize_substrate_bridge();
1229
1230			assert_ok!(submit_finality_proof(4));
1231			assert_err!(submit_finality_proof(3), Error::<TestRuntime>::OldHeader);
1232			assert_ok!(submit_finality_proof(5));
1233		})
1234	}
1235
1236	#[test]
1237	fn importing_header_enacts_new_authority_set() {
1238		run_test(|| {
1239			initialize_substrate_bridge();
1240
1241			let next_set_id = 2;
1242			let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)];
1243
1244			// Need to update the header digest to indicate that our header signals an authority set
1245			// change. The change will be enacted when we import our header.
1246			let mut header = test_header(2);
1247			header.digest = change_log(0);
1248
1249			// Create a valid justification for the header
1250			let justification = make_default_justification(&header);
1251
1252			// Let's import our test header
1253			let result = Pallet::<TestRuntime>::submit_finality_proof_ex(
1254				RuntimeOrigin::signed(1),
1255				Box::new(header.clone()),
1256				justification.clone(),
1257				TEST_GRANDPA_SET_ID,
1258				false,
1259			);
1260			assert_ok!(result);
1261			assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No);
1262
1263			// Make sure that our header is the best finalized
1264			assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
1265			assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
1266
1267			// Make sure that the authority set actually changed upon importing our header
1268			assert_eq!(
1269				<CurrentAuthoritySet<TestRuntime>>::get(),
1270				StoredAuthoritySet::<TestRuntime, ()>::try_new(next_authorities, next_set_id)
1271					.unwrap(),
1272			);
1273
1274			// Here
1275			assert_eq!(
1276				System::events(),
1277				vec![EventRecord {
1278					phase: Phase::Initialization,
1279					event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader {
1280						number: *header.number(),
1281						hash: header.hash(),
1282						grandpa_info: StoredHeaderGrandpaInfo {
1283							finality_proof: justification.clone(),
1284							new_verification_context: Some(
1285								<CurrentAuthoritySet<TestRuntime>>::get().into()
1286							),
1287						},
1288					}),
1289					topics: vec![],
1290				}],
1291			);
1292			assert_eq!(
1293				Pallet::<TestRuntime>::synced_headers_grandpa_info(),
1294				vec![StoredHeaderGrandpaInfo {
1295					finality_proof: justification,
1296					new_verification_context: Some(
1297						<CurrentAuthoritySet<TestRuntime>>::get().into()
1298					),
1299				}]
1300			);
1301		})
1302	}
1303
1304	#[test]
1305	fn relayer_pays_tx_fee_when_submitting_huge_mandatory_header() {
1306		run_test(|| {
1307			initialize_substrate_bridge();
1308
1309			// let's prepare a huge authorities change header, which is definitely above size limits
1310			let mut header = test_header(2);
1311			header.digest = change_log(0);
1312			header.digest.push(DigestItem::Other(vec![42u8; 1024 * 1024]));
1313			let justification = make_default_justification(&header);
1314
1315			// without large digest item ^^^ the relayer would have paid zero transaction fee
1316			// (`Pays::No`)
1317			let result = Pallet::<TestRuntime>::submit_finality_proof_ex(
1318				RuntimeOrigin::signed(1),
1319				Box::new(header.clone()),
1320				justification,
1321				TEST_GRANDPA_SET_ID,
1322				false,
1323			);
1324			assert_ok!(result);
1325			assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
1326
1327			// Make sure that our header is the best finalized
1328			assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
1329			assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
1330		})
1331	}
1332
1333	#[test]
1334	fn relayer_pays_tx_fee_when_submitting_justification_with_long_ancestry_votes() {
1335		run_test(|| {
1336			initialize_substrate_bridge();
1337
1338			// let's prepare a huge authorities change header, which is definitely above weight
1339			// limits
1340			let mut header = test_header(2);
1341			header.digest = change_log(0);
1342			let justification = make_justification_for_header(JustificationGeneratorParams {
1343				header: header.clone(),
1344				ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1,
1345				..Default::default()
1346			});
1347
1348			// without many headers in votes ancestries ^^^ the relayer would have paid zero
1349			// transaction fee (`Pays::No`)
1350			let result = Pallet::<TestRuntime>::submit_finality_proof_ex(
1351				RuntimeOrigin::signed(1),
1352				Box::new(header.clone()),
1353				justification,
1354				TEST_GRANDPA_SET_ID,
1355				false,
1356			);
1357			assert_ok!(result);
1358			assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
1359
1360			// Make sure that our header is the best finalized
1361			assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
1362			assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
1363		})
1364	}
1365
1366	#[test]
1367	fn importing_header_rejects_header_with_scheduled_change_delay() {
1368		run_test(|| {
1369			initialize_substrate_bridge();
1370
1371			// Need to update the header digest to indicate that our header signals an authority set
1372			// change. However, the change doesn't happen until the next block.
1373			let mut header = test_header(2);
1374			header.digest = change_log(1);
1375
1376			// Create a valid justification for the header
1377			let justification = make_default_justification(&header);
1378
1379			// Should not be allowed to import this header
1380			assert_err!(
1381				Pallet::<TestRuntime>::submit_finality_proof_ex(
1382					RuntimeOrigin::signed(1),
1383					Box::new(header),
1384					justification,
1385					TEST_GRANDPA_SET_ID,
1386					false,
1387				),
1388				<Error<TestRuntime>>::UnsupportedScheduledChange
1389			);
1390		})
1391	}
1392
1393	#[test]
1394	fn importing_header_rejects_header_with_forced_changes() {
1395		run_test(|| {
1396			initialize_substrate_bridge();
1397
1398			// Need to update the header digest to indicate that it signals a forced authority set
1399			// change.
1400			let mut header = test_header(2);
1401			header.digest = forced_change_log(0);
1402
1403			// Create a valid justification for the header
1404			let justification = make_default_justification(&header);
1405
1406			// Should not be allowed to import this header
1407			assert_err!(
1408				Pallet::<TestRuntime>::submit_finality_proof_ex(
1409					RuntimeOrigin::signed(1),
1410					Box::new(header),
1411					justification,
1412					TEST_GRANDPA_SET_ID,
1413					false,
1414				),
1415				<Error<TestRuntime>>::UnsupportedScheduledChange
1416			);
1417		})
1418	}
1419
1420	#[test]
1421	fn importing_header_rejects_header_with_too_many_authorities() {
1422		run_test(|| {
1423			initialize_substrate_bridge();
1424
1425			// Need to update the header digest to indicate that our header signals an authority set
1426			// change. However, the change doesn't happen until the next block.
1427			let mut header = test_header(2);
1428			header.digest = many_authorities_log();
1429
1430			// Create a valid justification for the header
1431			let justification = make_default_justification(&header);
1432
1433			// Should not be allowed to import this header
1434			assert_err!(
1435				Pallet::<TestRuntime>::submit_finality_proof_ex(
1436					RuntimeOrigin::signed(1),
1437					Box::new(header),
1438					justification,
1439					TEST_GRANDPA_SET_ID,
1440					false,
1441				),
1442				<Error<TestRuntime>>::TooManyAuthoritiesInSet
1443			);
1444		});
1445	}
1446
1447	#[test]
1448	fn verify_storage_proof_rejects_unknown_header() {
1449		run_test(|| {
1450			assert_noop!(
1451				Pallet::<TestRuntime>::verify_storage_proof(
1452					Default::default(),
1453					Default::default(),
1454				)
1455				.map(|_| ()),
1456				bp_header_chain::HeaderChainError::UnknownHeader,
1457			);
1458		});
1459	}
1460
1461	#[test]
1462	fn parse_finalized_storage_accepts_valid_proof() {
1463		run_test(|| {
1464			let (state_root, storage_proof) = bp_runtime::craft_valid_storage_proof();
1465
1466			let mut header = test_header(2);
1467			header.set_state_root(state_root);
1468
1469			let hash = header.hash();
1470			<BestFinalized<TestRuntime>>::put(HeaderId(2, hash));
1471			<ImportedHeaders<TestRuntime>>::insert(hash, header.build());
1472
1473			assert_ok!(Pallet::<TestRuntime>::verify_storage_proof(hash, storage_proof).map(|_| ()));
1474		});
1475	}
1476
1477	#[test]
1478	fn rate_limiter_disallows_free_imports_once_limit_is_hit_in_single_block() {
1479		run_test(|| {
1480			initialize_substrate_bridge();
1481
1482			let result = submit_mandatory_finality_proof(1, 1);
1483			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1484
1485			let result = submit_mandatory_finality_proof(2, 2);
1486			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1487
1488			let result = submit_mandatory_finality_proof(3, 3);
1489			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1490		})
1491	}
1492
1493	#[test]
1494	fn rate_limiter_invalid_requests_do_not_count_towards_request_count() {
1495		run_test(|| {
1496			let submit_invalid_request = || {
1497				let mut header = test_header(1);
1498				header.digest = change_log(0);
1499				let mut invalid_justification = make_default_justification(&header);
1500				invalid_justification.round = 42;
1501
1502				Pallet::<TestRuntime>::submit_finality_proof_ex(
1503					RuntimeOrigin::signed(1),
1504					Box::new(header),
1505					invalid_justification,
1506					TEST_GRANDPA_SET_ID,
1507					false,
1508				)
1509			};
1510
1511			initialize_substrate_bridge();
1512
1513			for _ in 0..<TestRuntime as Config>::MaxFreeHeadersPerBlock::get() + 1 {
1514				assert_err!(submit_invalid_request(), <Error<TestRuntime>>::InvalidJustification);
1515			}
1516
1517			// Can still submit free mandatory headers afterwards
1518			let result = submit_mandatory_finality_proof(1, 1);
1519			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1520
1521			let result = submit_mandatory_finality_proof(2, 2);
1522			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1523
1524			let result = submit_mandatory_finality_proof(3, 3);
1525			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1526		})
1527	}
1528
1529	#[test]
1530	fn rate_limiter_allows_request_after_new_block_has_started() {
1531		run_test(|| {
1532			initialize_substrate_bridge();
1533
1534			let result = submit_mandatory_finality_proof(1, 1);
1535			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1536
1537			let result = submit_mandatory_finality_proof(2, 2);
1538			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1539
1540			let result = submit_mandatory_finality_proof(3, 3);
1541			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1542
1543			next_block();
1544
1545			let result = submit_mandatory_finality_proof(4, 4);
1546			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1547
1548			let result = submit_mandatory_finality_proof(5, 5);
1549			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1550
1551			let result = submit_mandatory_finality_proof(6, 6);
1552			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1553		})
1554	}
1555
1556	#[test]
1557	fn rate_limiter_ignores_non_mandatory_headers() {
1558		run_test(|| {
1559			initialize_substrate_bridge();
1560
1561			let result = submit_finality_proof(1);
1562			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1563
1564			let result = submit_mandatory_finality_proof(2, 1);
1565			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1566
1567			let result = submit_finality_proof_with_set_id(3, 2);
1568			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1569
1570			let result = submit_mandatory_finality_proof(4, 2);
1571			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1572
1573			let result = submit_finality_proof_with_set_id(5, 3);
1574			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1575
1576			let result = submit_mandatory_finality_proof(6, 3);
1577			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1578		})
1579	}
1580
1581	#[test]
1582	fn may_import_non_mandatory_header_for_free() {
1583		run_test(|| {
1584			initialize_substrate_bridge();
1585
1586			// set best finalized to `100`
1587			const BEST: u8 = 12;
1588			fn reset_best() {
1589				BestFinalized::<TestRuntime, ()>::set(Some(HeaderId(
1590					BEST as _,
1591					Default::default(),
1592				)));
1593			}
1594
1595			// non-mandatory header is imported with fee
1596			reset_best();
1597			let non_free_header_number = BEST + FreeHeadersInterval::get() as u8 - 1;
1598			let result = submit_finality_proof(non_free_header_number);
1599			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1600
1601			// non-mandatory free header is imported without fee
1602			reset_best();
1603			let free_header_number = BEST + FreeHeadersInterval::get() as u8;
1604			let result = submit_finality_proof(free_header_number);
1605			assert_eq!(result.unwrap().pays_fee, Pays::No);
1606
1607			// another non-mandatory free header is imported without fee
1608			let free_header_number = BEST + FreeHeadersInterval::get() as u8 * 2;
1609			let result = submit_finality_proof(free_header_number);
1610			assert_eq!(result.unwrap().pays_fee, Pays::No);
1611
1612			// now the rate limiter starts charging fees even for free headers
1613			let free_header_number = BEST + FreeHeadersInterval::get() as u8 * 3;
1614			let result = submit_finality_proof(free_header_number);
1615			assert_eq!(result.unwrap().pays_fee, Pays::Yes);
1616
1617			// check that we can import for free if `improved_by` is larger
1618			// than the free interval
1619			next_block();
1620			reset_best();
1621			let free_header_number = FreeHeadersInterval::get() as u8 + 42;
1622			let result = submit_finality_proof(free_header_number);
1623			assert_eq!(result.unwrap().pays_fee, Pays::No);
1624
1625			// check that the rate limiter shares the counter between mandatory
1626			// and free non-mandatory headers
1627			next_block();
1628			reset_best();
1629			let free_header_number = BEST + FreeHeadersInterval::get() as u8 * 4;
1630			let result = submit_finality_proof(free_header_number);
1631			assert_eq!(result.unwrap().pays_fee, Pays::No);
1632			let result = submit_mandatory_finality_proof(free_header_number + 1, 1);
1633			assert_eq!(result.expect("call failed").pays_fee, Pays::No);
1634			let result = submit_mandatory_finality_proof(free_header_number + 2, 2);
1635			assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
1636		});
1637	}
1638
1639	#[test]
1640	fn should_prune_headers_over_headers_to_keep_parameter() {
1641		run_test(|| {
1642			initialize_substrate_bridge();
1643			assert_ok!(submit_finality_proof(1));
1644			let first_header_hash = Pallet::<TestRuntime>::best_finalized().unwrap().hash();
1645			next_block();
1646
1647			assert_ok!(submit_finality_proof(2));
1648			next_block();
1649			assert_ok!(submit_finality_proof(3));
1650			next_block();
1651			assert_ok!(submit_finality_proof(4));
1652			next_block();
1653			assert_ok!(submit_finality_proof(5));
1654			next_block();
1655
1656			assert_ok!(submit_finality_proof(6));
1657
1658			assert!(
1659				!ImportedHeaders::<TestRuntime, ()>::contains_key(first_header_hash),
1660				"First header should be pruned.",
1661			);
1662		})
1663	}
1664
1665	#[test]
1666	fn storage_keys_computed_properly() {
1667		assert_eq!(
1668			PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(),
1669			bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0,
1670		);
1671
1672		assert_eq!(
1673			CurrentAuthoritySet::<TestRuntime>::storage_value_final_key().to_vec(),
1674			bp_header_chain::storage_keys::current_authority_set_key("Grandpa").0,
1675		);
1676
1677		assert_eq!(
1678			BestFinalized::<TestRuntime>::storage_value_final_key().to_vec(),
1679			bp_header_chain::storage_keys::best_finalized_key("Grandpa").0,
1680		);
1681	}
1682
1683	#[test]
1684	fn test_bridge_grandpa_call_is_correctly_defined() {
1685		let header = test_header(0);
1686		let init_data = InitializationData {
1687			header: Box::new(header.clone()),
1688			authority_list: authority_list(),
1689			set_id: 1,
1690			operating_mode: BasicOperatingMode::Normal,
1691		};
1692		let justification = make_default_justification(&header);
1693
1694		let direct_initialize_call =
1695			Call::<TestRuntime>::initialize { init_data: init_data.clone() };
1696		let indirect_initialize_call = BridgeGrandpaCall::<TestHeader>::initialize { init_data };
1697		assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode());
1698
1699		let direct_submit_finality_proof_call = Call::<TestRuntime>::submit_finality_proof {
1700			finality_target: Box::new(header.clone()),
1701			justification: justification.clone(),
1702		};
1703		let indirect_submit_finality_proof_call =
1704			BridgeGrandpaCall::<TestHeader>::submit_finality_proof {
1705				finality_target: Box::new(header),
1706				justification,
1707			};
1708		assert_eq!(
1709			direct_submit_finality_proof_call.encode(),
1710			indirect_submit_finality_proof_call.encode()
1711		);
1712	}
1713
1714	generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
1715
1716	#[test]
1717	fn maybe_headers_to_keep_returns_correct_value() {
1718		assert_eq!(MaybeHeadersToKeep::<TestRuntime, ()>::get(), Some(mock::HeadersToKeep::get()));
1719	}
1720
1721	#[test]
1722	fn submit_finality_proof_requires_signed_origin() {
1723		run_test(|| {
1724			initialize_substrate_bridge();
1725
1726			let header = test_header(1);
1727			let justification = make_default_justification(&header);
1728
1729			assert_noop!(
1730				Pallet::<TestRuntime>::submit_finality_proof_ex(
1731					RuntimeOrigin::root(),
1732					Box::new(header),
1733					justification,
1734					TEST_GRANDPA_SET_ID,
1735					false,
1736				),
1737				DispatchError::BadOrigin,
1738			);
1739		})
1740	}
1741
1742	#[test]
1743	fn on_free_header_imported_never_sets_to_none() {
1744		run_test(|| {
1745			FreeHeadersRemaining::<TestRuntime, ()>::set(Some(2));
1746			on_free_header_imported::<TestRuntime, ()>();
1747			assert_eq!(FreeHeadersRemaining::<TestRuntime, ()>::get(), Some(1));
1748			on_free_header_imported::<TestRuntime, ()>();
1749			assert_eq!(FreeHeadersRemaining::<TestRuntime, ()>::get(), Some(0));
1750			on_free_header_imported::<TestRuntime, ()>();
1751			assert_eq!(FreeHeadersRemaining::<TestRuntime, ()>::get(), Some(0));
1752		})
1753	}
1754
1755	#[test]
1756	fn force_set_pallet_state_works() {
1757		run_test(|| {
1758			let header25 = test_header(25);
1759			let header50 = test_header(50);
1760			let ok_new_set_id = 100;
1761			let ok_new_authorities = authority_list();
1762			let bad_new_set_id = 100;
1763			let bad_new_authorities: Vec<_> = std::iter::repeat((ALICE.into(), 1))
1764				.take(MAX_BRIDGED_AUTHORITIES as usize + 1)
1765				.collect();
1766
1767			// initialize and import several headers
1768			initialize_substrate_bridge();
1769			assert_ok!(submit_finality_proof(30));
1770
1771			// wrong origin => error
1772			assert_noop!(
1773				Pallet::<TestRuntime>::force_set_pallet_state(
1774					RuntimeOrigin::signed(1),
1775					ok_new_set_id,
1776					ok_new_authorities.clone(),
1777					Box::new(header50.clone()),
1778				),
1779				DispatchError::BadOrigin,
1780			);
1781
1782			// too many authorities in the set => error
1783			assert_noop!(
1784				Pallet::<TestRuntime>::force_set_pallet_state(
1785					RuntimeOrigin::root(),
1786					bad_new_set_id,
1787					bad_new_authorities.clone(),
1788					Box::new(header50.clone()),
1789				),
1790				Error::<TestRuntime>::TooManyAuthoritiesInSet,
1791			);
1792
1793			// force import header 50 => ok
1794			assert_ok!(Pallet::<TestRuntime>::force_set_pallet_state(
1795				RuntimeOrigin::root(),
1796				ok_new_set_id,
1797				ok_new_authorities.clone(),
1798				Box::new(header50.clone()),
1799			),);
1800
1801			// force import header 25 after 50 => ok
1802			assert_ok!(Pallet::<TestRuntime>::force_set_pallet_state(
1803				RuntimeOrigin::root(),
1804				ok_new_set_id,
1805				ok_new_authorities.clone(),
1806				Box::new(header25.clone()),
1807			),);
1808
1809			// we may import better headers
1810			assert_noop!(submit_finality_proof(20), Error::<TestRuntime>::OldHeader);
1811			assert_ok!(submit_finality_proof_with_set_id(26, ok_new_set_id));
1812
1813			// we can even reimport header #50. It **will cause** some issues during pruning
1814			// (see below)
1815			assert_ok!(submit_finality_proof_with_set_id(50, ok_new_set_id));
1816
1817			// and all headers are available. Even though there are 4 headers, the ring
1818			// buffer thinks that there are 5, because we've imported header $50 twice
1819			assert!(GrandpaChainHeaders::<TestRuntime, ()>::finalized_header_state_root(
1820				test_header(30).hash()
1821			)
1822			.is_some());
1823			assert!(GrandpaChainHeaders::<TestRuntime, ()>::finalized_header_state_root(
1824				test_header(50).hash()
1825			)
1826			.is_some());
1827			assert!(GrandpaChainHeaders::<TestRuntime, ()>::finalized_header_state_root(
1828				test_header(25).hash()
1829			)
1830			.is_some());
1831			assert!(GrandpaChainHeaders::<TestRuntime, ()>::finalized_header_state_root(
1832				test_header(26).hash()
1833			)
1834			.is_some());
1835
1836			// next header import will prune header 30
1837			assert_ok!(submit_finality_proof_with_set_id(70, ok_new_set_id));
1838			// next header import will prune header 50
1839			assert_ok!(submit_finality_proof_with_set_id(80, ok_new_set_id));
1840			// next header import will prune header 25
1841			assert_ok!(submit_finality_proof_with_set_id(90, ok_new_set_id));
1842			// next header import will prune header 26
1843			assert_ok!(submit_finality_proof_with_set_id(100, ok_new_set_id));
1844			// next header import will prune header 50 again. But it is fine
1845			assert_ok!(submit_finality_proof_with_set_id(110, ok_new_set_id));
1846		});
1847	}
1848}