Skip to content

Commit 21821e4

Browse files
prestwichclaude
andauthored
docs: add Claude Code skill for signet-storage library reference (#17)
Adds a project-level Claude Code skill that provides a comprehensive reference for the storage library architecture, traits, types, tables, and usage patterns. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d81fd61 commit 21821e4

File tree

2 files changed

+266
-1
lines changed

2 files changed

+266
-1
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
---
2+
name: signet-storage
3+
description: Reference for the signet storage library architecture, traits, types, and usage patterns. Use when working with hot storage, cold storage, unified storage, revm integration, history reads, or any signet-storage crate.
4+
user-invocable: true
5+
---
6+
7+
# Signet Storage Library Reference
8+
9+
## Workspace Crates
10+
11+
| Crate | Path | Purpose |
12+
|-------|------|---------|
13+
| `signet-storage-types` | `crates/types/` | Shared primitive types (Account, ExecutedBlock, SealedHeader, etc.) |
14+
| `signet-cold` | `crates/cold/` | Async cold storage engine with task-based handles and LRU cache |
15+
| `signet-cold-mdbx` | `crates/cold-mdbx/` | MDBX backend implementing `ColdStorage` |
16+
| `signet-hot` | `crates/hot/` | Trait-based hot storage abstractions (HotKv, HistoryRead, RevmRead) |
17+
| `signet-hot-mdbx` | `crates/hot-mdbx/` | MDBX implementation of hot storage traits |
18+
| `signet-storage` | `crates/storage/` | Unified storage combining hot + cold |
19+
20+
## Architecture Overview
21+
22+
**Hot storage** provides fast transactional read/write of current blockchain
23+
state. It uses MDBX (or in-memory for tests) and supports height-aware
24+
historical state reconstruction through change sets.
25+
26+
**Cold storage** is append-only archival of full block data (transactions,
27+
receipts, events). It uses a task-based async pattern with channel-separated
28+
read/write handles and an LRU cache.
29+
30+
**Unified storage** (`UnifiedStorage`) combines both: hot writes are
31+
synchronous database transactions, cold writes are dispatched asynchronously
32+
(fire-and-forget). Hot errors are fatal; cold dispatch errors are recoverable.
33+
34+
## Trait Hierarchy
35+
36+
### Hot Storage
37+
38+
```
39+
HotKv -- Transaction factory (reader/writer)
40+
├── HotKvRead -- Raw + typed read operations
41+
│ ├── HotDbRead (blanket) -- Typed table accessors (get_header, get_account, etc.)
42+
│ │ ├── HistoryRead (blanket) -- History queries + height-aware reads
43+
│ │ └── UnsafeHistoryWrite (blanket) -- Low-level history writes
44+
│ └── RevmRead<T> -- revm DatabaseRef/Database adapter
45+
├── HotKvWrite (extends Read) -- Queued writes + commit
46+
│ ├── UnsafeDbWrite (blanket) -- Typed table writes
47+
│ ├── HistoryWrite (blanket) -- append_blocks, unwind_above, load_genesis
48+
│ └── RevmWrite<U> -- revm TryDatabaseCommit adapter
49+
```
50+
51+
### Cold Storage
52+
53+
```
54+
ColdStorage -- Async backend trait
55+
ColdStorageTask<B> -- Event loop processing requests
56+
ColdStorageHandle -- Full read/write handle (Deref to ReadHandle)
57+
ColdStorageReadHandle -- Read-only handle
58+
```
59+
60+
## Key Types
61+
62+
### `signet-storage-types`
63+
64+
- **`Account`** -- nonce, balance, optional bytecode_hash. Converts
65+
from/to `revm::state::AccountInfo`.
66+
- **`ExecutedBlock`** -- Complete block output: header, bundle,
67+
transactions, receipts, signet_events, zenith_header.
68+
- **`ExecutedBlockBuilder`** -- Builder for `ExecutedBlock`. Required:
69+
`header(SealedHeader)`, `bundle(BundleState)`. Optional: `transactions`,
70+
`receipts`, `signet_events`, `zenith_header`.
71+
- **`SealedHeader`** -- Type alias for `Sealed<Header>`.
72+
- **`ConfirmationMeta`** -- Block confirmation metadata (block_number,
73+
block_hash, transaction_index).
74+
- **`Confirmed<T>`** -- Re-exported from `signet-cold`, pairs a value
75+
with `ConfirmationMeta`.
76+
- **`TxLocation`** -- 16-byte fixed-size key (block number + tx index).
77+
- **`BlockNumberList`** / **`IntegerList`** -- Roaring bitmap-backed lists.
78+
- **`ShardedKey<T>`** -- Composite `(T, u64)` for sharded history tables.
79+
80+
### `signet-cold`
81+
82+
- **`ColdStorageTask::spawn(backend, cancel_token)`** -- Spawns the task
83+
loop and returns a `ColdStorageHandle`.
84+
- **`ColdStorageHandle`** -- Derefs to `ColdStorageReadHandle`. Write
85+
methods: `append_block()`, `append_blocks()`, `truncate_above()`.
86+
Fire-and-forget: `dispatch_append_blocks()`, `dispatch_truncate_above()`.
87+
- **`ColdStorageReadHandle`** -- Read methods:
88+
`get_header_by_number`, `get_header_by_hash`,
89+
`get_tx_by_hash`, `get_tx_by_block_and_index`,
90+
`get_receipt_by_hash`, `get_receipt_by_block_and_index`,
91+
`get_txs_in_block`, `get_tx_count`,
92+
`get_receipts_in_block`,
93+
`get_signet_events_by_number`, `get_zenith_header_by_number`,
94+
`get_latest_block`.
95+
96+
### `signet-hot`
97+
98+
- **`HotKv`** -- `reader()`, `writer()`, `revm_reader()`, `revm_writer()`,
99+
`revm_reader_at_height(height)`.
100+
- **`HotKvRead`** -- `raw_get`, `raw_get_dual`, `get<T: SingleKey>`,
101+
`get_dual<T: DualKey>`, `traverse<T>`, `traverse_dual<T>`.
102+
- **`HotKvWrite`** -- `queue_put<T>`, `queue_put_dual<T>`,
103+
`queue_delete<T>`, `queue_delete_dual<T>`, `queue_clear<T>`,
104+
`raw_commit(self)`.
105+
- **`HotDbRead`** (blanket) -- `get_header(n)`, `get_header_number(hash)`,
106+
`get_account(addr)`, `get_storage(addr, slot)`, `get_bytecode(hash)`.
107+
- **`HistoryRead`** (blanket) -- `get_account_at_height(addr, h)`,
108+
`get_storage_at_height(addr, slot, h)`, `get_account_change(block, addr)`,
109+
`get_storage_change(block, addr, slot)`, `get_chain_tip()`,
110+
`get_execution_range()`, `last_block_number()`, `has_block(n)`,
111+
`check_height(h)`.
112+
- **`HistoryWrite`** (blanket) -- `validate_chain_extension(headers)`,
113+
`append_blocks(iter)`, `unwind_above(block)`, `load_genesis(genesis, forks)`.
114+
- **`RevmRead<T>`** -- `new(reader)` for current state,
115+
`at_height(reader, height)` for historical state. Implements revm
116+
`Database` + `DatabaseRef`.
117+
- **`RevmWrite<U>`** -- `new(writer)`, `persist(self)`. Implements revm
118+
`Database` + `DatabaseRef` + `TryDatabaseCommit`.
119+
120+
### `signet-hot-mdbx`
121+
122+
- **`DatabaseArguments::new()`** -- Builder with `.with_geometry_max_size()`,
123+
`.with_sync_mode()`, `.with_growth_step()`, `.with_exclusive()`,
124+
`.with_max_readers()`. Terminal: `.open_ro(path)`, `.open_rw(path)`.
125+
- **`DatabaseEnv`** -- Implements `HotKv`.
126+
127+
### `signet-storage`
128+
129+
- **`UnifiedStorage::new(hot, cold_handle)`** -- `append_blocks(blocks)`,
130+
`unwind_above(block)`, `cold_lag()`, `replay_to_cold()`,
131+
`reader()`, `revm_reader()`, `revm_reader_at_height(h)`,
132+
`hot()`, `cold()`, `cold_reader()`.
133+
134+
## Hot Storage Tables
135+
136+
| Table | Key | Key2 | Value | Kind |
137+
|-------|-----|------|-------|------|
138+
| `Headers` | `BlockNumber` | -- | `Header` | SingleKey |
139+
| `HeaderNumbers` | `B256` | -- | `BlockNumber` | SingleKey |
140+
| `Bytecodes` | `B256` | -- | `Bytecode` | SingleKey |
141+
| `PlainAccountState` | `Address` | -- | `Account` | SingleKey |
142+
| `PlainStorageState` | `Address` | `U256` | `U256` | DualKey |
143+
| `AccountsHistory` | `Address` | `u64` | `BlockNumberList` | DualKey |
144+
| `AccountChangeSets` | `BlockNumber` | `Address` | `Account` | DualKey |
145+
| `StorageHistory` | `Address` | `ShardedKey<U256>` | `BlockNumberList` | DualKey |
146+
| `StorageChangeSets` | `(u64, Address)` | `U256` | `U256` | DualKey |
147+
148+
## Cold Storage Tables (MDBX)
149+
150+
| Table | Key | Key2 | Value |
151+
|-------|-----|------|-------|
152+
| `ColdHeaders` | `BlockNumber` | -- | `Header` |
153+
| `ColdTransactions` | `BlockNumber` | `u64` | `TransactionSigned` |
154+
| `ColdReceipts` | `BlockNumber` | `u64` | `Receipt` |
155+
| `ColdSignetEvents` | `BlockNumber` | `u64` | `DbSignetEvent` |
156+
| `ColdZenithHeaders` | `BlockNumber` | -- | `DbZenithHeader` |
157+
| `ColdBlockHashIndex` | `B256` | -- | `BlockNumber` |
158+
| `ColdTxHashIndex` | `B256` | -- | `TxLocation` |
159+
| `ColdMetadata` | `MetadataKey` | -- | `BlockNumber` |
160+
161+
## Feature Flags
162+
163+
| Crate | Feature | Effect |
164+
|-------|---------|--------|
165+
| `signet-cold` | `in-memory` | `mem::MemColdBackend` |
166+
| `signet-cold` | `test-utils` | Conformance tests + in-memory |
167+
| `signet-hot` | `in-memory` | `mem::MemKv` |
168+
| `signet-hot` | `test-utils` | Conformance tests + in-memory |
169+
| `signet-hot-mdbx` | `test-utils` | Test utilities + tempfile |
170+
| `signet-hot-mdbx` | `disable-lock` | Disables file-based storage lock |
171+
| `signet-cold-mdbx` | `test-utils` | Cold test utilities |
172+
| `signet-storage` | `test-utils` | Enables all sub-crate test-utils |
173+
174+
## Error Types
175+
176+
- **`HotKvError`** -- `Inner(E)`, `Deser`, `WriteLocked`, `NoBlocks`,
177+
`HeightOutOfRange`.
178+
- **`HistoryError<E>`** -- `NonContiguousBlock`, `ParentHashMismatch`,
179+
`DbNotEmpty`, `EmptyRange`, `NoBlocks`, `HeightOutOfRange`, `Db(E)`,
180+
`IntList`.
181+
- **`ColdStorageError`** -- `Backend(E)`, `NotFound`, `Cancelled`,
182+
`Backpressure`, `TaskTerminated`.
183+
- **`StorageError`** -- `Hot(HistoryError<HotKvError>)` |
184+
`Cold(ColdStorageError)`.
185+
186+
## Usage Patterns
187+
188+
### Setting up unified storage
189+
190+
```rust
191+
use signet_storage::{UnifiedStorage, ColdStorageTask};
192+
use signet_hot_mdbx::DatabaseArguments;
193+
use signet_cold_mdbx::MdbxColdBackend;
194+
use tokio_util::sync::CancellationToken;
195+
196+
// Open hot storage
197+
let hot = DatabaseArguments::new().open_rw(&hot_path).unwrap();
198+
199+
// Open cold backend and spawn task
200+
let cold_backend = MdbxColdBackend::open_rw(&cold_path).unwrap();
201+
let cancel = CancellationToken::new();
202+
let cold_handle = ColdStorageTask::spawn(cold_backend, cancel.clone());
203+
204+
// Create unified storage
205+
let storage = UnifiedStorage::new(hot, cold_handle);
206+
```
207+
208+
### Reading current state via revm
209+
210+
```rust
211+
let reader = storage.revm_reader().unwrap();
212+
let account = reader.basic_ref(address).unwrap();
213+
let value = reader.storage_ref(address, slot).unwrap();
214+
```
215+
216+
### Reading historical state
217+
218+
```rust
219+
let reader = storage.revm_reader_at_height(100).unwrap();
220+
let account = reader.basic_ref(address).unwrap();
221+
```
222+
223+
### Writing blocks
224+
225+
```rust
226+
use signet_storage_types::ExecutedBlockBuilder;
227+
228+
let block = ExecutedBlockBuilder::new()
229+
.header(sealed_header)
230+
.bundle(bundle_state)
231+
.transactions(txs)
232+
.receipts(receipts)
233+
.build()
234+
.unwrap();
235+
236+
storage.append_blocks(vec![block]).unwrap();
237+
```
238+
239+
### Querying cold storage directly
240+
241+
```rust
242+
let cold = storage.cold_reader();
243+
let header = cold.get_header_by_number(42).await.unwrap();
244+
let tx = cold.get_tx_by_hash(tx_hash).await.unwrap();
245+
let receipts = cold.get_receipts_in_block(42).await.unwrap();
246+
```
247+
248+
### Handling reorgs
249+
250+
```rust
251+
// Unwind hot state above block 100, truncate cold
252+
storage.unwind_above(100).unwrap();
253+
```
254+
255+
### Using hot storage directly
256+
257+
```rust
258+
use signet_hot::{HistoryRead, HotKv};
259+
260+
let hot = storage.hot();
261+
let reader = hot.reader().unwrap();
262+
let tip = reader.get_chain_tip().unwrap();
263+
let account = reader.get_account_at_height(address, 50).unwrap();
264+
```

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
.DS_Store
33
Cargo.lock
44
.idea/
5-
.claude/
5+
.claude/*
6+
!.claude/skills/

0 commit comments

Comments
 (0)