use crate::{
types::*, AffidavitKeys, AllowAffidavits, AuthorAffidavits,
BlockPointsStore, Config, CurrentSession, Error, Event, Internals, Pallet,
};
use frame_suite::{blockchain::*, elections::*, roles::*};
use frame_support::{
ensure,
traits::{fungible::Inspect, tokens::Precision},
};
use sp_core::Get;
use sp_runtime::{
traits::{One, Saturating},
DispatchError, DispatchResult, Vec, WeakBoundedVec,
};
impl<T: Config> ElectAuthors<AuthorOf<T>, ElectionVia<T>> for Internals<T> {
type Candidates = ElectionParams<T>;
type Elected = ElectionElects<T>;
fn prepare_authors(candidates: Self::Candidates) -> DispatchResult {
T::ElectionAdapter::prepare(candidates)?;
Ok(())
}
fn can_process_election(_runner: &Option<AuthorOf<T>>) -> DispatchResult {
let aff_window = Pallet::<T>::compute_affidavit_window()?;
let start_affidavit = aff_window.start;
let end_affidavit = aff_window.end;
let invariant = start_affidavit < end_affidavit;
debug_assert!(
invariant,
"Affidavit submission period is invalid, starts at block {:?} and ends at {:?}",
start_affidavit, end_affidavit
);
ensure!(invariant, Error::<T>::InvalidAffidavitPeriod);
let current_block = frame_system::Pallet::<T>::block_number();
ensure!(
start_affidavit <= current_block,
Error::<T>::NotAffidavitPeriod
);
let election_window = Pallet::<T>::compute_election_window()?;
let start_election = election_window.start;
ensure!(
start_election <= current_block,
Error::<T>::NotElectionPeriod
);
ensure!(
current_block <= end_affidavit,
Error::<T>::ElectionPeriodEnded
);
Ok(())
}
fn prepare_candidates() -> Result<Self::Candidates, DispatchError> {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let iter = AuthorAffidavits::<T>::iter_prefix((for_session,));
let mut candidates = Self::Candidates::default();
for (author, (_, weights)) in iter {
let mut election_weights = ElectionVia::<T>::default();
for weight in weights.iter().cloned() {
election_weights.extend(core::iter::once(weight));
}
candidates.extend(core::iter::once((author, election_weights)));
}
Ok(candidates)
}
#[inline]
fn reveal() -> Option<Self::Elected> {
T::ElectionAdapter::reveal()
}
fn on_elect_success(runner: &Option<AuthorOf<T>>) {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let current_block = frame_system::Pallet::<T>::block_number();
let Some(runner) = runner else {
debug_assert!(
false,
"authors elected for session {:?} at
block {:?} but election runner unavailable",
for_session, current_block
);
return;
};
#[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
{
if T::EmitEvents::get() {
Pallet::<T>::deposit_event(Event::<T>::ElectedInstance {
session: for_session,
runner: runner.clone(),
});
}
}
#[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
{
if T::EmitEvents::get() {
let Some(elects) = Self::reveal() else {
debug_assert!(
false,
"authors elected for session {:?} at
block {:?} by election runner {:?},
but reveal unavailable",
runner, for_session, current_block
);
return;
};
Pallet::<T>::deposit_event(Event::<T>::ElectedInstance {
session: for_session,
runner: runner.clone(),
elects,
});
}
}
}
fn on_elect_fail(runner: &Option<AuthorOf<T>>, error: DispatchError) {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let Some(runner) = runner else {
let current_block = frame_system::Pallet::<T>::block_number();
debug_assert!(
false,
"authors elected for session {:?} at
block {:?} but election runner unavailable",
for_session, current_block
);
return;
};
if T::EmitEvents::get() {
Pallet::<T>::deposit_event(Event::<T>::ElectionAttemptFailed {
session: for_session,
runner: runner.clone(),
error,
});
}
}
}
impl<T: Config> AuthorPoints<AuthorOf<T>, T::Points> for Pallet<T> {
fn points_of(author: &AuthorOf<T>) -> Result<T::Points, DispatchError> {
let current_session = CurrentSession::<T>::get();
let points = BlockPointsStore::<T>::get((current_session, author))
.ok_or(Error::<T>::BlockPointsNotFound)?;
Ok(points)
}
fn clear_points() {}
fn set_points(author: &AuthorOf<T>, points: T::Points) -> DispatchResult {
let current_session = CurrentSession::<T>::get();
BlockPointsStore::<T>::insert((current_session, author), points);
Ok(())
}
fn iter_points() -> impl Iterator<Item = (AuthorOf<T>, T::Points)> {
let current_session = CurrentSession::<T>::get();
BlockPointsStore::<T>::iter_prefix((current_session,))
}
}
impl<T: Config> RewardAuthors<AuthorOf<T>, AssetOf<T>, T::Points> for Internals<T> {
type AuthorPointsAdapter = T::PointsAdapter;
type PayoutFor = PayoutFor<T>;
type PayoutContext = T::InflationContext;
type PayoutModel = T::InflationModel;
fn payout_via() -> AssetOf<T> {
if T::InflateViaSupply::get() {
return T::Asset::total_issuance().into();
}
let backing_stake = T::RoleAdapter::total_backing();
let collateral_stake = T::RoleAdapter::total_collateral();
backing_stake.saturating_add(collateral_stake)
}
type PayeeList = PayeeList<T>;
type PayeeContext = T::RewardContext;
type PayeeModel = T::RewardModel;
fn reward(who: &AuthorOf<T>, value: AssetOf<T>) -> DispatchResult {
T::RoleAdapter::reward(who, value, Precision::BestEffort)?;
Ok(())
}
fn payout_for() -> Self::PayoutFor {
let iter = Self::AuthorPointsAdapter::iter_points();
let mut payout_for = Self::PayoutFor::default();
for (author, points) in iter {
payout_for.extend(core::iter::once((author, points)));
}
payout_for
}
fn on_reward_success(who: &AuthorOf<T>, value: AssetOf<T>) {
if T::EmitEvents::get() {
Pallet::<T>::deposit_event(Event::RewardInitiated {
author: who.clone(),
value,
});
}
}
fn on_reward_fail(who: &AuthorOf<T>, error: DispatchError) {
if T::EmitEvents::get() {
Pallet::<T>::deposit_event(Event::RewardFailed {
author: who.clone(),
error,
});
}
}
}
impl<T: Config> PenalizeAuthors<AuthorOf<T>, PenaltyOf<T>> for Internals<T> {
type PenaltyFor = PenaltyFor<T>;
type PenaltyContext = T::PenaltyContext;
type PenaltyModel = T::PenaltyModel;
fn penalize(who: &AuthorOf<T>, penalty: PenaltyOf<T>) -> DispatchResult {
<T::RoleAdapter as CompensateRoles<AuthorOf<T>>>::penalize(who, penalty)?;
Ok(())
}
fn on_penalty_success(who: &AuthorOf<T>, penalty: PenaltyOf<T>) {
if T::EmitEvents::get() {
Pallet::<T>::deposit_event(Event::<T>::PenaltyInitiated {
author: who.clone(),
penalty,
});
}
}
fn on_penalty_fail(who: &AuthorOf<T>, error: DispatchError) {
if T::EmitEvents::get() {
Pallet::<T>::deposit_event(Event::<T>::PenaltyFailed {
author: who.clone(),
error,
});
}
}
}
impl<T: Config> ElectionAffidavits<AffidavitId<T>, ElectionVia<T>> for Pallet<T> {
fn can_submit_affidavit(who: &AffidavitId<T>) -> DispatchResult {
ensure!(
AllowAffidavits::<T>::get(),
Error::<T>::AffidavitsNotAllowed
);
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let Some(author) = AffidavitKeys::<T>::get((for_session, who)) else {
let try_next_session =
AffidavitKeys::<T>::contains_key((for_session.saturating_add(One::one()), who));
ensure!(
!try_next_session,
Error::<T>::DeclareDuringNextAffidavitSession
);
return Err(Error::<T>::AffidavitAuthorNotFound.into());
};
<T::RoleAdapter as RoleManager<AuthorOf<T>>>::is_available(&author)?;
let aff_window = Pallet::<T>::compute_affidavit_window()?;
let start_block = aff_window.start;
let end_block = aff_window.end;
let current_block = frame_system::Pallet::<T>::block_number();
ensure!(start_block <= current_block, Error::<T>::NotAffidavitPeriod);
ensure!(current_block <= end_block, Error::<T>::AffidavitPeriodEnded);
Ok(())
}
fn submit_affidavit(who: &AffidavitId<T>, affidavit: &ElectionVia<T>) -> DispatchResult {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let author = AffidavitKeys::<T>::get((for_session, who))
.ok_or(Error::<T>::AffidavitAuthorNotFound)?;
let current_block = frame_system::Pallet::<T>::block_number();
let mut try_affidavit: Vec<ElectionWeight<T>> = affidavit.clone().into_iter().collect();
let result = WeakBoundedVec::<ElectionWeight<T>, T::MaxAffidavitWeights>::try_from(
try_affidavit.clone(),
);
let actual_affidavit = match result {
Ok(v) => v,
Err(_) => {
try_affidavit.sort_by(|a, b| b.cmp(a));
WeakBoundedVec::<ElectionWeight<T>, T::MaxAffidavitWeights>::force_from(
try_affidavit,
None,
)
}
};
AuthorAffidavits::<T>::insert((for_session, author), (current_block, actual_affidavit));
Ok(())
}
fn gen_affidavit(who: &AffidavitId<T>) -> Result<ElectionVia<T>, DispatchError> {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let author = AffidavitKeys::<T>::get((for_session, who))
.ok_or(Error::<T>::AffidavitAuthorNotFound)?;
let weights =
<T::ElectionAdapter as InspectWeight<AuthorOf<T>, ElectionVia<T>>>::weight_of(&author)?;
Ok(weights.into())
}
fn remove_affidavit(who: &AffidavitId<T>) -> DispatchResult {
Self::affidavit_exists(who)?;
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let author = AffidavitKeys::<T>::get((for_session, who))
.ok_or(Error::<T>::AffidavitAuthorNotFound)?;
AuthorAffidavits::<T>::remove((for_session, author));
Ok(())
}
fn get_affidavit(who: &AffidavitId<T>) -> Result<ElectionVia<T>, DispatchError> {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let author = AffidavitKeys::<T>::get((for_session, who))
.ok_or(Error::<T>::AffidavitAuthorNotFound)?;
let (_, affidavit) = AuthorAffidavits::<T>::get((for_session, author))
.ok_or(Error::<T>::AffidavitNotFound)?;
Ok(affidavit.into_iter().collect())
}
fn affidavit_exists(who: &AffidavitId<T>) -> DispatchResult {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
let author = AffidavitKeys::<T>::get((for_session, who))
.ok_or(Error::<T>::AffidavitAuthorNotFound)?;
ensure!(
AuthorAffidavits::<T>::contains_key((for_session, author)),
Error::<T>::AffidavitNotFound
);
Ok(())
}
fn clear_affidavits() {}
fn on_submit_affidavit(who: &AffidavitId<T>, _affidavit: &ElectionVia<T>) {
if T::EmitEvents::get() {
let for_session = CurrentSession::<T>::get().saturating_add(One::one());
#[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
{
let Some(author) = AffidavitKeys::<T>::get((for_session, who)) else {
return;
};
let affidavit = _affidavit;
Self::deposit_event(Event::<T>::AffidavitSubmitted {
afdt_id: who.clone(),
session: for_session,
author,
affidavit: affidavit.clone(),
});
}
#[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
{
Self::deposit_event(Event::<T>::AffidavitSubmitted {
afdt_id: who.clone(),
session: for_session,
});
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{mock::*, types::Duration};
use frame_suite::{blockchain::*, roles::*};
use frame_support::{
assert_err, assert_ok,
traits::{
tokens::{Fortitude, Precision},
EstimateNextSessionRotation,
},
};
use sp_runtime::WeakBoundedVec;
use std::vec;
#[test]
fn prepare_authors_success() {
chain_manager_test_ext().execute_with(|| {
let candidates = vec![
(ALICE, vec![(Funder::Direct(CHARLIE), 30)]),
(BOB, vec![(Funder::Direct(ALAN), 60)]),
(MIKE, vec![(Funder::Direct(NIX), 20)]),
];
System::set_block_number(6);
assert_ok!(Internals::prepare_authors(candidates));
let recent_elected = RecentElectedOn::get();
assert_eq!(recent_elected, 6);
assert_eq!(Elected::get((recent_elected, ALICE)), Some(()));
assert_eq!(Elected::get((recent_elected, BOB)), Some(()));
assert_eq!(Elected::get((recent_elected, MIKE)), Some(()));
})
}
#[test]
fn can_process_election_success() {
chain_manager_test_ext().execute_with(|| {
System::set_block_number(10);
let avg_session_len: BlockNumber = NextSessionRotation::average_session_length();
assert_eq!(avg_session_len, 600);
SessionStartsAt::put(15);
AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
let aff_begin_at = AffidavitBeginsAt::get();
assert_eq!(aff_begin_at, Duration::from_rational(2u32, 10u32));
AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
let aff_ends_at = AffidavitEndsAt::get();
assert_eq!(aff_ends_at, Duration::from_rational(8u32, 10u32));
ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
let election_bgn_at = ElectionBeginsAt::get();
assert_eq!(election_bgn_at, Duration::from_rational(5u32, 10u32));
System::set_block_number(134);
assert_err!(
Internals::can_process_election(&Some(ALICE)),
Error::NotAffidavitPeriod
);
System::set_block_number(314);
assert_err!(
Internals::can_process_election(&Some(ALICE)),
Error::NotElectionPeriod
);
System::set_block_number(315);
assert_ok!(Internals::can_process_election(&Some(ALICE)));
System::set_block_number(496);
assert_err!(
Internals::can_process_election(&Some(ALICE)),
Error::ElectionPeriodEnded
);
})
}
#[test]
#[should_panic]
fn can_process_election_panic_invalid_affidavit_period() {
chain_manager_test_ext().execute_with(|| {
SessionStartsAt::put(1);
AffidavitBeginsAt::put(Duration::from_rational(5u32, 10u32));
AffidavitEndsAt::put(Duration::from_rational(2u32, 10u32));
Internals::can_process_election(&Some(ALICE)).unwrap();
})
}
#[test]
fn prepare_candidates_success() {
chain_manager_test_ext().execute_with(|| {
set_session(1);
let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
set_default_users_balance_and_hold(users).unwrap();
let authors = vec![ALICE, BOB, MIKE];
enroll_authors_with_default_collateral(authors).unwrap();
direct_fund_author(CHARLIE, ALICE, STANDARD_FUND).unwrap();
direct_fund_author(ALAN, BOB, SMALL_FUND).unwrap();
direct_fund_author(NIX, MIKE, STANDARD_FUND).unwrap();
AffidavitKeys::insert((2, AFFIDAVIT_KEY_A), ALICE);
AffidavitKeys::insert((2, AFFIDAVIT_KEY_B), BOB);
AffidavitKeys::insert((2, AFFIDAVIT_KEY_C), MIKE);
let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
let candidates = Internals::prepare_candidates().unwrap();
let expected_candidates = vec![
(BOB, vec![(Funder::Direct(ALAN), SMALL_FUND)]),
(MIKE, vec![(Funder::Direct(NIX), STANDARD_FUND)]),
(ALICE, vec![(Funder::Direct(CHARLIE), STANDARD_FUND)]),
];
assert_eq!(candidates, expected_candidates);
})
}
#[test]
fn reveal_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&BOB, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&MIKE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&BOB,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&MIKE,
&Funder::Direct(NIX),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
AffidavitKeys::insert((1, AFFIDAVIT_KEY_B), BOB);
AffidavitKeys::insert((1, AFFIDAVIT_KEY_C), MIKE);
let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
let candidates = Internals::prepare_candidates().unwrap();
Internals::prepare_authors(candidates).unwrap();
let reveal = Internals::reveal().unwrap();
let expected_reveal = vec![BOB, MIKE, ALICE];
assert_eq!(reveal, expected_reveal);
})
}
#[test]
fn prepare_election_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&BOB, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&MIKE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&BOB,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&MIKE,
&Funder::Direct(NIX),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
System::set_block_number(10);
SessionStartsAt::put(15);
AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
System::set_block_number(15);
System::set_block_number(135);
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
AffidavitKeys::insert((1, AFFIDAVIT_KEY_B), BOB);
AffidavitKeys::insert((1, AFFIDAVIT_KEY_C), MIKE);
let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
System::set_block_number(315);
assert_ok!(Internals::prepare_election(&Some(ALICE)));
let reveal = Internals::reveal().unwrap();
let expected_reveal = vec![BOB, MIKE, ALICE];
assert_eq!(reveal, expected_reveal);
})
}
#[test]
fn points_of_success() {
chain_manager_test_ext().execute_with(|| {
set_default_user_balance_and_hold(ALICE).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
CurrentSession::put(1);
assert_err!(Pallet::points_of(&ALICE), Error::BlockPointsNotFound);
Pallet::add_point(&ALICE).unwrap();
let current_points = Pallet::points_of(&ALICE).unwrap();
assert_eq!(current_points, 1);
Pallet::add_point(&ALICE).unwrap();
Pallet::add_point(&ALICE).unwrap();
let current_points = Pallet::points_of(&ALICE).unwrap();
assert_eq!(current_points, 3);
Pallet::add_point(&ALICE).unwrap();
let current_points = Pallet::points_of(&ALICE).unwrap();
assert_eq!(current_points, 4);
})
}
#[test]
fn add_point_success() {
chain_manager_test_ext().execute_with(|| {
set_default_user_balance_and_hold(ALICE).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
CurrentSession::put(1);
assert!(PointsAdapter::points_of(&ALICE).is_err());
assert_ok!(Pallet::add_point(&ALICE));
let current_points = PointsAdapter::points_of(&ALICE).unwrap();
assert_eq!(current_points, 1);
assert_ok!(Pallet::add_point(&ALICE));
assert_ok!(Pallet::add_point(&ALICE));
let current_points = PointsAdapter::points_of(&ALICE).unwrap();
assert_eq!(current_points, 3);
})
}
#[test]
fn payout_via_returns_total_locked_stake_when_inflate_via_supply_is_disabled() {
chain_manager_test_ext().execute_with(|| {
let authors = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(authors).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
let payout = Internals::payout_via();
assert_eq!(payout, 575);
})
}
#[test]
fn reward_success() {
chain_manager_test_ext().execute_with(|| {
set_default_user_balance_and_hold(ALICE).unwrap();
System::set_block_number(5);
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
System::set_block_number(16);
assert_ok!(Internals::reward(&ALICE, 25));
let rewards_of = RoleAdapter::get_rewards_of(&ALICE).unwrap();
let expected_rewards_of = vec![(18, 25)];
assert_eq!(rewards_of, expected_rewards_of);
})
}
#[test]
fn payout_for_success() {
chain_manager_test_ext().execute_with(|| {
let authors = vec![ALICE, CHARLIE, BOB];
set_default_users_balance_and_hold(authors).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&CHARLIE, 100, Fortitude::Force).unwrap();
RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
CurrentSession::put(1);
Pallet::add_point(&ALICE).unwrap();
Pallet::add_point(&CHARLIE).unwrap();
Pallet::add_point(&BOB).unwrap();
Pallet::add_point(&BOB).unwrap();
let payout_for = Internals::payout_for();
let expected_payout_for = vec![(BOB, 2), (ALICE, 1), (CHARLIE, 1)];
assert_eq!(payout_for, expected_payout_for);
})
}
#[test]
fn payout_success() {
chain_manager_test_ext().execute_with(|| {
let authors = vec![ALICE, BOB, ALAN, MIKE];
set_default_users_balance_and_hold(authors).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&BOB,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
let payout = Internals::payout();
assert_eq!(payout, 100);
})
}
#[test]
fn reward_authors_success() {
chain_manager_test_ext().execute_with(|| {
let authors = vec![ALICE, BOB, ALAN, MIKE];
set_default_users_balance_and_hold(authors).unwrap();
System::set_block_number(5);
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&BOB,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
Pallet::add_point(&ALICE).unwrap();
Pallet::add_point(&ALICE).unwrap();
Pallet::add_point(&BOB).unwrap();
Pallet::add_point(&BOB).unwrap();
Pallet::add_point(&BOB).unwrap();
Pallet::add_point(&ALICE).unwrap();
Pallet::add_point(&ALICE).unwrap();
Pallet::add_point(&ALICE).unwrap();
System::set_block_number(16);
Internals::reward_authors();
let rewards_of_alice_id = RoleAdapter::get_rewards_of(&ALICE).unwrap();
let rewards_of_bob_id = RoleAdapter::get_rewards_of(&BOB).unwrap();
let expected_alice_id_rewards = vec![(18, 62)];
let expected_bob_id_rewards = vec![(18, 38)];
assert_eq!(rewards_of_alice_id, expected_alice_id_rewards);
assert_eq!(rewards_of_bob_id, expected_bob_id_rewards);
})
}
#[test]
fn penalize_success() {
chain_manager_test_ext().execute_with(|| {
set_default_user_balance_and_hold(ALICE).unwrap();
System::set_block_number(5);
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
System::set_block_number(16);
assert_ok!(Internals::penalize(&ALICE, PenaltyRatio::from_percent(5)));
let penalties_of = RoleAdapter::get_penalties_of(&ALICE).unwrap();
let expected_penalties_of = vec![(20, PenaltyRatio::from_percent(5))];
assert_eq!(penalties_of, expected_penalties_of);
})
}
#[test]
fn transform_penalty_success() {
chain_manager_test_ext().execute_with(|| {
let penalty_for = vec![
(ALICE, PenaltyRatio::from_percent(10)),
(MIKE, PenaltyRatio::from_percent(70)),
(BOB, PenaltyRatio::from_percent(90)),
(CHARLIE, PenaltyRatio::from_percent(80)),
];
let tran_penalty_for = Internals::transform_penalty(penalty_for);
let expected_tran = vec![
(ALICE, PenaltyRatio::from_percent(10)),
(MIKE, PenaltyRatio::from_percent(70)),
(BOB, PenaltyRatio::from_percent(70)),
(CHARLIE, PenaltyRatio::from_percent(70)),
];
assert_eq!(tran_penalty_for, expected_tran);
})
}
#[test]
fn penalize_authors_success() {
chain_manager_test_ext().execute_with(|| {
set_default_user_balance_and_hold(ALICE).unwrap();
set_default_user_balance_and_hold(BOB).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
System::set_block_number(16);
let penalty_for = vec![
(ALICE, PenaltyRatio::from_percent(25)),
(BOB, PenaltyRatio::from_percent(72)),
];
Internals::penalize_authors(penalty_for);
let penalties_of_alice_id = RoleAdapter::get_penalties_of(&ALICE).unwrap();
let expected_penalties_of_alice_id = vec![(20, PenaltyRatio::from_percent(25))];
assert_eq!(penalties_of_alice_id, expected_penalties_of_alice_id);
let penalties_of_bob_id = RoleAdapter::get_penalties_of(&BOB).unwrap();
let expected_penalties_of_bob_id = vec![(20, PenaltyRatio::from_percent(70))];
assert_eq!(penalties_of_bob_id, expected_penalties_of_bob_id);
})
}
#[test]
fn can_submit_affidait_success() {
let mut env = new_ocw_env();
env.ext.execute_with(|| {
set_session_config();
set_default_user_balance_and_hold(ALICE).unwrap();
let afdt_pub = generate_affidavit_id();
enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
ext_validate(ALICE, afdt_pub.clone()).unwrap();
System::set_block_number(AFDT_SUBMISSION_START - 1);
assert_err!(
Pallet::can_submit_affidavit(&afdt_pub),
Error::NotAffidavitPeriod
);
System::set_block_number(AFDT_SUBMISSION_START);
assert_ok!(Pallet::can_submit_affidavit(&afdt_pub));
System::set_block_number(AFDT_SUBMISSION_END + 1);
assert_err!(
Pallet::can_submit_affidavit(&afdt_pub),
Error::AffidavitPeriodEnded
);
})
}
#[test]
fn can_submit_affidait_err_affidavit_author_not_found() {
chain_manager_test_ext().execute_with(|| {
System::set_block_number(10);
SessionStartsAt::put(15);
AllowAffidavits::put(true);
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let avg_session_len: BlockNumber = NextSessionRotation::average_session_length();
assert_eq!(avg_session_len, 600);
AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
System::set_block_number(135);
assert_err!(
Pallet::can_submit_affidavit(&AFFIDAVIT_KEY_B),
Error::AffidavitAuthorNotFound
);
})
}
#[test]
fn gen_affidavit_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let election_via = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
let expected_affidavit =
vec![(Funder::Direct(ALAN), 150), (Funder::Direct(CHARLIE), 100)];
assert_eq!(election_via, expected_affidavit);
})
}
#[test]
fn gen_affidavit_err_affidavit_author_not_found() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
assert_err!(
Pallet::gen_affidavit(&AFFIDAVIT_KEY_B),
Error::AffidavitAuthorNotFound
);
})
}
#[test]
fn submit_affidavit_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
assert_ok!(Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit));
let author_affidavit = AuthorOfAffidavits::get((1, ALICE)).unwrap();
let vec = WeakBoundedVec::try_from(vec![
(Funder::Direct(MIKE), 125),
(Funder::Direct(ALAN), 150),
(Funder::Direct(CHARLIE), 100),
])
.unwrap();
let expected_affidavit = (10, vec);
assert_eq!(author_affidavit, expected_affidavit);
})
}
#[test]
fn get_affidavit_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
let actual_affidavit = Pallet::get_affidavit(&AFFIDAVIT_KEY_A).unwrap();
let expected_affidavit = vec![
(Funder::Direct(MIKE), 125),
(Funder::Direct(ALAN), 150),
(Funder::Direct(CHARLIE), 100),
];
assert_eq!(actual_affidavit, expected_affidavit);
})
}
#[test]
fn get_affidavit_err_affidavit_author_not_found() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
assert_err!(
Pallet::get_affidavit(&AFFIDAVIT_KEY_B),
Error::AffidavitAuthorNotFound
);
})
}
#[test]
fn get_affidavit_err_affidavit_not_found() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
System::set_block_number(10);
assert_err!(
Pallet::get_affidavit(&AFFIDAVIT_KEY_A),
Error::AffidavitNotFound
);
})
}
#[test]
fn affidavit_exists_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
assert_ok!(Pallet::affidavit_exists(&AFFIDAVIT_KEY_A),);
})
}
#[test]
fn affidavit_exists_err_affidavit_author_not_found() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
assert_err!(
Pallet::affidavit_exists(&AFFIDAVIT_KEY_B),
Error::AffidavitAuthorNotFound
);
})
}
#[test]
fn affidavit_exists_err_affidavit_not_found() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
System::set_block_number(10);
assert_err!(
Pallet::affidavit_exists(&AFFIDAVIT_KEY_A),
Error::AffidavitNotFound
);
})
}
#[test]
fn remove_affidavit_success() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
let actual_affidavit = AuthorOfAffidavits::get((1, ALICE));
assert!(actual_affidavit.is_some());
assert_ok!(Pallet::remove_affidavit(&AFFIDAVIT_KEY_A));
assert_eq!(AuthorOfAffidavits::get((1, ALICE)), None);
})
}
#[test]
fn remove_affidavit_err_affidavit_author_not_found() {
chain_manager_test_ext().execute_with(|| {
let users = vec![ALICE, CHARLIE, ALAN, MIKE];
set_default_users_balance_and_hold(users).unwrap();
RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(CHARLIE),
100,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(ALAN),
150,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
RoleAdapter::fund(
&ALICE,
&Funder::Direct(MIKE),
125,
Precision::Exact,
Fortitude::Force,
)
.unwrap();
AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
System::set_block_number(10);
Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
assert_err!(
Pallet::remove_affidavit(&AFFIDAVIT_KEY_B),
Error::AffidavitAuthorNotFound
);
})
}
}