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 RuntimeEvent: From<Event> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
261
262 type ValidatorId: Member
264 + Parameter
265 + MaybeSerializeDeserialize
266 + MaxEncodedLen
267 + Into<Self::AccountId>;
268
269 type ShouldEndSession: ShouldEndSession<BlockNumberFor<Self>>;
271
272 type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
276
277 type SessionManager: SessionManager<Self::ValidatorId, Self::Keys>;
279
280 type SessionHandler: SessionHandler<Self::ValidatorId>;
282
283 type Keys: OpaqueKeys + Member + Parameter + MaybeSerializeDeserialize;
285 }
286
287 pub type ValidatorList<T> = Vec<<T as Config>::ValidatorId>;
288 pub type ValidatorAndKeysList<T> = Vec<(<T as Config>::ValidatorId, <T as Config>::Keys)>;
289
290 #[pallet::genesis_config]
291 #[derive(frame_support::DefaultNoBound)]
292 pub struct GenesisConfig<T: Config> {
293 pub initial_validators: ValidatorAndKeysList<T>,
294 }
295
296 #[pallet::genesis_build]
297 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
298 fn build(&self) {
299 if T::SessionHandler::KEY_TYPE_IDS.len() != T::Keys::key_ids().len() {
300 panic!("Number of keys in session handler and session keys does not match");
301 }
302
303 T::SessionHandler::KEY_TYPE_IDS
304 .iter()
305 .zip(T::Keys::key_ids())
306 .enumerate()
307 .for_each(|(i, (sk, kk))| {
308 if sk != kk {
309 panic!(
310 "Session handler and session key expect different key type at index: {}",
311 i,
312 );
313 }
314 });
315
316 let maybe_genesis_validators = Pallet::<T>::new_session_genesis(0);
317 let initial_validators = match &maybe_genesis_validators {
318 Some(validators) => validators,
319 None => {
320 frame_support::print(
321 "No initial validator provided by `SessionManager`, use \
322 session config keys to generate initial validator set.",
323 );
324 &self.initial_validators
325 },
326 };
327 Pallet::<T>::rotate_validators(initial_validators);
328 T::SessionHandler::on_genesis_session::<T::Keys>(initial_validators);
329 T::SessionManager::start_session(0);
330 }
331 }
332
333 #[pallet::storage]
334 pub type Validators<T: Config> = StorageValue<_, ValidatorList<T>, ValueQuery>;
336
337 #[pallet::storage]
338 pub type ValidatorsAndKeys<T: Config> = StorageValue<_, ValidatorAndKeysList<T>, ValueQuery>;
339
340 #[pallet::storage]
342 pub type CurrentIndex<T> = StorageValue<_, SessionIndex, ValueQuery>;
343
344 #[pallet::storage]
350 pub type DisabledValidators<T> = StorageValue<_, Vec<u32>, ValueQuery>;
351
352 #[pallet::event]
353 #[pallet::generate_deposit(pub(super) fn deposit_event)]
354 pub enum Event {
355 NewSession { session_index: SessionIndex },
358 }
359
360 #[pallet::hooks]
361 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
362 fn on_initialize(n: BlockNumberFor<T>) -> Weight {
365 if T::ShouldEndSession::should_end_session(n) {
366 Self::rotate_session();
367 T::BlockWeights::get().max_block
368 } else {
369 Weight::zero()
373 }
374 }
375 }
376}
377
378impl<T: Config> Pallet<T> {
379 pub fn validators() -> Vec<T::ValidatorId> {
381 Validators::<T>::get()
382 }
383
384 pub fn validators_and_keys() -> Vec<(T::ValidatorId, T::Keys)> {
385 ValidatorsAndKeys::<T>::get()
386 }
387
388 pub fn current_index() -> SessionIndex {
390 CurrentIndex::<T>::get()
391 }
392
393 pub fn disabled_validators() -> Vec<u32> {
395 DisabledValidators::<T>::get()
396 }
397
398 pub fn rotate_session() {
400 let session_index = <CurrentIndex<T>>::get();
401 log::trace!(target: "runtime::session", "rotating session {:?}", session_index);
402
403 T::SessionHandler::on_before_session_ending();
404 T::SessionManager::end_session(session_index);
405
406 let session_index = session_index + 1;
407 <CurrentIndex<T>>::put(session_index);
408
409 let (validators, changed) = if let Some(validators) = Self::new_session(session_index) {
410 Self::rotate_validators(&validators);
411 (validators, true)
412 } else {
413 (ValidatorsAndKeys::<T>::get(), false)
414 };
415
416 T::SessionManager::start_session(session_index);
417 Self::deposit_event(Event::NewSession { session_index });
418 T::SessionHandler::on_new_session::<T::Keys>(changed, validators.as_ref(), &[]);
420 }
421
422 pub fn disable_index(i: u32) -> bool {
424 if i >= Validators::<T>::decode_len().unwrap_or(0) as u32 {
425 return false;
426 }
427
428 <DisabledValidators<T>>::mutate(|disabled| {
429 if let Err(index) = disabled.binary_search(&i) {
430 disabled.insert(index, i);
431 T::SessionHandler::on_disabled(i);
432 return true;
433 }
434
435 false
436 })
437 }
438
439 pub fn disable(c: &T::ValidatorId) -> bool {
445 ValidatorsAndKeys::<T>::get()
446 .iter()
447 .position(|(i, _)| i == c)
448 .map(|i| Self::disable_index(i as u32))
449 .unwrap_or(false)
450 }
451
452 pub fn acc_ids(validators: &[(T::ValidatorId, T::Keys)]) -> Vec<T::AccountId> {
453 validators.iter().map(|(v_id, _)| v_id.clone().into()).collect()
454 }
455 fn inc_provider(account: &T::AccountId) {
456 frame_system::Pallet::<T>::inc_providers(account);
457 }
458
459 fn dec_provider(account: &T::AccountId) -> Result<DecRefStatus, DispatchError> {
460 frame_system::Pallet::<T>::dec_providers(account)
461 }
462
463 fn change_account_providers(new_ids: &[T::AccountId], old_ids: &[T::AccountId]) {
464 let new_ids = BTreeSet::from_iter(new_ids);
465 let old_ids = BTreeSet::from_iter(old_ids);
466 let to_inc = new_ids.difference(&old_ids);
467 let to_dec = old_ids.difference(&new_ids);
468 for account in to_inc {
469 Self::inc_provider(account);
470 }
471 for account in to_dec {
472 Self::dec_provider(account).expect(
473 "We always match dec_providers with corresponding inc_providers, thus it cannot fail",
474 );
475 }
476 }
477
478 fn rotate_validators(new_validators: &ValidatorAndKeysList<T>) {
479 ValidatorsAndKeys::<T>::put(new_validators);
480 #[cfg(feature = "polkadot-js-compat")]
481 {
482 let validator_ids: Vec<_> = new_validators.iter().cloned().map(|v| v.0).collect();
483 Validators::<T>::put(validator_ids);
486 }
487 Self::change_account_providers(
488 &Self::acc_ids(new_validators),
489 &Self::acc_ids(&ValidatorsAndKeys::<T>::get()),
490 );
491 <DisabledValidators<T>>::take();
492 }
493
494 pub fn new_session(index: SessionIndex) -> Option<ValidatorAndKeysList<T>> {
495 let validators = T::SessionManager::new_session(index)?;
496 Some(validators)
497 }
498
499 pub fn new_session_genesis(index: SessionIndex) -> Option<ValidatorAndKeysList<T>> {
500 let validators = T::SessionManager::new_session_genesis(index)?;
501 Some(validators)
502 }
503}
504
505impl<T: Config> ValidatorRegistration<T::ValidatorId> for Pallet<T> {
506 fn is_registered(id: &T::ValidatorId) -> bool {
507 ValidatorsAndKeys::<T>::get().iter().any(|(vid, _)| vid == id)
508 }
509}
510
511impl<T: Config> EstimateNextNewSession<BlockNumberFor<T>> for Pallet<T> {
512 fn average_session_length() -> BlockNumberFor<T> {
513 T::NextSessionRotation::average_session_length()
514 }
515
516 fn estimate_next_new_session(now: BlockNumberFor<T>) -> (Option<BlockNumberFor<T>>, Weight) {
519 T::NextSessionRotation::estimate_next_session_rotation(now)
520 }
521}
522
523impl<T: Config> frame_support::traits::DisabledValidators for Pallet<T> {
524 fn is_disabled(index: u32) -> bool {
525 DisabledValidators::<T>::get().binary_search(&index).is_ok()
526 }
527
528 fn disabled_validators() -> Vec<u32> {
529 DisabledValidators::<T>::get()
530 }
531}
532
533pub struct FindAccountFromAuthorIndex<T, Inner>(core::marker::PhantomData<(T, Inner)>);
537
538impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
539 for FindAccountFromAuthorIndex<T, Inner>
540{
541 fn find_author<'a, I>(digests: I) -> Option<T::ValidatorId>
542 where
543 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
544 {
545 let i = Inner::find_author(digests)?;
546
547 let validators = ValidatorsAndKeys::<T>::get();
548 validators.get(i as usize).cloned().map(|validator_and_key| validator_and_key.0)
549 }
550}