referrerpolicy=no-referrer-when-downgrade

pallet_beefy/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19
20extern crate alloc;
21
22mod default_weights;
23mod equivocation;
24#[cfg(test)]
25mod mock;
26#[cfg(test)]
27mod tests;
28
29use alloc::{boxed::Box, vec::Vec};
30use codec::{Encode, MaxEncodedLen};
31use log;
32
33use frame_support::{
34	dispatch::{DispatchResultWithPostInfo, Pays},
35	pallet_prelude::*,
36	traits::{Get, OneSessionHandler},
37	weights::{constants::RocksDbWeight as DbWeight, Weight},
38	BoundedSlice, BoundedVec, Parameter,
39};
40use frame_system::{
41	ensure_none, ensure_signed,
42	pallet_prelude::{BlockNumberFor, HeaderFor, OriginFor},
43};
44use sp_consensus_beefy::{
45	AncestryHelper, AncestryHelperWeightInfo, AuthorityIndex, BeefyAuthorityId, ConsensusLog,
46	DoubleVotingProof, ForkVotingProof, FutureBlockVotingProof, OnNewValidatorSet, ValidatorSet,
47	BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID,
48};
49use sp_runtime::{
50	generic::DigestItem,
51	traits::{IsMember, Member, One},
52	RuntimeAppPublic,
53};
54use sp_session::{GetSessionNumber, GetValidatorCount};
55use sp_staking::{offence::OffenceReportSystem, SessionIndex};
56
57use crate::equivocation::EquivocationEvidenceFor;
58pub use crate::equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot};
59pub use pallet::*;
60
61const LOG_TARGET: &str = "runtime::beefy";
62
63#[frame_support::pallet]
64pub mod pallet {
65	use super::*;
66	use frame_system::{ensure_root, pallet_prelude::BlockNumberFor};
67
68	#[pallet::config]
69	pub trait Config: frame_system::Config {
70		/// Authority identifier type
71		type BeefyId: Member
72			+ Parameter
73			+ BeefyAuthorityId
74			+ MaybeSerializeDeserialize
75			+ MaxEncodedLen;
76
77		/// The maximum number of authorities that can be added.
78		#[pallet::constant]
79		type MaxAuthorities: Get<u32>;
80
81		/// The maximum number of nominators for each validator.
82		#[pallet::constant]
83		type MaxNominators: Get<u32>;
84
85		/// The maximum number of entries to keep in the set id to session index mapping.
86		///
87		/// Since the `SetIdSession` map is only used for validating equivocations this
88		/// value should relate to the bonding duration of whatever staking system is
89		/// being used (if any). If equivocation handling is not enabled then this value
90		/// can be zero.
91		#[pallet::constant]
92		type MaxSetIdSessionEntries: Get<u64>;
93
94		/// A hook to act on the new BEEFY validator set.
95		///
96		/// For some applications it might be beneficial to make the BEEFY validator set available
97		/// externally apart from having it in the storage. For instance you might cache a light
98		/// weight MMR root over validators and make it available for Light Clients.
99		type OnNewValidatorSet: OnNewValidatorSet<<Self as Config>::BeefyId>;
100
101		/// Hook for checking commitment canonicity.
102		type AncestryHelper: AncestryHelper<HeaderFor<Self>>
103			+ AncestryHelperWeightInfo<HeaderFor<Self>>;
104
105		/// Weights for this pallet.
106		type WeightInfo: WeightInfo;
107
108		/// The proof of key ownership, used for validating equivocation reports
109		/// The proof must include the session index and validator count of the
110		/// session at which the equivocation occurred.
111		type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount;
112
113		/// The equivocation handling subsystem.
114		///
115		/// Defines methods to publish, check and process an equivocation offence.
116		type EquivocationReportSystem: OffenceReportSystem<
117			Option<Self::AccountId>,
118			EquivocationEvidenceFor<Self>,
119		>;
120	}
121
122	#[pallet::pallet]
123	pub struct Pallet<T>(_);
124
125	/// The current authorities set
126	#[pallet::storage]
127	pub type Authorities<T: Config> =
128		StorageValue<_, BoundedVec<T::BeefyId, T::MaxAuthorities>, ValueQuery>;
129
130	/// The current validator set id
131	#[pallet::storage]
132	pub type ValidatorSetId<T: Config> =
133		StorageValue<_, sp_consensus_beefy::ValidatorSetId, ValueQuery>;
134
135	/// Authorities set scheduled to be used with the next session
136	#[pallet::storage]
137	pub type NextAuthorities<T: Config> =
138		StorageValue<_, BoundedVec<T::BeefyId, T::MaxAuthorities>, ValueQuery>;
139
140	/// A mapping from BEEFY set ID to the index of the *most recent* session for which its
141	/// members were responsible.
142	///
143	/// This is only used for validating equivocation proofs. An equivocation proof must
144	/// contains a key-ownership proof for a given session, therefore we need a way to tie
145	/// together sessions and BEEFY set ids, i.e. we need to validate that a validator
146	/// was the owner of a given key on a given session, and what the active set ID was
147	/// during that session.
148	///
149	/// TWOX-NOTE: `ValidatorSetId` is not under user control.
150	#[pallet::storage]
151	pub type SetIdSession<T: Config> =
152		StorageMap<_, Twox64Concat, sp_consensus_beefy::ValidatorSetId, SessionIndex>;
153
154	/// Block number where BEEFY consensus is enabled/started.
155	/// By changing this (through privileged `set_new_genesis()`), BEEFY consensus is effectively
156	/// restarted from the newly set block number.
157	#[pallet::storage]
158	pub type GenesisBlock<T: Config> = StorageValue<_, Option<BlockNumberFor<T>>, ValueQuery>;
159
160	#[pallet::genesis_config]
161	pub struct GenesisConfig<T: Config> {
162		/// Initial set of BEEFY authorities.
163		pub authorities: Vec<T::BeefyId>,
164		/// Block number where BEEFY consensus should start.
165		/// Should match the session where initial authorities are active.
166		/// *Note:* Ideally use block number where GRANDPA authorities are changed,
167		/// to guarantee the client gets a finality notification for exactly this block.
168		pub genesis_block: Option<BlockNumberFor<T>>,
169	}
170
171	impl<T: Config> Default for GenesisConfig<T> {
172		fn default() -> Self {
173			// BEEFY genesis will be first BEEFY-MANDATORY block,
174			// use block number one instead of chain-genesis.
175			let genesis_block = Some(One::one());
176			Self { authorities: Vec::new(), genesis_block }
177		}
178	}
179
180	#[pallet::genesis_build]
181	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
182		fn build(&self) {
183			Pallet::<T>::initialize(&self.authorities)
184				// we panic here as runtime maintainers can simply reconfigure genesis and restart
185				// the chain easily
186				.expect("Authorities vec too big");
187			GenesisBlock::<T>::put(&self.genesis_block);
188		}
189	}
190
191	#[pallet::error]
192	pub enum Error<T> {
193		/// A key ownership proof provided as part of an equivocation report is invalid.
194		InvalidKeyOwnershipProof,
195		/// A double voting proof provided as part of an equivocation report is invalid.
196		InvalidDoubleVotingProof,
197		/// A fork voting proof provided as part of an equivocation report is invalid.
198		InvalidForkVotingProof,
199		/// A future block voting proof provided as part of an equivocation report is invalid.
200		InvalidFutureBlockVotingProof,
201		/// The session of the equivocation proof is invalid
202		InvalidEquivocationProofSession,
203		/// The session of the equivocation proof is not in the mapping (anymore)
204		InvalidEquivocationProofSessionMember,
205		/// A given equivocation report is valid but already previously reported.
206		DuplicateOffenceReport,
207		/// Submitted configuration is invalid.
208		InvalidConfiguration,
209	}
210
211	#[pallet::call]
212	impl<T: Config> Pallet<T> {
213		/// Report voter equivocation/misbehavior. This method will verify the
214		/// equivocation proof and validate the given key ownership proof
215		/// against the extracted offender. If both are valid, the offence
216		/// will be reported.
217		#[pallet::call_index(0)]
218		#[pallet::weight(T::WeightInfo::report_double_voting(
219			key_owner_proof.validator_count(),
220			T::MaxNominators::get(),
221		))]
222		pub fn report_double_voting(
223			origin: OriginFor<T>,
224			equivocation_proof: Box<
225				DoubleVotingProof<
226					BlockNumberFor<T>,
227					T::BeefyId,
228					<T::BeefyId as RuntimeAppPublic>::Signature,
229				>,
230			>,
231			key_owner_proof: T::KeyOwnerProof,
232		) -> DispatchResultWithPostInfo {
233			let reporter = ensure_signed(origin)?;
234
235			T::EquivocationReportSystem::process_evidence(
236				Some(reporter),
237				EquivocationEvidenceFor::DoubleVotingProof(*equivocation_proof, key_owner_proof),
238			)?;
239			// Waive the fee since the report is valid and beneficial
240			Ok(Pays::No.into())
241		}
242
243		/// Report voter equivocation/misbehavior. This method will verify the
244		/// equivocation proof and validate the given key ownership proof
245		/// against the extracted offender. If both are valid, the offence
246		/// will be reported.
247		///
248		/// This extrinsic must be called unsigned and it is expected that only
249		/// block authors will call it (validated in `ValidateUnsigned`), as such
250		/// if the block author is defined it will be defined as the equivocation
251		/// reporter.
252		#[pallet::call_index(1)]
253		#[pallet::weight(T::WeightInfo::report_double_voting(
254			key_owner_proof.validator_count(),
255			T::MaxNominators::get(),
256		))]
257		pub fn report_double_voting_unsigned(
258			origin: OriginFor<T>,
259			equivocation_proof: Box<
260				DoubleVotingProof<
261					BlockNumberFor<T>,
262					T::BeefyId,
263					<T::BeefyId as RuntimeAppPublic>::Signature,
264				>,
265			>,
266			key_owner_proof: T::KeyOwnerProof,
267		) -> DispatchResultWithPostInfo {
268			ensure_none(origin)?;
269
270			T::EquivocationReportSystem::process_evidence(
271				None,
272				EquivocationEvidenceFor::DoubleVotingProof(*equivocation_proof, key_owner_proof),
273			)?;
274			Ok(Pays::No.into())
275		}
276
277		/// Reset BEEFY consensus by setting a new BEEFY genesis at `delay_in_blocks` blocks in the
278		/// future.
279		///
280		/// Note: `delay_in_blocks` has to be at least 1.
281		#[pallet::call_index(2)]
282		#[pallet::weight(<T as Config>::WeightInfo::set_new_genesis())]
283		pub fn set_new_genesis(
284			origin: OriginFor<T>,
285			delay_in_blocks: BlockNumberFor<T>,
286		) -> DispatchResult {
287			ensure_root(origin)?;
288			ensure!(delay_in_blocks >= One::one(), Error::<T>::InvalidConfiguration);
289			let genesis_block = frame_system::Pallet::<T>::block_number() + delay_in_blocks;
290			GenesisBlock::<T>::put(Some(genesis_block));
291			Ok(())
292		}
293
294		/// Report fork voting equivocation. This method will verify the equivocation proof
295		/// and validate the given key ownership proof against the extracted offender.
296		/// If both are valid, the offence will be reported.
297		#[pallet::call_index(3)]
298		#[pallet::weight(T::WeightInfo::report_fork_voting::<T>(
299			key_owner_proof.validator_count(),
300			T::MaxNominators::get(),
301			&equivocation_proof.ancestry_proof
302		))]
303		pub fn report_fork_voting(
304			origin: OriginFor<T>,
305			equivocation_proof: Box<
306				ForkVotingProof<
307					HeaderFor<T>,
308					T::BeefyId,
309					<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
310				>,
311			>,
312			key_owner_proof: T::KeyOwnerProof,
313		) -> DispatchResultWithPostInfo {
314			let reporter = ensure_signed(origin)?;
315
316			T::EquivocationReportSystem::process_evidence(
317				Some(reporter),
318				EquivocationEvidenceFor::ForkVotingProof(*equivocation_proof, key_owner_proof),
319			)?;
320			// Waive the fee since the report is valid and beneficial
321			Ok(Pays::No.into())
322		}
323
324		/// Report fork voting equivocation. This method will verify the equivocation proof
325		/// and validate the given key ownership proof against the extracted offender.
326		/// If both are valid, the offence will be reported.
327		///
328		/// This extrinsic must be called unsigned and it is expected that only
329		/// block authors will call it (validated in `ValidateUnsigned`), as such
330		/// if the block author is defined it will be defined as the equivocation
331		/// reporter.
332		#[pallet::call_index(4)]
333		#[pallet::weight(T::WeightInfo::report_fork_voting::<T>(
334			key_owner_proof.validator_count(),
335			T::MaxNominators::get(),
336			&equivocation_proof.ancestry_proof
337		))]
338		pub fn report_fork_voting_unsigned(
339			origin: OriginFor<T>,
340			equivocation_proof: Box<
341				ForkVotingProof<
342					HeaderFor<T>,
343					T::BeefyId,
344					<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
345				>,
346			>,
347			key_owner_proof: T::KeyOwnerProof,
348		) -> DispatchResultWithPostInfo {
349			ensure_none(origin)?;
350
351			T::EquivocationReportSystem::process_evidence(
352				None,
353				EquivocationEvidenceFor::ForkVotingProof(*equivocation_proof, key_owner_proof),
354			)?;
355			// Waive the fee since the report is valid and beneficial
356			Ok(Pays::No.into())
357		}
358
359		/// Report future block voting equivocation. This method will verify the equivocation proof
360		/// and validate the given key ownership proof against the extracted offender.
361		/// If both are valid, the offence will be reported.
362		#[pallet::call_index(5)]
363		#[pallet::weight(T::WeightInfo::report_future_block_voting(
364			key_owner_proof.validator_count(),
365			T::MaxNominators::get(),
366		))]
367		pub fn report_future_block_voting(
368			origin: OriginFor<T>,
369			equivocation_proof: Box<FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>>,
370			key_owner_proof: T::KeyOwnerProof,
371		) -> DispatchResultWithPostInfo {
372			let reporter = ensure_signed(origin)?;
373
374			T::EquivocationReportSystem::process_evidence(
375				Some(reporter),
376				EquivocationEvidenceFor::FutureBlockVotingProof(
377					*equivocation_proof,
378					key_owner_proof,
379				),
380			)?;
381			// Waive the fee since the report is valid and beneficial
382			Ok(Pays::No.into())
383		}
384
385		/// Report future block voting equivocation. This method will verify the equivocation proof
386		/// and validate the given key ownership proof against the extracted offender.
387		/// If both are valid, the offence will be reported.
388		///
389		/// This extrinsic must be called unsigned and it is expected that only
390		/// block authors will call it (validated in `ValidateUnsigned`), as such
391		/// if the block author is defined it will be defined as the equivocation
392		/// reporter.
393		#[pallet::call_index(6)]
394		#[pallet::weight(T::WeightInfo::report_future_block_voting(
395			key_owner_proof.validator_count(),
396			T::MaxNominators::get(),
397		))]
398		pub fn report_future_block_voting_unsigned(
399			origin: OriginFor<T>,
400			equivocation_proof: Box<FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>>,
401			key_owner_proof: T::KeyOwnerProof,
402		) -> DispatchResultWithPostInfo {
403			ensure_none(origin)?;
404
405			T::EquivocationReportSystem::process_evidence(
406				None,
407				EquivocationEvidenceFor::FutureBlockVotingProof(
408					*equivocation_proof,
409					key_owner_proof,
410				),
411			)?;
412			// Waive the fee since the report is valid and beneficial
413			Ok(Pays::No.into())
414		}
415	}
416
417	#[pallet::hooks]
418	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
419		#[cfg(feature = "try-runtime")]
420		fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
421			Self::do_try_state()
422		}
423	}
424
425	#[allow(deprecated)]
426	#[pallet::validate_unsigned]
427	impl<T: Config> ValidateUnsigned for Pallet<T> {
428		type Call = Call<T>;
429
430		fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
431			Self::pre_dispatch(call)
432		}
433
434		fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
435			Self::validate_unsigned(source, call)
436		}
437	}
438
439	impl<T: Config> Call<T> {
440		pub fn to_equivocation_evidence_for(&self) -> Option<EquivocationEvidenceFor<T>> {
441			match self {
442				Call::report_double_voting_unsigned { equivocation_proof, key_owner_proof } => {
443					Some(EquivocationEvidenceFor::<T>::DoubleVotingProof(
444						*equivocation_proof.clone(),
445						key_owner_proof.clone(),
446					))
447				},
448				Call::report_fork_voting_unsigned { equivocation_proof, key_owner_proof } => {
449					Some(EquivocationEvidenceFor::<T>::ForkVotingProof(
450						*equivocation_proof.clone(),
451						key_owner_proof.clone(),
452					))
453				},
454				Call::report_future_block_voting_unsigned {
455					equivocation_proof,
456					key_owner_proof,
457				} => Some(EquivocationEvidenceFor::<T>::FutureBlockVotingProof(
458					*equivocation_proof.clone(),
459					key_owner_proof.clone(),
460				)),
461				_ => None,
462			}
463		}
464	}
465
466	impl<T: Config> From<EquivocationEvidenceFor<T>> for Call<T> {
467		fn from(evidence: EquivocationEvidenceFor<T>) -> Self {
468			match evidence {
469				EquivocationEvidenceFor::DoubleVotingProof(equivocation_proof, key_owner_proof) => {
470					Call::report_double_voting_unsigned {
471						equivocation_proof: Box::new(equivocation_proof),
472						key_owner_proof,
473					}
474				},
475				EquivocationEvidenceFor::ForkVotingProof(equivocation_proof, key_owner_proof) => {
476					Call::report_fork_voting_unsigned {
477						equivocation_proof: Box::new(equivocation_proof),
478						key_owner_proof,
479					}
480				},
481				EquivocationEvidenceFor::FutureBlockVotingProof(
482					equivocation_proof,
483					key_owner_proof,
484				) => Call::report_future_block_voting_unsigned {
485					equivocation_proof: Box::new(equivocation_proof),
486					key_owner_proof,
487				},
488			}
489		}
490	}
491}
492
493#[cfg(any(feature = "try-runtime", test))]
494impl<T: Config> Pallet<T> {
495	/// Ensure the correctness of the state of this pallet.
496	///
497	/// This should be valid before or after each state transition of this pallet.
498	pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
499		Self::try_state_authorities()?;
500		Self::try_state_validators()?;
501
502		Ok(())
503	}
504
505	/// # Invariants
506	///
507	/// * `Authorities` should not exceed the `MaxAuthorities` capacity.
508	/// * `NextAuthorities` should not exceed the `MaxAuthorities` capacity.
509	fn try_state_authorities() -> Result<(), sp_runtime::TryRuntimeError> {
510		if let Some(authorities_len) = <Authorities<T>>::decode_len() {
511			ensure!(
512				authorities_len as u32 <= T::MaxAuthorities::get(),
513				"Authorities number exceeds what the pallet config allows."
514			);
515		} else {
516			return Err(sp_runtime::TryRuntimeError::Other(
517				"Failed to decode length of authorities",
518			));
519		}
520
521		if let Some(next_authorities_len) = <NextAuthorities<T>>::decode_len() {
522			ensure!(
523				next_authorities_len as u32 <= T::MaxAuthorities::get(),
524				"Next authorities number exceeds what the pallet config allows."
525			);
526		} else {
527			return Err(sp_runtime::TryRuntimeError::Other(
528				"Failed to decode length of next authorities",
529			));
530		}
531		Ok(())
532	}
533
534	/// # Invariants
535	///
536	/// `ValidatorSetId` must be present in `SetIdSession`
537	fn try_state_validators() -> Result<(), sp_runtime::TryRuntimeError> {
538		let validator_set_id = <ValidatorSetId<T>>::get();
539		ensure!(
540			SetIdSession::<T>::get(validator_set_id).is_some(),
541			"Validator set id must be present in SetIdSession"
542		);
543		Ok(())
544	}
545}
546
547impl<T: Config> Pallet<T> {
548	/// Return the current active BEEFY validator set.
549	pub fn validator_set() -> Option<ValidatorSet<T::BeefyId>> {
550		let validators: BoundedVec<T::BeefyId, T::MaxAuthorities> = Authorities::<T>::get();
551		let id: sp_consensus_beefy::ValidatorSetId = ValidatorSetId::<T>::get();
552		ValidatorSet::<T::BeefyId>::new(validators, id)
553	}
554
555	/// Submits an extrinsic to report a double voting equivocation. This method will create
556	/// an unsigned extrinsic with a call to `report_double_voting_unsigned` and
557	/// will push the transaction to the pool. Only useful in an offchain context.
558	pub fn submit_unsigned_double_voting_report(
559		equivocation_proof: DoubleVotingProof<
560			BlockNumberFor<T>,
561			T::BeefyId,
562			<T::BeefyId as RuntimeAppPublic>::Signature,
563		>,
564		key_owner_proof: T::KeyOwnerProof,
565	) -> Option<()> {
566		T::EquivocationReportSystem::publish_evidence(EquivocationEvidenceFor::DoubleVotingProof(
567			equivocation_proof,
568			key_owner_proof,
569		))
570		.ok()
571	}
572
573	/// Submits an extrinsic to report a fork voting equivocation. This method will create
574	/// an unsigned extrinsic with a call to `report_fork_voting_unsigned` and
575	/// will push the transaction to the pool. Only useful in an offchain context.
576	pub fn submit_unsigned_fork_voting_report(
577		equivocation_proof: ForkVotingProof<
578			HeaderFor<T>,
579			T::BeefyId,
580			<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
581		>,
582		key_owner_proof: T::KeyOwnerProof,
583	) -> Option<()> {
584		T::EquivocationReportSystem::publish_evidence(EquivocationEvidenceFor::ForkVotingProof(
585			equivocation_proof,
586			key_owner_proof,
587		))
588		.ok()
589	}
590
591	/// Submits an extrinsic to report a future block voting equivocation. This method will create
592	/// an unsigned extrinsic with a call to `report_future_block_voting_unsigned` and
593	/// will push the transaction to the pool. Only useful in an offchain context.
594	pub fn submit_unsigned_future_block_voting_report(
595		equivocation_proof: FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>,
596		key_owner_proof: T::KeyOwnerProof,
597	) -> Option<()> {
598		T::EquivocationReportSystem::publish_evidence(
599			EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, key_owner_proof),
600		)
601		.ok()
602	}
603
604	fn change_authorities(
605		new: BoundedVec<T::BeefyId, T::MaxAuthorities>,
606		queued: BoundedVec<T::BeefyId, T::MaxAuthorities>,
607	) {
608		Authorities::<T>::put(&new);
609
610		let new_id = ValidatorSetId::<T>::get() + 1u64;
611		ValidatorSetId::<T>::put(new_id);
612
613		NextAuthorities::<T>::put(&queued);
614
615		if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(new, new_id) {
616			let log = DigestItem::Consensus(
617				BEEFY_ENGINE_ID,
618				ConsensusLog::AuthoritiesChange(validator_set.clone()).encode(),
619			);
620			frame_system::Pallet::<T>::deposit_log(log);
621
622			let next_id = new_id + 1;
623			if let Some(next_validator_set) = ValidatorSet::<T::BeefyId>::new(queued, next_id) {
624				<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
625					&validator_set,
626					&next_validator_set,
627				);
628			}
629		}
630	}
631
632	fn initialize(authorities: &Vec<T::BeefyId>) -> Result<(), ()> {
633		if authorities.is_empty() {
634			return Ok(());
635		}
636
637		if !Authorities::<T>::get().is_empty() {
638			return Err(());
639		}
640
641		let bounded_authorities =
642			BoundedSlice::<T::BeefyId, T::MaxAuthorities>::try_from(authorities.as_slice())
643				.map_err(|_| ())?;
644
645		let id = GENESIS_AUTHORITY_SET_ID;
646		Authorities::<T>::put(bounded_authorities);
647		ValidatorSetId::<T>::put(id);
648		// Like `pallet_session`, initialize the next validator set as well.
649		NextAuthorities::<T>::put(bounded_authorities);
650
651		if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(authorities.clone(), id) {
652			let next_id = id + 1;
653			if let Some(next_validator_set) =
654				ValidatorSet::<T::BeefyId>::new(authorities.clone(), next_id)
655			{
656				<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
657					&validator_set,
658					&next_validator_set,
659				);
660			}
661		}
662
663		// NOTE: initialize first session of first set. this is necessary for
664		// the genesis set and session since we only update the set -> session
665		// mapping whenever a new session starts, i.e. through `on_new_session`.
666		SetIdSession::<T>::insert(0, 0);
667
668		Ok(())
669	}
670}
671
672impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
673	type Public = T::BeefyId;
674}
675
676impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T>
677where
678	T: pallet_session::Config,
679{
680	type Key = T::BeefyId;
681
682	fn on_genesis_session<'a, I: 'a>(validators: I)
683	where
684		I: Iterator<Item = (&'a T::AccountId, T::BeefyId)>,
685	{
686		let authorities = validators.map(|(_, k)| k).collect::<Vec<_>>();
687		// we panic here as runtime maintainers can simply reconfigure genesis and restart the
688		// chain easily
689		Self::initialize(&authorities).expect("Authorities vec too big");
690	}
691
692	fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I)
693	where
694		I: Iterator<Item = (&'a T::AccountId, T::BeefyId)>,
695	{
696		let next_authorities = validators.map(|(_, k)| k).collect::<Vec<_>>();
697		if next_authorities.len() as u32 > T::MaxAuthorities::get() {
698			log::error!(
699				target: LOG_TARGET,
700				"authorities list {:?} truncated to length {}",
701				next_authorities,
702				T::MaxAuthorities::get(),
703			);
704		}
705		let bounded_next_authorities =
706			BoundedVec::<_, T::MaxAuthorities>::truncate_from(next_authorities);
707
708		let next_queued_authorities = queued_validators.map(|(_, k)| k).collect::<Vec<_>>();
709		if next_queued_authorities.len() as u32 > T::MaxAuthorities::get() {
710			log::error!(
711				target: LOG_TARGET,
712				"queued authorities list {:?} truncated to length {}",
713				next_queued_authorities,
714				T::MaxAuthorities::get(),
715			);
716		}
717		let bounded_next_queued_authorities =
718			BoundedVec::<_, T::MaxAuthorities>::truncate_from(next_queued_authorities);
719
720		// Always issue a change on each `session`, even if validator set hasn't changed.
721		// We want to have at least one BEEFY mandatory block per session.
722		Self::change_authorities(bounded_next_authorities, bounded_next_queued_authorities);
723
724		let validator_set_id = ValidatorSetId::<T>::get();
725		// Update the mapping for the new set id that corresponds to the latest session (i.e. now).
726		let session_index = pallet_session::Pallet::<T>::current_index();
727		SetIdSession::<T>::insert(validator_set_id, &session_index);
728		// Prune old entry if limit reached.
729		let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1);
730		if validator_set_id >= max_set_id_session_entries {
731			SetIdSession::<T>::remove(validator_set_id - max_set_id_session_entries);
732		}
733	}
734
735	fn on_disabled(i: u32) {
736		let log = DigestItem::Consensus(
737			BEEFY_ENGINE_ID,
738			ConsensusLog::<T::BeefyId>::OnDisabled(i as AuthorityIndex).encode(),
739		);
740
741		frame_system::Pallet::<T>::deposit_log(log);
742	}
743}
744
745impl<T: Config> IsMember<T::BeefyId> for Pallet<T> {
746	fn is_member(authority_id: &T::BeefyId) -> bool {
747		Authorities::<T>::get().iter().any(|id| id == authority_id)
748	}
749}
750
751pub trait WeightInfo {
752	fn report_voting_equivocation(
753		votes_count: u32,
754		validator_count: u32,
755		max_nominators_per_validator: u32,
756	) -> Weight;
757
758	fn set_new_genesis() -> Weight;
759}
760
761pub(crate) trait WeightInfoExt: WeightInfo {
762	fn report_double_voting(validator_count: u32, max_nominators_per_validator: u32) -> Weight {
763		Self::report_voting_equivocation(2, validator_count, max_nominators_per_validator)
764	}
765
766	fn report_fork_voting<T: Config>(
767		validator_count: u32,
768		max_nominators_per_validator: u32,
769		ancestry_proof: &<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
770	) -> Weight {
771		<T::AncestryHelper as AncestryHelperWeightInfo<HeaderFor<T>>>::is_proof_optimal(&ancestry_proof)
772			.saturating_add(<T::AncestryHelper as AncestryHelperWeightInfo<HeaderFor<T>>>::extract_validation_context())
773			.saturating_add(
774				<T::AncestryHelper as AncestryHelperWeightInfo<HeaderFor<T>>>::is_non_canonical(
775					ancestry_proof,
776				),
777			)
778			.saturating_add(Self::report_voting_equivocation(
779				1,
780				validator_count,
781				max_nominators_per_validator,
782			))
783	}
784
785	fn report_future_block_voting(
786		validator_count: u32,
787		max_nominators_per_validator: u32,
788	) -> Weight {
789		// checking if the report is for a future block
790		DbWeight::get()
791			.reads(1)
792			// check and report the equivocated vote
793			.saturating_add(Self::report_voting_equivocation(
794				1,
795				validator_count,
796				max_nominators_per_validator,
797			))
798	}
799}
800
801impl<T> WeightInfoExt for T where T: WeightInfo {}