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