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