use core::marker::PhantomData;
use crate::{
types::*, AuthorAffidavits, Config, CurrentSession,
Error, Internals, Pallet,
};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use frame_suite::{blockchain::*, roles::RoleActivity};
use sp_runtime::{
traits::{Convert, One},
DispatchError,
};
#[derive(
Encode, Decode, Clone, Copy, Eq, PartialEq, TypeInfo, MaxEncodedLen, DecodeWithMemTracking,
)]
#[scale_info(skip_type_params(T))]
pub enum AuthorActivity<T: Config> {
SessionValidator,
ElectionCandidate,
ElectionWinner,
Indeterminate(PhantomData<T>),
}
impl<T: Config> Into<DispatchError> for AuthorActivity<T> {
fn into(self) -> DispatchError {
match self {
AuthorActivity::SessionValidator => Error::<T>::ActivelyValidating.into(),
AuthorActivity::ElectionCandidate => Error::<T>::ActivelyContestingElection.into(),
AuthorActivity::ElectionWinner => Error::<T>::ActivelyWarmingForValidation.into(),
AuthorActivity::Indeterminate(_) => Error::<T>::CannotDetermineAuthorActiveDuty.into(),
}
}
}
impl<T: Config> core::fmt::Debug for AuthorActivity<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::SessionValidator => f.write_str("SessionValidator"),
Self::ElectionCandidate => f.write_str("ElectionCandidate"),
Self::ElectionWinner => f.write_str("ElectionWinner"),
Self::Indeterminate(_) => f.write_str("Indeterminate"),
}
}
}
impl<T: Config> RoleActivity<AuthorOf<T>, AuthorTimeStampOf<T>> for Pallet<T> {
type Activity = AuthorActivity<T>;
fn is_idle(who: &AuthorOf<T>) -> Result<(), AuthorActivity<T>> {
let Some(validator) =
<Pallet<T> as Convert<AuthorOf<T>, Option<SessionId<T>>>>::convert(who.clone())
else {
return Ok(());
};
if pallet_session::Pallet::<T>::validators().contains(&validator) {
return Err(AuthorActivity::<T>::SessionValidator);
}
let current_session = CurrentSession::<T>::get();
let next_session = current_session.saturating_add(One::one());
let Ok(aff_window) = Pallet::<T>::compute_affidavit_window() else {
return Err(AuthorActivity::Indeterminate(PhantomData));
};
let start_affidavit = aff_window.start;
let end_affidavit = aff_window.end;
let current_block = frame_system::Pallet::<T>::block_number();
if current_block < start_affidavit {
return Ok(());
}
if current_block < end_affidavit {
if AuthorAffidavits::<T>::contains_key((next_session, who)) {
return Err(AuthorActivity::ElectionCandidate);
}
}
if current_block > end_affidavit {
if let Some(elected) =
<Internals<T> as ElectAuthors<AuthorOf<T>, ElectionVia<T>>>::reveal()
{
for elect in elected.into_iter() {
if *who == elect {
return Err(AuthorActivity::ElectionWinner);
}
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::mock::*;
use frame_suite::roles::*;
use frame_support::{assert_err, assert_ok, traits::tokens::Fortitude};
#[test]
fn is_idle_ok_author_cannot_be_mapped_to_session_validator_id() {
chain_manager_test_ext().execute_with(|| {
set_default_user_balance_and_hold(ALICE).unwrap();
RoleAdapter::enroll(&ALICE, 1000, Fortitude::Force).unwrap();
assert_ok!(Pallet::is_idle(&BOB));
})
}
#[test]
fn is_idle_err_author_is_an_active_validator() {
chain_manager_test_ext().execute_with(|| {
set_session_config();
System::set_block_number(SESSION_START);
set_default_user_balance_and_hold(ALICE).unwrap();
set_default_user_balance_and_hold(BOB).unwrap();
set_default_user_balance_and_hold(CHARLIE).unwrap();
set_default_user_balance_and_hold(MIKE).unwrap();
set_default_user_balance_and_hold(ALAN).unwrap();
enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE]).unwrap();
direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
let alice_aff = aff_pairs[0].2.clone();
let bob_aff = aff_pairs[1].2.clone();
let charlie_aff = aff_pairs[2].2.clone();
System::set_block_number(AFDT_SUBMISSION_START);
submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
System::set_block_number(ELECTION_START);
let actual_elected = run_election_and_elect_authors(ALICE).unwrap();
let expected_elected = vec![BOB, ALICE, CHARLIE];
assert_eq!(actual_elected, expected_elected);
insert_into_validator_set(actual_elected).unwrap();
System::set_block_number(AFDT_SUBMISSION_END);
assert_err!(Pallet::is_idle(&BOB), AuthorActivity::SessionValidator);
})
}
#[test]
fn is_idle_ok_non_validating_author_idle_before_affidavit_window() {
chain_manager_test_ext().execute_with(|| {
set_session_config();
CurrentSession::put(0);
System::set_block_number(SESSION_START);
set_default_user_balance_and_hold(ALICE).unwrap();
set_default_user_balance_and_hold(BOB).unwrap();
set_default_user_balance_and_hold(CHARLIE).unwrap();
set_default_user_balance_and_hold(NIX).unwrap();
set_default_user_balance_and_hold(MIKE).unwrap();
set_default_user_balance_and_hold(ALAN).unwrap();
enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE, NIX]).unwrap();
direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
let alice_aff = aff_pairs[0].2.clone();
let bob_aff = aff_pairs[1].2.clone();
let charlie_aff = aff_pairs[2].2.clone();
System::set_block_number(AFDT_SUBMISSION_START);
submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
System::set_block_number(ELECTION_START);
let actual_elected = run_election_and_elect_authors(ALICE).unwrap();
let expected_elected = vec![BOB, ALICE, CHARLIE];
assert_eq!(actual_elected, expected_elected);
insert_into_validator_set(actual_elected).unwrap();
System::set_block_number(SESSION_END);
CurrentSession::put(1);
System::set_block_number(SESSION_END + SESSION_START);
assert_ok!(Pallet::is_idle(&NIX),);
})
}
#[test]
fn is_idle_err_author_submited_affidavit_and_participating_in_election() {
chain_manager_test_ext().execute_with(|| {
set_session_config();
CurrentSession::put(0);
System::set_block_number(SESSION_START);
set_default_user_balance_and_hold(ALICE).unwrap();
set_default_user_balance_and_hold(BOB).unwrap();
set_default_user_balance_and_hold(CHARLIE).unwrap();
set_default_user_balance_and_hold(NIX).unwrap();
set_default_user_balance_and_hold(MIKE).unwrap();
set_default_user_balance_and_hold(ALAN).unwrap();
enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE, NIX]).unwrap();
direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
let alice_aff = aff_pairs[0].2.clone();
let bob_aff = aff_pairs[1].2.clone();
let charlie_aff = aff_pairs[2].2.clone();
System::set_block_number(AFDT_SUBMISSION_START);
submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
assert_err!(Pallet::is_idle(&ALICE), AuthorActivity::ElectionCandidate);
})
}
#[test]
fn is_idle_err_author_elected_and_awaiting_the_next_validation_session() {
chain_manager_test_ext().execute_with(|| {
set_session_config();
System::set_block_number(SESSION_START);
set_default_user_balance_and_hold(ALICE).unwrap();
set_default_user_balance_and_hold(BOB).unwrap();
set_default_user_balance_and_hold(CHARLIE).unwrap();
set_default_user_balance_and_hold(MIKE).unwrap();
set_default_user_balance_and_hold(ALAN).unwrap();
enroll_authors_with_default_collateral(vec![ALICE, BOB, CHARLIE]).unwrap();
direct_fund_author(MIKE, ALICE, STANDARD_FUND).unwrap();
direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
let aff_pairs = insert_affidavit_keys_for_authors(vec![ALICE, BOB, CHARLIE], 1);
let alice_aff = aff_pairs[0].2.clone();
let bob_aff = aff_pairs[1].2.clone();
let charlie_aff = aff_pairs[2].2.clone();
System::set_block_number(AFDT_SUBMISSION_START);
submit_affidavit_for_authors(vec![alice_aff, bob_aff, charlie_aff]).unwrap();
System::set_block_number(ELECTION_START);
let actual_elected = run_election_and_elect_authors(ALICE).unwrap();
let expected_elected = vec![BOB, ALICE, CHARLIE];
assert_eq!(actual_elected, expected_elected);
System::set_block_number(AFDT_SUBMISSION_END + 1);
assert_err!(Pallet::is_idle(&ALICE), AuthorActivity::ElectionWinner);
})
}
}