reth_db/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`](reth_db_api::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
19#[cfg(feature = "mdbx")]
20pub(crate) mod utils;
21
22use alloy_consensus::Header;
23use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash, TxNumber, B256};
24use reth_db_api::{
25 models::{
26 accounts::BlockNumberAddress,
27 blocks::{HeaderHash, StoredBlockOmmers},
28 storage_sharded_key::StorageShardedKey,
29 AccountBeforeTx, ClientVersion, CompactU256, IntegerList, ShardedKey,
30 StoredBlockBodyIndices, StoredBlockWithdrawals,
31 },
32 table::{Decode, DupSort, Encode, Table, TableInfo},
33};
34use reth_primitives::{Receipt, StorageEntry, TransactionSigned};
35use reth_primitives_traits::{Account, Bytecode};
36use reth_prune_types::{PruneCheckpoint, PruneSegment};
37use reth_stages_types::StageCheckpoint;
38use reth_trie_common::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey};
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::{TableViewer, Tables};
58/// use reth_db_api::table::{DupSort, Table};
59///
60/// struct MyTableViewer;
61///
62/// impl TableViewer<()> for MyTableViewer {
63/// type Error = &'static str;
64///
65/// fn view<T: Table>(&self) -> Result<(), Self::Error> {
66/// // operate on table in a generic way
67/// Ok(())
68/// }
69///
70/// fn view_dupsort<T: DupSort>(&self) -> Result<(), Self::Error> {
71/// // operate on a dupsort table in a generic way
72/// Ok(())
73/// }
74/// }
75///
76/// let viewer = MyTableViewer {};
77///
78/// let _ = Tables::Headers.view(&viewer);
79/// let _ = Tables::Transactions.view(&viewer);
80/// ```
81pub trait TableViewer<R> {
82 /// The error type returned by the viewer.
83 type Error;
84
85 /// Calls `view` with the correct table type.
86 fn view_rt(&self, table: Tables) -> Result<R, Self::Error> {
87 table.view(self)
88 }
89
90 /// Operate on the table in a generic way.
91 fn view<T: Table>(&self) -> Result<R, Self::Error>;
92
93 /// Operate on the dupsort table in a generic way.
94 ///
95 /// By default, the `view` function is invoked unless overridden.
96 fn view_dupsort<T: DupSort>(&self) -> Result<R, Self::Error> {
97 self.view::<T>()
98 }
99}
100
101/// General trait for defining the set of tables
102/// Used to initialize database
103pub trait TableSet {
104 /// Returns an iterator over the tables
105 fn tables() -> Box<dyn Iterator<Item = Box<dyn TableInfo>>>;
106}
107
108/// Defines all the tables in the database.
109#[macro_export]
110macro_rules! tables {
111 (@bool) => { false };
112 (@bool $($t:tt)+) => { true };
113
114 (@view $name:ident $v:ident) => { $v.view::<$name>() };
115 (@view $name:ident $v:ident $_subkey:ty) => { $v.view_dupsort::<$name>() };
116
117 (@value_doc $key:ty, $value:ty) => {
118 concat!("[`", stringify!($value), "`]")
119 };
120 // Don't generate links if we have generics
121 (@value_doc $key:ty, $value:ty, $($generic:ident),*) => {
122 concat!("`", stringify!($value), "`")
123 };
124
125 ($($(#[$attr:meta])* table $name:ident$(<$($generic:ident $(= $default:ty)?),*>)? { type Key = $key:ty; type Value = $value:ty; $(type SubKey = $subkey:ty;)? } )*) => {
126 // Table marker types.
127 $(
128 $(#[$attr])*
129 ///
130 #[doc = concat!("Marker type representing a database table mapping [`", stringify!($key), "`] to ", tables!(@value_doc $key, $value, $($($generic),*)?), ".")]
131 $(
132 #[doc = concat!("\n\nThis table's `DUPSORT` subkey is [`", stringify!($subkey), "`].")]
133 )?
134 pub struct $name$(<$($generic $( = $default)?),*>)? {
135 _private: std::marker::PhantomData<($($($generic,)*)?)>,
136 }
137
138 // Ideally this implementation wouldn't exist, but it is necessary to derive `Debug`
139 // when a type is generic over `T: Table`. See: https://github.com/rust-lang/rust/issues/26925
140 impl$(<$($generic),*>)? fmt::Debug for $name$(<$($generic),*>)? {
141 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
142 unreachable!("this type cannot be instantiated")
143 }
144 }
145
146 impl$(<$($generic),*>)? reth_db_api::table::Table for $name$(<$($generic),*>)?
147 where
148 $value: reth_db_api::table::Value + 'static
149 $($(,$generic: Send + Sync)*)?
150 {
151 const NAME: &'static str = table_names::$name;
152 const DUPSORT: bool = tables!(@bool $($subkey)?);
153
154 type Key = $key;
155 type Value = $value;
156 }
157
158 $(
159 impl DupSort for $name {
160 type SubKey = $subkey;
161 }
162 )?
163 )*
164
165 // Tables enum.
166 // NOTE: the ordering of the enum does not matter, but it is assumed that the discriminants
167 // start at 0 and increment by 1 for each variant (the default behavior).
168 // See for example `reth_db::implementation::mdbx::tx::Tx::db_handles`.
169
170 /// A table in the database.
171 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
172 pub enum Tables {
173 $(
174 #[doc = concat!("The [`", stringify!($name), "`] database table.")]
175 $name,
176 )*
177 }
178
179 impl Tables {
180 /// All the tables in the database.
181 pub const ALL: &'static [Self] = &[$(Self::$name,)*];
182
183 /// The number of tables in the database.
184 pub const COUNT: usize = Self::ALL.len();
185
186 /// Returns the name of the table as a string.
187 pub const fn name(&self) -> &'static str {
188 match self {
189 $(
190 Self::$name => table_names::$name,
191 )*
192 }
193 }
194
195 /// Returns `true` if the table is a `DUPSORT` table.
196 pub const fn is_dupsort(&self) -> bool {
197 match self {
198 $(
199 Self::$name => tables!(@bool $($subkey)?),
200 )*
201 }
202 }
203
204 /// The type of the given table in database.
205 pub const fn table_type(&self) -> TableType {
206 if self.is_dupsort() {
207 TableType::DupSort
208 } else {
209 TableType::Table
210 }
211 }
212
213 /// Allows to operate on specific table type
214 pub fn view<T, R>(&self, visitor: &T) -> Result<R, T::Error>
215 where
216 T: ?Sized + TableViewer<R>,
217 {
218 match self {
219 $(
220 Self::$name => tables!(@view $name visitor $($subkey)?),
221 )*
222 }
223 }
224 }
225
226 impl fmt::Debug for Tables {
227 #[inline]
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 f.write_str(self.name())
230 }
231 }
232
233 impl fmt::Display for Tables {
234 #[inline]
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 self.name().fmt(f)
237 }
238 }
239
240 impl std::str::FromStr for Tables {
241 type Err = String;
242
243 fn from_str(s: &str) -> Result<Self, Self::Err> {
244 match s {
245 $(
246 table_names::$name => Ok(Self::$name),
247 )*
248 s => Err(format!("unknown table: {s:?}")),
249 }
250 }
251 }
252
253 impl TableInfo for Tables {
254 fn name(&self) -> &'static str {
255 self.name()
256 }
257
258 fn is_dupsort(&self) -> bool {
259 self.is_dupsort()
260 }
261 }
262
263 impl TableSet for Tables {
264 fn tables() -> Box<dyn Iterator<Item = Box<dyn TableInfo>>> {
265 Box::new(Self::ALL.iter().map(|table| Box::new(*table) as Box<dyn TableInfo>))
266 }
267 }
268
269 // Need constants to match on in the `FromStr` implementation.
270 #[allow(non_upper_case_globals)]
271 mod table_names {
272 $(
273 pub(super) const $name: &'static str = stringify!($name);
274 )*
275 }
276
277 /// Maps a run-time [`Tables`] enum value to its corresponding compile-time [`Table`] type.
278 ///
279 /// This is a simpler alternative to [`TableViewer`].
280 ///
281 /// # Examples
282 ///
283 /// ```
284 /// use reth_db::{Tables, tables_to_generic};
285 /// use reth_db_api::table::Table;
286 ///
287 /// let table = Tables::Headers;
288 /// let result = tables_to_generic!(table, |GenericTable| <GenericTable as Table>::NAME);
289 /// assert_eq!(result, table.name());
290 /// ```
291 #[macro_export]
292 macro_rules! tables_to_generic {
293 ($table:expr, |$generic_name:ident| $e:expr) => {
294 match $table {
295 $(
296 Tables::$name => {
297 use $crate::tables::$name as $generic_name;
298 $e
299 },
300 )*
301 }
302 };
303 }
304 };
305}
306
307tables! {
308 /// Stores the header hashes belonging to the canonical chain.
309 table CanonicalHeaders {
310 type Key = BlockNumber;
311 type Value = HeaderHash;
312 }
313
314 /// Stores the total difficulty from a block header.
315 table HeaderTerminalDifficulties {
316 type Key = BlockNumber;
317 type Value = CompactU256;
318 }
319
320 /// Stores the block number corresponding to a header.
321 table HeaderNumbers {
322 type Key = BlockHash;
323 type Value = BlockNumber;
324 }
325
326 /// Stores header bodies.
327 table Headers<H = Header> {
328 type Key = BlockNumber;
329 type Value = H;
330 }
331
332 /// Stores block indices that contains indexes of transaction and the count of them.
333 ///
334 /// More information about stored indices can be found in the [`StoredBlockBodyIndices`] struct.
335 table BlockBodyIndices {
336 type Key = BlockNumber;
337 type Value = StoredBlockBodyIndices;
338 }
339
340 /// Stores the uncles/ommers of the block.
341 table BlockOmmers<H = Header> {
342 type Key = BlockNumber;
343 type Value = StoredBlockOmmers<H>;
344 }
345
346 /// Stores the block withdrawals.
347 table BlockWithdrawals {
348 type Key = BlockNumber;
349 type Value = StoredBlockWithdrawals;
350 }
351
352 /// Canonical only Stores the transaction body for canonical transactions.
353 table Transactions<T = TransactionSigned> {
354 type Key = TxNumber;
355 type Value = T;
356 }
357
358 /// Stores the mapping of the transaction hash to the transaction number.
359 table TransactionHashNumbers {
360 type Key = TxHash;
361 type Value = TxNumber;
362 }
363
364 /// Stores the mapping of transaction number to the blocks number.
365 ///
366 /// The key is the highest transaction ID in the block.
367 table TransactionBlocks {
368 type Key = TxNumber;
369 type Value = BlockNumber;
370 }
371
372 /// Canonical only Stores transaction receipts.
373 table Receipts<R = Receipt> {
374 type Key = TxNumber;
375 type Value = R;
376 }
377
378 /// Stores all smart contract bytecodes.
379 /// There will be multiple accounts that have same bytecode
380 /// So we would need to introduce reference counter.
381 /// This will be small optimization on state.
382 table Bytecodes {
383 type Key = B256;
384 type Value = Bytecode;
385 }
386
387 /// Stores the current state of an [`Account`].
388 table PlainAccountState {
389 type Key = Address;
390 type Value = Account;
391 }
392
393 /// Stores the current value of a storage key.
394 table PlainStorageState {
395 type Key = Address;
396 type Value = StorageEntry;
397 type SubKey = B256;
398 }
399
400 /// Stores pointers to block changeset with changes for each account key.
401 ///
402 /// Last shard key of the storage will contain `u64::MAX` `BlockNumber`,
403 /// this would allows us small optimization on db access when change is in plain state.
404 ///
405 /// Imagine having shards as:
406 /// * `Address | 100`
407 /// * `Address | u64::MAX`
408 ///
409 /// What we need to find is number that is one greater than N. Db `seek` function allows us to fetch
410 /// the shard that equal or more than asked. For example:
411 /// * For N=50 we would get first shard.
412 /// * for N=150 we would get second shard.
413 /// * If max block number is 200 and we ask for N=250 we would fetch last shard and
414 /// know that needed entry is in `AccountPlainState`.
415 /// * If there were no shard we would get `None` entry or entry of different storage key.
416 ///
417 /// Code example can be found in `reth_provider::HistoricalStateProviderRef`
418 table AccountsHistory {
419 type Key = ShardedKey<Address>;
420 type Value = BlockNumberList;
421 }
422
423 /// Stores pointers to block number changeset with changes for each storage key.
424 ///
425 /// Last shard key of the storage will contain `u64::MAX` `BlockNumber`,
426 /// this would allows us small optimization on db access when change is in plain state.
427 ///
428 /// Imagine having shards as:
429 /// * `Address | StorageKey | 100`
430 /// * `Address | StorageKey | u64::MAX`
431 ///
432 /// What we need to find is number that is one greater than N. Db `seek` function allows us to fetch
433 /// the shard that equal or more than asked. For example:
434 /// * For N=50 we would get first shard.
435 /// * for N=150 we would get second shard.
436 /// * If max block number is 200 and we ask for N=250 we would fetch last shard and
437 /// 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
535/// Keys for the `ChainState` table.
536#[derive(Ord, Clone, Eq, PartialOrd, PartialEq, Debug, Deserialize, Serialize, Hash)]
537pub enum ChainStateKey {
538 /// Last finalized block key
539 LastFinalizedBlock,
540 /// Last finalized block key
541 LastSafeBlockBlock,
542}
543
544impl Encode for ChainStateKey {
545 type Encoded = [u8; 1];
546
547 fn encode(self) -> Self::Encoded {
548 match self {
549 Self::LastFinalizedBlock => [0],
550 Self::LastSafeBlockBlock => [1],
551 }
552 }
553}
554
555impl Decode for ChainStateKey {
556 fn decode(value: &[u8]) -> Result<Self, reth_db_api::DatabaseError> {
557 match value {
558 [0] => Ok(Self::LastFinalizedBlock),
559 [1] => Ok(Self::LastSafeBlockBlock),
560 _ => Err(reth_db_api::DatabaseError::Decode),
561 }
562 }
563}
564
565// Alias types.
566
567/// List with transaction numbers.
568pub type BlockNumberList = IntegerList;
569
570/// Encoded stage id.
571pub type StageId = String;
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576 use std::str::FromStr;
577
578 #[test]
579 fn parse_table_from_str() {
580 for table in Tables::ALL {
581 assert_eq!(format!("{table:?}"), table.name());
582 assert_eq!(table.to_string(), table.name());
583 assert_eq!(Tables::from_str(table.name()).unwrap(), *table);
584 }
585 }
586}