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
43#![cfg_attr(not(feature = "std"), no_std)]
44
45use crate::CommitteeMember;
46use core::marker::PhantomData;
47use derive_new::new;
48use frame_system::pallet_prelude::BlockNumberFor;
49use log::info;
50use pallet_partner_chains_session::SessionIndex;
51use sp_std::vec::Vec;
52
53#[derive(new)]
54/// Session manager which takes committee from pallet_session_validator_management.
55pub struct ValidatorManagementSessionManager<T> {
56	_phantom: PhantomData<T>,
57}
58
59impl<T: crate::Config + pallet_session::Config>
60	pallet_partner_chains_session::SessionManager<T::AccountId, T::AuthorityKeys>
61	for ValidatorManagementSessionManager<T>
62{
63	fn new_session_genesis(
64		_new_index: SessionIndex,
65	) -> Option<Vec<(T::AccountId, T::AuthorityKeys)>> {
66		Some(
67			crate::Pallet::<T>::current_committee_storage()
68				.committee
69				.into_iter()
70				.map(|member| (member.authority_id().into(), member.authority_keys()))
71				.collect::<Vec<_>>(),
72		)
73	}
74
75	// Instead of Some((*).expect) we could just use (*). However, we rather panic in presence of
76	// important programming errors.
77	fn new_session(new_index: SessionIndex) -> Option<Vec<(T::AccountId, T::AuthorityKeys)>> {
78		info!("New session {new_index}");
79		pallet_session::pallet::CurrentIndex::<T>::put(new_index);
80		Some(
81			crate::Pallet::<T>::rotate_committee_to_next_epoch()
82				.expect(
83					"Session should never end without current epoch validators defined. \
84				Check ShouldEndSession implementation or if it is used before starting new session",
85				)
86				.into_iter()
87				.map(|member| (member.authority_id().into(), member.authority_keys()))
88				.collect(),
89		)
90	}
91
92	fn end_session(end_index: SessionIndex) {
93		info!("End session {end_index}");
94	}
95
96	// Session is expected to be at least 1 block behind sidechain epoch.
97	fn start_session(start_index: SessionIndex) {
98		let epoch_number = T::current_epoch_number();
99		info!("Start session {start_index}, epoch {epoch_number}");
100	}
101}
102
103/// This implementation tries to end each session in the first block of each sidechain epoch in which
104/// the committee for the epoch is defined.
105impl<T, ScEpochNumber> pallet_partner_chains_session::ShouldEndSession<BlockNumberFor<T>>
106	for ValidatorManagementSessionManager<T>
107where
108	T: crate::Config<ScEpochNumber = ScEpochNumber>,
109	ScEpochNumber: Clone + PartialOrd,
110{
111	fn should_end_session(_n: BlockNumberFor<T>) -> bool {
112		let current_epoch_number = T::current_epoch_number();
113
114		current_epoch_number > crate::Pallet::<T>::current_committee_storage().epoch
115			&& crate::Pallet::<T>::next_committee().is_some()
116	}
117}
118
119#[cfg(test)]
120mod tests {
121	use crate::mock::mock_pallet::CurrentEpoch;
122	use crate::mock::*;
123	use crate::session_manager::*;
124	use crate::*;
125	use pallet_partner_chains_session::ShouldEndSession;
126	pub const IRRELEVANT: u64 = 2;
127
128	type Manager = ValidatorManagementSessionManager<Test>;
129
130	#[test]
131	fn should_end_session_if_last_one_ended_late_and_new_committee_is_defined() {
132		let current_committee_epoch = 100;
133		let current_committee = ids_and_keys_fn(&[ALICE]);
134		let next_committee_epoch = 102;
135		let next_committee = ids_and_keys_fn(&[BOB]);
136
137		new_test_ext().execute_with(|| {
138			CurrentCommittee::<Test>::put(CommitteeInfo {
139				epoch: current_committee_epoch,
140				committee: current_committee,
141			});
142			CurrentEpoch::<Test>::set(current_committee_epoch + 2);
143			assert!(!Manager::should_end_session(IRRELEVANT));
144			NextCommittee::<Test>::put(CommitteeInfo {
145				epoch: next_committee_epoch,
146				committee: next_committee,
147			});
148			assert!(Manager::should_end_session(IRRELEVANT));
149		});
150	}
151}