pallet_session_validator_management/
pallet_session_support.rs1use crate::{CommitteeMember, CommitteeRotationStage, CommitteeRotationStages};
7use frame_support::traits::UnfilteredDispatchable;
8use frame_system::RawOrigin;
9use frame_system::pallet_prelude::BlockNumberFor;
10use log::{debug, info, warn};
11use sp_staking::SessionIndex;
12use sp_std::collections::btree_set::BTreeSet;
13use sp_std::{vec, vec::Vec};
14
15impl<T: crate::Config + pallet_session::Config> pallet_session::SessionManager<T::AccountId>
16 for crate::Pallet<T>
17where
18 <T as pallet_session::Config>::Keys: From<T::AuthorityKeys>,
19{
20 fn new_session_genesis(_new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
22 Some(
23 crate::Pallet::<T>::current_committee_storage()
24 .committee
25 .into_iter()
26 .map(|member| member.authority_id().into())
27 .collect::<Vec<_>>(),
28 )
29 }
30
31 fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
35 if CommitteeRotationStage::<T>::get() == CommitteeRotationStages::AdditionalSession {
36 info!("💼 Session manager: new additional session {new_index}");
37 CommitteeRotationStage::<T>::put(CommitteeRotationStages::AwaitEpochChange);
38 let committee = crate::Pallet::<T>::current_committee_storage().committee;
39 return Some(committee.iter().map(|member| member.authority_id().into()).collect());
40 }
41
42 info!("💼 Session manager: new_session {new_index}, rotating the committee");
43 let new_committee = crate::Pallet::<T>::rotate_committee_to_next_epoch().expect(
44 "Session should never end without current epoch validators defined. \
45 Check ShouldEndSession implementation or if it is used before starting new session",
46 );
47
48 let old_committee_accounts = crate::ProvidedAccounts::<T>::take();
49 let mut new_committee_accounts: BTreeSet<T::AccountId> = BTreeSet::new();
50
51 for member in new_committee.iter() {
52 let account = member.authority_id().into();
53
54 if !new_committee_accounts.contains(&account) {
55 new_committee_accounts.insert(account.clone());
56
57 if !old_committee_accounts.contains(&account) {
59 setup_block_producer::<T>(account, member.authority_keys());
60 }
61 }
62 }
63
64 for account in old_committee_accounts.difference(&new_committee_accounts) {
65 teardown_block_producer::<T>(account)
66 }
67
68 crate::ProvidedAccounts::<T>::set(new_committee_accounts.clone().try_into().unwrap());
69
70 Some(new_committee.into_iter().map(|member| member.authority_id().into()).collect())
71 }
72
73 fn end_session(end_index: SessionIndex) {
74 debug!("Session manager: End session {end_index}");
75 }
76
77 fn start_session(start_index: SessionIndex) {
79 let epoch_number = T::current_epoch_number();
80 debug!("Session manager: Start session {start_index}, epoch {epoch_number}");
81 }
82}
83
84fn setup_block_producer<T: crate::Config + pallet_session::Config>(
86 account: T::AccountId,
87 keys: T::AuthorityKeys,
88) where
89 <T as pallet_session::Config>::Keys: From<T::AuthorityKeys>,
90{
91 log::debug!(
92 "➕💼 Incrementing provider count and registering keys for block producer {account:?}"
93 );
94
95 frame_system::Pallet::<T>::inc_providers(&account);
96
97 let set_keys_result = pallet_session::Call::<T>::set_keys { keys: keys.into(), proof: vec![] }
98 .dispatch_bypass_filter(RawOrigin::Signed(account.clone()).into());
99
100 match set_keys_result {
101 Ok(_) => debug!("set_keys for {account:?}"),
102 Err(e) => {
103 info!("Could not set_keys for {account:?}, error: {:?}", e.error)
104 },
105 }
106}
107
108fn teardown_block_producer<T: crate::Config + pallet_session::Config>(account: &T::AccountId)
110where
111 <T as pallet_session::Config>::Keys: From<T::AuthorityKeys>,
112{
113 let purge_keys_result = pallet_session::Call::<T>::purge_keys {}
114 .dispatch_bypass_filter(RawOrigin::Signed(account.clone()).into());
115 match purge_keys_result {
116 Ok(_) => debug!("purge_keys for {account:?}"),
117 Err(e) => info!("Could not purge_keys for {account:?}, error: {:?}", e.error),
118 }
119 log::info!(
120 "➖💼 Decrementing provider count and deregisteringkeys for block producer {account:?}"
121 );
122 frame_system::Pallet::<T>::dec_providers(&account).expect(
123 "We always match dec_providers with corresponding inc_providers, thus it cannot fail",
124 );
125}
126
127impl<T, EpochNumber> pallet_session::ShouldEndSession<BlockNumberFor<T>> for crate::Pallet<T>
129where
130 T: crate::Config<ScEpochNumber = EpochNumber>,
131 EpochNumber: Clone + PartialOrd,
132{
133 fn should_end_session(n: BlockNumberFor<T>) -> bool {
134 let current_epoch_number = T::current_epoch_number();
135 let current_committee_epoch = crate::Pallet::<T>::current_committee_storage().epoch;
136 let next_committee_is_defined = crate::Pallet::<T>::next_committee().is_some();
137 if current_epoch_number > current_committee_epoch {
138 if next_committee_is_defined {
139 info!("Session manager: should_end_session({n:?}) = true");
140 CommitteeRotationStage::<T>::put(CommitteeRotationStages::NewSessionDueEpochChange);
141 true
142 } else {
143 warn!(
144 "Session manager: should_end_session({n:?}) 'current epoch' > 'committee epoch' but the next committee is not defined"
145 );
146 false
147 }
148 } else {
149 let stage = CommitteeRotationStage::<T>::get();
150 if stage == CommitteeRotationStages::NewSessionDueEpochChange {
151 CommitteeRotationStage::<T>::put(CommitteeRotationStages::AdditionalSession);
152 info!("Session manager: should_end_session({n:?}) to force the new committee");
153 true
154 } else {
155 false
156 }
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use crate::{
164 CommitteeInfo, CurrentCommittee, NextCommittee,
165 mock::{mock_pallet::CurrentEpoch, *},
166 tests::increment_epoch,
167 };
168 use pallet_session::ShouldEndSession;
169 pub const IRRELEVANT: u64 = 2;
170 use sp_runtime::testing::UintAuthorityId;
171
172 type Manager = crate::Pallet<Test>;
173
174 #[test]
175 fn should_end_session_if_last_one_ended_late_and_new_committee_is_defined() {
176 let current_committee_epoch = 100;
177 let current_committee = ids_and_keys_fn(&[ALICE]);
178 let next_committee_epoch = 102;
179 let next_committee = ids_and_keys_fn(&[BOB]);
180
181 new_test_ext().execute_with(|| {
182 CurrentCommittee::<Test>::put(CommitteeInfo {
183 epoch: current_committee_epoch,
184 committee: current_committee,
185 });
186 CurrentEpoch::<Test>::set(current_committee_epoch + 2);
187 assert!(!Manager::should_end_session(IRRELEVANT));
188 NextCommittee::<Test>::put(CommitteeInfo {
189 epoch: next_committee_epoch,
190 committee: next_committee,
191 });
192 assert!(Manager::should_end_session(IRRELEVANT));
193 });
194 }
195
196 #[test]
197 fn register_session_keys_for_provided_authorities() {
198 new_test_ext().execute_with(|| {
199 set_validators_directly(&[DAVE, EVE], 1).unwrap();
200 assert_eq!(Session::load_keys(&DAVE.authority_id), None);
202 assert_eq!(Session::load_keys(&EVE.authority_id), None);
203 increment_epoch();
204
205 start_session(1);
206
207 assert_eq!(
209 Session::load_keys(&DAVE.authority_id),
210 Some(SessionKeys { foo: UintAuthorityId(DAVE.authority_keys) })
211 );
212 assert_eq!(
213 Session::load_keys(&EVE.authority_id),
214 Some(SessionKeys { foo: UintAuthorityId(EVE.authority_keys) })
215 );
216 });
217 }
218
219 #[test]
220 fn ends_two_sessions_and_rotates_once_when_committee_changes() {
221 new_test_ext().execute_with(|| {
222 assert_eq!(Session::current_index(), 0);
223 assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 0);
224 increment_epoch();
225 set_validators_directly(&[CHARLIE, DAVE], 1).unwrap();
226
227 advance_one_block();
228 assert_eq!(Session::current_index(), 1);
229 assert_eq!(Session::validators(), Vec::<u64>::new());
231 assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 1);
232
233 advance_one_block();
234 assert_eq!(Session::current_index(), 2);
235 assert_eq!(Session::validators(), vec![CHARLIE.authority_id, DAVE.authority_id]);
236 assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 1);
237
238 for _i in 0..10 {
239 advance_one_block();
240 assert_eq!(Session::current_index(), 2);
241 assert_eq!(Session::validators(), vec![CHARLIE.authority_id, DAVE.authority_id]);
242 assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 1);
243 }
244 });
245 }
246}