frame_suite/
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 SUITE ```````````````````````````````
14// ===============================================================================
15
16//! Defines a **comprehensive framework for managing authors** (validators) in a
17//! decentralized  network maintaining a distributed, deterministic state machine.
18//!
19//! The network is considered as a set of **independent nodes operated by authors**,
20//! where each author is responsible for appending transactions that drive the state
21//! machine through deterministic transitions.
22//!
23//! To ensure consistency across the network, the system requires a mechanism to
24//! determine **which author can propose transactions at any given period**, in
25//! a way that is approved by a quorum of the authors themselves. This enables
26//! coordinated block production, fair transaction inclusion, and consensus-approved
27//! progression of the state machine while preserving author accountability.
28//!
29//! To ensure smooth operation, the system provides a **consistent and pluggable
30//! framework** for:
31//! - **Author elections** ([`ElectAuthors`]) determining which authors are
32//! responsible for producing the next set of blocks or transactions.
33//! - **Contribution tracking** ([`AuthorPoints`]) ephemeral points awarded for
34//! good behavior or participation during a session, allowing fair evaluation of
35//! author performance.
36//! - **Reward distribution** ([`RewardAuthors`]) single-point reward computation
37//! using accumulated points, distributing assets to authors based on their
38//! contributions.
39//! - **Penalty enforcement** ([`PenalizeAuthors`]) immediate sanctions for bad
40//! behavior, missed duties, or misbehavior.
41//! - **Affidavit handling** ([`ElectionAffidavits`]) voluntary self-reported
42//! election weights that authors may submit to participate in elections or help
43//! the system determine the next set of block producers.
44//!
45//! ## Design Goals
46//!
47//! 1. **End-to-end author lifecycle management**: track contributions, elect,
48//! reward, and penalize authors in a consistent way.
49//! 2. **Fair and transparent reward system**: ephemeral points track good behavior;
50//! rewards are computed at a single point in time for all authors and distributed
51//! according to points earned.
52//! 3. **Immediate accountability**: penalties are applied as soon as misbehavior
53//! is detected.
54//! 4. **Flexible and pluggable logic**: supports runtime-configurable
55//! [`plugins`](crate::plugins) for rewards, penalties, inflation adjustments, and
56//! election algorithms.
57//! 5. **Framework-agnostic architecture**: can be integrated in:
58//!    - **Standalone chains** with independent consensus and security.
59//!    - **Parachains or shared-security environments**, where authors act as
60//!    collators or validators.
61//!
62//! ## Terminology
63//!
64//! - **Authors**: Independent node operators responsible for producing blocks,
65//! validating transactions, or fulfilling consensus-critical roles.
66//! - **Points**: Temporary metrics representing author contributions for a session;
67//! used for computing rewards fairly at a single evaluation point.
68//! - **Rewards**: Asset payouts allocated based on points, configurable via
69//! runtime [`plugins`](crate::plugins).
70//! - **Penalties**: Immediate deductions or sanctions applied to authors for
71//! misbehavior.
72//! - **Affidavits**: Voluntary, self-reported election weights submitted by
73//! authors, used for lazy election mechanisms where authors influence election
74//! outcomes.
75//!
76//! By implementing these traits, a runtime gains a **robust, modular, and
77//! chain-agnostic system** for handling author performance, elections, rewards,
78//! penalties, and voluntary affidavits, ensuring predictable, fair, and transparent
79//! operation of the blockchain network.
80
81// ===============================================================================
82// ``````````````````````````````````` IMPORTS ```````````````````````````````````
83// ===============================================================================
84
85// --- Local crate imports ---
86use crate::{
87    base::{Buffer, Countable, Keyed, Percentage, Storable},
88    plugin_output, plugin_types,
89};
90
91// --- Substrate primitives ---
92use sp_runtime::{DispatchError, DispatchResult};
93
94// ===============================================================================
95// `````````````````````````````` AUTHOR-ELECTIONS ```````````````````````````````
96// ===============================================================================
97
98/// Higher-Level Trait for performing election of authors (e.g., block producers,
99/// validators, etc) in a FRAME-based runtime.
100///
101/// This trait abstracts the process of **candidate preparation**, **author
102/// selection**, and **election result handling** in a generic,
103/// [`pluggable`](crate::plugins) way.  
104///
105/// The design allows different implementations to define their own election logic -
106/// such as weighted randomness, reputation-based scoring, or round-robin selection -
107/// while maintaining a consistent interface.
108///
109/// ## Type Parameters
110/// - `Author`: The type representing an author or candidate
111/// - `ElectionWeight`: The type representing each candidate's election weight,
112///   score, or stake. Should be comparable [`Ord`].
113///
114/// ## Runner Model
115/// The election process can be executed in two modes:
116/// - **Global execution (`None`)**: The runtime itself triggers and executes
117///   the election (e.g., via inherent or root-driven logic).
118/// - **Author-driven execution (`Some(Author)`)**: A specific author acts as a
119///   **runner**, voluntarily or by assignment, to execute the election logic.
120///
121/// This enables flexible designs where:
122/// - Elections can be decentralized and triggered by participants.
123/// - A designated author (e.g., block producer, coordinator) can perform elections.
124/// - The runtime can still retain full control when required.
125///
126/// ## Usage
127///
128/// A runtime pallet implementing this trait would typically:
129/// 1. Collect candidates via [`Self::prepare_candidates`].
130/// 2. Elect authors via [`Self::prepare_authors`].
131/// 3. Handle success or failure with [`Self::on_elect_success`] or
132///   [`Self::on_elect_fail`], optionally using the `runner`.
133/// 4. Reveal the final elected authors with [`Self::reveal`].
134pub trait ElectAuthors<Author, ElectionWeight>
135where
136    Author: Keyed,
137    ElectionWeight: Ord + Storable,
138{
139    /// Type representing all candidates in the election.
140    type Candidates: Buffer<(Author, ElectionWeight)>;
141
142    /// Type representing the set of successfully elected authors.
143    type Elected: Buffer<Author>;
144
145    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
147    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148
149    /// Determines whether the election process can currently be run.
150    ///
151    /// ## Parameters
152    /// - `runner`: Optional executor of the election process.
153    ///   - `None`: runtime-driven execution.
154    ///   - `Some(author)`: author-driven execution.
155    ///
156    /// This is a pre-check to ensure that conditions are right for running
157    /// an election. For example, a runtime may enforce:
158    /// - Minimum number of candidates.
159    /// - Required system state (e.g., epoch boundaries).
160    /// - Timing constraints or cooldown periods.
161    /// - Authorization or eligibility of the `runner`.
162    ///
163    /// Returns `Ok(())` if the election is allowed to proceed, `DispatchError` otherwise.
164    fn can_process_election(_runner: &Option<Author>) -> DispatchResult;
165
166    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167    // ``````````````````````````````````` GETTERS ```````````````````````````````````
168    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
169
170    /// Returns the final set of recently elected authors.
171    ///
172    /// Implementations may retrieve this from storage or memory.
173    /// This is usually called after a successful [`Self::prepare_election`] run.
174    fn reveal() -> Option<Self::Elected>;
175
176    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
177    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
178    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
179
180    /// Prepares the set of candidates for the next election round.
181    ///
182    /// Typically this would collect eligible authors, validators, or accounts
183    /// from storage or an external data source, and attach their election weights.
184    ///
185    /// Returns either the collection of candidates or an error if preparation fails.
186    fn prepare_candidates() -> Result<Self::Candidates, DispatchError>;
187
188    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
189    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
190    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191    
192    /// Main entry point for the election process.
193    ///
194    /// Takes a collection of candidates (with weights) and performs
195    /// the author election logic - storing or returning the result as appropriate.
196    ///
197    /// Returns `Ok(())` if election succeeds, or `Err(DispatchError)` if
198    /// election logic fails.
199    fn prepare_authors(candidates: Self::Candidates) -> DispatchResult;
200
201    /// High-level orchestration function for running the entire election flow.
202    ///
203    /// ## Parameters
204    /// - `runner`: Optional executor of the election process.
205    ///   - `None`: election is executed by the runtime.
206    ///   - `Some(author)`: a specific author executes the election.
207    ///
208    /// ## Workflow
209    /// 1. Calls [`Self::can_process_election`] to validate execution conditions.
210    /// 2. Calls [`Self::prepare_candidates`] to collect all eligible candidates.
211    /// 3. Passes them to [`Self::prepare_authors`] to perform the election.
212    /// 4. Triggers [`Self::on_elect_success`] or [`Self::on_elect_fail`] with `runner`.
213    ///
214    /// This ensures consistent election control flow, centralized error handling,
215    /// and proper attribution of execution responsibility.
216    ///
217    /// Returns `DispatchError` if any stage fails.
218    fn prepare_election(runner: &Option<Author>) -> DispatchResult {
219        Self::can_process_election(runner)?;
220        let candidates = Self::prepare_candidates()?;
221        if let Err(e) = Self::prepare_authors(candidates) {
222            Self::on_elect_fail(runner, e);
223            return Err(e);
224        };
225        Self::on_elect_success(runner);
226        Ok(())
227    }
228
229    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
230    // ```````````````````````````````````` HOOKS ````````````````````````````````````
231    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
232
233    /// Hook called when an election completes successfully.
234    ///
235    /// ## Parameters
236    /// - `runner`: Optional executor of the election.
237    ///
238    /// To perform post-election actions such as:
239    /// - Persisting results to storage.
240    /// - Emitting pallet events.
241    /// - Updating system state or metrics.
242    /// - Attributing execution responsibility or rewards to the runner.
243    ///
244    /// Default implementation is no-op
245    fn on_elect_success(_runner: &Option<Author>) {}
246
247    /// Hook called when an election fails.
248    ///
249    /// ## Parameters
250    /// - `runner`: Optional executor of the election.
251    /// - `err`: The error that caused the failure.
252    ///
253    /// Runtime implementations can override this to:
254    /// - Emit error events.
255    /// - Retry logic.
256    /// - Fallback to default authors.
257    /// - Attribute failure or responsibility to the runner.
258    ///
259    /// The passed `DispatchError` provides detailed context on what went wrong.
260    /// Default implementation is no-op
261    fn on_elect_fail(_runner: &Option<Author>, _err: DispatchError) {}
262}
263
264// ===============================================================================
265// ```````````````````````````````` AUTHOR-POINTS ````````````````````````````````
266// ===============================================================================
267
268/// Trait representing **temporary, abstract points assigned to authors**.
269///
270/// These points are **not finalized rewards or assets**, but rather a
271/// lightweight mechanism to track "good behavior" or participation within a
272/// specific, temporary context, such as:
273/// - A single author round.
274/// - A session of block production.
275/// - A single task or contribution that is ephemeral in nature.
276///
277/// ## Design notes
278/// - Points are **incremented one at a time** using [`Self::add_point`], rather
279///   than in bulk. This enforces a clear, single-event granularity per point. It also
280///   simplifies logic for ephemeral metrics and prevents accidental double-counting.
281/// - Different implementations of this trait can represent **distinct points systems**
282///   or contexts. For example, one implementation could track "block production points",
283///   while another tracks "validation participation points".
284///
285/// ## Ephemeral behavior
286/// - Points do not correspond to finalized payments or balances.
287/// - They are **temporary**: cleared via [`Self::clear_points`] at the end of the round
288///   or session.
289/// - They provide a way to accumulate **good behavior metrics** for temporary evaluation.
290///
291/// ## Type Parameters
292/// - `Author`: The entity receiving points (e.g., `AccountId` or validator identifier).  
293/// - `Points`: The numeric type representing points; typically `u32`, `u64`. This type
294///   can be used to compute metrics, rankings, or temporary rewards.
295pub trait AuthorPoints<Author, Points>
296where
297    Author: Keyed,
298    Points: Countable,
299{
300    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
301    // ``````````````````````````````````` GETTERS ```````````````````````````````````
302    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
303
304    /// Returns the current points of a given author, if any.
305    ///
306    /// Points are **temporary** and reflect only the current round or session.
307    /// Returns a `DispatchError` if there is an issue reading storage or runtime state.
308    ///
309    /// ## Parameters
310    /// - `author`: The author whose points are queried.
311    ///
312    /// ## Returns
313    /// - `Ok(points)` if the author has points.
314    /// - `Err(DispatchError)` if reading fails.
315    fn points_of(author: &Author) -> Result<Points, DispatchError>;
316
317    /// Returns an iterator over all authors and their current points.
318    ///
319    /// This provides a view of the **entire ephemeral points state** for the
320    /// current round or session, typically used by runtime operations such as
321    /// reward computation, ranking, or evaluation.
322    ///
323    /// Since points are **ephemeral**, any such operation is expected to
324    /// eventually clear them via [`Self::clear_points`], which is left to
325    /// the caller's discretion.
326    ///
327    /// ## Returns
328    /// - An iterator yielding `(Author, Points)` pairs.
329    fn iter_points() -> impl Iterator<Item = (Author, Points)>;
330
331    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
332    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
333    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
334
335    /// Sets the points for a given author.
336    ///
337    /// This is the **primitive write operation** for point state.
338    /// All mutations to points (including increments) should be expressed
339    /// in terms of this method to ensure consistency.
340    ///
341    /// ## Returns
342    /// - `Ok(())` if the update succeeds.
343    /// - `Err(DispatchError)` if the operation fails.
344    fn set_points(author: &Author, points: Points) -> DispatchResult;
345
346    /// Adds a single point to a given author.
347    ///
348    /// Each invocation represents **one unit of contribution or good behavior**.
349    /// For bulk or weighted increments, implementers could wrap this in higher-level
350    /// logic externally, keeping the trait focused on single-point increments.
351    ///
352    /// This default implementation:
353    /// - Reads the current points (or assumes zero if none exist)
354    /// - Performs a **saturating addition** of one point
355    /// - Writes the updated value via [`Self::set_points`]
356    ///
357    /// Returns a `DispatchError` if updating fails (e.g., storage error or overflow).
358    fn add_point(author: &Author) -> DispatchResult {
359        let current = Self::points_of(author).unwrap_or_else(|_| Points::zero());
360        let new = current.saturating_add(Points::one());
361        Self::set_points(author, new)
362    }
363
364    /// Clears all points for all authors.
365    ///
366    /// - Typically called at the end of a round or session to reset the
367    ///   ephemeral scoring.
368    /// - Ensures the points system remains **context-specific** and avoids
369    ///   carry-over between rounds.
370    fn clear_points();
371}
372
373// ===============================================================================
374// ``````````````````````````````` AUTHOR-REWARDS ````````````````````````````````
375// ===============================================================================
376
377/// Provides a **plugin-driven reward system** for authors, connecting
378/// ephemeral **points** to actual **asset payouts**.
379///
380/// This trait is designed for modular, flexible, and runtime-configurable
381/// reward logic in Substrate pallets.
382///
383/// Points should reflect temporary contributions (e.g., block production,
384/// validation) and are **cleared after each reward cycle**.
385///
386/// ## Core Responsibilities
387/// 1. Track author points using [`Self::AuthorPointsAdapter`].
388/// 2. Compute the total payout using a runtime-configurable [`Self::PayoutModel`].
389/// 3. Generate a per-author payout list using [`Self::PayeeModel`].
390/// 4. Execute rewards safely, with callbacks for success or failure.
391///
392/// ## Type Parameters
393/// - `Author`: Entity receiving rewards.
394/// - `Asset`: Type of reward (e.g., token balance).
395/// - `Points`: Type representing ephemeral points .
396pub trait RewardAuthors<Author, Asset, Points>
397where
398    Author: Keyed,
399    Asset: crate::Asset,
400    Points: Countable,
401{
402    /// Adapter connecting **author points** with the reward logic.
403    ///
404    /// This associated type provides the bridge between ephemeral
405    /// **points** assigned to authors and the current reward computation system.  
406    ///
407    /// - Defined as an associated type because points may be **tracked
408    ///   externally** or come from non-local sources.
409    /// - Responsible for **capturing all point contributions** relevant to rewarding,
410    ///   ensuring accurate reward calculations.
411    type AuthorPointsAdapter: AuthorPoints<Author, Points>;
412
413    /// Collection of authors and their **ephemeral points** for the current
414    /// reward cycle.
415    ///
416    /// This type represents the **input to the [`Self::PayeeModel`] plugin**,
417    /// which determines how the total payout is distributed among authors
418    /// based on their points.
419    type PayoutFor: Buffer<(Author, Points)>;
420
421    /// Collection of authors and their **final reward amounts** for the current cycle.
422    ///
423    /// This type represents the **output of the [`Self::PayeeModel`] plugin**,
424    /// which maps author points and the total payout into actual rewards (`Asset`).
425    type PayeeList: Buffer<(Author, Asset)>;
426
427    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
428    // ``````````````````````````````````` GETTERS ```````````````````````````````````
429    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
430    
431    plugin_types!(
432        input: Asset,
433        output: Asset,
434        /// Plugin model for computing the **total reward pool**.
435        ///
436        /// This model determines how the raw total payout is transformed
437        /// into the final total reward.
438        ///
439        /// This plugin allows runtime-configurable logic to adjust the raw
440        /// total payout (`Asset`) before distributing it to authors.
441        ///
442        /// ## Input
443        /// - `Asset`: The raw total reward amount for the current cycle.
444        ///
445        /// ## Output
446        /// - `Asset`: The optimized total reward after applying payout logic
447        ///   (e.g., capping, scaling, or applying curves).
448        model: PayoutModel,
449
450        /// Provides optional runtime parameters for the payout
451        /// plugin [`Self::PayoutModel`].
452        ///
453        /// Enables dynamic configuration, such as thresholds, multipliers,
454        /// or other runtime-adjustable settings.
455        context: PayoutContext,
456    );
457
458    /// Returns the **raw, unprocessed total payout** for the current reward cycle.
459    ///
460    /// This is the low-level deterministic total reward amount before any adjustments:
461    /// - May reflect a global metric (e.g., total stake, contributions, or available funds).
462    /// - Can be modified by the [`Self::PayoutModel`] plugin to produce the final payout
463    ///   via [`Self::payout`].
464    /// - May be very large or very small depending on the cycle conditions.
465    fn payout_via() -> Asset;
466
467    /// Computes the **final total payout** for the current reward cycle.
468    ///
469    /// - Uses the [`Self::PayoutModel`] plugin via [`Self::payout_process`] to adjust
470    /// the raw payout ([`Self::payout_via`]).
471    /// - Can apply reward curves, scaling, caps, or other runtime-configurable rules.
472    /// - Represents the **high-level payout** that will be distributed to authors.
473    fn payout() -> Asset {
474        let via = Self::payout_via();
475        Self::payout_process(via)
476    }
477
478    plugin_output! {
479        /// [`Self::PayoutModel`] plugin output function.
480        /// Utilizes the plugin model's context [`Self::PayoutContext`]
481        fn payout_process,
482        input: Asset,
483        output: Asset,
484        model: Self::PayoutModel,
485        context: Self::PayoutContext
486    }
487
488    /// Returns the **authors and their accumulated points** for the current reward cycle.
489    ///
490    /// - Serves as the input for the [`Self::PayeeModel`] plugin to compute per-author payouts.
491    /// - Represents ephemeral contributions, which are cleared after reward distribution.
492    /// - Can include multiple roles or activities that contribute points for rewards.
493    fn payout_for() -> Self::PayoutFor;
494
495    plugin_types!(
496        input: (Asset, Self::PayoutFor),
497        output: Self::PayeeList,
498        /// Plugin model for computing **per-author payouts** from total payout and points.
499        ///
500        /// This plugin model responsible for distributing the total payout
501        /// among authors according to their points.
502        ///
503        /// - Input: Tuple (`total_payout`, [`Self::PayoutFor`]).
504        /// - Output: [`Self::PayeeList`].
505        ///
506        /// Computes the mapping from `(Author, Points)` to `(Author, Asset)`.
507        model: PayeeModel,
508
509        /// Provides optional runtime parameters for the payee plugin
510        /// [`Self::PayeeModel`] computation.
511        ///
512        /// Enables dynamic configuration, such as thresholds, multipliers,
513        /// or other runtime-adjustable settings.
514        context: PayeeContext,
515    );
516
517    plugin_output! {
518        /// [`Self::PayeeModel`] plugin output function.
519        /// Utilizes the plugin model's context [`Self::PayeeContext`]
520        fn payee_process,
521        input: (Asset, Self::PayoutFor),
522        output: Self::PayeeList,
523        model: Self::PayeeModel,
524        context: Self::PayeeContext
525    }
526
527    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
528    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
529    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
530
531    /// Distributes rewards to authors based on their points and available payout.
532    ///
533    /// ## Workflow
534    /// 1. Compute total payout using [`Self::payout`].
535    /// 2. If payout is zero, skip distribution.
536    /// 3. Generate per-author payout list via the [`Self::PayeeModel`] plugin
537    /// function [`Self::payee_process`].
538    /// 4. Distribute rewards using [`Self::reward`] for each author.
539    /// 5. Call [`Self::on_reward_success`] or [`Self::on_reward_fail`] callbacks
540    ///   for each author.
541    /// 6. Clear ephemeral points via [`Self::AuthorPointsAdapter`].
542    ///
543    /// ## Notes
544    /// - Ensure that enough points are supplied; otherwise, a few authors may receive
545    ///   disproportionately high rewards.
546    /// - Individual failures are handled gracefully and do not abort the reward cycle.
547    fn reward_authors() {
548        let payout = Self::payout();
549
550        if !payout.is_zero() {
551            let towards = Self::payout_for();
552
553            let payees = Self::payee_process((payout, towards));
554            for (ref id, value) in payees {
555                if let Err(err) = Self::reward(id, value) {
556                    // gracefully handle failures; cannot re-run entire distribution
557                    Self::on_reward_fail(id, err);
558                }
559                Self::on_reward_success(id, value);
560            }
561        }
562
563        Self::AuthorPointsAdapter::clear_points()
564    }
565
566    /// Distributes a reward `value` to a specific author `who`.
567    ///
568    /// ## Requirements
569    /// - Must be implemented by the pallet using this trait.
570    /// - Example implementations: token transfer, minting, or
571    ///   crediting balances.
572    fn reward(who: &Author, value: Asset) -> DispatchResult;
573
574    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
575    // ```````````````````````````````````` HOOKS ````````````````````````````````````
576    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
577
578    /// Callback invoked after a successful individual reward.
579    ///
580    /// Useful for logging, metrics collection, or triggering side-effects.
581    ///
582    /// Default is no-op
583    fn on_reward_success(_who: &Author, _value: Asset) {}
584
585    /// Callback invoked when an individual reward fails.
586    ///
587    /// Useful for logging, alerting, or implementing retry logic.
588    ///
589    /// Default is no-op
590    fn on_reward_fail(_who: &Author, _err: DispatchError) {}
591}
592
593// ===============================================================================
594// `````````````````````````````` AUTHOR-PENALTIES ```````````````````````````````
595// ===============================================================================
596
597/// Provides a **plugin-driven penalty system** for authors, allowing
598/// permanent penalties to be applied in a modular and runtime-configurable way.
599///
600/// ## Core Responsibilities
601/// 1. Accept a set of authors and penalties ([`Self::PenaltyFor`]).
602/// 2. Transform penalties via a runtime-configurable [`Self::PenaltyModel`] plugin.
603/// 3. Apply penalties individually with safe callbacks for success or failure.
604///
605/// ## Type Parameters
606/// - `Author`: Entity receiving the penalty.
607/// - `Penalty`: Type representing the penalty (e.g., token deduction, score reduction).
608pub trait PenalizeAuthors<Author, Penalty>
609where
610    Author: Keyed,
611    Penalty: Percentage,
612{
613    /// Collection of authors and their associated penalties for normalization.
614    ///
615    /// - Represents the **input** to the [`Self::PenaltyModel`] plugin.
616    /// - Represents the **output** after plugin transformation.
617    /// - Supports iteration, extension, and default construction.
618    type PenaltyFor: Buffer<(Author, Penalty)>;
619
620    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
621    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
622    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
623
624    plugin_types!(
625        input: Self::PenaltyFor,
626        output: Self::PenaltyFor,
627        /// The plugin model implementing the penalty transformation logic.
628        ///
629        /// - Input: [`Self::PenaltyFor`] collection.
630        /// - Output: Transformed [`Self::PenaltyFor`] collection.
631        ///
632        /// Transforms the mapping from `(Author, Penalty)` to `(Author, Penalty)`.
633        model: PenaltyModel,
634        /// Provides optional runtime parameters for the penalty-transformation
635        /// plugin [`Self::PenaltyModel`] computation.
636        ///
637        /// Enables dynamic configuration, such as thresholds, multipliers,
638        /// or other runtime-adjustable settings.
639        context: PenaltyContext,
640    );
641
642    /// Apply penalties to a list of authors.
643    ///
644    /// Workflow:
645    /// 1. Transform input penalties using [`Self::PenaltyModel`].
646    /// 2. Apply penalties to each author via [`Self::penalize`].
647    /// 3. Call [`Self::on_penalty_success`] or [`Self::on_penalty_fail`] for
648    ///   each author.
649    ///
650    /// Notes:
651    /// - Handles multiple penalties or single penalty uniformly.
652    /// - Failures for individual authors do not halt the process.
653    fn penalize_authors(towards: Self::PenaltyFor) {
654        let penalty_for = Self::transform_penalty(towards);
655        for (ref who, penalty) in penalty_for {
656            if let Err(err) = Self::penalize(who, penalty) {
657                Self::on_penalty_fail(who, err);
658            }
659            Self::on_penalty_success(&who, penalty);
660        }
661    }
662
663    plugin_output! {
664        /// Transforms raw penalties using the [`Self::PenaltyModel`] plugin.
665        ///
666        /// - Applies runtime-configured rules, multipliers, or caps.
667        /// - Utilizes the plugin model's context [`Self::PenaltyContext`]
668        fn transform_penalty,
669        input: Self::PenaltyFor,
670        output: Self::PenaltyFor,
671        model: Self::PenaltyModel,
672        context: Self::PenaltyContext
673    }
674
675    /// Applies a single penalty to an author.
676    ///
677    /// This is a **direct application** of a penalty and does not involve
678    /// transformation via the [`Self::PenaltyModel`] plugin.
679    fn penalize(who: &Author, penalty: Penalty) -> DispatchResult;
680
681    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
682    // ```````````````````````````````````` HOOKS ````````````````````````````````````
683    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
684
685    /// Callback invoked after a successful individual penalty.
686    ///
687    /// Can be used for logging, metrics, or side-effects.
688    ///
689    /// Default is no-op
690    fn on_penalty_success(_who: &Author, _penalty: Penalty) {}
691
692    /// Callback invoked when an author penalty fails.
693    ///
694    /// Can be used for logging, alerting, or retry logic.
695    ///
696    /// Default is no-op
697    fn on_penalty_fail(_who: &Author, _err: DispatchError) {}
698}
699
700// ===============================================================================
701// `````````````````````````````` AUTHOR-AFFIDAVITS ``````````````````````````````
702// ===============================================================================
703
704/// Defines the behavior for **author affidavits** - self-declared affirmations
705/// of election weights during a given cycle.
706///
707/// ## Concept
708/// Affidavits represent an **author's own self-declaration** of their election weight
709/// (e.g., performance, stake, or participation value) for the current election cycle.
710/// They are not requested or enforced by the system; instead, they are **voluntarily
711/// submitted by the authors** themselves.
712///
713/// These affidavits are **ephemeral**:
714/// - Stored temporarily for the next (upcoming) election cycle.
715/// - Must be cleared once the cycle ends or after all have been processed.
716/// - Can later be used by external modules (e.g., `SessionManager`)
717///   to inform election outcomes or validations.
718///
719/// ## Responsibilities
720/// This trait defines:
721/// - Submission and validation of author affidavits.
722/// - Storage and retrieval of **ephemeral** affidavit data.
723/// - Lifecycle management (generation, existence check, removal, clearing).
724///
725/// ## Type Parameters
726/// - `Author`: Entity submitting the affidavit (e.g., validator, collator,
727///   consortium-roles, etc).
728/// - `ElectionWeight`: The numeric or structured weight being affirmed.
729///   Requires [`Ord`].
730pub trait ElectionAffidavits<Author, ElectionWeight>
731where
732    Author: Keyed,
733    ElectionWeight: Ord + Storable,
734{
735    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
736    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
737    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
738
739    /// Determines whether an author is eligible to submit an affidavit
740    /// for the next election cycle.
741    ///
742    /// Implementations should define conditions such as:
743    /// - Whether the author can be part of the next election round.
744    /// - Whether the submission window is still open.
745    /// - Whether the author already submitted.
746    fn can_submit_affidavit(who: &Author) -> DispatchResult;
747
748    /// Checks whether an affidavit currently exists for the given author.
749    ///
750    /// ## Returns
751    /// - `Ok(())` if exists.
752    /// - `Err(DispatchError)` if not or on query failure.
753    fn affidavit_exists(who: &Author) -> DispatchResult;
754
755    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
756    // ``````````````````````````````````` GETTERS ```````````````````````````````````
757    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
758
759    /// Retrieves the **stored affidavit** for a given author.
760    ///
761    /// Used only for external queries to fetch the most recent
762    /// submitted affidavit.
763    ///
764    /// Should not be used internally during submission or processing of
765    /// a single affidavit.
766    fn get_affidavit(who: &Author) -> Result<ElectionWeight, DispatchError>;
767
768    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
769    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
770    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
771
772    /// Generates a new affidavit (i.e., computes or constructs an election weight)
773    /// for the given author.
774    ///
775    /// Typically derived from the author's participation or other context-dependent
776    /// metrics in the current cycle.  
777    ///
778    /// **Note:** This may produce a different value than any previously submitted
779    /// affidavit.
780    ///
781    /// ## Returns
782    /// - `Ok(ElectionWeight)` if generation is successful.
783    /// - `Err(DispatchError)` if generation fails.
784    fn gen_affidavit(who: &Author) -> Result<ElectionWeight, DispatchError>;
785
786    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
787    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
788    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
789
790    /// Submits an affidavit for the author for the next election cycle.
791    ///
792    /// ## Parameters
793    /// - `who`: Author submitting the affidavit.
794    /// - `affidavit`: The generated election weight for submission.
795    fn submit_affidavit(who: &Author, affidavit: &ElectionWeight) -> DispatchResult;
796
797    /// Processes the full lifecycle of an affidavit submission for a given author
798    /// for the next election cycle.
799    ///
800    /// This is the **entry point** for submitting an affidavit.  
801    /// Workflow:
802    /// 1. Check if the author **can** submit ([`Self::can_submit_affidavit`]).
803    /// 2. Generate the author's affidavit ([`Self::gen_affidavit`]).
804    /// 3. **Submit** the affidavit ([`Self::submit_affidavit`]) with the
805    ///   generated weight.
806    /// 4. Trigger optional post-submission hook ([`Self::on_submit_affidavit`]).
807    fn process_affidavit(who: &Author) -> DispatchResult {
808        Self::can_submit_affidavit(who)?;
809        let affidavit = Self::gen_affidavit(who)?;
810        Self::submit_affidavit(who, &affidavit)?;
811        Self::on_submit_affidavit(who, &affidavit);
812        Ok(())
813    }
814
815    /// Removes a stored affidavit for the given author.
816    ///
817    /// Typically invoked by **external modules** to remove an affidavit
818    /// before processing the collected affidavits for a cycle.
819    ///
820    /// Pre- or post-processing removal is allowed, as appropriate.
821    fn remove_affidavit(who: &Author) -> DispatchResult;
822
823    /// Clears **all** affidavits for the current election cycle.
824    ///
825    /// Should be called once all affidavits have been processed or validated,
826    /// ensuring that new cycles start with a clean state.
827    fn clear_affidavits();
828
829    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
830    // ```````````````````````````````````` HOOKS ````````````````````````````````````
831    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
832
833    /// Hook triggered **after a successful affidavit submission**.
834    ///
835    /// Allows external logic, e.g., logging, reward triggers, or
836    /// cross-pallet coordination.
837    ///
838    /// Default is no-op
839    fn on_submit_affidavit(_who: &Author, _affidavit: &ElectionWeight) {}
840}