pallet_chain_manager/
blockchain.rs

1// SPDX-License-Identifier: MPL-2.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// `````````````````````````````` BLOCKCHAIN ACTORS ``````````````````````````````
14// ===============================================================================
15
16//! Provides the **core runtime logic for managing blockchain actors**
17//! (authors/validators) across their full lifecycle.
18//!
19//! - **Election orchestration** (via [`ElectAuthors`])
20//! - **Affidavit submission and validation** (via [`ElectionAffidavits`])
21//! - **Contribution tracking** through session-scoped points (via
22//!   [`AuthorPoints`], although swappable via [`Config::PointsAdapter`])
23//! - **Reward scheduling** based on participation (via [`RewardAuthors`])
24//! - **Penalty scheduling** for misbehavior (via [`PenalizeAuthors`])
25//!
26//! The module acts as a **bridge layer** between generic trait abstractions and
27//! pallet-specific storage, timing, and role-management systems.
28//!
29//! ## Design Overview
30//!
31//! - **Session-driven lifecycle**:
32//!   All operations (affidavits, elections, points, rewards, penalties)
33//!   are scoped to sessions and aligned with deterministic session timing.
34//!
35//! - **Time-gated execution**:
36//!   Affidavit submission and election processing are strictly bounded by
37//!   windows derived from session start and configurable percentages.
38//!
39//! - **Separation of concerns**:
40//!   - This module coordinates *when* and *what* to execute.
41//!   - External adapters/plugins define *how* logic is executed:
42//!     - Election logic: [`Config::ElectionAdapter`]
43//!     - Reward logic: [`Config::RewardModel`], [`Config::InflationModel`]
44//!     - Penalty logic: [`Config::PenaltyModel`]
45//!
46//! - **Deterministic and auditable**:
47//!   All operations avoid side effects and remain reproducible across nodes.
48//!
49//! - **Deferred execution model**:
50//!   Rewards and penalties are **scheduled**, not immediately finalized,
51//!   allowing downstream systems to aggregate, adjust, or revert them.
52
53// ===============================================================================
54// ``````````````````````````````````` IMPORTS ```````````````````````````````````
55// ===============================================================================
56
57// --- Local crate imports ---
58use crate::{
59    types::*, AffidavitKeys, AllowAffidavits, AuthorAffidavits,
60    BlockPointsStore, Config, CurrentSession, Error, Event, Internals, Pallet,
61};
62
63// --- FRAME Suite ---
64use frame_suite::{blockchain::*, elections::*, roles::*};
65
66// --- FRAME Support ---
67use frame_support::{
68    ensure,
69    traits::{fungible::Inspect, tokens::Precision},
70};
71
72// --- Substrate primitives ---
73use sp_core::Get;
74use sp_runtime::{
75    traits::{One, Saturating},
76    DispatchError, DispatchResult, Vec, WeakBoundedVec,
77};
78
79// ===============================================================================
80// ```````````````````````````````` ELECT AUTHORS ````````````````````````````````
81// ===============================================================================
82
83/// Implementation of the [`ElectAuthors`] trait for the pallet internal type
84/// (not exposable).
85///
86/// This implementation bridges the generic [`ElectAuthors`] abstraction
87/// with the pallet's internal affidavit and role-management infrastructure,
88/// coordinating **candidate selection**, **time-gated election execution**,
89/// and **result revelation** for upcoming sessions.
90///
91/// ## Design Notes
92/// - Elections are **session-scoped** and always target the *upcoming* session.
93/// - Only authors who have successfully submitted affidavits are eligible.
94/// - All election logic is **time-gated** and derived from session timing,
95///   affidavit windows, and election offsets.
96/// - This layer is **deterministic**; it does not perform probabilistic or
97///   stateful election logic.
98///
99/// ## Implementation Notes
100/// - This implementation does **not** execute the election algorithm itself.
101/// - All ranking, scoring, and selection logic is delegated to the configured
102///   [`ElectionManager`] via the pallet's [`Config::ElectionAdapter`].
103///
104/// - This layer is responsible only for:
105///   - Validating election timing
106///   - Preparing candidate inputs
107///   - Revealing results from the election manager
108impl<T: Config> ElectAuthors<AuthorOf<T>, ElectionVia<T>> for Internals<T> {
109    /// Type representing the prepared election candidates.
110    ///
111    /// Typically a vector of author's ID and their corresponding election weights.
112    type Candidates = ElectionParams<T>;
113
114    /// Type representing the final elected author set.
115    ///
116    /// Typically a vector of author IDs.
117    type Elected = ElectionElects<T>;
118
119    /// Prepares election candidates via the configured election manager.
120    ///
121    /// - Acts as a thin delegation layer to [`ElectionManager::prepare`].
122    /// - Any failure here prevents election execution.
123    /// - Typically runs the election algorithm and stores the election result.
124    /// - Inconsistencies return explicit errors.
125    fn prepare_authors(candidates: Self::Candidates) -> DispatchResult {
126        T::ElectionAdapter::prepare(candidates)?;
127        Ok(())
128    }
129
130    /// Checks whether the election can be processed at the current block.
131    ///
132    /// ## Parameters
133    /// - `runner`: Optional executor of the election (runtime or author-driven).
134    ///   This is **not validated here**, but is assumed to be the entity
135    ///   responsible for executing the election, as permitted by the caller.
136    ///
137    /// ## Validation
138    /// - Ensures the affidavit window is valid (`start < end`).
139    /// - Ensures the current block is within the affidavit window.
140    /// - Ensures the election window has started.
141    /// - Ensures the election has not yet ended (bounded by affidavit end).
142    ///
143    /// Violations return explicit, user-facing errors.
144    fn can_process_election(_runner: &Option<AuthorOf<T>>) -> DispatchResult {
145        // Compute affidavit submission window
146        let aff_window = Pallet::<T>::compute_affidavit_window()?;
147        let start_affidavit = aff_window.start;
148        let end_affidavit = aff_window.end;
149
150        // Validate affidavit window configuration
151        let invariant = start_affidavit < end_affidavit;
152        debug_assert!(
153            invariant,
154            "Affidavit submission period is invalid, starts at block {:?} and ends at {:?}",
155            start_affidavit, end_affidavit
156        );
157        ensure!(invariant, Error::<T>::InvalidAffidavitPeriod);
158
159        let current_block = frame_system::Pallet::<T>::block_number();
160
161        // Ensure affidavit window has begun
162        ensure!(
163            start_affidavit <= current_block,
164            Error::<T>::NotAffidavitPeriod
165        );
166
167        // Compute election start within affidavit window
168        let election_window = Pallet::<T>::compute_election_window()?;
169        let start_election = election_window.start;
170
171        // Ensure election has started
172        ensure!(
173            start_election <= current_block,
174            Error::<T>::NotElectionPeriod
175        );
176
177        // Ensure election has not ended
178        ensure!(
179            current_block <= end_affidavit,
180            Error::<T>::ElectionPeriodEnded
181        );
182
183        Ok(())
184    }
185
186    /// Prepares the final list of candidates for election.
187    ///
188    /// ## Overview
189    /// - Iterates all affidavits submitted for the upcoming session.
190    /// - Extracts and normalizes each author's election weights.
191    /// - Produces a deterministic candidate list for the election manager.
192    ///
193    /// ## Notes
194    /// - Only affidavit-submitting authors are included.
195    /// - This function performs **no ranking or filtering**.
196    /// - Ordering guarantees are provided by downstream election logic.
197    fn prepare_candidates() -> Result<Self::Candidates, DispatchError> {
198        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
199
200        // Iterate affidavits for the upcoming session
201        let iter = AuthorAffidavits::<T>::iter_prefix((for_session,));
202
203        let mut candidates = Self::Candidates::default();
204
205        for (author, (_, weights)) in iter {
206            let mut election_weights = ElectionVia::<T>::default();
207
208            for weight in weights.iter().cloned() {
209                election_weights.extend(core::iter::once(weight));
210            }
211
212            candidates.extend(core::iter::once((author, election_weights)));
213        }
214        Ok(candidates)
215    }
216
217    /// Reveals the elected authors from the underlying election manager.
218    ///
219    /// Acts as a thin delegation layer to [`ElectionManager::reveal`].
220    ///
221    /// ## Failure Semantics
222    /// This may return `None` if:
223    /// - The election was never executed
224    /// - Preparation failed
225    /// - Minimum candidate constraints were not met
226    ///
227    /// ## Caller Responsibility
228    /// Callers **must** handle the `None` case gracefully,
229    /// typically by retaining the previously elected author set.
230    #[inline]
231    fn reveal() -> Option<Self::Elected> {
232        T::ElectionAdapter::reveal()
233    }
234
235    /// Hook invoked after a successful election preparation.
236    ///
237    /// Emits a [`Event::ElectedInstance`] event if [`Config::EmitEvents`] is `true`.
238    fn on_elect_success(runner: &Option<AuthorOf<T>>) {
239        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
240        let current_block = frame_system::Pallet::<T>::block_number();
241        let Some(runner) = runner else {
242            debug_assert!(
243                false,
244                "authors elected for session {:?} at 
245                block {:?} but election runner unavailable",
246                for_session, current_block
247            );
248            return;
249        };
250
251        #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
252        {
253            if T::EmitEvents::get() {
254                Pallet::<T>::deposit_event(Event::<T>::ElectedInstance {
255                    session: for_session,
256                    runner: runner.clone(),
257                });
258            }
259        }
260
261        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
262        {
263            if T::EmitEvents::get() {
264                let Some(elects) = Self::reveal() else {
265                    debug_assert!(
266                        false,
267                        "authors elected for session {:?} at 
268                        block {:?} by election runner {:?}, 
269                        but reveal unavailable",
270                        runner, for_session, current_block
271                    );
272                    return;
273                };
274                Pallet::<T>::deposit_event(Event::<T>::ElectedInstance {
275                    session: for_session,
276                    runner: runner.clone(),
277                    elects,
278                });
279            }
280        }
281    }
282
283    /// Hook invoked when an election attempt fails.
284    ///
285    /// Emits a [`Event::ElectionAttemptFailed`] event if [`Config::EmitEvents`] is `true`.
286    fn on_elect_fail(runner: &Option<AuthorOf<T>>, error: DispatchError) {
287        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
288        let Some(runner) = runner else {
289            let current_block = frame_system::Pallet::<T>::block_number();
290            debug_assert!(
291                false,
292                "authors elected for session {:?} at 
293                block {:?} but election runner unavailable",
294                for_session, current_block
295            );
296            return;
297        };
298        if T::EmitEvents::get() {
299            Pallet::<T>::deposit_event(Event::<T>::ElectionAttemptFailed {
300                session: for_session,
301                runner: runner.clone(),
302                error,
303            });
304        }
305    }
306}
307
308// ===============================================================================
309// ```````````````````````````````` AUTHOR POINTS ````````````````````````````````
310// ===============================================================================
311
312/// Implementation of the [`AuthorPoints`] trait for the pallet.
313///
314/// This implementation provides a **session-scoped accounting layer**
315/// for tracking and querying abstract points accumulated by authors
316/// during active validation.
317///
318/// Points represent **good behaviour signals**, specifically
319/// **block production contributions**, and serve as inputs to downstream
320/// reward and incentive mechanisms.
321/// They are *not* assets themselves and carry no immediate economic value.
322///
323/// ## Design Notes
324/// - Points are **scoped per session** and never aggregated across sessions.
325/// - Accumulation is **monotonic** within a session.
326/// - Each point corresponds to a **unit of block production activity**.
327/// - Points are intentionally retained after session end for:
328///   - Auditability
329///   - Historical analysis
330///   - Deterministic reward calculation
331/// - This layer is **deterministic and side-effect minimal**.
332///
333/// ## Implementation Notes
334/// - This implementation does not perform reward distribution.
335/// - Economic interpretation of points is delegated to [`RewardAuthors`].
336/// - Clearing of points is intentionally unsupported at this layer.
337impl<T: Config> AuthorPoints<AuthorOf<T>, T::Points> for Pallet<T> {
338    /// Returns the total accumulated points for an author
339    /// in the **current session**.
340    ///
341    /// ## Semantics
342    /// - Points are accumulated incrementally during the session.
343    /// - Each point reflects a **block production contribution**.
344    /// - Calling this function **mid-session** returns a partial total.
345    /// - Calling this function at **session end** yields the final value
346    ///   used for reward calculation.
347    ///
348    /// ## Errors
349    /// - Returns `DispatchError` if the author has not accumulated
350    ///   any points in the current session.
351    fn points_of(author: &AuthorOf<T>) -> Result<T::Points, DispatchError> {
352        let current_session = CurrentSession::<T>::get();
353        let points = BlockPointsStore::<T>::get((current_session, author))
354            .ok_or(Error::<T>::BlockPointsNotFound)?;
355        Ok(points)
356    }
357
358    /// **No-op method** for clearing accumulated points.
359    ///
360    /// Point data is retained indefinitely to:
361    /// - Preserve full historical traceability
362    /// - Support deterministic audits
363    /// - Avoid accidental data loss before reward finalization
364    ///
365    /// Any future clearing, pruning, or archival must be performed
366    /// via explicit governance or maintenance extrinsics.
367    fn clear_points() {}
368
369    /// Sets the points for an author in the current session.
370    ///
371    /// ## Semantics
372    /// - Overwrites the existing points value for the author.
373    /// - Acts as the **primitive storage write** for point updates.
374    ///
375    /// ## Notes
376    /// - Typically used internally by higher-level operations such as
377    ///   [`Self::add_point`].
378    fn set_points(author: &AuthorOf<T>, points: T::Points) -> DispatchResult {
379        let current_session = CurrentSession::<T>::get();
380        BlockPointsStore::<T>::insert((current_session, author), points);
381        Ok(())
382    }
383
384    /// Returns an iterator over all authors and their accumulated points
385    /// for the **current session**.
386    ///
387    /// ## Semantics
388    /// - Provides a complete view of the session-scoped points state.
389    /// - Includes all authors who have accumulated at least one point.
390    /// - The iterator reflects the **current state** and may change as
391    ///   new points are added during the session.
392    ///
393    /// ## Usage
394    /// - Intended for runtime operations such as:
395    ///   - Reward computation
396    ///   - Ranking or selection
397    ///   - Performance evaluation
398    ///
399    /// ## Notes
400    /// - Any clearing, pruning, or archival is the responsibility of
401    ///   external logic (e.g., governance or maintenance extrinsics).
402    fn iter_points() -> impl Iterator<Item = (AuthorOf<T>, T::Points)> {
403        let current_session = CurrentSession::<T>::get();
404        BlockPointsStore::<T>::iter_prefix((current_session,))
405    }
406}
407
408// ===============================================================================
409// ```````````````````````````````` REWARD AUTHORS ```````````````````````````````
410// ===============================================================================
411
412/// Implementation of the [`RewardAuthors`] trait for the pallet internal type
413/// (not exposable).
414///
415/// This implementation bridges **abstract author points** with the
416/// protocol's **reward and inflation mechanisms**, translating
417/// session-scoped behavioural signals into scheduled economic rewards.
418///
419/// This layer does **not** mint, transfer, or finalize rewards directly.
420/// Instead, it provides deterministic inputs to downstream reward logic
421/// owned by the configured [`RoleManager`] adapters.
422///
423/// ## Design Notes
424/// - Rewards are derived from **session-scoped point accumulation**.
425/// - Points are interpreted as **relative behavioural weights**, not
426///   absolute reward amounts.
427/// - The payout context is configurable and may be based on:
428///   - Total token issuance (inflation-based) or,
429///   - Total backing + collateral stake (stake-weighted)
430/// - All reward operations must remain **deterministic, auditable,
431///   and reversible** until finalization.
432///
433/// ## Implementation Notes
434/// - This implementation does not compute reward shares.
435/// - Reward distribution logic is delegated to:
436///   - [`Config::InflationModel`]
437///   - [`Config::RewardModel`]
438///   - [`CompensateRoles`]
439/// - This layer only exposes:
440///   - The payout context
441///   - The eligible payee set
442///   - A scheduling hook for rewards
443impl<T: Config> RewardAuthors<AuthorOf<T>, AssetOf<T>, T::Points> for Internals<T> {
444    /// Adapter used to query accumulated author points.
445    type AuthorPointsAdapter = T::PointsAdapter;
446
447    /// Type representing authors eligible for payout and their points.
448    ///
449    /// Typically a vector of author's ID and their correspoinding points.
450    type PayoutFor = PayoutFor<T>;
451
452    /// Context used by the inflation plugin model.
453    type PayoutContext = T::InflationContext;
454
455    /// Inflation plugin model used to derive reward budgets.
456    type PayoutModel = T::InflationModel;
457
458    /// Returns the total asset context used to compute rewards.
459    ///
460    /// ## Semantics
461    /// Depending on configuration, this returns:
462    /// - Total token issuance (supply-based inflation), or
463    /// - Total backing + collateral stake (stake-weighted inflation)
464    ///
465    /// ## Notes
466    /// - This value represents the **upper bound** for reward calculation.
467    /// - It does not imply immediate minting or transfer.
468    fn payout_via() -> AssetOf<T> {
469        // Use total token issuance if inflation is supply-based.
470        if T::InflateViaSupply::get() {
471            return T::Asset::total_issuance().into();
472        }
473
474        // Otherwise, use total locked stake (backing + collateral).
475        let backing_stake = T::RoleAdapter::total_backing();
476        let collateral_stake = T::RoleAdapter::total_collateral();
477        backing_stake.saturating_add(collateral_stake)
478    }
479
480    /// Type representing the set of reward payees.
481    ///
482    /// Typically a vector of author's ID and their
483    /// correspoinding reward asset amount.
484    type PayeeList = PayeeList<T>;
485
486    /// Context supplied to the reward plugin model.
487    type PayeeContext = T::RewardContext;
488
489    /// Reward plugin model used to translate points into payouts.
490    type PayeeModel = T::RewardModel;
491
492    /// Schedules a reward for the given author.
493    ///
494    /// Acts as a thin delegation layer to [`CompensateRoles::reward`].
495    ///
496    /// ## Semantics
497    /// - This function **does not finalize** the reward.
498    /// - Rewards are scheduled with best-effort precision.
499    /// - Downstream logic may:
500    ///   - Aggregate
501    ///   - Adjust
502    ///   - Revert
503    ///   the scheduled reward before finalization.
504    ///
505    /// ## Errors
506    /// Returns a `DispatchError` if reward scheduling fails.
507    fn reward(who: &AuthorOf<T>, value: AssetOf<T>) -> DispatchResult {
508        T::RoleAdapter::reward(who, value, Precision::BestEffort)?;
509        Ok(())
510    }
511
512    /// Returns the set of authors eligible for payout and their
513    /// accumulated points for the current session.
514    ///
515    /// ## Notes
516    /// - This function is expected to be called **at session end**.
517    /// - Calling it earlier may yield partial or unstable results.
518    /// - The returned data is treated as immutable for reward computation.
519    fn payout_for() -> Self::PayoutFor {
520        let iter = Self::AuthorPointsAdapter::iter_points();
521        let mut payout_for = Self::PayoutFor::default();
522        for (author, points) in iter {
523            payout_for.extend(core::iter::once((author, points)));
524        }
525
526        payout_for
527    }
528
529    /// Hook invoked after a reward is successfully applied to an author.
530    ///
531    /// This hook emits the `Rewarded` event, reflecting the
532    /// distributed reward amount for the given author.
533    fn on_reward_success(who: &AuthorOf<T>, value: AssetOf<T>) {
534        if T::EmitEvents::get() {
535            Pallet::<T>::deposit_event(Event::RewardInitiated {
536                author: who.clone(),
537                value,
538            });
539        }
540    }
541
542    /// Hook invoked when applying a reward to an author fails.
543    ///
544    /// This hook emits the `RewardFailed` event, reflecting the
545    /// error that prevented the reward from being applied.
546    fn on_reward_fail(who: &AuthorOf<T>, error: DispatchError) {
547        if T::EmitEvents::get() {
548            Pallet::<T>::deposit_event(Event::RewardFailed {
549                author: who.clone(),
550                error,
551            });
552        }
553    }
554}
555
556// ===============================================================================
557// ``````````````````````````````` PENALIZE AUTHORS ``````````````````````````````
558// ===============================================================================
559
560/// Implementation of the [`PenalizeAuthors`] trait for the pallet internal type
561/// (not-exposable).
562///
563/// This implementation bridges **author offence signals** with the
564/// protocol's **penalty and slashing mechanisms**, enabling penalties
565/// to be **scheduled and processed** according to runtime-defined rules.
566///
567/// Penalties, like rewards, follow a **deferred enforcement model**.
568/// They are recorded and transformed first, then enforced later by
569/// downstream role and penalty management logic.
570///
571/// ## Design Notes
572/// - Penalties are **author-scoped** and apply to active roles.
573/// - Enforcement is **scheduled**, not immediate.
574/// - Multiple penalties may be:
575///   - Aggregated
576///   - Scaled
577///   - Capped
578///   - Reverted
579///   prior to final enforcement.
580/// - Penalty values are interpreted as **inputs**, not final amounts.
581/// - Transformation and enforcement are governed by runtime-configured
582///   penalty models for flexibility and governance control.
583///
584/// ## Implementation Notes
585/// - This layer does **not** detect offences or compute severity.
586/// - It does **not** finalize or immediately apply penalties.
587/// - All penalty logic is delegated to:
588///   - [`Config::PenaltyModel`]
589///   - [`CompensateRoles::penalize`]
590/// - This implementation guarantees deterministic, auditable scheduling
591///   of penalties without side effects.
592impl<T: Config> PenalizeAuthors<AuthorOf<T>, PenaltyOf<T>> for Internals<T> {
593    /// Mapping of authors to their applied penalties (percentage typically).
594    type PenaltyFor = PenaltyFor<T>;
595
596    /// Context provided to the penalty plugin model for transformation.
597    type PenaltyContext = T::PenaltyContext;
598
599    /// Plugin Model responsible for transforming raw penalties according to
600    /// runtime-defined rules (e.g. caps, scaling, thresholds).
601    type PenaltyModel = T::PenaltyModel;
602
603    /// Applies a penalty to the given author.
604    ///
605    /// Acts as a thin delegation layer to [`CompensateRoles::penalize`].
606    ///
607    /// ## Semantics
608    /// - Penalties are **scheduled**, not applied immediately.
609    /// - Downstream logic may:
610    ///   - Aggregate multiple penalties
611    ///   - Scale or cap penalties
612    ///   - Delay or revert enforcement prior to finalization
613    ///
614    /// ## Notes
615    /// - This function does not persist offence metadata.
616    /// - Offence detection and validation are the responsibility
617    ///   of the caller.
618    ///
619    /// ## Errors
620    /// Returns a `DispatchError` if penalty scheduling fails.
621    fn penalize(who: &AuthorOf<T>, penalty: PenaltyOf<T>) -> DispatchResult {
622        <T::RoleAdapter as CompensateRoles<AuthorOf<T>>>::penalize(who, penalty)?;
623        Ok(())
624    }
625
626    /// Hook invoked after a penalty is successfully applied to an author.
627    ///
628    /// This hook emits the `Penalized` event, reflecting the
629    /// penalty enforced against the author.
630    fn on_penalty_success(who: &AuthorOf<T>, penalty: PenaltyOf<T>) {
631        if T::EmitEvents::get() {
632            Pallet::<T>::deposit_event(Event::<T>::PenaltyInitiated {
633                author: who.clone(),
634                penalty,
635            });
636        }
637    }
638
639    /// Hook invoked when applying a penalty to an author fails.
640    ///
641    /// This hook emits the `PenaltyFailed` event, reflecting the
642    /// error that prevented the penalty from being applied.
643    fn on_penalty_fail(who: &AuthorOf<T>, error: DispatchError) {
644        if T::EmitEvents::get() {
645            Pallet::<T>::deposit_event(Event::<T>::PenaltyFailed {
646                author: who.clone(),
647                error,
648            });
649        }
650    }
651}
652
653// ===============================================================================
654// ````````````````````````````` ELECTION AFFIDAVITS `````````````````````````````
655// ===============================================================================
656
657/// Implementation of the [`ElectionAffidavits`] trait for the pallet.
658///
659/// This implementation bridges the generic [`ElectionAffidavits`] abstraction
660/// with the pallet's internal affidavit registry ([`AuthorAffidavits`] &
661/// [`AffidavitKeys`]), enabling authors to **self-report their election weights**
662/// for upcoming sessions.
663///
664/// ## Design Notes
665/// - **Affidavit submission** is only allowed when [`AllowAffidavits`] is enabled.
666/// - Affidavits are stored *per session*, not globally, ensuring clean rotation.
667/// - **Time gating** is enforced through [`AffidavitBeginsAt`](crate::AffidavitBeginsAt) and 
668/// [`AffidavitEndsAt`](crate::AffidavitEndsAt), relative to average session length.
669/// - Affidavit data is immutable within its session once the submission period ends.
670/// - All operations must remain audit-safe and deterministic.
671///
672/// ## Implementation Notes
673/// This bridge layer does not perform any ranking, scoring, or weighting logic.
674/// Those responsibilities remain with the [`ElectAuthors`] and [`ElectionManager`]
675/// implementations. The affidavit simply represents a **candidate's declaration**
676/// of intent and associated metrics for the next election round.
677impl<T: Config> ElectionAffidavits<AffidavitId<T>, ElectionVia<T>> for Pallet<T> {
678    /// Checks whether an author can submit an affidavit for the upcoming session-election.
679    ///
680    /// - The global [`AllowAffidavits`] flag is enabled.
681    /// - The current block is within the configured affidavit submission window.
682    ///
683    /// DispatchError otherwise
684    fn can_submit_affidavit(who: &AffidavitId<T>) -> DispatchResult {
685        // Check if Affidavit model is initiated
686        ensure!(
687            AllowAffidavits::<T>::get(),
688            Error::<T>::AffidavitsNotAllowed
689        );
690
691        // Check if the author exists for the affidavit key ID
692        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
693        let Some(author) = AffidavitKeys::<T>::get((for_session, who)) else {
694            let try_next_session =
695                AffidavitKeys::<T>::contains_key((for_session.saturating_add(One::one()), who));
696            ensure!(
697                !try_next_session,
698                Error::<T>::DeclareDuringNextAffidavitSession
699            );
700            return Err(Error::<T>::AffidavitAuthorNotFound.into());
701        };
702
703        <T::RoleAdapter as RoleManager<AuthorOf<T>>>::is_available(&author)?;
704
705        // Compute allowed submission window relative to session timing.
706        let aff_window = Pallet::<T>::compute_affidavit_window()?;
707        let start_block = aff_window.start;
708        let end_block = aff_window.end;
709
710        let current_block = frame_system::Pallet::<T>::block_number();
711
712        // Ensure affidavit period has started
713        ensure!(start_block <= current_block, Error::<T>::NotAffidavitPeriod);
714
715        // Ensure affidavit period has not ended
716        ensure!(current_block <= end_block, Error::<T>::AffidavitPeriodEnded);
717
718        Ok(())
719    }
720
721    /// Submits a new affidavit for the next session.
722    ///
723    /// Directly inserts the affidavit into storage for the upcoming session.
724    ///
725    /// ## Details
726    /// - Persists the affidavit under the next session's affidavits mapping.
727    /// - Overwrites any previously submitted affidavit for the same session.
728    /// - Each author can maintain **only one recent affidavit per future session**.
729    fn submit_affidavit(who: &AffidavitId<T>, affidavit: &ElectionVia<T>) -> DispatchResult {
730        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
731        let author = AffidavitKeys::<T>::get((for_session, who))
732            .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
733        let current_block = frame_system::Pallet::<T>::block_number();
734        let mut try_affidavit: Vec<ElectionWeight<T>> = affidavit.clone().into_iter().collect();
735        let result = WeakBoundedVec::<ElectionWeight<T>, T::MaxAffidavitWeights>::try_from(
736            try_affidavit.clone(),
737        );
738        let actual_affidavit = match result {
739            Ok(v) => v,
740            Err(_) => {
741                // Sort in descending order
742                try_affidavit.sort_by(|a, b| b.cmp(a));
743                WeakBoundedVec::<ElectionWeight<T>, T::MaxAffidavitWeights>::force_from(
744                    try_affidavit,
745                    None,
746                )
747            }
748        };
749        AuthorAffidavits::<T>::insert((for_session, author), (current_block, actual_affidavit));
750        Ok(())
751    }
752
753    /// Generates an affidavit dynamically for the given author's affidavit ID.
754    ///
755    /// ## Overview
756    /// - Inspects abstract weight via [`InspectWeight`] from [`Config::ElectionAdapter`].
757    /// - Produces an [`ElectionVia`] structure that represents the
758    ///   author's self-declared election weights.
759    ///
760    /// ## Returns
761    /// - `Ok(ElectionVia)` on success.
762    /// - DispatchError otherwise
763    fn gen_affidavit(who: &AffidavitId<T>) -> Result<ElectionVia<T>, DispatchError> {
764        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
765        let author = AffidavitKeys::<T>::get((for_session, who))
766            .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
767        let weights =
768            <T::ElectionAdapter as InspectWeight<AuthorOf<T>, ElectionVia<T>>>::weight_of(&author)?;
769        Ok(weights.into())
770    }
771
772    /// Removes an existing upcoming-election affidavit for the given author.
773    ///
774    /// ## Workflow
775    /// 1. Ensures the affidavit exists.
776    /// 2. Removes it from storage for the next session.
777    ///
778    /// ## Notes
779    /// - Used primarily when an author wishes to withdraw from election participation.
780    fn remove_affidavit(who: &AffidavitId<T>) -> DispatchResult {
781        Self::affidavit_exists(who)?;
782        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
783        let author = AffidavitKeys::<T>::get((for_session, who))
784            .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
785        AuthorAffidavits::<T>::remove((for_session, author));
786        Ok(())
787    }
788
789    /// Retrieves an affidavit for the given author for the next session's election.
790    ///
791    /// ## Returns
792    /// - The [`ElectionVia`] structure associated with the author.
793    /// - DispatchError if no affidavit is stored for the next session election.
794    fn get_affidavit(who: &AffidavitId<T>) -> Result<ElectionVia<T>, DispatchError> {
795        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
796        let author = AffidavitKeys::<T>::get((for_session, who))
797            .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
798        let (_, affidavit) = AuthorAffidavits::<T>::get((for_session, author))
799            .ok_or(Error::<T>::AffidavitNotFound)?;
800        Ok(affidavit.into_iter().collect())
801    }
802
803    /// Checks if an affidavit exists for the given author for the upcoming election.
804    ///
805    /// ## Returns
806    /// - `Ok(())` if the affidavit exists.
807    /// - DispatchError otherwise.
808    fn affidavit_exists(who: &AffidavitId<T>) -> DispatchResult {
809        let for_session = CurrentSession::<T>::get().saturating_add(One::one());
810        let author = AffidavitKeys::<T>::get((for_session, who))
811            .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
812        ensure!(
813            AuthorAffidavits::<T>::contains_key((for_session, author)),
814            Error::<T>::AffidavitNotFound
815        );
816        Ok(())
817    }
818
819    /// No-op method.
820    ///
821    /// This low-level implementation is intentionally left empty.
822    ///
823    /// Affidavit clearing is deferred to higher-level logic to:
824    /// - Preserve full **historical traceability**.
825    /// - Prevent accidental data loss before election finalization.
826    ///
827    /// ## Notes
828    /// - The pallet should query affidavits per session **only once**.
829    /// - Re-querying beyond this point can cause election inconsistencies.
830    /// - Reserved for potential audit or archival extensions.
831    fn clear_affidavits() {}
832
833    /// Hook invoked after a successful affidavit submission.
834    ///
835    /// This hook emits the `AffidavitSubmitted` event, reflecting
836    /// the submitted election weight for the author.
837    fn on_submit_affidavit(who: &AffidavitId<T>, _affidavit: &ElectionVia<T>) {
838        if T::EmitEvents::get() {
839            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
840            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
841            {
842                let Some(author) = AffidavitKeys::<T>::get((for_session, who)) else {
843                    return;
844                };
845                let affidavit = _affidavit;
846                Self::deposit_event(Event::<T>::AffidavitSubmitted {
847                    afdt_id: who.clone(),
848                    session: for_session,
849                    author,
850                    affidavit: affidavit.clone(),
851                });
852            }
853            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
854            {
855                Self::deposit_event(Event::<T>::AffidavitSubmitted {
856                    afdt_id: who.clone(),
857                    session: for_session,
858                });
859            }
860        }
861    }
862}
863
864// ===============================================================================
865// `````````````````````````````````` UNIT TESTS `````````````````````````````````
866// ===============================================================================
867
868#[cfg(test)]
869mod tests {
870
871    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
872    // ```````````````````````````````````` IMPORTS ``````````````````````````````````
873    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
874
875    // --- Local crate imports ---
876    use crate::{mock::*, types::Duration};
877
878    // --- FRAME Suite ---
879    use frame_suite::{blockchain::*, roles::*};
880
881    // --- FRAME Support ---
882    use frame_support::{
883        assert_err, assert_ok,
884        traits::{
885            tokens::{Fortitude, Precision},
886            EstimateNextSessionRotation,
887        },
888    };
889
890    // --- Substrate primitives ---
891    use sp_runtime::WeakBoundedVec;
892
893    // --- Std ---
894    use std::vec;
895
896    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
897    // ```````````````````````````````` ELECT AUTHORS ````````````````````````````````
898    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
899
900    #[test]
901    fn prepare_authors_success() {
902        chain_manager_test_ext().execute_with(|| {
903            let candidates = vec![
904                (ALICE, vec![(Funder::Direct(CHARLIE), 30)]),
905                (BOB, vec![(Funder::Direct(ALAN), 60)]),
906                (MIKE, vec![(Funder::Direct(NIX), 20)]),
907            ];
908
909            System::set_block_number(6);
910            assert_ok!(Internals::prepare_authors(candidates));
911
912            let recent_elected = RecentElectedOn::get();
913            assert_eq!(recent_elected, 6);
914            assert_eq!(Elected::get((recent_elected, ALICE)), Some(()));
915            assert_eq!(Elected::get((recent_elected, BOB)), Some(()));
916            assert_eq!(Elected::get((recent_elected, MIKE)), Some(()));
917        })
918    }
919
920    #[test]
921    fn can_process_election_success() {
922        chain_manager_test_ext().execute_with(|| {
923            System::set_block_number(10);
924            // Average session length = Period = 1 * HOURS = 600 blocks
925            let avg_session_len: BlockNumber = NextSessionRotation::average_session_length();
926            assert_eq!(avg_session_len, 600);
927            // Session is set to start at block 15
928            SessionStartsAt::put(15);
929            // Affidavit submission begins at 20% of session length
930            // 20% of 600 = 120 blocks
931            // 15 + 120 => 135th block
932            AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
933            let aff_begin_at = AffidavitBeginsAt::get();
934            assert_eq!(aff_begin_at, Duration::from_rational(2u32, 10u32));
935            // Affidavit submission ends at 80% of session length
936            // 80% of 600 = 480 blocks
937            // 15 + 480 => 495th block
938            AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
939            let aff_ends_at = AffidavitEndsAt::get();
940            assert_eq!(aff_ends_at, Duration::from_rational(8u32, 10u32));
941            // Election processing begins at 50% of the affidavit window
942            // Affidavit window length = 495 - 135 = 360 blocks
943            // 50% of 360 = 180 blocks
944            // 135 + 180 = 315th block
945            ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
946            let election_bgn_at = ElectionBeginsAt::get();
947            assert_eq!(election_bgn_at, Duration::from_rational(5u32, 10u32));
948            // Before affidavit submission window starts (block < 135)
949            System::set_block_number(134);
950            assert_err!(
951                Internals::can_process_election(&Some(ALICE)),
952                Error::NotAffidavitPeriod
953            );
954            // After affidavit window starts but before election window begins (block < 315)
955            System::set_block_number(314);
956            assert_err!(
957                Internals::can_process_election(&Some(ALICE)),
958                Error::NotElectionPeriod
959            );
960            // Election window has started (block >= 315 and <= 495)
961            System::set_block_number(315);
962            assert_ok!(Internals::can_process_election(&Some(ALICE)));
963            // After affidavit window has ended (block > 495)
964            System::set_block_number(496);
965            assert_err!(
966                Internals::can_process_election(&Some(ALICE)),
967                Error::ElectionPeriodEnded
968            );
969        })
970    }
971
972    #[test]
973    #[should_panic]
974    fn can_process_election_panic_invalid_affidavit_period() {
975        chain_manager_test_ext().execute_with(|| {
976            SessionStartsAt::put(1);
977            AffidavitBeginsAt::put(Duration::from_rational(5u32, 10u32));
978            AffidavitEndsAt::put(Duration::from_rational(2u32, 10u32));
979            Internals::can_process_election(&Some(ALICE)).unwrap();
980        })
981    }
982
983    #[test]
984    fn prepare_candidates_success() {
985        chain_manager_test_ext().execute_with(|| {
986            set_session(1);
987            let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
988            set_default_users_balance_and_hold(users).unwrap();
989            let authors = vec![ALICE, BOB, MIKE];
990            enroll_authors_with_default_collateral(authors).unwrap();
991
992            direct_fund_author(CHARLIE, ALICE, STANDARD_FUND).unwrap();
993            direct_fund_author(ALAN, BOB, SMALL_FUND).unwrap();
994            direct_fund_author(NIX, MIKE, STANDARD_FUND).unwrap();
995
996            AffidavitKeys::insert((2, AFFIDAVIT_KEY_A), ALICE);
997            AffidavitKeys::insert((2, AFFIDAVIT_KEY_B), BOB);
998            AffidavitKeys::insert((2, AFFIDAVIT_KEY_C), MIKE);
999
1000            let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1001            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
1002            let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
1003            Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
1004            let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
1005            Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
1006
1007            let candidates = Internals::prepare_candidates().unwrap();
1008            let expected_candidates = vec![
1009                (BOB, vec![(Funder::Direct(ALAN), SMALL_FUND)]),
1010                (MIKE, vec![(Funder::Direct(NIX), STANDARD_FUND)]),
1011                (ALICE, vec![(Funder::Direct(CHARLIE), STANDARD_FUND)]),
1012            ];
1013            assert_eq!(candidates, expected_candidates);
1014        })
1015    }
1016
1017    #[test]
1018    fn reveal_success() {
1019        chain_manager_test_ext().execute_with(|| {
1020            let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
1021            set_default_users_balance_and_hold(users).unwrap();
1022
1023            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1024            RoleAdapter::enroll(&BOB, 200, Fortitude::Force).unwrap();
1025            RoleAdapter::enroll(&MIKE, 200, Fortitude::Force).unwrap();
1026
1027            RoleAdapter::fund(
1028                &ALICE,
1029                &Funder::Direct(CHARLIE),
1030                100,
1031                Precision::Exact,
1032                Fortitude::Force,
1033            )
1034            .unwrap();
1035            RoleAdapter::fund(
1036                &BOB,
1037                &Funder::Direct(ALAN),
1038                150,
1039                Precision::Exact,
1040                Fortitude::Force,
1041            )
1042            .unwrap();
1043            RoleAdapter::fund(
1044                &MIKE,
1045                &Funder::Direct(NIX),
1046                125,
1047                Precision::Exact,
1048                Fortitude::Force,
1049            )
1050            .unwrap();
1051
1052            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1053            AffidavitKeys::insert((1, AFFIDAVIT_KEY_B), BOB);
1054            AffidavitKeys::insert((1, AFFIDAVIT_KEY_C), MIKE);
1055
1056            let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1057            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
1058            let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
1059            Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
1060            let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
1061            Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
1062
1063            let candidates = Internals::prepare_candidates().unwrap();
1064            Internals::prepare_authors(candidates).unwrap();
1065
1066            let reveal = Internals::reveal().unwrap();
1067            let expected_reveal = vec![BOB, MIKE, ALICE];
1068            assert_eq!(reveal, expected_reveal);
1069        })
1070    }
1071
1072    #[test]
1073    fn prepare_election_success() {
1074        chain_manager_test_ext().execute_with(|| {
1075            let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
1076            set_default_users_balance_and_hold(users).unwrap();
1077
1078            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1079            RoleAdapter::enroll(&BOB, 200, Fortitude::Force).unwrap();
1080            RoleAdapter::enroll(&MIKE, 200, Fortitude::Force).unwrap();
1081
1082            RoleAdapter::fund(
1083                &ALICE,
1084                &Funder::Direct(CHARLIE),
1085                100,
1086                Precision::Exact,
1087                Fortitude::Force,
1088            )
1089            .unwrap();
1090            RoleAdapter::fund(
1091                &BOB,
1092                &Funder::Direct(ALAN),
1093                150,
1094                Precision::Exact,
1095                Fortitude::Force,
1096            )
1097            .unwrap();
1098            RoleAdapter::fund(
1099                &MIKE,
1100                &Funder::Direct(NIX),
1101                125,
1102                Precision::Exact,
1103                Fortitude::Force,
1104            )
1105            .unwrap();
1106
1107            System::set_block_number(10);
1108            // Average session length = Period = 1 * HOURS = 600 blocks
1109            // Session is set to start at block 15
1110            SessionStartsAt::put(15);
1111            // Affidavit submission begins at 20% of session length
1112            AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
1113            // Affidavit submission ends at 80% of session length
1114            AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
1115            // Election processing begins at 50% of the affidavit window
1116            ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
1117
1118            System::set_block_number(15);
1119            System::set_block_number(135);
1120            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1121            AffidavitKeys::insert((1, AFFIDAVIT_KEY_B), BOB);
1122            AffidavitKeys::insert((1, AFFIDAVIT_KEY_C), MIKE);
1123
1124            let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1125            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
1126            let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
1127            Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
1128            let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
1129            Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
1130
1131            System::set_block_number(315);
1132            assert_ok!(Internals::prepare_election(&Some(ALICE)));
1133
1134            let reveal = Internals::reveal().unwrap();
1135            let expected_reveal = vec![BOB, MIKE, ALICE];
1136            assert_eq!(reveal, expected_reveal);
1137        })
1138    }
1139
1140    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1141    // ```````````````````````````````` AUTHOR POINTS ````````````````````````````````
1142    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1143
1144    #[test]
1145    fn points_of_success() {
1146        chain_manager_test_ext().execute_with(|| {
1147            set_default_user_balance_and_hold(ALICE).unwrap();
1148            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1149            CurrentSession::put(1);
1150            assert_err!(Pallet::points_of(&ALICE), Error::BlockPointsNotFound);
1151            Pallet::add_point(&ALICE).unwrap();
1152            let current_points = Pallet::points_of(&ALICE).unwrap();
1153            assert_eq!(current_points, 1);
1154            Pallet::add_point(&ALICE).unwrap();
1155            Pallet::add_point(&ALICE).unwrap();
1156            let current_points = Pallet::points_of(&ALICE).unwrap();
1157            assert_eq!(current_points, 3);
1158            Pallet::add_point(&ALICE).unwrap();
1159            let current_points = Pallet::points_of(&ALICE).unwrap();
1160            assert_eq!(current_points, 4);
1161        })
1162    }
1163
1164    #[test]
1165    fn add_point_success() {
1166        chain_manager_test_ext().execute_with(|| {
1167            set_default_user_balance_and_hold(ALICE).unwrap();
1168            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1169            CurrentSession::put(1);
1170            assert!(PointsAdapter::points_of(&ALICE).is_err());
1171            assert_ok!(Pallet::add_point(&ALICE));
1172            let current_points = PointsAdapter::points_of(&ALICE).unwrap();
1173            assert_eq!(current_points, 1);
1174            assert_ok!(Pallet::add_point(&ALICE));
1175            assert_ok!(Pallet::add_point(&ALICE));
1176
1177            let current_points = PointsAdapter::points_of(&ALICE).unwrap();
1178            assert_eq!(current_points, 3);
1179        })
1180    }
1181
1182    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1183    // ``````````````````````````````` REWARD AUTHORS ````````````````````````````````
1184    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1185
1186    #[test]
1187    fn payout_via_returns_total_locked_stake_when_inflate_via_supply_is_disabled() {
1188        chain_manager_test_ext().execute_with(|| {
1189            let authors = vec![ALICE, CHARLIE, ALAN, MIKE];
1190            set_default_users_balance_and_hold(authors).unwrap();
1191
1192            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1193            RoleAdapter::fund(
1194                &ALICE,
1195                &Funder::Direct(CHARLIE),
1196                100,
1197                Precision::Exact,
1198                Fortitude::Force,
1199            )
1200            .unwrap();
1201            RoleAdapter::fund(
1202                &ALICE,
1203                &Funder::Direct(ALAN),
1204                150,
1205                Precision::Exact,
1206                Fortitude::Force,
1207            )
1208            .unwrap();
1209            RoleAdapter::fund(
1210                &ALICE,
1211                &Funder::Direct(MIKE),
1212                125,
1213                Precision::Exact,
1214                Fortitude::Force,
1215            )
1216            .unwrap();
1217
1218            let payout = Internals::payout_via();
1219            assert_eq!(payout, 575);
1220        })
1221    }
1222
1223    #[test]
1224    fn reward_success() {
1225        chain_manager_test_ext().execute_with(|| {
1226            set_default_user_balance_and_hold(ALICE).unwrap();
1227            System::set_block_number(5);
1228            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1229
1230            System::set_block_number(16);
1231            assert_ok!(Internals::reward(&ALICE, 25));
1232
1233            // Reward of 25 units is scheduled at block 18
1234            let rewards_of = RoleAdapter::get_rewards_of(&ALICE).unwrap();
1235            let expected_rewards_of = vec![(18, 25)];
1236            assert_eq!(rewards_of, expected_rewards_of);
1237        })
1238    }
1239
1240    #[test]
1241    fn payout_for_success() {
1242        chain_manager_test_ext().execute_with(|| {
1243            let authors = vec![ALICE, CHARLIE, BOB];
1244            set_default_users_balance_and_hold(authors).unwrap();
1245
1246            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1247            RoleAdapter::enroll(&CHARLIE, 100, Fortitude::Force).unwrap();
1248            RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
1249            CurrentSession::put(1);
1250            Pallet::add_point(&ALICE).unwrap();
1251            Pallet::add_point(&CHARLIE).unwrap();
1252            Pallet::add_point(&BOB).unwrap();
1253            Pallet::add_point(&BOB).unwrap();
1254
1255            let payout_for = Internals::payout_for();
1256            let expected_payout_for = vec![(BOB, 2), (ALICE, 1), (CHARLIE, 1)];
1257            assert_eq!(payout_for, expected_payout_for);
1258        })
1259    }
1260
1261    #[test]
1262    fn payout_success() {
1263        chain_manager_test_ext().execute_with(|| {
1264            let authors = vec![ALICE, BOB, ALAN, MIKE];
1265            set_default_users_balance_and_hold(authors).unwrap();
1266
1267            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1268            RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
1269            RoleAdapter::fund(
1270                &ALICE,
1271                &Funder::Direct(ALAN),
1272                150,
1273                Precision::Exact,
1274                Fortitude::Force,
1275            )
1276            .unwrap();
1277            RoleAdapter::fund(
1278                &BOB,
1279                &Funder::Direct(MIKE),
1280                125,
1281                Precision::Exact,
1282                Fortitude::Force,
1283            )
1284            .unwrap();
1285
1286            let payout = Internals::payout();
1287            // Since, the configured InflationModel is `ConstantPayout`, which always returns the
1288            // statically configured reward value (100).
1289            assert_eq!(payout, 100);
1290        })
1291    }
1292
1293    #[test]
1294    fn reward_authors_success() {
1295        chain_manager_test_ext().execute_with(|| {
1296            let authors = vec![ALICE, BOB, ALAN, MIKE];
1297            set_default_users_balance_and_hold(authors).unwrap();
1298
1299            System::set_block_number(5);
1300            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1301            RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
1302            RoleAdapter::fund(
1303                &ALICE,
1304                &Funder::Direct(ALAN),
1305                150,
1306                Precision::Exact,
1307                Fortitude::Force,
1308            )
1309            .unwrap();
1310            RoleAdapter::fund(
1311                &BOB,
1312                &Funder::Direct(MIKE),
1313                125,
1314                Precision::Exact,
1315                Fortitude::Force,
1316            )
1317            .unwrap();
1318
1319            Pallet::add_point(&ALICE).unwrap();
1320            Pallet::add_point(&ALICE).unwrap();
1321            Pallet::add_point(&BOB).unwrap();
1322            Pallet::add_point(&BOB).unwrap();
1323            Pallet::add_point(&BOB).unwrap();
1324            Pallet::add_point(&ALICE).unwrap();
1325            Pallet::add_point(&ALICE).unwrap();
1326            Pallet::add_point(&ALICE).unwrap();
1327
1328            System::set_block_number(16);
1329            Internals::reward_authors();
1330
1331            let rewards_of_alice_id = RoleAdapter::get_rewards_of(&ALICE).unwrap();
1332            let rewards_of_bob_id = RoleAdapter::get_rewards_of(&BOB).unwrap();
1333
1334            let expected_alice_id_rewards = vec![(18, 62)];
1335            let expected_bob_id_rewards = vec![(18, 38)];
1336
1337            assert_eq!(rewards_of_alice_id, expected_alice_id_rewards);
1338            assert_eq!(rewards_of_bob_id, expected_bob_id_rewards);
1339        })
1340    }
1341
1342    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1343    // ``````````````````````````````` PENALIZE AUTHORS ``````````````````````````````
1344    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1345
1346    #[test]
1347    fn penalize_success() {
1348        chain_manager_test_ext().execute_with(|| {
1349            set_default_user_balance_and_hold(ALICE).unwrap();
1350
1351            System::set_block_number(5);
1352            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1353
1354            System::set_block_number(16);
1355            assert_ok!(Internals::penalize(&ALICE, PenaltyRatio::from_percent(5)));
1356
1357            // Penalty of 5% is scheduled at block 20
1358            let penalties_of = RoleAdapter::get_penalties_of(&ALICE).unwrap();
1359            let expected_penalties_of = vec![(20, PenaltyRatio::from_percent(5))];
1360            assert_eq!(penalties_of, expected_penalties_of);
1361        })
1362    }
1363
1364    #[test]
1365    fn transform_penalty_success() {
1366        chain_manager_test_ext().execute_with(|| {
1367            let penalty_for = vec![
1368                (ALICE, PenaltyRatio::from_percent(10)),
1369                (MIKE, PenaltyRatio::from_percent(70)),
1370                (BOB, PenaltyRatio::from_percent(90)),
1371                (CHARLIE, PenaltyRatio::from_percent(80)),
1372            ];
1373            let tran_penalty_for = Internals::transform_penalty(penalty_for);
1374            // Since, the PenaltyModel used is `ThresholdPenalty` with `MyPenaltyThresholdContext` (70% threshold):
1375            // penalties above 70% are capped, and lower penalties are left unchanged
1376            let expected_tran = vec![
1377                (ALICE, PenaltyRatio::from_percent(10)),
1378                (MIKE, PenaltyRatio::from_percent(70)),
1379                (BOB, PenaltyRatio::from_percent(70)),
1380                (CHARLIE, PenaltyRatio::from_percent(70)),
1381            ];
1382            assert_eq!(tran_penalty_for, expected_tran);
1383        })
1384    }
1385
1386    #[test]
1387    fn penalize_authors_success() {
1388        chain_manager_test_ext().execute_with(|| {
1389            set_default_user_balance_and_hold(ALICE).unwrap();
1390            set_default_user_balance_and_hold(BOB).unwrap();
1391
1392            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1393            RoleAdapter::enroll(&BOB, 150, Fortitude::Force).unwrap();
1394
1395            System::set_block_number(16);
1396            let penalty_for = vec![
1397                (ALICE, PenaltyRatio::from_percent(25)),
1398                (BOB, PenaltyRatio::from_percent(72)),
1399            ];
1400
1401            Internals::penalize_authors(penalty_for);
1402
1403            let penalties_of_alice_id = RoleAdapter::get_penalties_of(&ALICE).unwrap();
1404            let expected_penalties_of_alice_id = vec![(20, PenaltyRatio::from_percent(25))];
1405            assert_eq!(penalties_of_alice_id, expected_penalties_of_alice_id);
1406            // BOB's penalty capped to 70%
1407            let penalties_of_bob_id = RoleAdapter::get_penalties_of(&BOB).unwrap();
1408            let expected_penalties_of_bob_id = vec![(20, PenaltyRatio::from_percent(70))];
1409            assert_eq!(penalties_of_bob_id, expected_penalties_of_bob_id);
1410        })
1411    }
1412
1413    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1414    // ````````````````````````````` ELECTION AFFIDAVITS `````````````````````````````
1415    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1416
1417    #[test]
1418    fn can_submit_affidait_success() {
1419        let mut env = new_ocw_env();
1420        env.ext.execute_with(|| {
1421            set_session_config();
1422            set_default_user_balance_and_hold(ALICE).unwrap();
1423            let afdt_pub = generate_affidavit_id();
1424            enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
1425            ext_validate(ALICE, afdt_pub.clone()).unwrap();
1426            System::set_block_number(AFDT_SUBMISSION_START - 1);
1427            assert_err!(
1428                Pallet::can_submit_affidavit(&afdt_pub),
1429                Error::NotAffidavitPeriod
1430            );
1431            System::set_block_number(AFDT_SUBMISSION_START);
1432            assert_ok!(Pallet::can_submit_affidavit(&afdt_pub));
1433            System::set_block_number(AFDT_SUBMISSION_END + 1);
1434            assert_err!(
1435                Pallet::can_submit_affidavit(&afdt_pub),
1436                Error::AffidavitPeriodEnded
1437            );
1438        })
1439    }
1440
1441    #[test]
1442    fn can_submit_affidait_err_affidavit_author_not_found() {
1443        chain_manager_test_ext().execute_with(|| {
1444            System::set_block_number(10);
1445            SessionStartsAt::put(15);
1446            AllowAffidavits::put(true);
1447            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1448            let avg_session_len: BlockNumber = NextSessionRotation::average_session_length();
1449            assert_eq!(avg_session_len, 600);
1450            AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
1451            AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
1452            ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
1453            System::set_block_number(135);
1454            assert_err!(
1455                Pallet::can_submit_affidavit(&AFFIDAVIT_KEY_B),
1456                Error::AffidavitAuthorNotFound
1457            );
1458        })
1459    }
1460
1461    #[test]
1462    fn gen_affidavit_success() {
1463        chain_manager_test_ext().execute_with(|| {
1464            let users = vec![ALICE, CHARLIE, ALAN];
1465            set_default_users_balance_and_hold(users).unwrap();
1466
1467            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1468            RoleAdapter::fund(
1469                &ALICE,
1470                &Funder::Direct(CHARLIE),
1471                100,
1472                Precision::Exact,
1473                Fortitude::Force,
1474            )
1475            .unwrap();
1476            RoleAdapter::fund(
1477                &ALICE,
1478                &Funder::Direct(ALAN),
1479                150,
1480                Precision::Exact,
1481                Fortitude::Force,
1482            )
1483            .unwrap();
1484
1485            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1486            let election_via = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1487            let expected_affidavit =
1488                vec![(Funder::Direct(ALAN), 150), (Funder::Direct(CHARLIE), 100)];
1489            assert_eq!(election_via, expected_affidavit);
1490        })
1491    }
1492
1493    #[test]
1494    fn gen_affidavit_err_affidavit_author_not_found() {
1495        chain_manager_test_ext().execute_with(|| {
1496            let users = vec![ALICE, CHARLIE, ALAN];
1497            set_default_users_balance_and_hold(users).unwrap();
1498
1499            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1500            RoleAdapter::fund(
1501                &ALICE,
1502                &Funder::Direct(CHARLIE),
1503                100,
1504                Precision::Exact,
1505                Fortitude::Force,
1506            )
1507            .unwrap();
1508            RoleAdapter::fund(
1509                &ALICE,
1510                &Funder::Direct(ALAN),
1511                150,
1512                Precision::Exact,
1513                Fortitude::Force,
1514            )
1515            .unwrap();
1516
1517            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1518            assert_err!(
1519                Pallet::gen_affidavit(&AFFIDAVIT_KEY_B),
1520                Error::AffidavitAuthorNotFound
1521            );
1522        })
1523    }
1524
1525    #[test]
1526    fn submit_affidavit_success() {
1527        chain_manager_test_ext().execute_with(|| {
1528            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1529            set_default_users_balance_and_hold(users).unwrap();
1530
1531            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1532            RoleAdapter::fund(
1533                &ALICE,
1534                &Funder::Direct(CHARLIE),
1535                100,
1536                Precision::Exact,
1537                Fortitude::Force,
1538            )
1539            .unwrap();
1540            RoleAdapter::fund(
1541                &ALICE,
1542                &Funder::Direct(ALAN),
1543                150,
1544                Precision::Exact,
1545                Fortitude::Force,
1546            )
1547            .unwrap();
1548            RoleAdapter::fund(
1549                &ALICE,
1550                &Funder::Direct(MIKE),
1551                125,
1552                Precision::Exact,
1553                Fortitude::Force,
1554            )
1555            .unwrap();
1556
1557            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1558            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1559            System::set_block_number(10);
1560            assert_ok!(Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit));
1561
1562            let author_affidavit = AuthorOfAffidavits::get((1, ALICE)).unwrap();
1563            let vec = WeakBoundedVec::try_from(vec![
1564                (Funder::Direct(MIKE), 125),
1565                (Funder::Direct(ALAN), 150),
1566                (Funder::Direct(CHARLIE), 100),
1567            ])
1568            .unwrap();
1569            let expected_affidavit = (10, vec);
1570            assert_eq!(author_affidavit, expected_affidavit);
1571        })
1572    }
1573
1574    #[test]
1575    fn get_affidavit_success() {
1576        chain_manager_test_ext().execute_with(|| {
1577            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1578            set_default_users_balance_and_hold(users).unwrap();
1579
1580            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1581            RoleAdapter::fund(
1582                &ALICE,
1583                &Funder::Direct(CHARLIE),
1584                100,
1585                Precision::Exact,
1586                Fortitude::Force,
1587            )
1588            .unwrap();
1589            RoleAdapter::fund(
1590                &ALICE,
1591                &Funder::Direct(ALAN),
1592                150,
1593                Precision::Exact,
1594                Fortitude::Force,
1595            )
1596            .unwrap();
1597            RoleAdapter::fund(
1598                &ALICE,
1599                &Funder::Direct(MIKE),
1600                125,
1601                Precision::Exact,
1602                Fortitude::Force,
1603            )
1604            .unwrap();
1605
1606            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1607            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1608            System::set_block_number(10);
1609            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
1610
1611            let actual_affidavit = Pallet::get_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1612            let expected_affidavit = vec![
1613                (Funder::Direct(MIKE), 125),
1614                (Funder::Direct(ALAN), 150),
1615                (Funder::Direct(CHARLIE), 100),
1616            ];
1617            assert_eq!(actual_affidavit, expected_affidavit);
1618        })
1619    }
1620
1621    #[test]
1622    fn get_affidavit_err_affidavit_author_not_found() {
1623        chain_manager_test_ext().execute_with(|| {
1624            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1625            set_default_users_balance_and_hold(users).unwrap();
1626
1627            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1628            RoleAdapter::fund(
1629                &ALICE,
1630                &Funder::Direct(CHARLIE),
1631                100,
1632                Precision::Exact,
1633                Fortitude::Force,
1634            )
1635            .unwrap();
1636            RoleAdapter::fund(
1637                &ALICE,
1638                &Funder::Direct(ALAN),
1639                150,
1640                Precision::Exact,
1641                Fortitude::Force,
1642            )
1643            .unwrap();
1644            RoleAdapter::fund(
1645                &ALICE,
1646                &Funder::Direct(MIKE),
1647                125,
1648                Precision::Exact,
1649                Fortitude::Force,
1650            )
1651            .unwrap();
1652
1653            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1654            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1655            System::set_block_number(10);
1656            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
1657
1658            assert_err!(
1659                Pallet::get_affidavit(&AFFIDAVIT_KEY_B),
1660                Error::AffidavitAuthorNotFound
1661            );
1662        })
1663    }
1664
1665    #[test]
1666    fn get_affidavit_err_affidavit_not_found() {
1667        chain_manager_test_ext().execute_with(|| {
1668            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1669            set_default_users_balance_and_hold(users).unwrap();
1670
1671            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1672            RoleAdapter::fund(
1673                &ALICE,
1674                &Funder::Direct(CHARLIE),
1675                100,
1676                Precision::Exact,
1677                Fortitude::Force,
1678            )
1679            .unwrap();
1680            RoleAdapter::fund(
1681                &ALICE,
1682                &Funder::Direct(ALAN),
1683                150,
1684                Precision::Exact,
1685                Fortitude::Force,
1686            )
1687            .unwrap();
1688            RoleAdapter::fund(
1689                &ALICE,
1690                &Funder::Direct(MIKE),
1691                125,
1692                Precision::Exact,
1693                Fortitude::Force,
1694            )
1695            .unwrap();
1696
1697            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1698            System::set_block_number(10);
1699
1700            assert_err!(
1701                Pallet::get_affidavit(&AFFIDAVIT_KEY_A),
1702                Error::AffidavitNotFound
1703            );
1704        })
1705    }
1706
1707    #[test]
1708    fn affidavit_exists_success() {
1709        chain_manager_test_ext().execute_with(|| {
1710            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1711            set_default_users_balance_and_hold(users).unwrap();
1712
1713            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1714            RoleAdapter::fund(
1715                &ALICE,
1716                &Funder::Direct(CHARLIE),
1717                100,
1718                Precision::Exact,
1719                Fortitude::Force,
1720            )
1721            .unwrap();
1722            RoleAdapter::fund(
1723                &ALICE,
1724                &Funder::Direct(ALAN),
1725                150,
1726                Precision::Exact,
1727                Fortitude::Force,
1728            )
1729            .unwrap();
1730            RoleAdapter::fund(
1731                &ALICE,
1732                &Funder::Direct(MIKE),
1733                125,
1734                Precision::Exact,
1735                Fortitude::Force,
1736            )
1737            .unwrap();
1738
1739            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1740            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1741            System::set_block_number(10);
1742            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
1743
1744            assert_ok!(Pallet::affidavit_exists(&AFFIDAVIT_KEY_A),);
1745        })
1746    }
1747
1748    #[test]
1749    fn affidavit_exists_err_affidavit_author_not_found() {
1750        chain_manager_test_ext().execute_with(|| {
1751            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1752            set_default_users_balance_and_hold(users).unwrap();
1753
1754            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1755            RoleAdapter::fund(
1756                &ALICE,
1757                &Funder::Direct(CHARLIE),
1758                100,
1759                Precision::Exact,
1760                Fortitude::Force,
1761            )
1762            .unwrap();
1763            RoleAdapter::fund(
1764                &ALICE,
1765                &Funder::Direct(ALAN),
1766                150,
1767                Precision::Exact,
1768                Fortitude::Force,
1769            )
1770            .unwrap();
1771            RoleAdapter::fund(
1772                &ALICE,
1773                &Funder::Direct(MIKE),
1774                125,
1775                Precision::Exact,
1776                Fortitude::Force,
1777            )
1778            .unwrap();
1779
1780            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1781            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1782            System::set_block_number(10);
1783            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
1784
1785            assert_err!(
1786                Pallet::affidavit_exists(&AFFIDAVIT_KEY_B),
1787                Error::AffidavitAuthorNotFound
1788            );
1789        })
1790    }
1791
1792    #[test]
1793    fn affidavit_exists_err_affidavit_not_found() {
1794        chain_manager_test_ext().execute_with(|| {
1795            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1796            set_default_users_balance_and_hold(users).unwrap();
1797
1798            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1799            RoleAdapter::fund(
1800                &ALICE,
1801                &Funder::Direct(CHARLIE),
1802                100,
1803                Precision::Exact,
1804                Fortitude::Force,
1805            )
1806            .unwrap();
1807            RoleAdapter::fund(
1808                &ALICE,
1809                &Funder::Direct(ALAN),
1810                150,
1811                Precision::Exact,
1812                Fortitude::Force,
1813            )
1814            .unwrap();
1815            RoleAdapter::fund(
1816                &ALICE,
1817                &Funder::Direct(MIKE),
1818                125,
1819                Precision::Exact,
1820                Fortitude::Force,
1821            )
1822            .unwrap();
1823
1824            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1825            System::set_block_number(10);
1826
1827            assert_err!(
1828                Pallet::affidavit_exists(&AFFIDAVIT_KEY_A),
1829                Error::AffidavitNotFound
1830            );
1831        })
1832    }
1833
1834    #[test]
1835    fn remove_affidavit_success() {
1836        chain_manager_test_ext().execute_with(|| {
1837            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1838            set_default_users_balance_and_hold(users).unwrap();
1839
1840            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1841            RoleAdapter::fund(
1842                &ALICE,
1843                &Funder::Direct(CHARLIE),
1844                100,
1845                Precision::Exact,
1846                Fortitude::Force,
1847            )
1848            .unwrap();
1849            RoleAdapter::fund(
1850                &ALICE,
1851                &Funder::Direct(ALAN),
1852                150,
1853                Precision::Exact,
1854                Fortitude::Force,
1855            )
1856            .unwrap();
1857            RoleAdapter::fund(
1858                &ALICE,
1859                &Funder::Direct(MIKE),
1860                125,
1861                Precision::Exact,
1862                Fortitude::Force,
1863            )
1864            .unwrap();
1865
1866            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1867            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1868            System::set_block_number(10);
1869            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
1870
1871            let actual_affidavit = AuthorOfAffidavits::get((1, ALICE));
1872            assert!(actual_affidavit.is_some());
1873            assert_ok!(Pallet::remove_affidavit(&AFFIDAVIT_KEY_A));
1874            assert_eq!(AuthorOfAffidavits::get((1, ALICE)), None);
1875        })
1876    }
1877
1878    #[test]
1879    fn remove_affidavit_err_affidavit_author_not_found() {
1880        chain_manager_test_ext().execute_with(|| {
1881            let users = vec![ALICE, CHARLIE, ALAN, MIKE];
1882            set_default_users_balance_and_hold(users).unwrap();
1883
1884            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
1885            RoleAdapter::fund(
1886                &ALICE,
1887                &Funder::Direct(CHARLIE),
1888                100,
1889                Precision::Exact,
1890                Fortitude::Force,
1891            )
1892            .unwrap();
1893            RoleAdapter::fund(
1894                &ALICE,
1895                &Funder::Direct(ALAN),
1896                150,
1897                Precision::Exact,
1898                Fortitude::Force,
1899            )
1900            .unwrap();
1901            RoleAdapter::fund(
1902                &ALICE,
1903                &Funder::Direct(MIKE),
1904                125,
1905                Precision::Exact,
1906                Fortitude::Force,
1907            )
1908            .unwrap();
1909
1910            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
1911            let affidavit = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
1912            System::set_block_number(10);
1913            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit).unwrap();
1914
1915            assert_err!(
1916                Pallet::remove_affidavit(&AFFIDAVIT_KEY_B),
1917                Error::AffidavitAuthorNotFound
1918            );
1919        })
1920    }
1921}