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}