1use core::marker::PhantomData;
27
28use crate::{
30 types::*, AuthorAffidavits, Config, CurrentSession,
31 Error, Internals, Pallet,
32};
33
34use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
36use scale_info::TypeInfo;
37
38use frame_suite::{blockchain::*, roles::RoleActivity};
40
41use sp_runtime::{
43 traits::{Convert, One},
44 DispatchError,
45};
46
47#[derive(
69 Encode, Decode, Clone, Copy, Eq, PartialEq, TypeInfo, MaxEncodedLen, DecodeWithMemTracking,
70)]
71#[scale_info(skip_type_params(T))]
72pub enum AuthorActivity<T: Config> {
73 SessionValidator,
75
76 ElectionCandidate,
79
80 ElectionWinner,
83
84 Indeterminate(PhantomData<T>),
87}
88
89impl<T: Config> Into<DispatchError> for AuthorActivity<T> {
90 fn into(self) -> DispatchError {
91 match self {
92 AuthorActivity::SessionValidator => Error::<T>::ActivelyValidating.into(),
93 AuthorActivity::ElectionCandidate => Error::<T>::ActivelyContestingElection.into(),
94 AuthorActivity::ElectionWinner => Error::<T>::ActivelyWarmingForValidation.into(),
95 AuthorActivity::Indeterminate(_) => Error::<T>::CannotDetermineAuthorActiveDuty.into(),
96 }
97 }
98}
99
100impl<T: Config> core::fmt::Debug for AuthorActivity<T> {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 match self {
103 Self::SessionValidator => f.write_str("SessionValidator"),
104 Self::ElectionCandidate => f.write_str("ElectionCandidate"),
105 Self::ElectionWinner => f.write_str("ElectionWinner"),
106 Self::Indeterminate(_) => f.write_str("Indeterminate"),
107 }
108 }
109}
110
111impl<T: Config> RoleActivity<AuthorOf<T>, AuthorTimeStampOf<T>> for Pallet<T> {
129 type Activity = AuthorActivity<T>;
131
132 fn is_idle(who: &AuthorOf<T>) -> Result<(), AuthorActivity<T>> {
138 let Some(validator) =
141 <Pallet<T> as Convert<AuthorOf<T>, Option<SessionId<T>>>>::convert(who.clone())
142 else {
143 return Ok(());
144 };
145
146 if pallet_session::Pallet::<T>::validators().contains(&validator) {
148 return Err(AuthorActivity::<T>::SessionValidator);
149 }
150
151 let current_session = CurrentSession::<T>::get();
152 let next_session = current_session.saturating_add(One::one());
153
154 let Ok(aff_window) = Pallet::<T>::compute_affidavit_window() else {
156 return Err(AuthorActivity::Indeterminate(PhantomData));
157 };
158 let start_affidavit = aff_window.start;
159 let end_affidavit = aff_window.end;
160
161 let current_block = frame_system::Pallet::<T>::block_number();
162
163 if current_block < start_affidavit {
165 return Ok(());
166 }
167
168 if current_block < end_affidavit {
171 if AuthorAffidavits::<T>::contains_key((next_session, who)) {
172 return Err(AuthorActivity::ElectionCandidate);
173 }
174 }
175
176 if current_block > end_affidavit {
179 if let Some(elected) =
180 <Internals<T> as ElectAuthors<AuthorOf<T>, ElectionVia<T>>>::reveal()
181 {
182 for elect in elected.into_iter() {
183 if *who == elect {
184 return Err(AuthorActivity::ElectionWinner);
185 }
186 }
187 }
188 }
189
190 Ok(())
191 }
192}
193
194#[cfg(test)]
199mod tests {
200
201 use crate::mock::*;
207
208 use frame_suite::roles::*;
210
211 use frame_support::{assert_err, assert_ok, traits::tokens::Fortitude};
213
214 #[test]
219 fn is_idle_ok_author_cannot_be_mapped_to_session_validator_id() {
220 chain_manager_test_ext().execute_with(|| {
221 set_default_user_balance_and_hold(ALICE).unwrap();
222 RoleAdapter::enroll(&ALICE, 1000, Fortitude::Force).unwrap();
223
224 assert_ok!(Pallet::is_idle(&BOB));
225 })
226 }
227
228 #[test]
229 fn is_idle_err_author_is_an_active_validator() {
230 chain_manager_test_ext().execute_with(|| {
231 set_session_config();
232 System::set_block_number(SESSION_START);
233 set_default_user_balance_and_hold(ALICE).unwrap();
234 set_default_user_balance_and_hold(BOB).unwrap();
235 set_default_user_balance_and_hold(CHARLIE).unwrap();
236 set_default_user_balance_and_hold(MIKE).unwrap();
237 set_default_user_balance_and_hold(ALAN).unwrap();
238
239 enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE]).unwrap();
240
241 direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
242 direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
243
244 let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
245 let alice_aff = aff_pairs[0].2.clone();
246 let bob_aff = aff_pairs[1].2.clone();
247 let charlie_aff = aff_pairs[2].2.clone();
248
249 System::set_block_number(AFDT_SUBMISSION_START);
250 submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
251
252 System::set_block_number(ELECTION_START);
253 let actual_elected = run_election_and_elect_authors(ALICE).unwrap();
254
255 let expected_elected = vec![BOB, ALICE, CHARLIE];
256 assert_eq!(actual_elected, expected_elected);
257 insert_into_validator_set(actual_elected).unwrap();
258
259 System::set_block_number(AFDT_SUBMISSION_END);
260 assert_err!(Pallet::is_idle(&BOB), AuthorActivity::SessionValidator);
261 })
262 }
263
264 #[test]
265 fn is_idle_ok_non_validating_author_idle_before_affidavit_window() {
266 chain_manager_test_ext().execute_with(|| {
267 set_session_config();
268 CurrentSession::put(0);
269 System::set_block_number(SESSION_START);
270 set_default_user_balance_and_hold(ALICE).unwrap();
271 set_default_user_balance_and_hold(BOB).unwrap();
272 set_default_user_balance_and_hold(CHARLIE).unwrap();
273 set_default_user_balance_and_hold(NIX).unwrap();
274
275 set_default_user_balance_and_hold(MIKE).unwrap();
276 set_default_user_balance_and_hold(ALAN).unwrap();
277
278 enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE, NIX]).unwrap();
279
280 direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
281 direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
282
283 let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
284 let alice_aff = aff_pairs[0].2.clone();
285 let bob_aff = aff_pairs[1].2.clone();
286 let charlie_aff = aff_pairs[2].2.clone();
287
288 System::set_block_number(AFDT_SUBMISSION_START);
289 submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
290
291 System::set_block_number(ELECTION_START);
292 let actual_elected = run_election_and_elect_authors(ALICE).unwrap();
293
294 let expected_elected = vec![BOB, ALICE, CHARLIE];
295 assert_eq!(actual_elected, expected_elected);
296 insert_into_validator_set(actual_elected).unwrap();
297
298 System::set_block_number(SESSION_END);
299
300 CurrentSession::put(1);
301 System::set_block_number(SESSION_END + SESSION_START);
302
303 assert_ok!(Pallet::is_idle(&NIX),);
304 })
305 }
306
307 #[test]
308 fn is_idle_err_author_submited_affidavit_and_participating_in_election() {
309 chain_manager_test_ext().execute_with(|| {
310 set_session_config();
311 CurrentSession::put(0);
312 System::set_block_number(SESSION_START);
313 set_default_user_balance_and_hold(ALICE).unwrap();
314 set_default_user_balance_and_hold(BOB).unwrap();
315 set_default_user_balance_and_hold(CHARLIE).unwrap();
316 set_default_user_balance_and_hold(NIX).unwrap();
317
318 set_default_user_balance_and_hold(MIKE).unwrap();
319 set_default_user_balance_and_hold(ALAN).unwrap();
320
321 enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE, NIX]).unwrap();
322
323 direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
324 direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
325
326 let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
327 let alice_aff = aff_pairs[0].2.clone();
328 let bob_aff = aff_pairs[1].2.clone();
329 let charlie_aff = aff_pairs[2].2.clone();
330
331 System::set_block_number(AFDT_SUBMISSION_START);
332 submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
333
334 assert_err!(Pallet::is_idle(&ALICE), AuthorActivity::ElectionCandidate);
335 })
336 }
337
338 #[test]
339 fn is_idle_err_author_elected_and_awaiting_the_next_validation_session() {
340 chain_manager_test_ext().execute_with(|| {
341 set_session_config();
342 System::set_block_number(SESSION_START);
343 set_default_user_balance_and_hold(ALICE).unwrap();
344 set_default_user_balance_and_hold(BOB).unwrap();
345 set_default_user_balance_and_hold(CHARLIE).unwrap();
346 set_default_user_balance_and_hold(MIKE).unwrap();
347 set_default_user_balance_and_hold(ALAN).unwrap();
348
349 enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE]).unwrap();
350
351 direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
352 direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
353
354 let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
355 let alice_aff = aff_pairs[0].2.clone();
356 let bob_aff = aff_pairs[1].2.clone();
357 let charlie_aff = aff_pairs[2].2.clone();
358
359 System::set_block_number(AFDT_SUBMISSION_START);
360 submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
361
362 System::set_block_number(ELECTION_START);
363 let actual_elected = run_election_and_elect_authors(ALICE).unwrap();
364
365 let expected_elected = vec![BOB, ALICE, CHARLIE];
366 assert_eq!(actual_elected, expected_elected);
367
368 System::set_block_number(AFDT_SUBMISSION_END + 1);
369 assert_err!(Pallet::is_idle(&ALICE), AuthorActivity::ElectionWinner);
370 })
371 }
372}