pallet_session_validator_management/
session_manager.rs

1//! Implements [pallet_partner_chains_session::SessionManager] and [pallet_partner_chains_session::ShouldEndSession],
2//! Partner Chain's version of Substrate's [pallet_session].
3//!
4//! To wire the [pallet_partner_chains_session] pallet, a stub version of [pallet_session] has to be wired first.
5//! The config for this should be generated with [a macro](pallet_partner_chains_session::impl_pallet_session_config) provided by this crate:
6//! ```rust, ignore
7//! pallet_partner_chains_session::impl_pallet_session_config!(Runtime);
8//! ```
9//! which expands to:
10//! ```rust, ignore
11//! impl pallet_session::Config for Runtime where Runtime: pallet_partner_chains_session::Config { /* ... */ }
12//! ```
13//!
14//! The partner chains session pallet has to be configured, for example:
15//! ```rust, ignore
16//! impl pallet_partner_chains_session::Config for Runtime {
17//! 	type RuntimeEvent = RuntimeEvent;
18//! 	type ValidatorId = <Self as frame_system::Config>::AccountId;
19//! 	type ShouldEndSession = ValidatorManagementSessionManager<Runtime>;
20//! 	type NextSessionRotation = ();
21//! 	type SessionManager = ValidatorManagementSessionManager<Runtime>;
22//! 	type SessionHandler = <opaque::SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
23//! 	type Keys = opaque::SessionKeys;
24//! }
25//! ```
26//!
27//! The order matters when wiring the pallets into the runtime!
28//! Partner Chains session_manager [ValidatorManagementSessionManager] writes to [pallet_session::pallet::CurrentIndex].
29//! [pallet_partner_chains_session] needs to come last for correct initialization order.
30//! [ValidatorManagementSessionManager] is wired in by [pallet_partner_chains_session].
31//!
32//! ```rust, ignore
33//! construct_runtime!(
34//! 	pub struct Runtime {
35//! 		// ...
36//! 		SubstrateSession: pallet_session,
37//! 		PcSession: pallet_partner_chains_session,
38//! 		// ...
39//! 	}
40//! );
41//! ```
42
43use crate::CommitteeMember;
44use core::marker::PhantomData;
45use derive_new::new;
46use frame_system::pallet_prelude::BlockNumberFor;
47use log::info;
48use pallet_partner_chains_session::SessionIndex;
49use sp_std::vec::Vec;
50
51#[derive(new)]
52/// Session manager which takes committee from pallet_session_validator_management.
53pub struct ValidatorManagementSessionManager<T> {
54	_phantom: PhantomData<T>,
55}
56
57impl<T: crate::Config + pallet_session::Config>
58	pallet_partner_chains_session::SessionManager<T::AccountId, T::AuthorityKeys>
59	for ValidatorManagementSessionManager<T>
60{
61	fn new_session_genesis(
62		_new_index: SessionIndex,
63	) -> Option<Vec<(T::AccountId, T::AuthorityKeys)>> {
64		Some(
65			crate::Pallet::<T>::current_committee_storage()
66				.committee
67				.into_iter()
68				.map(|member| (member.authority_id().into(), member.authority_keys()))
69				.collect::<Vec<_>>(),
70		)
71	}
72
73	// Instead of Some((*).expect) we could just use (*). However, we rather panic in presence of
74	// important programming errors.
75	fn new_session(new_index: SessionIndex) -> Option<Vec<(T::AccountId, T::AuthorityKeys)>> {
76		info!("New session {new_index}");
77		pallet_session::pallet::CurrentIndex::<T>::put(new_index);
78		Some(
79			crate::Pallet::<T>::rotate_committee_to_next_epoch()
80				.expect(
81					"Session should never end without current epoch validators defined. \
82				Check ShouldEndSession implementation or if it is used before starting new session",
83				)
84				.into_iter()
85				.map(|member| (member.authority_id().into(), member.authority_keys()))
86				.collect(),
87		)
88	}
89
90	fn end_session(end_index: SessionIndex) {
91		info!("End session {end_index}");
92	}
93
94	// Session is expected to be at least 1 block behind sidechain epoch.
95	fn start_session(start_index: SessionIndex) {
96		let epoch_number = T::current_epoch_number();
97		info!("Start session {start_index}, epoch {epoch_number}");
98	}
99}
100
101/// This implementation tries to end each session in the first block of each sidechain epoch in which
102/// the committee for the epoch is defined.
103impl<T, ScEpochNumber> pallet_partner_chains_session::ShouldEndSession<BlockNumberFor<T>>
104	for ValidatorManagementSessionManager<T>
105where
106	T: crate::Config<ScEpochNumber = ScEpochNumber>,
107	ScEpochNumber: Clone + PartialOrd,
108{
109	fn should_end_session(_n: BlockNumberFor<T>) -> bool {
110		let current_epoch_number = T::current_epoch_number();
111
112		current_epoch_number > crate::Pallet::<T>::current_committee_storage().epoch
113			&& crate::Pallet::<T>::next_committee().is_some()
114	}
115}
116
117#[cfg(test)]
118mod tests {
119	use crate::mock::mock_pallet::CurrentEpoch;
120	use crate::mock::*;
121	use crate::session_manager::*;
122	use crate::*;
123	use pallet_partner_chains_session::ShouldEndSession;
124	pub const IRRELEVANT: u64 = 2;
125
126	type Manager = ValidatorManagementSessionManager<Test>;
127
128	#[test]
129	fn should_end_session_if_last_one_ended_late_and_new_committee_is_defined() {
130		let current_committee_epoch = 100;
131		let current_committee = ids_and_keys_fn(&[ALICE]);
132		let next_committee_epoch = 102;
133		let next_committee = ids_and_keys_fn(&[BOB]);
134
135		new_test_ext().execute_with(|| {
136			CurrentCommittee::<Test>::put(CommitteeInfo {
137				epoch: current_committee_epoch,
138				committee: current_committee,
139			});
140			CurrentEpoch::<Test>::set(current_committee_epoch + 2);
141			assert!(!Manager::should_end_session(IRRELEVANT));
142			NextCommittee::<Test>::put(CommitteeInfo {
143				epoch: next_committee_epoch,
144				committee: next_committee,
145			});
146			assert!(Manager::should_end_session(IRRELEVANT));
147		});
148	}
149}