signet_cold/lib.rs
1//! Async cold storage engine for historical Ethereum data.
2//!
3//! This module provides an abstraction over various backend storage systems
4//! for historical blockchain data. Unlike hot storage which uses transaction
5//! semantics for mutable state, cold storage is optimized for:
6//!
7//! - **Append-only writes** with block-ordered data
8//! - **Efficient bulk reads** by block number or index
9//! - **Truncation** (reorg handling) that removes data beyond a certain block
10//! - **Index maintenance** for hash-based lookups
11//!
12//! # Architecture
13//!
14//! The cold storage engine uses a task-based architecture:
15//!
16//! - [`ColdStorage`] trait defines the backend interface
17//! - [`ColdStorageTask`] processes requests from channels
18//! - [`ColdStorageHandle`] provides full read/write access
19//! - [`ColdStorageReadHandle`] provides read-only access
20//!
21//! # Channel Separation
22//!
23//! Reads and writes use **separate channels**:
24//!
25//! - **Read channel**: Shared between [`ColdStorageHandle`] and
26//! [`ColdStorageReadHandle`]. Reads are processed concurrently (up to 64 in
27//! flight).
28//! - **Write channel**: Exclusive to [`ColdStorageHandle`]. Writes are
29//! processed sequentially to maintain ordering.
30//!
31//! This design allows read-heavy workloads to proceed without being blocked by
32//! write operations, while ensuring write ordering is preserved.
33//!
34//! # Consistency Model
35//!
36//! Cold storage is **eventually consistent** with hot storage. Hot storage is
37//! always authoritative.
38//!
39//! ## When Cold May Lag
40//!
41//! - **Normal operation**: Writes are dispatched asynchronously. Cold may be a
42//! few blocks behind hot during normal block processing.
43//! - **Backpressure**: If cold storage cannot keep up, the write channel fills.
44//! Dispatch methods return [`ColdStorageError::Backpressure`].
45//! - **Task termination**: If the cold storage task stops, writes cannot be
46//! dispatched. Dispatch methods return [`ColdStorageError::TaskTerminated`].
47//!
48//! ## When Cold May Have Stale Data
49//!
50//! - **Failed truncate after reorg**: If a truncate dispatch fails, cold may
51//! temporarily contain blocks that hot has unwound. This is safe because hot
52//! is authoritative, but cold queries may return stale data.
53//!
54//! ## Recovery Procedures
55//!
56//! Use these methods on `UnifiedStorage` (from `signet-storage`) to detect and
57//! recover from inconsistencies:
58//!
59//! - **`cold_lag()`**: Returns `Some(first_missing_block)` if cold is behind
60//! hot. Returns `None` if synced.
61//! - **`replay_to_cold()`**: Re-sends blocks to cold storage. Use after
62//! detecting a gap or recovering from task failure.
63//!
64//! # Example
65//!
66//! ```ignore
67//! use tokio_util::sync::CancellationToken;
68//! use signet_cold::{ColdStorageTask, mem::MemColdBackend};
69//!
70//! let cancel = CancellationToken::new();
71//! let handle = ColdStorageTask::spawn(MemColdBackend::new(), cancel);
72//!
73//! // Use the handle to interact with cold storage
74//! let header = handle.get_header_by_number(100).await?;
75//!
76//! // Get a read-only handle for query-only components
77//! let reader = handle.reader();
78//! let tx = reader.get_tx_by_hash(hash).await?;
79//! ```
80//!
81//! # Future Work: Streaming Writes
82//!
83//! For bulk data loading (e.g., initial sync or historical backfill), a
84//! streaming write interface is planned:
85//!
86//! ```ignore
87//! /// Streaming write session for bulk data loading.
88//! ///
89//! /// This type enables efficient bulk writes by buffering data and
90//! /// batching backend operations. Use for initial sync or historical
91//! /// backfill scenarios.
92//! pub struct ColdStreamingWrite { /* ... */ }
93//!
94//! impl ColdStreamingWrite {
95//! /// Create a new streaming write session.
96//! ///
97//! /// # Arguments
98//! ///
99//! /// * `handle` - The cold storage handle to write through
100//! /// * `buffer_capacity` - Number of blocks to buffer before flushing
101//! pub fn new(handle: &ColdStorageHandle, buffer_capacity: usize) -> Self;
102//!
103//! /// Push a block to the write buffer.
104//! ///
105//! /// May trigger an automatic flush if the buffer is full.
106//! pub async fn push(&mut self, block: BlockData) -> ColdResult<()>;
107//!
108//! /// Flush buffered blocks to storage.
109//! pub async fn flush(&mut self) -> ColdResult<()>;
110//!
111//! /// Create a checkpoint at the given block number.
112//! ///
113//! /// Flushes the buffer and records that blocks up to this number
114//! /// have been durably written. Useful for resumable sync.
115//! pub async fn checkpoint(&mut self, block: BlockNumber) -> ColdResult<()>;
116//!
117//! /// Finish the streaming session.
118//! ///
119//! /// Flushes any remaining buffered data.
120//! pub async fn finish(self) -> ColdResult<()>;
121//! }
122//! ```
123//!
124//! This is a design sketch; no implementation is provided yet.
125//!
126//! # Feature Flags
127//!
128//! - **`in-memory`**: Enables the `mem` module, providing an in-memory
129//! [`ColdStorage`] backend for testing.
130//! - **`test-utils`**: Enables the `conformance` module with backend
131//! conformance tests. Implies `in-memory`.
132
133#![warn(
134 missing_copy_implementations,
135 missing_debug_implementations,
136 missing_docs,
137 unreachable_pub,
138 clippy::missing_const_for_fn,
139 rustdoc::all
140)]
141#![cfg_attr(not(test), warn(unused_crate_dependencies))]
142#![deny(unused_must_use, rust_2018_idioms)]
143#![cfg_attr(docsrs, feature(doc_cfg))]
144
145mod error;
146pub use error::{ColdResult, ColdStorageError};
147mod request;
148pub use request::{AppendBlockRequest, ColdReadRequest, ColdWriteRequest, Responder};
149mod specifier;
150pub use alloy::rpc::types::{Filter, Log as RpcLog};
151pub use signet_storage_types::{Confirmed, Recovered};
152pub use specifier::{
153 HeaderSpecifier, ReceiptSpecifier, SignetEventsSpecifier, TransactionSpecifier,
154 ZenithHeaderSpecifier,
155};
156
157mod cold_receipt;
158pub use cold_receipt::ColdReceipt;
159mod stream;
160pub use stream::{StreamParams, produce_log_stream_default};
161mod traits;
162pub use traits::{BlockData, ColdStorage, LogStream};
163
164pub mod connect;
165pub use connect::ColdConnect;
166
167/// Task module containing the storage task runner and handles.
168pub mod task;
169pub use task::{ColdStorageHandle, ColdStorageReadHandle, ColdStorageTask};
170
171/// Conformance tests for cold storage backends.
172#[cfg(any(test, feature = "test-utils"))]
173pub mod conformance;
174
175#[cfg(any(test, feature = "in-memory"))]
176pub mod mem;