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::{Accrete, ForksHandler, 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, btree_set::BTreeSet}, vec::Vec};
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 conveninence 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/// - `Handler`: A type implementing [`ForksHandler`] using [`ForkLocalDepot`]
871/// that manages the fork graph and scope tracking for this storage backend.
872///
873/// This type is a **marker only** and carries no runtime data.
874#[derive(Clone, Copy, Debug, Default)]
875pub struct ForkAware<Context, Value, Routine, Handler>(PhantomData<(Value, Context, Routine, Handler)>)
876where
877 Context: frame_system::Config,
878 Value: Portable,
879 Routine: Routines<BlockNumberFor<Context>>,
880 Handler: ForksHandler<Context, ForkLocalDepot>;
881
882/// Default backend marker implementation for all valid [`ForkAware`] specializations.
883///
884/// This blanket implementation marks every well-formed [`ForkAware<Context, Value, Routine>`]
885/// type as a supported Substrate offchain storage backend.
886impl<Context, Value, Routine, Handler> SubstrateOffchainStorage for ForkAware<Context, Value, Routine, Handler>
887where
888 Context: frame_system::Config,
889 Value: Portable,
890 Routine: Routines<BlockNumberFor<Context>>,
891 Handler: ForksHandler<Context, ForkLocalDepot>,
892{
893}
894
895/// **Fork-Aware Offchain Storage Kind/Backend** Default [`KeyValueStore`]
896/// Implementation.
897///
898/// Intended for Substrate FRAME-based runtimes only.
899///
900/// This implementation is backed by **fork-aware offchain storage**
901/// ([`StorageValueRef::local`]).
902///
903/// The timestamp type is the runtime's block number ([`BlockNumberFor`]),
904/// ensuring that all logs are tagged with the block context in which
905/// the operation occurred.
906impl<T, Value, Routine, Handler> KeyValueStore<Value, BlockNumberFor<T>> for ForkAware<T, Value, Routine, Handler>
907where
908 T: frame_system::Config,
909 Value: Portable,
910 Routine: OffchainStorageError<Self> + Routines<BlockNumberFor<T>>,
911 Handler: ForksHandler<T, ForkLocalDepot> + Logging<BlockNumberFor<T>, Level = LogLevel, Logger = DispatchError>,
912{
913 /// Keys are raw byte slices; allows flexible usage for any encoded identifier.
914 type Key = [u8];
915
916 /// Value type implementing [`Encode`] and [`Decode`]
917 type Value = Value;
918
919 /// Writes a value to **fork-aware offchain storage**.
920 ///
921 /// For **get-check-set** use [`Self::mutate`] instead for concurrency safety.
922 ///
923 /// Values written using this method are scoped to the current fork and
924 /// will be reverted automatically if the fork is abandoned.
925 fn insert(
926 key: &Self::Key,
927 value: &Value,
928 target: Option<&str>,
929 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
930 ) -> Result<(), Self::Logger> {
931 let key = key.to_vec();
932 let scope_key = Handler::add_to_scope(key, target, fmt)?;
933 let storage_ref = StorageValueRef::persistent(&scope_key);
934 storage_ref.set(value);
935 Ok(())
936 }
937
938 /// Reads a value from **fork-aware offchain storage**.
939 ///
940 /// The returned value reflects only the state of the current fork and
941 /// may differ across competing forks.
942 fn get(
943 key: &Self::Key,
944 target: Option<&str>,
945 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
946 ) -> Result<Option<Value>, Self::Logger> {
947 let scope_key = Handler::gen_scope_item_key(&key.to_vec());
948 if !Handler::scope_item_exists(&scope_key, target, fmt)? {
949 return Ok(None)
950 };
951
952 let storage_ref = StorageValueRef::persistent(&scope_key);
953 let block = frame_system::Pallet::<T>::block_number();
954
955 let Ok(value) = storage_ref.get() else {
956 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
957 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
958 block,
959 target,
960 fmt,
961 ));
962 };
963
964 Ok(value)
965 }
966
967 /// Removes a value from **fork-aware offchain storage**.
968 ///
969 /// Removals are scoped to the current fork and are reverted automatically
970 /// if the fork is abandoned.
971 fn remove(
972 key: &Self::Key,
973 target: Option<&str>,
974 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
975 ) -> Result<Option<Value>, Self::Logger> {
976 let scope_key = Handler::gen_scope_item_key(&key.to_vec());
977 if !Handler::scope_item_exists(&scope_key, target, fmt)? {
978 return Ok(None)
979 }
980
981 let storage_ref = StorageValueRef::persistent(&scope_key);
982 let block = frame_system::Pallet::<T>::block_number();
983
984 let Ok(existing) = storage_ref.get::<Value>() else {
985 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
986 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
987 block,
988 target,
989 fmt,
990 ));
991 };
992
993 let mut storage_ref = storage_ref;
994 storage_ref.clear();
995 Handler::remove_from_scope(&scope_key, target, fmt)?;
996
997 Ok(existing)
998 }
999
1000 /// Performs an atomic read-modify-write operation on **fork-aware offchain
1001 /// storage**.
1002 ///
1003 /// The closure is invoked with the current value, if any, and **must return**
1004 /// the new value to store. Removal is not supported by this method.
1005 ///
1006 /// Mutations performed by this method:
1007 /// - are visible only on the current fork,
1008 /// - are reverted automatically on re-orgs,
1009 /// - and are safe to use with speculative chain state.
1010 fn mutate<F>(
1011 key: &Self::Key,
1012 f: F,
1013 target: Option<&str>,
1014 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1015 ) -> Result<(), Self::Logger>
1016 where
1017 F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
1018 {
1019
1020 let scope_key = Handler::gen_scope_item_key(&key.to_vec());
1021 if !Handler::scope_item_exists(&scope_key, target, fmt)? {
1022 let value = f(Ok(None))?;
1023 Self::insert(key, &value, target, fmt)?;
1024 return Ok(())
1025 }
1026
1027 let storage_ref = StorageValueRef::persistent(&scope_key);
1028 let block = frame_system::Pallet::<T>::block_number();
1029
1030 let res = storage_ref.mutate::<Value, Self::Logger, _>(|current| {
1031 // Normalize storage read into Result<Option<Value>, Logged>
1032 let current = match current {
1033 Ok(opt) => Ok(opt),
1034 Err(_) => Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1035 &<Routine as OffchainStorageError<Self>>::decode_failed().into(),
1036 block,
1037 target,
1038 fmt,
1039 )),
1040 };
1041
1042 // Delegate mutation logic to caller
1043 f(current)
1044 });
1045
1046 match res {
1047 Ok(_) => Ok(()),
1048
1049 Err(MutateStorageError::ConcurrentModification(_)) => {
1050 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1051 &<Routine as OffchainStorageError<Self>>::concurrent_mutation().into(),
1052 block,
1053 target,
1054 fmt,
1055 ));
1056 }
1057
1058 Err(MutateStorageError::ValueFunctionFailed(logged)) => {
1059 Err(<Self as Logging<BlockNumberFor<T>>>::error(
1060 &logged, block, target, fmt,
1061 ))
1062 }
1063 }
1064 }
1065}
1066
1067// ===============================================================================
1068// ````````````````````````` FINALIZED STORAGE UTILITIES `````````````````````````
1069// ===============================================================================
1070
1071/// Defines a **finality evaluation policy** for values managed by
1072/// [`Finalized`] storage.
1073///
1074/// Intended for **Substrate FRAME-based runtimes only**.
1075///
1076/// This trait specifies the parameters used to derive a **confidence signal**
1077/// for speculative, fork-aware data based on:
1078/// - elapsed wall-clock time, and
1079/// - block-scoped repeated observations.
1080///
1081/// It only answers:
1082/// - *how long* a value must survive before it may be considered stable, and
1083/// - *how many distinct block observations* are required to strengthen confidence.
1084///
1085/// The policy provides *inputs* to confidence evaluation and does not
1086/// imply on-chain finality or absolute truth.
1087pub trait FinalizedPolicy<Context>
1088where
1089 Context: pallet_timestamp::Config,
1090{
1091 /// Wall-clock **elapsed time window** that must pass *after the first
1092 /// observation* before a value may begin to contribute to a stronger
1093 /// confidence signal.
1094 ///
1095 /// Conceptually, this represents the delay between:
1096 /// - an **initial observation window**, and
1097 /// - an **optimal finalized window** where confidence can be evaluated.
1098 ///
1099 /// The duration is expressed using the runtime's timestamp type
1100 /// (see [`pallet_timestamp::Config::Moment`]).
1101 fn finality_after() -> <Context as pallet_timestamp::Config>::Moment;
1102
1103 /// Number of **distinct blocks** in which the value must be observed
1104 /// *after* the finality window has elapsed.
1105 ///
1106 /// Observations are block-scoped:
1107 /// - At most one observation per block is counted.
1108 /// - Repeated OCW executions within the same block do not increase this value.
1109 ///
1110 /// This parameter acts as a confidence-strengthening threshold
1111 /// to guard against transient forks.
1112 fn finality_ticks() -> BlockNumberFor<Context>;
1113}
1114
1115/// Defines **caller-facing error signals** specific to
1116/// [`Finalized`] storage.
1117///
1118/// Intended for **Substrate FRAME-based runtimes only**.
1119///
1120/// This trait allows callers to control how **semantic invariant violations**
1121/// detected by the `Finalized` storage model are surfaced as
1122/// [`DispatchError`] values.
1123///
1124/// These errors:
1125/// - originate from finality-specific consistency checks,
1126/// - are logged by the storage layer,
1127/// - and are returned to the caller as *signals* of inconsistency,
1128/// not as definitive storage failures.
1129///
1130/// This trait does not affect storage behavior. It only defines
1131/// which error values are emitted when an invariant is violated.
1132pub trait FinalizedOffchainStorageError<Context, Value>
1133where
1134 Context: frame_system::Config,
1135{
1136 /// Concrete error type chosen by the caller.
1137 ///
1138 /// This error is converted into a [`DispatchError`] before being
1139 /// logged or returned.
1140 type Error: RuntimeError;
1141
1142 /// Emitted when a **fork-aware value hash exists without a corresponding
1143 /// entry in persistent storage**.
1144 ///
1145 /// This indicates a hanging speculative value. The fork-aware entry
1146 /// is cleaned up automatically before this error is returned.
1147 fn hanging_hash() -> Self::Error;
1148
1149 /// Emitted when **persistent storage contains no value at all**
1150 /// for the given key.
1151 ///
1152 /// This means there is **no speculative value being tracked**,
1153 /// and therefore the fork-aware entry has no semantic meaning.
1154 ///
1155 /// When this condition is detected, the fork-aware entry is
1156 /// cleaned up automatically before this error is returned.
1157 fn hanging_value() -> Self::Error;
1158}
1159
1160// ===============================================================================
1161// `````````````````````````````` FINALIZED STORAGE ``````````````````````````````
1162// ===============================================================================
1163
1164/// Marker type for finality-aware offchain storage, combining fork-aware and
1165/// persistent state to derive confidence-graded values via routine-defined
1166/// policies and observations.
1167///
1168/// Intended for **Substrate FRAME-based runtimes only**.
1169///
1170/// The `Finalized` storage model:
1171/// - records values speculatively using fork-aware storage,
1172/// - tracks historical observations in persistent storage,
1173/// - and exposes values only after evaluating time and observation-based
1174/// finality guarantees.
1175///
1176/// Finality is determined by:
1177/// - a wall-clock time window (see [`FinalizedPolicy`]),
1178/// - and repeated successful observations.
1179///
1180/// This marker is used to specialize [`KeyValueStore`] implementations that
1181/// combine [`ForkAware`] and [`Persistent`] storage to provide confidence-graded
1182/// values (see [`Confidence`]).
1183///
1184/// ## Behavioral contract
1185///
1186/// Any routine using `Finalized` storage **must provide error policies for the
1187/// exact internal storage forms used by this model** via
1188/// [`OffchainStorageError`]:
1189///
1190/// - [`ForkAware<Context, ValueHash, Routine>`], which stores speculative
1191/// fork-local identity using [`ValueHash`], and
1192/// - [`Persistent<Context, Ledger<Context, Moment<Context>, Value>, Routine>`],
1193/// which stores the persistent observation ledger using [`Ledger`] and
1194/// wall-clock [`Moment`].
1195///
1196/// In addition, the routine must define:
1197/// - a [`FinalizedPolicy`] describing when a value becomes stable, and
1198/// - [`FinalizedOffchainStorageError`] values for finality-specific
1199/// invariant violations.
1200///
1201/// Together, these requirements ensure that:
1202/// - fork-aware and persistent state remain consistent,
1203/// - semantic invariants are enforced at a single, centralized layer,
1204/// - and all failures are surfaced as **caller-defined error signals**
1205/// and logged exactly once.
1206///
1207/// ## Value-first semantics
1208///
1209/// This storage model is **value-first**: confidence is tied to the observed
1210/// *value*, not just the key. If the same value is inserted again for the same
1211/// key, its accumulated confidence is **reset**, as the insertion is treated as
1212/// a fresh observation sequence.
1213///
1214/// Callers are therefore responsible for deciding whether repeated insertions
1215/// of the same value are semantically meaningful. To avoid unintended confidence
1216/// resets, routines should refrain from inserting identical values multiple
1217/// times unless a reset is explicitly desired.
1218///
1219/// ## Timestamp semantics
1220///
1221/// All logging and routine behavior associated with this storage model is
1222/// **explicitly bound to block numbers** via [`BlockNumberFor<Context>`].
1223/// This type does **not** accept a generic timestamp parameter.
1224///
1225/// Wall-clock time, when required for finality evaluation, is obtained
1226/// explicitly from [`pallet_timestamp`].
1227///
1228/// ## Type parameters
1229///
1230/// - `Context`: The active runtime type (i.e. a type implementing
1231/// [`frame_system::Config`]). This binds the storage model to a specific
1232/// runtime configuration.
1233/// - `Value`: The value type whose finality is being tracked.
1234/// - `Routine`: A routine type implementing [`Routines`] parameterized
1235/// by [`BlockNumberFor<Context>`], allowing logging, error handling,
1236/// policy evaluation, and invariant enforcement to be specialized
1237/// at the type level.
1238/// - `Handler`: A type implementing [`ForksHandler`] using [`ForkLocalDepot`]
1239/// that manages the fork graph and scope tracking for this storage backend.
1240///
1241/// This type is a **marker only** and carries no runtime data.
1242#[derive(Clone, Copy, Debug)]
1243pub struct Finalized<Context, Value, Routine, Handler>(PhantomData<(Value, Context, Routine, Handler)>)
1244where
1245 Context: frame_system::Config,
1246 Value: Portable,
1247 Routine: Routines<BlockNumberFor<Context>>,
1248 Handler: ForksHandler<Context, ForkLocalDepot>;
1249
1250/// Stable, fork-independent identifier for values managed by [`Finalized`] storage.
1251///
1252/// `ValueHash` is computed using the `blake2_256` hash of the
1253/// SCALE-encoded representation of a value.
1254///
1255/// Within the [`Finalized`] storage model, this hash is used to:
1256/// - identify the *actual content* associated with a fork-aware key,
1257/// - correlate speculative fork-aware entries with their corresponding
1258/// persistent ledger records,
1259/// - and track value observations across forks.
1260///
1261/// Fork-aware storage records only the `ValueHash`, while the full value
1262/// and its observation metadata are stored persistently. This ensures that
1263/// semantic identity is preserved across re-orgs while allowing speculative
1264/// state to be reverted safely.
1265#[derive(Encode, Decode, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
1266pub struct ValueHash(pub [u8; 32]);
1267
1268impl ValueHash {
1269 pub fn new(hash: [u8; 32]) -> Self {
1270 ValueHash(hash)
1271 }
1272}
1273
1274/// Persistent observation record for a value managed by [`Finalized`] storage.
1275///
1276/// An `Observation` captures *when* a value was seen and *how many distinct
1277/// blocks* it has survived after entering the finality window.
1278///
1279/// This structure does not imply finality by itself; it only provides
1280/// the evidence required by [`FinalizedPolicy`] to derive a
1281/// [`Confidence`] level.
1282#[derive(Encode, Decode, Debug, Clone)]
1283pub struct Observation<Context, Value>
1284where
1285 Context: pallet_timestamp::Config,
1286{
1287 /// Wall-clock time when the value was first observed.
1288 pub first_seen: Moment<Context>,
1289
1290 /// Wall-clock time when the value was last observed.
1291 pub last_seen: Moment<Context>,
1292
1293 /// Number of distinct blocks in which the value was observed
1294 /// after the finality window elapsed.
1295 pub blocks_seen: BlockNumberFor<Context>,
1296
1297 /// The observed value.
1298 pub value: Value,
1299}
1300
1301/// Persistent observation ledger used by [`Finalized`] storage.
1302///
1303/// A `Ledger` maps a stable [`ValueHash`] to its corresponding
1304/// [`Observation`] record.
1305///
1306/// Within the [`Finalized`] storage model:
1307/// - The ledger is stored in **persistent offchain storage**.
1308/// - Entries are **fork-independent** and survive chain re-organizations.
1309/// - Each entry accumulates observation history across OCW executions.
1310///
1311/// The ledger acts as the authoritative source of truth for:
1312/// - value identity (via [`ValueHash`]),
1313/// - temporal stability,
1314/// - and block-scoped confirmation counts.
1315///
1316/// It is consulted to derive confidence levels (see [`Confidence`])
1317/// and to detect and clean up fork-aware inconsistencies.
1318#[derive(Encode, Decode, Debug, Clone)]
1319pub struct Ledger<Context, Value>(pub ConfidenceMap<Context, Value>)
1320where
1321 Context: pallet_timestamp::Config,
1322 Value: Encode + Decode + Clone;
1323
1324// Type Alias for Persistent Ledger
1325type ConfidenceMap<Context, Value> = BTreeMap<ValueHash, Observation<Context, Value>>;
1326
1327/// Confidence **signal** derived for a value evaluated by [`Finalized`] storage.
1328///
1329/// This enum represents the outcome of applying a [`FinalizedPolicy`] to an
1330/// observed value, based on elapsed time and block-scoped observations.
1331///
1332/// It expresses a **signal of stability**, not on-chain finality and not a
1333/// definitive statement of truth.
1334#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
1335pub enum Confidence<Value>
1336where
1337 Value: Portable,
1338{
1339 /// A **strong confidence signal**.
1340 ///
1341 /// The value has:
1342 /// - survived the configured finality time window, and
1343 /// - been observed across enough distinct blocks.
1344 ///
1345 /// This signal suggests that the value is *likely stable* and that
1346 /// irreversible or non-recoverable actions may be reasonable.
1347 Safe(Value),
1348
1349 /// A **weak confidence signal**.
1350 ///
1351 /// The value has survived the finality time window, but has **not yet**
1352 /// accumulated enough block-scoped observations.
1353 ///
1354 /// This signal suggests that only optimistic or recoverable actions
1355 /// should be considered.
1356 Risky(Value),
1357
1358 /// A **negative confidence signal**.
1359 ///
1360 /// The value exists, but the finality time window has **not** elapsed yet.
1361 ///
1362 /// This signal suggests that no action-reversible or irreversible-
1363 /// should be taken at this stage.
1364 Unsafe(Value),
1365}
1366
1367/// Wall-clock timestamp type used by [`Finalized`] storage.
1368///
1369/// An alias for the timestamp type provided by [`pallet_timestamp`]
1370/// for the given runtime.
1371///
1372/// It is used exclusively for **time-based finality evaluation**
1373/// (for example, measuring how long a value has survived),
1374/// and is **not** used for ordering, counting, or block-based logic.
1375///
1376/// Block-scoped semantics (such as observation counts) are expressed
1377/// separately via [`BlockNumberFor`].
1378pub type Moment<T> = <T as pallet_timestamp::Config>::Moment;
1379
1380/// [`KeyValueStore`] implementation for [`Finalized`] storage semantics.
1381///
1382/// This implementation materializes the behavioral contract defined by
1383/// [`Finalized`] by combining:
1384/// - fork-aware storage for speculative state,
1385/// - persistent storage for observation history,
1386/// - and routine-defined policies for finality evaluation and error signaling.
1387///
1388/// The required bounds ensure that the routine:
1389/// - defines **when** a value becomes stable ([`FinalizedPolicy`]),
1390/// - provides caller-defined error signals for finality invariants
1391/// ([`FinalizedOffchainStorageError`]),
1392/// - and supplies [`OffchainStorageError`] error policies for the
1393/// **exact storage forms** used internally by this model for:
1394/// - [`ForkAware<.., ValueHash, ..>`] using [`ValueHash`], and
1395/// - [`Persistent<.., Ledger<...>, ..>`] using [`Ledger`].
1396///
1397/// This guarantees that all storage failures and semantic violations are
1398/// surfaced consistently as caller-defined errors and are logged exactly
1399/// once at the correct abstraction layer.
1400impl<T, Value, Routine, Handler> KeyValueStore<Value, BlockNumberFor<T>> for Finalized<T, Value, Routine, Handler>
1401where
1402 T: pallet_timestamp::Config,
1403 Value: Portable,
1404 Routine: FinalizedOffchainStorageError<T, Value>
1405 + FinalizedPolicy<T>
1406 + OffchainStorageError<Persistent<T, Ledger<T, Value>, Routine>>
1407 + OffchainStorageError<ForkAware<T, ValueHash, Routine, Handler>>
1408 + Routines<BlockNumberFor<T>>,
1409 Handler: ForksHandler<T, ForkLocalDepot> + Logging<BlockNumberFor<T>, Level = LogLevel, Logger = DispatchError>,
1410{
1411 /// Keys are raw byte slices; allows flexible usage for any encoded identifier.
1412 type Key = [u8];
1413
1414 /// Return value type used when querying a key.
1415 ///
1416 /// The value is wrapped in [`Confidence`], representing a
1417 /// **confidence signal** derived from the [`Finalized`] storage
1418 /// model rather than a definitive truth or on-chain finality.
1419 type Value = Confidence<Value>;
1420
1421 /// Inserts a value **speculatively** under finality-aware semantics.
1422 ///
1423 /// This operation:
1424 /// - computes a stable [`ValueHash`] for fork-independent identity,
1425 /// - records the hash in **fork-aware storage** (speculative marker),
1426 /// - and inserts or updates an [`Observation`] in the **persistent ledger**.
1427 ///
1428 /// No confidence is implied by insertion alone; this operation only
1429 /// records *existence* and initializes observation tracking.
1430 fn insert(
1431 key: &Self::Key,
1432 value: &Value,
1433 target: Option<&str>,
1434 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1435 ) -> Result<(), Self::Logger> {
1436 // Compute stable value hash (identity across forks)
1437 let hash = ValueHash(blake2_256(&value.encode()));
1438
1439 // Read wall-clock time (confidence anchor)
1440 let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
1441
1442 // Write fork-aware speculative marker
1443 // (this is fork-local and will be reorged)
1444 ForkAware::<T, ValueHash, Routine, Handler>::insert(key, &hash, target, fmt)?;
1445
1446 // Persistent ledger mutation
1447 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1448 key,
1449 |current| {
1450 let mut ledger = match current {
1451 Ok(Some(existing)) => existing,
1452 Ok(None) => Ledger(ConfidenceMap::new()),
1453 Err(logged) => return Err(logged),
1454 };
1455
1456 ledger.0.insert(
1457 hash,
1458 Observation {
1459 first_seen: now,
1460 last_seen: now,
1461 blocks_seen: Zero::zero(),
1462 value: value.clone(),
1463 },
1464 );
1465
1466 Ok(ledger)
1467 },
1468 target,
1469 fmt,
1470 )?;
1471
1472 Ok(())
1473 }
1474
1475 /// Reads the value associated with the current fork and derives the
1476 /// value wrapped in a [`Confidence`] signal.
1477 ///
1478 /// Returned signals:
1479 /// - [`Confidence::Unsafe`] - finality window not elapsed.
1480 /// - [`Confidence::Risky`] - time elapsed, insufficient block observations.
1481 /// - [`Confidence::Safe`] - time and observation thresholds satisfied.
1482 ///
1483 /// Any detected invariant violation (for example, a fork-aware hash
1484 /// without a ledger entry) is logged and cleaned up automatically.
1485 fn get(
1486 key: &Self::Key,
1487 target: Option<&str>,
1488 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1489 ) -> Result<Option<Confidence<Value>>, Self::Logger> {
1490 let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
1491 let block = frame_system::Pallet::<T>::block_number();
1492
1493 // Read fork-aware speculative hash
1494 let hash = match ForkAware::<T, ValueHash, Routine, Handler>::get(key, target, fmt)? {
1495 Some(h) => h,
1496 None => return Ok(None),
1497 };
1498
1499 // Will be produced inside mutation
1500 let mut result: Option<Confidence<Value>> = None;
1501
1502 // Atomic persistent ledger mutation
1503 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1504 key,
1505 |current| {
1506 let mut ledger = match current {
1507 Ok(Some(l)) => l,
1508 Ok(None) => {
1509 // Fork-aware exists but ledger missing -> clean fork-aware
1510 ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
1511 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1512 &<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value()
1513 .into(),
1514 block,
1515 target,
1516 fmt,
1517 ));
1518 }
1519 Err(logged) => return Err(logged),
1520 };
1521
1522 let obs = match ledger.0.get_mut(&hash) {
1523 Some(o) => o,
1524 None => {
1525 // Fork-aware hash has no backing ledger entry -> cleanup
1526 ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
1527 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1528 &<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_hash()
1529 .into(),
1530 block,
1531 target,
1532 fmt,
1533 ));
1534 }
1535 };
1536
1537 // Snapshot for confidence computation
1538 let first_seen = obs.first_seen;
1539 let last_seen = obs.last_seen;
1540 let obs_count = obs.blocks_seen;
1541 let value = obs.value.clone();
1542
1543 // Confidence evaluation
1544 let after = <Routine as FinalizedPolicy<T>>::finality_after();
1545 let ticks = <Routine as FinalizedPolicy<T>>::finality_ticks();
1546
1547 // Update observation metadata
1548 obs.last_seen = now;
1549
1550 // Evaluate confidence based on temporal finality and repeated observations.
1551 //
1552 // Time-window finality check
1553 // - `first_seen`: moment of the first successful observation (insertion time)
1554 // - `after`: required finality window duration
1555 // - `last_seen`: moment of the most recent successful observation (from storage)
1556 //
1557 // If (first_seen + after) > last_seen, the value has NOT yet survived
1558 // the required finality window - meaning we have not observed it
1559 // long enough across time. The value remains `Unsafe`.
1560 //
1561 // Otherwise, the value has lived past the temporal finality window,
1562 // and we can evaluate observation-based stability.
1563 let confidence = match first_seen.saturating_add(after) > last_seen {
1564 // Still within the finality time window -> not stable yet.
1565 true => Confidence::Unsafe(value),
1566
1567 // Time window satisfied; now evaluate repeated observations.
1568 false => match obs_count < ticks {
1569 true => {
1570 // We only increment observation ticks if this observation
1571 // occurred in a strictly new moment. Multiple observations
1572 // within the same moment do not increase confidence.
1573 if last_seen < now {
1574 obs.blocks_seen += One::one();
1575 }
1576
1577 // Not enough distinct-moment observations yet -> still risky.
1578 Confidence::Risky(value)
1579 }
1580
1581 // Required number of distinct-moment observations reached,
1582 // and the time window has already elapsed -> value is finalized.
1583 false => Confidence::Safe(value),
1584 },
1585 };
1586
1587 result = Some(confidence);
1588
1589 Ok(ledger)
1590 },
1591 target,
1592 fmt,
1593 )?;
1594
1595 Ok(result)
1596 }
1597
1598 /// Removes the value associated with the **current fork**.
1599 ///
1600 /// Removal semantics:
1601 /// - The fork-aware marker is always removed first.
1602 /// - The corresponding persistent ledger entry is removed next.
1603 /// - The ledger itself is deleted if it becomes empty.
1604 ///
1605 /// This ensures no semantic or historical state is left behind once
1606 /// the value is no longer relevant.
1607 fn remove(
1608 key: &Self::Key,
1609 target: Option<&str>,
1610 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1611 ) -> Result<Option<Value>, Self::Logger> {
1612 let block = frame_system::Pallet::<T>::block_number();
1613
1614 // Read fork-aware hash (what we are removing)
1615 let hash = match ForkAware::<T, ValueHash, Routine, Handler>::get(key, target, fmt)? {
1616 Some(h) => h,
1617 None => return Ok(None), // nothing to remove
1618 };
1619
1620 // Always remove fork-aware entry first
1621 ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
1622
1623 // Remove from persistent ledger atomically
1624 let mut removed: Option<Value> = None;
1625
1626 let mut is_empty = false;
1627
1628 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1629 key,
1630 |current| {
1631 let mut ledger = match current {
1632 Ok(Some(l)) => l,
1633 Ok(None) => {
1634 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1635 &<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value()
1636 .into(),
1637 block,
1638 target,
1639 fmt,
1640 ));
1641 }
1642 Err(logged) => return Err(logged),
1643 };
1644
1645 if let Some(obs) = ledger.0.remove(&hash) {
1646 removed = Some(obs.value);
1647 }
1648
1649 if ledger.0.is_empty() {
1650 is_empty = true;
1651 }
1652
1653 Ok(ledger)
1654 },
1655 target,
1656 fmt,
1657 )?;
1658
1659 // Drop empty ledger (no semantic meaning left)
1660 if is_empty {
1661 Persistent::<T, Ledger<T, Value>, Routine>::remove(key, target, fmt)?;
1662 }
1663
1664 Ok(removed)
1665 }
1666
1667 /// Mutates the value associated with a key under **finality-aware semantics**.
1668 ///
1669 /// The closure `f` receives the current value, if any, and must return
1670 /// a new value to replace it.
1671 ///
1672 /// Replacing a value **resets all finality observations**: the new value
1673 /// is treated as freshly observed and must re-accumulate confidence.
1674 ///
1675 /// The update is scoped to the current fork and preserves all storage
1676 /// invariants. Any detected invariant violation is logged once and
1677 /// cleaned up automatically before the error is returned.
1678 fn mutate<F>(
1679 key: &Self::Key,
1680 f: F,
1681 target: Option<&str>,
1682 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1683 ) -> Result<(), Self::Logger>
1684 where
1685 F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
1686 {
1687 let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
1688 let block = frame_system::Pallet::<T>::block_number();
1689
1690 // Fork-aware mutation is the outer authority
1691 let result = ForkAware::<T, ValueHash, Routine, Handler>::mutate(
1692 key,
1693 |current_hash| {
1694 // Resolve current value from persistent ledger
1695 let current_value = match current_hash {
1696 Err(logged) => {
1697 return Err(logged);
1698 }
1699
1700 Ok(None) => None,
1701
1702 Ok(Some(hash)) => {
1703 let ledger =
1704 Persistent::<T, Ledger<T, Value>, Routine>::get(key, target, fmt)?;
1705
1706 let ledger = match ledger {
1707 Some(l) => l,
1708 None => {
1709 return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
1710 &<Routine as FinalizedOffchainStorageError<
1711 T,
1712 Value,
1713 >>::hanging_value()
1714 .into(),
1715 block,
1716 target,
1717 fmt,
1718 ));
1719 }
1720 };
1721
1722 match ledger.0.get(&hash) {
1723 Some(obs) => Some(obs.value.clone()),
1724 None => None,
1725 }
1726 }
1727 };
1728
1729 // Delegate domain mutation
1730 let new_value = f(Ok(current_value))?;
1731
1732 // Compute new identity
1733 let new_hash = ValueHash(blake2_256(&new_value.encode()));
1734
1735 // Update persistent ledger
1736 Persistent::<T, Ledger<T, Value>, Routine>::mutate(
1737 key,
1738 |ledger_result| {
1739 let mut ledger = match ledger_result {
1740 Ok(Some(l)) => l,
1741 Ok(None) => Ledger(ConfidenceMap::new()),
1742 Err(logged) => return Err(logged),
1743 };
1744
1745 // Remove old observation
1746 if let Ok(Some(old_hash)) = current_hash {
1747 ledger.0.remove(&old_hash);
1748 }
1749
1750 // Insert new observation
1751 ledger.0.insert(
1752 new_hash,
1753 Observation {
1754 first_seen: now,
1755 last_seen: now,
1756 blocks_seen: Zero::zero(),
1757 value: new_value,
1758 },
1759 );
1760
1761 Ok(ledger)
1762 },
1763 target,
1764 fmt,
1765 )?;
1766 // Commit new fork-aware hash
1767 Ok(new_hash)
1768 },
1769 target,
1770 fmt,
1771 );
1772
1773 match result {
1774 Ok(_) => Ok(()),
1775
1776 Err(logged) => {
1777 if logged
1778 == <Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value().into()
1779 {
1780 ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
1781 }
1782 Err(logged)
1783 }
1784 }
1785 }
1786}
1787
1788// ===============================================================================
1789// `````````````````````````````````` ROUTINES ```````````````````````````````````
1790// ===============================================================================
1791
1792/// Fork-local storage scope for [`ForksHandler`] implementing [`ForkScopes`](crate::ForkScopes).
1793///
1794/// `ForkLocalDepot` is the branch-local scope container used by the
1795/// fork-aware offchain execution system to track visibility of
1796/// fork-scoped storage entries across branch lineage.
1797///
1798/// It does not store the actual values themselves.
1799///
1800/// Instead, it stores only deterministic 32-byte keys (`[u8; 32]`)
1801/// representing items written into fork-aware storage systems such as:
1802///
1803/// - [`ForkAware`]
1804/// - [`Finalized`]
1805///
1806/// These keys act as stable scope references that allow the fork graph
1807/// to answer:
1808///
1809/// ```ignore
1810/// "does this item exist on this branch or any reachable ancestor branch?"
1811/// ```
1812///
1813/// without requiring repeated traversal of historical parent branches.
1814///
1815/// ## Why this exists
1816///
1817/// In fork-aware OCW execution, each branch must maintain isolated local
1818/// state while still inheriting valid reachable state from its lineage.
1819///
1820/// Example:
1821///
1822/// ```text
1823/// A -> B -> C
1824/// |-- D
1825/// |-- D'
1826/// ```
1827///
1828/// Here:
1829///
1830/// - `D` and `D'` must not overwrite each other
1831/// - both branches must still see inherited state from `A -> B -> C`
1832///
1833/// `ForkLocalDepot` provides that visibility layer by separating:
1834///
1835/// - current-generation writes
1836/// - inherited historical writes
1837///
1838/// instead of repeatedly walking parent branches during every lookup.
1839///
1840/// ## Fork inheritance model
1841///
1842/// When a new sibling branch is created:
1843///
1844/// ```text
1845/// Parent branch:
1846/// A -> B -> C
1847///
1848/// New sibling:
1849/// |-- D'
1850/// ```
1851///
1852/// the child branch receives:
1853///
1854/// ```text
1855/// inherited_keys(child)
1856/// = inherited_keys(parent) + local_keys(parent)
1857/// ```
1858///
1859/// while starting with:
1860///
1861/// ```text
1862/// local_keys(child) = {}
1863/// ```
1864///
1865/// This ensures:
1866///
1867/// - parent state remains reachable
1868/// - new writes stay isolated to the new fork
1869/// - existence checks remain O(log n)
1870/// - no ancestry walking is required for normal reads
1871///
1872/// ## Example
1873///
1874/// ```text
1875/// Original branch:
1876///
1877/// local_keys = {k1, k2}
1878/// inherited_keys = {}
1879///
1880/// After fork:
1881///
1882/// local_keys = {}
1883/// inherited_keys = {k1, k2}
1884///
1885/// New write:
1886///
1887/// local_keys = {k3}
1888/// inherited_keys = {k1, k2}
1889/// ```
1890///
1891/// The child branch can see:
1892///
1893/// ```text
1894/// {k1, k2, k3}
1895/// ```
1896///
1897/// while sibling branches remain isolated from `k3`.
1898#[derive(Encode, Decode, Clone, Debug, Default)]
1899pub struct ForkLocalDepot {
1900 /// Keys inherited from previous generations through [`Accrete`].
1901 ///
1902 /// These represent all reachable historical entries inherited from
1903 /// ancestor branches.
1904 ///
1905 /// They are not created in the current branch generation, but remain
1906 /// visible because they were promoted forward during fork creation.
1907 ///
1908 /// This allows branch-local reads to access valid ancestor state
1909 /// without walking parent branches repeatedly.
1910 pub inherited_keys: BTreeSet<[u8; 32]>,
1911
1912 /// Keys created only in the current local generation.
1913 ///
1914 /// These represent the newest writes belonging exclusively to the
1915 /// current branch path.
1916 ///
1917 /// They are isolated to this branch until another fork occurs,
1918 /// at which point they are promoted into `inherited_keys` of the
1919 /// child branch through [`Accrete::accrete()`].
1920 ///
1921 /// This ensures writes remain fork-local while still preserving
1922 /// deterministic lineage inheritance for future branches.
1923 pub local_keys: BTreeSet<[u8; 32]>,
1924}
1925
1926impl Accrete for ForkLocalDepot {
1927 /// The original payload used to derive deterministic keys.
1928 ///
1929 /// Only the generated `[u8; 32]` key is stored internally.
1930 type Item = Vec<u8>;
1931
1932 /// Create the next generation.
1933 ///
1934 /// All current local keys are promoted into inherited history,
1935 /// and the returned generation starts with a fresh empty local layer.
1936 fn accrete(&self) -> Self {
1937 let mut inherited = self.inherited_keys.clone();
1938
1939 // Promote current local generation into inherited lineage
1940 inherited.extend(self.local_keys.iter().copied());
1941
1942 Self {
1943 inherited_keys: inherited,
1944 local_keys: BTreeSet::new(),
1945 }
1946 }
1947
1948 /// Returns inherited keys only.
1949 fn inherited(&self) -> Vec<[u8; 32]> {
1950 self.inherited_keys
1951 .iter()
1952 .copied()
1953 .collect()
1954 }
1955
1956 /// Returns current local generation keys only.
1957 fn local(&self) -> Vec<[u8; 32]> {
1958 self.local_keys
1959 .iter()
1960 .copied()
1961 .collect()
1962 }
1963
1964 /// Insert an item's deterministic key into the local generation.
1965 ///
1966 /// The payload itself is not stored here,
1967 /// only its stable key hash.
1968 ///
1969 /// Returns the deterministic key used for future lookups.
1970 fn add_to_local(
1971 &mut self,
1972 item: Self::Item,
1973 ) -> [u8; 32] {
1974 let key = Self::make_key(&item);
1975
1976 self.local_keys.insert(key);
1977
1978 key
1979 }
1980
1981 /// Checks existence only in local generation.
1982 fn exists_in_local(
1983 &self,
1984 key: &[u8; 32],
1985 ) -> bool {
1986 self.local_keys.contains(key)
1987 }
1988
1989 /// Checks existence only in inherited generations.
1990 fn exists_in_inherited(
1991 &self,
1992 key: &[u8; 32],
1993 ) -> bool {
1994 self.inherited_keys.contains(key)
1995 }
1996
1997 /// Remove a key only from the local generation.
1998 fn remove_from_local(
1999 &mut self,
2000 key: &[u8; 32],
2001 ) {
2002 self.local_keys.remove(key);
2003 }
2004
2005 /// Remove a key only from inherited generations.
2006 fn remove_from_inherited(
2007 &mut self,
2008 key: &[u8; 32],
2009 ) {
2010 self.inherited_keys.remove(key);
2011 }
2012}
2013
2014
2015/// **Authorization interface for a [`Routines`]**.
2016///
2017/// `RoutineOf` defines **who is allowed to execute** a routine at a given
2018/// point in time. It separates **authorization** from **execution**, which
2019/// is especially important in offchain contexts where signing keys,
2020/// rotation, and node-local state must be handled explicitly.
2021///
2022/// ## Why this exists
2023///
2024/// Offchain workers do not have the same execution guarantees as runtime
2025/// calls:
2026/// - there is no transactional rollback,
2027/// - failures do not revert state,
2028/// - and execution is best-effort.
2029///
2030/// Because of this, *authorization must be explicit* and *checked separately*
2031/// before a routine is allowed to run. `RoutineOf` provides a uniform way to:
2032///
2033/// - derive the concrete identifier (e.g. public key) authorized to run a routine,
2034/// - enforce key rotation and role-based access,
2035/// - fail early if the node is misconfigured or missing required keys.
2036///
2037/// ## Design principles
2038///
2039/// - `who()` must be **pure**: it must not mutate state.
2040/// - Failures are logged via [`Logging`] and treated as hard stops.
2041/// - The returned `Identifier` is typically used to sign payloads or
2042/// parameterize execution.
2043///
2044/// ## Example
2045///
2046/// ```text
2047/// Determine authorized signer
2048/// |
2049/// V
2050/// who() -> PublicKey
2051/// |
2052/// V
2053/// run_service(by = PublicKey)
2054/// ```
2055pub trait RoutineOf<Identifier, TimeStamp>: Logging<TimeStamp> + Routines<TimeStamp>
2056where
2057 TimeStamp: Time,
2058 Identifier: Portable,
2059{
2060 /// Returns the identifier authorized to execute the routine.
2061 ///
2062 /// If no valid identifier exists (e.g. missing key, inconsistent state),
2063 /// an error is logged and execution must not proceed.
2064 fn who(at: &TimeStamp) -> Result<Identifier, Self::Logger>;
2065}
2066
2067/// **Structured execution interface for offchain routines**.
2068///
2069/// `Routines` provides a **disciplined execution model** for offchain workers,
2070/// replacing ad-hoc logic with explicit phases and well-defined failure
2071/// semantics.
2072///
2073/// ## Why structured routines are needed
2074///
2075/// Offchain workers are fundamentally different from runtime calls:
2076///
2077/// | Runtime calls | Offchain workers |
2078/// |----------------------------------|------------------------------------|
2079/// | Transactional | Best-effort |
2080/// | Automatic rollback on error | No rollback |
2081/// | State changes are atomic | Partial execution is possible |
2082/// | Errors bubble naturally | Errors must be handled manually |
2083///
2084/// As a result, offchain logic **must be structured explicitly** to ensure:
2085///
2086/// - invariants are checked before execution,
2087/// - routines run to *intentional completion*,
2088/// - partial state does not silently corrupt future runs,
2089/// - failures are observable and diagnosable.
2090///
2091/// The `Routines` trait enforces this structure.
2092///
2093/// ## Execution model
2094///
2095/// ```text
2096/// |-------------|
2097/// | can_run() | <- check invariants, prerequisites
2098/// |-----|-------|
2099/// |
2100/// V
2101/// |-------------|
2102/// | run_service | <- perform the operation
2103/// |-----|-------|
2104/// |
2105/// V
2106/// |-------------|
2107/// | on_ran_* | <- bookkeeping, metrics, logging
2108/// |-------------|
2109/// ```
2110///
2111/// Each phase has a distinct responsibility, making offchain logic easier
2112/// to reason about, test, and evolve.
2113///
2114/// ## Failure semantics
2115///
2116/// - Any failure is **logged** via [`Logging`] and returned as `Logger`.
2117/// - Callers must treat failures as *hard stops* for the current routine.
2118/// - Subsequent routines may or may not execute, depending on orchestration.
2119///
2120/// This explicit handling avoids implicit control flow and makes routine
2121/// dependencies visible.
2122///
2123/// ## Arranging multiple routines
2124///
2125/// Structured routines compose naturally into pipelines:
2126///
2127/// ```text
2128/// Example
2129/// -------
2130/// Init -> Declare -> Rotate -> Elect
2131/// ```
2132///
2133/// Each routine:
2134/// - validates its own prerequisites,
2135/// - executes independently,
2136/// - leaves the system in a well-defined state.
2137///
2138/// This allows offchain workers to act as **deterministic coordinators**
2139/// rather than monolithic scripts.
2140///
2141/// ## Logging and observability
2142///
2143/// Because routines run outside the runtime's transactional model,
2144/// **logging is the primary observability mechanism**.
2145///
2146/// By integrating with [`Logging`]:
2147/// - all errors are logged exactly once,
2148/// - routine boundaries are visible in logs,
2149/// - execution can be traced across blocks.
2150///
2151/// This makes post-mortem debugging and operational monitoring feasible.
2152///
2153/// ## Example usage
2154///
2155/// ```text
2156/// let routine = MyRoutine { at: block };
2157///
2158/// if routine.can_run().is_ok() {
2159/// routine.run_service()?;
2160/// routine.on_ran_service();
2161/// }
2162/// ```
2163pub trait Routines<TimeStamp>: Logging<TimeStamp>
2164where
2165 TimeStamp: Time,
2166{
2167 /// Checks whether the routine is allowed to run.
2168 ///
2169 /// This method must:
2170 /// - validate prerequisites,
2171 /// - check invariants,
2172 /// - avoid mutating state.
2173 ///
2174 /// It exists to prevent partial execution in environments without
2175 /// rollback guarantees.
2176 fn can_run(&self) -> Result<(), Self::Logger>;
2177
2178 /// Executes the routine's core logic.
2179 ///
2180 /// Implementations should assume that `can_run` has already succeeded
2181 /// and focus solely on performing the intended operation.
2182 fn run_service(&self) -> Result<(), Self::Logger>;
2183
2184 /// Hook invoked after successful execution.
2185 ///
2186 /// This method is intended for:
2187 /// - logging,
2188 /// - metrics,
2189 /// - bookkeeping,
2190 /// - or emitting side effects that must only occur on success.
2191 ///
2192 /// The default implementation is a no-op.
2193 fn on_ran_service(&self) {}
2194}