pallet_chain_manager/
lib.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// ```````````````````````````` PALLET CHAIN-MANAGER `````````````````````````````
14// ===============================================================================
15
16//! The **Chain Manager pallet** is the primary orchestration layer for
17//! managing **session validators** as a coordinated, session-driven system.
18//!
19//! It governs the full validator lifecycle spanning participation,
20//! election, activation, rewards, and penalties by materializing the
21//! abstractions defined in [`frame_suite::blockchain`]
22//! into a concrete runtime execution model.
23//!
24//! This pallet does not introduce new primitives. Instead, it binds together
25//! traits, plugins, and adapters to drive deterministic validator selection
26//! and behavior across sessions, enabling the game-theoretic guarantees
27//! required for a decentralized network.
28//!
29//! - [`Config`] - Runtime configuration  
30//! - [`Call`] - Dispatchable extrinsics (includes unsigned)  
31//! - [`Pallet`] - External usage and trait implementations  
32//!
33//! ## Overview
34//!
35//! A **validator (author)** in this system is an actor who:
36//!
37//! - signals intent to participate in validation,
38//! - submits affidavit data (backers) for election,
39//! - competes in a session-based selection process,
40//! - becomes an active session validator upon selection,
41//! - receives rewards or penalties based on behavior.
42//!
43//! Validators are modeled as **session-scoped actors**, where
44//! participation, selection, and compensation are tied to
45//! deterministic session transitions.
46//!
47//! ## Architectural Role
48//!
49//! The pallet acts as an **orchestration boundary**, integrating:
50//!
51//! - role management and funding via [`Config::RoleAdapter`]  
52//! - election logic via [`Config::ElectionAdapter`]  
53//! - asset interactions via [`Config::Asset`]  
54//! - session and authorship via [`pallet_session`] and [`pallet_authorship`]  
55//! - offence handling via [`pallet_offences`]  
56//!
57//! It only tightly integrates with these essential core pallets, allowing
58//! any Substrate runtime that composes them to adopt this pallet
59//! for validator lifecycle and session management.
60//!
61//! ## Validator Lifecycle
62//!
63//! The system progresses through **session-scoped phases**:
64//!
65//! - **Pursuing Validation**  
66//!   Authors signal intent to validate and may pause participation (`chill`).
67//!
68//! - **Affidavit Phase**  
69//!   Active participants submit affidavit declarations representing
70//!   election weights for the upcoming validator set.
71//!
72//! - **Election Phase**  
73//!   Elections are executed for all active participants to determine
74//!   the next session validators.
75//!
76//! - **Activation**  
77//!   Elected authors transition into active session validators.
78//!
79//! - **Reward & Penalty**  
80//!   Validators are compensated or penalized based on behavior.
81//!
82//! All transitions are derived relative to session progression,
83//! ensuring predictable and deterministic execution.
84//!
85//! ## Execution Model
86//!
87//! The pallet operates as a **session-driven orchestration engine**,
88//! primarily driven by **offchain workers and unsigned extrinsics**.
89//!
90//! Once validation intent is externally invoked by an author, the system
91//! progresses automatically:
92//!
93//! - **Offchain workers**  
94//!   coordinate affidavit submission, election execution, and key rotation  
95//!
96//! - **Unsigned extrinsics**  
97//!   are submitted to finalize deterministic state transitions  
98//!
99//! - **Block hooks (`on_initialize`)**  
100//!   process accumulated state and scheduled transitions  
101//!
102//! This enables:
103//!
104//! - automated validator lifecycle progression  
105//! - continuous election participation for active candidates  
106//! - minimal manual interaction beyond intent signaling  
107//!
108//! ## Economic Model
109//!
110//! Rewards and penalties are **externally defined and injected**:
111//!
112//! - [`Config::InflationModel`]: derives total session payout  
113//! - [`Config::RewardModel`]: distributes rewards from points  
114//! - [`Config::PenaltyModel`]: transforms and normalizes penalties  
115//!
116//! Final application is delegated via [`Config::RoleAdapter`],
117//! ensuring consistent integration with role and funding systems.
118//!
119//! ## Design Intent
120//!
121//! This pallet is a **composition layer**, not a monolithic system:
122//!
123//! - structure is defined via traits  
124//! - behavior is injected via plugins  
125//! - coordination is driven by sessions and routines  
126//!
127//! enabling a modular, replaceable, and evolvable validator system
128//! while preserving strong type safety and deterministic execution.
129//!
130//! ## Development Feature Gate
131//!
132//! This pallet includes a `dev` feature gate for development and testing.
133//!
134//! Core functionality is exposed via public APIs for RPC and UI usage.
135//! The `dev` feature provides thin wrapper extrinsics and extended
136//! event emissions for direct inspection.
137//!
138//! This feature must be disabled in production runtimes due to
139//! additional debugging overhead.
140
141#![cfg_attr(not(feature = "std"), no_std)]
142
143// ===============================================================================
144// `````````````````````````````````` MODULES ````````````````````````````````````
145// ===============================================================================
146
147#[cfg(feature = "runtime-benchmarks")]
148mod benchmarking;
149#[cfg(test)]
150pub(crate) mod mock;
151mod mocks {
152    #[path = "balances.rs"]
153    #[cfg(test)]
154    pub mod balances;
155
156    #[path = "xp.rs"]
157    #[cfg(test)]
158    pub mod xp;
159}
160#[cfg(test)]
161mod tests;
162mod blockchain;
163mod offence;
164mod roles;
165mod routines;
166mod session;
167pub mod types;
168pub mod crypto;
169pub mod weights;
170
171// Re-Exports for `Config` usage
172
173pub use crate::crypto::AffidavitCryptoEd25519;
174pub use crate::crypto::AffidavitCryptoSr25519;
175
176// ===============================================================================
177// `````````````````````````````` PALLET MODULE ``````````````````````````````````
178// ===============================================================================
179
180pub use pallet::*;
181
182#[frame_support::pallet]
183pub mod pallet {
184
185    // ===============================================================================
186    // ````````````````````````````````` IMPORTS `````````````````````````````````````
187    // ===============================================================================
188
189    // --- Local crate imports ---
190    use super::*;
191    use crate::{
192        crypto::ValidatePayload,
193        routines::*,
194        types::*,
195        weights::*,
196    };
197
198    // --- Core / Std ---
199    use core::fmt::Debug;
200
201    // --- FRAME Suite ---
202    use frame_suite::{
203        Countable, ForkLocalDepot, ForksHandler, blockchain::*, elections::*, plugin_types, roles::*, routines::*
204    };
205
206    // --- FRAME Support ---
207    use frame_support::{
208        dispatch::DispatchResult,
209        pallet_prelude::{StorageValue, *},
210        traits::{
211            fungible::{Inspect, Mutate},
212            EstimateNextSessionRotation,
213        },
214    };
215
216    // --- FRAME System ---
217    use frame_system::{
218        offchain::{AppCrypto, SignedPayload},
219        pallet_prelude::{BlockNumberFor, *},
220    };
221
222    // --- Substrate primitives ---
223    use sp_core::Get;
224    use sp_runtime::{
225        RuntimeAppPublic, SaturatedConversion, WeakBoundedVec, traits::{Convert, IdentifyAccount, Saturating}
226    };
227
228    // ===============================================================================
229    // `````````````````````````````` PALLET MARKER ``````````````````````````````````
230    // ===============================================================================
231
232    /// Primary Marker type for the **Chain Manager pallet**.
233    ///
234    /// This pallet provides implementations for traits from
235    /// [`blockchain`](frame_suite::blockchain), [`roles`](frame_suite::roles),
236    /// [`session`](pallet_session), [`offences`](sp_staking::offence)
237    ///
238    /// Implemented traits:
239    ///
240    /// - [`AuthorPoints`]
241    /// - [`ElectionAffidavits`]
242    /// - [`SessionManager`](pallet_session::SessionManager)
243    /// - [`OnOffenceHandler`](sp_staking::offence::OnOffenceHandler)
244    /// - [`RoleActivity`]
245    /// - [`Convert<AuthorId, Option<SessionId>>`](sp_runtime::traits::Convert)
246    #[pallet::pallet]
247    pub struct Pallet<T>(PhantomData<T>);
248
249    // ===============================================================================
250    // ```````````````````````````` INTERNAL PALLET MARKER ```````````````````````````
251    // ===============================================================================
252
253    /// Internal helper struct for implementing not-exposable
254    /// [`blockchain`](frame_suite::blockchain) trait operations.
255    ///
256    /// `Internals` implements the blockchain low-level helper traits:
257    ///
258    /// - [`RewardAuthors`](frame_suite::blockchain::RewardAuthors)
259    /// - [`ElectAuthors`](frame_suite::blockchain::ElectAuthors)
260    /// - [`PenalizeAuthors`](frame_suite::blockchain::PenalizeAuthors)
261    pub(crate) struct Internals<T: Config>(PhantomData<T>);
262
263    // ===============================================================================
264    // `````````````````````````````` CONFIG TRAIT ```````````````````````````````````
265    // ===============================================================================
266
267    /// Configuration trait for the Chain Manager pallet.
268    ///
269    /// This trait defines the types, constants, and dependencies
270    /// that the runtime must provide for this pallet to function.
271    ///
272    /// It extends several other FRAME pallets' `Config` traits, ensuring tight
273    /// integration with Substrate's session, authorship, time-management and
274    /// offence-handling subsystems.
275    ///
276    /// ### Dependencies
277    ///
278    /// Dependencies other than [`frame_system::Config`]:
279    /// - [`pallet_authorship::Config`]: Provides access to the current block author
280    ///   and authorship tracking, used to assign and record block production points.
281    /// - [`pallet_offences::Config`]: Enables detection and handling of offences for
282    ///   misbehaving authors, required for penalty enforcement.
283    /// - [`pallet_session::Config`]: Manages session rotation and validator/author sets;
284    ///   supports session-aware election and reward cycles.
285    /// - [`pallet_session::historical::Config`]: Provides access to historical session
286    ///   data for offence handling.
287    /// - [`pallet_timestamp::Config`] : Provides access to a monotonic deterministic
288    ///   onchain unix timestamp.
289    /// - [`frame_system::offchain::CreateSignedTransaction`]: Enables offchain workers
290    ///   to sign and submit transactions. Required for unsigned extrinsics that rely
291    ///   on signed payload verification ([`ValidateUnsigned`]).
292    #[pallet::config]
293    pub trait Config: frame_system::Config
294        + pallet_authorship::Config
295        + pallet_offences::Config
296        + pallet_session::Config
297        + pallet_session::historical::Config
298        + pallet_timestamp::Config
299        + frame_system::offchain::CreateSignedTransaction<<Self as frame_system::Config>::RuntimeCall>
300    {
301        // --- Runtime Anchors ---
302
303        /// Extrinsic calls aggregation type for the runtime.
304        type RuntimeCall: From<Call<Self>> + Into<<Self as frame_system::Config>::RuntimeCall>;
305
306        /// Events aggregation type for the runtime.
307        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
308
309        // --- Pallet Adapters ---
310
311        /// Adapter type for **author role, and compensation management**.
312        ///
313        /// This associated type integrates multiple traits that together define how
314        /// authors are **managed**, **rewarded**, and **funded**.
315        ///
316        /// It acts as a unified interface between this pallet and the underlying
317        /// role-management, and reward systems.
318        ///
319        /// ## Required Traits
320        /// - [`RoleManager`]: Core author management - handles registration and membership.
321        /// - [`CompensateRoles`]: Defines logic for author **rewards and penalties**.
322        /// - [`FundRoles`]: Enables **external funding or backing** mechanisms for authors.
323        type RoleAdapter: RoleManager<AuthorOf<Self>>
324            // Since `OnOffenceHandler` uses `Perbill` directly hence
325            + CompensateRoles<AuthorOf<Self>, Ratio = PenaltyRatio>
326            + FundRoles<AuthorOf<Self>>;
327
328        /// Adapter type for role **author election management system**.
329        ///
330        /// This associated type integrates multiple traits that together define how
331        /// authors are **elected**.
332        ///
333        /// ## Required Traits
334        /// - [`ElectionManager`]: Conducts author **elections** on behalf of this pallet.
335        /// - [`InspectWeight`]: Provides APIs to **query election weights** for authors.
336        type ElectionAdapter: ElectionManager<AuthorOf<Self>>
337            + InspectWeight<AuthorOf<Self>, ElectionVia<Self>>;
338
339        /// The **asset type** used for distributing rewards and penalties.
340        ///
341        /// This represents a fungible unit (e.g., a token balance) over which
342        /// the pallet performs **reward**, **penalty**, and **funding** operations.
343        ///
344        /// ## Requirements
345        /// - Must implement [`Inspect`], enabling the pallet to query and verify
346        ///   account balances or holdings of authors.
347        ///
348        /// ## Example
349        /// ```ignore
350        /// type Asset = pallet_assets::Pallet<T>;
351        /// ```
352        type Asset: Inspect<
353                AuthorOf<Self>,
354                // Ensures that the pallet's `Asset` type aligns with the assets
355                // used by role-manager system.
356                //
357                // Guarantees that rewards, penalties, and funding operations
358                // use a **consistent asset type**.
359                Balance = AssetOf<Self>,
360            > + Mutate<AuthorOf<Self>>;
361
362        /// Adapter for managing and querying **author points**.
363        ///
364        /// Provides the implementation for tracking points (e.g., block production
365        /// or performance metrics) associated with each author.
366        ///
367        /// This can be set to [`Pallet<Self>`] if using the chain-manager pallet's
368        /// internal implementation, or provided externally via another pallet or adapter.
369        type PointsAdapter: AuthorPoints<AuthorOf<Self>, Self::Points>;
370
371        // --- Pallets-exposed Additional Types ---
372
373        /// Provides the logic to **estimate the length of the next session**.
374        ///
375        /// Used to compute
376        /// - affidavit submission windows,
377        /// - election start blocks, and
378        /// - session timing.
379        type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
380
381        /// Custom application crypto for **affidavit submission and rotation**.
382        ///
383        /// Defines the cryptographic scheme used by offchain workers when
384        /// submitting and rotating affidavits. This associated type creates
385        /// a dedicated `KeyId` namespace for affidavit-related operations.
386        ///
387        /// ## Supported Cryptography
388        /// - [`AffidavitCryptoEd25519`]
389        /// - [`AffidavitCryptoSr25519`]
390        ///
391        /// Either scheme may be used depending on the desired security and
392        /// performance characteristics.
393        type AffidavitCrypto: frame_system::offchain::AppCrypto<Self::Public, Self::Signature>;
394
395        // --- Scalars ---
396
397        /// Type representing **good-behaviour points** accumulated by an author.
398        ///
399        /// This is an abstract point type used to measure good behaviour
400        /// (e.g. block production) within a session.
401        ///
402        /// ## Design Notes
403        /// - Points are **session-scoped** and non-transferable.
404        /// - This is **not** an economic asset.
405        /// - Points are interpreted later by reward and penalty logic
406        ///   and may be transformed into actual asset movements.
407        type Points: Countable;
408
409        // --- Plugins ---
410
411        // Plugin for computing **inflation-adjusted total payout**.
412        plugin_types!(
413            input: AssetOf<Self>,
414            output: AssetOf<Self>,
415
416            /// Inflation adjustment **plugin model**.
417            ///
418            /// This model defines the logic used to transform the raw total asset
419            /// (`AssetOf`) into an inflation-adjusted payout before distribution
420            /// to authors.
421            ///
422            /// Conceptually similar to `PayoutModel` in `RewardAuthors`, but focused
423            /// specifically on inflation-based adjustments in a more layman-friendly
424            /// abstraction.
425            ///
426            /// ## Input
427            /// - [`AssetOf`]: The raw total asset for the current cycle.
428            ///
429            /// ## Output
430            /// - [`AssetOf`]: The total asset after applying inflation rules.
431            ///
432            /// Designed to be runtime-configurable via [`Self::InflationContext`].
433            ///
434            /// Designed to be selectable using template plugin models in
435            /// [`frame_plugins::rewards::payout`] or custom model defining
436            /// macros via [`frame_suite::plugins`].
437            model: InflationModel,
438
439            /// Runtime **context** for the inflation adjustment plugin model.
440            ///
441            /// Provides configurable parameters that influence how the
442            /// [`Self::InflationModel`] behaves at runtime.
443            ///
444            /// ## Examples
445            /// - Inflation rates
446            /// - Upper or lower payout caps
447            /// - Scaling coefficients or dampening factors
448            ///
449            /// This allows the inflation logic to be tuned without changing the model
450            /// implementation itself.
451            context: InflationContext,
452        );
453
454        // Plugin for computing **per-author rewards** from total payout and points.
455        plugin_types!(
456            input: (AssetOf<Self>, PayoutFor<Self>),
457            output: PayeeList<Self>,
458
459            /// Per-author **reward distribution plugin model**.
460            ///
461            /// Responsible for mapping each author's contribution to a concrete payout.
462            /// Conceptually performs:
463            ///
464            /// `(Author, Points)` -> `(Author, AssetOf)`
465            ///
466            /// ## Input
467            /// - ([`AssetOf`], [`PayoutFor`]):
468            ///   The total payout for the cycle and the per-author points allocation.
469            ///
470            /// ## Output
471            /// - [`PayeeList`]:
472            ///   The finalized list of authors and their respective rewards.
473            ///
474            /// ## Notes
475            /// - Maps each author's points to their final reward share.
476            /// - Designed to be runtime-configurable via [`Self::RewardContext`].
477            ///
478            /// Designed to be selectable using template plugin models in
479            /// [`frame_plugins::rewards::payee`] or custom model defining
480            /// macros via [`frame_suite::plugins`].
481            model: RewardModel,
482
483            /// Runtime **context** for configuring reward plugin computation.
484            ///
485            /// Supplies parameters that influence how rewards are calculated and
486            /// distributed by the [`Self::RewardModel`].
487            ///
488            /// ## Examples
489            /// - Multipliers or weights
490            /// - Per-author or global caps
491            /// - Curves, thresholds, or smoothing factors
492            ///
493            /// This separation allows payout logic to evolve independently from
494            /// runtime tuning and governance decisions.
495            context: RewardContext,
496        );
497
498        // Plugin for **transforming penalties** of authors.
499        plugin_types!(
500            input: PenaltyFor<Self>,
501            output: PenaltyFor<Self>,
502
503            /// **Penalty transformation plugin model**.
504            ///
505            /// Defines how author penalties are adjusted before being applied.
506            /// Conceptually performs:
507            ///
508            /// `(Author, Penalty)` -> `(Author, Penalty)`
509            ///
510            /// ## Input
511            /// - [`PenaltyFor`]:
512            ///   The raw per-author penalties for the current cycle.
513            ///
514            /// ## Output
515            /// - [`PenaltyFor`]:
516            ///   The penalties after applying transformation rules.
517            ///
518            /// ## Notes
519            /// - Supports caps, scaling, or other penalty transformation logic.
520            /// - Runtime behavior is influenced by [`Self::PenaltyContext`].
521            ///
522            /// Designed to be selectable using template plugin models in
523            /// [`frame_plugins::penalty`] or custom model defining
524            /// macros via [`frame_suite::plugins`].
525            model: PenaltyModel,
526
527            /// Runtime **context** for penalty plugin transformation.
528            ///
529            /// Supplies parameters that configure how the [`Self::PenaltyModel`] operates
530            /// at runtime.
531            ///
532            /// ## Examples
533            /// - Multipliers or dampening factors
534            /// - Upper or lower penalty caps
535            /// - Thresholds or step functions
536            ///
537            /// This allows penalty policy changes without modifying the model
538            /// implementation.
539            context: PenaltyContext,
540        );
541
542        // --- Weights ---
543
544        /// Weight information for extrinsics in this pallet.
545        type WeightInfo: WeightInfo;
546
547        // --- Constants ---
548
549        /// **Flag for reward inflation model**.
550        ///
551        /// Determines how the total reward pool is computed:
552        /// - `true`: total asset supply is used to calculate inflation/rewards.
553        /// - `false`: total locked stake of authors is used instead.  
554        ///
555        /// This allows flexible reward strategies depending on economic design.
556        #[pallet::constant]
557        type InflateViaSupply: Get<bool> + Clone + Debug;
558
559        /// Maximum number of **weights an author can submit in an affidavit**.
560        ///
561        /// Weight represents backers or collateral information essential for
562        /// conducting elections.
563        ///
564        /// Limits storage and ensures predictable handling of submitted affidavits.
565        /// If an author submits more than this number, the weights may be truncated.
566        ///
567        /// Each weight must remain sortable (`Ord`) for ensuring priority ordering during
568        /// truncation to this upper bound.
569        #[pallet::constant]
570        type MaxAffidavitWeights: Get<u32> + Clone + Debug;
571
572        const MAX_FORKS: u32;
573
574        const MAX_FORK_RECOVERY_TRAVERSAL: u32;
575
576        /// Controls emission of [`Event`] via `deposit_event`.
577        ///
578        /// Recommended:
579        /// - `false` for production runtimes (to reduce overhead)
580        /// - `true` for development and mock runtimes (for testing and
581        /// observability)
582        #[pallet::constant]
583        type EmitEvents: Get<bool> + Clone + Debug;
584    }
585
586    // ===============================================================================
587    // ``````````````````````````````` GENESIS CONFIG ````````````````````````````````
588    // ===============================================================================
589
590    /// Genesis configuration for the **Chain Manager pallet**.
591    ///
592    /// Provides the **initial runtime parameters** governing session-driven
593    /// validator orchestration, including affidavit flow, election timing,
594    /// transaction prioritization, and finality safeguards at chain genesis.
595    ///
596    /// These values define the **baseline execution schedule and coordination rules**
597    /// for validator lifecycle before any sessions are processed.
598    #[pallet::genesis_config]
599    pub struct GenesisConfig<T: Config> {
600        /// Whether affidavit submission is enabled.
601        /// - `true`: authors may submit affidavit data during the configured window.
602        /// - `false`: affidavit submission is entirely disabled.
603        pub allow_affidavits: bool,
604
605        /// Relative point within a session at which the affidavit phase begins.
606        ///
607        /// Expressed as a fraction of the session (e.g., `0.2` = 20% into the session).
608        /// Defines when authors can start submitting affidavit.
609        /// Submissions made before this point are not permitted.
610        pub afdvt_begins_at: Duration,
611
612        /// Relative point within a session at which the affidavit phase ends.
613        ///
614        /// Must be greater than `afdvt_begins_at` and within session bounds.
615        /// After this point, no new affidavit submissions are accepted.
616        pub afdvt_ends_at: Duration,
617
618        /// Relative point within a session at which election execution begins.
619        ///
620        /// Typically aligned with or after the affidavit phase to ensure
621        /// all candidate data is available for selection.
622        pub election_begins_at: Duration,
623
624        /// Number of points awarded to the author responsible for executing elections.
625        ///
626        /// Acts as an incentive for offchain workers or authors coordinating
627        /// election execution in a timely manner.
628        pub election_runner_points: T::Points,
629
630        /// Transaction priority assigned to validation-related unsigned extrinsics.
631        ///
632        /// Higher priority ensures inclusion in blocks under contention.
633        /// Should typically be the highest among lifecycle operations.
634        pub validate_tx_priority: TransactionPriority,
635
636        /// Transaction priority assigned to affidavit submission extrinsics.
637        ///
638        /// Balanced to allow fair participation without starving higher-priority
639        /// operations such as validation.
640        pub affidavit_tx_priority: TransactionPriority,
641
642        /// Transaction priority assigned to election execution extrinsics.
643        ///
644        /// Ensures elections are processed in time, but typically lower than
645        /// validation and affidavit priorities.
646        pub election_tx_priority: TransactionPriority,
647
648        /// Time-based delay (in milliseconds) before an operation is considered final.
649        ///
650        /// Helps mitigate premature execution in unstable network conditions
651        /// or during short-lived forks.
652        pub finality_after: u64,
653
654        /// Block-based confirmation threshold for finality.
655        ///
656        /// Represents the number of distinct blocks that must pass before
657        /// an operation is finalized, providing deterministic safety
658        /// against reorgs.
659        pub finality_ticks: BlockNumberFor<T>,
660    }
661
662    impl<T: Config> Default for GenesisConfig<T> {
663        fn default() -> Self {
664            Self {
665                allow_affidavits: false,
666                afdvt_begins_at: Duration::from_rational(2u32, 10u32), // 20%
667                afdvt_ends_at: Duration::from_rational(8u32, 10u32),   // 80%
668                election_begins_at: Duration::from_rational(5u32, 10u32), // 50%
669                election_runner_points: 10u8.into(),
670                validate_tx_priority: 1_000_000,
671                affidavit_tx_priority: 850_000,
672                election_tx_priority: 700_000,
673                // 1 minute delay
674                finality_after: 60_000,
675                // 5 distinct blocks
676                finality_ticks: 5u32.into(),
677            }
678        }
679    }
680
681    #[pallet::genesis_build]
682    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
683        fn build(&self) {
684            // --- Affidavit window ---
685            assert!(
686                self.afdvt_begins_at < self.afdvt_ends_at,
687                "`AffidavitBeginsAt` must be less than `AffidavitEndsAt`"
688            );
689            assert!(
690                self.afdvt_ends_at <= Duration::one(),
691                "`AffidavitEndsAt` cannot exceed 100%"
692            );
693            assert!(
694                self.election_begins_at <= Duration::one(),
695                "`ElectionBeginsAt` must be within 0-100% of affidavit window"
696            );
697
698            // --- Tx priorities ---
699            assert!(self.validate_tx_priority > 0);
700            assert!(self.election_tx_priority > 0);
701            assert!(self.affidavit_tx_priority > 0);
702
703            // --- Finality ---
704            assert!(self.finality_after > 0);
705            assert!(self.finality_ticks > Zero::zero());
706
707            AllowAffidavits::<T>::put(self.allow_affidavits);
708            AffidavitBeginsAt::<T>::put(self.afdvt_begins_at);
709            AffidavitEndsAt::<T>::put(self.afdvt_ends_at);
710            ElectionBeginsAt::<T>::put(self.election_begins_at);
711            ElectionRunnerPoints::<T>::put(self.election_runner_points);
712            ValidateTxPriority::<T>::put(self.validate_tx_priority);
713            ElectionTxPriority::<T>::put(self.election_tx_priority);
714            AffidavitTxPriority::<T>::put(self.affidavit_tx_priority);
715            let moment: Moment<T> = self.finality_after.saturated_into();
716            FinalityAfter::<T>::put(moment);
717            FinalityTicks::<T>::put(self.finality_ticks);
718        }
719    }
720
721    // ===============================================================================
722    // ```````````````````````````````` STORAGE TYPES ````````````````````````````````
723    // ===============================================================================
724
725    /// The **current running session index**.
726    ///
727    /// Used to track session-aware operations such as:
728    /// - Author points accumulation ([`AuthorPoints`])
729    /// - Reward and penalty distribution
730    /// - Affidavit submission and election preparation
731    #[pallet::storage]
732    pub type CurrentSession<T: Config> = StorageValue<_, SessionIndex, ValueQuery>;
733
734    /// Storage mapping of **authors' submitted affidavits per session**.
735    ///
736    /// Keyed by:
737    /// 1. [`SessionIndex`] - the session for which the affidavit applies for election.
738    /// 2. [`AuthorOf`] - the author submitting the affidavit
739    ///
740    /// Value is a tuple of:
741    /// - [`BlockNumberFor`]: the block when the affidavit was submitted and,
742    /// - A `WeakBoundedVec` of [`ElectionWeight`]: the author's declared weights,
743    /// bounded to prevent excessive storage
744    ///
745    /// This storage is used for:
746    /// - Preparing election candidates for the next session
747    #[pallet::storage]
748    pub type AuthorAffidavits<T: Config> = StorageNMap<
749        _,
750        (
751            NMapKey<Blake2_128Concat, SessionIndex>,
752            NMapKey<Blake2_128Concat, AuthorOf<T>>,
753        ),
754        (
755            BlockNumberFor<T>,
756            WeakBoundedVec<ElectionWeight<T>, T::MaxAffidavitWeights>,
757        ),
758        OptionQuery,
759    >;
760
761    /// **Author points accumulated per session**.
762    ///
763    /// This storage tracks **block-level points** awarded to authors for good behavior
764    /// during block production. Each increment represents a positive contribution,
765    /// such as successfully producing a block or validating transactions correctly.
766    ///
767    /// Keyed by:
768    /// 1. [`SessionIndex`]: the session in which points are earned.
769    /// 2. [`AuthorOf`]: the author receiving the points.
770    ///
771    /// Value:
772    /// - [`Config::Points`]: the total points accumulated by the author in that session.
773    ///
774    /// Notes:
775    /// - This serves as the **high-level points store** for reward calculation.
776    /// - Each block-level good behavior contributes **one point** to this tally.
777    /// - Points are **session-scoped and ephemeral**; typically cleared after reward distribution.
778    /// - Can be used as a reference for evaluating other contributions or behaviors of authors.
779    #[pallet::storage]
780    pub type BlockPointsStore<T: Config> = StorageNMap<
781        _,
782        (
783            NMapKey<Blake2_128Concat, SessionIndex>,
784            NMapKey<Blake2_128Concat, AuthorOf<T>>,
785        ),
786        T::Points,
787        OptionQuery,
788    >;
789
790    /// **Start block of the current session**.
791    ///
792    /// Tracks the block number when the current session began.  
793    /// Used for:
794    /// - Computing session-relative timing for elections and affidavits.
795    #[pallet::storage]
796    pub type SessionStartAt<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery>;
797
798    /// **Flag to enable or disable affidavit submissions**.
799    ///
800    /// Controls whether authors can submit self-reported election weights.  
801    /// When `true`, affidavit-related functions are active; when `false`, submissions
802    /// are blocked. Initially, this is expected to be `false` and gradually enabled once
803    /// the required authors are ready.
804    #[pallet::storage]
805    pub type AllowAffidavits<T: Config> = StorageValue<_, bool, ValueQuery>;
806
807    // /// **Flag for reward inflation model**.
808    // ///
809    // /// Determines how the total reward pool is computed:
810    // /// - `true`: total asset supply is used to calculate inflation/rewards.
811    // /// - `false`: total locked stake of authors is used instead.
812    // ///
813    // /// This allows flexible reward strategies depending on economic design.
814    // #[pallet::storage]
815    // pub type InflateViaSupply<T: Config> = StorageValue<_, bool, ValueQuery>;
816
817    /// **Start of affidavit submission window** as a percentage ([`PerThing`]) of the
818    /// current session. Authors submit affidavits for the **next upcoming session**
819    /// starting from this point.
820    ///
821    /// ## Examples
822    /// - If the current session is 1000 blocks long and `AffidavitBeginsAt = 20%`,
823    ///   affidavit submissions can start at block `200` of the current session.
824    /// - Authors cannot submit affidavits before this block, even if they are ready.
825    #[pallet::storage]
826    pub type AffidavitBeginsAt<T: Config> = StorageValue<_, Duration, ValueQuery>;
827
828    /// **End of affidavit submission window** as a percentage ([`PerThing`]) of the
829    /// current session.
830    ///
831    /// Authors must submit affidavits **before this block**, leaving room for the
832    /// election process for the next upcoming session.
833    ///
834    /// ## Examples
835    /// - If the current session is 100 blocks long and `AffidavitEndsAt = 80%`,
836    ///   affidavit submissions must end by block `800`.
837    /// - The period from `AffidavitBeginsAt` to `AffidavitEndsAt` defines the **affidavit
838    ///   submission window**.
839    /// - Elections for the next session should be conducted before this period ends.
840    #[pallet::storage]
841    pub type AffidavitEndsAt<T: Config> = StorageValue<_, Duration, ValueQuery>;
842
843    /// **Start of the election window** as a percentage ([`PerThing`]) of the **affidavit
844    /// submission period** in the current session.
845    ///
846    /// This does **not** refer to a percentage of the full session, but rather the **relative
847    /// position within the affidavit submission window**. Authors submit affidavits first, then
848    /// after this point, the election process can start.
849    ///
850    /// ## Examples
851    /// - Suppose the affidavit window spans blocks 200-800 (`AffidavitBeginsAt = 20%`,
852    ///   `AffidavitEndsAt = 80%` in a 100-block session).
853    /// - If `ElectionBeginsAt = 50%`, then the election starts halfway through the
854    ///   affidavit window: 200 + 50% * (800 - 200) = block 500.
855    /// - Timeline:
856    ///     - 200-500: affidavit submission period
857    ///     - 500-800: election preparation
858    #[pallet::storage]
859    pub type ElectionBeginsAt<T: Config> = StorageValue<_, Duration, ValueQuery>;
860
861    /// **Block points allocated for the election runner**.
862    ///
863    /// Determines how many [`Config::Points`] are required or suitable for an author
864    /// to act as the election runner in the upcoming session.
865    ///
866    /// This value is used to assess election participation eligibility or priority.
867    #[pallet::storage]
868    pub type ElectionRunnerPoints<T: Config> = StorageValue<_, T::Points, ValueQuery>;
869
870    /// **Pending upgrade of election runner points**.
871    ///
872    /// When an upgrade occurs, this should be set to `None` after being applied.
873    ///
874    /// ## Notes
875    /// - Upgrades should occur **after all author rewards have been distributed**
876    ///   and before the next reward cycle starts.
877    /// - Ensures fairness: if upgraded mid-session, authors who have not yet
878    ///   submitted affidavits may be disadvantaged in election chances.
879    /// - Any user of [`ElectionRunnerPoints`] should take this storage value into account
880    /// for the upcoming session.
881    #[pallet::storage]
882    pub type ElectionRunnerPointsUpgrade<T: Config> =
883        StorageValue<_, Option<T::Points>, ValueQuery>;
884
885    /// **Offchain affidavit keys registered by an author for a given session**.
886    ///
887    /// Retrieves the author who submitted affidavit keys, tied to a specific session.  
888    ///
889    /// - Authenticate offchain submissions (affidavits) for the upcoming session.
890    /// - Ensure that only valid authors can participate in elections or submit weights.
891    /// - Maintain session-specific separation, so keys do not persist beyond their intended session.
892    ///
893    /// ## Storage Structure
894    /// - [`SessionIndex`]: the session for which the keys are valid.
895    /// - [`AffidavitId`]: the actual affidavit public key ID, type chosen by
896    /// the runtime configuration [`Config::AffidavitCrypto`].
897    /// - [`AuthorOf<T>`]: the author who owns the key-pairs for signing.
898    #[pallet::storage]
899    pub type AffidavitKeys<T: Config> = StorageNMap<
900        _,
901        (
902            NMapKey<Blake2_128Concat, SessionIndex>,
903            NMapKey<Blake2_128Concat, AffidavitId<T>>,
904        ),
905        AuthorOf<T>,
906        OptionQuery,
907    >;
908
909    /// **Tracks the author who prepared the election for a given session**.
910    ///
911    /// Stores the identity of the election runner and the block number at which
912    /// they executed the election process for the **upcoming session**.
913    ///
914    /// This storage is **tied to the session index** and may be **overwritten**
915    /// if multiple election runs occur within the same session.  
916    /// This ensures that the latest election runner and block number are always recorded.
917    ///
918    /// This allows the pallet to:
919    /// - Identify which author acted as the election runner for auditing or reward purposes.
920    /// - Record the exact block when the election was conducted, ensuring deterministic session transitions.
921    /// - Keep track of the most recent election preparation for the session.
922    ///
923    /// ## Storage Structure
924    /// - [`SessionIndex`]: the session for which the election was prepared.
925    /// - ([`AuthorOf`], [`BlockNumberFor`]): tuple of the election runner and the block number of execution.
926    /// - [`OptionQuery`]: returns `None` if no election has been prepared for the session or yet.
927    #[pallet::storage]
928    pub type ElectsPreparedBy<T: Config> = StorageMap<
929        _,
930        Blake2_128Concat,
931        SessionIndex,
932        (AuthorOf<T>, BlockNumberFor<T>),
933        OptionQuery,
934    >;
935
936    /// Transaction priority for the [`Pallet::validate`] extrinsic.
937    ///
938    /// Used because `validate` is an **unsigned extrinsic** that carries a
939    /// signed payload, rather than relying on a conventional signed origin.
940    ///
941    /// This extrinsic **is propagated to other peers** and participates in
942    /// normal transaction pool ordering, so its priority directly affects
943    /// inclusion and ordering behavior.
944    #[pallet::storage]
945    pub type ValidateTxPriority<T: Config> = StorageValue<_, TransactionPriority, ValueQuery>;
946
947    /// Transaction priority for the [`Pallet::elect`] extrinsic.
948    ///
949    /// This extrinsic is **submitted locally by an offchain worker** and
950    /// **not propagated to other peers** via the transaction pool.
951    ///
952    /// The priority therefore only affects **local block construction**
953    /// and ordering relative to other locally submitted transactions.
954    #[pallet::storage]
955    pub type ElectionTxPriority<T: Config> = StorageValue<_, TransactionPriority, ValueQuery>;
956
957    /// Transaction priority for the [`Pallet::declare`] extrinsic.
958    ///
959    /// Controls the relative importance of affidavit submissions in the
960    /// transaction pool.
961    ///
962    /// This extrinsic **is propagated to other peers**, so its priority
963    /// influences network-wide inclusion and ordering behavior.
964    #[pallet::storage]
965    pub type AffidavitTxPriority<T: Config> = StorageValue<_, TransactionPriority, ValueQuery>;
966
967    /// Wall-clock delay (in timestamp units) that must elapse after the
968    /// first observation of a value before it becomes eligible to
969    /// strengthen its confidence signal.
970    ///
971    /// Measured from the timestamp of the initial observation:
972    ///
973    /// `current_timestamp >= first_observation_timestamp + FinalityAfter`
974    ///
975    /// This acts as a stability window to prevent immediate confidence
976    /// escalation for newly observed values.
977    ///
978    /// ### Note
979    /// - Must be strictly greater than zero.
980    /// - Should be large enough to tolerate short-lived forks.
981    #[pallet::storage]
982    pub type FinalityAfter<T: Config> = StorageValue<_, Moment<T>, ValueQuery>;
983
984    /// Number of distinct block observations required *after* the
985    /// [`FinalityAfter`] window has elapsed in order to strengthen
986    /// the confidence signal of a value.
987    ///
988    /// Observations are block-scoped:
989    /// - At most one observation per block is counted.
990    /// - Multiple observations within the same block do not increase the count.
991    ///
992    /// A value may strengthen its confidence only when:
993    /// 1. The [`FinalityAfter`] delay has elapsed, and
994    /// 2. It has been observed in at least `FinalityTicks`
995    ///    distinct blocks thereafter.
996    ///
997    /// ### Note
998    /// - Must be strictly greater than zero.
999    /// - Larger values increase fork tolerance but delay confidence.
1000    #[pallet::storage]
1001    pub type FinalityTicks<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery>;
1002
1003    // ===============================================================================
1004    // ```````````````````````````````````` ERROR ````````````````````````````````````
1005    // ===============================================================================
1006
1007    #[pallet::error]
1008    pub enum Error<T> {
1009        /// The requested affidavit-ready author was not found.
1010        ///
1011        /// This may happen if:
1012        /// - The author has not declared readiness for validation via  
1013        /// `validate` extrinsic
1014        /// - The author has not declared affidavit keys for the upcoming session
1015        /// - The provided affidavit key is invalid
1016        AffidavitAuthorNotFound,
1017
1018        /// Block production points for the specified author were not found.
1019        BlockPointsNotFound,
1020
1021        /// The author has exhausted the maximum allowed block production points
1022        /// for the current session.
1023        ///
1024        /// Consider scaling the points type or constraining accumulation
1025        /// according to block production frequency.
1026        BlockPointsExhausted,
1027
1028        /// The specified author has not submitted an affidavit
1029        /// for the upcoming session.
1030        AffidavitNotFound,
1031
1032        /// Affidavit submissions are not enabled by configuration.
1033        AffidavitsNotAllowed,
1034
1035        /// The affidavit submission window for the upcoming session
1036        /// has not started yet in the current session.
1037        NotAffidavitPeriod,
1038
1039        /// The affidavit submission window for the upcoming session has ended.
1040        ///
1041        /// The author must wait for the next session's affidavit period.
1042        AffidavitPeriodEnded,
1043
1044        /// The election period for the upcoming session
1045        /// has not started yet in the current session.
1046        NotElectionPeriod,
1047
1048        /// Invalid affidavit configuration.
1049        ///
1050        /// `AffidavitBeginsAt` must be less than or equal to `AffidavitEndsAt`.
1051        InvalidAffidavitPeriod,
1052
1053        /// The election period for the upcoming session has ended.
1054        ///
1055        /// The author must wait for the next session's election period.
1056        ElectionPeriodEnded,
1057
1058        /// The author has not registered ownership of the given affidavit key.
1059        ///
1060        /// The key itself may be valid, but it does not belong to the author.
1061        AuthorNotAffidavitOwner,
1062
1063        /// Failed to query the session ID for the author.
1064        ///
1065        /// This usually indicates that the author does not hold
1066        /// an active author role.
1067        SessionIdQueryFailed,
1068
1069        /// The author cannot chill immediately because they are
1070        /// an elected validator in the current session.
1071        ValidatorCannotChill,
1072
1073        /// The author cannot chill immediately because they have submitted
1074        /// an affidavit and are a candidate for election.
1075        CandidateCannotChill,
1076
1077        /// The author cannot chill immediately because they are elected
1078        /// for the upcoming session.
1079        ElectedCannotChill,
1080
1081        /// An active affidavit key already exists in the node's local keystore.
1082        ///
1083        /// No new key generation is required. If the node has a valid author role,
1084        /// it may already be ready for `validate` extrinsic.
1085        ///
1086        /// Or if the author is ready to declare affidavit he is eligible to do so.
1087        AffidavitKeyExists,
1088
1089        /// A next-session affidavit key already exists in the node's local keystore.
1090        ///
1091        /// An affidavit declaration is ready to be posted and the key
1092        /// is ready for rotation.
1093        NextAffidavitKeyExists,
1094
1095        /// Failed to sign the affidavit submission extrinsic payload
1096        /// using the active affidavit key.
1097        CannotSignAffidavitTxPayload,
1098
1099        /// Failed to construct and sign validate payload
1100        /// using the active affidavit key.
1101        CannotSignValidateTxPayload,
1102
1103        /// The node is expected to hold an active affidavit key, but none was found.
1104        ///
1105        /// This indicates an inconsistency between earlier logic and current storage.
1106        /// The OCW will retry, but persistent failure suggests storage corruption.
1107        ExpectedToHoldActiveAffidavitKey,
1108
1109        /// The node is expected to not hold a active affidavit key in offchain storage,
1110        /// but it is available.
1111        ///
1112        /// This indicates an inconsistency between earlier logic and current storage.
1113        /// The OCW will retry, but persistent failure suggests storage corruption.
1114        ExpectedToNotHoldActiveAffidavitKey,
1115
1116        /// The node is expected to hold the next affidavit key during
1117        /// the key-rotation lifecycle, but it is missing.
1118        ///
1119        /// This indicates an inconsistency between earlier logic and current storage.
1120        /// The OCW will retry, but persistent failure suggests storage corruption.
1121        ExpectedToHoldNextAffidavitKey,
1122
1123        /// The node is expected to hold a finalized next affidavit key
1124        /// in offchain storage, but it is missing.
1125        ///
1126        /// This indicates an inconsistency between earlier logic and current storage.
1127        /// The OCW will retry, but persistent failure suggests storage corruption.
1128        ExpectedToHoldFinalizedNextAffidavitKey,
1129
1130        /// An offchain storage operation failed.
1131        ///
1132        /// The OCW halts further decisions for safety.
1133        /// Thus, re-runs in next block execution.
1134        OCWStorageDecisionHalt,
1135
1136        /// The offchain worker failed to submit the affidavit extrinsic
1137        /// for unknown reasons.
1138        CannotSubmitAffidavitTx,
1139
1140        /// Extrinsic submission for declaring an affidavit failed.
1141        FailedToDeclareAffidavit,
1142
1143        /// The election period for the upcoming session
1144        /// has not started yet.
1145        YetToElectAuthors,
1146
1147        /// Failed to sign the election extrinsic payload using
1148        /// the very recently rotated affidavit key.
1149        ///
1150        /// Subsequent OCW executions at next block shall attempt again.
1151        CannotSignElectionTxPayload,
1152
1153        /// The offchain worker failed to submit the election extrinsic
1154        /// for unknown reasons.
1155        ///
1156        /// Subsequent OCW executions at next block shall attempt again.
1157        CannotSubmitElectionTx,
1158
1159        /// Extrinsic submission for electing authors failed.
1160        ///
1161        /// Subsequent OCW executions at next block shall attempt again.
1162        ExtrinsicFailedToElectAuthors,
1163
1164        /// A new affidavit key was generated but failed to persist locally.
1165        ///
1166        /// The OCW will attempt another affidavit submission with a new key.
1167        ///
1168        /// Persistent failure indicates storage corruption.
1169        SetNewAffidavitKeyFailed,
1170
1171        /// A new affidavit key for rotation and promotion as active
1172        /// affidavit key was generated but failed to persist locally.
1173        ///
1174        /// The OCW will attempt another affidavit key-rotation with a new key.
1175        ///
1176        /// Persistent failure indicates storage corruption.
1177        SetNextAffidavitKeyFailed,
1178
1179        /// The active affidavit key is expected in the local keystore
1180        /// but could not be found.
1181        ///
1182        /// Recovery requires regenerating the key under the configured
1183        /// `KeyTypeId` or chilling and restarting validation.
1184        ExpectedActiveAffidavitKeyPairNotFound,
1185
1186        /// The rotated next affidavit key is expected in the local keystore
1187        /// but could not be found.
1188        ///
1189        /// Recovery requires regenerating the key under the configured
1190        /// `KeyTypeId` or chilling and restarting validation.
1191        ExpectedNextAffidavitKeyPairNotFound,
1192
1193        /// Validation has been stopped due to affidavit key rotation failure,
1194        /// storage corruption, or failed submissions.
1195        ///
1196        /// Manual re-validation via `validate` extrinsic is required.
1197        ValidationStopped,
1198
1199        /// The author is actively validating in the current session.
1200        ///
1201        /// The `chill` extrinsic is blocked until validation completes
1202        /// or the session ends.
1203        ActivelyValidating,
1204
1205        /// The author has submitted an affidavit and is actively
1206        /// participating in the election.
1207        ///
1208        /// The author may `chill` if allowed or wait until
1209        /// the election concludes to check assigned duty.
1210        ActivelyContestingElection,
1211
1212        /// The author has been elected for the upcoming session
1213        /// and is preparing for validation duties.
1214        ///
1215        /// The `chill` extrinsic is blocked until duties complete.
1216        ActivelyWarmingForValidation,
1217
1218        /// The author is detected as active, but the specific duty
1219        /// cannot be determined due to inconsistent state.
1220        ///
1221        /// The `chill` extrinsic may be attempted to resolve the state.
1222        CannotDetermineAuthorActiveDuty,
1223
1224        /// Offchain storage Active Affidavit Key is not yet finalized.
1225        ///
1226        /// The key exists optimistically in fork-aware storage, but the block
1227        /// containing the finalized value may still be subject to re-org.
1228        /// Until finalized, speculative forks may observe different values.
1229        ///
1230        /// Until it is finalized subsequent OCWs may result in this state.
1231        ActiveAfdtKeyNotYetFinalized,
1232
1233        /// Failed to decode the finalized Active Affidavit Key public key value
1234        /// from persistent offchain storage.
1235        ///
1236        /// Persistent failure indicates either storage corruption or a type
1237        /// mismatch. Manual intervention may be required. Clearing both
1238        /// fork-aware and persistent offchain storage allows OCWs to
1239        /// reinitialize the key lifecycle.
1240        ActiveAfdtKeyFinalizedValueDecodeFail,
1241
1242        /// Failed to decode the speculative hash of the Active Affidavit Key
1243        /// public key from fork-aware offchain storage.
1244        ///
1245        /// This hash is used to reference the actual value stored in
1246        /// persistent offchain storage. Persistent failure indicates
1247        /// corruption or decoding inconsistencies.
1248        /// Clearing both fork-aware and persistent storage may resolve it.
1249        ActiveAfdtKeySpeculativeHashDecodeFail,
1250
1251        /// Concurrent mutation detected while accessing the finalized
1252        /// Active Affidavit Key public key value.
1253        ///
1254        /// The operation will be retried by the OCW. Persistent failure
1255        /// indicates a potential deadlock or storage corruption.
1256        /// Clearing both fork-aware and persistent offchain storage
1257        /// may be required.
1258        ActiveAfdtKeyFinalizedValueConcurrentMutation,
1259
1260        /// Concurrent mutation detected while accessing the speculative
1261        /// hash of the Active Affidavit Key public key.
1262        ///
1263        /// The hash resides in fork-aware storage while the actual value
1264        /// exists in persistent storage. The operation will be retried
1265        /// by the OCW. Persistent failure indicates storage corruption
1266        /// or a deadlock condition.
1267        ActiveAfdtKeySpeculativeHashConcurrentMutation,
1268
1269        /// A finalized Active Affidavit Key public key value exists in
1270        /// persistent offchain storage without a corresponding
1271        /// fork-aware hash.
1272        ///
1273        /// This results in a hanging value with no canonical reference.
1274        /// The persistent value will be cleared automatically. Any
1275        /// remaining fork-aware entries may also become hanging and
1276        /// will be cleaned on access.
1277        ActiveAfdtKeyFinalizedHangingValue,
1278
1279        /// A speculative fork-aware hash for the Active Affidavit Key exists
1280        /// without a corresponding persistent public key value.
1281        ///
1282        /// Since the actual value cannot be recovered, the hanging
1283        /// speculative hash will be cleared automatically.
1284        ActiveAfdtKeySpeculativeHangingHash,
1285
1286        /// Offchain storage Next Affidavit Key is not yet finalized.
1287        ///
1288        /// The key exists optimistically in fork-aware storage, but the block
1289        /// containing the finalized value may still be subject to re-org.
1290        /// Until finalized, speculative forks may observe different values.
1291        ///
1292        /// Until it is finalized subsequent OCWs may result in this state.
1293        NextAfdtKeyNotYetFinalized,
1294
1295        /// Failed to decode the finalized Next Affidavit Key public key value
1296        /// from persistent offchain storage.
1297        ///
1298        /// Persistent failure indicates either storage corruption or a type
1299        /// mismatch. Manual intervention may be required. Clearing both
1300        /// fork-aware and persistent offchain storage allows OCWs to
1301        /// reinitialize the key lifecycle.
1302        NextAfdtKeyFinalizedValueDecodeFail,
1303
1304        /// Failed to decode the speculative hash of the Next Affidavit Key
1305        /// public key from fork-aware offchain storage.
1306        ///
1307        /// This hash is used to reference the actual value stored in
1308        /// persistent offchain storage. Persistent failure indicates
1309        /// corruption or decoding inconsistencies.
1310        /// Clearing both fork-aware and persistent storage may resolve it.
1311        NextAfdtKeySpeculativeHashDecodeFail,
1312
1313        /// Concurrent mutation detected while accessing the finalized
1314        /// Next Affidavit Key public key value.
1315        ///
1316        /// The operation will be retried by the OCW. Persistent failure
1317        /// indicates a potential deadlock or storage corruption.
1318        /// Clearing both fork-aware and persistent offchain storage
1319        /// may be required.
1320        NextAfdtKeyFinalizedValueConcurrentMutation,
1321
1322        /// Concurrent mutation detected while accessing the speculative
1323        /// hash of the Next Affidavit Key public key.
1324        ///
1325        /// The hash resides in fork-aware storage while the actual value
1326        /// exists in persistent storage. The operation will be retried
1327        /// by the OCW. Persistent failure indicates storage corruption
1328        /// or a deadlock condition.
1329        NextAfdtKeySpeculativeHashConcurrentMutation,
1330
1331        /// A finalized Next Affidavit Key public key value exists in
1332        /// persistent offchain storage without a corresponding
1333        /// fork-aware hash.
1334        ///
1335        /// This results in a hanging value with no canonical reference.
1336        /// The persistent value will be cleared automatically. Any
1337        /// remaining fork-aware entries may also become hanging and
1338        /// will be cleaned on access.
1339        NextAfdtKeyFinalizedHangingValue,
1340
1341        /// A speculative fork-aware hash for the Next Affidavit Key exists
1342        /// without a corresponding persistent public key value.
1343        ///
1344        /// Since the actual value cannot be recovered, the hanging
1345        /// speculative hash will be cleared automatically.
1346        NextAfdtKeySpeculativeHangingHash,
1347
1348        /// The affidavit submission extrinsic has been sent by the OCW
1349        /// and is awaiting on-chain state confirmation and key rotation.
1350        AffidavitTxAwaitingStatus,
1351
1352        /// Not an error.
1353        ///
1354        /// Indicates that a neccessary election attempt was made early,
1355        /// so the OCW will proceed with affidavit submission instead.
1356        ProceedingToAffidavitSubmission,
1357
1358        /// Affidavit submission was declined because the author
1359        /// has already rotated keys for the next session.
1360        ///
1361        /// The author must wait for the next affidavit window.
1362        DeclareDuringNextAffidavitSession,
1363
1364        /// An affidavit is declared and the key is rotated before the
1365        /// current affidavit submission period itself.
1366        DeclaredBeforeAffidavitPeriod,
1367
1368        /// The given affidavit key is not the recently rotated affidavit key,
1369        /// from the recently declared affidavit.
1370        ///
1371        /// This implies the author have declared their affidavit and have rotated
1372        /// a new key for the the election after the upcoming election.
1373        InvalidRotatedAffidavitKey,
1374
1375        /// The requested block author of this very current block is not available.
1376        BlockAuthorNotFound,
1377
1378        /// An election is attempted by a non-block author. Elections can only be
1379        /// processed by block-authors only in their authored blocks only.
1380        TriedElectingByNonBlockAuthor,
1381
1382        /// The active affidavit key is utilized for affidavit-declaration and
1383        /// not for processing election.
1384        ///
1385        /// Election can only be processed by recently rotated affidavit key, which
1386        /// poses for the next affidavit-declaration session also.
1387        ///
1388        /// All these can be valid only if the author started validating, else its
1389        /// a dormant affidavit key
1390        AffidavitKeyForDeclaration,
1391
1392        /// The provided value must be non-zero.
1393        ///
1394        /// Returned when a parameter or input is required to be strictly
1395        /// greater than zero.
1396        ValueCannotBeZero,
1397
1398        /// Internal success signal for the affidavit key initialization routine.
1399        ///
1400        /// Used to indicate successful execution of the initialization flow.
1401        InitAffidavitKeyRoutineSuccess,
1402
1403        /// Internal success signal for the election execution routine.
1404        ///
1405        /// Indicates that the election process completed successfully.
1406        TryElectionRoutineSuccess,
1407
1408        /// Internal success signal for the affidavit declaration routine.
1409        ///
1410        /// Indicates that the affidavit has been successfully declared.
1411        DeclarAffidavitRoutineSuccess,
1412
1413        /// Internal success signal for the affidavit key rotation routine.
1414        ///
1415        /// Indicates that the key rotation process completed successfully.
1416        RotateAffidavitKeyRoutineSuccess,
1417
1418        /// The author is already in a chilled (inactive) state.
1419        ///
1420        /// Returned when attempting to chill an author that is already inactive.
1421        AuthorAlreadyChilling,
1422
1423        /// Authors are successfully elected, but cannot be revealed for
1424        /// some reasons unknown.
1425        ElectedButCannotReveal,
1426
1427        /// Author declared an affidavit, but it could not be retrieved
1428        /// for reasons unknown.
1429        DeclaredAffidavitNotFound,
1430
1431        /// The caller is not the current block author.
1432        ///
1433        /// Returned when an operation requires block authorship,
1434        /// but the caller does not match the current block author.
1435        NotABlockAuthor,
1436
1437        /// No public key matching the given affidavit identifier was found
1438        /// in the node-local keystore.
1439        ///
1440        /// The key may have been lost, never generated on this node, or
1441        /// the identifier does not correspond to any locally held key pair.
1442        AfdtPublicKeyNotFound,
1443
1444        /// No affidavit key is registered for the author in any relevant
1445        /// future session scope (next session or next affidavit session).
1446        ///
1447        /// The author has not called [`Pallet::validate`] or has already chilled.
1448        AffidavitKeyPairNotFound,
1449
1450        /// The election result could not be revealed.
1451        ///
1452        /// No election has been executed for the current session, or the
1453        /// underlying election manager returned no result.
1454        UnableToRevealElected,
1455
1456        /// The fork graph has reached the maximum number of concurrent forks.
1457        ///
1458        /// No new sibling branch can be created until an existing fork is
1459        /// resolved or pruned. This indicates an unusually high degree of
1460        /// chain instability relative to the configured `MAX_FORKS` constant.
1461        MaxOCWForksAttained,
1462
1463        /// Fork-aware offchain storage was accessed before the fork graph
1464        /// was initialized via `ForksHandler::start`.
1465        OCWForksNotEnabled,
1466
1467        /// The fork graph is in an inconsistent state.
1468        ///
1469        /// An internal invariant was violated, such as a branch entry that
1470        /// cannot be decoded or a divider that references a non-existent branch.
1471        /// Persistent failure indicates offchain storage corruption.
1472        OCWForksInconsistent,
1473    }
1474
1475    // ===============================================================================
1476    // ```````````````````````````````````` EVENTS ```````````````````````````````````
1477    // ===============================================================================
1478
1479    #[pallet::event]
1480    #[pallet::generate_deposit(pub(super) fn deposit_event)]
1481    pub enum Event<T: Config> {
1482        /// Emitted when an election attempt fails.
1483        ElectionAttemptFailed {
1484            session: SessionIndex,
1485            error: DispatchError,
1486            runner: AuthorOf<T>,
1487        },
1488
1489        /// Emitted after a successful election instance.
1490        ElectedInstance {
1491            session: SessionIndex,
1492            runner: AuthorOf<T>,
1493            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1494            elects: ElectionElects<T>,
1495        },
1496
1497        /// Emitted after a reward is successfully delegated to be
1498        /// applied to an author via [`CompensateRoles`].
1499        RewardInitiated {
1500            author: AuthorOf<T>,
1501            value: AssetOf<T>,
1502        },
1503
1504        /// Emitted when rewarding an author via [`CompensateRoles`] fails.
1505        RewardFailed {
1506            author: AuthorOf<T>,
1507            error: DispatchError,
1508        },
1509
1510        /// Emitted after a penalty is successfully delegated to be
1511        /// applied to an author via [`CompensateRoles`].
1512        PenaltyInitiated {
1513            author: AuthorOf<T>,
1514            penalty: PenaltyRatio,
1515        },
1516
1517        /// Emitted when applying a penalty to an author via
1518        /// [`CompensateRoles`] fails.
1519        PenaltyFailed {
1520            author: AuthorOf<T>,
1521            error: DispatchError,
1522        },
1523
1524        /// Emitted after a successful affidavit submission for the
1525        /// election to be conducted for the upcoming session.
1526        AffidavitSubmitted {
1527            afdt_id: AffidavitId<T>,
1528            session: SessionIndex,
1529            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1530            author: AuthorOf<T>,
1531            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1532            affidavit: ElectionVia<T>
1533        },
1534
1535        /// Emitted when an author successfully declares their affidavit signing 
1536        /// key for the next session
1537        ValidationBegins {
1538            author: AuthorOf<T>,
1539            for_session: SessionIndex
1540        },
1541
1542        /// Emitted when an author successfully initiates the chilling process
1543        /// by removing their affidavit key for a future session.
1544        ChillingBegins {
1545            author: AuthorOf<T>,
1546            for_session: SessionIndex
1547        },
1548
1549        /// Emitted by [`Pallet::inspect_affidavit`] for direct inspection of a
1550        /// stored affidavit and its declared election weights.
1551        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1552        InspectAffidavit {
1553            author: AuthorOf<T>,
1554            afdt_id: AffidavitId<T>,
1555            session: SessionIndex,
1556            affidavit: ElectionVia<T>
1557        },
1558
1559        /// Emitted by [`Pallet::inspect_elects`] for direct inspection of the
1560        /// currently revealed elected author set.
1561        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1562        InspectElects {
1563            elects: ElectionElects<T>
1564        },
1565
1566        /// Emitted by [`Pallet::prepare_validation_payload`] exposing the signed
1567        /// payload and signature required to submit a [`Pallet::validate`] extrinsic.  
1568        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1569        InspectValidatePayload {
1570            payload: ValidatePayloadOf<T>,
1571            signature: T::Signature,
1572        },
1573
1574        /// A genesis config parameter was updated forcefully.
1575        GenesisConfigUpdated(ForceGenesisConfig<T>),
1576    }
1577
1578    // ===============================================================================
1579    // ```````````````````````````````````` HOOKS ````````````````````````````````````
1580    // ===============================================================================
1581
1582    #[pallet::hooks]
1583    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1584        /// Offchain Worker entry point coordinating affidavit lifecycle and elections.
1585        ///
1586        /// This function acts as a **deterministic orchestrator** for a sequence of
1587        /// structured offchain routines, executed once per block in best-effort mode.
1588        /// It does not implement business logic itself; instead, it delegates all
1589        /// responsibility to well-defined [`Routines`] and [`RoutineOf`] abstractions.
1590        ///
1591        /// ## Execution Model
1592        /// 
1593        /// All routines execute inside [`ForksHandler::start`], which resolves and
1594        /// persists the current fork graph branch before any routine runs.
1595        /// Each routine calls [`Routines::run_service`] directly, followed by
1596        /// [`Routines::on_ran_service`] on success. The routines are executed
1597        /// **imperatively and sequentially** with **fail-fast semantics**:
1598        ///
1599        /// 1. Initialize the node-local active affidavit key (if required).
1600        /// 2. Attempt to run the election early (if the election window permits).
1601        /// 3. Declare an affidavit for the upcoming session (if eligible).
1602        /// 4. Rotate affidavit keys for the next session.
1603        ///
1604        /// If any routine fails, execution stops immediately for the current block.
1605        /// All failures are already logged by the routine itself; the OCW hook
1606        /// performs no retries, compensation, or error interpretation.
1607        ///
1608        /// ## Semantics & Guarantees
1609        /// - **Best-effort execution**: no transactional rollback is assumed.
1610        /// - **Idempotent by design**: repeated execution across blocks or forks is safe.
1611        /// - **Authorization-aware**: each routine explicitly determines its authorized
1612        ///   signer via [`RoutineOf::who`] before execution.
1613        /// - **Fork-safe**: correctness is preserved across re-orgs through
1614        ///   fork-aware ([`frame_suite::ForkAware`]) and
1615        ///   finalized ([`frame_suite::Finalized`]) offchain
1616        ///   storage semantics.
1617        ///
1618        /// ## Notes
1619        /// - This function intentionally returns early on failure.
1620        /// - All observability is provided via structured logging inside routines.
1621        /// - Progress is achieved through repeated OCW invocations over time.
1622        fn offchain_worker(block: BlockNumberFor<T>) {
1623            <Pallet<T> as ForksHandler<T, ForkLocalDepot>>::start(None, None, ||{
1624
1625                //------ Initiate Affidavit Key ---------
1626
1627                let init = InitAffidavitKey { at: block };
1628                let Ok(_) = 
1629                <InitAffidavitKey<T> as Routines<BlockNumberFor<T>>>::run_service(&init) else {
1630                    return;
1631                };
1632                <InitAffidavitKey<T> as Routines<BlockNumberFor<T>>>::on_ran_service(&init);
1633
1634                //------ Try Electing Authors Early ---------
1635
1636                let Ok(new_afdt_key) =
1637                <TryElection<T> as RoutineOf<T::Public, BlockNumberFor<T>>>::who(&block) else {
1638                    return;
1639                };
1640                let election = TryElection {
1641                    by: new_afdt_key,
1642                    at: block,
1643                };
1644                let Ok(_) = 
1645                <TryElection<T> as Routines<BlockNumberFor<T>>>::run_service(&election) else {
1646                    return;
1647                };
1648                <TryElection<T> as Routines<BlockNumberFor<T>>>::on_ran_service(&election);
1649
1650                //------ Declare Affidavit  ---------
1651
1652                let Ok(afdt_key) =
1653                <DeclareAffidavit<T> as RoutineOf<T::Public, BlockNumberFor<T>>>::who(&block) else {
1654                    return;
1655                };
1656
1657                let declare = DeclareAffidavit {
1658                    by: afdt_key,
1659                    at: block,
1660                };
1661                let Ok(_) = 
1662                <DeclareAffidavit<T> as Routines<BlockNumberFor<T>>>::run_service(&declare) else {
1663                    return;
1664                };
1665                <DeclareAffidavit<T> as Routines<BlockNumberFor<T>>>::on_ran_service(&declare);
1666
1667                //------ Rotate Affidavit Keys ---------
1668
1669                let Ok(next_afdt_key) =
1670                <RotateAffidavitKey<T> as RoutineOf<T::Public, BlockNumberFor<T>>>::who(&block) else {
1671                    return;
1672                };
1673
1674                let rotate = RotateAffidavitKey {
1675                    by: next_afdt_key,
1676                    at: block,
1677                };
1678                let Ok(_) =
1679                <RotateAffidavitKey<T> as Routines<BlockNumberFor<T>>>::run_service(&rotate) else {
1680                    return;
1681                };
1682                <RotateAffidavitKey<T> as Routines<BlockNumberFor<T>>>::on_ran_service(&rotate);
1683            });
1684        }
1685
1686        /// Called at the beginning of each block.
1687        ///
1688        /// Awards block production points to the current block author.
1689        fn on_initialize(_block: BlockNumberFor<T>) -> Weight {
1690            // Try to get the current block author from the authorship pallet
1691            let Some(author) = pallet_authorship::Pallet::<T>::author() else {
1692                // If no author is found (weight for 1 read operation),
1693                return T::DbWeight::get().reads(1);
1694            };
1695
1696            // Increment the block points for the author.
1697            // Ignoring errors here, as missing role checks or exhausted points are handled elsewhere.
1698            let _ = <Pallet<T> as AuthorPoints<AuthorOf<T>, T::Points>>::add_point(&author);
1699            <T as Config>::WeightInfo::on_initialize_with_author()
1700        }
1701    }
1702
1703    // ===============================================================================
1704    // `````````````````````````````````` EXTRINSICS `````````````````````````````````
1705    // ===============================================================================
1706
1707    #[pallet::call]
1708    impl<T: Config> Pallet<T> {
1709        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1710        // ```````````````````````````````` DISPATCHABLES ````````````````````````````````
1711        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1712
1713        /// Register an author's new **affidavit signing key** for the upcoming session's
1714        /// participation as a validator.
1715        ///
1716        /// This extrinsic allows an author to declare their **off-chain signing key** that
1717        /// will be used to sign affidavits for the next session's election.
1718        ///
1719        /// It ensures that only valid and available authors can register keys.
1720        ///
1721        /// ## Notes
1722        /// - Affidavit Keys are session-specific and are updated each session.
1723        /// - This extrinsic allows submission of affidavit for next session only.
1724        /// - Affidavit Declaration/Submission will rotate keys once every submission
1725        /// for further sessions then.
1726        ///
1727        /// ## Errors
1728        /// Returns a `DispatchError` if author-role authorization fails.
1729        #[pallet::call_index(0)]
1730        #[pallet::weight(<T as Config>::WeightInfo::validate())]
1731        pub fn validate(
1732            origin: OriginFor<T>,
1733            payload: ValidatePayloadOf<T>,
1734            // Signature verification happens via `ValidateUnsigned::validate::unsigned`
1735            // can avoid here
1736            _signature: T::Signature,
1737        ) -> DispatchResult {
1738            // Verify signed origin i.e., author
1739            let author = ensure_signed(origin)?;
1740
1741            // Ensure the author exists and is available in the role system
1742            <T::RoleAdapter as RoleManager<AuthorOf<T>>>::role_exists(&author)?;
1743            <T::RoleAdapter as RoleManager<AuthorOf<T>>>::is_available(&author)?;
1744
1745            // Compute the session for which the key is valid
1746            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
1747            let public = SignedPayload::<T>::public(&payload);
1748            let affidavit_pub: AffidavitId<T> = public.clone().into_account().into();
1749
1750            // Store the key for the upcoming session
1751            AffidavitKeys::<T>::insert((for_session, affidavit_pub), author.clone());
1752
1753            Self::deposit_event(Event::<T>::ValidationBegins { author, for_session });
1754            Ok(())
1755        }
1756
1757        /// Submit an **affidavit for the upcoming session election**.
1758        ///
1759        /// This extrinsic allows an author to declare their election weights
1760        /// (affidavit) for the next session. It also rotates the author's signing
1761        /// key for the subsequent affidavit.
1762        ///
1763        /// ## Parameters
1764        /// - `origin`: Must be a signed account corresponding to the author
1765        ///   submitting the affidavit.
1766        /// - `payload`: The payload that was signed off-chain representing the
1767        ///   affidavit data.
1768        /// - `signature`: The author's signature of the payload, used for verification.
1769        /// - `new_key`: A new affidavit signing key to replace the current one for the
1770        ///  next upcoming session.
1771        ///
1772        /// ## Notes
1773        /// - Affidavit submissions are session-specific.
1774        /// - Key rotation ensures authors maintain fresh signing keys for security.
1775        /// - Only validated and available authors can submit affidavits.
1776        ///
1777        /// ## Errors
1778        /// Returns a `DispatchError` if un-privileged to submit an affidavit.
1779        #[pallet::call_index(1)]
1780        #[pallet::weight(<T as Config>::WeightInfo::declare())]
1781        pub fn declare(
1782            origin: OriginFor<T>,
1783            payload: AffidavitPayloadOf<T>,
1784            // Signature verification happens via `ValidateUnsigned::validate::unsigned`
1785            // can avoid here
1786            _signature: T::Signature,
1787        ) -> DispatchResult {
1788            ensure_none(origin)?;
1789
1790            let public = SignedPayload::<T>::public(&payload);
1791            let new_affidavit_pub = payload.rotate.clone();
1792            let affidavit_pub: AffidavitId<T> = public.clone().into_account().into();
1793
1794            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
1795            // Ensure author has a registered key for the upcoming session
1796            let author = AffidavitKeys::<T>::get((for_session, &affidavit_pub))
1797                .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
1798
1799            // Process the affidavit
1800            <Pallet<T> as ElectionAffidavits<AffidavitId<T>, ElectionVia<T>>>::process_affidavit(
1801                &affidavit_pub.clone(),
1802            )?;
1803            
1804            // Rotate key for the next affidavit
1805            AffidavitKeys::<T>::insert(
1806                (
1807                    for_session.saturating_add(One::one()),
1808                    new_affidavit_pub.clone(),
1809                ),
1810                &author,
1811            );
1812
1813            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1814            {
1815                if !T::EmitEvents::get() {
1816                    let block = frame_system::Pallet::<T>::block_number();
1817                    let Ok(affidavit) = Self::get_affidavit(&affidavit_pub) else {
1818                        debug_assert!(
1819                            false,
1820                            "author declared affidavit for session {:?} at block {:?}, but it could not be retrieved",
1821                            for_session, block
1822                        );
1823                        return Err(Error::<T>::DeclaredAffidavitNotFound.into());
1824                    };
1825                    Self::deposit_event(Event::<T>::AffidavitSubmitted {
1826                        afdt_id: affidavit_pub,
1827                        session: for_session,
1828                        author,
1829                        affidavit,
1830                    });
1831                }
1832            }
1833
1834            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1835            {
1836                if !T::EmitEvents::get() {
1837                    Self::deposit_event(Event::<T>::AffidavitSubmitted {
1838                        afdt_id: affidavit_pub,
1839                        session: for_session,
1840                    });
1841                }
1842            }
1843            Ok(())
1844        }
1845
1846        /// Execute the election for the upcoming session.
1847        ///
1848        /// This extrinsic allows an author to act as the **election runner**
1849        /// for the election session (next-session). It verifies the author's
1850        /// affidavit-based signature, prepares the election using all submitted
1851        /// affidavits, and records the runner for audit and reward attribution.
1852        ///
1853        /// Since this is an **unsigned extrinsic**, it is constructed and
1854        /// submitted by validator offchain workers (OCWs).
1855        ///
1856        /// Although unsigned, it is treated as a **pseudo-inherent**:
1857        /// - It is authorized via affidavit signature verification
1858        /// - It may carry rewards for successfully running the election
1859        /// - It is **expected** to be submitted locally by validators rather
1860        ///   than propagated by external transaction authors
1861        ///
1862        /// ## Errors
1863        /// Returns a `DispatchError` if election execution or authorization fails.
1864        #[pallet::call_index(2)]
1865        #[pallet::weight(<T as Config>::WeightInfo::elect())]
1866        pub fn elect(
1867            origin: OriginFor<T>,
1868            payload: ElectionPayloadOf<T>,
1869            // Signature verification happens via `ValidateUnsigned`
1870            // can avoid here
1871            _signature: T::Signature,
1872        ) -> DispatchResult {
1873            ensure_none(origin)?;
1874
1875            // Determine the session for which this election applies (next session)
1876            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
1877
1878            let public = SignedPayload::<T>::public(&payload);
1879            let affidavit_pub: AffidavitId<T> = public.clone().into_account().into();
1880
1881            // Ensure the author has a valid affidavit key registered
1882            // Election keys are registered for session+2 because:
1883            // - Current session: ongoing
1884            // - Next session: affidavits are submitted
1885            // - Session after next: election is executed
1886            // Hence authors who submitted affidavits, submit next election key, hence
1887            // queriable.
1888            // This makes only authors who submitted affidavits to run election
1889            let author = AffidavitKeys::<T>::get((
1890                for_session.saturating_add(One::one()),
1891                &affidavit_pub.clone(),
1892            ))
1893            .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
1894
1895            let block_author =
1896                pallet_authorship::Pallet::<T>::author().ok_or(Error::<T>::BlockAuthorNotFound)?;
1897            // This ensures authors who have bypassed `ValidateUnsigned` via
1898            // `propagate = false` for `elect` unsigned extrinsic
1899            // shall be captured by the runtime itself
1900            ensure!(
1901                author == block_author,
1902                Error::<T>::TriedElectingByNonBlockAuthor
1903            );
1904
1905            // Run the election preparation logic
1906            if let Err(error) = Internals::<T>::prepare_election(&Some(author.clone())) {
1907                if !T::EmitEvents::get() {
1908                    Self::deposit_event(Event::<T>::ElectionAttemptFailed {
1909                        session: for_session,
1910                        runner: author.clone(),
1911                        error,
1912                    });
1913                }
1914                return Err(error);
1915            };
1916
1917            // Record the election runner and the block at which the election was conducted
1918            let current_block = frame_system::Pallet::<T>::block_number();
1919            ElectsPreparedBy::<T>::insert(for_session, (&author, current_block));
1920
1921            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1922            {
1923                if !T::EmitEvents::get() {
1924                    Self::deposit_event(Event::<T>::ElectedInstance {
1925                        session: for_session,
1926                        runner: author,
1927                    });
1928                }
1929            }
1930
1931            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1932            {
1933                if !T::EmitEvents::get() {
1934                    let Some(elects) = Internals::<T>::reveal() else {
1935                        debug_assert!(
1936                            false,
1937                            "authors elected for session {:?} at 
1938                            block {:?} by election runner {:?}, 
1939                            but reveal unavailable",
1940                            for_session, current_block, &author
1941                        );
1942                        return Err(Error::<T>::ElectedButCannotReveal.into());
1943                    };
1944                    Self::deposit_event(Event::<T>::ElectedInstance {
1945                        session: for_session,
1946                        runner: author,
1947                        elects,
1948                    });
1949                }
1950            }
1951            Ok(())
1952        }
1953
1954        /// Request to **step back or "chill" immediately from election participation**
1955        /// by erasing affidavit keys.
1956        ///
1957        /// This safely avoids penalties by pausing an author's duties.
1958        ///
1959        /// This extrinsic allows an author to withdraw from participating in upcoming
1960        /// elections or prevent submitting future affidavits. The actual effect
1961        /// depends on the **current block** relative to the **affidavit submissions
1962        /// and election windows**.
1963        ///
1964        /// Note that its always advised to `chill` validation before `resign` author role
1965        /// to skip unnecessary invalid affidavit declarations.
1966        ///
1967        /// ## Parameters
1968        /// - `origin`: Must be a signed account corresponding to the author i.e.,
1969        ///   controller/role account.
1970        /// - `affidavit_pub` : Public affidavit key registered for a new session's
1971        ///   affidavit submission.
1972        ///
1973        /// ## Notes
1974        /// - Removing affidavit keys ensures authors cannot unfairly influence
1975        ///   future elections.
1976        /// - By inspecting returned errors, callers can **compute the optimal
1977        ///   chill window**.
1978        ///
1979        /// ## Errors
1980        /// Returns a `DispatchError` with diagnostic in case of irrevocable
1981        /// duties assigned.
1982        #[pallet::call_index(3)]
1983        #[pallet::weight(<T as Config>::WeightInfo::chill())]
1984        pub fn chill(origin: OriginFor<T>, affidavit_pub: AffidavitId<T>) -> DispatchResult {
1985            let author = ensure_signed(origin)?;
1986
1987            Self::can_chill(author.clone(), affidavit_pub.clone())?;
1988
1989            let current_session = CurrentSession::<T>::get();
1990            let next_session = current_session.saturating_add(One::one());
1991            let next_afdt_session = next_session.saturating_add(One::one());
1992
1993            // If rotated key exists, remove it.
1994            if AffidavitKeys::<T>::contains_key((next_afdt_session, &affidavit_pub)) {
1995                AffidavitKeys::<T>::remove((next_afdt_session, &affidavit_pub));
1996                Self::deposit_event(Event::<T>::ChillingBegins { author, for_session: next_afdt_session });
1997                return Ok(());
1998            }
1999
2000            // Otherwise remove next-session key if it still exists.
2001            if AffidavitKeys::<T>::contains_key((next_session, &affidavit_pub)) {
2002                AffidavitKeys::<T>::remove((next_session, &affidavit_pub));
2003            }
2004            Self::deposit_event(Event::<T>::ChillingBegins { author, for_session: next_session });
2005        
2006            Ok(())
2007        }
2008
2009        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010        // ````````````````````````````````` INSPECTORS ``````````````````````````````````
2011        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2012        
2013        /// Emit the currently revealed elected author set as an event for inspection.
2014        ///
2015        /// This is a **read-only convenience extrinsic** that does not mutate any state.
2016        /// It retrieves the election result from the underlying election manager via
2017        /// [`Pallet::get_elects`] and emits it as an [`Event::InspectElects`] event.
2018        ///
2019        /// ## Notes
2020        /// - Gated by the `dev` feature. Must not be included in production runtimes.
2021        /// - Fails if no election result is available, i.e. no election has been
2022        ///   executed or the result cannot be revealed.
2023        ///
2024        /// ## Errors
2025        /// Returns [`Error::UnableToRevealElected`] if the election result is unavailable.
2026        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
2027        #[pallet::call_index(4)]
2028        #[pallet::weight(<T as Config>::WeightInfo::inspect_elects())]
2029        pub fn inspect_elects(
2030            origin: OriginFor<T>,
2031        ) -> DispatchResult {
2032            ensure_signed(origin)?;
2033            let elects = Self::get_elects()?;
2034            Self::deposit_event(Event::InspectElects { elects });
2035            Ok(())
2036        }
2037
2038        /// Construct and emit a signed [`ValidatePayload`] and its signature as an event.
2039        ///
2040        /// This is a **read-only convenience extrinsic** that does not mutate any on-chain state.
2041        /// It is intended to support the [`Self::validate`] extrinsic workflow by producing
2042        /// the signed payload and signature required to call it.
2043        ///
2044        /// Since [`Self::validate`] accepts both a signed origin and an unsigned payload,
2045        /// callers need a way to obtain a correctly signed payload before submitting.
2046        /// This extrinsic bridges that gap by performing the signing on behalf of the
2047        /// local node and emitting the result as an [`Event::InspectValidatePayload`] event.
2048        ///
2049        /// ## Execution Model
2050        /// This extrinsic reads from **node-local offchain storage** to retrieve the
2051        /// active affidavit key. It is therefore **client-specific**: the result reflects
2052        /// the state of the validator node executing the call, and may differ across nodes.
2053        ///
2054        /// ## Notes
2055        /// - Gated by the `dev` feature. Must not be included in production runtimes.
2056        /// - The emitted payload and signature can be submitted directly to [`Self::validate`].
2057        /// - Requires that the node holds a finalized active affidavit key in offchain storage.
2058        ///
2059        /// ## Errors
2060        /// Returns a `DispatchError` if the active affidavit key is not finalized,
2061        /// not found in the local keystore, or signing fails.
2062        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
2063        #[pallet::call_index(5)]
2064        #[pallet::weight(<T as Config>::WeightInfo::prepare_validation_payload())]
2065        pub fn prepare_validation_payload(
2066            origin: OriginFor<T>,
2067        ) -> DispatchResult {
2068            ensure_signed(origin)?;
2069            let (payload, signature) = Self::sign_validate_payload()?;
2070            Self::deposit_event(Event::InspectValidatePayload { payload, signature });
2071            Ok(())
2072        }        
2073
2074        /// Emit the stored affidavit for a given affidavit identifier as an event.
2075        ///
2076        /// This is a **read-only convenience extrinsic** that does not mutate any state.
2077        /// It retrieves the affidavit associated with the provided [`AffidavitId`] from
2078        /// storage and emits it as an [`Event::InspectAffidavit`] event, making the
2079        /// affidavit weights observable without requiring direct storage queries.
2080        ///
2081        /// The retrieved affidavit corresponds to the **upcoming session's election**
2082        /// (current session index + 1).
2083        ///
2084        /// ## Notes
2085        /// - Gated by the `dev` feature. Must not be included in production runtimes.
2086        /// - Useful for verifying that a previously submitted affidavit was stored correctly
2087        ///   before the election window opens.
2088        ///
2089        /// ## Errors
2090        /// Returns [`Error::AffidavitAuthorNotFound`] if no author is mapped to the given key,
2091        /// or [`Error::AffidavitNotFound`] if no affidavit has been submitted for that author.
2092        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
2093        #[pallet::call_index(6)]
2094        #[pallet::weight(<T as Config>::WeightInfo::inspect_affidavit())]
2095        pub fn inspect_affidavit(
2096            origin: OriginFor<T>,
2097            afdt_id: AffidavitId<T>,
2098        ) -> DispatchResult {
2099            let author = ensure_signed(origin)?;
2100            let affidavit = Self::get_affidavit(&afdt_id)?;
2101            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
2102            Self::deposit_event(Event::InspectAffidavit { author, session: for_session, afdt_id, affidavit });
2103            Ok(())
2104        }
2105
2106        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2107        // ``````````````````````````````` ROOT PRIVILEGED ```````````````````````````````
2108        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2109
2110        /// Force-update a selected genesis configuration parameter.
2111        ///
2112        /// **Origin:** Root only.
2113        ///
2114        /// This extrinsic allows privileged modification of runtime parameters
2115        /// that were originally defined at genesis.
2116        /// 
2117        /// - `AllowAffidavits` - Enables or disables affidavit submission.
2118        /// - `AffidavitBeginsAt` - Updates the start of the affidavit submission window.
2119        /// - `AffidavitEndsAt` - Updates the end of the affidavit submission window.
2120        /// - `ElectionBeginsAt` - Updates when election execution begins within the session.
2121        /// - `ElectionRunnerPointsUpgrade` - Updates the reward points for election runners.
2122        /// - `ValidateTxPriority` - Updates the priority for validation-related extrinsics.
2123        /// - `ElectionTxPriority` - Updates the priority for election execution extrinsics.
2124        /// - `AffidavitTxPriority` - Updates the priority for affidavit submission extrinsics.
2125        /// - `FinalityAfter` - Updates the time-based delay before operations are considered final.
2126        /// - `FinalityTicks` - Updates the block-based confirmation threshold for finality.
2127        ///
2128        /// The call enforces consistency constraints where applicable:
2129        /// - Affidavit window ordering:
2130        ///   - `AffidavitBeginsAt < AffidavitEndsAt`
2131        ///   - `AffidavitEndsAt > AffidavitBeginsAt`
2132        /// - The following values must be non-zero:
2133        ///   - transaction priorities
2134        ///   - finality thresholds (`FinalityAfter`, `FinalityTicks`)
2135        ///
2136        /// This call directly overwrites storage and emits an event containing the
2137        /// updated configuration variant.
2138        #[pallet::call_index(7)]
2139        #[pallet::weight(<T as Config>::WeightInfo::force_allow_affidavits()
2140            .max(<T as Config>::WeightInfo::force_affidavit_begins_at())
2141            .max(<T as Config>::WeightInfo::force_affidavit_ends_at())
2142            .max(<T as Config>::WeightInfo::force_election_begins_at())
2143            .max(<T as Config>::WeightInfo::force_election_runner_points_upgrade())
2144            .max(<T as Config>::WeightInfo::force_validate_tx_priority())
2145            .max(<T as Config>::WeightInfo::force_election_tx_priority())
2146            .max(<T as Config>::WeightInfo::force_affidavit_tx_priority())
2147            .max(<T as Config>::WeightInfo::force_finality_after())
2148            .max(<T as Config>::WeightInfo::force_finality_ticks())
2149        )]
2150        pub fn force_genesis_config(
2151            origin: OriginFor<T>,
2152            field: ForceGenesisConfig<T>,
2153        ) -> DispatchResult {
2154            ensure_root(origin)?;
2155            match field {
2156                ForceGenesisConfig::AllowAffidavits(value) => AllowAffidavits::<T>::put(value),
2157                ForceGenesisConfig::AffidavitBeginsAt(aff_begins) => {
2158                    ensure!(
2159                        aff_begins < AffidavitEndsAt::<T>::get(),
2160                        Error::<T>::InvalidAffidavitPeriod
2161                    );
2162                    AffidavitBeginsAt::<T>::put(aff_begins);
2163                }
2164                ForceGenesisConfig::AffidavitEndsAt(aff_ends) => {
2165                    ensure!(
2166                        aff_ends > AffidavitBeginsAt::<T>::get(),
2167                        Error::<T>::InvalidAffidavitPeriod
2168                    );
2169                    AffidavitEndsAt::<T>::put(aff_ends);
2170                }
2171                ForceGenesisConfig::ElectionBeginsAt(elect_begins) => {
2172                    ElectionBeginsAt::<T>::put(elect_begins);
2173                }
2174                ForceGenesisConfig::ElectionRunnerPointsUpgrade(points) => {
2175                    ElectionRunnerPointsUpgrade::<T>::put(points);
2176                }
2177                ForceGenesisConfig::ValidateTxPriority(priority) => {
2178                    ensure!(priority > Zero::zero(), Error::<T>::ValueCannotBeZero);
2179                    ValidateTxPriority::<T>::put(priority);
2180                }
2181                ForceGenesisConfig::ElectionTxPriority(priority) => {
2182                    ensure!(priority > Zero::zero(), Error::<T>::ValueCannotBeZero);
2183                    ElectionTxPriority::<T>::put(priority);
2184                }
2185                ForceGenesisConfig::AffidavitTxPriority(priority) => {
2186                    ensure!(priority > Zero::zero(), Error::<T>::ValueCannotBeZero);
2187                    AffidavitTxPriority::<T>::put(priority);
2188                }
2189                ForceGenesisConfig::FinalityAfter(moment) => {
2190                    ensure!(moment > Zero::zero(), Error::<T>::ValueCannotBeZero);
2191                    FinalityAfter::<T>::put(moment);
2192                }
2193                ForceGenesisConfig::FinalityTicks(block) => {
2194                    ensure!(block > Zero::zero(), Error::<T>::ValueCannotBeZero);
2195                    FinalityTicks::<T>::put(block);
2196                }
2197            }
2198            Self::deposit_event(Event::GenesisConfigUpdated(field));
2199            Ok(())
2200        }
2201    }
2202
2203    // ===============================================================================
2204    // ````````````````````````````````` PUBLIC APIS `````````````````````````````````
2205    // ===============================================================================
2206
2207    impl<T: Config> Pallet<T> {
2208        /// Returns `Ok(())` if the author is permitted to submit the [`Self::chill`] extrinsic
2209        /// using the given affidavit key.
2210        ///
2211        /// Performs a read-only pre-check verifying that chilling does not
2212        /// conflict with any active or pending duty. The check branches on which
2213        /// session scope the key belongs to:
2214        ///
2215        /// - **Next affidavit session (current + 2)**: author has already declared and
2216        ///   rotated. Chilling is allowed only after the election window closes and
2217        ///   the author is not in the revealed elected set.
2218        ///
2219        /// - **Next session (current + 1)**: author has registered but may not have
2220        ///   declared yet. Chilling is allowed before the affidavit window or after
2221        ///   it closes without a declaration. Rejected if a declaration exists but
2222        ///   the key is stale relative to the expected post-rotation key.
2223        ///
2224        /// ## Parameters
2225        /// - `author`: The author requesting to chill.
2226        /// - `affidavit_pub`: The affidavit key registered via [`Self::validate`]
2227        ///   or rotated during [`Self::declare`].
2228        ///
2229        /// ## Returns
2230        /// - `Ok(())` if chilling is permitted.
2231        /// - `Err(ValidatorCannotChill)` if the author is active in the current session.
2232        /// - `Err(CandidateCannotChill)` if the election window is still open.
2233        /// - `Err(ElectedCannotChill)` if the author appears in the revealed elected set.
2234        /// - `Err(InvalidRotatedAffidavitKey)` if a declaration exists but the key is stale.
2235        /// - `Err(DispatchError)` for ownership, timing, or configuration violations.
2236        pub fn can_chill(author: AuthorOf<T>, affidavit_pub: AffidavitId<T>) -> DispatchResult {
2237            let current_session = CurrentSession::<T>::get();
2238            let next_session = current_session.saturating_add(One::one());
2239            let next_afdt_session = next_session.saturating_add(One::one());
2240
2241            // Convert author -> validator id for the current session
2242            // If conversion fails, runtime cannot determine validator status
2243            let Some(validator) =
2244                <Pallet<T> as Convert<AuthorOf<T>, Option<SessionId<T>>>>::convert(author.clone())
2245            else {
2246                return Err(Error::<T>::SessionIdQueryFailed.into());
2247            };
2248
2249            // Active validators of the *current* session are not allowed to chill.
2250            // Reason: they may still be required if elections fail or re-run.
2251            ensure!(
2252                !pallet_session::Pallet::<T>::validators().contains(&validator),
2253                Error::<T>::ValidatorCannotChill
2254            );
2255
2256            // Compute affidavit submission window for the upcoming election
2257            let aff_window = Self::compute_affidavit_window()?;
2258            let start_affidavit = aff_window.start;
2259            let end_affidavit = aff_window.end;
2260            // Ensure configuration is sane: submission window must be valid
2261            debug_assert!(
2262                start_affidavit < end_affidavit,
2263                "Affidavit submission period invalid: starts at {:?}, ends at {:?}",
2264                start_affidavit,
2265                end_affidavit
2266            );
2267            ensure!(
2268                start_affidavit < end_affidavit,
2269                Error::<T>::InvalidAffidavitPeriod
2270            );
2271
2272            let current_block = frame_system::Pallet::<T>::block_number();
2273
2274            // CASE 1: Key belongs to next-next session (already rotated)
2275            //
2276            // Optimistically we need to check.
2277            //
2278            // This means:
2279            // - Author already declared an affidavit for `next_session`
2280            // - During declaration, a new key was rotated for `next_afdt_session`
2281            // Hence this author may very-well be a candidate for the ongoing election.
2282            if let Some(id) = AffidavitKeys::<T>::get((next_afdt_session, &affidavit_pub)) {
2283                ensure!(id == author, Error::<T>::AuthorNotAffidavitOwner);
2284                // Sanity: a rotated key implies the affidavit must have been declared
2285                // within the valid submission window.
2286                debug_assert!(
2287                    !(current_block < start_affidavit),
2288                    "affidavit key is for next-next session, effectively means it
2289                    declared affidavit and rotated next key, but it happened before
2290                    affidavit submission period itself? for author {:?} and afdt key {:?},
2291                    rotated during elected session {:?}, and rotated for session {:?}",
2292                    author,
2293                    affidavit_pub,
2294                    next_session,
2295                    next_afdt_session
2296                );
2297                ensure!(
2298                    !(current_block < start_affidavit),
2299                    Error::<T>::DeclaredBeforeAffidavitPeriod
2300                );
2301
2302                // If still within affidavit submission window:
2303                // author is an active candidate -> cannot chill.
2304                // It is possible that the author is not present in the elected
2305                // list at this stage.
2306                // In that case, we ideally should purge:
2307                //   - the declared affidavit for `next_session`,
2308                //   - the affidavit key for `next_session`,
2309                //   - and the rotated affidavit key for `next_afdt_session`.
2310                // However, at this point we only have access to the
2311                // `next_afdt_session` affidavit key i.e., rotated key.
2312                ensure!(
2313                    current_block >= end_affidavit,
2314                    Error::<T>::CandidateCannotChill
2315                );
2316
2317                // After the election period:
2318                // Only authors who were NOT elected are allowed to chill.
2319                //
2320                // We already enforced earlier that active validators of the *current session*
2321                // cannot chill. This guarantees that even if the `reveal` result here is
2322                // slightly outdated or later ignored by the session (e.g. the same validators
2323                // are re-posted due to election issues), safety is preserved.
2324                //
2325                // Therefore:
2326                // - If the author appears in the revealed elected set -> they must not chill.
2327                // - If the author does NOT appear -> they are not part of the upcoming elected set.
2328                // - Even if the reveal is stale, it can only reflect a previous valid election
2329                //   outcome, and cannot incorrectly allow a current validator to chill.
2330                //
2331                // Hence, it is safe to rely on this check to prevent elected (or soon-to-be
2332                // elected) authors from chilling.
2333                let elected = <Internals<T> as ElectAuthors<AuthorOf<T>, ElectionVia<T>>>::reveal();
2334                if let Some(elected) = elected {
2335                    for elect in elected.into_iter() {
2336                        if author == elect {
2337                            return Err(Error::<T>::ElectedCannotChill.into());
2338                        }
2339                    }
2340                }
2341
2342                // Election finished and author not elected:
2343                // author can chill at next-next election.
2344                return Ok(());
2345            }
2346
2347            // CASE 2: Key belongs to next session
2348            //
2349            // This means the author has registered an affidavit key but may NOT yet
2350            // declared the affidavit for the upcoming election.
2351            let id = AffidavitKeys::<T>::get((next_session, &affidavit_pub))
2352                .ok_or(Error::<T>::AffidavitAuthorNotFound)?;
2353            ensure!(id == author, Error::<T>::AuthorNotAffidavitOwner);
2354
2355            // Subcase A: Before affidavit submission window begins
2356            //
2357            // Author is simply opting out before participating in election.
2358            if current_block < start_affidavit {
2359                let afdt_decl = AuthorAffidavits::<T>::contains_key((next_session, &author));
2360
2361                // Sanity: affidavit should not be declared before submission window.
2362                debug_assert!(
2363                    !afdt_decl,
2364                    "affidavit is declared before affidavit submission period
2365                    by author {:?} for election conducted for session {:?} using
2366                    afdt-key {:?}",
2367                    author, next_session, affidavit_pub
2368                );
2369                ensure!(!afdt_decl, Error::<T>::DeclaredBeforeAffidavitPeriod);
2370
2371                return Ok(());
2372            }
2373
2374            // Subcase B: During or after affidavit submission window
2375            match AuthorAffidavits::<T>::contains_key((next_session, &author)) {
2376                true => {
2377                    // Affidavit has already been declared for `next_session`.
2378                    //
2379                    // Normally, this implies that a new (rotated) affidavit key for `next_afdt_session`
2380                    // should have been registered as part of the declaration flow.
2381                    //
2382                    // If we still reached here with the current key, it likely means the provided key
2383                    // is stale or does not correspond to the rotated key expected after declaration.
2384                    //
2385                    // However, we must not blindly assume rotation always changes the key. In some
2386                    // implementations (e.g., custom declaration flows outside this pallet's OCWs),
2387                    // the same affidavit key might be reused during rotation.
2388                    //
2389                    // Therefore, rather than mutating any state based on uncertain assumptions,
2390                    // we conservatively reject the call with an explicit error.
2391                    return Err(Error::<T>::InvalidRotatedAffidavitKey.into());
2392                }
2393                false => {
2394                    // Either still inside submission window but not yet declared,
2395                    // or window already ended and author implicitly chilled.
2396                    return Ok(());
2397                }
2398            }
2399        }
2400
2401        /// Returns `Ok(())` if the author is eligible to submit a [`Self::validate`] extrinsic.
2402        ///
2403        /// Performs a pre-check for validation readiness by verifying that:
2404        /// - the author is not already an active validator in the current session,
2405        /// - the author exists in the role system via [`Config::RoleAdapter`], and
2406        /// - the author is currently marked as available.
2407        ///
2408        /// Intended for use by offchain workers and RPC consumers before constructing
2409        /// and submitting a [`Self::validate`] extrinsic.
2410        ///
2411        /// ## Parameters
2412        /// - `author`: The author whose validation eligibility is being checked.
2413        ///
2414        /// ## Returns
2415        /// - `Ok(())` if all conditions for validation readiness are satisfied.
2416        /// - `Err(ActivelyValidating)` if the author is already in the active validator set.
2417        /// - `Err(DispatchError)` if the role check or availability check fails.
2418        pub fn can_validate(author: AuthorOf<T>) -> DispatchResult {
2419            if Self::is_validating(author.clone()) {
2420                return Err(Error::<T>::ActivelyValidating.into());
2421            }
2422            <T::RoleAdapter as RoleManager<AuthorOf<T>>>::role_exists(&author)?;
2423            <T::RoleAdapter as RoleManager<AuthorOf<T>>>::is_available(&author)?;
2424            Ok(())
2425        }
2426
2427        /// Returns `true` if the author is an active validator in the **current session**.
2428        ///
2429        /// Converts the author identifier into its session-specific validator id using
2430        /// the configured [`Convert`] implementation, then checks whether that validator
2431        /// appears in the current session's active validator set via [`pallet_session`].
2432        ///
2433        /// Returns `false` if the author-to-validator conversion fails, as the runtime
2434        /// cannot determine validator status without a valid session identity.
2435        ///
2436        /// ## Parameters
2437        /// - `author`: The author whose active validation status is being checked.
2438        pub fn is_validating(author: AuthorOf<T>) -> bool {
2439            // Convert author -> validator id for the current session
2440            let Some(validator) =
2441                <Pallet<T> as Convert<AuthorOf<T>, Option<SessionId<T>>>>::convert(author.clone())
2442            else {
2443                return false;
2444            };
2445
2446            // Active validators of the *current* session.
2447            if pallet_session::Pallet::<T>::validators().contains(&validator) {
2448                return true;
2449            }
2450            false
2451        }
2452
2453        /// Returns `true` if the author has no registered affidavit key in any
2454        /// relevant future session scope and is therefore in a **chilled** state.
2455        ///
2456        /// This is the logical inverse of [`Self::is_pursuing`] and delegates
2457        /// directly to [`Self::get_runtime_afdt_key`].
2458        ///
2459        /// ## Parameters
2460        /// - `author`: The author whose chilling status is being checked.
2461        pub fn is_chilling(author: AuthorOf<T>) -> bool {
2462            Self::get_runtime_afdt_key(author).is_err()
2463        }
2464
2465        /// Retrieve the author's registered affidavit key and its associated session
2466        /// from the two relevant future session scopes.
2467        ///
2468        /// Searches across:
2469        /// - the **next affidavit session** (current + 2), checked first, and
2470        /// - the **next session** (current + 1), checked second.
2471        ///
2472        /// The next affidavit session is checked first because a key stored there
2473        /// indicates a more advanced lifecycle state: the author has already declared
2474        /// an affidavit for the next session and rotated to a fresh key for the
2475        /// session after.
2476        ///
2477        /// ## Parameters
2478        /// - `author`: The author whose registered affidavit key is being retrieved.
2479        ///
2480        /// ## Returns
2481        /// - `Ok((session, affidavit_key))` if a key owned by the author is found
2482        ///   in either future session scope.
2483        /// - `Err(AffidavitKeyPairNotFound)` if no registered key exists for the author.
2484        pub fn get_runtime_afdt_key(
2485            author: AuthorOf<T>,
2486        ) -> Result<(SessionIndex, AffidavitId<T>), DispatchError> {
2487            let current_session = CurrentSession::<T>::get();
2488            let next_session = current_session.saturating_add(One::one());
2489            let next_afdt_session = next_session.saturating_add(One::one());
2490
2491            if let Some((affidavit_pub, _)) = AffidavitKeys::<T>::iter_prefix((next_afdt_session,))
2492                .find(|(_, owner)| *owner == author)
2493            {
2494                return Ok((next_afdt_session, affidavit_pub));
2495            }
2496
2497            if let Some((affidavit_pub, _)) =
2498                AffidavitKeys::<T>::iter_prefix((next_session,)).find(|(_, owner)| *owner == author)
2499            {
2500                return Ok((next_session, affidavit_pub));
2501            }
2502
2503            Err(Error::<T>::AffidavitKeyPairNotFound.into())
2504        }
2505
2506        /// Resolve an affidavit account identifier to its corresponding public key
2507        /// in the **node-local keystore**.
2508        ///
2509        /// Iterates all locally available affidavit application keys, derives the
2510        /// account-form identifier for each, and returns the public key whose derived
2511        /// account matches the provided `afdt_key`.
2512        ///
2513        /// ## Parameters
2514        /// - `afdt_key`: The affidavit account identifier to resolve.
2515        ///
2516        /// ## Returns
2517        /// - `Ok(public_key)` if a matching key is found in the local keystore.
2518        /// - `Err(AfdtPublicKeyNotFound)` if no locally held key derives to the given identifier.
2519        ///
2520        /// ## Note
2521        /// This function reads from the node-local keystore and may return different
2522        /// results across nodes depending on which keys each node holds.
2523        pub fn get_public_key(afdt_key: AffidavitId<T>) -> Result<T::Public, DispatchError> {
2524            let all_keys =
2525                    <<T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::RuntimeAppPublic
2526                        as RuntimeAppPublic>::all();
2527            for key in all_keys.into_iter() {
2528                let generic_pub: <T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::GenericPublic =
2529                    key.into();
2530                let public: T::Public = generic_pub.into();
2531                let account: AffidavitId<T> = public.clone().into_account().into();
2532
2533                if account == afdt_key {
2534                    return Ok(public);
2535                }
2536            }
2537            Err(Error::<T>::AfdtPublicKeyNotFound.into())
2538        }
2539
2540        /// Retrieve the currently finalized **active affidavit key** from node-local
2541        /// offchain storage.
2542        ///
2543        /// Reads the active affidavit key using finalized offchain storage semantics
2544        /// and returns it only when its confidence level is [`Confidence::Safe`].
2545        /// Keys that exist but have not yet reached safe confidence are rejected,
2546        /// as they may still be subject to re-org.
2547        ///
2548        /// ## Returns
2549        /// - `Ok(key)` if a finalized key exists and is marked as [`Confidence::Safe`].
2550        /// - `Err(ActiveAfdtKeyFinalizedHangingValue)` if a finalized value exists
2551        ///   without a corresponding fork-aware reference.
2552        /// - `Err(ActiveAfdtKeyNotYetFinalized)` if the key exists but has not yet
2553        ///   reached safe confidence.
2554        /// - `Err(DispatchError)` if the offchain storage read itself fails.
2555        ///
2556        /// ## Note
2557        /// This value is maintained in node-local offchain storage and is not part
2558        /// of on-chain state. Results may differ across nodes.
2559        pub fn get_finalized_afdt_key() -> Result<AffidavitId<T>, DispatchError> {
2560            let result = Finalized::<T, AffidavitId<T>, DeclareAffidavit<T>, Pallet<T>>::get(
2561                ACTIVE_AFDT_KEY,
2562                LOG_TARGET_AFDT,
2563                None,
2564            );
2565            match result {
2566                Ok(None) => Err(Error::<T>::ActiveAfdtKeyFinalizedHangingValue.into()),
2567                Ok(Some(Confidence::Safe(key))) => Ok(key),
2568                Ok(Some(_)) => Err(Error::<T>::ActiveAfdtKeyNotYetFinalized.into()),
2569                Err(e) => Err(e),
2570            }
2571        }
2572
2573        /// Construct and sign a [`ValidatePayload`] using the currently finalized
2574        /// active affidavit key from node-local offchain storage.
2575        ///
2576        /// Performs the following steps in sequence:
2577        /// - retrieves the finalized active affidavit key via [`Self::get_finalized_afdt_key`],
2578        /// - resolves that key to a local public key via [`Self::get_public_key`],
2579        /// - constructs a [`ValidatePayload`] wrapping the resolved public key, and
2580        /// - signs the payload using the configured [`Config::AffidavitCrypto`] scheme.
2581        ///
2582        /// The resulting payload and signature are the exact inputs required by
2583        /// the [`Self::validate`] extrinsic.
2584        ///
2585        /// ## Returns
2586        /// - `Ok((payload, signature))` if the key is finalized, locally available,
2587        ///   and signing succeeds.
2588        /// - `Err(CannotSignValidateTxPayload)` if signing fails.
2589        /// - `Err(DispatchError)` if key retrieval or resolution fails.
2590        ///
2591        /// ## Note
2592        /// This function reads from node-local offchain storage and the local keystore.
2593        /// Results are node-specific and may differ across validators.
2594        pub fn sign_validate_payload() -> Result<(ValidatePayloadOf<T>, T::Signature), DispatchError> {
2595            let active_afdt_key = Self::get_finalized_afdt_key()?;
2596            let afdt_pub = Self::get_public_key(active_afdt_key)?;
2597            let payload = ValidatePayload { public: afdt_pub };
2598            let Some(signature) = <ValidatePayload<T::Public> as SignedPayload<T>>::sign::<
2599                T::AffidavitCrypto,
2600            >(&payload) else {
2601                return Err(Error::<T>::CannotSignValidateTxPayload.into());
2602            };
2603            Ok((payload, signature))
2604        }
2605
2606        /// Retrieve the currently revealed elected author set from the election manager.
2607        ///
2608        /// Delegates to [`ElectAuthors::reveal`] and returns the result as a 
2609        /// concrete [`ElectionElects`] value.
2610        ///
2611        /// This function does not trigger or re-run an election. It only reads
2612        /// the result of the most recently completed election preparation.
2613        /// If no election has been executed or the result is unavailable,
2614        /// it returns an error.
2615        ///
2616        /// ## Returns
2617        /// - `Ok(ElectionElects)` containing the elected author set.
2618        /// - `Err(UnableToRevealElected)` if no election result is currently available.
2619        pub fn get_elects() -> Result<ElectionElects<T>, DispatchError> {
2620            let Some(elects) = <Internals<T> as ElectAuthors<AuthorOf<T>, ElectionVia<T>>>::reveal() else {
2621                return Err(Error::<T>::UnableToRevealElected.into())
2622            };
2623            Ok(elects)
2624        }
2625
2626        /// Retrieve the submitted affidavit for a given affidavit identifier
2627        /// targeting the **upcoming session's election**.
2628        ///
2629        /// Resolves the affidavit key to its owning author, then returns the
2630        /// author's declared election weights for the next session (current + 1).
2631        ///
2632        /// ## Parameters
2633        /// - `afdt_id`: The affidavit key identifier to look up.
2634        ///
2635        /// ## Returns
2636        /// - `Ok(ElectionVia)` containing the author's declared election weights.
2637        /// - `Err(DispatchError)` otherwise.
2638        pub fn fetch_affidavit(afdt_id: AffidavitId<T>) -> Result<ElectionVia<T>, DispatchError> {
2639            Self::get_affidavit(&afdt_id)
2640        }
2641
2642        /// Retrieve the submitted affidavit for a given affidavit identifier
2643        /// targeting a **specific session**.
2644        ///
2645        /// Unlike [`Self::fetch_affidavit`], which always targets the upcoming session,
2646        /// this function allows querying affidavits for any session by index.
2647        /// It resolves the affidavit key to its registered author for the given session,
2648        /// then returns that author's declared election weights.
2649        ///
2650        /// ## Parameters
2651        /// - `afdt_id`: The affidavit key identifier to look up.
2652        /// - `session`: The session index for which the affidavit is queried.
2653        ///
2654        /// ## Returns
2655        /// - `Ok(ElectionVia)` containing the author's declared election weights for the session.
2656        /// - `Err(AffidavitKeyPairNotFound)` if the key is not registered for the given session.
2657        /// - `Err(AffidavitNotFound)` if the author has not submitted an affidavit for that session.
2658        pub fn fetch_affidavit_for(afdt_id: AffidavitId<T>, session: SessionIndex) -> Result<ElectionVia<T>, DispatchError> {
2659            let Some(author) = AffidavitKeys::<T>::get((session, afdt_id)) else { return Err(Error::<T>::AffidavitKeyPairNotFound.into()) };
2660            let Some((_, affidavit)) = AuthorAffidavits::<T>::get((session, author)) else { return  Err(Error::<T>::AffidavitNotFound.into()) };
2661            Ok(affidavit.into_iter().collect())
2662        }
2663
2664        /// Returns `true` if the author has submitted an affidavit and is
2665        /// actively contesting the **upcoming session's election**.
2666        ///
2667        /// An author is considered contesting when a valid affidavit entry exists
2668        /// in storage for the next session (current + 1). This indicates that the
2669        /// author has declared election weights and is a candidate for selection.
2670        ///
2671        /// ## Parameters
2672        /// - `author`: The author whose election candidacy is being checked.
2673        ///
2674        /// ## Returns
2675        /// - `true` if the author has a stored affidavit for the upcoming session.
2676        /// - `false` otherwise, including when the author has never submitted
2677        ///   an affidavit or has withdrawn.
2678        pub fn is_contesting(author: AuthorOf<T>) -> bool {
2679            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
2680            let Some((_, _)) = AuthorAffidavits::<T>::get((for_session, author)) else { return false };
2681            true
2682        }
2683
2684        /// Returns `true` if the author is actively **pursuing validation**
2685        /// by maintaining a registered affidavit key for a future session.
2686        ///
2687        /// An author is considered pursuing when they have a registered affidavit key
2688        /// in either the next session or the next affidavit session scope, meaning
2689        /// they have not chilled and intend to participate in upcoming elections.
2690        ///
2691        /// This is the logical inverse of [`Self::is_chilling`].
2692        ///
2693        /// ## Parameters
2694        /// - `author`: The author whose pursuit status is being checked.
2695        ///
2696        /// ## Returns
2697        /// - `true` if the author holds an affidavit key for any relevant future session.
2698        /// - `false` if the author has no registered keys and is effectively chilled.
2699        pub fn is_pursuing(author: AuthorOf<T>) -> bool {
2700            !Self::is_chilling(author)
2701        }
2702
2703        /// Returns `Ok(())` if the author identified by the given affidavit key
2704        /// is eligible to submit an affidavit declaration for the upcoming session.
2705        ///
2706        /// This function performs a full pre-check for the [`Self::declare`] extrinsic,
2707        /// verifying that:
2708        /// - the affidavit key is registered for the upcoming session,
2709        /// - the global [`AllowAffidavits`] flag is enabled,
2710        /// - the author associated with the key is available in the role system, and
2711        /// - the current block falls within the configured affidavit submission window.
2712        ///
2713        /// Intended for use by offchain workers and RPC consumers to determine
2714        /// whether an affidavit declaration can be safely submitted at the current block.
2715        ///
2716        /// ## Parameters
2717        /// - `afdt_id`: The affidavit key identifier to evaluate.
2718        ///
2719        /// ## Returns
2720        /// - `Ok(())` if all conditions for affidavit submission are satisfied.
2721        /// - `Err(DispatchError)` otherwise.
2722        pub fn can_declare(afdt_id: AffidavitId<T>) -> DispatchResult {
2723            let for_session = CurrentSession::<T>::get().saturating_add(One::one());
2724            ensure!(
2725                AffidavitKeys::<T>::contains_key((for_session, &afdt_id)), 
2726                Error::<T>::AffidavitAuthorNotFound
2727            );
2728            Self::can_submit_affidavit(&afdt_id)
2729        }
2730
2731        /// Returns `Ok(())` if the given author is eligible to submit an election
2732        /// extrinsic at the current block.
2733        ///
2734        /// This function performs a full pre-check for the [`Self::elect`] extrinsic,
2735        /// verifying that:
2736        /// - the current block has an identifiable block author,
2737        /// - the provided author matches the current block author, and
2738        /// - the current block falls within the configured election window.
2739        ///
2740        /// Only the block author may run the election for a given block, ensuring
2741        /// that election submissions cannot be spoofed by non-producing validators.
2742        ///
2743        /// ## Parameters
2744        /// - `author`: The author asserting eligibility to run the election.
2745        ///
2746        /// ## Returns
2747        /// - `Ok(())` if the author is the current block author and the election window is open.
2748        /// - `Err(DispatchError)` otherwise.
2749        pub fn can_elect(author: AuthorOf<T>) -> DispatchResult {
2750            let block_author = pallet_authorship::Pallet::<T>::author().ok_or(Error::<T>::BlockAuthorNotFound)?;
2751            ensure!(
2752                author == block_author,
2753                Error::<T>::NotABlockAuthor
2754            );
2755            <Internals<T> as ElectAuthors<AuthorOf<T>, ElectionVia<T>>>::can_process_election(&Some(block_author))?;
2756            Ok(())
2757        }
2758
2759        /// Computes the affidavit submission window for the current session.
2760        ///
2761        /// The window is derived from the session start and average session length:
2762        /// ```text
2763        /// start = session_start + (affidavit_begins_at * avg_session_length)
2764        /// end   = session_start + (affidavit_ends_at   * avg_session_length)
2765        /// ```
2766        ///
2767        /// Note: `affidavit_begins_at` and `affidavit_ends_at` are **percentages**
2768        /// of the session length and are applied to compute block offsets.
2769        ///
2770        /// ## Returns
2771        /// - `Ok(AffidavitWindow)` containing start and end blocks
2772        /// - `DispatchError` otherwise
2773        ///
2774        /// ## Notes
2775        /// - The resulting window is session-relative and recalculated each session.
2776        /// - Affidavits submitted outside this window should be rejected.
2777        pub fn compute_affidavit_window() -> Result<AffidavitWindow<T>, DispatchError> {
2778            let session_start = SessionStartAt::<T>::get();
2779            let avg_session_len =
2780                <<T as crate::Config>::NextSessionRotation as EstimateNextSessionRotation<
2781                    BlockNumberFor<T>,
2782                >>::average_session_length();
2783
2784            let begin_at = AffidavitBeginsAt::<T>::get();
2785            let begin_offset = begin_at.mul_floor(avg_session_len);
2786            let start_block = session_start.saturating_add(begin_offset);
2787
2788            let ends_at = AffidavitEndsAt::<T>::get();
2789            let invariant = ends_at > begin_at;
2790            debug_assert!(
2791                invariant,
2792                "invalid affidavit period configured during genesis or 
2793                root update to storage values, affidavit begins at {:?}
2794                and ends at {:?}",
2795                begin_at, ends_at
2796            );
2797            ensure!(invariant, Error::<T>::InvalidAffidavitPeriod);
2798            let end_offset = ends_at.mul_floor(avg_session_len);
2799            let end_block = session_start.saturating_add(end_offset);
2800            let aff_window = AffidavitWindow::<T> {
2801                start: start_block,
2802                end: end_block,
2803            };
2804            Ok(aff_window)
2805        }
2806
2807        /// Computes the election window for the current session.
2808        ///
2809        /// The election window is a sub-range of the affidavit window:
2810        /// ```text
2811        /// start = affidavit_start + (election_begins_at * (affidavit_end - affidavit_start))
2812        /// end   = affidavit_end
2813        /// ```
2814        ///
2815        /// Note:
2816        /// - `election_begins_at` is a **percentage** of the affidavit window range.
2817        /// - The election always **ends when the affidavit window ends**.
2818        ///
2819        /// ## Diagram
2820        /// ```text
2821        /// |--------- Affidavit Window ---------|
2822        /// |------|-----------------------------|
2823        ///        ^                             ^
2824        ///   election_start               election_end (= affidavit_end)
2825        /// ```
2826        ///
2827        /// ## Returns
2828        /// - `Ok(ElectionWindow)` containing start and end blocks
2829        /// - `DispatchError` otherwise
2830        pub fn compute_election_window() -> Result<ElectionWindow<T>, DispatchError> {
2831            let afdt_window = Self::compute_affidavit_window()?;
2832            let start_affidavit = afdt_window.start;
2833            let end_affidavit = afdt_window.end;
2834            let election_begin_at = ElectionBeginsAt::<T>::get();
2835            let affidavit_range = end_affidavit.saturating_sub(start_affidavit);
2836            let start_portion = election_begin_at.mul_floor(affidavit_range);
2837            let start_election = start_affidavit.saturating_add(start_portion);
2838            let elect_window = ElectionWindow::<T> {
2839                start: start_election,
2840                end: end_affidavit,
2841            };
2842            Ok(elect_window)
2843        }
2844    }
2845
2846    // ===============================================================================
2847    // `````````````````````````````` VALIDATE UNSIGNED ``````````````````````````````
2848    // ===============================================================================
2849
2850    impl<T: Config> ValidateUnsigned for Pallet<T> {
2851        type Call = Call<T>;
2852
2853        fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
2854            let Ok(aff_window) = Self::compute_affidavit_window() else {
2855                return InvalidTransaction::BadProof.into();
2856            };
2857            match call {
2858                Call::declare { payload, signature } => {
2859                    if !SignedPayload::<T>::verify::<T::AffidavitCrypto>(payload, signature.clone())
2860                    {
2861                        return InvalidTransaction::BadProof.into();
2862                    }
2863
2864                    let end_block = aff_window.end;
2865
2866                    // Current block number (fork-relative).
2867                    // Safe to use for freshness/expiry checks since validity is evaluated per fork head.
2868                    let current_block = frame_system::Pallet::<T>::block_number();
2869
2870                    // Reject if the affidavit window has expired.
2871                    // This is fork-safe because it is a monotonic freshness check.
2872                    if current_block > end_block {
2873                        return InvalidTransaction::Stale.into();
2874                    }
2875
2876                    let public = SignedPayload::<T>::public(payload);
2877                    let affidavit_pub: AffidavitId<T> = public.clone().into_account().into();
2878
2879                    let for_session = CurrentSession::<T>::get().saturating_add(One::one());
2880
2881                    // Ensure the signer is registered as a valid affidavit key for that session.
2882                    if !AffidavitKeys::<T>::contains_key((for_session, &affidavit_pub)) {
2883                        return InvalidTransaction::BadSigner.into();
2884                    }
2885
2886                    // Longevity: how long (in blocks) this transaction should remain valid in the pool.
2887                    // Derived from remaining time until the affidavit window closes.
2888                    let longetivity = end_block.saturating_sub(current_block).into();
2889
2890                    return ValidTransaction::with_tag_prefix("declare")
2891                        .priority(AffidavitTxPriority::<T>::get())
2892                        .longevity(longetivity.low_u64())
2893                        // Provide a uniqueness tag to prevent duplicate unsigned submissions
2894                        // per (session, validator). The tuple is SCALE-encoded and used as the pool key.
2895                        .and_provides((for_session, affidavit_pub))
2896                        // Allow propagation to other nodes (normal gossip).
2897                        .propagate(true)
2898                        .build();
2899                }
2900                Call::validate { payload, signature } => {
2901                    if !SignedPayload::<T>::verify::<T::AffidavitCrypto>(payload, signature.clone())
2902                    {
2903                        return InvalidTransaction::BadProof.into();
2904                    }
2905                    let avg_session_len = <<T as crate::Config>::NextSessionRotation as EstimateNextSessionRotation<BlockNumberFor<T>>>::average_session_length();
2906                    let session_start = SessionStartAt::<T>::get();
2907                    let current_block = frame_system::Pallet::<T>::block_number();
2908                    // Longevity: remaining blocks until end of session.
2909                    // This keeps the tx in the pool only while session is still active.
2910                    let longetivity = session_start
2911                        .saturating_add(avg_session_len)
2912                        .saturating_sub(current_block)
2913                        .into();
2914
2915                    return ValidTransaction::with_tag_prefix("validate")
2916                        .priority(ValidateTxPriority::<T>::get())
2917                        .longevity(longetivity.low_u64())
2918                        // Provide a uniqueness tag based on the SCALE-encoded payload.
2919                        // This prevents duplicate submissions of the exact same validation payload
2920                        // from coexisting in the transaction pool (replay/spam protection).
2921                        .and_provides(payload)
2922                        .propagate(true)
2923                        .build();
2924                }
2925                Call::elect { payload, signature } => {
2926                    if !SignedPayload::<T>::verify::<T::AffidavitCrypto>(payload, signature.clone())
2927                    {
2928                        return InvalidTransaction::BadProof.into();
2929                    }
2930                    let end_affidavit = aff_window.end;
2931                    let current_block = frame_system::Pallet::<T>::block_number();
2932
2933                    // Reject if election period has expired.
2934                    // Fork-safe freshness check.
2935                    if current_block > end_affidavit {
2936                        return InvalidTransaction::Stale.into();
2937                    }
2938
2939                    // Elections target the session after next (current + 2), since
2940                    // affidavit declaration would have resulted in newly rotated key.
2941                    let for_session = CurrentSession::<T>::get().saturating_add(2u32.into());
2942
2943                    let public = SignedPayload::<T>::public(payload);
2944                    let affidavit_pub: AffidavitId<T> = public.clone().into_account().into();
2945
2946                    // Ensure signer is eligible for that future session.
2947                    if !AffidavitKeys::<T>::contains_key((for_session, &affidavit_pub.clone())) {
2948                        return InvalidTransaction::BadSigner.into();
2949                    }
2950
2951                    // Longevity tied to remaining election window.
2952                    let longetivity = end_affidavit.saturating_sub(current_block).into();
2953
2954                    return ValidTransaction::with_tag_prefix("elect")
2955                        .priority(ElectionTxPriority::<T>::get())
2956                        .longevity(longetivity.low_u64())
2957                        // Provide uniqueness per (future session, validator)
2958                        // to avoid duplicate elections.
2959                        .and_provides((for_session, affidavit_pub))
2960                        // Do not propagate: only the block author (local node)
2961                        // should include this tx, As its checked in the runtime via
2962                        // pallet-authorship.
2963                        .propagate(false)
2964                        .build();
2965                }
2966                _ => InvalidTransaction::Call.into(),
2967            }
2968        }
2969    }
2970}
2971
2972// ===============================================================================
2973// `````````````````````````````````` API TESTS ``````````````````````````````````
2974// ===============================================================================
2975
2976#[cfg(test)]
2977mod ext_tests {
2978
2979    // ===============================================================================
2980    // ``````````````````````````````````` IMPORTS ```````````````````````````````````
2981    // ===============================================================================
2982
2983    // --- Local crate imports ---
2984    use crate::{mock::*, types::ForceGenesisConfig};
2985
2986    // --- Scale-codec crates ---
2987    use codec::{Decode, Encode};
2988
2989    // --- FRAME Suite ---
2990    use frame_suite::{blockchain::*, roles::*};
2991
2992    // --- FRAME Support ---
2993    use frame_support::{
2994        assert_err, assert_noop, assert_ok,
2995        pallet_prelude::{TransactionSource, ValidateUnsigned},
2996        traits::{Hooks, tokens::{Fortitude, Precision}},
2997    };
2998
2999    // --- Substrate primitives ---
3000    use sp_runtime::{
3001        traits::{Convert, Zero},
3002        transaction_validity::InvalidTransaction,
3003        DispatchError, WeakBoundedVec,
3004    };
3005
3006    // ===============================================================================
3007    // ```````````````````````````````````` HOOKS ````````````````````````````````````
3008    // ===============================================================================
3009
3010    #[test]
3011    fn on_initialize_success() {
3012        chain_manager_test_ext().execute_with(|| {
3013            CurrentSession::put(1);
3014            SessionStartsAt::put(10);
3015
3016            set_user_balance_and_hold(ALICE, 100, 100).unwrap();
3017            RoleAdapter::enroll(&ALICE, 100, Fortitude::Force).unwrap();
3018
3019            set_block_author(ALICE);
3020
3021            assert!(PointsAdapter::points_of(&ALICE).is_err());
3022
3023            System::set_block_number(11);
3024            Pallet::on_initialize(11);
3025
3026            // Check that ALICE received a point for this session
3027            let points = PointsAdapter::points_of(&ALICE).unwrap();
3028            assert_eq!(points, 1);
3029        });
3030    }
3031
3032    #[test]
3033    fn ocw_initializes_active_affidavit_key() {
3034        let mut env = new_ocw_env();
3035        env.ext.execute_with(|| {
3036            ocw_step();
3037            assert!(affidavit_key_count().is_zero());
3038            assert!(get_afdt_key().is_none());
3039            ocw_step();
3040            assert!(get_afdt_key().is_some());
3041            let afdt_key_1 = get_afdt_key().unwrap();
3042            assert!(get_public_key(afdt_key_1.clone()).is_some());
3043
3044            ocw_step();
3045            // Since, key is already initialized, no new key is initialized.
3046            assert!(get_afdt_key().is_some());
3047            let afdt_key_2 = get_afdt_key().unwrap();
3048            assert!(get_public_key(afdt_key_2.clone()).is_some());
3049            assert_eq!(afdt_key_1, afdt_key_2);
3050        });
3051    }
3052
3053    #[test]
3054    fn ocw_declares_affidavit() {
3055        let mut env = new_ocw_env();
3056        env.ext.execute_with(|| {
3057            init_fork_graph();
3058            set_session_config();
3059            CurrentSession::set(1);
3060
3061            set_default_user_balance_and_hold(ALICE).unwrap();
3062            set_default_user_balance_and_hold(ALAN).unwrap();
3063
3064            enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
3065            direct_fund_author(ALAN, ALICE, STANDARD_FUND).unwrap();
3066
3067            assert!(affidavit_key_count().is_zero());
3068            assert!(get_finalized_afdt_key().is_none());
3069            while System::block_number() < AFDT_SUBMISSION_START {
3070                ocw_step();
3071            }
3072            assert!(get_afdt_key().is_some());
3073            let afdt_key_1 = get_afdt_key().unwrap();
3074            assert!(get_public_key(afdt_key_1.clone()).is_some());
3075            assert!(get_next_afdt_key().is_some());
3076
3077            let afdt_key = get_afdt_key().unwrap();
3078            let for_session = CurrentSession::get() + 1;
3079            AffidavitKeys::insert((for_session, afdt_key.clone()), ALICE);
3080
3081            env.pool_state.write().transactions.clear();
3082
3083            let tx = env.pool_state.read().transactions.clone();
3084            assert_eq!(tx.len(), 0);
3085            ocw_step();
3086            let tx = env.pool_state.read().transactions.clone();
3087            assert_eq!(tx.len(), 1);
3088            let submited_ext = env.pool_state.read().transactions.last().unwrap().clone();
3089            let ext_decode = UncheckedExtrinsic::decode(&mut &submited_ext[..]).unwrap();
3090            assert!(matches!(
3091                ext_decode.function,
3092                RuntimeCall::ChainManager(crate::Call::declare { .. })
3093            ));
3094            ocw_step();
3095            let tx = env.pool_state.read().transactions.clone();
3096            assert_eq!(tx.len(), 2);
3097            let submited_ext = env.pool_state.read().transactions.last().unwrap().clone();
3098            let ext_decode = UncheckedExtrinsic::decode(&mut &submited_ext[..]).unwrap();
3099            assert!(matches!(
3100                ext_decode.function,
3101                RuntimeCall::ChainManager(crate::Call::declare { .. })
3102            ));
3103        })
3104    }
3105
3106    #[test]
3107    fn ocw_submits_election_tx_when_eligible() {
3108        let mut env = new_ocw_env();
3109        env.ext.execute_with(|| {
3110            set_session_config();
3111            CurrentSession::put(1);
3112
3113            set_default_user_balance_and_hold(ALICE).unwrap();
3114            set_default_user_balance_and_hold(ALAN).unwrap();
3115
3116            enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
3117            direct_fund_author(ALAN, ALICE, STANDARD_FUND).unwrap();
3118
3119            // prepare active key first.
3120            while System::block_number() < AFDT_SUBMISSION_START {
3121                ocw_step();
3122            }
3123
3124            let afdt_key = get_afdt_key().unwrap();
3125            let for_session = CurrentSession::get() + 1;
3126            AffidavitKeys::insert((for_session, afdt_key.clone()), ALICE);
3127
3128            ocw_step();
3129
3130            let tx = env.pool_state.read().transactions.clone();
3131            assert_eq!(tx.len(), 1);
3132            let submited_ext = env.pool_state.read().transactions.last().unwrap().clone();
3133            let ext_decode = UncheckedExtrinsic::decode(&mut &submited_ext[..]).unwrap();
3134            assert!(matches!(
3135                ext_decode.function,
3136                RuntimeCall::ChainManager(crate::Call::declare { .. })
3137            ));
3138            // declare executed
3139            let next_afdt_key = get_next_afdt_key().unwrap();
3140            AffidavitKeys::insert((for_session + 1, next_afdt_key), ALICE);
3141
3142            // move into just before election-eligible block
3143            while System::block_number() < ELECTION_START {
3144                ocw_step();
3145            }
3146            env.pool_state.write().transactions.clear();
3147
3148            let tx = env.pool_state.read().transactions.clone();
3149            assert_eq!(tx.len(), 0);
3150            ocw_step();
3151            let tx = env.pool_state.read().transactions.clone();
3152            assert_eq!(tx.len(), 1);
3153            let submited_ext = env.pool_state.read().transactions.last().unwrap().clone();
3154            let ext_decode = UncheckedExtrinsic::decode(&mut &submited_ext[..]).unwrap();
3155            assert!(matches!(
3156                ext_decode.function,
3157                RuntimeCall::ChainManager(crate::Call::elect { .. })
3158            ));
3159        });
3160    }
3161
3162    // ===============================================================================
3163    // `````````````````````````````` VALIDATE UNSIGNED ``````````````````````````````
3164    // ===============================================================================
3165
3166    #[test]
3167    fn validate_unsigned_validate_accepts_valid_payload() {
3168        let mut env = new_ocw_env();
3169        env.ext.execute_with(|| {
3170            set_session(1);
3171            set_session_config();
3172            System::set_block_number(1);
3173
3174            let public = generate_affidavit_keypair();
3175
3176            let payload = ValidatePayloadOf {
3177                public: public.clone(),
3178            };
3179            let signature = sign_payload(&payload.encode(), public);
3180
3181            let call = Call::validate { payload, signature };
3182
3183            let validity = Pallet::validate_unsigned(TransactionSource::External, &call);
3184
3185            let valid = validity.unwrap();
3186            assert_eq!(valid.priority, ValidateTxPriority::get());
3187            assert_eq!(valid.longevity, 600);
3188            assert!(valid.propagate);
3189        });
3190    }
3191
3192    #[test]
3193    fn validate_unsigned_validate_rejects_bad_signature() {
3194        let mut env = new_ocw_env();
3195        env.ext.execute_with(|| {
3196            set_session(1);
3197            set_session_config();
3198            System::set_block_number(1);
3199
3200            let public = generate_affidavit_keypair();
3201            let wrong_public = generate_affidavit_keypair();
3202
3203            let payload = ValidatePayloadOf {
3204                public: public.clone(),
3205            };
3206            let bad_signature = sign_payload(&payload.encode(), wrong_public);
3207
3208            let call = Call::validate {
3209                payload,
3210                signature: bad_signature,
3211            };
3212
3213            let validity = Pallet::validate_unsigned(TransactionSource::External, &call);
3214
3215            assert_err!(validity, InvalidTransaction::BadProof);
3216        });
3217    }
3218
3219    #[test]
3220    fn validate_unsigned_declare_affidavit_accepts_registered_key_within_window() {
3221        let mut env = new_ocw_env();
3222        env.ext.execute_with(|| {
3223            set_session(1);
3224            set_session_config();
3225            System::set_block_number(AFDT_SUBMISSION_START);
3226
3227            let afdt_id = generate_affidavit_id();
3228            let public = get_public_key(afdt_id.clone()).unwrap();
3229            let next_afdt_id = generate_affidavit_id();
3230
3231            let for_session = CurrentSession::get() + 1;
3232            register_affidavit_key(for_session, afdt_id, ALICE);
3233
3234            let payload = AffidavitPayloadOf {
3235                public: public.clone(),
3236                rotate: next_afdt_id,
3237            };
3238            let signature = sign_payload(&payload.encode(), public);
3239
3240            let call = Call::declare { payload, signature };
3241
3242            let validity = Pallet::validate_unsigned(TransactionSource::External, &call);
3243
3244            let valid = validity.unwrap();
3245            assert_eq!(valid.priority, AffidavitTxPriority::get());
3246            assert_eq!(valid.longevity, AFDT_SUBMISSION_END - AFDT_SUBMISSION_START);
3247            assert!(valid.propagate);
3248        });
3249    }
3250
3251    #[test]
3252    fn validate_unsigned_declare_affidavit_rejects_bad_signature() {
3253        let mut env = new_ocw_env();
3254        env.ext.execute_with(|| {
3255            set_session(1);
3256            set_session_config();
3257            System::set_block_number(AFDT_SUBMISSION_START);
3258
3259            let afdt_id = generate_affidavit_id();
3260            let public = get_public_key(afdt_id.clone()).unwrap();
3261            let wrong_public = generate_affidavit_keypair();
3262            let next_afdt_id = generate_affidavit_id();
3263
3264            let for_session = CurrentSession::get() + 1;
3265            register_affidavit_key(for_session, afdt_id, ALICE);
3266
3267            let payload = AffidavitPayloadOf {
3268                public: public.clone(),
3269                rotate: next_afdt_id,
3270            };
3271            let bad_signature = sign_payload(&payload.encode(), wrong_public);
3272
3273            let call = Call::declare {
3274                payload,
3275                signature: bad_signature,
3276            };
3277
3278            let validity = Pallet::validate_unsigned(TransactionSource::External, &call);
3279
3280            assert_err!(validity, InvalidTransaction::BadProof);
3281        });
3282    }
3283
3284    #[test]
3285    fn validate_unsigned_declare_affidavit_rejects_unregistered_key() {
3286        let mut env = new_ocw_env();
3287        env.ext.execute_with(|| {
3288            set_session(1);
3289            set_session_config();
3290            System::set_block_number(AFDT_SUBMISSION_START);
3291
3292            let afdt_id = generate_affidavit_id();
3293            let public = get_public_key(afdt_id).unwrap();
3294            let next_afdt_id = generate_affidavit_id();
3295
3296            let payload = AffidavitPayloadOf {
3297                public: public.clone(),
3298                rotate: next_afdt_id,
3299            };
3300            let signature = sign_payload(&payload.encode(), public);
3301
3302            let call = Call::declare { payload, signature };
3303
3304            let validity = Pallet::validate_unsigned(TransactionSource::External, &call);
3305
3306            assert_err!(validity, InvalidTransaction::BadSigner);
3307        });
3308    }
3309
3310    #[test]
3311    fn validate_unsigned_declare_affidavit_rejects_stale_call() {
3312        let mut env = new_ocw_env();
3313        env.ext.execute_with(|| {
3314            set_session(1);
3315            set_session_config();
3316            System::set_block_number(AFDT_SUBMISSION_END + 1);
3317
3318            let afdt_id = generate_affidavit_id();
3319            let public = get_public_key(afdt_id.clone()).unwrap();
3320            let next_afdt_id = generate_affidavit_id();
3321
3322            let for_session = CurrentSession::get() + 1;
3323            register_affidavit_key(for_session, afdt_id, ALICE);
3324
3325            let payload = AffidavitPayloadOf {
3326                public: public.clone(),
3327                rotate: next_afdt_id,
3328            };
3329            let signature = sign_payload(&payload.encode(), public);
3330
3331            let call = Call::declare { payload, signature };
3332
3333            let validity = Pallet::validate_unsigned(TransactionSource::External, &call);
3334
3335            assert_err!(validity, InvalidTransaction::Stale);
3336        });
3337    }
3338
3339    #[test]
3340    fn validate_unsigned_elect_authors_accepts_registered_rotated_key() {
3341        let mut env = new_ocw_env();
3342        env.ext.execute_with(|| {
3343            set_session(1);
3344            set_session_config();
3345            System::set_block_number(ELECTION_START);
3346
3347            let afdt_id = generate_affidavit_id();
3348            let public = get_public_key(afdt_id.clone()).unwrap();
3349
3350            let for_session = CurrentSession::get() + 2;
3351            register_affidavit_key(for_session, afdt_id, ALICE);
3352
3353            let payload = ElectionPayloadOf {
3354                public: public.clone(),
3355            };
3356            let signature = sign_payload(&payload.encode(), public);
3357
3358            let call = Call::elect { payload, signature };
3359
3360            let validity = Pallet::validate_unsigned(TransactionSource::Local, &call);
3361
3362            let valid = validity.unwrap();
3363            assert_eq!(valid.priority, ElectionTxPriority::get());
3364            assert_eq!(valid.longevity, AFDT_SUBMISSION_END - ELECTION_START);
3365            assert!(!valid.propagate);
3366        });
3367    }
3368
3369    #[test]
3370    fn validate_unsigned_elect_authors_rejects_bad_signature() {
3371        let mut env = new_ocw_env();
3372        env.ext.execute_with(|| {
3373            set_session(1);
3374            set_session_config();
3375            System::set_block_number(ELECTION_START);
3376
3377            let afdt_id = generate_affidavit_id();
3378            let public = get_public_key(afdt_id.clone()).unwrap();
3379            let wrong_public = generate_affidavit_keypair();
3380
3381            let for_session = CurrentSession::get() + 2;
3382            register_affidavit_key(for_session, afdt_id, ALICE);
3383
3384            let payload = ElectionPayloadOf {
3385                public: public.clone(),
3386            };
3387            let bad_signature = sign_payload(&payload.encode(), wrong_public);
3388
3389            let call = Call::elect {
3390                payload,
3391                signature: bad_signature,
3392            };
3393
3394            let validity = Pallet::validate_unsigned(TransactionSource::Local, &call);
3395
3396            assert_err!(validity, InvalidTransaction::BadProof);
3397        });
3398    }
3399
3400    #[test]
3401    fn validate_unsigned_elect_authors_rejects_unregistered_key() {
3402        let mut env = new_ocw_env();
3403        env.ext.execute_with(|| {
3404            set_session(1);
3405            set_session_config();
3406            System::set_block_number(ELECTION_START);
3407
3408            let afdt_id = generate_affidavit_id();
3409            let public = get_public_key(afdt_id).unwrap();
3410
3411            let payload = ElectionPayloadOf {
3412                public: public.clone(),
3413            };
3414            let signature = sign_payload(&payload.encode(), public);
3415
3416            let call = Call::elect { payload, signature };
3417
3418            let validity = Pallet::validate_unsigned(TransactionSource::Local, &call);
3419
3420            assert_err!(validity, InvalidTransaction::BadSigner);
3421        });
3422    }
3423
3424    #[test]
3425    fn validate_unsigned_elect_authors_rejects_stale_call() {
3426        let mut env = new_ocw_env();
3427        env.ext.execute_with(|| {
3428            set_session(1);
3429            set_session_config();
3430            System::set_block_number(AFDT_SUBMISSION_END + 1);
3431
3432            let afdt_id = generate_affidavit_id();
3433            let public = get_public_key(afdt_id.clone()).unwrap();
3434
3435            let for_session = CurrentSession::get() + 2;
3436            register_affidavit_key(for_session, afdt_id, ALICE);
3437
3438            let payload = ElectionPayloadOf {
3439                public: public.clone(),
3440            };
3441            let signature = sign_payload(&payload.encode(), public);
3442
3443            let call = Call::elect { payload, signature };
3444
3445            let validity = Pallet::validate_unsigned(TransactionSource::Local, &call);
3446
3447            assert_err!(validity, InvalidTransaction::Stale);
3448        });
3449    }
3450
3451    // ===============================================================================
3452    // ````````````````````````````````` EXTRINSICS ``````````````````````````````````
3453    // ===============================================================================
3454
3455    #[test]
3456    fn validate_success() {
3457        let mut env = new_ocw_env();
3458        env.ext.execute_with(|| {
3459            init_fork_graph();
3460            set_session_config();
3461            CurrentSession::put(1);
3462
3463            set_default_user_balance_and_hold(ALICE).unwrap();
3464
3465            enroll_author_with_default_collateral(ALICE).unwrap();
3466
3467            let afdt_id = generate_affidavit_id();
3468            insert_active_afdt_key(afdt_id.clone()).unwrap();
3469            run_to_block(120);
3470
3471            assert!(!AffidavitKeys::contains_key((2, afdt_id.clone())));
3472
3473            let (payload, signature) = Pallet::sign_validate_payload().unwrap();
3474            assert_ok!(Pallet::validate(
3475                RuntimeOrigin::signed(ALICE),
3476                payload,
3477                signature,
3478            ));
3479
3480            assert!(AffidavitKeys::contains_key((2, afdt_id)));
3481        })
3482    }
3483
3484    #[test]
3485    fn validate_err_bad_origin() {
3486        let mut env = new_ocw_env();
3487        env.ext.execute_with(|| {
3488            init_fork_graph();
3489            set_session_config();
3490            CurrentSession::put(1);
3491
3492            set_default_user_balance_and_hold(ALICE).unwrap();
3493
3494            enroll_author_with_default_collateral(ALICE).unwrap();
3495
3496            let afdt_id = generate_affidavit_id();
3497            insert_active_afdt_key(afdt_id.clone()).unwrap();
3498            run_to_block(120);
3499
3500            let (payload, signature) = Pallet::sign_validate_payload().unwrap();
3501            assert_noop!(
3502                Pallet::validate(RuntimeOrigin::root(), payload, signature,),
3503                DispatchError::BadOrigin
3504            );
3505        })
3506    }
3507
3508    #[test]
3509    fn chill_err_session_id_query_failed() {
3510        let mut env = new_ocw_env();
3511        env.ext.execute_with(|| {
3512            CurrentSession::put(1);
3513
3514            set_default_user_balance_and_hold(ALICE).unwrap();
3515            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3516
3517            let afdt_id = generate_affidavit_id();
3518
3519            assert_noop!(
3520                Pallet::chill(RuntimeOrigin::signed(BOB), afdt_id),
3521                Error::SessionIdQueryFailed
3522            );
3523        })
3524    }
3525
3526    #[test]
3527    fn chill_err_validator_cannot_chill() {
3528        let mut env = new_ocw_env();
3529        env.ext.execute_with(|| {
3530            CurrentSession::put(1);
3531
3532            set_default_user_balance_and_hold(ALICE).unwrap();
3533            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3534
3535            let afdt_id = generate_affidavit_id();
3536
3537            let alice_session_id = Pallet::convert(ALICE.clone()).unwrap();
3538            Validators::put(vec![alice_session_id]);
3539
3540            assert_err!(
3541                Pallet::chill(RuntimeOrigin::signed(ALICE), afdt_id),
3542                Error::ValidatorCannotChill
3543            )
3544        })
3545    }
3546
3547    #[test]
3548    fn chill_err_author_not_affidavit_owner() {
3549        let mut env = new_ocw_env();
3550        env.ext.execute_with(|| {
3551            set_session_config();
3552            CurrentSession::put(1);
3553
3554            set_default_user_balance_and_hold(ALICE).unwrap();
3555            set_default_user_balance_and_hold(BOB).unwrap();
3556
3557            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3558            RoleAdapter::enroll(&BOB, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3559
3560            let afdt_id = generate_affidavit_id();
3561
3562            System::set_block_number(150);
3563            ext_validate(ALICE, afdt_id.clone()).unwrap();
3564
3565            let nxt_afdt_id = generate_affidavit_id();
3566            let payload = TestAfdtPayload {
3567                active_afdt_pub: afdt_id.clone(),
3568                next_afdt_pub: nxt_afdt_id.clone(),
3569            };
3570            System::set_block_number(AFDT_SUBMISSION_START);
3571            ext_declare_affidavit(ALICE, payload).unwrap();
3572
3573            assert_err!(
3574                Pallet::chill(RuntimeOrigin::signed(BOB), nxt_afdt_id),
3575                Error::AuthorNotAffidavitOwner
3576            )
3577        })
3578    }
3579
3580    #[test]
3581    #[should_panic]
3582    fn chill_err_declared_before_affidavit_period() {
3583        let mut env = new_ocw_env();
3584        env.ext.execute_with(|| {
3585            set_session_config();
3586            CurrentSession::put(1);
3587
3588            set_default_user_balance_and_hold(ALICE).unwrap();
3589
3590            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3591
3592            let afdt_id = generate_affidavit_id();
3593
3594            System::set_block_number(150);
3595            ext_validate(ALICE, afdt_id.clone()).unwrap();
3596
3597            let nxt_afdt_id = generate_affidavit_id();
3598            let payload = TestAfdtPayload {
3599                active_afdt_pub: afdt_id.clone(),
3600                next_afdt_pub: nxt_afdt_id.clone(),
3601            };
3602            System::set_block_number(AFDT_SUBMISSION_START);
3603            ext_declare_affidavit(ALICE, payload).unwrap();
3604
3605            System::set_block_number(AFDT_SUBMISSION_START - 1);
3606            Pallet::chill(RuntimeOrigin::signed(ALICE), nxt_afdt_id).unwrap();
3607        })
3608    }
3609
3610    #[test]
3611    fn chill_err_candidate_cannot_chill() {
3612        let mut env = new_ocw_env();
3613        env.ext.execute_with(|| {
3614            set_session_config();
3615            CurrentSession::put(1);
3616
3617            set_default_user_balance_and_hold(ALICE).unwrap();
3618
3619            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3620
3621            let afdt_id = generate_affidavit_id();
3622
3623            System::set_block_number(150);
3624            ext_validate(ALICE, afdt_id.clone()).unwrap();
3625
3626            let nxt_afdt_id = generate_affidavit_id();
3627            let payload = TestAfdtPayload {
3628                active_afdt_pub: afdt_id.clone(),
3629                next_afdt_pub: nxt_afdt_id.clone(),
3630            };
3631            System::set_block_number(AFDT_SUBMISSION_START);
3632            ext_declare_affidavit(ALICE, payload).unwrap();
3633
3634            assert_noop!(
3635                Pallet::chill(RuntimeOrigin::signed(ALICE), nxt_afdt_id),
3636                Error::CandidateCannotChill
3637            );
3638        })
3639    }
3640
3641    #[test]
3642    fn chill_err_elected_cannot_chill() {
3643        let mut env = new_ocw_env();
3644        env.ext.execute_with(|| {
3645            set_session_config();
3646            CurrentSession::put(1);
3647
3648            set_default_user_balance_and_hold(ALICE).unwrap();
3649            set_default_user_balance_and_hold(BOB).unwrap();
3650            set_default_user_balance_and_hold(ALAN).unwrap();
3651            set_default_user_balance_and_hold(CHARLIE).unwrap();
3652            set_default_user_balance_and_hold(AMY).unwrap();
3653            set_default_user_balance_and_hold(NIX).unwrap();
3654
3655            enroll_authors_with_default_collateral(vec![ALICE, BOB, ALAN]).unwrap();
3656            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
3657            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
3658            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
3659
3660            let alice_afdt_id = generate_affidavit_id();
3661            let alan_afdt_id = generate_affidavit_id();
3662            let bob_afdt_id = generate_affidavit_id();
3663
3664            System::set_block_number(150);
3665            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
3666            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
3667            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
3668
3669            let alice_nxt_afdt_id = generate_affidavit_id();
3670            let alan_nxt_afdt_id: AccountId = generate_affidavit_id();
3671            let bob_nxt_afdt_id: AccountId = generate_affidavit_id();
3672
3673            let alice_payload = TestAfdtPayload {
3674                active_afdt_pub: alice_afdt_id.clone(),
3675                next_afdt_pub: alice_nxt_afdt_id.clone(),
3676            };
3677
3678            let alan_payload = TestAfdtPayload {
3679                active_afdt_pub: alan_afdt_id.clone(),
3680                next_afdt_pub: alan_nxt_afdt_id.clone(),
3681            };
3682
3683            let bob_payload = TestAfdtPayload {
3684                active_afdt_pub: bob_afdt_id.clone(),
3685                next_afdt_pub: bob_nxt_afdt_id.clone(),
3686            };
3687
3688            System::set_block_number(AFDT_SUBMISSION_START);
3689            ext_declare_affidavit(ALICE, alice_payload).unwrap();
3690            ext_declare_affidavit(ALAN, alan_payload).unwrap();
3691            ext_declare_affidavit(BOB, bob_payload).unwrap();
3692
3693            System::set_block_number(ELECTION_START);
3694            let elected = ext_elect_authors(ALICE, alice_nxt_afdt_id.clone()).unwrap();
3695            assert!(elected.iter().any(|id| *id == ALICE));
3696
3697            System::set_block_number(AFDT_SUBMISSION_END + 1);
3698            assert_noop!(
3699                Pallet::chill(RuntimeOrigin::signed(ALICE), alice_nxt_afdt_id),
3700                Error::ElectedCannotChill
3701            );
3702        })
3703    }
3704
3705    #[test]
3706    fn chill_err_author_not_affidavit_owner_of_the_registered_key() {
3707        let mut env = new_ocw_env();
3708        env.ext.execute_with(|| {
3709            set_session_config();
3710            CurrentSession::put(1);
3711
3712            set_default_user_balance_and_hold(ALICE).unwrap();
3713            set_default_user_balance_and_hold(BOB).unwrap();
3714
3715            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3716            RoleAdapter::enroll(&BOB, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3717
3718            let afdt_id = generate_affidavit_id();
3719
3720            System::set_block_number(150);
3721            ext_validate(ALICE, afdt_id.clone()).unwrap();
3722
3723            assert_noop!(
3724                Pallet::chill(RuntimeOrigin::signed(BOB), afdt_id),
3725                Error::AuthorNotAffidavitOwner
3726            );
3727        })
3728    }
3729
3730    #[test]
3731    fn chill_err_invalid_rotated_affidavit_key() {
3732        let mut env = new_ocw_env();
3733        env.ext.execute_with(|| {
3734            set_session_config();
3735            CurrentSession::put(1);
3736            set_default_user_balance_and_hold(ALICE).unwrap();
3737            RoleAdapter::enroll(&ALICE, STANDARD_COLLATERAL, Fortitude::Force).unwrap();
3738
3739            let afdt_id = generate_affidavit_id();
3740            System::set_block_number(150);
3741            ext_validate(ALICE, afdt_id.clone()).unwrap();
3742            let gen_afdt = Pallet::gen_affidavit(&afdt_id).unwrap();
3743            Pallet::submit_affidavit(&afdt_id, &gen_afdt).unwrap();
3744            assert_noop!(
3745                Pallet::chill(RuntimeOrigin::signed(ALICE), afdt_id),
3746                Error::InvalidRotatedAffidavitKey
3747            );
3748        })
3749    }
3750
3751    #[test]
3752    fn chill_success_chilling_unelected_author_after_election() {
3753        let mut env = new_ocw_env();
3754        env.ext.execute_with(|| {
3755            set_session_config();
3756            CurrentSession::put(1);
3757
3758            set_default_user_balance_and_hold(ALICE).unwrap();
3759            set_default_user_balance_and_hold(BOB).unwrap();
3760            set_default_user_balance_and_hold(ALAN).unwrap();
3761            set_default_user_balance_and_hold(CHARLIE).unwrap();
3762            set_default_user_balance_and_hold(AMY).unwrap();
3763            set_default_user_balance_and_hold(NIX).unwrap();
3764            set_default_user_balance_and_hold(MIKE).unwrap();
3765            set_default_user_balance_and_hold(LAYA).unwrap();
3766            set_default_user_balance_and_hold(DAVE).unwrap();
3767            set_default_user_balance_and_hold(JIM).unwrap();
3768
3769            let authors = vec![ALICE, BOB, ALAN, MIKE, LAYA, DAVE, JIM];
3770            enroll_authors_with_default_collateral(authors.clone()).unwrap();
3771            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
3772            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
3773            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
3774
3775            let alice_afdt_id = generate_affidavit_id();
3776            let alan_afdt_id = generate_affidavit_id();
3777            let bob_afdt_id = generate_affidavit_id();
3778            let mike_afdt_id = generate_affidavit_id();
3779            let laya_afdt_id = generate_affidavit_id();
3780            let dev_afdt_id = generate_affidavit_id();
3781            let jim_afdt_id = generate_affidavit_id();
3782
3783            System::set_block_number(150);
3784            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
3785            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
3786            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
3787            ext_validate(MIKE, mike_afdt_id.clone()).unwrap();
3788            ext_validate(LAYA, laya_afdt_id.clone()).unwrap();
3789            ext_validate(DAVE, dev_afdt_id.clone()).unwrap();
3790            ext_validate(JIM, jim_afdt_id.clone()).unwrap();
3791
3792            let alice_nxt_afdt_id = generate_affidavit_id();
3793            let alan_nxt_afdt_id = generate_affidavit_id();
3794            let bob_nxt_afdt_id = generate_affidavit_id();
3795            let jim_nxt_afdt_id = generate_affidavit_id();
3796            let mike_nxt_afdt_id = generate_affidavit_id();
3797            let dev_nxt_afdt_id = generate_affidavit_id();
3798            let laya_nxt_afdt_id = generate_affidavit_id();
3799
3800            let alice_payload = TestAfdtPayload {
3801                active_afdt_pub: alice_afdt_id.clone(),
3802                next_afdt_pub: alice_nxt_afdt_id.clone(),
3803            };
3804
3805            let alan_payload = TestAfdtPayload {
3806                active_afdt_pub: alan_afdt_id.clone(),
3807                next_afdt_pub: alan_nxt_afdt_id.clone(),
3808            };
3809
3810            let bob_payload = TestAfdtPayload {
3811                active_afdt_pub: bob_afdt_id.clone(),
3812                next_afdt_pub: bob_nxt_afdt_id.clone(),
3813            };
3814
3815            let mike_payload = TestAfdtPayload {
3816                active_afdt_pub: mike_afdt_id.clone(),
3817                next_afdt_pub: mike_nxt_afdt_id.clone(),
3818            };
3819
3820            let laya_payload = TestAfdtPayload {
3821                active_afdt_pub: laya_afdt_id.clone(),
3822                next_afdt_pub: laya_nxt_afdt_id.clone(),
3823            };
3824
3825            let jim_payload = TestAfdtPayload {
3826                active_afdt_pub: jim_afdt_id.clone(),
3827                next_afdt_pub: jim_nxt_afdt_id.clone(),
3828            };
3829
3830            let dev_payload = TestAfdtPayload {
3831                active_afdt_pub: dev_afdt_id.clone(),
3832                next_afdt_pub: dev_nxt_afdt_id.clone(),
3833            };
3834
3835            System::set_block_number(AFDT_SUBMISSION_START);
3836            ext_declare_affidavit(ALICE, alice_payload).unwrap();
3837            ext_declare_affidavit(ALAN, alan_payload).unwrap();
3838            ext_declare_affidavit(BOB, bob_payload).unwrap();
3839            ext_declare_affidavit(MIKE, mike_payload).unwrap();
3840            ext_declare_affidavit(LAYA, laya_payload).unwrap();
3841            ext_declare_affidavit(JIM, jim_payload).unwrap();
3842            ext_declare_affidavit(DAVE, dev_payload).unwrap();
3843
3844            System::set_block_number(ELECTION_START);
3845            let elected = ext_elect_authors(ALICE, alice_nxt_afdt_id.clone()).unwrap();
3846            let mut not_elected = ALICE;
3847            for author in authors {
3848                if !elected.contains(&author) {
3849                    not_elected = author
3850                }
3851            }
3852            // dbg!(not_elected.clone());
3853            assert!(not_elected == JIM);
3854            System::set_block_number(AFDT_SUBMISSION_END + 1);
3855            assert_ok!(Pallet::chill(
3856                RuntimeOrigin::signed(JIM),
3857                jim_nxt_afdt_id.clone()
3858            ),);
3859            let nxt_afdt_session = CurrentSession::get() + 2;
3860            assert!(!AffidavitKeys::contains_key((
3861                nxt_afdt_session,
3862                jim_nxt_afdt_id
3863            )))
3864        })
3865    }
3866
3867    #[test]
3868    fn chill_success_before_start_affidavit() {
3869        let mut env = new_ocw_env();
3870        env.ext.execute_with(|| {
3871            set_session_config();
3872            CurrentSession::put(1);
3873
3874            set_default_user_balance_and_hold(ALICE).unwrap();
3875            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
3876
3877            let afdt_pub = generate_affidavit_id();
3878            System::set_block_number(AFDT_SUBMISSION_START - 25);
3879            ext_validate(ALICE, afdt_pub.clone()).unwrap();
3880
3881            assert_ok!(Pallet::chill(
3882                RuntimeOrigin::signed(ALICE),
3883                afdt_pub.clone()
3884            ));
3885            let for_session = CurrentSession::get() + 1;
3886            assert!(!AffidavitKeys::contains_key((
3887                for_session,
3888                afdt_pub.clone()
3889            )));
3890        })
3891    }
3892
3893    #[test]
3894    fn chill_success_during_affidavit_period_but_before_declaring() {
3895        let mut env = new_ocw_env();
3896        env.ext.execute_with(|| {
3897            set_session_config();
3898            CurrentSession::put(1);
3899
3900            set_default_user_balance_and_hold(ALICE).unwrap();
3901            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
3902
3903            let afdt_pub = generate_affidavit_id();
3904            System::set_block_number(AFDT_SUBMISSION_START - 25);
3905            ext_validate(ALICE, afdt_pub.clone()).unwrap();
3906            System::set_block_number(AFDT_SUBMISSION_START + 50);
3907            assert_ok!(Pallet::chill(
3908                RuntimeOrigin::signed(ALICE),
3909                afdt_pub.clone()
3910            ));
3911            let for_session = CurrentSession::get() + 1;
3912            assert!(!AffidavitKeys::contains_key((
3913                for_session,
3914                afdt_pub.clone()
3915            )));
3916        })
3917    }
3918
3919    #[test]
3920    fn chill_success_after_end_affidavit_without_declaring() {
3921        let mut env = new_ocw_env();
3922        env.ext.execute_with(|| {
3923            set_session_config();
3924            CurrentSession::put(1);
3925
3926            set_default_user_balance_and_hold(ALICE).unwrap();
3927            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
3928
3929            let afdt_pub = generate_affidavit_id();
3930            System::set_block_number(AFDT_SUBMISSION_START - 25);
3931            ext_validate(ALICE, afdt_pub.clone()).unwrap();
3932
3933            System::set_block_number(AFDT_SUBMISSION_END + 1);
3934            assert_ok!(Pallet::chill(
3935                RuntimeOrigin::signed(ALICE),
3936                afdt_pub.clone()
3937            ));
3938            let for_session = CurrentSession::get() + 1;
3939            assert!(!AffidavitKeys::contains_key((
3940                for_session,
3941                afdt_pub.clone()
3942            )));
3943        })
3944    }
3945
3946    #[test]
3947    fn chill_err_bad_origin() {
3948        let mut env = new_ocw_env();
3949        env.ext.execute_with(|| {
3950            set_session_config();
3951            CurrentSession::put(1);
3952
3953            set_default_user_balance_and_hold(ALICE).unwrap();
3954            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
3955
3956            let afdt_pub = generate_affidavit_id();
3957
3958            let for_session = CurrentSession::get() + 1;
3959            ext_validate(ALICE, afdt_pub.clone()).unwrap();
3960            System::set_block_number(AFDT_SUBMISSION_START - 25);
3961            assert_noop!(
3962                Pallet::chill(RuntimeOrigin::root(), afdt_pub.clone()),
3963                sp_runtime::DispatchError::BadOrigin
3964            );
3965            assert!(AffidavitKeys::contains_key((for_session, afdt_pub.clone())));
3966        })
3967    }
3968
3969    #[test]
3970    fn declare_affidavit_success() {
3971        let mut env = new_ocw_env();
3972        env.ext.execute_with(|| {
3973            init_fork_graph();
3974            set_session_config();
3975            CurrentSession::put(1);
3976
3977            set_default_users_balance_and_hold(vec![ALICE, BOB]).unwrap();
3978
3979            enroll_author_with_default_collateral(ALICE).unwrap();
3980            direct_fund_author(BOB, ALICE, 300).unwrap();
3981
3982            let afdt_id: sp_runtime::AccountId32 = generate_affidavit_id();
3983            insert_active_afdt_key(afdt_id.clone()).unwrap();
3984            ext_validate(ALICE, afdt_id.clone()).unwrap();
3985
3986            let for_session = CurrentSession::get() + 1;
3987            assert!(AffidavitKeys::contains_key((for_session, afdt_id.clone())));
3988
3989            let next_adft_id = generate_affidavit_id();
3990
3991            let public = get_public_key(afdt_id.clone()).unwrap();
3992            let affidavit_payload = AffidavitPayloadOf {
3993                public: public.clone(),
3994                rotate: next_adft_id.clone(),
3995            };
3996            let signature = sign_payload(&affidavit_payload.encode(), public);
3997
3998            assert!(!AuthorOfAffidavits::contains_key((for_session, ALICE)));
3999
4000            System::set_block_number(AFDT_SUBMISSION_START);
4001            assert_ok!(Pallet::declare(
4002                RuntimeOrigin::none(),
4003                affidavit_payload,
4004                signature
4005            ));
4006
4007            assert!(AuthorOfAffidavits::contains_key((for_session, ALICE)));
4008            let actual_afdt = AuthorOfAffidavits::get((for_session, ALICE)).unwrap();
4009            let afdt: WeakBoundedVec<(Funder, u64), _> =
4010                WeakBoundedVec::try_from(vec![(Funder::Direct(BOB), 300)]).unwrap();
4011            let expected_afdt = (AFDT_SUBMISSION_START, afdt);
4012            assert_eq!(actual_afdt, expected_afdt);
4013
4014            let for_session_2 = CurrentSession::get() + 2;
4015
4016            assert!(AffidavitKeys::contains_key((for_session_2, next_adft_id)));
4017
4018            #[cfg(not(feature = "dev"))]
4019            {
4020                System::assert_last_event(Event::AffidavitSubmitted { 
4021                    afdt_id: afdt_id, 
4022                    session: for_session
4023                    }
4024                    .into()
4025                );
4026            }
4027
4028            #[cfg(feature = "dev")]
4029            {
4030                let exp_afdt = Pallet::get_affidavit(&afdt_id).unwrap();
4031                System::assert_last_event(Event::AffidavitSubmitted { 
4032                    afdt_id: afdt_id, 
4033                    session: for_session,
4034                    author: ALICE,
4035                    affidavit: exp_afdt
4036                    }
4037                    .into()
4038                );
4039            }
4040        })
4041    }
4042
4043    #[test]
4044    fn declare_affidavit_err_bad_origin() {
4045        let mut env = new_ocw_env();
4046        env.ext.execute_with(|| {
4047            init_fork_graph();
4048            set_session_config();
4049            CurrentSession::put(1);
4050
4051            set_default_users_balance_and_hold(vec![ALICE, BOB]).unwrap();
4052
4053            enroll_author_with_default_collateral(ALICE).unwrap();
4054            direct_fund_author(BOB, ALICE, 300).unwrap();
4055
4056            let afdt_id = generate_affidavit_id();
4057            insert_active_afdt_key(afdt_id.clone()).unwrap();
4058            ext_validate(ALICE, afdt_id.clone()).unwrap();
4059
4060            let for_session = CurrentSession::get() + 1;
4061            assert!(AffidavitKeys::contains_key((for_session, afdt_id.clone())));
4062
4063            let next_adft_id = generate_affidavit_id();
4064
4065            let public = get_public_key(afdt_id.clone()).unwrap();
4066            let affidavit_payload = AffidavitPayloadOf {
4067                public: public.clone(),
4068                rotate: next_adft_id.clone(),
4069            };
4070            let signature = sign_payload(&affidavit_payload.encode(), public);
4071
4072            assert!(!AuthorOfAffidavits::contains_key((for_session, ALICE)));
4073
4074            System::set_block_number(AFDT_SUBMISSION_START);
4075
4076            System::set_block_number(136);
4077            assert_noop!(
4078                Pallet::declare(
4079                    RuntimeOrigin::signed(ALICE),
4080                    affidavit_payload.clone(),
4081                    signature.clone()
4082                ),
4083                sp_runtime::DispatchError::BadOrigin
4084            );
4085
4086            assert_noop!(
4087                Pallet::declare(RuntimeOrigin::root(), affidavit_payload, signature),
4088                sp_runtime::DispatchError::BadOrigin
4089            );
4090        })
4091    }
4092
4093    #[test]
4094    fn declare_affidavit_err_affidavit_author_not_found() {
4095        let mut env = new_ocw_env();
4096        env.ext.execute_with(|| {
4097            init_fork_graph();
4098            set_session_config();
4099            CurrentSession::put(1);
4100
4101            set_default_users_balance_and_hold(vec![ALICE, BOB]).unwrap();
4102
4103            enroll_author_with_default_collateral(ALICE).unwrap();
4104            direct_fund_author(BOB, ALICE, 300).unwrap();
4105
4106            let afdt_id = generate_affidavit_id();
4107            insert_active_afdt_key(afdt_id.clone()).unwrap();
4108            ext_validate(ALICE, afdt_id.clone()).unwrap();
4109
4110            let for_session = CurrentSession::get() + 1;
4111            assert!(AffidavitKeys::contains_key((for_session, afdt_id.clone())));
4112
4113            let next_adft_id = generate_affidavit_id();
4114
4115            let _public = get_public_key(afdt_id.clone()).unwrap();
4116
4117            let dummy_public = generate_affidavit_keypair();
4118            let affidavit_payload = AffidavitPayloadOf {
4119                public: dummy_public.clone(),
4120                rotate: next_adft_id.clone(),
4121            };
4122            let signature = sign_payload(&affidavit_payload.encode(), dummy_public);
4123
4124            assert!(!AuthorOfAffidavits::contains_key((for_session, ALICE)));
4125
4126            System::set_block_number(AFDT_SUBMISSION_START);
4127
4128            assert_noop!(
4129                Pallet::declare(RuntimeOrigin::none(), affidavit_payload, signature),
4130                Error::AffidavitAuthorNotFound
4131            );
4132        })
4133    }
4134
4135    #[test]
4136    fn elect_authors_success() {
4137        let mut env = new_ocw_env();
4138        env.ext.execute_with(|| {
4139            set_session_config();
4140            CurrentSession::put(1);
4141            let users = vec![ALICE, BOB, ALAN, CHARLIE, AMY, NIX, MIKE, LAYA, DAVE, JIM];
4142            set_default_users_balance_and_hold(users).unwrap();
4143
4144            let authors = vec![ALICE, BOB, ALAN, MIKE, LAYA, DAVE, JIM];
4145            enroll_authors_with_default_collateral(authors.clone()).unwrap();
4146            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
4147            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
4148            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
4149
4150            let alice_afdt_id = generate_affidavit_id();
4151            let alan_afdt_id = generate_affidavit_id();
4152            let bob_afdt_id = generate_affidavit_id();
4153            let mike_afdt_id = generate_affidavit_id();
4154            let laya_afdt_id = generate_affidavit_id();
4155            let dev_afdt_id = generate_affidavit_id();
4156            let jim_afdt_id = generate_affidavit_id();
4157
4158            System::set_block_number(150);
4159            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
4160            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
4161            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
4162            ext_validate(MIKE, mike_afdt_id.clone()).unwrap();
4163            ext_validate(LAYA, laya_afdt_id.clone()).unwrap();
4164            ext_validate(DAVE, dev_afdt_id.clone()).unwrap();
4165            ext_validate(JIM, jim_afdt_id.clone()).unwrap();
4166
4167            let alice_nxt_afdt_id = generate_affidavit_id();
4168            let alan_nxt_afdt_id = generate_affidavit_id();
4169            let bob_nxt_afdt_id = generate_affidavit_id();
4170            let jim_nxt_afdt_id = generate_affidavit_id();
4171            let mike_nxt_afdt_id = generate_affidavit_id();
4172            let dev_nxt_afdt_id = generate_affidavit_id();
4173            let laya_nxt_afdt_id = generate_affidavit_id();
4174
4175            let alice_payload = TestAfdtPayload {
4176                active_afdt_pub: alice_afdt_id.clone(),
4177                next_afdt_pub: alice_nxt_afdt_id.clone(),
4178            };
4179
4180            let alan_payload = TestAfdtPayload {
4181                active_afdt_pub: alan_afdt_id.clone(),
4182                next_afdt_pub: alan_nxt_afdt_id.clone(),
4183            };
4184
4185            let bob_payload = TestAfdtPayload {
4186                active_afdt_pub: bob_afdt_id.clone(),
4187                next_afdt_pub: bob_nxt_afdt_id.clone(),
4188            };
4189
4190            let mike_payload = TestAfdtPayload {
4191                active_afdt_pub: mike_afdt_id.clone(),
4192                next_afdt_pub: mike_nxt_afdt_id.clone(),
4193            };
4194
4195            let laya_payload = TestAfdtPayload {
4196                active_afdt_pub: laya_afdt_id.clone(),
4197                next_afdt_pub: laya_nxt_afdt_id.clone(),
4198            };
4199
4200            let jim_payload = TestAfdtPayload {
4201                active_afdt_pub: jim_afdt_id.clone(),
4202                next_afdt_pub: jim_nxt_afdt_id.clone(),
4203            };
4204
4205            let dev_payload = TestAfdtPayload {
4206                active_afdt_pub: dev_afdt_id.clone(),
4207                next_afdt_pub: dev_nxt_afdt_id.clone(),
4208            };
4209
4210            System::set_block_number(AFDT_SUBMISSION_START);
4211            ext_declare_affidavit(ALICE, alice_payload).unwrap();
4212            ext_declare_affidavit(ALAN, alan_payload).unwrap();
4213            ext_declare_affidavit(BOB, bob_payload).unwrap();
4214            ext_declare_affidavit(MIKE, mike_payload).unwrap();
4215            ext_declare_affidavit(LAYA, laya_payload).unwrap();
4216            ext_declare_affidavit(JIM, jim_payload).unwrap();
4217            ext_declare_affidavit(DAVE, dev_payload).unwrap();
4218
4219            let public = get_public_key(alice_nxt_afdt_id).unwrap();
4220            let payload = ElectionPayloadOf {
4221                public: public.clone(),
4222            };
4223            let signature = sign_payload(&payload.encode(), public);
4224            set_block_author(ALICE);
4225            System::set_block_number(ELECTION_START);
4226            assert_ok!(Pallet::elect(RuntimeOrigin::none(), payload, signature));
4227            let actual_elected = Internals::reveal().unwrap();
4228            let expected_elected = vec![BOB, MIKE, LAYA, DAVE, ALICE, ALAN];
4229            assert_eq!(actual_elected, expected_elected);
4230
4231            let for_session = CurrentSession::get() + 1;
4232            #[cfg(feature = "dev")]
4233            System::assert_last_event(Event::ElectedInstance {
4234                    session: for_session,
4235                    runner: ALICE,
4236                    elects: actual_elected
4237                }
4238                .into()
4239            );
4240
4241            #[cfg(not(feature = "dev"))]
4242            System::assert_last_event(Event::ElectedInstance {
4243                    session: for_session,
4244                    runner: ALICE,
4245                }
4246                .into()
4247            );
4248        })
4249    }
4250
4251    #[test]
4252    fn elect_authors_err_bad_origin() {
4253        let mut env = new_ocw_env();
4254        env.ext.execute_with(|| {
4255            set_session_config();
4256            CurrentSession::put(1);
4257            let users = vec![ALICE, BOB, ALAN, CHARLIE, AMY, NIX, MIKE, LAYA, DAVE, JIM];
4258            set_default_users_balance_and_hold(users).unwrap();
4259
4260            let authors = vec![ALICE, BOB, ALAN, MIKE, LAYA, DAVE, JIM];
4261            enroll_authors_with_default_collateral(authors.clone()).unwrap();
4262            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
4263            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
4264            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
4265
4266            let alice_afdt_id = generate_affidavit_id();
4267            let alan_afdt_id = generate_affidavit_id();
4268            let bob_afdt_id = generate_affidavit_id();
4269            let mike_afdt_id = generate_affidavit_id();
4270            let laya_afdt_id = generate_affidavit_id();
4271            let dev_afdt_id = generate_affidavit_id();
4272            let jim_afdt_id = generate_affidavit_id();
4273
4274            System::set_block_number(150);
4275            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
4276            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
4277            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
4278            ext_validate(MIKE, mike_afdt_id.clone()).unwrap();
4279            ext_validate(LAYA, laya_afdt_id.clone()).unwrap();
4280            ext_validate(DAVE, dev_afdt_id.clone()).unwrap();
4281            ext_validate(JIM, jim_afdt_id.clone()).unwrap();
4282
4283            let alice_nxt_afdt_id = generate_affidavit_id();
4284            let alan_nxt_afdt_id = generate_affidavit_id();
4285            let bob_nxt_afdt_id = generate_affidavit_id();
4286            let jim_nxt_afdt_id = generate_affidavit_id();
4287            let mike_nxt_afdt_id = generate_affidavit_id();
4288            let dev_nxt_afdt_id = generate_affidavit_id();
4289            let laya_nxt_afdt_id = generate_affidavit_id();
4290
4291            let alice_payload = TestAfdtPayload {
4292                active_afdt_pub: alice_afdt_id.clone(),
4293                next_afdt_pub: alice_nxt_afdt_id.clone(),
4294            };
4295
4296            let alan_payload = TestAfdtPayload {
4297                active_afdt_pub: alan_afdt_id.clone(),
4298                next_afdt_pub: alan_nxt_afdt_id.clone(),
4299            };
4300
4301            let bob_payload = TestAfdtPayload {
4302                active_afdt_pub: bob_afdt_id.clone(),
4303                next_afdt_pub: bob_nxt_afdt_id.clone(),
4304            };
4305
4306            let mike_payload = TestAfdtPayload {
4307                active_afdt_pub: mike_afdt_id.clone(),
4308                next_afdt_pub: mike_nxt_afdt_id.clone(),
4309            };
4310
4311            let laya_payload = TestAfdtPayload {
4312                active_afdt_pub: laya_afdt_id.clone(),
4313                next_afdt_pub: laya_nxt_afdt_id.clone(),
4314            };
4315
4316            let jim_payload = TestAfdtPayload {
4317                active_afdt_pub: jim_afdt_id.clone(),
4318                next_afdt_pub: jim_nxt_afdt_id.clone(),
4319            };
4320
4321            let dev_payload = TestAfdtPayload {
4322                active_afdt_pub: dev_afdt_id.clone(),
4323                next_afdt_pub: dev_nxt_afdt_id.clone(),
4324            };
4325
4326            System::set_block_number(AFDT_SUBMISSION_START);
4327            ext_declare_affidavit(ALICE, alice_payload).unwrap();
4328            ext_declare_affidavit(ALAN, alan_payload).unwrap();
4329            ext_declare_affidavit(BOB, bob_payload).unwrap();
4330            ext_declare_affidavit(MIKE, mike_payload).unwrap();
4331            ext_declare_affidavit(LAYA, laya_payload).unwrap();
4332            ext_declare_affidavit(JIM, jim_payload).unwrap();
4333            ext_declare_affidavit(DAVE, dev_payload).unwrap();
4334
4335            let public = get_public_key(alice_nxt_afdt_id).unwrap();
4336            let payload = ElectionPayloadOf {
4337                public: public.clone(),
4338            };
4339            let signature = sign_payload(&payload.encode(), public);
4340            set_block_author(ALICE);
4341            System::set_block_number(ELECTION_START);
4342
4343            assert_err!(
4344                Pallet::elect(
4345                    RuntimeOrigin::signed(ALICE),
4346                    payload.clone(),
4347                    signature.clone()
4348                ),
4349                DispatchError::BadOrigin
4350            );
4351
4352            assert_err!(
4353                Pallet::elect(RuntimeOrigin::root(), payload.clone(), signature.clone()),
4354                DispatchError::BadOrigin
4355            );
4356        })
4357    }
4358
4359    #[test]
4360    fn elect_authors_err_affidavit_author_not_found() {
4361        let mut env = new_ocw_env();
4362        env.ext.execute_with(|| {
4363            set_session_config();
4364            CurrentSession::put(1);
4365            let users = vec![ALICE, BOB, ALAN, CHARLIE, AMY, NIX, MIKE, LAYA, DAVE, JIM];
4366            set_default_users_balance_and_hold(users).unwrap();
4367
4368            let authors = vec![ALICE, BOB, ALAN, MIKE, LAYA, DAVE, JIM];
4369            enroll_authors_with_default_collateral(authors.clone()).unwrap();
4370            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
4371            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
4372            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
4373
4374            let alice_afdt_id = generate_affidavit_id();
4375            let alan_afdt_id = generate_affidavit_id();
4376            let bob_afdt_id = generate_affidavit_id();
4377            let mike_afdt_id = generate_affidavit_id();
4378            let laya_afdt_id = generate_affidavit_id();
4379            let dev_afdt_id = generate_affidavit_id();
4380            let jim_afdt_id = generate_affidavit_id();
4381
4382            System::set_block_number(150);
4383            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
4384            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
4385            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
4386            ext_validate(MIKE, mike_afdt_id.clone()).unwrap();
4387            ext_validate(LAYA, laya_afdt_id.clone()).unwrap();
4388            ext_validate(DAVE, dev_afdt_id.clone()).unwrap();
4389            ext_validate(JIM, jim_afdt_id.clone()).unwrap();
4390
4391            let alice_nxt_afdt_id = generate_affidavit_id();
4392            let alan_nxt_afdt_id = generate_affidavit_id();
4393            let bob_nxt_afdt_id = generate_affidavit_id();
4394            let jim_nxt_afdt_id = generate_affidavit_id();
4395            let mike_nxt_afdt_id = generate_affidavit_id();
4396            let dev_nxt_afdt_id = generate_affidavit_id();
4397            let laya_nxt_afdt_id = generate_affidavit_id();
4398
4399            let alice_payload = TestAfdtPayload {
4400                active_afdt_pub: alice_afdt_id.clone(),
4401                next_afdt_pub: alice_nxt_afdt_id.clone(),
4402            };
4403
4404            let alan_payload = TestAfdtPayload {
4405                active_afdt_pub: alan_afdt_id.clone(),
4406                next_afdt_pub: alan_nxt_afdt_id.clone(),
4407            };
4408
4409            let bob_payload = TestAfdtPayload {
4410                active_afdt_pub: bob_afdt_id.clone(),
4411                next_afdt_pub: bob_nxt_afdt_id.clone(),
4412            };
4413
4414            let mike_payload = TestAfdtPayload {
4415                active_afdt_pub: mike_afdt_id.clone(),
4416                next_afdt_pub: mike_nxt_afdt_id.clone(),
4417            };
4418
4419            let laya_payload = TestAfdtPayload {
4420                active_afdt_pub: laya_afdt_id.clone(),
4421                next_afdt_pub: laya_nxt_afdt_id.clone(),
4422            };
4423
4424            let jim_payload = TestAfdtPayload {
4425                active_afdt_pub: jim_afdt_id.clone(),
4426                next_afdt_pub: jim_nxt_afdt_id.clone(),
4427            };
4428
4429            let dev_payload = TestAfdtPayload {
4430                active_afdt_pub: dev_afdt_id.clone(),
4431                next_afdt_pub: dev_nxt_afdt_id.clone(),
4432            };
4433
4434            System::set_block_number(AFDT_SUBMISSION_START);
4435            ext_declare_affidavit(ALICE, alice_payload).unwrap();
4436            ext_declare_affidavit(ALAN, alan_payload).unwrap();
4437            ext_declare_affidavit(BOB, bob_payload).unwrap();
4438            ext_declare_affidavit(MIKE, mike_payload).unwrap();
4439            ext_declare_affidavit(LAYA, laya_payload).unwrap();
4440            ext_declare_affidavit(JIM, jim_payload).unwrap();
4441            ext_declare_affidavit(DAVE, dev_payload).unwrap();
4442
4443            let _public = get_public_key(alice_nxt_afdt_id).unwrap();
4444
4445            let dummy_public = generate_affidavit_keypair();
4446            let payload = ElectionPayloadOf {
4447                public: dummy_public.clone(),
4448            };
4449            let signature = sign_payload(&payload.encode(), dummy_public);
4450            set_block_author(ALICE);
4451            System::set_block_number(ELECTION_START);
4452
4453            assert_err!(
4454                Pallet::elect(RuntimeOrigin::none(), payload, signature),
4455                Error::AffidavitAuthorNotFound
4456            );
4457        })
4458    }
4459
4460    #[test]
4461    fn elect_authors_err_tried_electing_by_non_block_author() {
4462        let mut env = new_ocw_env();
4463        env.ext.execute_with(|| {
4464            set_session_config();
4465            CurrentSession::put(1);
4466            let users = vec![ALICE, BOB, ALAN, CHARLIE, AMY, NIX, MIKE, LAYA, DAVE, JIM];
4467            set_default_users_balance_and_hold(users).unwrap();
4468
4469            let authors = vec![ALICE, BOB, ALAN, MIKE, LAYA, DAVE, JIM];
4470            enroll_authors_with_default_collateral(authors.clone()).unwrap();
4471            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
4472            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
4473            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
4474
4475            let alice_afdt_id = generate_affidavit_id();
4476            let alan_afdt_id = generate_affidavit_id();
4477            let bob_afdt_id = generate_affidavit_id();
4478            let mike_afdt_id = generate_affidavit_id();
4479            let laya_afdt_id = generate_affidavit_id();
4480            let dev_afdt_id = generate_affidavit_id();
4481            let jim_afdt_id = generate_affidavit_id();
4482
4483            System::set_block_number(150);
4484            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
4485            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
4486            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
4487            ext_validate(MIKE, mike_afdt_id.clone()).unwrap();
4488            ext_validate(LAYA, laya_afdt_id.clone()).unwrap();
4489            ext_validate(DAVE, dev_afdt_id.clone()).unwrap();
4490            ext_validate(JIM, jim_afdt_id.clone()).unwrap();
4491
4492            let alice_nxt_afdt_id = generate_affidavit_id();
4493            let alan_nxt_afdt_id = generate_affidavit_id();
4494            let bob_nxt_afdt_id = generate_affidavit_id();
4495            let jim_nxt_afdt_id = generate_affidavit_id();
4496            let mike_nxt_afdt_id = generate_affidavit_id();
4497            let dev_nxt_afdt_id = generate_affidavit_id();
4498            let laya_nxt_afdt_id = generate_affidavit_id();
4499
4500            let alice_payload = TestAfdtPayload {
4501                active_afdt_pub: alice_afdt_id.clone(),
4502                next_afdt_pub: alice_nxt_afdt_id.clone(),
4503            };
4504
4505            let alan_payload = TestAfdtPayload {
4506                active_afdt_pub: alan_afdt_id.clone(),
4507                next_afdt_pub: alan_nxt_afdt_id.clone(),
4508            };
4509
4510            let bob_payload = TestAfdtPayload {
4511                active_afdt_pub: bob_afdt_id.clone(),
4512                next_afdt_pub: bob_nxt_afdt_id.clone(),
4513            };
4514
4515            let mike_payload = TestAfdtPayload {
4516                active_afdt_pub: mike_afdt_id.clone(),
4517                next_afdt_pub: mike_nxt_afdt_id.clone(),
4518            };
4519
4520            let laya_payload = TestAfdtPayload {
4521                active_afdt_pub: laya_afdt_id.clone(),
4522                next_afdt_pub: laya_nxt_afdt_id.clone(),
4523            };
4524
4525            let jim_payload = TestAfdtPayload {
4526                active_afdt_pub: jim_afdt_id.clone(),
4527                next_afdt_pub: jim_nxt_afdt_id.clone(),
4528            };
4529
4530            let dev_payload = TestAfdtPayload {
4531                active_afdt_pub: dev_afdt_id.clone(),
4532                next_afdt_pub: dev_nxt_afdt_id.clone(),
4533            };
4534
4535            System::set_block_number(AFDT_SUBMISSION_START);
4536            ext_declare_affidavit(ALICE, alice_payload).unwrap();
4537            ext_declare_affidavit(ALAN, alan_payload).unwrap();
4538            ext_declare_affidavit(BOB, bob_payload).unwrap();
4539            ext_declare_affidavit(MIKE, mike_payload).unwrap();
4540            ext_declare_affidavit(LAYA, laya_payload).unwrap();
4541            ext_declare_affidavit(JIM, jim_payload).unwrap();
4542            ext_declare_affidavit(DAVE, dev_payload).unwrap();
4543
4544            let public = get_public_key(alice_nxt_afdt_id).unwrap();
4545
4546            let payload = ElectionPayloadOf {
4547                public: public.clone(),
4548            };
4549            let signature = sign_payload(&payload.encode(), public);
4550            set_block_author(BOB);
4551            System::set_block_number(ELECTION_START);
4552            assert_err!(
4553                Pallet::elect(RuntimeOrigin::none(), payload, signature),
4554                Error::TriedElectingByNonBlockAuthor
4555            );
4556        })
4557    }
4558
4559    #[cfg(feature = "dev")]
4560    #[test]
4561    fn inspect_elects_success() {
4562        let mut env = new_ocw_env();
4563        env.ext.execute_with(||{
4564            set_session_config();
4565            CurrentSession::put(1);
4566            let users = vec![ALICE, BOB, ALAN, CHARLIE, AMY, NIX, MIKE, LAYA, DAVE, JIM];
4567            set_default_users_balance_and_hold(users).unwrap();
4568
4569            let authors = vec![ALICE, BOB, ALAN, MIKE, LAYA, DAVE, JIM];
4570            enroll_authors_with_default_collateral(authors.clone()).unwrap();
4571            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
4572            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
4573            direct_fund_author(CHARLIE, ALAN, LARGE_FUND).unwrap();
4574
4575            let alice_afdt_id = generate_affidavit_id();
4576            let alan_afdt_id = generate_affidavit_id();
4577            let bob_afdt_id = generate_affidavit_id();
4578            let mike_afdt_id = generate_affidavit_id();
4579            let laya_afdt_id = generate_affidavit_id();
4580            let dev_afdt_id = generate_affidavit_id();
4581            let jim_afdt_id = generate_affidavit_id();
4582
4583            System::set_block_number(150);
4584            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
4585            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
4586            ext_validate(ALAN, alan_afdt_id.clone()).unwrap();
4587            ext_validate(MIKE, mike_afdt_id.clone()).unwrap();
4588            ext_validate(LAYA, laya_afdt_id.clone()).unwrap();
4589            ext_validate(DAVE, dev_afdt_id.clone()).unwrap();
4590            ext_validate(JIM, jim_afdt_id.clone()).unwrap();
4591
4592            let alice_nxt_afdt_id = generate_affidavit_id();
4593            let alan_nxt_afdt_id = generate_affidavit_id();
4594            let bob_nxt_afdt_id = generate_affidavit_id();
4595            let jim_nxt_afdt_id = generate_affidavit_id();
4596            let mike_nxt_afdt_id = generate_affidavit_id();
4597            let dev_nxt_afdt_id = generate_affidavit_id();
4598            let laya_nxt_afdt_id = generate_affidavit_id();
4599
4600            let alice_payload = TestAfdtPayload {
4601                active_afdt_pub: alice_afdt_id.clone(),
4602                next_afdt_pub: alice_nxt_afdt_id.clone(),
4603            };
4604
4605            let alan_payload = TestAfdtPayload {
4606                active_afdt_pub: alan_afdt_id.clone(),
4607                next_afdt_pub: alan_nxt_afdt_id.clone(),
4608            };
4609
4610            let bob_payload = TestAfdtPayload {
4611                active_afdt_pub: bob_afdt_id.clone(),
4612                next_afdt_pub: bob_nxt_afdt_id.clone(),
4613            };
4614
4615            let mike_payload = TestAfdtPayload {
4616                active_afdt_pub: mike_afdt_id.clone(),
4617                next_afdt_pub: mike_nxt_afdt_id.clone(),
4618            };
4619
4620            let laya_payload = TestAfdtPayload {
4621                active_afdt_pub: laya_afdt_id.clone(),
4622                next_afdt_pub: laya_nxt_afdt_id.clone(),
4623            };
4624
4625            let jim_payload = TestAfdtPayload {
4626                active_afdt_pub: jim_afdt_id.clone(),
4627                next_afdt_pub: jim_nxt_afdt_id.clone(),
4628            };
4629
4630            let dev_payload = TestAfdtPayload {
4631                active_afdt_pub: dev_afdt_id.clone(),
4632                next_afdt_pub: dev_nxt_afdt_id.clone(),
4633            };
4634
4635            System::set_block_number(AFDT_SUBMISSION_START);
4636            ext_declare_affidavit(ALICE, alice_payload).unwrap();
4637            ext_declare_affidavit(ALAN, alan_payload).unwrap();
4638            ext_declare_affidavit(BOB, bob_payload).unwrap();
4639            ext_declare_affidavit(MIKE, mike_payload).unwrap();
4640            ext_declare_affidavit(LAYA, laya_payload).unwrap();
4641            ext_declare_affidavit(JIM, jim_payload).unwrap();
4642            ext_declare_affidavit(DAVE, dev_payload).unwrap();
4643
4644            let public = get_public_key(alice_nxt_afdt_id).unwrap();
4645            let payload = ElectionPayloadOf {
4646                public: public.clone(),
4647            };
4648            let signature = sign_payload(&payload.encode(), public);
4649            set_block_author(ALICE);
4650            System::set_block_number(ELECTION_START);
4651            assert_ok!(Pallet::elect(RuntimeOrigin::none(), payload, signature));
4652            assert_ok!(Pallet::inspect_elects(RuntimeOrigin::signed(AMY)));
4653            let expected_elects = vec![BOB, MIKE, LAYA, DAVE, ALICE, ALAN];
4654            System::assert_last_event(
4655                Event::InspectElects { 
4656                    elects: expected_elects 
4657                }
4658                .into()
4659            );
4660        })
4661    }
4662
4663    #[cfg(feature = "dev")]
4664    #[test]
4665    fn prepare_validation_payload_success() {
4666        let mut env = new_ocw_env();
4667        env.ext.execute_with(|| {
4668            System::set_block_number(10);
4669            Pallet::offchain_worker(10);
4670            assert_err!(
4671                Pallet::prepare_validation_payload(RuntimeOrigin::signed(ALICE)),
4672                Error::ActiveAfdtKeyNotYetFinalized
4673            );
4674
4675            while System::block_number() < 30 {
4676                ocw_step();
4677            }
4678
4679            assert_ok!(Pallet::prepare_validation_payload(RuntimeOrigin::signed(ALICE))); 
4680        })
4681    }
4682
4683    #[cfg(feature = "dev")]
4684    #[test]
4685    fn inspect_affidavit_success() {
4686        let mut env = new_ocw_env();
4687        env.ext.execute_with(|| {
4688            set_session_config();
4689            CurrentSession::put(1);
4690            let users = vec![ALICE, BOB, ALAN, AMY, JIM, NIX, LAYA];
4691            set_default_users_balance_and_hold(users).unwrap();
4692
4693            let authors = vec![ALICE, BOB];
4694            enroll_authors_with_default_collateral(authors.clone()).unwrap();
4695            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
4696            direct_fund_author(AMY, BOB, SMALL_FUND).unwrap();
4697            direct_fund_author(ALAN, BOB, LARGE_FUND).unwrap();
4698            direct_fund_author(JIM, ALICE, SMALL_FUND).unwrap();
4699            direct_fund_author(LAYA, ALICE, STANDARD_FUND).unwrap();
4700
4701            let alice_afdt_id = generate_affidavit_id();
4702            let bob_afdt_id = generate_affidavit_id();
4703
4704            System::set_block_number(150);
4705            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
4706            ext_validate(BOB, bob_afdt_id.clone()).unwrap();
4707
4708            let alice_nxt_afdt_id = generate_affidavit_id();
4709            let bob_nxt_afdt_id = generate_affidavit_id();
4710
4711
4712            let alice_payload = TestAfdtPayload {
4713                active_afdt_pub: alice_afdt_id.clone(),
4714                next_afdt_pub: alice_nxt_afdt_id.clone(),
4715            };
4716
4717            let bob_payload = TestAfdtPayload {
4718                active_afdt_pub: bob_afdt_id.clone(),
4719                next_afdt_pub: bob_nxt_afdt_id.clone(),
4720            };
4721
4722            System::set_block_number(AFDT_SUBMISSION_START);
4723            ext_declare_affidavit(ALICE, alice_payload).unwrap();
4724            ext_declare_affidavit(BOB, bob_payload).unwrap();
4725
4726            assert_ok!(Pallet::inspect_affidavit(RuntimeOrigin::signed(ALICE), alice_afdt_id.clone()));
4727            let expected_affidavit = vec![(Funder::Direct(LAYA), STANDARD_FUND), (Funder::Direct(NIX), STANDARD_FUND), (Funder::Direct(JIM), SMALL_FUND)];
4728            System::assert_last_event(
4729                Event::InspectAffidavit { 
4730                    author: ALICE, 
4731                    afdt_id: alice_afdt_id, 
4732                    session: 2, 
4733                    affidavit: expected_affidavit 
4734                }
4735                .into()
4736            );
4737        })
4738    }
4739
4740    
4741    #[test]
4742    fn force_genesis_config_err_bad_origin() {
4743        chain_manager_test_ext().execute_with(|| {
4744            assert_noop!(
4745                ChainManager::force_genesis_config(
4746                    RuntimeOrigin::signed(ALICE),
4747                    ForceGenesisConfig::AllowAffidavits(true)
4748                ),
4749                sp_runtime::DispatchError::BadOrigin
4750            );
4751        });
4752    }
4753
4754    #[test]
4755    fn force_allow_affidavits_success() {
4756        chain_manager_test_ext().execute_with(|| {
4757            assert_eq!(AllowAffidavits::get(), false);
4758            assert_ok!(ChainManager::force_genesis_config(
4759                RuntimeOrigin::root(),
4760                ForceGenesisConfig::AllowAffidavits(true)
4761            ));
4762            assert_eq!(AllowAffidavits::get(), true);
4763        });
4764    }
4765
4766    #[test]
4767    fn force_affidavit_begins_at_err_invalid_affidavit_period() {
4768        chain_manager_test_ext().execute_with(|| {
4769            AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
4770            assert_noop!(
4771                ChainManager::force_genesis_config(
4772                    RuntimeOrigin::root(),
4773                    ForceGenesisConfig::AffidavitBeginsAt(Duration::from_rational(8u32, 10u32))
4774                ),
4775                Error::InvalidAffidavitPeriod
4776            );
4777        });
4778    }
4779
4780    #[test]
4781    fn force_affidavit_ends_at_err_invalid_affidavit_period() {
4782        chain_manager_test_ext().execute_with(|| {
4783            AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
4784            assert_noop!(
4785                ChainManager::force_genesis_config(
4786                    RuntimeOrigin::root(),
4787                    ForceGenesisConfig::AffidavitEndsAt(Duration::from_rational(2u32, 10u32))
4788                ),
4789                Error::InvalidAffidavitPeriod
4790            );
4791        });
4792    }
4793
4794    #[test]
4795    fn force_affidavit_period_updates_correctly() {
4796        chain_manager_test_ext().execute_with(|| {
4797            assert_eq!(
4798                AffidavitBeginsAt::get(),
4799                Duration::from_rational(2u32, 10u32)
4800            );
4801            assert_eq!(AffidavitEndsAt::get(), Duration::from_rational(8u32, 10u32));
4802            assert_eq!(
4803                ElectionBeginsAt::get(),
4804                Duration::from_rational(5u32, 10u32)
4805            );
4806
4807            let begin = Duration::from_rational(3u32, 10u32);
4808            let end = Duration::from_rational(7u32, 10u32);
4809            let election = Duration::from_rational(4u32, 10u32);
4810
4811            assert_ok!(ChainManager::force_genesis_config(
4812                RuntimeOrigin::root(),
4813                ForceGenesisConfig::AffidavitBeginsAt(begin)
4814            ));
4815
4816            assert_ok!(ChainManager::force_genesis_config(
4817                RuntimeOrigin::root(),
4818                ForceGenesisConfig::AffidavitEndsAt(end)
4819            ));
4820
4821            assert_ok!(ChainManager::force_genesis_config(
4822                RuntimeOrigin::root(),
4823                ForceGenesisConfig::ElectionBeginsAt(election)
4824            ));
4825
4826            assert_eq!(AffidavitBeginsAt::get(), begin);
4827            assert_eq!(AffidavitEndsAt::get(), end);
4828            assert_eq!(ElectionBeginsAt::get(), election);
4829        });
4830    }
4831
4832    #[test]
4833    fn force_election_runner_points_success() {
4834        chain_manager_test_ext().execute_with(|| {
4835            assert_eq!(ElectionRunnerPointsUpgrade::get(), None);
4836            assert_ok!(ChainManager::force_genesis_config(
4837                RuntimeOrigin::root(),
4838                ForceGenesisConfig::ElectionRunnerPointsUpgrade(Some(10))
4839            ));
4840            assert_eq!(ElectionRunnerPointsUpgrade::get(), Some(10));
4841
4842            assert_ok!(ChainManager::force_genesis_config(
4843                RuntimeOrigin::root(),
4844                ForceGenesisConfig::ElectionRunnerPointsUpgrade(None)
4845            ));
4846            assert_eq!(ElectionRunnerPointsUpgrade::get(), None);
4847        })
4848    }
4849
4850    #[test]
4851    fn force_validate_tx_priority_success() {
4852        chain_manager_test_ext().execute_with(|| {
4853            assert_eq!(ValidateTxPriority::get(), 1_000_000);
4854            assert_ok!(ChainManager::force_genesis_config(
4855                RuntimeOrigin::root(),
4856                ForceGenesisConfig::ValidateTxPriority(1)
4857            ));
4858            assert_eq!(ValidateTxPriority::get(), 1);
4859        })
4860    }
4861
4862    #[test]
4863    fn force_validate_tx_priority_err_value_cannot_be_zero() {
4864        chain_manager_test_ext().execute_with(|| {
4865            assert_eq!(ValidateTxPriority::get(), 1_000_000);
4866            assert_noop!(
4867                ChainManager::force_genesis_config(
4868                    RuntimeOrigin::root(),
4869                    ForceGenesisConfig::ValidateTxPriority(0)
4870                ),
4871                Error::ValueCannotBeZero
4872            );
4873        })
4874    }
4875
4876    #[test]
4877    fn force_affidavit_tx_priority_success() {
4878        chain_manager_test_ext().execute_with(|| {
4879            assert_eq!(AffidavitTxPriority::get(), 850_000);
4880            assert_ok!(ChainManager::force_genesis_config(
4881                RuntimeOrigin::root(),
4882                ForceGenesisConfig::AffidavitTxPriority(2)
4883            ));
4884            assert_eq!(AffidavitTxPriority::get(), 2);
4885        })
4886    }
4887
4888    #[test]
4889    fn force_affidavit_tx_priority_err_value_cannot_be_zero() {
4890        chain_manager_test_ext().execute_with(|| {
4891            assert_eq!(AffidavitTxPriority::get(), 850_000);
4892            assert_noop!(
4893                ChainManager::force_genesis_config(
4894                    RuntimeOrigin::root(),
4895                    ForceGenesisConfig::AffidavitTxPriority(0)
4896                ),
4897                Error::ValueCannotBeZero
4898            );
4899        })
4900    }
4901
4902    #[test]
4903    fn force_election_tx_priority_success() {
4904        chain_manager_test_ext().execute_with(|| {
4905            assert_eq!(ElectionTxPriority::get(), 700_000);
4906            assert_ok!(ChainManager::force_genesis_config(
4907                RuntimeOrigin::root(),
4908                ForceGenesisConfig::ElectionTxPriority(3)
4909            ));
4910            assert_eq!(ElectionTxPriority::get(), 3);
4911        })
4912    }
4913
4914    #[test]
4915    fn force_election_tx_priority_err_value_cannot_be_zero() {
4916        chain_manager_test_ext().execute_with(|| {
4917            assert_eq!(ElectionTxPriority::get(), 700_000);
4918            assert_noop!(
4919                ChainManager::force_genesis_config(
4920                    RuntimeOrigin::root(),
4921                    ForceGenesisConfig::ElectionTxPriority(0)
4922                ),
4923                Error::ValueCannotBeZero
4924            );
4925        })
4926    }
4927
4928    #[test]
4929    fn force_finality_after_success() {
4930        chain_manager_test_ext().execute_with(|| {
4931            assert_eq!(FinalityAfter::get(), 60_000);
4932            assert_ok!(ChainManager::force_genesis_config(
4933                RuntimeOrigin::root(),
4934                ForceGenesisConfig::FinalityAfter(90_000)
4935            ));
4936            assert_eq!(FinalityAfter::get(), 90_000);
4937        })
4938    }
4939
4940    #[test]
4941    fn force_finality_after_err_value_cannot_be_zero() {
4942        chain_manager_test_ext().execute_with(|| {
4943            assert_eq!(FinalityAfter::get(), 60_000);
4944            assert_noop!(
4945                ChainManager::force_genesis_config(
4946                    RuntimeOrigin::root(),
4947                    ForceGenesisConfig::FinalityAfter(0)
4948                ),
4949                Error::ValueCannotBeZero
4950            );
4951        })
4952    }
4953
4954    #[test]
4955    fn force_finality_ticks_success() {
4956        chain_manager_test_ext().execute_with(|| {
4957            assert_eq!(FinalityTicks::get(), 5);
4958            assert_ok!(ChainManager::force_genesis_config(
4959                RuntimeOrigin::root(),
4960                ForceGenesisConfig::FinalityTicks(3)
4961            ));
4962            assert_eq!(FinalityTicks::get(), 3);
4963        })
4964    }
4965
4966    #[test]
4967    fn force_finality_ticks_err_value_cannot_be_zero() {
4968        chain_manager_test_ext().execute_with(|| {
4969            assert_eq!(FinalityTicks::get(), 5);
4970            assert_noop!(
4971                ChainManager::force_genesis_config(
4972                    RuntimeOrigin::root(),
4973                    ForceGenesisConfig::FinalityTicks(0)
4974                ),
4975                Error::ValueCannotBeZero
4976            );
4977        })
4978    }
4979
4980    // ===============================================================================
4981    // ````````````````````````````````` PUBLIC APIS `````````````````````````````````
4982    // ===============================================================================
4983
4984    #[test]
4985    fn is_validating_success() {
4986        chain_manager_test_ext().execute_with(|| {
4987            set_default_user_balance_and_hold(ALICE).unwrap();
4988            set_default_user_balance_and_hold(BOB).unwrap();
4989            set_default_user_balance_and_hold(ALAN).unwrap();
4990            enroll_authors_with_default_collateral(vec![ALICE, BOB, ALAN]).unwrap();
4991
4992            let alice_id = Pallet::convert(ALICE).unwrap();
4993            let bob_id = Pallet::convert(BOB).unwrap();
4994
4995            Validators::put(vec![bob_id, alice_id]);
4996
4997            assert!(Pallet::is_validating(ALICE));
4998            assert!(Pallet::is_validating(BOB));
4999            assert!(!Pallet::is_validating(ALAN));
5000        })
5001    }
5002
5003    #[test]
5004    fn get_runtime_afdt_key_success() {
5005        let mut env = new_ocw_env();
5006        env.ext.execute_with(|| {
5007            set_default_user_balance_and_hold(ALICE).unwrap();
5008            set_default_user_balance_and_hold(BOB).unwrap();
5009            set_default_user_balance_and_hold(ALAN).unwrap();
5010            enroll_authors_with_default_collateral(vec![ALICE, BOB, ALAN]).unwrap();
5011
5012            let alice_afdt_id = generate_affidavit_id();
5013            let alan_afdt_id = generate_affidavit_id();
5014
5015            CurrentSession::put(1);
5016            let next_session = CurrentSession::get() + 1;
5017            let next_afdt_session = next_session + 1;
5018
5019            AffidavitKeys::insert((next_session, alice_afdt_id.clone()), ALICE);
5020            AffidavitKeys::insert((next_afdt_session, alan_afdt_id.clone()), ALAN);
5021
5022            let alice_active_afdt_key = Pallet::get_runtime_afdt_key(ALICE);
5023            let bob_active_afdt_key = Pallet::get_runtime_afdt_key(BOB);
5024            let alan_active_afdt_key = Pallet::get_runtime_afdt_key(ALAN);
5025
5026            assert_ok!(alan_active_afdt_key.clone());
5027            assert_ok!(alice_active_afdt_key. clone());
5028            assert_err!(bob_active_afdt_key, Error::AffidavitKeyPairNotFound);
5029
5030            assert_eq!(
5031                alan_active_afdt_key.unwrap(),
5032                (next_afdt_session, alan_afdt_id)
5033            );
5034            assert_eq!(
5035                alice_active_afdt_key.unwrap(),
5036                (next_session, alice_afdt_id)
5037            );
5038        })
5039    }
5040
5041    #[test]
5042    fn is_chilling_success() {
5043        let mut env = new_ocw_env();
5044        env.ext.execute_with(|| {
5045            set_default_user_balance_and_hold(ALICE).unwrap();
5046            set_default_user_balance_and_hold(BOB).unwrap();
5047            set_default_user_balance_and_hold(ALAN).unwrap();
5048            enroll_authors_with_default_collateral(vec![ALICE, BOB, ALAN]).unwrap();
5049
5050            let alice_afdt_id = generate_affidavit_id();
5051            let alan_afdt_id = generate_affidavit_id();
5052
5053            CurrentSession::put(1);
5054            let next_session = CurrentSession::get() + 1;
5055            let next_afdt_session = next_session + 1;
5056
5057            AffidavitKeys::insert((next_session, alice_afdt_id), ALICE);
5058            AffidavitKeys::insert((next_afdt_session, alan_afdt_id), ALAN);
5059
5060            assert!(!Pallet::is_chilling(ALICE));
5061            assert!(!Pallet::is_chilling(ALAN));
5062            assert!(Pallet::is_chilling(BOB));
5063        })
5064    }
5065
5066    #[test]
5067    fn get_finalized_afdt_key_success() {
5068        let mut env = new_ocw_env();
5069        env.ext.execute_with(|| {
5070            System::set_block_number(10);
5071            Pallet::offchain_worker(10);
5072            let active_afdt = Pallet::get_finalized_afdt_key();
5073            assert_err!(active_afdt, Error::ActiveAfdtKeyNotYetFinalized);
5074
5075            while System::block_number() < 30 {
5076                ocw_step();
5077            }
5078
5079            let active_afdt = Pallet::get_finalized_afdt_key();
5080            assert_ok!(active_afdt);
5081        })
5082    }
5083
5084    #[test]
5085    fn sign_validate_payload_success() {
5086        let mut env = new_ocw_env();
5087        env.ext.execute_with(|| {
5088            System::set_block_number(10);
5089            Pallet::offchain_worker(10);
5090            let sign_payload = Pallet::sign_validate_payload();
5091            assert_err!(sign_payload, Error::ActiveAfdtKeyNotYetFinalized);
5092
5093            while System::block_number() < 30 {
5094                ocw_step();
5095            }
5096
5097            let sign_payload = Pallet::sign_validate_payload();
5098            assert_ok!(sign_payload);
5099        })
5100    }
5101
5102    #[test]
5103    fn get_public_key_sucess() {
5104        let mut env = new_ocw_env();
5105        env.ext.execute_with(|| {
5106            System::set_block_number(10);
5107            Pallet::offchain_worker(10);
5108            while System::block_number() < 30 {
5109                ocw_step();
5110            }
5111
5112            let afdt_key = Pallet::get_finalized_afdt_key().unwrap();
5113            let pub_key = Pallet::get_public_key(afdt_key);
5114            assert_ok!(pub_key);
5115        })
5116    }
5117
5118    #[test]
5119    fn get_elects_success() {
5120        chain_manager_test_ext().execute_with(|| {
5121            let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
5122            set_default_users_balance_and_hold(users).unwrap();
5123
5124            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
5125            RoleAdapter::enroll(&BOB, 200, Fortitude::Force).unwrap();
5126            RoleAdapter::enroll(&MIKE, 200, Fortitude::Force).unwrap();
5127
5128            RoleAdapter::fund(
5129                &ALICE,
5130                &Funder::Direct(CHARLIE),
5131                100,
5132                Precision::Exact,
5133                Fortitude::Force,
5134            )
5135            .unwrap();
5136            RoleAdapter::fund(
5137                &BOB,
5138                &Funder::Direct(ALAN),
5139                150,
5140                Precision::Exact,
5141                Fortitude::Force,
5142            )
5143            .unwrap();
5144            RoleAdapter::fund(
5145                &MIKE,
5146                &Funder::Direct(NIX),
5147                125,
5148                Precision::Exact,
5149                Fortitude::Force,
5150            )
5151            .unwrap();
5152
5153            System::set_block_number(10);
5154            SessionStartsAt::put(15);
5155            AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
5156            AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
5157            ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
5158
5159            System::set_block_number(15);
5160            System::set_block_number(135);
5161            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
5162            AffidavitKeys::insert((1, AFFIDAVIT_KEY_B), BOB);
5163            AffidavitKeys::insert((1, AFFIDAVIT_KEY_C), MIKE);
5164
5165            let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
5166            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
5167            let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
5168            Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
5169            let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
5170            Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
5171
5172            System::set_block_number(315);
5173            assert_ok!(Internals::prepare_election(&Some(ALICE)));
5174
5175            let actual_elects = Pallet::get_elects().unwrap();
5176            let expected_reveal = vec![BOB, MIKE, ALICE];
5177            assert_eq!(actual_elects, expected_reveal);
5178        })
5179    }
5180
5181    #[test]
5182    fn get_elects_returns_err_unable_to_reveal_elected() {
5183        chain_manager_test_ext().execute_with(|| {
5184            let users = vec![ALICE, CHARLIE, ALAN, MIKE, BOB, NIX];
5185            set_default_users_balance_and_hold(users).unwrap();
5186
5187            RoleAdapter::enroll(&ALICE, 200, Fortitude::Force).unwrap();
5188            RoleAdapter::enroll(&BOB, 200, Fortitude::Force).unwrap();
5189            RoleAdapter::enroll(&MIKE, 200, Fortitude::Force).unwrap();
5190
5191            RoleAdapter::fund(
5192                &ALICE,
5193                &Funder::Direct(CHARLIE),
5194                100,
5195                Precision::Exact,
5196                Fortitude::Force,
5197            )
5198            .unwrap();
5199            RoleAdapter::fund(
5200                &BOB,
5201                &Funder::Direct(ALAN),
5202                150,
5203                Precision::Exact,
5204                Fortitude::Force,
5205            )
5206            .unwrap();
5207            RoleAdapter::fund(
5208                &MIKE,
5209                &Funder::Direct(NIX),
5210                125,
5211                Precision::Exact,
5212                Fortitude::Force,
5213            )
5214            .unwrap();
5215
5216            System::set_block_number(10);
5217            SessionStartsAt::put(15);
5218            AffidavitBeginsAt::put(Duration::from_rational(2u32, 10u32));
5219            AffidavitEndsAt::put(Duration::from_rational(8u32, 10u32));
5220            ElectionBeginsAt::put(Duration::from_rational(5u32, 10u32));
5221
5222            System::set_block_number(15);
5223            System::set_block_number(135);
5224            AffidavitKeys::insert((1, AFFIDAVIT_KEY_A), ALICE);
5225            AffidavitKeys::insert((1, AFFIDAVIT_KEY_B), BOB);
5226            AffidavitKeys::insert((1, AFFIDAVIT_KEY_C), MIKE);
5227
5228            let affidavit_alice_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_A).unwrap();
5229            Pallet::submit_affidavit(&AFFIDAVIT_KEY_A, &affidavit_alice_id).unwrap();
5230            let affidavit_bob_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_B).unwrap();
5231            Pallet::submit_affidavit(&AFFIDAVIT_KEY_B, &affidavit_bob_id).unwrap();
5232            let affidavit_mike_id = Pallet::gen_affidavit(&AFFIDAVIT_KEY_C).unwrap();
5233            Pallet::submit_affidavit(&AFFIDAVIT_KEY_C, &affidavit_mike_id).unwrap();
5234
5235            let elects = Pallet::get_elects();
5236            assert_err!(elects, Error::UnableToRevealElected);
5237        })
5238    }
5239
5240    #[test]
5241    fn fetch_affidavit_success() {
5242        let mut env = new_ocw_env();
5243        env.ext.execute_with(||{
5244            set_session_config();
5245            CurrentSession::put(1);
5246            let users = vec![ALICE, NIX, AMY, CHARLIE];
5247            set_default_users_balance_and_hold(users).unwrap();
5248
5249            enroll_author_with_default_collateral(ALICE).unwrap();
5250            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
5251            direct_fund_author(AMY, ALICE, SMALL_FUND).unwrap();
5252            direct_fund_author(CHARLIE, ALICE, LARGE_FUND).unwrap();
5253
5254            let alice_afdt_id = generate_affidavit_id();
5255
5256            System::set_block_number(150);
5257            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
5258
5259            let alice_nxt_afdt_id = generate_affidavit_id();
5260
5261            let alice_payload = TestAfdtPayload {
5262                active_afdt_pub: alice_afdt_id.clone(),
5263                next_afdt_pub: alice_nxt_afdt_id.clone(),
5264            };
5265
5266            System::set_block_number(AFDT_SUBMISSION_START);
5267            ext_declare_affidavit(ALICE, alice_payload).unwrap(); 
5268
5269            let actual_affidavit = Pallet::fetch_affidavit(alice_afdt_id.clone()).unwrap();
5270            let expected_affidavit = vec![(Funder::Direct(NIX), STANDARD_FUND), (Funder::Direct(CHARLIE), LARGE_FUND), (Funder::Direct(AMY), SMALL_FUND)];
5271            assert_eq!(actual_affidavit, expected_affidavit);
5272
5273            assert!(Pallet::fetch_affidavit(alice_nxt_afdt_id).is_err());
5274        })      
5275    }
5276
5277    #[test]
5278    fn fetch_affidavit_for_success() {
5279        let mut env = new_ocw_env();
5280        env.ext.execute_with(||{
5281            set_session_config();
5282            CurrentSession::put(1);
5283            let users = vec![ALICE, NIX, AMY, CHARLIE];
5284            set_default_users_balance_and_hold(users).unwrap();
5285
5286            enroll_author_with_default_collateral(ALICE).unwrap();
5287            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
5288            direct_fund_author(AMY, ALICE, SMALL_FUND).unwrap();
5289            direct_fund_author(CHARLIE, ALICE, LARGE_FUND).unwrap();
5290
5291            let alice_afdt_id = generate_affidavit_id();
5292
5293            System::set_block_number(150);
5294            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
5295
5296            let alice_nxt_afdt_id = generate_affidavit_id();
5297
5298            let alice_payload = TestAfdtPayload {
5299                active_afdt_pub: alice_afdt_id.clone(),
5300                next_afdt_pub: alice_nxt_afdt_id.clone(),
5301            };
5302
5303            System::set_block_number(AFDT_SUBMISSION_START);
5304            ext_declare_affidavit(ALICE, alice_payload).unwrap(); 
5305
5306            let for_session = CurrentSession::get() + 1;
5307            let actual_affidavit = Pallet::fetch_affidavit_for(alice_afdt_id.clone(), for_session).unwrap();
5308            let expected_affidavit = vec![(Funder::Direct(NIX), STANDARD_FUND), (Funder::Direct(CHARLIE), LARGE_FUND), (Funder::Direct(AMY), SMALL_FUND)];
5309            assert_eq!(actual_affidavit, expected_affidavit);
5310
5311            let for_session = CurrentSession::get() + 2;
5312            assert_err!(Pallet::fetch_affidavit_for(alice_afdt_id, for_session), Error::AffidavitKeyPairNotFound);
5313        })      
5314    }
5315    
5316    #[test]
5317    fn is_contesting_success() {
5318        let mut env = new_ocw_env();
5319        env.ext.execute_with(||{
5320            set_session_config();
5321            CurrentSession::put(1);
5322            let users = vec![ALICE, NIX];
5323            set_default_users_balance_and_hold(users).unwrap();
5324
5325            enroll_author_with_default_collateral(ALICE).unwrap();
5326            direct_fund_author(NIX, ALICE, STANDARD_FUND).unwrap();
5327
5328            let alice_afdt_id = generate_affidavit_id();
5329
5330            System::set_block_number(150);
5331            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
5332
5333            let alice_nxt_afdt_id = generate_affidavit_id();
5334
5335            let alice_payload = TestAfdtPayload {
5336                active_afdt_pub: alice_afdt_id.clone(),
5337                next_afdt_pub: alice_nxt_afdt_id.clone(),
5338            };
5339
5340            System::set_block_number(AFDT_SUBMISSION_START);
5341            ext_declare_affidavit(ALICE, alice_payload).unwrap(); 
5342
5343            assert!(Pallet::is_contesting(ALICE));           
5344            assert!(!Pallet::is_contesting(NIX));  
5345        })
5346    }
5347
5348    #[test]
5349    fn is_pursuing_success() {
5350        let mut env = new_ocw_env();
5351        env.ext.execute_with(|| {
5352            set_default_user_balance_and_hold(ALICE).unwrap();
5353            set_default_user_balance_and_hold(BOB).unwrap();
5354            set_default_user_balance_and_hold(ALAN).unwrap();
5355            enroll_authors_with_default_collateral(vec![ALICE, BOB, ALAN]).unwrap();
5356
5357            let alice_afdt_id = generate_affidavit_id();
5358            let alan_afdt_id = generate_affidavit_id();
5359
5360            CurrentSession::put(1);
5361            let next_session = CurrentSession::get() + 1;
5362            let next_afdt_session = next_session + 1;
5363
5364            AffidavitKeys::insert((next_session, alice_afdt_id), ALICE);
5365            AffidavitKeys::insert((next_afdt_session, alan_afdt_id), ALAN);
5366
5367            assert!(Pallet::is_pursuing(ALICE));
5368            assert!(Pallet::is_pursuing(ALAN));
5369            assert!(!Pallet::is_pursuing(BOB));
5370        })
5371    }
5372
5373    #[test]
5374    fn can_declare_success() {
5375        let mut env = new_ocw_env();
5376        env.ext.execute_with(||{
5377            set_session_config();
5378            CurrentSession::put(1);
5379            set_default_user_balance_and_hold(ALICE).unwrap();
5380            enroll_author_with_default_collateral(ALICE).unwrap();
5381            let alice_afdt_id = generate_affidavit_id();
5382
5383            System::set_block_number(150);
5384            ext_validate(ALICE, alice_afdt_id.clone()).unwrap();
5385
5386            assert_ok!(Pallet::can_declare(alice_afdt_id));            
5387        })
5388    }
5389
5390    #[test]
5391    fn can_declare_err_affidavit_author_not_found() {
5392        let mut env = new_ocw_env();
5393        env.ext.execute_with(||{
5394            set_session_config();
5395            CurrentSession::put(1);
5396            set_default_user_balance_and_hold(ALICE).unwrap();
5397            enroll_author_with_default_collateral(ALICE).unwrap();
5398            let alice_afdt_id = generate_affidavit_id();
5399
5400            assert_err!(Pallet::can_declare(alice_afdt_id), Error::AffidavitAuthorNotFound);            
5401        })
5402    }
5403
5404    #[test]
5405    fn can_elect_success() {
5406        let mut env = new_ocw_env();
5407        env.ext.execute_with(||{
5408            set_session_config();
5409            CurrentSession::put(1);
5410            set_default_user_balance_and_hold(ALICE).unwrap();
5411            enroll_author_with_default_collateral(ALICE).unwrap();
5412
5413            System::set_block_number(450);
5414            set_block_author(ALICE);
5415            assert_ok!(Pallet::can_elect(ALICE));            
5416        })        
5417    }
5418
5419    #[test]
5420    fn can_elect_err_not_a_block_author() {
5421        let mut env = new_ocw_env();
5422        env.ext.execute_with(||{
5423            set_session_config();
5424            CurrentSession::put(1);
5425            set_default_user_balance_and_hold(BOB).unwrap();
5426            enroll_author_with_default_collateral(BOB).unwrap();
5427            set_default_user_balance_and_hold(ALICE).unwrap();
5428            enroll_author_with_default_collateral(ALICE).unwrap();
5429
5430            System::set_block_number(450);
5431            set_block_author(ALICE);
5432            assert_err!(Pallet::can_elect(BOB), Error::NotABlockAuthor);            
5433        })        
5434    }
5435}