diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index f6a35f68a4a1f..c9dbdb3b2e16a 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -124,6 +124,7 @@ impl pallet_mmr::Config for Test { impl pallet_beefy::Config for Test { type BeefyId = BeefyId; + type MaxAuthorities = ConstU32<100>; } parameter_types! { diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index 14e7ac26cdb6e..a0cba2270b37d 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -17,9 +17,13 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::Encode; +use codec::{Encode, MaxEncodedLen}; -use frame_support::{traits::OneSessionHandler, Parameter}; +use frame_support::{ + log, + traits::{Get, OneSessionHandler}, + BoundedSlice, BoundedVec, Parameter, +}; use sp_runtime::{ generic::DigestItem, @@ -42,28 +46,28 @@ pub use pallet::*; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config { /// Authority identifier type - type BeefyId: Member + Parameter + RuntimeAppPublic + MaybeSerializeDeserialize; + type BeefyId: Member + + Parameter + + RuntimeAppPublic + + MaybeSerializeDeserialize + + MaxEncodedLen; + + /// The maximum number of authorities that can be added. + type MaxAuthorities: Get; } #[pallet::pallet] - #[pallet::without_storage_info] pub struct Pallet(PhantomData); - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet {} - /// The current authorities set #[pallet::storage] #[pallet::getter(fn authorities)] - pub(super) type Authorities = StorageValue<_, Vec, ValueQuery>; + pub(super) type Authorities = + StorageValue<_, BoundedVec, ValueQuery>; /// The current validator set id #[pallet::storage] @@ -74,7 +78,8 @@ pub mod pallet { /// Authorities set scheduled to be used with the next session #[pallet::storage] #[pallet::getter(fn next_authorities)] - pub(super) type NextAuthorities = StorageValue<_, Vec, ValueQuery>; + pub(super) type NextAuthorities = + StorageValue<_, BoundedVec, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -91,7 +96,10 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - Pallet::::initialize_authorities(&self.authorities); + Pallet::::initialize_authorities(&self.authorities) + // we panic here as runtime maintainers can simply reconfigure genesis and restart + // the chain easily + .expect("Authorities vec too big"); } } } @@ -99,12 +107,15 @@ pub mod pallet { impl Pallet { /// Return the current active BEEFY validator set. pub fn validator_set() -> Option> { - let validators: Vec = Self::authorities(); + let validators: BoundedVec = Self::authorities(); let id: beefy_primitives::ValidatorSetId = Self::validator_set_id(); ValidatorSet::::new(validators, id) } - fn change_authorities(new: Vec, queued: Vec) { + fn change_authorities( + new: BoundedVec, + queued: BoundedVec, + ) { >::put(&new); let next_id = Self::validator_set_id() + 1u64; @@ -120,17 +131,23 @@ impl Pallet { >::put(&queued); } - fn initialize_authorities(authorities: &[T::BeefyId]) { + fn initialize_authorities(authorities: &[T::BeefyId]) -> Result<(), ()> { if authorities.is_empty() { - return + return Ok(()) + } + + if !>::get().is_empty() { + return Err(()) } - assert!(>::get().is_empty(), "Authorities are already initialized!"); + let bounded_authorities = + BoundedSlice::::try_from(authorities)?; - >::put(authorities); + >::put(bounded_authorities); >::put(0); // Like `pallet_session`, initialize the next validator set as well. - >::put(authorities); + >::put(bounded_authorities); + Ok(()) } } @@ -146,7 +163,9 @@ impl OneSessionHandler for Pallet { I: Iterator, { let authorities = validators.map(|(_, k)| k).collect::>(); - Self::initialize_authorities(&authorities); + // we panic here as runtime maintainers can simply reconfigure genesis and restart the + // chain easily + Self::initialize_authorities(&authorities).expect("Authorities vec too big"); } fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I) @@ -154,11 +173,30 @@ impl OneSessionHandler for Pallet { I: Iterator, { let next_authorities = validators.map(|(_, k)| k).collect::>(); + if next_authorities.len() as u32 > T::MaxAuthorities::get() { + log::error!( + target: "runtime::beefy", + "authorities list {:?} truncated to length {}", + next_authorities, T::MaxAuthorities::get(), + ); + } + let bounded_next_authorities = + BoundedVec::<_, T::MaxAuthorities>::truncate_from(next_authorities); + let next_queued_authorities = queued_validators.map(|(_, k)| k).collect::>(); + if next_queued_authorities.len() as u32 > T::MaxAuthorities::get() { + log::error!( + target: "runtime::beefy", + "queued authorities list {:?} truncated to length {}", + next_queued_authorities, T::MaxAuthorities::get(), + ); + } + let bounded_next_queued_authorities = + BoundedVec::<_, T::MaxAuthorities>::truncate_from(next_queued_authorities); // Always issue a change on each `session`, even if validator set hasn't changed. // We want to have at least one BEEFY mandatory block per session. - Self::change_authorities(next_authorities, next_queued_authorities); + Self::change_authorities(bounded_next_authorities, bounded_next_queued_authorities); } fn on_disabled(i: u32) { diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index 7a8f15cd51d29..27796b5b2206c 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -52,7 +52,7 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Beefy: pallet_beefy::{Pallet, Call, Config, Storage}, + Beefy: pallet_beefy::{Pallet, Config, Storage}, Session: pallet_session::{Pallet, Call, Storage, Event, Config}, } ); @@ -86,6 +86,7 @@ impl frame_system::Config for Test { impl pallet_beefy::Config for Test { type BeefyId = BeefyId; + type MaxAuthorities = ConstU32<100>; } parameter_types! { diff --git a/primitives/runtime/src/bounded/bounded_vec.rs b/primitives/runtime/src/bounded/bounded_vec.rs index 4493d9f8b0198..fe832b07f9588 100644 --- a/primitives/runtime/src/bounded/bounded_vec.rs +++ b/primitives/runtime/src/bounded/bounded_vec.rs @@ -146,6 +146,15 @@ impl<'a, T, S> From> for &'a [T] { } } +impl<'a, T, S> Clone for BoundedSlice<'a, T, S> { + fn clone(&self) -> Self { + BoundedSlice(self.0, PhantomData) + } +} + +// Since a reference `&T` is always `Copy`, so is `BoundedSlice<'a, T, S>`. +impl<'a, T, S> Copy for BoundedSlice<'a, T, S> {} + impl<'a, T, S> sp_std::iter::IntoIterator for BoundedSlice<'a, T, S> { type Item = &'a T; type IntoIter = sp_std::slice::Iter<'a, T>;