pallet_session_validator_management/
pallet_session_support.rs

1//! Implements Substrate's [pallet_session].
2//!
3//! This implementation has lag of one additional PC epoch when applying committees to sessions.
4use crate::CommitteeMember;
5use core::marker::PhantomData;
6use derive_new::new;
7use frame_system::pallet_prelude::BlockNumberFor;
8use log::{debug, warn};
9use pallet_partner_chains_session::SessionIndex;
10use sp_std::vec::Vec;
11
12/// Implements [pallet_session::SessionManager] and [pallet_session::ShouldEndSession] integrated with [crate::Pallet].
13///
14/// To use it, wire it in runtime configuration of [`pallet_session`].
15#[allow(dead_code)]
16#[derive(new)]
17pub struct PalletSessionSupport<T> {
18	_phantom: PhantomData<T>,
19}
20
21impl<T: crate::Config + pallet_session::Config> pallet_session::SessionManager<T::AccountId>
22	for PalletSessionSupport<T>
23{
24	/// Sets the first validator-set by mapping the current committee from [crate::Pallet]
25	fn new_session_genesis(_new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
26		Some(
27			crate::Pallet::<T>::current_committee_storage()
28				.committee
29				.into_iter()
30				.map(|member| member.authority_id().into())
31				.collect::<Vec<_>>(),
32		)
33	}
34
35	/// Rotates the committee in [crate::Pallet] and plans this new committee as upcoming validator-set.
36	/// Updates the session index of [`pallet_session`].
37	// Instead of Some((*).expect) we could just use (*). However, we rather panic in presence of important programming errors.
38	fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
39		debug!("PalletSessionSupport: New session {new_index}");
40		pallet_session::pallet::CurrentIndex::<T>::put(new_index);
41		Some(
42			crate::Pallet::<T>::rotate_committee_to_next_epoch()
43				.expect(
44					"PalletSessionSupport: Session should never end without current epoch validators defined. This may be caused by ShouldEndSession invalid behavior or being called before starting new session",
45				).into_iter().map(|member| member.authority_id().into()).collect::<Vec<_>>(),
46		)
47	}
48
49	fn end_session(end_index: SessionIndex) {
50		debug!("PalletSessionSupport: End session {end_index}");
51	}
52
53	// Session is expected to be at least 1 block behind sidechain epoch.
54	fn start_session(start_index: SessionIndex) {
55		let epoch_number = T::current_epoch_number();
56		debug!("PalletSessionSupport: Start session {start_index}, epoch {epoch_number}");
57	}
58}
59
60/// Tries to end each session in the first block of each partner chains epoch in which the committee for the epoch is defined.
61impl<T, EpochNumber> pallet_session::ShouldEndSession<BlockNumberFor<T>> for PalletSessionSupport<T>
62where
63	T: crate::Config<ScEpochNumber = EpochNumber>,
64	EpochNumber: Clone + PartialOrd,
65{
66	fn should_end_session(n: BlockNumberFor<T>) -> bool {
67		let current_epoch_number = T::current_epoch_number();
68		let current_committee_epoch = crate::Pallet::<T>::current_committee_storage().epoch;
69		let next_committee_is_defined = crate::Pallet::<T>::next_committee().is_some();
70		if current_epoch_number > current_committee_epoch {
71			if next_committee_is_defined {
72				debug!("PalletSessionSupport: should_end_session({n:?}) = true");
73				true
74			} else {
75				warn!(
76					"PalletSessionSupport: should_end_session({n:?}) 'current epoch' > 'committee epoch' but the next committee is not defined"
77				);
78				false
79			}
80		} else {
81			debug!("PalletSessionSupport: should_end_session({n:?}) = false");
82			false
83		}
84	}
85}
86
87#[cfg(test)]
88mod tests {
89	use super::*;
90	use crate::{
91		CommitteeInfo, CurrentCommittee, NextCommittee, mock::mock_pallet::CurrentEpoch, mock::*,
92	};
93	use pallet_session::ShouldEndSession;
94	pub const IRRELEVANT: u64 = 2;
95
96	type Manager = PalletSessionSupport<Test>;
97
98	#[test]
99	fn should_end_session_if_last_one_ended_late_and_new_committee_is_defined() {
100		let current_committee_epoch = 100;
101		let current_committee = ids_and_keys_fn(&[ALICE]);
102		let next_committee_epoch = 102;
103		let next_committee = ids_and_keys_fn(&[BOB]);
104
105		new_test_ext().execute_with(|| {
106			CurrentCommittee::<Test>::put(CommitteeInfo {
107				epoch: current_committee_epoch,
108				committee: current_committee,
109			});
110			CurrentEpoch::<Test>::set(current_committee_epoch + 2);
111			assert!(!Manager::should_end_session(IRRELEVANT));
112			NextCommittee::<Test>::put(CommitteeInfo {
113				epoch: next_committee_epoch,
114				committee: next_committee,
115			});
116			assert!(Manager::should_end_session(IRRELEVANT));
117		});
118	}
119}