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