pallet_chain_manager/routines.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// ````````````````````````````````` OCW ROUTINES ````````````````````````````````
14// ===============================================================================
15
16//! Offchain routines orchestrating the affidavit lifecycle and election execution.
17//!
18//! This module implements a coordinated set of Offchain Worker (OCW) routines
19//! that drive the lifecycle of authors from affidavit key initialization to
20//! election participation and key rotation.
21//!
22//! ## Lifecycle Pipeline
23//!
24//! The system operates as a continuous OCW-driven pipeline:
25//!
26//! ```text
27//! InitAffidavitKey -> TryElection -> DeclareAffidavit -> RotateAffidavitKey
28//! ```
29//!
30//! Each stage is independently executable and relies on repeated OCW execution
31//! across blocks to eventually converge to a consistent state.
32//!
33//! ## Responsibilities
34//!
35//! ### 1. Affidavit Key Initialization (`InitAffidavitKey`)
36//! - Generates an ephemeral affidavit key pair using application crypto.
37//! - Persists the public identifier in [`Finalized`] offchain storage.
38//! - Ensures exactly one active affidavit key exists per node.
39//!
40//! ### 2. Election Execution (`TryElection`)
41//! - Opportunistically attempts to run the election for the upcoming session.
42//! - Uses the currently active affidavit key for authorization.
43//! - Ensures at-most-once execution per author per session.
44//! - Designed to be non-blocking and retry-safe.
45//!
46//! ### 3. Affidavit Declaration (`DeclareAffidavit`)
47//! - Submits a signed affidavit to signal participation in the next
48//! session's election.
49//! - Prepares and finalizes the next affidavit key for rotation.
50//! - Ensures eligibility and timing constraints via runtime checks.
51//!
52//! ### 4. Key Rotation (`RotateAffidavitKey`)
53//! - Finalizes transition from next -> active affidavit key.
54//! - Confirms successful affidavit submission via runtime state.
55//! - Performs cleanup and ensures lifecycle continuity.
56//!
57//! ## Storage & Finality
58//!
59//! Uses layered offchain storage:
60//! - [`ForkAware`]: fork-safe speculative state
61//! - [`Persistent`]: durable observation ledger
62//! - [`Finalized`]: stable values via [`Confidence`]
63//!
64//! Finality is governed by [`FinalizedPolicy`] using:
65//! - time delay ([`FinalityAfter`])
66//! - observation count ([`FinalityTicks`])
67//!
68//! ## Execution Model
69//!
70//! - **Idempotent**: All routines can run repeatedly without side effects.
71//! - **Non-blocking**: Routines exit early when prerequisites are unmet.
72//! - **Opportunistic**: Actions may be attempted before full readiness.
73//! - **Eventually consistent**: Correct state is reached through repetition.
74//!
75//! ## Security Model
76//!
77//! - Uses **ephemeral affidavit keys** instead of long-term authority keys.
78//! - Enforces **key rotation per lifecycle**.
79//! - Limits signing scope to specific operations (affidavit/election).
80//! - Reduces attack surface and key exposure risk.
81//!
82//! ## Failure Handling
83//!
84//! - Storage inconsistencies trigger **hard stops** to prevent unsafe execution.
85//! - Failed extrinsics are logged and retried in future OCW runs.
86//! - Missing runtime reflection (e.g. failed affidavit) triggers **state reset**.
87//!
88//! This ensures the system never remains in a partially inconsistent state.
89
90// ===============================================================================
91// ``````````````````````````````````` IMPORTS ```````````````````````````````````
92// ===============================================================================
93
94// --- Local crate imports ---
95use crate::{
96 crypto::*, types::*, AffidavitKeys, Config, CurrentSession, ElectsPreparedBy, Error,
97 FinalityAfter, FinalityTicks, Internals, Pallet, SessionStartAt,
98};
99
100// --- FRAME Suite ---
101use frame_suite::{ForksHandler, blockchain::*, routines::*};
102
103// --- FRAME Support ---
104use frame_support::traits::EstimateNextSessionRotation;
105
106// --- FRAME System ---
107use frame_system::{
108 offchain::{AppCrypto, SendSignedTransaction, SignedPayload, Signer},
109 pallet_prelude::BlockNumberFor,
110};
111
112// --- Substrate primitives ---
113use sp_runtime::{
114 DispatchError, RuntimeAppPublic, traits::{IdentifyAccount, One, Saturating}
115};
116
117//--- Scale-info ---
118use scale_info::prelude::{format, string::String};
119
120// ===============================================================================
121// ```````````````````````````````` LOG CONSTANTS ````````````````````````````````
122// ===============================================================================
123
124/// Log target (classifier) for affidavit logging.
125///
126/// All pallet-specific offchain logs (affidavits) should
127/// use this target to allow fine-grained filtering at the node level.
128pub const LOG_TARGET_AFDT: Option<&'static str> = Some("AFFIDAVIT");
129
130/// Log target (classifier) for elections logging.
131///
132/// All pallet-specific offchain logs (elections) should
133/// use this target to allow fine-grained filtering at the node level.
134pub const LOG_TARGET_ELEC: Option<&'static str> = Some("ELECTION");
135
136// ===============================================================================
137// ```````````````````````` INITIATE AFFIDAVIT KEY (OCW) `````````````````````````
138// ===============================================================================
139
140// --- Keys ---
141
142/// Offchain key identifier for the **active affidavit key**.
143///
144/// Used as the base identifier for storing and retrieving the
145/// currently active affidavit key from offchain storage.
146///
147/// The value stored under this key must survive fork re-orgs and
148/// confidence evaluation before it is considered safe for usage.
149pub const ACTIVE_AFDT_KEY: &'static [u8] = b"ACTIVE_AFDT_KEY";
150
151// ===============================================================================
152// ```````````````````````````````` LOG FORMATTER ````````````````````````````````
153// ===============================================================================
154
155/// Emoji indicator mapped to each [`LogLevel`] for visual log scanning.
156const EMOJI_DEBUG: &str = "๐";
157const EMOJI_ERROR: &str = "๐จ";
158const EMOJI_INFO: &str = "๐ฃ";
159const EMOJI_WARN: &str = "โ ๏ธ";
160
161/// Returns the emoji indicator associated with a given [`LogLevel`].
162///
163/// Used internally by [`std_fmt`] to embed a visual severity cue
164/// into the formatted log line.
165#[inline(always)]
166fn level_emoji(level: &LogLevel) -> &'static str {
167 match level {
168 LogLevel::Debug => EMOJI_DEBUG,
169 LogLevel::Error => EMOJI_ERROR,
170 LogLevel::Info => EMOJI_INFO,
171 LogLevel::Warn => EMOJI_WARN,
172 }
173}
174
175/// Standard log formatter for OCW routines.
176///
177/// Produces a consistently structured, human-readable log line that
178/// embeds block context, severity, routing target, and message body.
179///
180/// ### Output format
181///
182/// ```text
183/// ๐งฑ [<block>] <emoji> [<LEVEL>] ๐ฏ [<target>] ๐งพ <message>
184/// ```
185///
186/// ### Example
187///
188/// ```text
189/// ๐งฑ [312] ๐ฃ [Info] ๐ฏ [AFFIDAVIT] ๐งพ Module(9): InitAffidavitKeyRoutineSuccess
190/// ```
191pub fn std_fmt<T: Config>(
192 timestamp: BlockNumberFor<T>,
193 level: &LogLevel,
194 target: &str,
195 message: &str,
196) -> String {
197 format!(
198 "๐งฑ [{:?}] {} [{:?}] ๐ฏ [{}] ๐งพ {}",
199 timestamp,
200 level_emoji(level),
201 level,
202 target,
203 message
204 )
205}
206
207// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
208// ``````````````````````````````` ERROR PROVIDERS ```````````````````````````````
209// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
210
211/// Error mapping for **fork-aware speculative storage** access
212/// during affidavit key initialization.
213///
214/// This implementation provides pallet-specific error variants
215/// expected to be convertible into [`DispatchError`](sp_runtime::DispatchError).
216///
217/// ## Scope
218/// - Applies to speculative, fork-aware storage keyed by a hash.
219/// - Focuses on failures related to the speculative hash of the
220/// finalized affidavit key value.
221impl<T: Config> OffchainStorageError<ForkAware<T, ValueHash, InitAffidavitKey<T>, Pallet<T>>>
222 for InitAffidavitKey<T>
223{
224 type Error = Error<T>;
225
226 /// Speculative hash decoding failed.
227 fn decode_failed() -> Self::Error {
228 Error::<T>::ActiveAfdtKeySpeculativeHashDecodeFail
229 }
230
231 /// Concurrent mutation detected while accessing fork-aware storage.
232 fn concurrent_mutation() -> Self::Error {
233 Error::<T>::ActiveAfdtKeySpeculativeHashConcurrentMutation
234 }
235}
236
237/// Error mapping for **persistent finalized offchain storage**
238/// during affidavit key initialization.
239///
240/// This implementation handles failures when interacting with the
241/// persistent ledger that stores finalized affidavit key values,
242/// wrapped in [`Confidence`] to reflect finality guarantees.
243///
244/// ## Scope
245/// - Applies to persistent, non-speculative storage.
246/// - Covers decoding and concurrent mutation failures.
247impl<T: Config> OffchainStorageError<Persistent<T, Ledger<T, AffidavitId<T>>, InitAffidavitKey<T>>>
248 for InitAffidavitKey<T>
249{
250 type Error = Error<T>;
251 /// Finalized value decoding failed.
252 fn decode_failed() -> Self::Error {
253 Error::<T>::ActiveAfdtKeyFinalizedValueDecodeFail
254 }
255 /// Concurrent mutation detected while accessing persistent storage.
256 fn concurrent_mutation() -> Self::Error {
257 Error::<T>::ActiveAfdtKeyFinalizedValueConcurrentMutation
258 }
259}
260
261/// Invariant enforcement for **finalized offchain storage**.
262///
263/// This implementation defines errors for high-level coordination
264/// failures between speculative and persistent storage layers.
265///
266/// Cleanups will be implicitly handled by the storage itself.
267impl<T: Config> FinalizedOffchainStorageError<T, AffidavitId<T>> for InitAffidavitKey<T> {
268 type Error = Error<T>;
269
270 /// A speculative hash **must not exist** without a
271 /// corresponding persistent value.
272 fn hanging_hash() -> Self::Error {
273 Error::<T>::ActiveAfdtKeySpeculativeHangingHash
274 }
275
276 /// A persistent value **must not exist** without holding its corresponding
277 /// speculative hash's value.
278 fn hanging_value() -> Self::Error {
279 Error::<T>::ActiveAfdtKeyFinalizedHangingValue
280 }
281}
282
283// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284// `````````````````````````````` FINALIZED POLICY ```````````````````````````````
285// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286
287/// This policy defines when a finalized offchain storage value
288/// is considered **safe for irreversible side effects**, such as:
289/// - key promotion,
290/// - state transitions,
291/// - or cleanup of speculative storage.
292///
293/// The policy is consumed by the [`Finalized`] storage abstraction
294/// in conjunction with [`Confidence`] to determine optimal-finality.
295impl<T: Config> FinalizedPolicy<T> for InitAffidavitKey<T> {
296 /// Returns the wall-clock time after which a value is considered final.
297 fn finality_after() -> <T as pallet_timestamp::Config>::Moment {
298 FinalityAfter::<T>::get()
299 }
300
301 /// Returns the number of block confirmations required to reach finality after
302 /// the wall-clock time reached.
303 fn finality_ticks() -> BlockNumberFor<T> {
304 FinalityTicks::<T>::get()
305 }
306}
307
308// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
309// `````````````````````````````````` ROUTINES ```````````````````````````````````
310// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
311
312/// An Offchain Worker (OCW) routine responsible for initializing the
313/// **active affidavit key** for the local node.
314///
315/// This routine bootstraps an affidavit application key **per node**,
316/// regardless of whether the node is acting as an author i.e.,
317/// validator node.
318///
319/// The affidavit key is a **rotated operational key** used exclusively
320/// for affidavit-related signing. Only a single affidavit key is expected
321/// to be active at any given time, and the runtime relies on this invariant
322/// being upheld.
323impl<T: Config> Routines<BlockNumberFor<T>> for InitAffidavitKey<T> {
324 /// Determines whether the affidavit key initialization routine
325 /// should run.
326 ///
327 /// Initialization **must not run** if:
328 /// - an active affidavit key is already stored in the offchain storage, and
329 /// - the corresponding key pair exists in the node's affidavit keystore (app crypto).
330 ///
331 /// Any storage inconsistency (e.g. corrupted or undecodable data)
332 /// is treated as a **hard stop** and causes the routine to refuse
333 /// execution, since proceeding could violate runtime expectations.
334 fn can_run(&self) -> Result<(), Self::Logger> {
335 // Optimistically retrieve the currently active affidavit key
336 let result =
337 Finalized::<T, AffidavitId<T>, Self, Pallet<T>>::get(ACTIVE_AFDT_KEY, LOG_TARGET_AFDT, None);
338
339 // Only allow confident and safe storage values which are finalized as per our policy.
340 let afdt_key = match result {
341 Ok(None) => return Ok(()),
342 Ok(Some(Confidence::Safe(key))) => key,
343 Ok(Some(_)) => {
344 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
345 &Error::<T>::ActiveAfdtKeyNotYetFinalized.into(),
346 self.at,
347 LOG_TARGET_AFDT,
348 Some(std_fmt::<T>),
349 ))
350 }
351 Err(_) => {
352 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
353 &Error::<T>::OCWStorageDecisionHalt.into(),
354 self.at,
355 LOG_TARGET_AFDT,
356 Some(std_fmt::<T>),
357 ));
358 }
359 };
360
361 // Retrieve all affidavit keys currently available in the node keystore
362 let all_keys =
363 <<T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::RuntimeAppPublic
364 as RuntimeAppPublic>::all();
365
366 // Fast path: no keys exist in the keystore
367 if all_keys.is_empty() {
368 return Ok(());
369 }
370
371 // Check whether the active affidavit key is already available for signing
372 for key in all_keys.into_iter() {
373 let generic_pub:
374 <T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::GenericPublic =
375 key.into();
376 let public: T::Public = generic_pub.into();
377 let account: AffidavitId<T> = public.clone().into_account().into();
378
379 if account == afdt_key {
380 // Active affidavit key already exists and is usable
381 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
382 &Error::<T>::AffidavitKeyExists.into(),
383 self.at,
384 LOG_TARGET_AFDT,
385 Some(std_fmt::<T>),
386 ));
387 }
388 }
389
390 Ok(())
391 }
392
393 /// Initializes a new affidavit key pair and marks it as the
394 /// **active affidavit key** for the node.
395 ///
396 /// This routine:
397 /// 1. Generates a new affidavit application key pair in the local keystore.
398 /// 2. Extracts the public key and derives its corresponding `AffidavitId`.
399 /// 3. Stores the public identifier as the active affidavit key in
400 /// the offchain storage.
401 ///
402 /// Affidavit keys are **rotated regularly** (e.g. per session). The node's
403 /// long-term authority or author role i.e., stash key is never used directly
404 /// for affidavit signing, reducing operational risk.
405 ///
406 /// If storing the active affidavit key fails, the generated key pair
407 /// becomes unreachable and is effectively discarded. The routine will
408 /// retry on subsequent block OCW executions until initialization succeeds.
409 fn run_service(&self) -> Result<(), Self::Logger> {
410 if let Err(e) = Self::can_run(&self) {
411 // Fast Path if initialization is not required
412 if e == Error::<T>::AffidavitKeyExists.into() {
413 return Ok(());
414 }
415 return Err(e);
416 }
417
418 // Generate a new affidavit application key pair
419 let key =
420 <<T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::RuntimeAppPublic
421 as RuntimeAppPublic>::generate_pair(None);
422
423 // Raw conversions
424 let generic_pub: <T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::GenericPublic =
425 key.into();
426 let public: T::Public = generic_pub.into();
427 let account: AffidavitId<T> = public.clone().into_account().into();
428
429 // Stabilize the public identifier as the active affidavit key
430 if Finalized::<T, AffidavitId<T>, Self, Pallet<T>>::insert(
431 ACTIVE_AFDT_KEY,
432 &account,
433 LOG_TARGET_AFDT,
434 None,
435 )
436 .is_err()
437 {
438 let block = self.at;
439 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
440 &Error::<T>::SetNewAffidavitKeyFailed.into(),
441 block,
442 LOG_TARGET_AFDT,
443 Some(std_fmt::<T>),
444 ));
445 }
446
447 Ok(())
448 }
449
450 /// Logs a `info` message on a successful [`InitAffidavitKey`] routine.
451 fn on_ran_service(&self) {
452 <Self as Logging<BlockNumberFor<T>>>::debug(
453 &Error::<T>::InitAffidavitKeyRoutineSuccess.into(),
454 self.at,
455 LOG_TARGET_AFDT,
456 Some(std_fmt::<T>),
457 );
458 }
459}
460
461// ===============================================================================
462// ````````````````````````````` TRY ELECTION (OCW) ``````````````````````````````
463// ===============================================================================
464
465// `TryElection` reuses `DeclareAffidavit` offchain storage for key coordination
466// and state tracking, avoiding duplication.
467// No additional offchain key-value storage is defined for this routine.
468
469// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
470// ````````````````````````````````` ROUTINE OF ``````````````````````````````````
471// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
472
473/// Authorization layer for the **election execution routine**.
474///
475/// This implementation resolves the **affidavit public key** authorized
476/// to run the election flow. It deliberately reuses the key-resolution
477/// logic from [`DeclareAffidavit`] to enforce strict lifecycle ordering:
478///
479/// ```text
480/// Affidavit Declaration -> Key Rotation -> Election
481/// ```
482///
483/// By delegating authorization instead of duplicating it, this layer
484/// guarantees that elections are executed only by authors who have
485/// successfully completed the affidavit and key-rotation process.
486impl<T: Config> RoutineOf<T::Public, BlockNumberFor<T>> for TryElection<T> {
487 /// Determines the affidavit public key authorized to run the election routine.
488 ///
489 /// ## Semantics
490 /// - Reuses the **currently active affidavit key** resolved by
491 /// [`DeclareAffidavit`].
492 /// - Ensures that election execution is authorized by the same
493 /// operational key that is eligible for affidavit declaration.
494 ///
495 /// ## Rationale
496 /// Elections are permitted **only after** a successful affidavit
497 /// declaration and key-rotation cycle. By delegating to
498 /// `DeclareAffidavit::who`, this routine enforces a strict
499 /// lifecycle ordering without duplicating key-resolution logic.
500 ///
501 /// ## Key Resolution Note
502 /// Although this appears syntactically as the *active affidavit key*,
503 /// it semantically represents the **recently rotated next affidavit key**
504 /// that was promoted to active status by the previous OCW execution's
505 /// final routine ([`RotateAffidavitKey`]).
506 fn who(at: &BlockNumberFor<T>) -> Result<T::Public, Self::Logger> {
507 // Returns only if the Active Affidavit Key is Initialized, Stored and Finalized
508 let who = <DeclareAffidavit<T> as RoutineOf<T::Public, BlockNumberFor<T>>>::who(at)?;
509 Ok(who)
510 }
511}
512
513// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
514// `````````````````````````````````` ROUTINES ```````````````````````````````````
515// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
516
517/// Offchain routine responsible for **opportunistic election execution**.
518///
519/// This routine attempts to submit the `elect` extrinsic for the
520/// upcoming session using the **currently active affidavit key**.
521///
522/// It is intentionally designed to be:
523/// - **Non-blocking**: exits silently when prerequisites are unmet.
524/// - **Idempotent**: safe to run repeatedly across blocks and forks.
525/// - **Pre-emptive**: elections may be attempted before the OCW pipeline
526/// fully converges, succeeding in later executions.
527///
528/// The routine participates in the OCW execution pipeline:
529///
530/// ```text
531/// InitAffidavitKey -> TryElection -> DeclareAffidavit -> RotateAffidavitKey
532/// ```
533///
534/// and relies on repeated OCW invocations to eventually satisfy all
535/// temporal and state-dependent constraints.
536impl<T: Config> Routines<BlockNumberFor<T>> for TryElection<T> {
537 /// Checks whether an election can be processed in the current block.
538 ///
539 /// ## Semantics
540 /// - Delegates election window validation to
541 /// [`ElectAuthors::can_process_election`].
542 /// - Does **not** treat an unavailable election window as an error.
543 ///
544 /// ## Behavior
545 /// - If the election window is not open, the routine exits early
546 /// with an informational log.
547 /// - Hard failures (e.g. invalid configuration) are surfaced
548 /// as logged errors.
549 ///
550 /// This design allows the OCW orchestrator to continue executing
551 /// subsequent routines (e.g. affidavit declaration) in the same block.
552 fn can_run(&self) -> Result<(), Self::Logger> {
553 if let Err(e) =
554 <Internals<T> as ElectAuthors<AuthorOf<T>, ElectionVia<T>>>::can_process_election(&None)
555 {
556 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
557 &e,
558 self.at,
559 LOG_TARGET_ELEC,
560 Some(std_fmt::<T>),
561 ));
562 }
563 Ok(())
564 }
565
566 /// Attempts to submit the election transaction for the upcoming session.
567 ///
568 /// ## Execution Strategy
569 /// This routine is **opportunistic and non-blocking**:
570 ///
571 /// - If the election window is not open, execution passes silently to next routine.
572 /// - If the author is not eligible, execution exits silently.
573 /// - If the author already acted as the election runner, execution exits silently.
574 ///
575 /// This behavior is intentional and allows the OCW pipeline to be
576 /// orchestrated in the following order:
577 ///
578 /// ```text
579 /// InitAffidavitKey -> TryElection -> DeclareAffidavit -> RotateAffidavitKey
580 /// ```
581 ///
582 /// Elections are therefore attempted *pre-emptively* and may succeed
583 /// in a later OCW invocation once all prerequisites converge.
584 ///
585 /// ## Semantics
586 /// - Uses the **currently active affidavit key** to authorize the election.
587 /// - Submits an unsigned `elect` extrinsic signed offchain.
588 /// - Ensures that each author runs the election at most once per session.
589 ///
590 /// ## Failure Handling
591 /// - All signing or submission failures are logged as errors.
592 /// - No retries are attempted in the same block.
593 /// - Subsequent OCW executions may retry automatically.
594 fn run_service(&self) -> Result<(), Self::Logger> {
595 // Exit early if election window is unavailable
596 // And try the next routine of declaring affidavit
597 if let Err(_) = Self::can_run(&self) {
598 return Ok(());
599 }
600
601 let for_session = CurrentSession::<T>::get().saturating_add(One::one());
602
603 // Resolve author from affidavit key registered for election (session + 2)
604 let afdt_pub: AffidavitId<T> = self.by.clone().into_account().into();
605 let Some(author) =
606 AffidavitKeys::<T>::get((for_session.saturating_add(One::one()), afdt_pub))
607 else {
608 // The active affidavit key is not for elections
609 // but for declaring affidavit
610 <Self as Logging<BlockNumberFor<T>>>::debug(
611 &Error::<T>::AffidavitKeyForDeclaration.into(),
612 self.at,
613 LOG_TARGET_ELEC,
614 Some(std_fmt::<T>),
615 );
616 return Ok(());
617 };
618
619 // Prevent duplicate election execution by the same author
620 // Until this reflects, repeated OCW executions may submit
621 // duplicate transactions which is expected to be filtered by
622 // `ValidateUnsigned`'s `ValidTransaction::and_provides()`
623 if let Some((runner, _)) = ElectsPreparedBy::<T>::get(for_session) {
624 if author == runner {
625 // Already ran election
626 // Try declaring affidavit during the upcoming session
627 return Ok(());
628 }
629 }
630
631 let payload = ElectionPayload {
632 public: self.by.clone(),
633 };
634
635 // Sign election payload using affidavit key
636 let Some(signature) =
637 <ElectionPayload<T::Public> as SignedPayload<T>>::sign::<T::AffidavitCrypto>(&payload)
638 else {
639 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
640 &Error::<T>::CannotSignElectionTxPayload.into(),
641 self.at,
642 LOG_TARGET_ELEC,
643 Some(std_fmt::<T>),
644 ));
645 };
646
647 // Submit election extrinsic
648 let signer = Signer::<T, T::AffidavitCrypto>::any_account();
649 let result = signer.send_signed_transaction(|_| {
650 <T as crate::Config>::RuntimeCall::from(crate::Call::elect {
651 payload: payload.clone(),
652 signature: signature.clone(),
653 })
654 .into()
655 });
656
657 match result {
658 Some((_, Ok(_))) => Ok(()),
659
660 Some((_, Err(_))) => Err(<Self as Logging<BlockNumberFor<T>>>::error(
661 &Error::<T>::ExtrinsicFailedToElectAuthors.into(),
662 self.at,
663 LOG_TARGET_ELEC,
664 Some(std_fmt::<T>),
665 )),
666
667 None => Err(<Self as Logging<BlockNumberFor<T>>>::error(
668 &Error::<T>::CannotSubmitElectionTx.into(),
669 self.at,
670 LOG_TARGET_ELEC,
671 Some(std_fmt::<T>),
672 )),
673 }
674 }
675
676 /// Logs a `info` message on a successful [`TryElection`] routine.
677 fn on_ran_service(&self) {
678 <Self as Logging<BlockNumberFor<T>>>::debug(
679 &Error::<T>::TryElectionRoutineSuccess.into(),
680 self.at,
681 LOG_TARGET_ELEC,
682 Some(std_fmt::<T>),
683 );
684 }
685}
686
687// ===============================================================================
688// ``````````````````````````` DECLARE AFFIDAVIT (OCW) ```````````````````````````
689// ===============================================================================
690
691// --- Keys ---
692
693/// Offchain storage key identifying the **next affidavit key**.
694///
695/// This key represents the affidavit public identifier that:
696/// - is declared during the current affidavit window,
697/// - undergoes finality evaluation via [`Finalized`] storage semantics,
698/// - and is later promoted to the **active affidavit key**
699/// by the [`RotateAffidavitKey`] routine.
700///
701/// The value stored under this key is **session-forward looking** and
702/// must survive fork re-orgs and confidence evaluation before it is
703/// considered safe for activation.
704pub const NEXT_AFDT_KEY: &'static [u8] = b"NEXT_AFDT_KEY";
705
706// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
707// ``````````````````````````````` ERROR PROVIDERS ```````````````````````````````
708// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
709
710/// Error policy for **fork-aware speculative storage** of the next affidavit key.
711///
712/// This implementation defines how storage-layer failures originating
713/// from fork-aware offchain storage are surfaced as
714/// routine-specific [`DispatchError`](sp_runtime::DispatchError) values.
715///
716/// These errors relate exclusively to the **speculative identity**
717/// (`ValueHash`) used to track the next affidavit key across forks.
718///
719/// Any failure reported here:
720/// - is logged exactly once by the storage layer,
721/// - and returned unchanged to the caller.
722impl<T: Config> OffchainStorageError<ForkAware<T, ValueHash, DeclareAffidavit<T>, Pallet<T>>>
723 for DeclareAffidavit<T>
724{
725 type Error = Error<T>;
726
727 /// Returned when decoding the speculative hash fails.
728 ///
729 /// Indicates corrupted or unexpected fork-local storage state.
730 fn decode_failed() -> Self::Error {
731 Error::<T>::NextAfdtKeySpeculativeHashDecodeFail
732 }
733
734 /// Returned when a concurrent mutation of the speculative hash is detected.
735 ///
736 /// Indicates overlapping OCW executions or race conditions.
737 fn concurrent_mutation() -> Self::Error {
738 Error::<T>::NextAfdtKeySpeculativeHashConcurrentMutation
739 }
740}
741
742/// Error policy for **persistent finalized storage** of the next affidavit key.
743///
744/// This implementation defines error signaling for failures that occur
745/// while interacting with the persistent observation ledger backing
746/// the [`Finalized`] storage model.
747///
748/// These errors concern the **finalized value and its observation history**,
749/// not speculative fork-local state.
750impl<T: Config> OffchainStorageError<Persistent<T, Ledger<T, AffidavitId<T>>, DeclareAffidavit<T>>>
751 for DeclareAffidavit<T>
752{
753 type Error = Error<T>;
754
755 /// Returned when decoding the persistent ledger entry fails.
756 ///
757 /// Indicates corrupted or incompatible persisted offchain data.
758 fn decode_failed() -> Self::Error {
759 Error::<T>::NextAfdtKeyFinalizedValueDecodeFail
760 }
761
762 /// Returned when a concurrent mutation of the persistent ledger is detected.
763 ///
764 /// Indicates contention between multiple OCW executions.
765 fn concurrent_mutation() -> Self::Error {
766 Error::<T>::NextAfdtKeyFinalizedValueConcurrentMutation
767 }
768}
769
770/// Finality-specific invariant errors for the next affidavit key.
771///
772/// These errors are emitted when semantic inconsistencies are detected
773/// between fork-aware and persistent storage layers.
774///
775/// Such conditions indicate **partial or invalid state**, and the
776/// storage layer automatically performs cleanup before returning
777/// these errors.
778impl<T: Config> FinalizedOffchainStorageError<T, AffidavitId<T>> for DeclareAffidavit<T> {
779 type Error = Error<T>;
780
781 /// Emitted when a speculative fork-aware hash exists
782 /// without a corresponding persistent ledger entry.
783 fn hanging_hash() -> Self::Error {
784 Error::<T>::NextAfdtKeySpeculativeHangingHash
785 }
786
787 /// Emitted when **persistent storage contains no value** corresponding to
788 /// an existing speculative (fork-aware) hash.
789 fn hanging_value() -> Self::Error {
790 Error::<T>::NextAfdtKeyFinalizedHangingValue
791 }
792}
793
794// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
795// `````````````````````````````` FINALIZED POLICY ```````````````````````````````
796// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
797
798/// Finality evaluation policy for the **next affidavit key**.
799///
800/// This implementation **reuses the finality parameters defined by
801/// [`InitAffidavitKey`]**, ensuring that both the *active* and *next*
802/// affidavit keys are evaluated under **identical confidence guarantees**.
803///
804/// ## Design Rationale
805///
806/// Every routine that uses [`Finalized`] storage:
807/// - must define exactly **one finality policy**, and
808/// - that policy is **scoped to the routine**, not to the storage backend.
809///
810/// Since the *next affidavit key* participates in the **same operational
811/// lifecycle** as the active affidavit key (generation -> observation -> rotation),
812/// it is intentionally governed by the same time- and observation-based
813/// confidence thresholds.
814///
815/// ## Invariants
816///
817/// - [`Finalized`] storage is **strictly constrained**:
818/// - one logical value per routine,
819/// - one finality policy per value.
820/// - Unlike [`ForkAware`] or [`Persistent`] storage backends, [`Finalized`]
821/// does **not** allow multiple independent values or heterogeneous policies.
822///
823/// Reusing the policy from [`InitAffidavitKey`] preserves these invariants
824/// while avoiding duplicated configuration and potential divergence.
825impl<T: Config> FinalizedPolicy<T> for DeclareAffidavit<T> {
826 /// Wall-clock delay required before confidence evaluation begins.
827 fn finality_after() -> <T as pallet_timestamp::Config>::Moment {
828 <InitAffidavitKey<T> as FinalizedPolicy<T>>::finality_after()
829 }
830
831 /// Number of block-scoped observations required to reach strong confidence.
832 fn finality_ticks() -> BlockNumberFor<T> {
833 <InitAffidavitKey<T> as FinalizedPolicy<T>>::finality_ticks()
834 }
835}
836
837// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
838// `````````````````````````````````` ROUTINE OF `````````````````````````````````
839// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
840
841/// Declares the authorization requirements for the [`DeclareAffidavit`] routine.
842///
843/// This implementation specifies **who is permitted to execute** the routine
844/// at a given point in time.
845///
846/// The routine is restricted to the node's **active affidavit key**. This
847/// ensures that affidavit declarations are signed only by the currently
848/// designated, rotated operational key, rather than by long-term authority
849/// or stash keys.
850///
851/// The returned `T::Public` value represents the concrete public key that
852/// must be used to sign the affidavit payload at the given block number.
853impl<T: Config> RoutineOf<T::Public, BlockNumberFor<T>> for DeclareAffidavit<T> {
854 /// Determines which public key is authorized to execute this routine.
855 ///
856 /// ## Why this check exists
857 ///
858 /// Affidavit declarations are security-sensitive operations. To reduce
859 /// the blast radius of key compromise and to support regular key rotation,
860 /// only the **currently active affidavit key** is permitted to authorize
861 /// this routine.
862 ///
863 /// The active affidavit key:
864 /// - is stored and ensured by [`Finalized`] offchain storage,
865 /// - is expected to have a corresponding key pair in the node's keystore,
866 /// - and must be explicitly initialized before this routine can run.
867 ///
868 /// ## Failure semantics
869 ///
870 /// - Any storage inconsistency is treated as a **hard error**, since it
871 /// indicates corrupted or unexpected node state.
872 /// - If no active affidavit key is configured, the caller is considered
873 /// misconfigured and execution is refused.
874 /// - If the active affidavit key exists but the corresponding key pair
875 /// is missing from the keystore, execution is refused.
876 fn who(at: &BlockNumberFor<T>) -> Result<T::Public, Self::Logger> {
877 // Initialized by `InitAffidavitKey`; reused here to avoid duplicating implementations
878 let result = Finalized::<T, AffidavitId<T>, InitAffidavitKey<T>, Pallet<T>>::get(
879 ACTIVE_AFDT_KEY,
880 LOG_TARGET_AFDT,
881 None,
882 );
883
884 let afdt_key = match result {
885 Ok(Some(Confidence::Safe(key))) => key,
886 Ok(None) => {
887 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
888 &Error::<T>::ExpectedToHoldActiveAffidavitKey.into(),
889 *at,
890 LOG_TARGET_AFDT,
891 Some(std_fmt::<T>),
892 ))
893 }
894 Ok(Some(_)) => {
895 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
896 &Error::<T>::ActiveAfdtKeyNotYetFinalized.into(),
897 *at,
898 LOG_TARGET_AFDT,
899 Some(std_fmt::<T>),
900 ))
901 }
902 Err(_) => {
903 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
904 &Error::<T>::OCWStorageDecisionHalt.into(),
905 *at,
906 LOG_TARGET_AFDT,
907 Some(std_fmt::<T>),
908 ));
909 }
910 };
911
912 // Retrieve all affidavit keys currently available in the local keystore
913 let all_keys =
914 <<T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::RuntimeAppPublic
915 as RuntimeAppPublic>::all();
916
917 // Ensure the active affidavit key has a corresponding key pair
918 for key in all_keys.into_iter() {
919 let generic_pub:
920 <T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::GenericPublic =
921 key.into();
922 let public: T::Public = generic_pub.into();
923 let account: AffidavitId<T> = public.clone().into_account().into();
924
925 if account == afdt_key {
926 // Authorized signer found
927 return Ok(public);
928 }
929 }
930
931 // Active affidavit key exists but is not usable for signing
932 Err(<Self as Logging<BlockNumberFor<T>>>::error(
933 &Error::<T>::ExpectedActiveAffidavitKeyPairNotFound.into(),
934 *at,
935 LOG_TARGET_AFDT,
936 Some(std_fmt::<T>),
937 ))
938 }
939}
940
941// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
942// ``````````````````````````````````` ROUTINES ``````````````````````````````````
943// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
944
945/// Offchain routine responsible for **affidavit declaration** and
946/// **preparing key rotation for the next election cycle**.
947///
948/// ## What an affidavit represents
949///
950/// Declaring an affidavit signals that an author:
951/// - is **ready to participate in the upcoming election**, and
952/// - satisfies all protocol-defined requirements (e.g. backing, support).
953///
954/// An affidavit:
955/// - is submitted via an **unsigned extrinsic**,
956/// - is allowed **only within a bounded affidavit window**, and
957/// - is restricted to **valid authors** holding the correct operational role.
958///
959/// ## Responsibilities of this routine
960///
961/// This OCW routine performs the **offchain coordination** required to:
962///
963/// 1. Verify that affidavit submission is currently permitted.
964/// 2. Ensure a **next affidavit key** exists and is finalized.
965/// 3. Submit the `declare` extrinsic using the **active affidavit key**.
966/// 4. Attach the **next affidavit key** for rotation into the upcoming session.
967///
968/// ## Key separation and security
969///
970/// - Long-term authority / stash keys are **never used** here.
971/// - All signing is performed using **ephemeral affidavit keys**
972/// managed via application crypto and rotated regularly.
973/// - This minimizes the blast radius of key compromise and
974/// enables strict lifecycle enforcement.
975///
976/// ## Execution model
977///
978/// This routine is designed to be **optimistic and non-blocking**:
979///
980/// - It may be executed before the node has fully completed validation
981/// via `validate` extrinsic.
982/// - Runtime-side checks ultimately decide whether the extrinsic succeeds.
983/// - Failures are logged and retried automatically in later OCW executions.
984///
985/// This makes the routine safe to run repeatedly without risking
986/// inconsistent on-chain state.
987impl<T: Config> Routines<BlockNumberFor<T>> for DeclareAffidavit<T> {
988 /// Determines whether the affidavit declaration routine may proceed.
989 ///
990 /// ## Semantics
991 ///
992 /// - Ensures that a **next affidavit key** exists in finalized offchain
993 /// storage, else initiates and finalizes one.
994 /// - Verifies that the **current affidavit key** is eligible to submit
995 /// an affidavit in the runtime.
996 ///
997 /// ## Key handling
998 ///
999 /// - The active affidavit key (`self.by`) **must already exist** in the
1000 /// node's keystore.
1001 /// - Author primary keys are **never consulted**.
1002 ///
1003 /// ## Optimistic execution
1004 ///
1005 /// This function may be called **before** the author has successfully
1006 /// executed the on-chain `validate`.
1007 ///
1008 /// That is safe because:
1009 /// - Runtime-side checks enforce correctness.
1010 /// - This routine merely prepares and submits the transaction.
1011 ///
1012 /// If the author has not yet validated their affidavit key on-chain,
1013 /// the submission will be rejected harmlessly.
1014 fn can_run(&self) -> Result<(), Self::Logger> {
1015 let afdt_pub = &self.by;
1016 let block = self.at;
1017
1018 // Ensure a next affidavit key exists (or create one)
1019 let result =
1020 Finalized::<T, AffidavitId<T>, Self, Pallet<T>>::get(NEXT_AFDT_KEY, LOG_TARGET_AFDT, None);
1021
1022 match result {
1023 // No next key yet -> generate and persist one
1024 Ok(None) => {
1025 let new_key =
1026 <<T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::RuntimeAppPublic
1027 as RuntimeAppPublic>::generate_pair(None);
1028
1029 let public: T::Public = {
1030 let generic_pub:
1031 <T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::GenericPublic =
1032 new_key.into();
1033 generic_pub.into()
1034 };
1035
1036 let new_afdt_key = public.clone().into_account();
1037
1038 if Finalized::<T, AffidavitId<T>, Self, Pallet<T>>::insert(
1039 NEXT_AFDT_KEY,
1040 &new_afdt_key,
1041 LOG_TARGET_AFDT,
1042 None,
1043 )
1044 .is_err()
1045 {
1046 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1047 &Error::<T>::SetNextAffidavitKeyFailed.into(),
1048 block,
1049 LOG_TARGET_AFDT,
1050 Some(std_fmt::<T>),
1051 ));
1052 }
1053 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
1054 &Error::<T>::NextAfdtKeyNotYetFinalized.into(),
1055 block,
1056 LOG_TARGET_AFDT,
1057 Some(std_fmt::<T>),
1058 ));
1059 }
1060
1061 // Next key exists and finalized
1062 Ok(Some(Confidence::Safe(_))) => {}
1063
1064 Ok(Some(_)) => {
1065 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
1066 &Error::<T>::NextAfdtKeyNotYetFinalized.into(),
1067 block,
1068 LOG_TARGET_AFDT,
1069 Some(std_fmt::<T>),
1070 ))
1071 }
1072
1073 // Storage inconsistency -> hard stop
1074 Err(_) => {
1075 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1076 &Error::<T>::OCWStorageDecisionHalt.into(),
1077 block,
1078 LOG_TARGET_AFDT,
1079 Some(std_fmt::<T>),
1080 ));
1081 }
1082 }
1083
1084 // Check runtime-level affidavit eligibility
1085 if let Err(e) =
1086 <Pallet<T> as ElectionAffidavits<AffidavitId<T>, ElectionVia<T>>>::can_submit_affidavit(
1087 (&afdt_pub.clone().into_account()).into(),
1088 )
1089 {
1090 return Err(<Self as Logging<BlockNumberFor<T>>>::debug(
1091 &e,
1092 block,
1093 LOG_TARGET_AFDT,
1094 Some(std_fmt::<T>),
1095 ));
1096 }
1097
1098 Ok(())
1099 }
1100
1101 /// Submits the `declare` extrinsic.
1102 ///
1103 /// ## Preconditions
1104 ///
1105 /// - The active affidavit key is authorized and present in the keystore.
1106 /// - The next affidavit key exists and has reached sufficient finality.
1107 ///
1108 /// ## Behavior
1109 ///
1110 /// - Signs the affidavit payload using the **current affidavit key**.
1111 /// - Includes the **next affidavit key** for rotation.
1112 /// - Submits the extrinsic to the transaction pool.
1113 ///
1114 /// ## Important notes
1115 ///
1116 /// - This routine **does not apply any on-chain state changes itself**.
1117 /// - It only submits a transaction; actual effects occur later
1118 /// during block execution.
1119 /// - Any failure is logged and retried in future OCW runs.
1120 fn run_service(&self) -> Result<(), Self::Logger> {
1121 Self::can_run(self)?;
1122
1123 let afdt_key = &self.by;
1124 let block = self.at;
1125
1126 let result =
1127 Finalized::<T, AffidavitId<T>, Self, Pallet<T>>::get(NEXT_AFDT_KEY, LOG_TARGET_AFDT, None);
1128
1129 let next_afdt_key = match result {
1130 Ok(Some(Confidence::Safe(key))) => key,
1131 Ok(Some(_)) => {
1132 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1133 &Error::<T>::ExpectedToHoldFinalizedNextAffidavitKey.into(),
1134 block,
1135 LOG_TARGET_AFDT,
1136 Some(std_fmt::<T>),
1137 ))
1138 }
1139 Ok(None) => {
1140 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1141 &Error::<T>::ExpectedToHoldFinalizedNextAffidavitKey.into(),
1142 block,
1143 LOG_TARGET_AFDT,
1144 Some(std_fmt::<T>),
1145 ))
1146 }
1147 Err(_) => {
1148 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1149 &Error::<T>::OCWStorageDecisionHalt.into(),
1150 block,
1151 LOG_TARGET_AFDT,
1152 Some(std_fmt::<T>),
1153 ))
1154 }
1155 };
1156
1157 let payload = AffidavitPayload {
1158 public: afdt_key.clone(),
1159 rotate: next_afdt_key.clone(),
1160 };
1161
1162 let Some(signature) =
1163 <AffidavitPayload<T::Public, AffidavitId<T>> as SignedPayload<T>>::sign::<
1164 T::AffidavitCrypto,
1165 >(&payload)
1166 else {
1167 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1168 &Error::<T>::CannotSignAffidavitTxPayload.into(),
1169 block,
1170 LOG_TARGET_AFDT,
1171 Some(std_fmt::<T>),
1172 ));
1173 };
1174
1175 let signer = Signer::<T, T::AffidavitCrypto>::any_account();
1176
1177 // If repeated OCW executions, this may result in duplicate transactions which
1178 // shall be filtered out by `ValidateUnsigned`'s `ValidTransaction::and_provides()`
1179 let result = signer.send_signed_transaction(|_| {
1180 <T as crate::Config>::RuntimeCall::from(crate::Call::<T>::declare {
1181 payload: payload.clone(),
1182 signature: signature.clone(),
1183 })
1184 .into()
1185 });
1186
1187 match result {
1188 Some((_, Ok(_))) => Ok(()),
1189
1190 Some((_, Err(_))) => Err(<Self as Logging<BlockNumberFor<T>>>::error(
1191 &Error::<T>::FailedToDeclareAffidavit.into(),
1192 block,
1193 LOG_TARGET_AFDT,
1194 Some(std_fmt::<T>),
1195 )),
1196
1197 None => Err(<Self as Logging<BlockNumberFor<T>>>::error(
1198 &Error::<T>::CannotSubmitAffidavitTx.into(),
1199 block,
1200 LOG_TARGET_AFDT,
1201 Some(std_fmt::<T>),
1202 )),
1203 }
1204 }
1205
1206 /// Logs a `info` message on a successful [`DeclareAffidavit`] routine.
1207 fn on_ran_service(&self) {
1208 <Self as Logging<BlockNumberFor<T>>>::debug(
1209 &Error::<T>::DeclarAffidavitRoutineSuccess.into(),
1210 self.at,
1211 LOG_TARGET_AFDT,
1212 Some(std_fmt::<T>),
1213 );
1214 }
1215}
1216// ===============================================================================
1217// ````````````````````````` ROTATE AFFIDAVIT KEY (OCW) ``````````````````````````
1218// ===============================================================================
1219
1220// `RotateAffidavitKey` reuses `DeclareAffidavit` and `InitAffidavitKey` offchain
1221// storage for key coordination and state tracking, avoiding duplication.
1222// No additional offchain key-value storage is defined for this routine.
1223
1224// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1225// ````````````````````````````````` ROUTINE OF ``````````````````````````````````
1226// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1227
1228/// Authorization layer for affidavit key rotation.
1229///
1230/// Resolves the **finalized next affidavit key** prepared by
1231/// [`DeclareAffidavit`] and ensures it is locally usable for signing.
1232///
1233/// This enforces that rotation is performed only with a key that:
1234/// - has reached finality, and
1235/// - exists in the node's keystore.
1236impl<T: Config> RoutineOf<T::Public, BlockNumberFor<T>> for RotateAffidavitKey<T> {
1237 /// Determines the next affidavit public key authorized to **finalize key rotation**.
1238 ///
1239 /// ## Semantics
1240 ///
1241 /// - Resolves the **next affidavit key** previously prepared and finalized
1242 /// by the [`DeclareAffidavit`] routine.
1243 /// - Requires the key to be in a [`Confidence::Safe`] state, ensuring it has
1244 /// survived fork re-orgs and met finality requirements.
1245 ///
1246 /// ## Lifecycle position
1247 ///
1248 /// Reaching this routine implies:
1249 ///
1250 /// - The `declare` extrinsic has already been **submitted**, and
1251 /// - The node is now waiting to observe whether that transaction has been
1252 /// **accepted and reflected in runtime storage**.
1253 ///
1254 /// This routine therefore does **not generate or mutate keys**. It only:
1255 /// - re-reads the finalized *next* affidavit key, and
1256 /// - verifies that the corresponding key pair still exists in the local keystore.
1257 ///
1258 /// ## Failure semantics
1259 ///
1260 /// - Missing or non-finalized next affidavit key is treated as a hard stop.
1261 /// - Storage inconsistencies indicate OCW coordination failure and halt execution.
1262 /// - If the key exists in storage but not in the keystore, the node is considered
1263 /// misconfigured.
1264 fn who(at: &BlockNumberFor<T>) -> Result<T::Public, Self::Logger> {
1265 let result = Finalized::<T, AffidavitId<T>, DeclareAffidavit<T>, Pallet<T>>::get(
1266 NEXT_AFDT_KEY,
1267 LOG_TARGET_AFDT,
1268 None,
1269 );
1270
1271 let afdt_key = match result {
1272 Ok(Some(Confidence::Safe(key))) => key,
1273
1274 Ok(None) => {
1275 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1276 &Error::<T>::ExpectedToHoldFinalizedNextAffidavitKey.into(),
1277 *at,
1278 LOG_TARGET_AFDT,
1279 Some(std_fmt::<T>),
1280 ))
1281 }
1282
1283 Ok(Some(_)) => {
1284 return Err(<Self as Logging<BlockNumberFor<T>>>::info(
1285 &Error::<T>::ExpectedToHoldFinalizedNextAffidavitKey.into(),
1286 *at,
1287 LOG_TARGET_AFDT,
1288 Some(std_fmt::<T>),
1289 ))
1290 }
1291
1292 Err(_) => {
1293 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1294 &Error::<T>::OCWStorageDecisionHalt.into(),
1295 *at,
1296 LOG_TARGET_AFDT,
1297 Some(std_fmt::<T>),
1298 ));
1299 }
1300 };
1301
1302 // Ensure the finalized next affidavit key exists in the local keystore
1303 let all_keys =
1304 <<T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::RuntimeAppPublic
1305 as RuntimeAppPublic>::all();
1306
1307 for key in all_keys {
1308 let generic_pub:
1309 <T::AffidavitCrypto as AppCrypto<T::Public, T::Signature>>::GenericPublic =
1310 key.into();
1311 let public: T::Public = generic_pub.into();
1312 let account: AffidavitId<T> = public.clone().into_account().into();
1313
1314 if account == afdt_key {
1315 return Ok(public);
1316 }
1317 }
1318
1319 // Finalized key exists, but no signing key is available locally
1320 Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1321 &Error::<T>::ExpectedNextAffidavitKeyPairNotFound.into(),
1322 *at,
1323 LOG_TARGET_AFDT,
1324 Some(std_fmt::<T>),
1325 ))
1326 }
1327}
1328
1329// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1330// `````````````````````````````````` ROUTINES ```````````````````````````````````
1331// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1332
1333/// Offchain routine responsible for **finalizing affidavit key rotation**.
1334///
1335/// Completes the lifecycle by promoting the finalized next affidavit key
1336/// to active status once the runtime reflects successful affidavit submission.
1337///
1338/// This routine:
1339/// - waits for on-chain confirmation of the rotated key,
1340/// - performs a safe transition from next -> active key,
1341/// - resets state on failure to avoid inconsistent lifecycle progression.
1342///
1343/// Designed to be idempotent and driven by repeated OCW execution.
1344impl<T: Config> Routines<BlockNumberFor<T>> for RotateAffidavitKey<T> {
1345 /// Determines whether affidavit key rotation may be finalized.
1346 ///
1347 /// ## Semantics
1348 ///
1349 /// - Checks whether the **next affidavit key** has been successfully
1350 /// registered in runtime storage for the **session after next**
1351 /// (`current_session + 2`) (for which next election will be conducted).
1352 /// - Presence of this key in [`AffidavitKeys`] is treated as confirmation
1353 /// that the `declare` extrinsic was accepted.
1354 ///
1355 /// ## Waiting behavior
1356 ///
1357 /// - If the key is not yet visible, the routine waits passively.
1358 /// - While the affidavit window is still open, this is logged as
1359 /// an informational "awaiting status" condition.
1360 ///
1361 /// ## Failure and recovery
1362 ///
1363 /// If the affidavit window has **closed** and:
1364 /// - the next affidavit key is still not registered, then
1365 /// - the system assumes the affidavit transaction failed or was dropped.
1366 ///
1367 /// In that case:
1368 /// - All affidavit-related offchain state is cleared.
1369 /// - Active validation is considered stopped.
1370 /// - The node must restart the lifecycle via `validate` extrinsic in a later block.
1371 ///
1372 /// This aggressive reset prevents the OCW from getting stuck
1373 /// in a half-rotated or inconsistent state.
1374 fn can_run(&self) -> Result<(), Self::Logger> {
1375 let current_session = CurrentSession::<T>::get();
1376 let for_session = current_session.saturating_add(2);
1377 let next_afdt_pub: AffidavitId<T> = self.by.clone().into_account().into();
1378
1379 if !AffidavitKeys::<T>::contains_key((for_session, next_afdt_pub)) {
1380 // Determine end of session
1381 let session_start = SessionStartAt::<T>::get();
1382 let avg_session_len =
1383 <<T as crate::Config>::NextSessionRotation as EstimateNextSessionRotation<
1384 BlockNumberFor<T>,
1385 >>::average_session_length();
1386 let end_block = session_start.saturating_add(avg_session_len);
1387
1388 // If window expired, perform hard recovery
1389 if self.at >= end_block {
1390 Finalized::<T, AffidavitId<T>, DeclareAffidavit<T>, Pallet<T>>::remove(
1391 NEXT_AFDT_KEY,
1392 LOG_TARGET_AFDT,
1393 None,
1394 )
1395 .map_err(|_| {
1396 <Self as Logging<BlockNumberFor<T>>>::warn(
1397 &Error::<T>::OCWStorageDecisionHalt.into(),
1398 self.at,
1399 LOG_TARGET_AFDT,
1400 Some(std_fmt::<T>),
1401 )
1402 })?;
1403
1404 Finalized::<T, AffidavitId<T>, InitAffidavitKey<T>, Pallet<T>>::remove(
1405 ACTIVE_AFDT_KEY,
1406 LOG_TARGET_AFDT,
1407 None,
1408 )
1409 .map_err(|_| {
1410 <Self as Logging<BlockNumberFor<T>>>::warn(
1411 &Error::<T>::OCWStorageDecisionHalt.into(),
1412 self.at,
1413 LOG_TARGET_AFDT,
1414 Some(std_fmt::<T>),
1415 )
1416 })?;
1417
1418 return Err(<T as Logging<BlockNumberFor<T>>>::error(
1419 &Error::<T>::ValidationStopped.into(),
1420 self.at,
1421 LOG_TARGET_AFDT,
1422 Some(std_fmt::<T>),
1423 ));
1424 }
1425
1426 // Still waiting for runtime reflection
1427 return Err(<T as Logging<BlockNumberFor<T>>>::debug(
1428 &Error::<T>::AffidavitTxAwaitingStatus.into(),
1429 self.at,
1430 LOG_TARGET_AFDT,
1431 Some(std_fmt::<T>),
1432 ));
1433 }
1434
1435 Ok(())
1436 }
1437
1438 /// Finalizes affidavit key rotation after successful affidavit acceptance.
1439 ///
1440 /// ## Behavior
1441 ///
1442 /// Once the runtime reflects the rotated affidavit key:
1443 ///
1444 /// 1. The **next affidavit key** is removed from finalized offchain storage.
1445 /// 2. The **active affidavit key** is updated to the rotated key.
1446 ///
1447 /// This completes the affidavit lifecycle for the current session and
1448 /// enables subsequent OCW executions to:
1449 /// - attempt elections via [`TryElection`], and
1450 /// - later begin a new affidavit cycle.
1451 ///
1452 /// ## Guarantees
1453 ///
1454 /// - Rotation is performed **exactly once** per successful affidavit.
1455 /// - Any storage failure is treated as a coordination halt.
1456 fn run_service(&self) -> Result<(), Self::Logger> {
1457 Self::can_run(self)?;
1458
1459 Finalized::<T, AffidavitId<T>, DeclareAffidavit<T>, Pallet<T>>::remove(
1460 NEXT_AFDT_KEY,
1461 LOG_TARGET_AFDT,
1462 None,
1463 )
1464 .map_err(|_| {
1465 <Self as Logging<BlockNumberFor<T>>>::warn(
1466 &Error::<T>::OCWStorageDecisionHalt.into(),
1467 self.at,
1468 LOG_TARGET_AFDT,
1469 Some(std_fmt::<T>),
1470 )
1471 })?;
1472
1473 Finalized::<T, AffidavitId<T>, InitAffidavitKey<T>, Pallet<T>>::mutate(
1474 ACTIVE_AFDT_KEY,
1475 |_| Ok(self.by.clone().into_account().into()),
1476 LOG_TARGET_AFDT,
1477 None,
1478 )
1479 .map_err(|_| {
1480 <Self as Logging<BlockNumberFor<T>>>::warn(
1481 &Error::<T>::OCWStorageDecisionHalt.into(),
1482 self.at,
1483 LOG_TARGET_AFDT,
1484 Some(std_fmt::<T>),
1485 )
1486 })?;
1487
1488 Ok(())
1489 }
1490
1491 /// Logs a `info` message on a successful [`RotateAffidavitKey`] routine.
1492 fn on_ran_service(&self) {
1493 <Self as Logging<BlockNumberFor<T>>>::debug(
1494 &Error::<T>::RotateAffidavitKeyRoutineSuccess.into(),
1495 self.at,
1496 LOG_TARGET_AFDT,
1497 Some(std_fmt::<T>),
1498 );
1499 }
1500}
1501
1502// ===============================================================================
1503// ``````````````````````````````` ROUTINES HANDLER ``````````````````````````````
1504// ===============================================================================
1505
1506/// Implements the fork graph management for this pallet's offchain worker.
1507///
1508/// Binds the pallet to the [`ForksHandler`] trait using [`ForkLocalDepot`]
1509/// as the scope type, wiring fork graph constants and error variants defined
1510/// in [`Config`] and [`Error`] into the fork resolution and recovery logic.
1511///
1512/// This impl is the prerequisite for calling [`ForksHandler::start`] inside
1513/// [`Hooks::offchain_worker`](frame_support::traits::Hooks::offchain_worker),
1514/// which resolves the current fork branch before any routine executes.
1515impl<T: Config> ForksHandler<T, ForkLocalDepot> for Pallet<T> {
1516 const TAG: &[u8] = b"pallet_chain_manager";
1517
1518 const MAX_FORKS: u32 = T::MAX_FORKS;
1519
1520 const MAX_RECOVER_TRAVERSAL: u32 = T::MAX_FORK_RECOVERY_TRAVERSAL;
1521
1522 fn max_forks_error() -> DispatchError {
1523 Error::<T>::MaxOCWForksAttained.into()
1524 }
1525
1526 fn forks_not_enabled() -> DispatchError {
1527 Error::<T>::OCWForksNotEnabled.into()
1528 }
1529
1530 fn inconsistent_forks() -> DispatchError {
1531 Error::<T>::OCWForksInconsistent.into()
1532 }
1533}
1534
1535// ===============================================================================
1536// ```````````````````````````````` ROUTINES TESTS ```````````````````````````````
1537// ===============================================================================
1538
1539#[cfg(test)]
1540pub mod tests {
1541
1542 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1543 // ```````````````````````````````````` IMPORTS ``````````````````````````````````
1544 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1545
1546 // --- Local crate imports ---
1547 use crate::mock::*;
1548
1549 // --- Scale-codec crates ---
1550 use codec::Encode;
1551
1552 // --- FRAME Benchmarking ---
1553 use frame_benchmarking::account;
1554
1555 // --- FRAME Suite ---
1556 use frame_suite::routines::*;
1557
1558 // --- FRAME Support ---
1559 use frame_support::{assert_err, assert_ok, traits::{EstimateNextSessionRotation}};
1560
1561 // --- Substrate primitives ---
1562 use sp_core::blake2_256;
1563
1564 // --- std ---
1565 use std::collections::BTreeMap;
1566
1567 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1568 // `````````````````````````````` INIT-AFFIDAVIT-KEY `````````````````````````````
1569 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1570
1571 #[test]
1572 fn init_affidavit_key_can_run_return_err_affidavit_key_exists() {
1573 let mut env = new_ocw_env();
1574 env.ext.execute_with(|| {
1575 init_fork_graph();
1576 let aff_id = generate_affidavit_id();
1577 insert_active_afdt_key(aff_id).unwrap();
1578 run_to_block(20);
1579 let routine = InitAffidavitKey { at: 20 };
1580
1581 assert_err!(routine.can_run(), Error::AffidavitKeyExists);
1582 })
1583 }
1584
1585 #[test]
1586 fn init_affidavit_key_can_run_return_err_active_afdt_key_not_yet_finalized() {
1587 let mut env = new_ocw_env();
1588 env.ext.execute_with(|| {
1589 init_fork_graph();
1590 let aff_id = generate_affidavit_id();
1591 insert_active_afdt_key(aff_id).unwrap();
1592 run_to_block(10);
1593 let routine = InitAffidavitKey { at: 10 };
1594 assert_err!(routine.can_run(), Error::ActiveAfdtKeyNotYetFinalized);
1595 })
1596 }
1597
1598 #[test]
1599 fn init_affidavit_key_can_run_return_ok_since_aff_key_dose_not_exists_in_keystore() {
1600 let mut env = new_ocw_env();
1601 env.ext.execute_with(|| {
1602 init_fork_graph();
1603 let aff_id: AffidavitId = account("dummy", 1, 1);
1604 insert_active_afdt_key(aff_id).unwrap();
1605 run_to_block(20);
1606 let routine = InitAffidavitKey { at: 20 };
1607 assert_ok!(routine.can_run());
1608 })
1609 }
1610
1611 #[test]
1612 fn init_affidavit_key_run_service_returns_ok_since_affidavit_key_already_exists() {
1613 let mut env = new_ocw_env();
1614 env.ext.execute_with(|| {
1615 init_fork_graph();
1616 let aff_id = generate_affidavit_id();
1617 insert_active_afdt_key(aff_id).unwrap();
1618 run_to_block(20);
1619 let routine = InitAffidavitKey { at: 20 };
1620 assert_ok!(routine.run_service());
1621 })
1622 }
1623
1624 #[test]
1625 fn init_affidavit_key_run_service_returns_ok_after_successful_initialization() {
1626 let mut env = new_ocw_env();
1627 env.ext.execute_with(|| {
1628 init_fork_graph();
1629 let routine = InitAffidavitKey { at: 10 };
1630 assert_ok!(routine.can_run());
1631 let aff_key = get_afdt_key();
1632 assert!(aff_key.is_none());
1633 assert_eq!(affidavit_key_count(), 0);
1634 assert_ok!(routine.run_service());
1635 assert_err!(routine.can_run(), Error::ActiveAfdtKeyNotYetFinalized);
1636 run_to_block(30);
1637 assert_err!(routine.can_run(), Error::AffidavitKeyExists);
1638 let aff_key = get_finalized_afdt_key();
1639 assert!(aff_key.is_some());
1640 assert_eq!(affidavit_key_count(), 1);
1641 })
1642 }
1643
1644 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1645 // ````````````````````````````````` TRY-ELECTION ````````````````````````````````
1646 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1647
1648 #[test]
1649 fn try_election_can_run_returns_ok() {
1650 let mut env = new_ocw_env();
1651 env.ext.execute_with(|| {
1652 init_fork_graph();
1653 let aff_id = generate_affidavit_id();
1654 insert_active_afdt_key(aff_id.clone()).unwrap();
1655 let pub_afdt = get_public_key(aff_id.clone()).unwrap();
1656 run_to_block(300);
1657 let election_routine = TryElection {
1658 by: pub_afdt,
1659 at: 300,
1660 };
1661 assert_ok!(election_routine.can_run());
1662 })
1663 }
1664
1665 #[test]
1666 fn try_election_can_run_returns_err_not_affidavit_period() {
1667 let mut env = new_ocw_env();
1668 env.ext.execute_with(|| {
1669 init_fork_graph();
1670 let aff_id = generate_affidavit_id();
1671 insert_active_afdt_key(aff_id.clone()).unwrap();
1672 let pub_afdt = get_public_key(aff_id.clone()).unwrap();
1673 run_to_block(250);
1674 let election_routine = TryElection {
1675 by: pub_afdt,
1676 at: 250,
1677 };
1678 assert_err!(election_routine.can_run(), Error::NotElectionPeriod);
1679 })
1680 }
1681
1682 #[test]
1683 fn try_election_run_service_returns_ok_not_election_window() {
1684 let mut env = new_ocw_env();
1685 env.ext.execute_with(|| {
1686 init_fork_graph();
1687 let aff_id = generate_affidavit_id();
1688 insert_active_afdt_key(aff_id.clone()).unwrap();
1689 let pub_afdt = get_public_key(aff_id.clone()).unwrap();
1690 run_to_block(250);
1691 let election_routine = TryElection {
1692 by: pub_afdt,
1693 at: 250,
1694 };
1695 assert_ok!(election_routine.run_service());
1696 })
1697 }
1698
1699 #[test]
1700 fn try_election_run_service_returns_ok_for_duplicate_election_execution() {
1701 let mut env = new_ocw_env();
1702 env.ext.execute_with(|| {
1703 CurrentSession::put(1);
1704 SessionStartsAt::put(1);
1705 init_fork_graph();
1706 run_to_block(10);
1707 let aff_id = generate_affidavit_id();
1708 insert_active_afdt_key(aff_id.clone()).unwrap();
1709 let pub_afdt = get_public_key(aff_id.clone()).unwrap();
1710
1711 set_default_user_balance_and_hold(ALICE).unwrap();
1712 set_default_user_balance_and_hold(ALAN).unwrap();
1713
1714 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
1715 direct_fund_author(ALAN, ALICE, 500).unwrap();
1716
1717 // Simulate declared affidavit
1718 let for_session = CurrentSession::get() + 2;
1719 let afdt_key = get_afdt_key().unwrap();
1720 AffidavitKeys::insert((for_session, afdt_key), ALICE);
1721 // Simulate election already executed
1722 let for_session = CurrentSession::get() + 1;
1723 ElectsPreparedBy::insert(for_session, (ALICE, 310));
1724
1725 run_to_block(350);
1726 let election_routine = TryElection {
1727 by: pub_afdt,
1728 at: 350,
1729 };
1730 assert_ok!(election_routine.run_service());
1731 let txs_len = env.pool_state.read().transactions.len();
1732 assert_eq!(txs_len, 0);
1733 })
1734 }
1735
1736 #[test]
1737 fn try_election_run_service_returns_ok_when_extrinsic_submitted_succesfully() {
1738 let mut env = new_ocw_env();
1739 env.ext.execute_with(|| {
1740 CurrentSession::put(1);
1741 SessionStartsAt::put(1);
1742 init_fork_graph();
1743 run_to_block(10);
1744 let aff_id = generate_affidavit_id();
1745 insert_active_afdt_key(aff_id.clone()).unwrap();
1746 let pub_afdt = get_public_key(aff_id.clone()).unwrap();
1747
1748 set_default_user_balance_and_hold(ALICE).unwrap();
1749 set_default_user_balance_and_hold(ALAN).unwrap();
1750
1751 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
1752 direct_fund_author(ALAN, ALICE, 500).unwrap();
1753
1754 let aff_window = compute_affidavit_window().unwrap();
1755 let aff_begin = aff_window.start;
1756 run_to_block(aff_begin);
1757 AllowAffidavits::put(true);
1758 let for_session = CurrentSession::get() + 2;
1759 let afdt_key = get_finalized_afdt_key().unwrap();
1760 // declare affidavit simulation
1761 AffidavitKeys::insert((for_session, afdt_key), ALICE);
1762
1763 run_to_block(350);
1764 let election_routine = TryElection {
1765 by: pub_afdt,
1766 at: 350,
1767 };
1768
1769 let tx_len = env.pool_state.read().transactions.len();
1770 assert_eq!(tx_len, 0);
1771 assert_ok!(election_routine.run_service());
1772 let tx_len = env.pool_state.read().transactions.len();
1773 assert_eq!(tx_len, 1);
1774 })
1775 }
1776
1777 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1778 // `````````````````````````````` DECLARE-AFFIDAVIT ``````````````````````````````
1779 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1780
1781 #[test]
1782 fn declare_affidavit_who_returns_public_key() {
1783 let mut env = new_ocw_env();
1784 env.ext.execute_with(|| {
1785 init_fork_graph();
1786 let init_routine = InitAffidavitKey { at: 10 };
1787 init_routine.run_service().unwrap();
1788 run_to_block(30);
1789 let afdt_key = get_finalized_afdt_key().unwrap();
1790 let public_key = get_public_key(afdt_key);
1791 assert!(public_key.is_some());
1792 let pub_key = public_key.unwrap();
1793
1794 let who = DeclareAffidavit::who(&30).unwrap();
1795 assert_eq!(who, pub_key);
1796 })
1797 }
1798
1799 #[test]
1800 fn declare_affidavit_who_returns_err_expected_to_hold_active_affidavit_key() {
1801 let mut env = new_ocw_env();
1802 env.ext.execute_with(|| {
1803 init_fork_graph();
1804 let who = DeclareAffidavit::who(&10);
1805 assert!(who.is_err());
1806 assert_err!(who, Error::ExpectedToHoldActiveAffidavitKey);
1807 })
1808 }
1809
1810 #[test]
1811 fn declare_affidavit_who_returns_err_active_afdt_key_not_yet_finalized() {
1812 let mut env = new_ocw_env();
1813 env.ext.execute_with(|| {
1814 init_fork_graph();
1815 FinalityAfter::put(60_000);
1816 let init_routine = InitAffidavitKey { at: 10 };
1817 init_routine.run_service().unwrap();
1818 run_to_block(15);
1819 let who = DeclareAffidavit::who(&10);
1820 assert!(who.is_err());
1821 assert_err!(who, Error::ActiveAfdtKeyNotYetFinalized);
1822 })
1823 }
1824
1825 #[test]
1826 fn declare_affidavit_who_returns_err_expected_active_affidavit_key_pair_not_found() {
1827 let mut env = new_ocw_env();
1828 env.ext.execute_with(|| {
1829 init_fork_graph();
1830 let aff_id: AffidavitId = account("dummy", 1, 1);
1831 insert_active_afdt_key(aff_id).unwrap();
1832 run_to_block(20);
1833 let who = DeclareAffidavit::who(&10);
1834 assert!(who.is_err());
1835 assert_err!(who, Error::ExpectedActiveAffidavitKeyPairNotFound);
1836 })
1837 }
1838
1839 #[test]
1840 fn declare_affidavit_can_run_returns_ok() {
1841 let mut env = new_ocw_env();
1842 env.ext.execute_with(|| {
1843 SessionStartsAt::put(1);
1844 init_fork_graph();
1845 let init_routine = InitAffidavitKey { at: 10 };
1846 init_routine.run_service().unwrap();
1847 run_to_block(30);
1848 let afdt_key = get_finalized_afdt_key().unwrap();
1849 let public_key = get_public_key(afdt_key.clone());
1850 assert!(public_key.is_some());
1851 let pub_key = public_key.unwrap();
1852
1853 set_default_user_balance_and_hold(ALICE).unwrap();
1854 set_default_user_balance_and_hold(ALAN).unwrap();
1855
1856 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
1857 direct_fund_author(ALAN, ALICE, 500).unwrap();
1858
1859 let aff_window = compute_affidavit_window().unwrap();
1860 let aff_begin = aff_window.start;
1861 run_to_block(aff_begin);
1862 AllowAffidavits::put(true);
1863 let declare_routine = DeclareAffidavit {
1864 by: pub_key,
1865 at: aff_begin + 5,
1866 };
1867
1868 run_to_block(aff_begin + 5);
1869 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
1870 let for_session = CurrentSession::get() + 1;
1871 // Validate
1872 AffidavitKeys::insert((for_session, afdt_key), ALICE);
1873 run_to_block(aff_begin + 30);
1874 assert_ok!(declare_routine.can_run());
1875 })
1876 }
1877
1878 #[test]
1879 fn declate_affidavit_run_service_submits_the_extrinsic_and_returns_ok() {
1880 let mut env = new_ocw_env();
1881 env.ext.execute_with(|| {
1882 init_fork_graph();
1883 SessionStartsAt::put(1);
1884 let init_routine = InitAffidavitKey { at: 10 };
1885 init_routine.run_service().unwrap();
1886 run_to_block(30);
1887 let afdt_key = get_finalized_afdt_key().unwrap();
1888 let public_key = get_public_key(afdt_key.clone());
1889 assert!(public_key.is_some());
1890 let pub_key = public_key.unwrap();
1891
1892 set_default_user_balance_and_hold(ALICE).unwrap();
1893 set_default_user_balance_and_hold(ALAN).unwrap();
1894
1895 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
1896 direct_fund_author(ALAN, ALICE, 500).unwrap();
1897
1898 let aff_window = compute_affidavit_window().unwrap();
1899 let aff_begin = aff_window.start;
1900 run_to_block(aff_begin);
1901 AllowAffidavits::put(true);
1902 let declare_routine = DeclareAffidavit {
1903 by: pub_key,
1904 at: aff_begin + 5,
1905 };
1906
1907 run_to_block(aff_begin + 5);
1908 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
1909 let for_session = CurrentSession::get() + 1;
1910 // Validate
1911 AffidavitKeys::insert((for_session, afdt_key), ALICE);
1912 run_to_block(aff_begin + 30);
1913 let txs_len = env.pool_state.read().transactions.len();
1914 assert_eq!(txs_len, 0);
1915 assert_ok!(declare_routine.run_service());
1916 let txs_len = env.pool_state.read().transactions.len();
1917 assert_eq!(txs_len, 1);
1918 })
1919 }
1920
1921 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1922 // ```````````````````````````` ROTATE-AFFIDAVIT-KEY `````````````````````````````
1923 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1924
1925 #[test]
1926 fn rotate_affidavit_key_who_returns_public_key() {
1927 let mut env = new_ocw_env();
1928 env.ext.execute_with(|| {
1929 SessionStartsAt::put(1);
1930 init_fork_graph();
1931 let init_routine = InitAffidavitKey { at: 10 };
1932 init_routine.run_service().unwrap();
1933 run_to_block(30);
1934 let afdt_key = get_finalized_afdt_key().unwrap();
1935 let public_key = get_public_key(afdt_key.clone());
1936 assert!(public_key.is_some());
1937 let pub_key = public_key.unwrap();
1938
1939 set_default_user_balance_and_hold(ALICE).unwrap();
1940 set_default_user_balance_and_hold(ALAN).unwrap();
1941
1942 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
1943 direct_fund_author(ALAN, ALICE, 500).unwrap();
1944
1945 let aff_window = compute_affidavit_window().unwrap();
1946 let aff_begin = aff_window.start;
1947 run_to_block(aff_begin);
1948 AllowAffidavits::put(true);
1949 let declare_routine = DeclareAffidavit {
1950 by: pub_key,
1951 at: aff_begin + 5,
1952 };
1953
1954 run_to_block(aff_begin + 5);
1955 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
1956 let for_session = CurrentSession::get() + 1;
1957 // Validate
1958 AffidavitKeys::insert((for_session, afdt_key), ALICE);
1959 run_to_block(aff_begin + 30);
1960 let txs_len = env.pool_state.read().transactions.len();
1961 assert_eq!(txs_len, 0);
1962 assert_ok!(declare_routine.run_service());
1963 let txs_len = env.pool_state.read().transactions.len();
1964 assert_eq!(txs_len, 1);
1965
1966 let rotate_afdt_key = get_finalized_next_afdt_key();
1967 assert!(rotate_afdt_key.is_some());
1968 let rotate_afdt_key = rotate_afdt_key.unwrap();
1969 let rotate_pub_key = get_public_key(rotate_afdt_key).unwrap();
1970
1971 let rotate_at = aff_begin + 35;
1972 let _rotate_routine = RotateAffidavitKey {
1973 by: rotate_pub_key.clone(),
1974 at: rotate_at,
1975 };
1976 run_to_block(rotate_at);
1977 let actual_rotate_pub_key = RotateAffidavitKey::who(&rotate_at).unwrap();
1978 assert_eq!(actual_rotate_pub_key, rotate_pub_key);
1979 })
1980 }
1981
1982 #[test]
1983 fn rotate_affidavit_key_who_returns_err_expected_to_hold_finalized_next_afdt_key() {
1984 let mut env = new_ocw_env();
1985 env.ext.execute_with(|| {
1986 SessionStartsAt::put(1);
1987 init_fork_graph();
1988 let init_routine = InitAffidavitKey { at: 10 };
1989 init_routine.run_service().unwrap();
1990 run_to_block(30);
1991 let afdt_key = get_finalized_afdt_key().unwrap();
1992 let public_key = get_public_key(afdt_key.clone());
1993 assert!(public_key.is_some());
1994 let pub_key = public_key.unwrap();
1995
1996 set_default_user_balance_and_hold(ALICE).unwrap();
1997 set_default_user_balance_and_hold(ALAN).unwrap();
1998
1999 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
2000 direct_fund_author(ALAN, ALICE, 500).unwrap();
2001
2002 let aff_window = compute_affidavit_window().unwrap();
2003 let aff_begin = aff_window.start;
2004 run_to_block(aff_begin);
2005 AllowAffidavits::put(true);
2006 let declare_routine = DeclareAffidavit {
2007 by: pub_key,
2008 at: aff_begin + 5,
2009 };
2010
2011 run_to_block(aff_begin + 5);
2012 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
2013 let for_session = CurrentSession::get() + 1;
2014 // Validate
2015 AffidavitKeys::insert((for_session, afdt_key), ALICE);
2016
2017 let rotate_afdt_key = get_next_afdt_key();
2018 assert!(rotate_afdt_key.is_some());
2019 let rotate_afdt_key = rotate_afdt_key.unwrap();
2020 let rotate_pub_key = get_public_key(rotate_afdt_key).unwrap();
2021
2022 let rotate_at = aff_begin + 10;
2023 let _rotate_routine = RotateAffidavitKey {
2024 by: rotate_pub_key.clone(),
2025 at: rotate_at,
2026 };
2027 run_to_block(rotate_at);
2028 assert_err!(
2029 RotateAffidavitKey::who(&rotate_at),
2030 Error::ExpectedToHoldFinalizedNextAffidavitKey
2031 );
2032 })
2033 }
2034
2035 #[test]
2036 fn rotate_affidavit_key_can_run_returns_ok() {
2037 let mut env = new_ocw_env();
2038 env.ext.execute_with(|| {
2039 SessionStartsAt::put(1);
2040 init_fork_graph();
2041 let init_routine = InitAffidavitKey { at: 10 };
2042 init_routine.run_service().unwrap();
2043 run_to_block(30);
2044 let afdt_key = get_finalized_afdt_key().unwrap();
2045 let public_key = get_public_key(afdt_key.clone());
2046 assert!(public_key.is_some());
2047 let pub_key = public_key.unwrap();
2048
2049 set_default_user_balance_and_hold(ALICE).unwrap();
2050 set_default_user_balance_and_hold(ALAN).unwrap();
2051
2052 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
2053 direct_fund_author(ALAN, ALICE, 500).unwrap();
2054
2055 let aff_window = compute_affidavit_window().unwrap();
2056 let aff_begin = aff_window.start;
2057 run_to_block(aff_begin);
2058 AllowAffidavits::put(true);
2059 let declare_routine = DeclareAffidavit {
2060 by: pub_key,
2061 at: aff_begin + 5,
2062 };
2063
2064 run_to_block(aff_begin + 5);
2065 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
2066 let for_session = CurrentSession::get() + 1;
2067 // Validate
2068 AffidavitKeys::insert((for_session, afdt_key), ALICE);
2069 run_to_block(aff_begin + 30);
2070 let txs_len = env.pool_state.read().transactions.len();
2071 assert_eq!(txs_len, 0);
2072 assert_ok!(declare_routine.run_service());
2073 let txs_len = env.pool_state.read().transactions.len();
2074 assert_eq!(txs_len, 1);
2075 let rotate_afdt_key = get_finalized_next_afdt_key();
2076 assert!(rotate_afdt_key.is_some());
2077 let rotate_afdt_key = rotate_afdt_key.unwrap();
2078 let rotate_pub_key = get_public_key(rotate_afdt_key.clone()).unwrap();
2079
2080 let rotate_at = aff_begin + 35;
2081 let rotate_routine = RotateAffidavitKey {
2082 by: rotate_pub_key.clone(),
2083 at: rotate_at,
2084 };
2085 run_to_block(rotate_at);
2086 let for_session = CurrentSession::get() + 2;
2087 // Declare affidavit extrinsic executed simulation
2088 AffidavitKeys::insert((for_session, rotate_afdt_key), ALICE);
2089 assert_ok!(rotate_routine.can_run());
2090 })
2091 }
2092
2093 #[test]
2094 fn rotate_affidavit_key_can_run_returns_err_affidavit_tx_awaiting_status() {
2095 let mut env = new_ocw_env();
2096 env.ext.execute_with(|| {
2097 SessionStartsAt::put(1);
2098 init_fork_graph();
2099 let init_routine = InitAffidavitKey { at: 10 };
2100 init_routine.run_service().unwrap();
2101 run_to_block(30);
2102 let afdt_key = get_finalized_afdt_key().unwrap();
2103 let public_key = get_public_key(afdt_key.clone());
2104 assert!(public_key.is_some());
2105 let pub_key = public_key.unwrap();
2106
2107 set_default_user_balance_and_hold(ALICE).unwrap();
2108 set_default_user_balance_and_hold(ALAN).unwrap();
2109
2110 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
2111 direct_fund_author(ALAN, ALICE, 500).unwrap();
2112
2113 let aff_window = compute_affidavit_window().unwrap();
2114 let aff_begin = aff_window.start;
2115 run_to_block(aff_begin);
2116 AllowAffidavits::put(true);
2117 let declare_routine = DeclareAffidavit {
2118 by: pub_key,
2119 at: aff_begin + 5,
2120 };
2121
2122 run_to_block(aff_begin + 5);
2123 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
2124 let for_session = CurrentSession::get() + 1;
2125 // Validate
2126 AffidavitKeys::insert((for_session, afdt_key), ALICE);
2127 run_to_block(aff_begin + 30);
2128 let txs_len = env.pool_state.read().transactions.len();
2129 assert_eq!(txs_len, 0);
2130 assert_ok!(declare_routine.run_service());
2131 let txs_len = env.pool_state.read().transactions.len();
2132 assert_eq!(txs_len, 1);
2133 let rotate_afdt_key = get_finalized_next_afdt_key();
2134 assert!(rotate_afdt_key.is_some());
2135 let rotate_afdt_key = rotate_afdt_key.unwrap();
2136 let rotate_pub_key = get_public_key(rotate_afdt_key.clone()).unwrap();
2137
2138 let rotate_at = aff_begin + 35;
2139 let rotate_routine = RotateAffidavitKey {
2140 by: rotate_pub_key.clone(),
2141 at: rotate_at,
2142 };
2143 run_to_block(rotate_at);
2144 // Declare affidavit not yet executed
2145 assert_err!(rotate_routine.can_run(), Error::AffidavitTxAwaitingStatus);
2146 })
2147 }
2148
2149 #[test]
2150 fn rotate_affidavit_key_can_run_returns_err_validation_stopped_when_window_expired() {
2151 let mut env = new_ocw_env();
2152 env.ext.execute_with(|| {
2153 SessionStartsAt::put(1);
2154 init_fork_graph();
2155 let init_routine = InitAffidavitKey { at: 10 };
2156 init_routine.run_service().unwrap();
2157 run_to_block(30);
2158 let afdt_key = get_finalized_afdt_key().unwrap();
2159 let public_key = get_public_key(afdt_key.clone());
2160 assert!(public_key.is_some());
2161 let pub_key = public_key.unwrap();
2162
2163 set_default_user_balance_and_hold(ALICE).unwrap();
2164 set_default_user_balance_and_hold(ALAN).unwrap();
2165
2166 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
2167 direct_fund_author(ALAN, ALICE, 500).unwrap();
2168
2169 let aff_window = compute_affidavit_window().unwrap();
2170 let aff_begin = aff_window.start;
2171 run_to_block(aff_begin);
2172 AllowAffidavits::put(true);
2173 let declare_routine = DeclareAffidavit {
2174 by: pub_key,
2175 at: aff_begin + 5,
2176 };
2177
2178 run_to_block(aff_begin + 5);
2179 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
2180 let for_session = CurrentSession::get() + 1;
2181 // Validate
2182 AffidavitKeys::insert((for_session, afdt_key), ALICE);
2183 run_to_block(aff_begin + 30);
2184 let txs_len = env.pool_state.read().transactions.len();
2185 assert_eq!(txs_len, 0);
2186 assert_ok!(declare_routine.run_service());
2187 let txs_len = env.pool_state.read().transactions.len();
2188 assert_eq!(txs_len, 1);
2189 let rotate_afdt_key = get_finalized_next_afdt_key();
2190 assert!(rotate_afdt_key.is_some());
2191 let rotate_afdt_key = rotate_afdt_key.unwrap();
2192 let rotate_pub_key = get_public_key(rotate_afdt_key.clone()).unwrap();
2193
2194 let avg_session_len: BlockNumber = NextSessionRotation::average_session_length();
2195 let rotate_at = avg_session_len + 10;
2196 let rotate_routine = RotateAffidavitKey {
2197 by: rotate_pub_key.clone(),
2198 at: rotate_at,
2199 };
2200 run_to_block(rotate_at);
2201 // Declare affidavit not yet executed and the session window expired
2202 assert_err!(rotate_routine.can_run(), Error::ValidationStopped);
2203 let aff_key = get_afdt_key();
2204 assert!(aff_key.is_none());
2205 let next_aff_key = get_next_afdt_key();
2206 assert!(next_aff_key.is_none());
2207 })
2208 }
2209
2210 #[test]
2211 fn rotate_affidavit_key_run_service_returns_ok() {
2212 let mut env = new_ocw_env();
2213 env.ext.execute_with(|| {
2214 SessionStartsAt::put(1);
2215 init_fork_graph();
2216 let init_routine = InitAffidavitKey { at: 10 };
2217 init_routine.run_service().unwrap();
2218 run_to_block(30);
2219 let afdt_key = get_finalized_afdt_key().unwrap();
2220 let public_key = get_public_key(afdt_key.clone());
2221 assert!(public_key.is_some());
2222 let pub_key = public_key.unwrap();
2223
2224 set_default_user_balance_and_hold(ALICE).unwrap();
2225 set_default_user_balance_and_hold(ALAN).unwrap();
2226
2227 enroll_authors_with_default_collateral(vec![ALICE]).unwrap();
2228 direct_fund_author(ALAN, ALICE, 500).unwrap();
2229
2230 let aff_window = compute_affidavit_window().unwrap();
2231 let aff_begin = aff_window.start;
2232 run_to_block(aff_begin);
2233 AllowAffidavits::put(true);
2234 let declare_routine = DeclareAffidavit {
2235 by: pub_key,
2236 at: aff_begin + 5,
2237 };
2238
2239 run_to_block(aff_begin + 5);
2240 assert_err!(declare_routine.can_run(), Error::NextAfdtKeyNotYetFinalized);
2241 let for_session = CurrentSession::get() + 1;
2242 // Validate
2243 AffidavitKeys::insert((for_session, afdt_key), ALICE);
2244 run_to_block(aff_begin + 30);
2245 let txs_len = env.pool_state.read().transactions.len();
2246 assert_eq!(txs_len, 0);
2247 assert_ok!(declare_routine.run_service());
2248 let txs_len = env.pool_state.read().transactions.len();
2249 assert_eq!(txs_len, 1);
2250 let rotate_afdt_key = get_finalized_next_afdt_key();
2251 assert!(rotate_afdt_key.is_some());
2252 let rotate_afdt_key = rotate_afdt_key.unwrap();
2253 let rotate_pub_key = get_public_key(rotate_afdt_key.clone()).unwrap();
2254
2255 let rotate_at = aff_begin + 35;
2256 let rotate_routine = RotateAffidavitKey {
2257 by: rotate_pub_key.clone(),
2258 at: rotate_at,
2259 };
2260 run_to_block(rotate_at);
2261 let for_session = CurrentSession::get() + 2;
2262 // Declare affidavit extrinsic executed simulation
2263 AffidavitKeys::insert((for_session, rotate_afdt_key.clone()), ALICE);
2264
2265 assert_ok!(rotate_routine.run_service());
2266 let next_afdt_key = get_next_afdt_key();
2267 assert!(next_afdt_key.is_none());
2268 let afdt_key = get_afdt_key();
2269 assert!(afdt_key.is_some());
2270 assert_eq!(afdt_key.unwrap(), rotate_afdt_key);
2271 })
2272 }
2273
2274 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2275 // `````````````````````````` FINALIZED (OFFCHAIN-STORE) `````````````````````````
2276 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2277 #[test]
2278 fn finalized_insert_success() {
2279 let mut env = new_ocw_env();
2280 env.ext.execute_with(|| {
2281 init_fork_graph();
2282 let key = b"ACTIVE_KEY";
2283 let value: AccountId = account("dummyid", 1, 1);
2284 assert_ok!(FinalizedInitAfdtKey::insert(key, &value, None, None));
2285
2286 let fork_aware_value = ForkAwareInitAfdtKey::get(key, None, None).unwrap();
2287 assert!(fork_aware_value.is_some());
2288 let value_hash = fork_aware_value.unwrap();
2289
2290 let persistent_value = PersistentInitAfdtKey::get(key, None, None).unwrap();
2291 assert!(persistent_value.is_some());
2292 let ledger = persistent_value.unwrap();
2293 let observation = ledger.0.get(&value_hash).unwrap();
2294 assert_eq!(observation.first_seen, 12000);
2295 assert_eq!(observation.last_seen, 12000);
2296 assert_eq!(observation.blocks_seen, 0);
2297 assert_eq!(observation.value, value);
2298 })
2299 }
2300
2301 #[test]
2302 fn finalized_get_success() {
2303 let mut env = new_ocw_env();
2304 env.ext.execute_with(|| {
2305 let key = b"ACTIVE_KEY";
2306 init_fork_graph();
2307 let value: AccountId = account("dummyid", 1, 1);
2308 assert_ok!(FinalizedInitAfdtKey::insert(key, &value, None, None));
2309
2310 // since, the first_seen + finalty_after > last_seen and obs_block < finalty_ticks
2311 // confidance = Unsafe(value)
2312 let finalized_get = FinalizedInitAfdtKey::get(key, None, None).unwrap();
2313 assert!(finalized_get.is_some());
2314 let confidance_value = finalized_get.unwrap();
2315 assert_eq!(confidance_value, Confidence::Unsafe(value.clone()));
2316
2317 // since, the first_seen + finalty_after < last_seen and obs_block < finalty_ticks
2318 // confidance = Risky(value)
2319 run_to_block_with_finalized_key(12, key);
2320 let finalized_get = FinalizedInitAfdtKey::get(key, None, None).unwrap();
2321 assert!(finalized_get.is_some());
2322 let confidance_value = finalized_get.unwrap();
2323 assert_eq!(confidance_value, Confidence::Risky(value.clone()));
2324
2325 // since, the first_seen + finalty_after < last_seen and obs_block >= finalty_ticks
2326 // confidance = Safe(value)
2327 run_to_block_with_finalized_key(17, key);
2328 let finalized_get = FinalizedInitAfdtKey::get(key, None, None).unwrap();
2329 assert!(finalized_get.is_some());
2330 let confidance_value = finalized_get.unwrap();
2331 assert_eq!(confidance_value, Confidence::Safe(value));
2332 })
2333 }
2334
2335 #[test]
2336 fn finalized_get_returns_err_due_to_hanging_value() {
2337 let mut env = new_ocw_env();
2338 env.ext.execute_with(|| {
2339 init_fork_graph();
2340 let key = b"ACTIVE_KEY";
2341 let value: AccountId = account("dummyid", 1, 1);
2342 let hash = blake2_256(&value.encode());
2343 let value_hash = ValueHash::new(hash);
2344 assert_ok!(ForkAwareInitAfdtKey::insert(key, &value_hash, None, None));
2345
2346 let finalized_get = FinalizedInitAfdtKey::get(key, None, None);
2347 assert!(finalized_get.is_err());
2348 let e = finalized_get.unwrap_err();
2349 assert_eq!(e, Error::ActiveAfdtKeyFinalizedHangingValue.into());
2350
2351 // forkaware entry cleaned due to hanging value
2352 let forkaware_lookup = ForkAwareInitAfdtKey::get(key, None, None).unwrap();
2353 assert!(forkaware_lookup.is_none());
2354 })
2355 }
2356
2357 #[test]
2358 fn finalized_get_returns_err_due_to_hanging_hash() {
2359 let mut env = new_ocw_env();
2360 env.ext.execute_with(|| {
2361 init_fork_graph();
2362 let key = b"ACTIVE_KEY";
2363 let value: AccountId = account("dummyid", 1, 1);
2364 let hash = blake2_256(&value.encode());
2365 let value_hash = ValueHash::new(hash);
2366
2367 assert_ok!(ForkAwareInitAfdtKey::insert(key, &value_hash, None, None));
2368
2369 let other_value: AccountId = account("otherdummyid", 1, 1);
2370 let other_hash = blake2_256(&other_value.encode());
2371 let other_value_hash = ValueHash::new(other_hash);
2372
2373 let observation = Observation::<Test, AccountId> {
2374 first_seen: 6000,
2375 last_seen: 6000,
2376 blocks_seen: 0,
2377 value: other_value,
2378 };
2379
2380 let mut map = BTreeMap::new();
2381 map.insert(other_value_hash, observation);
2382
2383 let ledger = Ledger::<Test, AccountId>(map);
2384
2385 assert_ok!(PersistentInitAfdtKey::insert(
2386 key,
2387 &ledger,
2388 None,
2389 None
2390 ));
2391
2392 let finalized_get = FinalizedInitAfdtKey::get(key, None, None);
2393 assert!(finalized_get.is_err());
2394
2395 let e = finalized_get.unwrap_err();
2396 assert_eq!(e, Error::ActiveAfdtKeySpeculativeHangingHash.into());
2397
2398 let forkaware_lookup = ForkAwareInitAfdtKey::get(key, None, None).unwrap();
2399 assert!(forkaware_lookup.is_none());
2400 let persistent = PersistentInitAfdtKey::get(key, None, None).unwrap();
2401 assert!(persistent.is_some());
2402 })
2403 }
2404
2405 #[test]
2406 fn finalized_remove_success() {
2407 let mut env = new_ocw_env();
2408 env.ext.execute_with(|| {
2409 init_fork_graph();
2410 let key = b"ACTIVE_KEY";
2411 let value: AccountId = account("dummyid", 1, 1);
2412 assert_ok!(FinalizedInitAfdtKey::insert(key, &value, None, None));
2413
2414 let finalized_inspect = FinalizedInitAfdtKey::get(key, None, None).unwrap();
2415 assert!(finalized_inspect.is_some());
2416
2417 assert_ok!(FinalizedInitAfdtKey::remove(key, None, None));
2418
2419 let finalized_inspect = FinalizedInitAfdtKey::get(key, None, None).unwrap();
2420 assert!(finalized_inspect.is_none());
2421 })
2422 }
2423
2424 #[test]
2425 fn finalized_mutate_success() {
2426 let mut env = new_ocw_env();
2427 env.ext.execute_with(|| {
2428 init_fork_graph();
2429 let key = b"ACTIVE_KEY";
2430 let value: AccountId = account("dummyid", 1, 1);
2431 assert_ok!(FinalizedInitAfdtKey::insert(key, &value, None, None));
2432
2433 let new_val: AccountId = account("newdummyid", 1, 1);
2434 assert_ok!(FinalizedInitAfdtKey::mutate(
2435 key,
2436 |val| {
2437 match val {
2438 Ok(Some(_v)) => Ok(new_val.clone()),
2439 Ok(None) => Ok(new_val.clone()),
2440 Err(e) => Err(e),
2441 }
2442 },
2443 None,
2444 None
2445 ));
2446
2447 let finalized_get = FinalizedInitAfdtKey::get(key, None, None).unwrap();
2448 let new_finalized_value = match finalized_get {
2449 Some(new_val) => match new_val {
2450 Confidence::Unsafe(id) => id,
2451 _ => value,
2452 },
2453 None => value,
2454 };
2455
2456 assert_eq!(new_finalized_value, new_val);
2457 let persistent = PersistentInitAfdtKey::get(key, None, None).unwrap().unwrap();
2458 let (_, obs) = persistent.0.iter().next().unwrap();
2459 assert_eq!(obs.blocks_seen, 0);
2460 assert_eq!(obs.value, new_val);
2461 })
2462 }
2463}
2464
2465// ===============================================================================
2466// ````````````````````````````````` SERIAL TESTS ````````````````````````````````
2467// ===============================================================================
2468
2469// These tests are isolated in a dedicated module because they rely on a **global logger state**.
2470// Rust tests run in parallel by default, which causes race conditions and flaky failures when
2471// multiple tests attempt to read/write from the same global logger.
2472//
2473// To avoid this, each test in this module is:
2474// - Annotated with `#[serial]` (from the `serial_test` crate) to enforce sequential execution
2475// - Marked with `#[ignore]` to prevent accidental execution during standard test runs
2476//
2477// Running these tests:
2478// --------------------
2479// Since they are ignored by default, you must run them explicitly and ensure single-threaded
2480// execution:
2481//
2482// `cargo test serial_tests -- --ignored --test-threads=1`
2483//
2484#[cfg(test)]
2485mod serial_tests {
2486 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2487 // ``````````````````````````````````` IMPORTS ```````````````````````````````````
2488 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2489 // --- Local crate imports ---
2490 use crate::mock::*;
2491
2492 // --- Test Utils ---
2493 use serial_test::serial;
2494
2495 // --- FRAME Suite ---
2496 use frame_suite::routines::*;
2497
2498 // --- FRAME Support ---
2499 use frame_support::assert_ok;
2500
2501 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2502 // ``````````````````````````````````` LOGGING ```````````````````````````````````
2503 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2504
2505 #[serial]
2506 #[test]
2507 #[ignore = "relies on global logger; fails under parallel test execution"]
2508 fn init_affidavit_key_on_ran_service() {
2509 chain_manager_test_ext().execute_with(|| {
2510 let logger = init_logger();
2511
2512 System::set_block_number(105);
2513 let init_afdt_routine = InitAffidavitKey { at: 105 };
2514 InitAffidavitKey::on_ran_service(&init_afdt_routine);
2515
2516 let record = logger.last().unwrap();
2517 assert_eq!(record.target(), "AFFIDAVIT");
2518 assert_eq!(record.level(), log::Level::Debug);
2519 let expected_msg = format!(
2520 "๐งฑ [105] ๐ [Debug] ๐ฏ [AFFIDAVIT] ๐งพ Module(9): InitAffidavitKeyRoutineSuccess"
2521 );
2522 let actual_msg = record.args().to_string();
2523 assert_eq!(actual_msg, expected_msg);
2524 })
2525 }
2526
2527 #[serial]
2528 #[test]
2529 #[ignore = "relies on global logger; fails under parallel test execution"]
2530 fn try_election_on_ran_service() {
2531 let mut env = new_ocw_env();
2532 env.ext.execute_with(|| {
2533 let logger = init_logger();
2534
2535 System::set_block_number(105);
2536 let by = generate_affidavit_keypair();
2537 let try_election_routine = TryElection { by: by, at: 105 };
2538 TryElection::on_ran_service(&try_election_routine);
2539
2540 let record = logger.last().unwrap();
2541 assert_eq!(record.target(), "ELECTION");
2542 assert_eq!(record.level(), log::Level::Debug);
2543 let expected_msg =
2544 format!("๐งฑ [105] ๐ [Debug] ๐ฏ [ELECTION] ๐งพ Module(9): TryElectionRoutineSuccess");
2545 let actual_msg = record.args().to_string();
2546 assert_eq!(actual_msg, expected_msg);
2547 })
2548 }
2549
2550 #[serial]
2551 #[test]
2552 #[ignore = "relies on global logger; fails under parallel test execution"]
2553 fn declare_affidavit_on_ran_service() {
2554 let mut env = new_ocw_env();
2555 env.ext.execute_with(|| {
2556 let logger = init_logger();
2557
2558 System::set_block_number(105);
2559 let by = generate_affidavit_keypair();
2560 let declare_afdt_routine = DeclareAffidavit { by, at: 105 };
2561 DeclareAffidavit::on_ran_service(&declare_afdt_routine);
2562
2563 let record = logger.last().unwrap();
2564 assert_eq!(record.target(), "AFFIDAVIT");
2565 assert_eq!(record.level(), log::Level::Debug);
2566 let expected_msg = format!(
2567 "๐งฑ [105] ๐ [Debug] ๐ฏ [AFFIDAVIT] ๐งพ Module(9): DeclarAffidavitRoutineSuccess"
2568 );
2569 let actual_msg = record.args().to_string();
2570 assert_eq!(actual_msg, expected_msg);
2571 })
2572 }
2573
2574 #[serial]
2575 #[test]
2576 #[ignore = "relies on global logger; fails under parallel test execution"]
2577 fn rotate_affidavit_key_on_ran_service() {
2578 let mut env = new_ocw_env();
2579 env.ext.execute_with(|| {
2580 let logger = init_logger();
2581
2582 System::set_block_number(105);
2583 let by = generate_affidavit_keypair();
2584 let rotate_afdt_routine = RotateAffidavitKey { by, at: 105 };
2585 RotateAffidavitKey::on_ran_service(&rotate_afdt_routine);
2586
2587 let record = logger.last().unwrap();
2588 assert_eq!(record.target(), "AFFIDAVIT");
2589 assert_eq!(record.level(), log::Level::Debug);
2590 let expected_msg = format!(
2591 "๐งฑ [105] ๐ [Debug] ๐ฏ [AFFIDAVIT] ๐งพ Module(9): RotateAffidavitKeyRoutineSuccess"
2592 );
2593 let actual_msg = record.args().to_string();
2594 assert_eq!(actual_msg, expected_msg);
2595 })
2596 }
2597
2598 #[serial]
2599 #[test]
2600 #[ignore = "relies on global logger; fails under parallel test execution"]
2601 fn try_election_run_service_returns_ok_when_active_afdt_key_is_not_declared_yet() {
2602 let mut env = new_ocw_env();
2603 env.ext.execute_with(|| {
2604 init_fork_graph();
2605 let log_record = init_logger();
2606 let aff_id = generate_affidavit_id();
2607 insert_active_afdt_key(aff_id.clone()).unwrap();
2608 let pub_afdt = get_public_key(aff_id.clone()).unwrap();
2609 run_to_block(300);
2610 let election_routine = TryElection {
2611 by: pub_afdt,
2612 at: 300,
2613 };
2614 assert_ok!(election_routine.run_service());
2615
2616 let last_log = log_record.last().unwrap();
2617 assert_eq!(last_log.level(), log::Level::Debug);
2618 assert!(last_log
2619 .args()
2620 .to_string()
2621 .contains("AffidavitKeyForDeclaration"));
2622 assert_eq!(last_log.target(), "ELECTION");
2623 })
2624 }
2625}