Skip to main content

reth_db_api/tables/
mod.rs

1//! Tables and data models.
2//!
3//! # Overview
4//!
5//! This module defines the tables in reth, as well as some table-related abstractions:
6//!
7//! - [`codecs`] integrates different codecs into [`Encode`] and [`Decode`]
8//! - [`models`](crate::models) defines the values written to tables
9//!
10//! # Database Tour
11//!
12//! TODO(onbjerg): Find appropriate format for this...
13
14pub mod codecs;
15
16mod raw;
17pub use raw::{RawDupSort, RawKey, RawTable, RawValue, TableRawRow};
18
19use crate::{
20    models::{
21        accounts::BlockNumberAddress,
22        blocks::{HeaderHash, StoredBlockOmmers},
23        storage_sharded_key::StorageShardedKey,
24        AccountBeforeTx, ClientVersion, CompactU256, IntegerList, ShardedKey,
25        StoredBlockBodyIndices, StoredBlockWithdrawals,
26    },
27    table::{Decode, DupSort, Encode, Table, TableInfo},
28};
29use alloy_consensus::Header;
30use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash, TxNumber, B256};
31use reth_ethereum_primitives::{Receipt, TransactionSigned};
32use reth_primitives_traits::{Account, Bytecode, StorageEntry};
33use reth_prune_types::{PruneCheckpoint, PruneSegment};
34use reth_stages_types::StageCheckpoint;
35use reth_trie_common::{
36    BranchNodeCompact, PackedStorageTrieEntry, PackedStoredNibbles, PackedStoredNibblesSubKey,
37    StorageTrieEntry, StoredNibbles, StoredNibblesSubKey,
38};
39use serde::{Deserialize, Serialize};
40use std::fmt;
41
42/// Enum for the types of tables present in libmdbx.
43#[derive(Debug, PartialEq, Eq, Copy, Clone)]
44pub enum TableType {
45    /// key value table
46    Table,
47    /// Duplicate key value table
48    DupSort,
49}
50
51/// The general purpose of this is to use with a combination of Tables enum,
52/// by implementing a `TableViewer` trait you can operate on db tables in an abstract way.
53///
54/// # Example
55///
56/// ```
57/// use reth_db_api::{
58///     table::{DupSort, Table},
59///     TableViewer, Tables,
60/// };
61///
62/// struct MyTableViewer;
63///
64/// impl TableViewer<()> for MyTableViewer {
65///     type Error = &'static str;
66///
67///     fn view<T: Table>(&self) -> Result<(), Self::Error> {
68///         // operate on table in a generic way
69///         Ok(())
70///     }
71///
72///     fn view_dupsort<T: DupSort>(&self) -> Result<(), Self::Error> {
73///         // operate on a dupsort table in a generic way
74///         Ok(())
75///     }
76/// }
77///
78/// let viewer = MyTableViewer {};
79///
80/// let _ = Tables::Headers.view(&viewer);
81/// let _ = Tables::Transactions.view(&viewer);
82/// ```
83pub trait TableViewer<R> {
84    /// The error type returned by the viewer.
85    type Error;
86
87    /// Calls `view` with the correct table type.
88    fn view_rt(&self, table: Tables) -> Result<R, Self::Error> {
89        table.view(self)
90    }
91
92    /// Operate on the table in a generic way.
93    fn view<T: Table>(&self) -> Result<R, Self::Error>;
94
95    /// Operate on the dupsort table in a generic way.
96    ///
97    /// By default, the `view` function is invoked unless overridden.
98    fn view_dupsort<T: DupSort>(&self) -> Result<R, Self::Error>
99    where
100        T::Value: reth_primitives_traits::ValueWithSubKey<SubKey = T::SubKey>,
101    {
102        self.view::<T>()
103    }
104}
105
106/// General trait for defining the set of tables
107/// Used to initialize database
108pub trait TableSet {
109    /// Returns an iterator over the tables
110    fn tables() -> Box<dyn Iterator<Item = Box<dyn TableInfo>>>;
111}
112
113/// Defines all the tables in the database.
114#[macro_export]
115macro_rules! tables {
116    (@bool) => { false };
117    (@bool $($t:tt)+) => { true };
118
119    (@view $name:ident $v:ident) => { $v.view::<$name>() };
120    (@view $name:ident $v:ident $_subkey:ty) => { $v.view_dupsort::<$name>() };
121
122    (@value_doc $key:ty, $value:ty) => {
123        concat!("[`", stringify!($value), "`]")
124    };
125    // Don't generate links if we have generics
126    (@value_doc $key:ty, $value:ty, $($generic:ident),*) => {
127        concat!("`", stringify!($value), "`")
128    };
129
130    ($($(#[$attr:meta])* table $name:ident$(<$($generic:ident $(= $default:ty)?),*>)? { type Key = $key:ty; type Value = $value:ty; $(type SubKey = $subkey:ty;)? } )*) => {
131        // Table marker types.
132        $(
133            $(#[$attr])*
134            ///
135            #[doc = concat!("Marker type representing a database table mapping [`", stringify!($key), "`] to ", tables!(@value_doc $key, $value, $($($generic),*)?), ".")]
136            $(
137                #[doc = concat!("\n\nThis table's `DUPSORT` subkey is [`", stringify!($subkey), "`].")]
138            )?
139            pub struct $name$(<$($generic $( = $default)?),*>)? {
140                _private: std::marker::PhantomData<($($($generic,)*)?)>,
141            }
142
143            // Ideally this implementation wouldn't exist, but it is necessary to derive `Debug`
144            // when a type is generic over `T: Table`. See: https://github.com/rust-lang/rust/issues/26925
145            impl$(<$($generic),*>)? fmt::Debug for $name$(<$($generic),*>)? {
146                fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
147                    unreachable!("this type cannot be instantiated")
148                }
149            }
150
151            impl$(<$($generic),*>)? $crate::table::Table for $name$(<$($generic),*>)?
152            where
153                $value: $crate::table::Value + 'static
154                $($(,$generic: Send + Sync)*)?
155            {
156                const NAME: &'static str = table_names::$name;
157                const DUPSORT: bool = tables!(@bool $($subkey)?);
158
159                type Key = $key;
160                type Value = $value;
161            }
162
163            $(
164                impl DupSort for $name {
165                    type SubKey = $subkey;
166                }
167            )?
168        )*
169
170        // Tables enum.
171
172        /// A table in the database.
173        #[derive(Clone, Copy, PartialEq, Eq, Hash)]
174        pub enum Tables {
175            $(
176                #[doc = concat!("The [`", stringify!($name), "`] database table.")]
177                $name,
178            )*
179        }
180
181        impl Tables {
182            /// All the tables in the database.
183            pub const ALL: &'static [Self] = &[$(Self::$name,)*];
184
185            /// The number of tables in the database.
186            pub const COUNT: usize = Self::ALL.len();
187
188            /// Returns the name of the table as a string.
189            pub const fn name(&self) -> &'static str {
190                match self {
191                    $(
192                        Self::$name => table_names::$name,
193                    )*
194                }
195            }
196
197            /// Returns `true` if the table is a `DUPSORT` table.
198            pub const fn is_dupsort(&self) -> bool {
199                match self {
200                    $(
201                        Self::$name => tables!(@bool $($subkey)?),
202                    )*
203                }
204            }
205
206            /// The type of the given table in database.
207            pub const fn table_type(&self) -> TableType {
208                if self.is_dupsort() {
209                    TableType::DupSort
210                } else {
211                    TableType::Table
212                }
213            }
214
215            /// Allows to operate on specific table type
216            pub fn view<T, R>(&self, visitor: &T) -> Result<R, T::Error>
217            where
218                T: ?Sized + TableViewer<R>,
219            {
220                match self {
221                    $(
222                        Self::$name => tables!(@view $name visitor $($subkey)?),
223                    )*
224                }
225            }
226        }
227
228        impl fmt::Debug for Tables {
229            #[inline]
230            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231                f.write_str(self.name())
232            }
233        }
234
235        impl fmt::Display for Tables {
236            #[inline]
237            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238                self.name().fmt(f)
239            }
240        }
241
242        impl std::str::FromStr for Tables {
243            type Err = String;
244
245            fn from_str(s: &str) -> Result<Self, Self::Err> {
246                match s {
247                    $(
248                        table_names::$name => Ok(Self::$name),
249                    )*
250                    s => Err(format!("unknown table: {s:?}")),
251                }
252            }
253        }
254
255        impl TableInfo for Tables {
256            fn name(&self) -> &'static str {
257                self.name()
258            }
259
260            fn is_dupsort(&self) -> bool {
261                self.is_dupsort()
262            }
263        }
264
265        impl TableSet for Tables {
266            fn tables() -> Box<dyn Iterator<Item = Box<dyn TableInfo>>> {
267                Box::new(Self::ALL.iter().map(|table| Box::new(*table) as Box<dyn TableInfo>))
268            }
269        }
270
271        // Need constants to match on in the `FromStr` implementation.
272        #[expect(non_upper_case_globals)]
273        mod table_names {
274            $(
275                pub(super) const $name: &'static str = stringify!($name);
276            )*
277        }
278
279        /// Maps a run-time [`Tables`] enum value to its corresponding compile-time [`Table`] type.
280        ///
281        /// This is a simpler alternative to [`TableViewer`].
282        ///
283        /// # Examples
284        ///
285        /// ```
286        /// use reth_db_api::{table::Table, Tables, tables_to_generic};
287        ///
288        /// let table = Tables::Headers;
289        /// let result = tables_to_generic!(table, |GenericTable| <GenericTable as Table>::NAME);
290        /// assert_eq!(result, table.name());
291        /// ```
292        #[macro_export]
293        macro_rules! tables_to_generic {
294            ($table:expr, |$generic_name:ident| $e:expr) => {
295                match $table {
296                    $(
297                        Tables::$name => {
298                            use $crate::tables::$name as $generic_name;
299                            $e
300                        },
301                    )*
302                }
303            };
304        }
305    };
306}
307
308tables! {
309    /// Stores the header hashes belonging to the canonical chain.
310    table CanonicalHeaders {
311        type Key = BlockNumber;
312        type Value = HeaderHash;
313    }
314
315    /// Stores the total difficulty from block headers.
316    /// Note: Deprecated.
317    table HeaderTerminalDifficulties {
318        type Key = BlockNumber;
319        type Value = CompactU256;
320    }
321
322    /// Stores the block number corresponding to a header.
323    table HeaderNumbers {
324        type Key = BlockHash;
325        type Value = BlockNumber;
326    }
327
328    /// Stores header bodies.
329    table Headers<H = Header> {
330        type Key = BlockNumber;
331        type Value = H;
332    }
333
334    /// Stores block indices that contains indexes of transaction and the count of them.
335    ///
336    /// More information about stored indices can be found in the [`StoredBlockBodyIndices`] struct.
337    table BlockBodyIndices {
338        type Key = BlockNumber;
339        type Value = StoredBlockBodyIndices;
340    }
341
342    /// Stores the uncles/ommers of the block.
343    table BlockOmmers<H = Header> {
344        type Key = BlockNumber;
345        type Value = StoredBlockOmmers<H>;
346    }
347
348    /// Stores the block withdrawals.
349    table BlockWithdrawals {
350        type Key = BlockNumber;
351        type Value = StoredBlockWithdrawals;
352    }
353
354    /// Canonical only Stores the transaction body for canonical transactions.
355    table Transactions<T = TransactionSigned> {
356        type Key = TxNumber;
357        type Value = T;
358    }
359
360    /// Stores the mapping of the transaction hash to the transaction number.
361    table TransactionHashNumbers {
362        type Key = TxHash;
363        type Value = TxNumber;
364    }
365
366    /// Stores the mapping of transaction number to the blocks number.
367    ///
368    /// The key is the highest transaction ID in the block.
369    table TransactionBlocks {
370        type Key = TxNumber;
371        type Value = BlockNumber;
372    }
373
374    /// Canonical only Stores transaction receipts.
375    table Receipts<R = Receipt> {
376        type Key = TxNumber;
377        type Value = R;
378    }
379
380    /// Stores all smart contract bytecodes.
381    /// There will be multiple accounts that have same bytecode
382    /// So we would need to introduce reference counter.
383    /// This will be small optimization on state.
384    table Bytecodes {
385        type Key = B256;
386        type Value = Bytecode;
387    }
388
389    /// Stores the current state of an [`Account`].
390    table PlainAccountState {
391        type Key = Address;
392        type Value = Account;
393    }
394
395    /// Stores the current value of a storage key.
396    table PlainStorageState {
397        type Key = Address;
398        type Value = StorageEntry;
399        type SubKey = B256;
400    }
401
402    /// Stores pointers to block changeset with changes for each account key.
403    ///
404    /// Last shard key of the storage will contain `u64::MAX` `BlockNumber`,
405    /// this would allows us small optimization on db access when change is in plain state.
406    ///
407    /// Imagine having shards as:
408    /// * `Address | 100`
409    /// * `Address | u64::MAX`
410    ///
411    /// What we need to find is number that is one greater than N. Db `seek` function allows us to fetch
412    /// the shard that equal or more than asked. For example:
413    /// * For N=50 we would get first shard.
414    /// * for N=150 we would get second shard.
415    /// * If max block number is 200 and we ask for N=250 we would fetch last shard and know that needed entry is in `AccountPlainState`.
416    /// * If there were no shard we would get `None` entry or entry of different storage key.
417    ///
418    /// Code example can be found in `reth_provider::HistoricalStateProviderRef`
419    table AccountsHistory {
420        type Key = ShardedKey<Address>;
421        type Value = BlockNumberList;
422    }
423
424    /// Stores pointers to block number changeset with changes for each storage key.
425    ///
426    /// Last shard key of the storage will contain `u64::MAX` `BlockNumber`,
427    /// this would allows us small optimization on db access when change is in plain state.
428    ///
429    /// Imagine having shards as:
430    /// * `Address | StorageKey | 100`
431    /// * `Address | StorageKey | u64::MAX`
432    ///
433    /// What we need to find is number that is one greater than N. Db `seek` function allows us to fetch
434    /// the shard that equal or more than asked. For example:
435    /// * For N=50 we would get first shard.
436    /// * for N=150 we would get second shard.
437    /// * If max block number is 200 and we ask for N=250 we would fetch last shard and know that needed entry is in `StoragePlainState`.
438    /// * If there were no shard we would get `None` entry or entry of different storage key.
439    ///
440    /// Code example can be found in `reth_provider::HistoricalStateProviderRef`
441    table StoragesHistory {
442        type Key = StorageShardedKey;
443        type Value = BlockNumberList;
444    }
445
446    /// Stores the state of an account before a certain transaction changed it.
447    /// Change on state can be: account is created, selfdestructed, touched while empty
448    /// or changed balance,nonce.
449    table AccountChangeSets {
450        type Key = BlockNumber;
451        type Value = AccountBeforeTx;
452        type SubKey = Address;
453    }
454
455    /// Stores the state of a storage key before a certain transaction changed it.
456    /// If [`StorageEntry::value`] is zero, this means storage was not existing
457    /// and needs to be removed.
458    table StorageChangeSets {
459        type Key = BlockNumberAddress;
460        type Value = StorageEntry;
461        type SubKey = B256;
462    }
463
464    /// Stores the current state of an [`Account`] indexed with `keccak256Address`
465    /// This table is in preparation for merklization and calculation of state root.
466    /// We are saving whole account data as it is needed for partial update when
467    /// part of storage is changed. Benefit for merklization is that hashed addresses are sorted.
468    table HashedAccounts {
469        type Key = B256;
470        type Value = Account;
471    }
472
473    /// Stores the current storage values indexed with `keccak256Address` and
474    /// hash of storage key `keccak256key`.
475    /// This table is in preparation for merklization and calculation of state root.
476    /// Benefit for merklization is that hashed addresses/keys are sorted.
477    table HashedStorages {
478        type Key = B256;
479        type Value = StorageEntry;
480        type SubKey = B256;
481    }
482
483    /// Stores the current state's Merkle Patricia Tree.
484    table AccountsTrie {
485        type Key = StoredNibbles;
486        type Value = BranchNodeCompact;
487    }
488
489    /// From `HashedAddress` => `NibblesSubKey` => Intermediate value
490    table StoragesTrie {
491        type Key = B256;
492        type Value = StorageTrieEntry;
493        type SubKey = StoredNibblesSubKey;
494    }
495
496    /// Stores the transaction sender for each canonical transaction.
497    /// It is needed to speed up execution stage and allows fetching signer without doing
498    /// transaction signed recovery
499    table TransactionSenders {
500        type Key = TxNumber;
501        type Value = Address;
502    }
503
504    /// Stores the highest synced block number and stage-specific checkpoint of each stage.
505    table StageCheckpoints {
506        type Key = StageId;
507        type Value = StageCheckpoint;
508    }
509
510    /// Stores arbitrary data to keep track of a stage first-sync progress.
511    table StageCheckpointProgresses {
512        type Key = StageId;
513        type Value = Vec<u8>;
514    }
515
516    /// Stores the highest pruned block number and prune mode of each prune segment.
517    table PruneCheckpoints {
518        type Key = PruneSegment;
519        type Value = PruneCheckpoint;
520    }
521
522    /// Stores the history of client versions that have accessed the database with write privileges by unix timestamp in seconds.
523    table VersionHistory {
524        type Key = u64;
525        type Value = ClientVersion;
526    }
527
528    /// Stores generic chain state info, like the last finalized block.
529    table ChainState {
530        type Key = ChainStateKey;
531        type Value = BlockNumber;
532    }
533
534    /// Stores generic node metadata as key-value pairs.
535    /// Can store feature flags, configuration markers, and other node-specific data.
536    table Metadata {
537        type Key = String;
538        type Value = Vec<u8>;
539    }
540}
541
542/// Packed-encoding view of the [`AccountsTrie`] table.
543///
544/// Uses [`PackedStoredNibbles`] (33-byte) keys instead of [`StoredNibbles`] (65-byte).
545/// Shares the same underlying MDBX table — this is a type-level view for storage v2.
546#[derive(Debug)]
547pub struct PackedAccountsTrie;
548
549impl Table for PackedAccountsTrie {
550    const NAME: &'static str = <AccountsTrie as Table>::NAME;
551    const DUPSORT: bool = false;
552    type Key = PackedStoredNibbles;
553    type Value = BranchNodeCompact;
554}
555
556/// Packed-encoding view of the [`StoragesTrie`] table.
557///
558/// Uses [`PackedStoredNibblesSubKey`] (33-byte) subkeys instead of [`StoredNibblesSubKey`]
559/// (65-byte). Shares the same underlying MDBX table — this is a type-level view for storage v2.
560#[derive(Debug)]
561pub struct PackedStoragesTrie;
562
563impl Table for PackedStoragesTrie {
564    const NAME: &'static str = <StoragesTrie as Table>::NAME;
565    const DUPSORT: bool = true;
566    type Key = B256;
567    type Value = PackedStorageTrieEntry;
568}
569
570impl DupSort for PackedStoragesTrie {
571    type SubKey = PackedStoredNibblesSubKey;
572}
573
574/// Keys for the `ChainState` table.
575#[derive(Ord, Clone, Eq, PartialOrd, PartialEq, Debug, Deserialize, Serialize, Hash)]
576pub enum ChainStateKey {
577    /// Last finalized block key
578    LastFinalizedBlock,
579    /// Last safe block key
580    LastSafeBlock,
581}
582
583impl Encode for ChainStateKey {
584    type Encoded = [u8; 1];
585
586    fn encode(self) -> Self::Encoded {
587        match self {
588            Self::LastFinalizedBlock => [0],
589            Self::LastSafeBlock => [1],
590        }
591    }
592}
593
594impl Decode for ChainStateKey {
595    fn decode(value: &[u8]) -> Result<Self, crate::DatabaseError> {
596        match value {
597            [0] => Ok(Self::LastFinalizedBlock),
598            [1] => Ok(Self::LastSafeBlock),
599            _ => Err(crate::DatabaseError::Decode),
600        }
601    }
602}
603
604// Alias types.
605
606/// List with transaction numbers.
607pub type BlockNumberList = IntegerList;
608
609/// Encoded stage id.
610pub type StageId = String;
611
612#[cfg(test)]
613mod tests {
614    use super::*;
615    use std::str::FromStr;
616
617    #[test]
618    fn parse_table_from_str() {
619        for table in Tables::ALL {
620            assert_eq!(format!("{table:?}"), table.name());
621            assert_eq!(table.to_string(), table.name());
622            assert_eq!(Tables::from_str(table.name()).unwrap(), *table);
623        }
624    }
625}