pallet_partner_chains_session/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
88
89extern crate alloc;
90
91use alloc::collections::BTreeSet;
92use frame_support::{
93 ConsensusEngineId,
94 traits::{
95 EstimateNextNewSession, EstimateNextSessionRotation, FindAuthor, OneSessionHandler,
96 ValidatorRegistration,
97 },
98 weights::Weight,
99};
100use frame_system::DecRefStatus;
101use frame_system::pallet_prelude::BlockNumberFor;
102pub use pallet::*;
103use sp_runtime::{DispatchError, KeyTypeId, RuntimeAppPublic, traits::OpaqueKeys};
104pub use sp_staking::SessionIndex;
105use sp_std::prelude::*;
106
107#[cfg(feature = "pallet-session-compat")]
108pub mod pallet_session_compat;
109
110pub trait ShouldEndSession<BlockNumber> {
112 fn should_end_session(now: BlockNumber) -> bool;
114}
115
116pub trait SessionManager<ValidatorId, Keys> {
118 fn new_session(new_index: SessionIndex) -> Option<Vec<(ValidatorId, Keys)>>;
132 fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<(ValidatorId, Keys)>> {
137 Self::new_session(new_index)
138 }
139 fn end_session(end_index: SessionIndex);
144 fn start_session(start_index: SessionIndex);
148}
149
150impl<A, B> SessionManager<A, B> for () {
151 fn new_session(_: SessionIndex) -> Option<Vec<(A, B)>> {
152 None
153 }
154 fn start_session(_: SessionIndex) {}
155 fn end_session(_: SessionIndex) {}
156}
157
158pub trait SessionHandler<ValidatorId> {
160 const KEY_TYPE_IDS: &'static [KeyTypeId];
166
167 fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(ValidatorId, Ks)]);
172
173 fn on_new_session<Ks: OpaqueKeys>(
179 changed: bool,
180 validators: &[(ValidatorId, Ks)],
181 queued_validators: &[(ValidatorId, Ks)],
182 );
183
184 fn on_before_session_ending() {}
189
190 fn on_disabled(validator_index: u32);
192}
193
194#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
195#[tuple_types_custom_trait_bound(OneSessionHandler<AId>)]
196impl<AId> SessionHandler<AId> for Tuple {
197 for_tuples!(
198 const KEY_TYPE_IDS: &'static [KeyTypeId] = &[ #( <Tuple::Key as RuntimeAppPublic>::ID ),* ];
199 );
200
201 fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(AId, Ks)]) {
202 for_tuples!(
203 #(
204 let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
205 .filter_map(|k|
206 k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
207 )
208 );
209
210 Tuple::on_genesis_session(our_keys);
211 )*
212 )
213 }
214
215 fn on_new_session<Ks: OpaqueKeys>(
216 changed: bool,
217 validators: &[(AId, Ks)],
218 queued_validators: &[(AId, Ks)],
219 ) {
220 for_tuples!(
221 #(
222 let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
223 .filter_map(|k|
224 k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
225 ));
226 let queued_keys: Box<dyn Iterator<Item=_>> = Box::new(queued_validators.iter()
227 .filter_map(|k|
228 k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
229 ));
230 Tuple::on_new_session(changed, our_keys, queued_keys);
231 )*
232 )
233 }
234
235 fn on_before_session_ending() {
236 for_tuples!( #( Tuple::on_before_session_ending(); )* )
237 }
238
239 fn on_disabled(i: u32) {
240 for_tuples!( #( Tuple::on_disabled(i); )* )
241 }
242}
243
244#[frame_support::pallet]
245pub mod pallet {
246 use super::*;
247 use frame_support::pallet_prelude::*;
248
249 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
251
252 #[pallet::pallet]
253 #[pallet::storage_version(STORAGE_VERSION)]
254 #[pallet::without_storage_info]
255 pub struct Pallet<T>(_);
256
257 #[pallet::config]
258 pub trait Config: frame_system::Config {
259 type ValidatorId: Member
261 + Parameter
262 + MaybeSerializeDeserialize
263 + MaxEncodedLen
264 + Into<Self::AccountId>;
265
266 type ShouldEndSession: ShouldEndSession<BlockNumberFor<Self>>;
268
269 type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
273
274 type SessionManager: SessionManager<Self::ValidatorId, Self::Keys>;
276
277 type SessionHandler: SessionHandler<Self::ValidatorId>;
279
280 type Keys: OpaqueKeys + Member + Parameter + MaybeSerializeDeserialize;
282 }
283
284 pub type ValidatorList<T> = Vec<<T as Config>::ValidatorId>;
285 pub type ValidatorAndKeysList<T> = Vec<(<T as Config>::ValidatorId, <T as Config>::Keys)>;
286
287 #[pallet::genesis_config]
288 #[derive(frame_support::DefaultNoBound)]
289 pub struct GenesisConfig<T: Config> {
290 pub initial_validators: ValidatorAndKeysList<T>,
291 }
292
293 #[pallet::genesis_build]
294 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
295 fn build(&self) {
296 if T::SessionHandler::KEY_TYPE_IDS.len() != T::Keys::key_ids().len() {
297 panic!("Number of keys in session handler and session keys does not match");
298 }
299
300 T::SessionHandler::KEY_TYPE_IDS
301 .iter()
302 .zip(T::Keys::key_ids())
303 .enumerate()
304 .for_each(|(i, (sk, kk))| {
305 if sk != kk {
306 panic!(
307 "Session handler and session key expect different key type at index: {}",
308 i,
309 );
310 }
311 });
312
313 let maybe_genesis_validators = Pallet::<T>::new_session_genesis(0);
314 let initial_validators = match &maybe_genesis_validators {
315 Some(validators) => validators,
316 None => {
317 frame_support::print(
318 "No initial validator provided by `SessionManager`, use \
319 session config keys to generate initial validator set.",
320 );
321 &self.initial_validators
322 },
323 };
324 Pallet::<T>::rotate_validators(initial_validators);
325 T::SessionHandler::on_genesis_session::<T::Keys>(initial_validators);
326 T::SessionManager::start_session(0);
327 }
328 }
329
330 #[pallet::storage]
331 pub type Validators<T: Config> = StorageValue<_, ValidatorList<T>, ValueQuery>;
333
334 #[pallet::storage]
335 pub type ValidatorsAndKeys<T: Config> = StorageValue<_, ValidatorAndKeysList<T>, ValueQuery>;
336
337 #[pallet::storage]
339 pub type CurrentIndex<T> = StorageValue<_, SessionIndex, ValueQuery>;
340
341 #[pallet::storage]
347 pub type DisabledValidators<T> = StorageValue<_, Vec<u32>, ValueQuery>;
348
349 #[pallet::event]
350 #[pallet::generate_deposit(pub(super) fn deposit_event)]
351 pub enum Event {
352 NewSession { session_index: SessionIndex },
355 }
356
357 #[pallet::hooks]
358 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
359 fn on_initialize(n: BlockNumberFor<T>) -> Weight {
362 if T::ShouldEndSession::should_end_session(n) {
363 Self::rotate_session();
364 T::BlockWeights::get().max_block
365 } else {
366 Weight::zero()
370 }
371 }
372 }
373}
374
375impl<T: Config> Pallet<T> {
376 pub fn validators() -> Vec<T::ValidatorId> {
378 Validators::<T>::get()
379 }
380
381 pub fn validators_and_keys() -> Vec<(T::ValidatorId, T::Keys)> {
382 ValidatorsAndKeys::<T>::get()
383 }
384
385 pub fn current_index() -> SessionIndex {
387 CurrentIndex::<T>::get()
388 }
389
390 pub fn disabled_validators() -> Vec<u32> {
392 DisabledValidators::<T>::get()
393 }
394
395 pub fn rotate_session() {
397 let session_index = <CurrentIndex<T>>::get();
398 log::trace!(target: "runtime::session", "rotating session {:?}", session_index);
399
400 T::SessionHandler::on_before_session_ending();
401 T::SessionManager::end_session(session_index);
402
403 let session_index = session_index + 1;
404 <CurrentIndex<T>>::put(session_index);
405
406 let (validators, changed) = if let Some(validators) = Self::new_session(session_index) {
407 Self::rotate_validators(&validators);
408 (validators, true)
409 } else {
410 (ValidatorsAndKeys::<T>::get(), false)
411 };
412
413 T::SessionManager::start_session(session_index);
414 Self::deposit_event(Event::NewSession { session_index });
415 T::SessionHandler::on_new_session::<T::Keys>(changed, validators.as_ref(), &[]);
417 }
418
419 pub fn disable_index(i: u32) -> bool {
421 if i >= Validators::<T>::decode_len().unwrap_or(0) as u32 {
422 return false;
423 }
424
425 <DisabledValidators<T>>::mutate(|disabled| {
426 if let Err(index) = disabled.binary_search(&i) {
427 disabled.insert(index, i);
428 T::SessionHandler::on_disabled(i);
429 return true;
430 }
431
432 false
433 })
434 }
435
436 pub fn disable(c: &T::ValidatorId) -> bool {
442 ValidatorsAndKeys::<T>::get()
443 .iter()
444 .position(|(i, _)| i == c)
445 .map(|i| Self::disable_index(i as u32))
446 .unwrap_or(false)
447 }
448
449 pub fn acc_ids(validators: &[(T::ValidatorId, T::Keys)]) -> Vec<T::AccountId> {
450 validators.iter().map(|(v_id, _)| v_id.clone().into()).collect()
451 }
452 fn inc_provider(account: &T::AccountId) {
453 frame_system::Pallet::<T>::inc_providers(account);
454 }
455
456 fn dec_provider(account: &T::AccountId) -> Result<DecRefStatus, DispatchError> {
457 frame_system::Pallet::<T>::dec_providers(account)
458 }
459
460 fn change_account_providers(new_ids: &[T::AccountId], old_ids: &[T::AccountId]) {
461 let new_ids = BTreeSet::from_iter(new_ids);
462 let old_ids = BTreeSet::from_iter(old_ids);
463 let to_inc = new_ids.difference(&old_ids);
464 let to_dec = old_ids.difference(&new_ids);
465 for account in to_inc {
466 Self::inc_provider(account);
467 }
468 for account in to_dec {
469 Self::dec_provider(account).expect(
470 "We always match dec_providers with corresponding inc_providers, thus it cannot fail",
471 );
472 }
473 }
474
475 fn rotate_validators(new_validators: &ValidatorAndKeysList<T>) {
476 ValidatorsAndKeys::<T>::put(new_validators);
477 #[cfg(feature = "polkadot-js-compat")]
478 {
479 let validator_ids: Vec<_> = new_validators.iter().cloned().map(|v| v.0).collect();
480 Validators::<T>::put(validator_ids);
483 }
484 Self::change_account_providers(
485 &Self::acc_ids(new_validators),
486 &Self::acc_ids(&ValidatorsAndKeys::<T>::get()),
487 );
488 <DisabledValidators<T>>::take();
489 }
490
491 pub fn new_session(index: SessionIndex) -> Option<ValidatorAndKeysList<T>> {
492 let validators = T::SessionManager::new_session(index)?;
493 Some(validators)
494 }
495
496 pub fn new_session_genesis(index: SessionIndex) -> Option<ValidatorAndKeysList<T>> {
497 let validators = T::SessionManager::new_session_genesis(index)?;
498 Some(validators)
499 }
500}
501
502impl<T: Config> ValidatorRegistration<T::ValidatorId> for Pallet<T> {
503 fn is_registered(id: &T::ValidatorId) -> bool {
504 ValidatorsAndKeys::<T>::get().iter().any(|(vid, _)| vid == id)
505 }
506}
507
508impl<T: Config> EstimateNextNewSession<BlockNumberFor<T>> for Pallet<T> {
509 fn average_session_length() -> BlockNumberFor<T> {
510 T::NextSessionRotation::average_session_length()
511 }
512
513 fn estimate_next_new_session(now: BlockNumberFor<T>) -> (Option<BlockNumberFor<T>>, Weight) {
516 T::NextSessionRotation::estimate_next_session_rotation(now)
517 }
518}
519
520impl<T: Config> frame_support::traits::DisabledValidators for Pallet<T> {
521 fn is_disabled(index: u32) -> bool {
522 DisabledValidators::<T>::get().binary_search(&index).is_ok()
523 }
524
525 fn disabled_validators() -> Vec<u32> {
526 DisabledValidators::<T>::get()
527 }
528}
529
530pub struct FindAccountFromAuthorIndex<T, Inner>(core::marker::PhantomData<(T, Inner)>);
534
535impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
536 for FindAccountFromAuthorIndex<T, Inner>
537{
538 fn find_author<'a, I>(digests: I) -> Option<T::ValidatorId>
539 where
540 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
541 {
542 let i = Inner::find_author(digests)?;
543
544 let validators = ValidatorsAndKeys::<T>::get();
545 validators.get(i as usize).cloned().map(|validator_and_key| validator_and_key.0)
546 }
547}