frame_suite/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// ``````````````````````````` OFFCHAIN ROUTINES SUITE ```````````````````````````
14// ===============================================================================
15
16//! Best-effort execution framework for offchain routines with
17//! explicit logging and storage semantics.
18//!
19//! In FRAME, most logic is executed via dispatchable extrinsics or inherents,
20//! both of which execute transactionally and revert on failure.
21//!
22//! Offchain workers operate outside that model: they run asynchronously,
23//! without rollback, and with only best-effort guarantees.
24//!
25//! This module introduces **routines** as a structured way to write such logic,
26//! where each routine is context-driven and multiple routines can be composed
27//! and executed under best-effort guarantees.
28//!
29//! # What routines provide
30//!
31//! - A disciplined execution model via [`Routines`]
32//! - Per-routine semantics, including authorization through [`RoutineOf`]
33//! and domain-specific error policies
34//! - Composability, allowing multiple routines to run independently under
35//! best-effort guarantees
36//!
37//! Unlike extrinsics, routines are not atomic. Failures are local and do not
38//! prevent other routines from executing.
39//!
40//! # Logging over rollback
41//!
42//! In the absence of transactional guarantees, routines rely on [`Logging`]:
43//!
44//! - errors are recorded and returned, not used to revert execution
45//! - observability replaces rollback as the primary debugging mechanism
46//!
47//! # Storage as execution semantics
48//!
49//! Routines can integrate with [`KeyValueStore`] abstractions backed by
50//! offchain storage models such as [`Persistent`], [`ForkAware`], and [`Finalized`].
51//!
52//! These make fork behavior explicit and allow safe state handling across
53//! re-orgs and repeated execution.
54//!
55//! # Summary
56//!
57//! Routines provide a structured, context-aware model for offchain execution:
58//! - best-effort instead of transactional
59//! - logging-driven instead of rollback-driven
60//! - explicit in both execution and storage semantics
61
62// ===============================================================================
63// ``````````````````````````````````` IMPORTS ```````````````````````````````````
64// ===============================================================================
65
66// --- Local crate imports ---
67use crate::base::{Elastic, Portable, Probe, RuntimeEnum, RuntimeError, Time};
68
69// --- Scale-codec crates ---
70use codec::{Decode, Encode, MaxEncodedLen};
71use scale_info::{
72 prelude::{
73 format,
74 string::{String, ToString},
75 },
76 TypeInfo,
77};
78
79// --- Core / Std ---
80use core::marker::PhantomData;
81
82// --- FRAME System ---
83use frame_system::pallet_prelude::BlockNumberFor;
84
85// --- Substrate primitives ---
86use sp_core::blake2_256;
87use sp_runtime::{
88 offchain::storage::{MutateStorageError, StorageValueRef},
89 traits::{Debug, One, Saturating, Zero},
90 DispatchError,
91};
92
93// --- Substrate std (no_std helpers) ---
94use sp_std::collections::btree_map::BTreeMap;
95
96// ===============================================================================
97// ``````````````````````````````````` LOGGING ```````````````````````````````````
98// ===============================================================================
99
100/// Defines the function signature that can be passed to the logging system
101/// to **customize how log messages are formatted**.
102///
103/// ## Params
104/// - `TS`: The type of the timestamp (e.g., block number, system time, or any type
105/// implementing [`Debug`]).
106/// - `L`: The type representing log level (e.g., [`LogLevel`]).
107/// - `target` (`&str`): the logging target, typically the module or subsystem.
108/// - `message` (`&str`): the main log message.
109///
110/// It returns a `String` containing the fully formatted log line.
111///
112/// This allows the caller to completely customize the log output format, e.g.,
113/// changing the order, adding emojis, colors, or any additional context.
114pub type LogFormatter<TS, L> = fn(timestamp: TS, level: &L, target: &str, message: &str) -> String;
115
116/// Trait for structured logging in detached, asynchronous routines.
117///
118/// This trait is intended for use in detached, asynchronous [`Routines`] or functions
119/// where errors are handled gracefully rather than propagated. The logging system
120/// records errors, warnings, info, or debug messages without affecting control flow.
121///
122/// ## Type Parameters
123/// - `Timestamp`: The type used for timestamps in logs.
124pub trait Logging<Timestamp>
125where
126 Timestamp: Time,
127{
128 /// The error/logging type propagated through the API.
129 ///
130 /// Logging responsibility is directional:
131 ///
132 /// - If a `Logger` is returned from the provider (i.e., received by the caller),
133 /// it has already been logged.
134 /// - If a `Logger` is constructed by the caller and returned to the provider,
135 /// it is expected that the provider will perform the logging.
136 type Logger: Elastic + RuntimeEnum;
137
138 /// The log level type (Info/Warn/Error/Debug)
139 type Level: RuntimeEnum + From<&'static str>;
140
141 /// Default log target if none is provided.
142 ///
143 /// The **log target** is a label that identifies the source of a log message,
144 /// typically a pallet, module, or subsystem. It helps to categorize and filter
145 /// logs, making it easier to trace where messages come from in a complex runtime.
146 ///
147 /// For example, in a log line like:
148 /// `[12345][INFO][pallet_template] Templating period has not started`
149 ///
150 /// - `12345` is the timestamp/block number
151 /// - `INFO` is the log level
152 /// - `pallet_template` is the **log target**
153 /// - The rest is the message
154 ///
155 /// If the caller does not provide a target, the `FALLBACK_TARGET` constant is
156 /// used as a default to ensure all logs have a meaningful source label.
157 const FALLBACK_TARGET: &'static str;
158
159 /// Core logging function that all helpers delegate to.
160 ///
161 /// This central function ensures consistent structure and formatting
162 /// across all log messages.
163 ///
164 /// The optional `LogFormatter` lets you override the default output style.
165 ///
166 /// A formatter could add JSON structure, include node metadata, or embed
167 /// contextual tags.
168 fn log(
169 level: Self::Level,
170 err: &Self::Logger,
171 timestamp: Timestamp,
172 target: Option<&str>,
173 fmt: Option<LogFormatter<Timestamp, Self::Level>>,
174 ) -> Self::Logger;
175
176 /// Logs an info-level message.
177 ///
178 /// Includes an optional custom formatter and log target.
179 #[inline]
180 fn info(
181 err: &Self::Logger,
182 timestamp: Timestamp,
183 target: Option<&str>,
184 fmt: Option<LogFormatter<Timestamp, Self::Level>>,
185 ) -> Self::Logger
186 where
187 Self: Sized,
188 {
189 Self::log(Self::Level::from("info"), err, timestamp, target, fmt)
190 }
191
192 /// Logs a warning-level message.
193 ///
194 /// Includes an optional custom formatter and log target.
195 #[inline]
196 fn warn(
197 err: &Self::Logger,
198 timestamp: Timestamp,
199 target: Option<&str>,
200 fmt: Option<LogFormatter<Timestamp, Self::Level>>,
201 ) -> Self::Logger
202 where
203 Self: Sized,
204 {
205 Self::log(Self::Level::from("warn"), err, timestamp, target, fmt)
206 }
207
208 /// Logs an error-level message.
209 ///
210 /// Includes an optional custom formatter and log target.
211 #[inline]
212 fn error(
213 err: &Self::Logger,
214 timestamp: Timestamp,
215 target: Option<&str>,
216 fmt: Option<LogFormatter<Timestamp, Self::Level>>,
217 ) -> Self::Logger
218 where
219 Self: Sized,
220 {
221 Self::log(Self::Level::from("error"), err, timestamp, target, fmt)
222 }
223
224 /// Logs a debug-level message.
225 ///
226 /// Includes an optional custom formatter and log target.
227 #[inline]
228 fn debug(
229 err: &Self::Logger,
230 timestamp: Timestamp,
231 target: Option<&str>,
232 fmt: Option<LogFormatter<Timestamp, Self::Level>>,
233 ) -> Self::Logger
234 where
235 Self: Sized,
236 {
237 Self::log(Self::Level::from("debug"), err, timestamp, target, fmt)
238 }
239}
240
241/// Blanket implementation of `Logging` for any runtime-type.
242impl<T, Time> Logging<Time> for T
243where
244 Time: crate::Time,
245{
246 /// The type taken and returned for logging.
247 ///
248 /// We simply return the same [`DispatchError`] that was logged,
249 /// so logging does not change control flow or error propagation.
250 ///
251 /// `DispatchError` is used because in Substrate it encompasses **all**
252 /// runtime errors - including module errors, token errors, arithmetic
253 /// issues, and transactional boundaries - making it the universal
254 /// substrate-side error representation.
255 type Logger = DispatchError;
256
257 /// The log level type.
258 ///
259 /// We use the `LogLevel` enum to standardize severity levels
260 /// (Info, Warn, Error, Debug) across all routine logs.
261 type Level = LogLevel;
262
263 /// Default logging target if none is provided.
264 ///
265 /// Most routines, especially offchain workers or background tasks, use this target
266 /// for simplicity.
267 ///
268 /// It allows a consistent place to look for routine logs without requiring every
269 /// call to specify a target.
270 ///
271 /// ️**Note**: This target is only a convenience and may be somewhat vague.
272 /// To ensure errors can still be traced accurately, the logged messages should
273 /// include additional metadata (e.g., module name, error index, or contextual info)
274 /// so that the source of the error can be identified even if the target is generic.
275 const FALLBACK_TARGET: &str = "routine";
276
277 fn log(
278 level: Self::Level,
279 err: &Self::Logger,
280 timestamp: Time,
281 target: Option<&str>,
282 fmt: Option<LogFormatter<Time, Self::Level>>,
283 ) -> Self::Logger {
284 use log::{debug, error, info, warn};
285
286 // Determine the actual logging target
287 let actual_target = target.unwrap_or(<Self as Logging<Time>>::FALLBACK_TARGET);
288
289 // Convert the DispatchError into a human-readable message
290 let message = match err {
291 DispatchError::Other(str) => str.to_string(),
292 DispatchError::Module(module_error) => {
293 if let Some(msg) = module_error.message {
294 format!("Module({}): {}", module_error.index, msg)
295 } else {
296 // fallback to raw bytes if no message available
297 format!(
298 "Module({}) raw error: {:?}",
299 module_error.index, module_error.error
300 )
301 }
302 }
303 DispatchError::CannotLookup => "CannotLookup".to_string(),
304 DispatchError::BadOrigin => "BadOrigin".to_string(),
305 DispatchError::ConsumerRemaining => "ConsumerRemaining".to_string(),
306 DispatchError::NoProviders => "NoProviders".to_string(),
307 DispatchError::TooManyConsumers => "TooManyConsumers".to_string(),
308 DispatchError::Token(token_error) => match token_error {
309 sp_runtime::TokenError::FundsUnavailable => {
310 "TokenError: FundsUnavailable".to_string()
311 }
312 sp_runtime::TokenError::OnlyProvider => "TokenError: OnlyProvider".to_string(),
313 sp_runtime::TokenError::BelowMinimum => "TokenError: BelowMinimum".to_string(),
314 sp_runtime::TokenError::CannotCreate => "TokenError: CannotCreate".to_string(),
315 sp_runtime::TokenError::UnknownAsset => "TokenError: UnknownAsset".to_string(),
316 sp_runtime::TokenError::Frozen => "TokenError: Frozen".to_string(),
317 sp_runtime::TokenError::Unsupported => "TokenError: Unsupported".to_string(),
318 sp_runtime::TokenError::CannotCreateHold => {
319 "TokenError: CannotCreateHold".to_string()
320 }
321 sp_runtime::TokenError::NotExpendable => "TokenError: NotExpendable".to_string(),
322 sp_runtime::TokenError::Blocked => "TokenError: Blocked".to_string(),
323 },
324 DispatchError::Arithmetic(arithmetic_error) => match arithmetic_error {
325 sp_runtime::ArithmeticError::Underflow => "ArithmeticError: Underflow".to_string(),
326 sp_runtime::ArithmeticError::Overflow => "ArithmeticError: Overflow".to_string(),
327 sp_runtime::ArithmeticError::DivisionByZero => {
328 "ArithmeticError: DivisionByZero".to_string()
329 }
330 },
331 DispatchError::Transactional(transactional_error) => match transactional_error {
332 sp_runtime::TransactionalError::LimitReached => {
333 "TransactionalError: LimitReached".to_string()
334 }
335 sp_runtime::TransactionalError::NoLayer => {
336 "TransactionalError: NoLayer".to_string()
337 }
338 },
339 DispatchError::Exhausted => "Exhausted".to_string(),
340 DispatchError::Corruption => "Corruption".to_string(),
341 DispatchError::Unavailable => "Unavailable".to_string(),
342 DispatchError::RootNotAllowed => "RootNotAllowed".to_string(),
343 DispatchError::Trie(trie_error) => match trie_error {
344 frame_support::traits::TrieError::InvalidStateRoot => {
345 "TrieError: InvalidStateRoot".to_string()
346 }
347 frame_support::traits::TrieError::IncompleteDatabase => {
348 "TrieError: IncompleteDatabase".to_string()
349 }
350 frame_support::traits::TrieError::ValueAtIncompleteKey => {
351 "TrieError: ValueAtIncompleteKey".to_string()
352 }
353 frame_support::traits::TrieError::DecoderError => {
354 "TrieError: DecoderError".to_string()
355 }
356 frame_support::traits::TrieError::InvalidHash => {
357 "TrieError: InvalidHash".to_string()
358 }
359 frame_support::traits::TrieError::DuplicateKey => {
360 "TrieError: DuplicateKey".to_string()
361 }
362 frame_support::traits::TrieError::ExtraneousNode => {
363 "TrieError: ExtraneousNode".to_string()
364 }
365 frame_support::traits::TrieError::ExtraneousValue => {
366 "TrieError: ExtraneousValue".to_string()
367 }
368 frame_support::traits::TrieError::ExtraneousHashReference => {
369 "TrieError: ExtraneousHashReference".to_string()
370 }
371 frame_support::traits::TrieError::InvalidChildReference => {
372 "TrieError: InvalidChildReference".to_string()
373 }
374 frame_support::traits::TrieError::ValueMismatch => {
375 "TrieError: ValueMismatch".to_string()
376 }
377 frame_support::traits::TrieError::IncompleteProof => {
378 "TrieError: IncompleteProof".to_string()
379 }
380 frame_support::traits::TrieError::RootMismatch => {
381 "TrieError: RootMismatch".to_string()
382 }
383 frame_support::traits::TrieError::DecodeError => {
384 "TrieError: DecodeError".to_string()
385 }
386 },
387 };
388
389 // Apply optional custom formatting or default format
390 let log_line = if let Some(f) = fmt {
391 f(timestamp, &level, actual_target, &message)
392 } else {
393 format!(
394 "[{:?}][{}][{}] {}",
395 timestamp,
396 level.as_str(),
397 actual_target,
398 message
399 )
400 };
401
402 // Emit the log using the appropriate level macro
403 match level {
404 LogLevel::Error => error!(target: actual_target, "{}", log_line),
405 LogLevel::Warn => warn!(target: actual_target, "{}", log_line),
406 LogLevel::Debug => debug!(target: actual_target, "{}", log_line),
407 LogLevel::Info => info!(target: actual_target, "{}", log_line),
408 }
409
410 // Return the original error for convenience
411 *err
412 }
413}
414
415// ===============================================================================
416// `````````````````````````````` LOGGING UTILITIES ``````````````````````````````
417// ===============================================================================
418
419/// Represents log severity levels.
420#[derive(Clone, Copy, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
421pub enum LogLevel {
422 /// Informational messages
423 Info,
424 /// Warnings
425 Warn,
426 /// Errors
427 Error,
428 /// Debug-level messages
429 Debug,
430}
431
432impl From<&'static str> for LogLevel {
433 /// Converts a string literal into a `LogLevel`.
434 /// Defaults to `Debug` for unrecognized strings.
435 fn from(s: &'static str) -> Self {
436 match s {
437 "warn" => LogLevel::Warn,
438 "error" => LogLevel::Error,
439 "debug" => LogLevel::Debug,
440 "info" => LogLevel::Info,
441 _ => LogLevel::Debug,
442 }
443 }
444}
445
446impl LogLevel {
447 /// Returns the string representation of the log level.
448 pub fn as_str(&self) -> &'static str {
449 match self {
450 LogLevel::Info => "INFO",
451 LogLevel::Warn => "WARN",
452 LogLevel::Error => "ERROR",
453 LogLevel::Debug => "DEBUG",
454 }
455 }
456}
457
458// ===============================================================================
459// ``````````````````````````````` KEY-VALUE STORE ```````````````````````````````
460// ===============================================================================
461
462/// Trait for a simple key-value storage with integrated logging.
463///
464/// This trait extends [`Logging`] to ensure that any storage operation
465/// (insert or get) automatically logs errors in a standardized way.
466///
467/// This is generic over `TimeStamp` so logs can carry either a block number,
468/// system timestamp, or any other type that implements [`Debug`].
469pub trait KeyValueStore<Value, TimeStamp>: Logging<TimeStamp>
470where
471 TimeStamp: Time,
472{
473 /// Type of the key, maybe a slice instead of a bounded array.
474 type Key: Probe + ?Sized;
475
476 /// Value of the key.
477 type Value: Portable;
478
479 /// Inserts a value into the store for a given key.
480 ///
481 /// ## Parameters
482 /// - `key`: The key under which to insert the value.
483 /// - `value`: The value to store.
484 /// - `target`: Optional log target (e.g., `"runtime::storage::balances"`).
485 /// - `fmt`: Optional custom log formatter. If `None`, the default log format is used.
486 ///
487 /// ## Returns
488 /// - `Ok(())` on success.
489 /// - `Err(Logger)` if an error occurs; already logged.
490 fn insert(
491 key: &Self::Key,
492 value: &Value,
493 target: Option<&str>,
494 fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
495 ) -> Result<(), Self::Logger>;
496
497 /// Retrieves a value from the store for a given key.
498 ///
499 /// ## Parameters
500 /// - `key`: The key to look up.
501 /// - `target`: Optional log target (e.g., `"runtime::storage::balances"`).
502 /// - `fmt`: Optional custom log formatter. If `None`, the default log format is used.
503 ///
504 /// ## Returns
505 /// - `Ok(Some(value))` if key exists.
506 /// - `Ok(None)` if key does not exist.
507 /// - `Err(Logger)` if retrieval fails; already logged.
508 fn get(
509 key: &Self::Key,
510 target: Option<&str>,
511 fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
512 ) -> Result<Option<Self::Value>, Self::Logger>;
513
514 /// Removes the value associated with the given key.
515 ///
516 /// If the key exists, the stored value is removed and returned.
517 /// If the key does not exist, `Ok(None)` is returned.
518 ///
519 /// ## Parameters
520 /// - `key`: The key whose associated value should be removed.
521 /// - `target`: Optional log target (e.g., `"runtime::storage::offchain"`).
522 /// - `fmt`: Optional custom log formatter. If `None`, the default log format is used.
523 ///
524 /// ## Returns
525 /// - `Ok(Some(value))` if the key existed and the value was removed.
526 /// - `Ok(None)` if the key did not exist.
527 /// - `Err(Logger)` if removal fails; already logged.
528 fn remove(
529 key: &Self::Key,
530 target: Option<&str>,
531 fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
532 ) -> Result<Option<Value>, Self::Logger>;
533
534 /// Mutates the value associated with the given key.
535 ///
536 /// The mutation closure is invoked with the **current value state**:
537 ///
538 /// - `Ok(Some(value))` if the key exists and the value was successfully read.
539 /// - `Ok(None)` if the key does not exist, allowing the caller to initialize it.
540 /// - `Err(Self::Logger)` if reading or decoding the existing value fails
541 /// (this error is already logged).
542 ///
543 /// The closure **must return a value** to be written back to storage.
544 /// Removal is **not supported** via this method; use [`Self::remove`] explicitly
545 /// if deletion is required.
546 ///
547 /// Any error returned by the closure ([`Logging::Logger`]) **must not be logged
548 /// by the caller**. This function will automatically log all errors returned from
549 /// the closure, ensuring consistent logging behavior.
550 ///
551 /// ## Parameters
552 /// - `key`: The key whose associated value should be mutated.
553 /// - `f`: A closure that receives the current value state and returns
554 /// the new value to store, or a domain-level error.
555 /// - `target`: Optional log target for storage-related logs.
556 /// - `fmt`: Optional custom log formatter.
557 ///
558 /// ## Returns
559 /// - `Ok(())` if the mutation succeeds.
560 /// - `Err(Logger)` if mutation fails; already logged.
561 fn mutate<F>(
562 key: &Self::Key,
563 f: F,
564 target: Option<&str>,
565 fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
566 ) -> Result<(), Self::Logger>
567 where
568 F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>;
569}
570
571// ===============================================================================
572// `````````````````````````` OFFCHAIN-STORAGE UTILITIES `````````````````````````
573// ===============================================================================
574
575/// Marker trait for Substrate offchain storage kinds (backends).
576///
577/// Used exclusively for compile-time specialization and trait bounds.
578/// This trait has no methods and implies no behavior.
579pub trait SubstrateOffchainStorage {}
580
581/// Defines how **offchain storage failures are reported to callers**.
582///
583/// Intended for **Substrate FRAME-based runtimes only**.
584///
585/// This trait assigns that responsibility to the **caller (routine)** by
586/// allowing it to define the concrete error values used to represent storage
587/// failures in its own domain.
588///
589/// The `Kind` parameter identifies the offchain storage backend this policy
590/// applies to (for example, persistent or fork-aware storage). It may carry
591/// additional type parameters, but this trait makes no assumptions about
592/// their meaning.
593///
594/// This trait defines **policy only** and introduces no runtime behavior.
595pub trait OffchainStorageError<Kind>
596where
597 Kind: SubstrateOffchainStorage,
598{
599 /// Caller-defined error type used for storage failures.
600 ///
601 /// The same value is logged and returned as a [`DispatchError`].
602 type Error: RuntimeError;
603
604 /// Error used when decoding a stored value fails.
605 fn decode_failed() -> Self::Error;
606
607 /// Error used when a concurrent mutation of the stored value is detected.
608 fn concurrent_mutation() -> Self::Error;
609}
610
611// ===============================================================================
612// `````````````````````````````` PERSISTENT STORAGE `````````````````````````````
613// ===============================================================================
614
615/// Marker type for persistent offchain storage, providing fork-independent,
616/// non-reverting state with routine-defined error and logging semantics.
617///
618/// Intended for **Substrate FRAME-based runtimes only**.
619///
620/// Persistent offchain storage is **not fork-aware**:
621/// - Values persist across block re-organizations.
622/// - Values are shared across all forks.
623/// - Values are **not reverted** if the current fork is abandoned.
624///
625/// This marker is used to specialize [`KeyValueStore`] implementations
626/// backed by [`StorageValueRef::persistent`].
627///
628/// ## Error policy requirement
629///
630/// When [`KeyValueStore`] is **used for this type**, the corresponding
631/// `Routine` **must also implement** [`OffchainStorageError`] for the
632/// *same specialized* `Persistent<Context, Value, Routine>` type.
633///
634/// This ensures that:
635/// - storage-level failures are surfaced as **caller-defined errors**,
636/// - errors are attributed to the routine's domain rather than the
637/// storage layer,
638/// - and each failure is logged exactly once.
639///
640/// ## Timestamp semantics
641///
642/// This storage backend is **explicitly bound to block numbers** as its
643/// timestamp source. It does **not** accept a generic timestamp parameter.
644///
645/// All logging and routine behavior associated with this backend uses
646/// [`BlockNumberFor`], reflecting its intended use inside
647/// FRAME-based runtimes.
648///
649/// ## Type parameters
650///
651/// - `Context`: The active runtime type (i.e. a type implementing
652/// [`frame_system::Config`]). This binds the storage to a specific
653/// runtime configuration.
654/// - `Value`: The value type stored in persistent offchain storage.
655/// - `Routine`: A routine type implementing [`Routines`] parameterized
656/// by [`BlockNumberFor`], ensuring logging, error handling,
657/// and behavior are specialized to block-based execution.
658///
659/// This type is a **marker only** and carries no runtime data.
660#[derive(Clone, Copy, Debug, Default)]
661pub struct Persistent<Context, Value, Routine>(PhantomData<(Value, Context, Routine)>)
662where
663 Context: frame_system::Config,
664 Value: Portable,
665 Routine: Routines<BlockNumberFor<Context>>;
666
667/// Default backend marker implementation for all valid [`Persistent`] specializations.
668///
669/// This blanket implementation marks every well-formed [`Persistent<Context, Value, Routine>`]
670/// type as a supported Substrate offchain storage backend.
671impl<Context, Value, Routine> SubstrateOffchainStorage for Persistent<Context, Value, Routine>
672where
673 Context: frame_system::Config,
674 Value: Portable,
675 Routine: Routines<BlockNumberFor<Context>>,
676{
677}
678
679/// **Peristent Offchain Storage Kind/Backend** Default [`KeyValueStore`]
680/// Implementation.
681///
682/// Intended for Substrate FRAME-based runtimes only.
683///
684/// The timestamp type is the runtime's block number ([`BlockNumberFor`]),
685/// ensuring that all logs are tagged with the block context in which
686/// the operation occurred.
687impl<T, Value, Routine> KeyValueStore<Value, BlockNumberFor<T>> for Persistent<T, Value, Routine>
688where
689 T: frame_system::Config,
690 Value: Portable,
691 Routine: OffchainStorageError<Self> + Routines<BlockNumberFor<T>>,
692{
693 /// Keys are raw byte slices; allows flexible usage for any encoded identifier.
694 type Key = [u8];
695
696 /// Value type implementing [`Encode`] and [`Decode`]
697 type Value = Value;
698
699 /// This writes directly to **persistent offchain storage** and is therefore
700 /// not reverted on chain re-orgs.
701 fn insert(
702 key: &Self::Key,
703 value: &Value,
704 _target: Option<&str>,
705 _fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
706 ) -> Result<(), Self::Logger> {
707 let storage_ref = StorageValueRef::persistent(key);
708 storage_ref.set(value);
709 Ok(())
710 }
711
712 /// Reads from **persistent offchain storage**, which is shared across forks
713 /// and survives re-orgs.
714 fn get(
715 key: &Self::Key,
716 target: Option<&str>,
717 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
718 ) -> Result<Option<Value>, Self::Logger> {
719 let storage_ref = StorageValueRef::persistent(key);
720 let block = frame_system::Pallet::<T>::block_number();
721
722 // Attempt to read from storage.
723 let Ok(value) = storage_ref.get() else {
724 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
725 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
726 block,
727 target,
728 fmt,
729 ));
730 };
731
732 Ok(value)
733 }
734
735 /// Removes the value from **persistent offchain storage**.
736 ///
737 /// Since persistent storage is not fork-aware, removals are permanent and
738 /// are not reverted on chain re-orgs.
739 fn remove(
740 key: &Self::Key,
741 target: Option<&str>,
742 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
743 ) -> Result<Option<Value>, Self::Logger> {
744 let storage_ref = StorageValueRef::persistent(key);
745 let block = frame_system::Pallet::<T>::block_number();
746
747 // Read existing value first
748 let Ok(existing) = storage_ref.get::<Value>() else {
749 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
750 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
751 block,
752 target,
753 fmt,
754 ));
755 };
756 // Remove the value
757 let mut storage_ref = storage_ref;
758 storage_ref.clear();
759
760 Ok(existing)
761 }
762
763 /// Mutates the value associated with the given key in **persistent offchain storage**.
764 ///
765 /// The closure is invoked with the current value, if any, and **must return**
766 /// the new value to store. Removal is not supported by this method.
767 ///
768 /// Persistent storage is **not fork-aware**: mutations persist across re-orgs
769 /// and are visible on all forks.
770 fn mutate<F>(
771 key: &Self::Key,
772 f: F,
773 target: Option<&str>,
774 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
775 ) -> Result<(), Self::Logger>
776 where
777 F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
778 {
779 let storage_ref = StorageValueRef::persistent(key);
780 let block = frame_system::Pallet::<T>::block_number();
781
782 let res = storage_ref.mutate::<Value, Self::Logger, _>(|current| {
783 // Decode / retrieval phase
784 let current = match current {
785 Ok(v) => Ok(v), // v = Option<Value>
786 Err(_) => Err(<Self as Logging<BlockNumberFor<T>>>::warn(
787 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
788 block,
789 target,
790 fmt,
791 )),
792 };
793
794 // Delegate domain mutation logic
795 f(current)
796 });
797
798 match res {
799 // Value successfully written
800 Ok(_) => Ok(()),
801
802 // Storage-level race
803 Err(MutateStorageError::ConcurrentModification(_)) => {
804 let logged = <Self as Logging<BlockNumberFor<T>>>::warn(
805 &<Routine as OffchainStorageError<Self>>::concurrent_mutation().into(),
806 block,
807 target,
808 fmt,
809 );
810 Err(logged)
811 }
812
813 // Closure returned a domain error
814 Err(MutateStorageError::ValueFunctionFailed(logged)) => {
815 Err(<Self as Logging<BlockNumberFor<T>>>::error(
816 &logged, block, target, fmt,
817 ))
818 }
819 }
820 }
821}
822
823// ===============================================================================
824// `````````````````````````````` FORK-AWARE STORAGE `````````````````````````````
825// ===============================================================================
826
827/// Marker type for fork-aware offchain storage, enabling re-org-sensitive,
828/// fork-scoped state with routine-defined error and logging semantics.
829///
830/// Intended for **Substrate FRAME-based runtimes only**.
831///
832/// Fork-aware offchain storage is **re-org sensitive**:
833/// - Values are scoped to the current fork.
834/// - Values are reverted if the fork is abandoned.
835/// - Values are not shared across competing forks.
836///
837/// This marker is used to specialize [`KeyValueStore`] implementations
838/// backed by fork-aware offchain storage via [`StorageValueRef::local`].
839///
840/// ## Error policy requirement
841///
842/// When [`KeyValueStore`] is **used for this type**, the corresponding
843/// `Routine` **must also implement** [`OffchainStorageError`] for the
844/// *same specialized* `Persistent<Context, Value, Routine>` type.
845///
846/// This ensures that:
847/// - storage-level failures are surfaced as **caller-defined errors**,
848/// - errors are attributed to the routine's domain rather than the
849/// storage layer,
850/// - and each failure is logged exactly once.
851///
852/// ## Timestamp semantics
853///
854/// This storage backend is **explicitly bound to block numbers** as its
855/// timestamp source. It does **not** accept a generic timestamp parameter.
856///
857/// All logging and routine behavior associated with this backend uses
858/// [`BlockNumberFor`], reflecting its intended use inside
859/// FRAME-based runtimes.
860///
861/// ## Type parameters
862///
863/// - `Context`: The active runtime type (i.e. a type implementing
864/// [`frame_system::Config`]). This binds the storage to a specific
865/// runtime configuration.
866/// - `Value`: The value type stored in fork-aware offchain storage.
867/// - `Routine`: A routine type implementing [`Routines`] parameterized
868/// by [`BlockNumberFor`], ensuring logging, error handling,
869/// and behavior are specialized to block-based execution.
870///
871/// This type is a **marker only** and carries no runtime data.
872#[derive(Clone, Copy, Debug, Default)]
873pub struct ForkAware<Context, Value, Routine>(PhantomData<(Value, Context, Routine)>)
874where
875 Context: frame_system::Config,
876 Value: Portable,
877 Routine: Routines<BlockNumberFor<Context>>;
878
879/// Default backend marker implementation for all valid [`ForkAware`] specializations.
880///
881/// This blanket implementation marks every well-formed [`ForkAware<Context, Value, Routine>`]
882/// type as a supported Substrate offchain storage backend.
883impl<Context, Value, Routine> SubstrateOffchainStorage for ForkAware<Context, Value, Routine>
884where
885 Context: frame_system::Config,
886 Value: Portable,
887 Routine: Routines<BlockNumberFor<Context>>,
888{
889}
890
891/// **Fork-Aware Offchain Storage Kind/Backend** Default [`KeyValueStore`]
892/// Implementation.
893///
894/// Intended for Substrate FRAME-based runtimes only.
895///
896/// This implementation is backed by **fork-aware offchain storage**
897/// ([`StorageValueRef::local`]).
898///
899/// The timestamp type is the runtime's block number ([`BlockNumberFor`]),
900/// ensuring that all logs are tagged with the block context in which
901/// the operation occurred.
902impl<T, Value, Routine> KeyValueStore<Value, BlockNumberFor<T>> for ForkAware<T, Value, Routine>
903where
904 T: frame_system::Config,
905 Value: Portable,
906 Routine: OffchainStorageError<Self> + Routines<BlockNumberFor<T>>,
907{
908 /// Keys are raw byte slices; allows flexible usage for any encoded identifier.
909 type Key = [u8];
910
911 /// Value type implementing [`Encode`] and [`Decode`]
912 type Value = Value;
913
914 /// Writes a value to **fork-aware offchain storage**.
915 ///
916 /// For **get-check-set** use [`Self::mutate`] instead for concurrency safety.
917 ///
918 /// Values written using this method are scoped to the current fork and
919 /// will be reverted automatically if the fork is abandoned.
920 fn insert(
921 key: &Self::Key,
922 value: &Value,
923 _target: Option<&str>,
924 _fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
925 ) -> Result<(), Self::Logger> {
926 let storage_ref = StorageValueRef::local(key);
927 storage_ref.set(value);
928 Ok(())
929 }
930
931 /// Reads a value from **fork-aware offchain storage**.
932 ///
933 /// The returned value reflects only the state of the current fork and
934 /// may differ across competing forks.
935 fn get(
936 key: &Self::Key,
937 target: Option<&str>,
938 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
939 ) -> Result<Option<Value>, Self::Logger> {
940 let storage_ref = StorageValueRef::local(key);
941 let block = frame_system::Pallet::<T>::block_number();
942
943 let Ok(value) = storage_ref.get() else {
944 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
945 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
946 block,
947 target,
948 fmt,
949 ));
950 };
951
952 Ok(value)
953 }
954
955 /// Removes a value from **fork-aware offchain storage**.
956 ///
957 /// Removals are scoped to the current fork and are reverted automatically
958 /// if the fork is abandoned.
959 fn remove(
960 key: &Self::Key,
961 target: Option<&str>,
962 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
963 ) -> Result<Option<Value>, Self::Logger> {
964 let storage_ref = StorageValueRef::local(key);
965 let block = frame_system::Pallet::<T>::block_number();
966
967 let Ok(existing) = storage_ref.get::<Value>() else {
968 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
969 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
970 block,
971 target,
972 fmt,
973 ));
974 };
975
976 let mut storage_ref = storage_ref;
977 storage_ref.clear();
978
979 Ok(existing)
980 }
981
982 /// Performs an atomic read–modify–write operation on **fork-aware offchain
983 /// storage**.
984 ///
985 /// The closure is invoked with the current value, if any, and **must return**
986 /// the new value to store. Removal is not supported by this method.
987 ///
988 /// Mutations performed by this method:
989 /// - are visible only on the current fork,
990 /// - are reverted automatically on re-orgs,
991 /// - and are safe to use with speculative chain state.
992 fn mutate<F>(
993 key: &Self::Key,
994 f: F,
995 target: Option<&str>,
996 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
997 ) -> Result<(), Self::Logger>
998 where
999 F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
1000 {
1001 let storage_ref = StorageValueRef::local(key);
1002 let block = frame_system::Pallet::<T>::block_number();
1003
1004 let res = storage_ref.mutate::<Value, Self::Logger, _>(|current| {
1005 // Normalize storage read into Result<Option<Value>, Logged>
1006 let current = match current {
1007 Ok(opt) => Ok(opt),
1008 Err(_) => Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1009 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
1010 block,
1011 target,
1012 fmt,
1013 )),
1014 };
1015
1016 // Delegate mutation logic to caller
1017 f(current)
1018 });
1019
1020 match res {
1021 Ok(_) => Ok(()),
1022
1023 Err(MutateStorageError::ConcurrentModification(_)) => {
1024 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1025 &<Routine as OffchainStorageError<Self>>::concurrent_mutation().into(),
1026 block,
1027 target,
1028 fmt,
1029 ));
1030 }
1031
1032 Err(MutateStorageError::ValueFunctionFailed(logged)) => {
1033 Err(<Self as Logging<BlockNumberFor<T>>>::error(
1034 &logged, block, target, fmt,
1035 ))
1036 }
1037 }
1038 }
1039}
1040
1041// ===============================================================================
1042// ````````````````````````` FINALIZED STORAGE UTILITIES `````````````````````````
1043// ===============================================================================
1044
1045/// Defines a **finality evaluation policy** for values managed by
1046/// [`Finalized`] storage.
1047///
1048/// Intended for **Substrate FRAME-based runtimes only**.
1049///
1050/// This trait specifies the parameters used to derive a **confidence signal**
1051/// for speculative, fork-aware data based on:
1052/// - elapsed wall-clock time, and
1053/// - block-scoped repeated observations.
1054///
1055/// It only answers:
1056/// - *how long* a value must survive before it may be considered stable, and
1057/// - *how many distinct block observations* are required to strengthen confidence.
1058///
1059/// The policy provides *inputs* to confidence evaluation and does not
1060/// imply on-chain finality or absolute truth.
1061pub trait FinalizedPolicy<Context>
1062where
1063 Context: pallet_timestamp::Config,
1064{
1065 /// Wall-clock **elapsed time window** that must pass *after the first
1066 /// observation* before a value may begin to contribute to a stronger
1067 /// confidence signal.
1068 ///
1069 /// Conceptually, this represents the delay between:
1070 /// - an **initial observation window**, and
1071 /// - an **optimal finalized window** where confidence can be evaluated.
1072 ///
1073 /// The duration is expressed using the runtime’s timestamp type
1074 /// (see [`pallet_timestamp::Config::Moment`]).
1075 fn finality_after() -> <Context as pallet_timestamp::Config>::Moment;
1076
1077 /// Number of **distinct blocks** in which the value must be observed
1078 /// *after* the finality window has elapsed.
1079 ///
1080 /// Observations are block-scoped:
1081 /// - At most one observation per block is counted.
1082 /// - Repeated OCW executions within the same block do not increase this value.
1083 ///
1084 /// This parameter acts as a confidence-strengthening threshold
1085 /// to guard against transient forks.
1086 fn finality_ticks() -> BlockNumberFor<Context>;
1087}
1088
1089/// Defines **caller-facing error signals** specific to
1090/// [`Finalized`] storage.
1091///
1092/// Intended for **Substrate FRAME-based runtimes only**.
1093///
1094/// This trait allows callers to control how **semantic invariant violations**
1095/// detected by the `Finalized` storage model are surfaced as
1096/// [`DispatchError`] values.
1097///
1098/// These errors:
1099/// - originate from finality-specific consistency checks,
1100/// - are logged by the storage layer,
1101/// - and are returned to the caller as *signals* of inconsistency,
1102/// not as definitive storage failures.
1103///
1104/// This trait does not affect storage behavior. It only defines
1105/// which error values are emitted when an invariant is violated.
1106pub trait FinalizedOffchainStorageError<Context, Value>
1107where
1108 Context: frame_system::Config,
1109{
1110 /// Concrete error type chosen by the caller.
1111 ///
1112 /// This error is converted into a [`DispatchError`] before being
1113 /// logged or returned.
1114 type Error: RuntimeError;
1115
1116 /// Emitted when a **fork-aware value hash exists without a corresponding
1117 /// entry in persistent storage**.
1118 ///
1119 /// This indicates a hanging speculative value. The fork-aware entry
1120 /// is cleaned up automatically before this error is returned.
1121 fn hanging_hash() -> Self::Error;
1122
1123 /// Emitted when **persistent storage contains no value at all**
1124 /// for the given key.
1125 ///
1126 /// This means there is **no speculative value being tracked**,
1127 /// and therefore the fork-aware entry has no semantic meaning.
1128 ///
1129 /// When this condition is detected, the fork-aware entry is
1130 /// cleaned up automatically before this error is returned.
1131 fn hanging_value() -> Self::Error;
1132}
1133
1134// ===============================================================================
1135// `````````````````````````````` FINALIZED STORAGE ``````````````````````````````
1136// ===============================================================================
1137
1138/// Marker type for finality-aware offchain storage, combining fork-aware and
1139/// persistent state to derive confidence-graded values via routine-defined
1140/// policies and observations.
1141///
1142/// Intended for **Substrate FRAME-based runtimes only**.
1143///
1144/// The `Finalized` storage model:
1145/// - records values speculatively using fork-aware storage,
1146/// - tracks historical observations in persistent storage,
1147/// - and exposes values only after evaluating time and observation-based
1148/// finality guarantees.
1149///
1150/// Finality is determined by:
1151/// - a wall-clock time window (see [`FinalizedPolicy`]),
1152/// - and repeated successful observations.
1153///
1154/// This marker is used to specialize [`KeyValueStore`] implementations that
1155/// combine [`ForkAware`] and [`Persistent`] storage to provide confidence-graded
1156/// values (see [`Confidence`]).
1157///
1158/// ## Behavioral contract
1159///
1160/// Any routine using `Finalized` storage **must provide error policies for the
1161/// exact internal storage forms used by this model** via
1162/// [`OffchainStorageError`]:
1163///
1164/// - [`ForkAware<Context, ValueHash, Routine>`], which stores speculative
1165/// fork-local identity using [`ValueHash`], and
1166/// - [`Persistent<Context, Ledger<Context, Moment<Context>, Value>, Routine>`],
1167/// which stores the persistent observation ledger using [`Ledger`] and
1168/// wall-clock [`Moment`].
1169///
1170/// In addition, the routine must define:
1171/// - a [`FinalizedPolicy`] describing when a value becomes stable, and
1172/// - [`FinalizedOffchainStorageError`] values for finality-specific
1173/// invariant violations.
1174///
1175/// Together, these requirements ensure that:
1176/// - fork-aware and persistent state remain consistent,
1177/// - semantic invariants are enforced at a single, centralized layer,
1178/// - and all failures are surfaced as **caller-defined error signals**
1179/// and logged exactly once.
1180///
1181/// ## Value-first semantics
1182///
1183/// This storage model is **value-first**: confidence is tied to the observed
1184/// *value*, not just the key. If the same value is inserted again for the same
1185/// key, its accumulated confidence is **reset**, as the insertion is treated as
1186/// a fresh observation sequence.
1187///
1188/// Callers are therefore responsible for deciding whether repeated insertions
1189/// of the same value are semantically meaningful. To avoid unintended confidence
1190/// resets, routines should refrain from inserting identical values multiple
1191/// times unless a reset is explicitly desired.
1192///
1193/// ## Timestamp semantics
1194///
1195/// All logging and routine behavior associated with this storage model is
1196/// **explicitly bound to block numbers** via [`BlockNumberFor<Context>`].
1197/// This type does **not** accept a generic timestamp parameter.
1198///
1199/// Wall-clock time, when required for finality evaluation, is obtained
1200/// explicitly from [`pallet_timestamp`].
1201///
1202/// ## Type parameters
1203///
1204/// - `Context`: The active runtime type (i.e. a type implementing
1205/// [`frame_system::Config`]). This binds the storage model to a specific
1206/// runtime configuration.
1207/// - `Value`: The value type whose finality is being tracked.
1208/// - `Routine`: A routine type implementing [`Routines`] parameterized
1209/// by [`BlockNumberFor<Context>`], allowing logging, error handling,
1210/// policy evaluation, and invariant enforcement to be specialized
1211/// at the type level.
1212///
1213/// This type is a **marker only** and carries no runtime data.
1214#[derive(Clone, Copy, Debug)]
1215pub struct Finalized<Context, Value, Routine>(PhantomData<(Value, Context, Routine)>)
1216where
1217 Context: frame_system::Config,
1218 Value: Portable,
1219 Routine: Routines<BlockNumberFor<Context>>;
1220
1221/// Stable, fork-independent identifier for values managed by [`Finalized`] storage.
1222///
1223/// `ValueHash` is computed using the `blake2_256` hash of the
1224/// SCALE-encoded representation of a value.
1225///
1226/// Within the [`Finalized`] storage model, this hash is used to:
1227/// - identify the *actual content* associated with a fork-aware key,
1228/// - correlate speculative fork-aware entries with their corresponding
1229/// persistent ledger records,
1230/// - and track value observations across forks.
1231///
1232/// Fork-aware storage records only the `ValueHash`, while the full value
1233/// and its observation metadata are stored persistently. This ensures that
1234/// semantic identity is preserved across re-orgs while allowing speculative
1235/// state to be reverted safely.
1236#[derive(Encode, Decode, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
1237pub struct ValueHash(pub [u8; 32]);
1238
1239impl ValueHash {
1240 pub fn new(hash: [u8; 32]) -> Self {
1241 ValueHash(hash)
1242 }
1243}
1244
1245/// Persistent observation record for a value managed by [`Finalized`] storage.
1246///
1247/// An `Observation` captures *when* a value was seen and *how many distinct
1248/// blocks* it has survived after entering the finality window.
1249///
1250/// This structure does not imply finality by itself; it only provides
1251/// the evidence required by [`FinalizedPolicy`] to derive a
1252/// [`Confidence`] level.
1253#[derive(Encode, Decode, Debug, Clone)]
1254pub struct Observation<Context, Value>
1255where
1256 Context: pallet_timestamp::Config,
1257{
1258 /// Wall-clock time when the value was first observed.
1259 pub first_seen: Moment<Context>,
1260
1261 /// Wall-clock time when the value was last observed.
1262 pub last_seen: Moment<Context>,
1263
1264 /// Number of distinct blocks in which the value was observed
1265 /// after the finality window elapsed.
1266 pub blocks_seen: BlockNumberFor<Context>,
1267
1268 /// The observed value.
1269 pub value: Value,
1270}
1271
1272/// Persistent observation ledger used by [`Finalized`] storage.
1273///
1274/// A `Ledger` maps a stable [`ValueHash`] to its corresponding
1275/// [`Observation`] record.
1276///
1277/// Within the [`Finalized`] storage model:
1278/// - The ledger is stored in **persistent offchain storage**.
1279/// - Entries are **fork-independent** and survive chain re-organizations.
1280/// - Each entry accumulates observation history across OCW executions.
1281///
1282/// The ledger acts as the authoritative source of truth for:
1283/// - value identity (via [`ValueHash`]),
1284/// - temporal stability,
1285/// - and block-scoped confirmation counts.
1286///
1287/// It is consulted to derive confidence levels (see [`Confidence`])
1288/// and to detect and clean up fork-aware inconsistencies.
1289#[derive(Encode, Decode, Debug, Clone)]
1290pub struct Ledger<Context, Value>(pub ConfidenceMap<Context, Value>)
1291where
1292 Context: pallet_timestamp::Config,
1293 Value: Encode + Decode + Clone;
1294
1295// Type Alias for Persistent Ledger
1296type ConfidenceMap<Context, Value> = BTreeMap<ValueHash, Observation<Context, Value>>;
1297
1298/// Confidence **signal** derived for a value evaluated by [`Finalized`] storage.
1299///
1300/// This enum represents the outcome of applying a [`FinalizedPolicy`] to an
1301/// observed value, based on elapsed time and block-scoped observations.
1302///
1303/// It expresses a **signal of stability**, not on-chain finality and not a
1304/// definitive statement of truth.
1305#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
1306pub enum Confidence<Value>
1307where
1308 Value: Portable,
1309{
1310 /// A **strong confidence signal**.
1311 ///
1312 /// The value has:
1313 /// - survived the configured finality time window, and
1314 /// - been observed across enough distinct blocks.
1315 ///
1316 /// This signal suggests that the value is *likely stable* and that
1317 /// irreversible or non-recoverable actions may be reasonable.
1318 Safe(Value),
1319
1320 /// A **weak confidence signal**.
1321 ///
1322 /// The value has survived the finality time window, but has **not yet**
1323 /// accumulated enough block-scoped observations.
1324 ///
1325 /// This signal suggests that only optimistic or recoverable actions
1326 /// should be considered.
1327 Risky(Value),
1328
1329 /// A **negative confidence signal**.
1330 ///
1331 /// The value exists, but the finality time window has **not** elapsed yet.
1332 ///
1333 /// This signal suggests that no action-reversible or irreversible-
1334 /// should be taken at this stage.
1335 Unsafe(Value),
1336}
1337
1338/// Wall-clock timestamp type used by [`Finalized`] storage.
1339///
1340/// An alias for the timestamp type provided by [`pallet_timestamp`]
1341/// for the given runtime.
1342///
1343/// It is used exclusively for **time-based finality evaluation**
1344/// (for example, measuring how long a value has survived),
1345/// and is **not** used for ordering, counting, or block-based logic.
1346///
1347/// Block-scoped semantics (such as observation counts) are expressed
1348/// separately via [`BlockNumberFor`].
1349pub type Moment<T> = <T as pallet_timestamp::Config>::Moment;
1350
1351/// [`KeyValueStore`] implementation for [`Finalized`] storage semantics.
1352///
1353/// This implementation materializes the behavioral contract defined by
1354/// [`Finalized`] by combining:
1355/// - fork-aware storage for speculative state,
1356/// - persistent storage for observation history,
1357/// - and routine-defined policies for finality evaluation and error signaling.
1358///
1359/// The required bounds ensure that the routine:
1360/// - defines **when** a value becomes stable ([`FinalizedPolicy`]),
1361/// - provides caller-defined error signals for finality invariants
1362/// ([`FinalizedOffchainStorageError`]),
1363/// - and supplies [`OffchainStorageError`] error policies for the
1364/// **exact storage forms** used internally by this model for:
1365/// - [`ForkAware<.., ValueHash, ..>`] using [`ValueHash`], and
1366/// - [`Persistent<.., Ledger<...>, ..>`] using [`Ledger`].
1367///
1368/// This guarantees that all storage failures and semantic violations are
1369/// surfaced consistently as caller-defined errors and are logged exactly
1370/// once at the correct abstraction layer.
1371impl<T, Value, Routine> KeyValueStore<Value, BlockNumberFor<T>> for Finalized<T, Value, Routine>
1372where
1373 T: pallet_timestamp::Config,
1374 Value: Portable,
1375 Routine: FinalizedOffchainStorageError<T, Value>
1376 + FinalizedPolicy<T>
1377 + OffchainStorageError<Persistent<T, Ledger<T, Value>, Routine>>
1378 + OffchainStorageError<ForkAware<T, ValueHash, Routine>>
1379 + Routines<BlockNumberFor<T>>,
1380{
1381 /// Keys are raw byte slices; allows flexible usage for any encoded identifier.
1382 type Key = [u8];
1383
1384 /// Return value type used when querying a key.
1385 ///
1386 /// The value is wrapped in [`Confidence`], representing a
1387 /// **confidence signal** derived from the [`Finalized`] storage
1388 /// model rather than a definitive truth or on-chain finality.
1389 type Value = Confidence<Value>;
1390
1391 /// Inserts a value **speculatively** under finality-aware semantics.
1392 ///
1393 /// This operation:
1394 /// - computes a stable [`ValueHash`] for fork-independent identity,
1395 /// - records the hash in **fork-aware storage** (speculative marker),
1396 /// - and inserts or updates an [`Observation`] in the **persistent ledger**.
1397 ///
1398 /// No confidence is implied by insertion alone; this operation only
1399 /// records *existence* and initializes observation tracking.
1400 fn insert(
1401 key: &Self::Key,
1402 value: &Value,
1403 target: Option<&str>,
1404 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1405 ) -> Result<(), Self::Logger> {
1406 // Compute stable value hash (identity across forks)
1407 let hash = ValueHash(blake2_256(&value.encode()));
1408
1409 // Read wall-clock time (confidence anchor)
1410 let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
1411
1412 // Write fork-aware speculative marker
1413 // (this is fork-local and will be reorged)
1414 ForkAware::<T, ValueHash, Routine>::insert(key, &hash, target, fmt)?;
1415
1416 // Persistent ledger mutation
1417 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1418 key,
1419 |current| {
1420 let mut ledger = match current {
1421 Ok(Some(existing)) => existing,
1422 Ok(None) => Ledger(ConfidenceMap::new()),
1423 Err(logged) => return Err(logged),
1424 };
1425
1426 ledger.0.insert(
1427 hash,
1428 Observation {
1429 first_seen: now,
1430 last_seen: now,
1431 blocks_seen: Zero::zero(),
1432 value: value.clone(),
1433 },
1434 );
1435
1436 Ok(ledger)
1437 },
1438 target,
1439 fmt,
1440 )?;
1441
1442 Ok(())
1443 }
1444
1445 /// Reads the value associated with the current fork and derives the
1446 /// value wrapped in a [`Confidence`] signal.
1447 ///
1448 /// Returned signals:
1449 /// - [`Confidence::Unsafe`] - finality window not elapsed.
1450 /// - [`Confidence::Risky`] - time elapsed, insufficient block observations.
1451 /// - [`Confidence::Safe`] - time and observation thresholds satisfied.
1452 ///
1453 /// Any detected invariant violation (for example, a fork-aware hash
1454 /// without a ledger entry) is logged and cleaned up automatically.
1455 fn get(
1456 key: &Self::Key,
1457 target: Option<&str>,
1458 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1459 ) -> Result<Option<Confidence<Value>>, Self::Logger> {
1460 let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
1461 let block = frame_system::Pallet::<T>::block_number();
1462
1463 // Read fork-aware speculative hash
1464 let hash = match ForkAware::<T, ValueHash, Routine>::get(key, target, fmt)? {
1465 Some(h) => h,
1466 None => return Ok(None),
1467 };
1468
1469 // Will be produced inside mutation
1470 let mut result: Option<Confidence<Value>> = None;
1471
1472 // Atomic persistent ledger mutation
1473 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1474 key,
1475 |current| {
1476 let mut ledger = match current {
1477 Ok(Some(l)) => l,
1478 Ok(None) => {
1479 // Fork-aware exists but ledger missing -> clean fork-aware
1480 ForkAware::<T, ValueHash, Routine>::remove(key, target, fmt)?;
1481 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1482 &<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value()
1483 .into(),
1484 block,
1485 target,
1486 fmt,
1487 ));
1488 }
1489 Err(logged) => return Err(logged),
1490 };
1491
1492 let obs = match ledger.0.get_mut(&hash) {
1493 Some(o) => o,
1494 None => {
1495 // Fork-aware hash has no backing ledger entry -> cleanup
1496 ForkAware::<T, ValueHash, Routine>::remove(key, target, fmt)?;
1497 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1498 &<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_hash()
1499 .into(),
1500 block,
1501 target,
1502 fmt,
1503 ));
1504 }
1505 };
1506
1507 // Snapshot for confidence computation
1508 let first_seen = obs.first_seen;
1509 let last_seen = obs.last_seen;
1510 let obs_count = obs.blocks_seen;
1511 let value = obs.value.clone();
1512
1513 // Confidence evaluation
1514 let after = <Routine as FinalizedPolicy<T>>::finality_after();
1515 let ticks = <Routine as FinalizedPolicy<T>>::finality_ticks();
1516
1517 // Update observation metadata
1518 obs.last_seen = now;
1519
1520 // Evaluate confidence based on temporal finality and repeated observations.
1521 //
1522 // Time-window finality check
1523 // - `first_seen`: moment of the first successful observation (insertion time)
1524 // - `after`: required finality window duration
1525 // - `last_seen`: moment of the most recent successful observation (from storage)
1526 //
1527 // If (first_seen + after) > last_seen, the value has NOT yet survived
1528 // the required finality window - meaning we have not observed it
1529 // long enough across time. The value remains `Unsafe`.
1530 //
1531 // Otherwise, the value has lived past the temporal finality window,
1532 // and we can evaluate observation-based stability.
1533 let confidence = match first_seen.saturating_add(after) > last_seen {
1534 // Still within the finality time window -> not stable yet.
1535 true => Confidence::Unsafe(value),
1536
1537 // Time window satisfied; now evaluate repeated observations.
1538 false => match obs_count < ticks {
1539 true => {
1540 // We only increment observation ticks if this observation
1541 // occurred in a strictly new moment. Multiple observations
1542 // within the same moment do not increase confidence.
1543 if last_seen < now {
1544 obs.blocks_seen += One::one();
1545 }
1546
1547 // Not enough distinct-moment observations yet -> still risky.
1548 Confidence::Risky(value)
1549 }
1550
1551 // Required number of distinct-moment observations reached,
1552 // and the time window has already elapsed -> value is finalized.
1553 false => Confidence::Safe(value),
1554 },
1555 };
1556
1557 result = Some(confidence);
1558
1559 Ok(ledger)
1560 },
1561 target,
1562 fmt,
1563 )?;
1564
1565 Ok(result)
1566 }
1567
1568 /// Removes the value associated with the **current fork**.
1569 ///
1570 /// Removal semantics:
1571 /// - The fork-aware marker is always removed first.
1572 /// - The corresponding persistent ledger entry is removed next.
1573 /// - The ledger itself is deleted if it becomes empty.
1574 ///
1575 /// This ensures no semantic or historical state is left behind once
1576 /// the value is no longer relevant.
1577 fn remove(
1578 key: &Self::Key,
1579 target: Option<&str>,
1580 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1581 ) -> Result<Option<Value>, Self::Logger> {
1582 let block = frame_system::Pallet::<T>::block_number();
1583
1584 // Read fork-aware hash (what we are removing)
1585 let hash = match ForkAware::<T, ValueHash, Routine>::get(key, target, fmt)? {
1586 Some(h) => h,
1587 None => return Ok(None), // nothing to remove
1588 };
1589
1590 // Always remove fork-aware entry first
1591 ForkAware::<T, ValueHash, Routine>::remove(key, target, fmt)?;
1592
1593 // Remove from persistent ledger atomically
1594 let mut removed: Option<Value> = None;
1595
1596 let mut is_empty = false;
1597
1598 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1599 key,
1600 |current| {
1601 let mut ledger = match current {
1602 Ok(Some(l)) => l,
1603 Ok(None) => {
1604 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1605 &<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value()
1606 .into(),
1607 block,
1608 target,
1609 fmt,
1610 ));
1611 }
1612 Err(logged) => return Err(logged),
1613 };
1614
1615 if let Some(obs) = ledger.0.remove(&hash) {
1616 removed = Some(obs.value);
1617 }
1618
1619 if ledger.0.is_empty() {
1620 is_empty = true;
1621 }
1622
1623 Ok(ledger)
1624 },
1625 target,
1626 fmt,
1627 )?;
1628
1629 // Drop empty ledger (no semantic meaning left)
1630 if is_empty {
1631 Persistent::<T, Ledger<T, Value>, Routine>::remove(key, target, fmt)?;
1632 }
1633
1634 Ok(removed)
1635 }
1636
1637 /// Mutates the value associated with a key under **finality-aware semantics**.
1638 ///
1639 /// The closure `f` receives the current value, if any, and must return
1640 /// a new value to replace it.
1641 ///
1642 /// Replacing a value **resets all finality observations**: the new value
1643 /// is treated as freshly observed and must re-accumulate confidence.
1644 ///
1645 /// The update is scoped to the current fork and preserves all storage
1646 /// invariants. Any detected invariant violation is logged once and
1647 /// cleaned up automatically before the error is returned.
1648 fn mutate<F>(
1649 key: &Self::Key,
1650 f: F,
1651 target: Option<&str>,
1652 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1653 ) -> Result<(), Self::Logger>
1654 where
1655 F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
1656 {
1657 let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
1658 let block = frame_system::Pallet::<T>::block_number();
1659
1660 // Fork-aware mutation is the outer authority
1661 let result = ForkAware::<T, ValueHash, Routine>::mutate(
1662 key,
1663 |current_hash| {
1664 // Resolve current value from persistent ledger
1665 let current_value = match current_hash {
1666 Err(logged) => {
1667 return Err(logged);
1668 }
1669
1670 Ok(None) => None,
1671
1672 Ok(Some(hash)) => {
1673 let ledger =
1674 Persistent::<T, Ledger<T, Value>, Routine>::get(key, target, fmt)?;
1675
1676 let ledger = match ledger {
1677 Some(l) => l,
1678 None => {
1679 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1680 &<Routine as FinalizedOffchainStorageError<
1681 T,
1682 Value,
1683 >>::hanging_value()
1684 .into(),
1685 block,
1686 target,
1687 fmt,
1688 ));
1689 }
1690 };
1691
1692 match ledger.0.get(&hash) {
1693 Some(obs) => Some(obs.value.clone()),
1694 None => None,
1695 }
1696 }
1697 };
1698
1699 // Delegate domain mutation
1700 let new_value = f(Ok(current_value))?;
1701
1702 // Compute new identity
1703 let new_hash = ValueHash(blake2_256(&new_value.encode()));
1704
1705 // Update persistent ledger
1706 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1707 key,
1708 |ledger_result| {
1709 let mut ledger = match ledger_result {
1710 Ok(Some(l)) => l,
1711 Ok(None) => Ledger(ConfidenceMap::new()),
1712 Err(logged) => return Err(logged),
1713 };
1714
1715 // Remove old observation
1716 if let Ok(Some(old_hash)) = current_hash {
1717 ledger.0.remove(&old_hash);
1718 }
1719
1720 // Insert new observation
1721 ledger.0.insert(
1722 new_hash,
1723 Observation {
1724 first_seen: now,
1725 last_seen: now,
1726 blocks_seen: Zero::zero(),
1727 value: new_value,
1728 },
1729 );
1730
1731 Ok(ledger)
1732 },
1733 target,
1734 fmt,
1735 )?;
1736 // Commit new fork-aware hash
1737 Ok(new_hash)
1738 },
1739 target,
1740 fmt,
1741 );
1742
1743 match result {
1744 Ok(_) => Ok(()),
1745
1746 Err(logged) => {
1747 if logged
1748 == <Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value().into()
1749 {
1750 ForkAware::<T, ValueHash, Routine>::remove(key, target, fmt)?;
1751 }
1752 Err(logged)
1753 }
1754 }
1755 }
1756}
1757
1758// ===============================================================================
1759// `````````````````````````````````` ROUTINES ```````````````````````````````````
1760// ===============================================================================
1761
1762/// **Authorization interface for a [`Routines`]**.
1763///
1764/// `RoutineOf` defines **who is allowed to execute** a routine at a given
1765/// point in time. It separates **authorization** from **execution**, which
1766/// is especially important in offchain contexts where signing keys,
1767/// rotation, and node-local state must be handled explicitly.
1768///
1769/// ## Why this exists
1770///
1771/// Offchain workers do not have the same execution guarantees as runtime
1772/// calls:
1773/// - there is no transactional rollback,
1774/// - failures do not revert state,
1775/// - and execution is best-effort.
1776///
1777/// Because of this, *authorization must be explicit* and *checked separately*
1778/// before a routine is allowed to run. `RoutineOf` provides a uniform way to:
1779///
1780/// - derive the concrete identifier (e.g. public key) authorized to run a routine,
1781/// - enforce key rotation and role-based access,
1782/// - fail early if the node is misconfigured or missing required keys.
1783///
1784/// ## Design principles
1785///
1786/// - `who()` must be **pure**: it must not mutate state.
1787/// - Failures are logged via [`Logging`] and treated as hard stops.
1788/// - The returned `Identifier` is typically used to sign payloads or
1789/// parameterize execution.
1790///
1791/// ## Example
1792///
1793/// ```text
1794/// Determine authorized signer
1795/// |
1796/// V
1797/// who() -> PublicKey
1798/// |
1799/// V
1800/// run_service(by = PublicKey)
1801/// ```
1802pub trait RoutineOf<Identifier, TimeStamp>: Logging<TimeStamp> + Routines<TimeStamp>
1803where
1804 TimeStamp: Time,
1805 Identifier: Portable,
1806{
1807 /// Returns the identifier authorized to execute the routine.
1808 ///
1809 /// If no valid identifier exists (e.g. missing key, inconsistent state),
1810 /// an error is logged and execution must not proceed.
1811 fn who(at: &TimeStamp) -> Result<Identifier, Self::Logger>;
1812}
1813
1814/// **Structured execution interface for offchain routines**.
1815///
1816/// `Routines` provides a **disciplined execution model** for offchain workers,
1817/// replacing ad-hoc logic with explicit phases and well-defined failure
1818/// semantics.
1819///
1820/// ## Why structured routines are needed
1821///
1822/// Offchain workers are fundamentally different from runtime calls:
1823///
1824/// | Runtime calls | Offchain workers |
1825/// |----------------------------------|------------------------------------|
1826/// | Transactional | Best-effort |
1827/// | Automatic rollback on error | No rollback |
1828/// | State changes are atomic | Partial execution is possible |
1829/// | Errors bubble naturally | Errors must be handled manually |
1830///
1831/// As a result, offchain logic **must be structured explicitly** to ensure:
1832///
1833/// - invariants are checked before execution,
1834/// - routines run to *intentional completion*,
1835/// - partial state does not silently corrupt future runs,
1836/// - failures are observable and diagnosable.
1837///
1838/// The `Routines` trait enforces this structure.
1839///
1840/// ## Execution model
1841///
1842/// ```text
1843/// |-------------|
1844/// | can_run() | <- check invariants, prerequisites
1845/// |-----|-------|
1846/// |
1847/// V
1848/// |-------------|
1849/// | run_service | <- perform the operation
1850/// |-----|-------|
1851/// |
1852/// V
1853/// |-------------|
1854/// | on_ran_* | <- bookkeeping, metrics, logging
1855/// |-------------|
1856/// ```
1857///
1858/// Each phase has a distinct responsibility, making offchain logic easier
1859/// to reason about, test, and evolve.
1860///
1861/// ## Failure semantics
1862///
1863/// - Any failure is **logged** via [`Logging`] and returned as `Logger`.
1864/// - Callers must treat failures as *hard stops* for the current routine.
1865/// - Subsequent routines may or may not execute, depending on orchestration.
1866///
1867/// This explicit handling avoids implicit control flow and makes routine
1868/// dependencies visible.
1869///
1870/// ## Arranging multiple routines
1871///
1872/// Structured routines compose naturally into pipelines:
1873///
1874/// ```text
1875/// Example
1876/// -------
1877/// Init -> Declare -> Rotate -> Elect
1878/// ```
1879///
1880/// Each routine:
1881/// - validates its own prerequisites,
1882/// - executes independently,
1883/// - leaves the system in a well-defined state.
1884///
1885/// This allows offchain workers to act as **deterministic coordinators**
1886/// rather than monolithic scripts.
1887///
1888/// ## Logging and observability
1889///
1890/// Because routines run outside the runtime’s transactional model,
1891/// **logging is the primary observability mechanism**.
1892///
1893/// By integrating with [`Logging`]:
1894/// - all errors are logged exactly once,
1895/// - routine boundaries are visible in logs,
1896/// - execution can be traced across blocks.
1897///
1898/// This makes post-mortem debugging and operational monitoring feasible.
1899///
1900/// ## Example usage
1901///
1902/// ```text
1903/// let routine = MyRoutine { at: block };
1904///
1905/// if routine.can_run().is_ok() {
1906/// routine.run_service()?;
1907/// routine.on_ran_service();
1908/// }
1909/// ```
1910pub trait Routines<TimeStamp>: Logging<TimeStamp>
1911where
1912 TimeStamp: Time,
1913{
1914 /// Checks whether the routine is allowed to run.
1915 ///
1916 /// This method must:
1917 /// - validate prerequisites,
1918 /// - check invariants,
1919 /// - avoid mutating state.
1920 ///
1921 /// It exists to prevent partial execution in environments without
1922 /// rollback guarantees.
1923 fn can_run(&self) -> Result<(), Self::Logger>;
1924
1925 /// Executes the routine’s core logic.
1926 ///
1927 /// Implementations should assume that `can_run` has already succeeded
1928 /// and focus solely on performing the intended operation.
1929 fn run_service(&self) -> Result<(), Self::Logger>;
1930
1931 /// Hook invoked after successful execution.
1932 ///
1933 /// This method is intended for:
1934 /// - logging,
1935 /// - metrics,
1936 /// - bookkeeping,
1937 /// - or emitting side effects that must only occur on success.
1938 ///
1939 /// The default implementation is a no-op.
1940 fn on_ran_service(_: &Self) {}
1941}