Skip to content

Commit 84c813c

Browse files
committed
feat: add storage interface with pruning methods
1 parent b5d8c02 commit 84c813c

File tree

4 files changed

+178
-1
lines changed

4 files changed

+178
-1
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/exex/external-proofs/Cargo.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,20 @@ workspace = true
1414
[dependencies]
1515
reth-exex.workspace = true
1616

17+
# ethereum
18+
alloy-primitives = { workspace = true }
19+
1720
# reth
18-
reth-provider.workspace = true
21+
reth-db-api.workspace = true
1922
reth-node-types.workspace = true
2023
reth-node-api.workspace = true
24+
reth-primitives-traits.workspace = true
25+
reth-provider.workspace = true
26+
reth-trie.workspace = true
2127

2228
# misc
29+
auto_impl.workspace = true
30+
async-trait.workspace = true
2331
eyre.workspace = true
2432
futures-util.workspace = true
33+
serde.workspace = true

crates/exex/external-proofs/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use reth_provider::StateReader;
77

88
use reth_exex::{ExExContext, ExExEvent};
99

10+
mod storage;
11+
1012
/// Saves and serves trie nodes to make proofs faster. This handles the process of
1113
/// saving the current state, new blocks as they're added, and serving proof RPCs
1214
/// based on the saved data.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#![allow(dead_code, unreachable_pub)]
2+
use alloy_primitives::{map::HashMap, B256, U256};
3+
use async_trait::async_trait;
4+
use auto_impl::auto_impl;
5+
use reth_primitives_traits::Account;
6+
use reth_trie::{updates::TrieUpdates, BranchNodeCompact, HashedPostState, Nibbles};
7+
use std::fmt::Debug;
8+
9+
/// Error type for storage operations
10+
pub enum ExternalStorageError {
11+
Other(eyre::Error),
12+
}
13+
14+
/// Result type for storage operations
15+
pub type ExternalStorageResult<T> = Result<T, ExternalStorageError>;
16+
17+
/// Seeks and iterates over trie nodes in the database by path (lexicographical order)
18+
pub trait ExternalTrieCursor: Send + Sync {
19+
/// Seek to an exact path, otherwise return None if not found.
20+
fn seek_exact(
21+
&mut self,
22+
path: Nibbles,
23+
) -> ExternalStorageResult<Option<(Nibbles, BranchNodeCompact)>>;
24+
25+
/// Seek to a path, otherwise return the first path greater than the given path
26+
/// lexicographically.
27+
fn seek(
28+
&mut self,
29+
path: Nibbles,
30+
) -> ExternalStorageResult<Option<(Nibbles, BranchNodeCompact)>>;
31+
32+
/// Move the cursor to the next path and return it.
33+
fn next(&mut self) -> ExternalStorageResult<Option<(Nibbles, BranchNodeCompact)>>;
34+
35+
/// Get the current path.
36+
fn current(&mut self) -> ExternalStorageResult<Option<Nibbles>>;
37+
}
38+
39+
/// Seeks and iterates over hashed entries in the database by key.
40+
pub trait ExternalHashedCursor: Send + Sync {
41+
/// Value returned by the cursor.
42+
type Value: std::fmt::Debug;
43+
44+
/// Seek an entry greater or equal to the given key and position the cursor there.
45+
/// Returns the first entry with the key greater or equal to the sought key.
46+
fn seek(&mut self, key: B256) -> ExternalStorageResult<Option<(B256, Self::Value)>>;
47+
48+
/// Move the cursor to the next entry and return it.
49+
fn next(&mut self) -> ExternalStorageResult<Option<(B256, Self::Value)>>;
50+
}
51+
52+
/// Trait for reading trie nodes from the database.
53+
///
54+
/// Only leaf nodes and some branch nodes are stored. The bottom layer of branch nodes
55+
/// are not stored to reduce write amplification. This matches Reth's non-historical trie storage.
56+
#[async_trait]
57+
#[auto_impl(Arc)]
58+
pub trait ExternalStorage: Send + Sync + Debug {
59+
type TrieCursor: ExternalTrieCursor;
60+
type StorageCursor: ExternalHashedCursor<Value = U256>;
61+
type AccountHashedCursor: ExternalHashedCursor<Value = Account>;
62+
63+
/// Store a batch of account trie branches. Used for saving existing state. For live state
64+
/// capture, use [store_trie_updates](ExternalStorage::store_trie_updates).
65+
async fn store_account_branches(
66+
&self,
67+
block_number: u64,
68+
updates: Vec<(Nibbles, Option<BranchNodeCompact>)>,
69+
) -> ExternalStorageResult<()>;
70+
71+
/// Store a batch of storage trie branches. Used for saving existing state.
72+
async fn store_storage_branches(
73+
&self,
74+
block_number: u64,
75+
hashed_address: B256,
76+
items: Vec<(Nibbles, Option<BranchNodeCompact>)>,
77+
) -> ExternalStorageResult<()>;
78+
79+
/// Store a batch of account trie leaf nodes. Used for saving existing state.
80+
async fn store_hashed_accounts(
81+
&self,
82+
accounts: Vec<(B256, Option<Account>)>,
83+
block_number: u64,
84+
) -> ExternalStorageResult<()>;
85+
86+
/// Store a batch of storage trie leaf nodes. Used for saving existing state.
87+
async fn store_hashed_storages(
88+
&self,
89+
hashed_address: B256,
90+
storages: Vec<(B256, U256)>,
91+
block_number: u64,
92+
) -> ExternalStorageResult<()>;
93+
94+
/// Get the earliest block number and hash that has been stored
95+
///
96+
/// This is used to determine the block number of trie nodes with block number 0.
97+
/// All earliest block numbers are stored in 0 to reduce updates required to prune trie nodes.
98+
async fn get_earliest_block_number(&self) -> ExternalStorageResult<Option<(u64, B256)>>;
99+
100+
/// Get a trie cursor for the storage backend
101+
fn trie_cursor(
102+
&self,
103+
hashed_address: Option<B256>,
104+
max_block_number: u64,
105+
) -> ExternalStorageResult<Self::TrieCursor>;
106+
107+
/// Get a storage cursor for the storage backend
108+
fn storage_hashed_cursor(
109+
&self,
110+
hashed_address: B256,
111+
max_block_number: u64,
112+
) -> ExternalStorageResult<Self::StorageCursor>;
113+
114+
/// Get an account hashed cursor for the storage backend
115+
fn account_hashed_cursor(
116+
&self,
117+
max_block_number: u64,
118+
) -> ExternalStorageResult<Self::AccountHashedCursor>;
119+
120+
/// Store a batch of trie updates.
121+
///
122+
/// If wiped is true, the entire storage trie is wiped, but this is unsupported going forward,
123+
/// so should only happen for legacy reasons.
124+
async fn store_trie_updates(
125+
&self,
126+
block_number: u64,
127+
trie_updates: TrieUpdates,
128+
post_state: HashedPostState,
129+
) -> ExternalStorageResult<()>;
130+
131+
/// Fetch all updates for a given block number.
132+
async fn fetch_trie_updates(
133+
&self,
134+
block_number: u64,
135+
) -> ExternalStorageResult<(TrieUpdates, HashedPostState)>;
136+
137+
/// Applies `TrieUpdates` to the earliest state (updating/deleting nodes) and updates the
138+
/// earliest block number.
139+
async fn prune_earliest_state(
140+
&self,
141+
new_earliest_block_number: u64,
142+
branches_diff: TrieUpdates,
143+
leaves_diff: HashedPostState,
144+
) -> ExternalStorageResult<()>;
145+
146+
/// Deletes all updates > `latest_common_block_number` and
147+
async fn replace_updates(
148+
&self,
149+
latest_common_block_number: u64,
150+
blocks_to_add: HashMap<u64, TrieUpdates>,
151+
) -> ExternalStorageResult<()>;
152+
153+
/// Set the earliest block number and hash that has been stored
154+
async fn set_earliest_block_number(
155+
&self,
156+
block_number: u64,
157+
hash: B256,
158+
) -> ExternalStorageResult<()>;
159+
}

0 commit comments

Comments
 (0)