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}