Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/exex/external-proofs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ alloy-primitives = { workspace = true }
# reth
reth-chainspec.workspace = true
reth-db-api.workspace = true
reth-db.workspace = true
reth-node-types.workspace = true
reth-node-api.workspace = true
reth-primitives-traits.workspace = true
Expand All @@ -29,6 +30,7 @@ reth-trie.workspace = true
# misc
auto_impl.workspace = true
async-trait.workspace = true
bytes.workspace = true
eyre.workspace = true
futures-util.workspace = true
serde.workspace = true
Expand Down
5 changes: 2 additions & 3 deletions crates/exex/external-proofs/src/backfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,8 @@ impl<'a, Tx: DbTx, S: OpProofsStorage + Send> BackfillJob<'a, Tx, S> {
#[cfg(test)]
mod tests {
use super::*;
use crate::{
in_memory::InMemoryProofsStorage,
storage::{OpProofsHashedCursor, OpProofsTrieCursor},
use crate::storage::{
in_memory::InMemoryProofsStorage, OpProofsHashedCursor, OpProofsTrieCursor,
};
use alloy_primitives::{keccak256, Address, U256};
use reth_db::{test_utils::create_test_rw_db, Database};
Expand Down
6 changes: 1 addition & 5 deletions crates/exex/external-proofs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! OP Proofs ExEx - processes blocks and tracks state changes

use crate::{backfill::BackfillJob, in_memory::InMemoryProofsStorage};
use crate::{backfill::BackfillJob, storage::in_memory::InMemoryProofsStorage};
use futures_util::TryStreamExt;
use reth_chainspec::ChainInfo;
use reth_exex::{ExExContext, ExExEvent};
Expand All @@ -10,12 +10,8 @@ use reth_provider::{BlockNumReader, DBProvider, DatabaseProviderFactory};
use std::sync::Arc;

pub mod backfill;
pub mod in_memory;
pub mod storage;

#[cfg(test)]
mod storage_tests;

/// Saves and serves trie nodes to make proofs faster. This handles the process of
/// saving the current state, new blocks as they're added, and serving proof RPCs
/// based on the saved data.
Expand Down
12 changes: 12 additions & 0 deletions crates/exex/external-proofs/src/storage/in_memory/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! In-memory implementation of `OpProofsStorage` for testing purposes.
//!
//! This module provides a complete in-memory implementation of the
//! [`OpProofsStorage`](crate::storage::OpProofsStorage) trait that can be used for
//! testing and development. The implementation uses tokio async `RwLock`
//! for thread-safe concurrent access and stores all data in memory using `BTreeMap` collections.

mod store;
pub use store::InMemoryProofsStorage;

#[cfg(test)]
mod store_tests;
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
//! In-memory implementation of `OpProofsStorage` for testing purposes.
//!
//! This module provides a complete in-memory implementation of the [`OpProofsStorage`] trait
//! that can be used for testing and development. The implementation uses tokio async `RwLock`
//! for thread-safe concurrent access and stores all data in memory using `BTreeMap` collections.

use alloy_primitives::{map::HashMap, B256, U256};
use async_trait::async_trait;
use reth_primitives_traits::Account;
use reth_trie::{updates::TrieUpdates, BranchNodeCompact, HashedPostState, Nibbles};
use std::{collections::BTreeMap, sync::Arc};
use tokio::sync::RwLock;

use super::storage::{
use crate::storage::{
BlockStateDiff, OpProofsHashedCursor, OpProofsStorage, OpProofsStorageError,
OpProofsStorageResult, OpProofsTrieCursor,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

#[cfg(test)]
mod tests {
use crate::{
in_memory::InMemoryProofsStorage,
storage::{
BlockStateDiff, OpProofsHashedCursor, OpProofsStorage, OpProofsStorageError,
OpProofsTrieCursor,
},
use crate::storage::{
in_memory::InMemoryProofsStorage, BlockStateDiff, OpProofsHashedCursor, OpProofsStorage,
OpProofsStorageError, OpProofsTrieCursor,
};
use alloy_primitives::{map::HashMap, B256, U256};
use reth_primitives_traits::Account;
Expand Down
64 changes: 64 additions & 0 deletions crates/exex/external-proofs/src/storage/mdbx/cursor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::storage::{OpProofsHashedCursor, OpProofsStorageResult, OpProofsTrieCursor};
use alloy_primitives::{B256, U256};
use reth_primitives_traits::Account;
use reth_trie::{BranchNodeCompact, Nibbles};

/// MDBX implementation of `OpProofsTrieCursor`.
#[derive(Debug)]
pub struct MdbxTrieCursor {}

impl OpProofsTrieCursor for MdbxTrieCursor {
fn seek_exact(
&mut self,
_path: Nibbles,
) -> OpProofsStorageResult<Option<(Nibbles, BranchNodeCompact)>> {
unimplemented!()
}

fn seek(
&mut self,
_path: Nibbles,
) -> OpProofsStorageResult<Option<(Nibbles, BranchNodeCompact)>> {
unimplemented!()
}

fn next(&mut self) -> OpProofsStorageResult<Option<(Nibbles, BranchNodeCompact)>> {
unimplemented!()
}

fn current(&mut self) -> OpProofsStorageResult<Option<Nibbles>> {
unimplemented!()
}
}

/// MDBX implementation of `OpProofsHashedCursor` for storage state.
#[derive(Debug)]
pub struct MdbxStorageCursor {}

impl OpProofsHashedCursor for MdbxStorageCursor {
type Value = U256;

fn seek(&mut self, _key: B256) -> OpProofsStorageResult<Option<(B256, Self::Value)>> {
unimplemented!()
}

fn next(&mut self) -> OpProofsStorageResult<Option<(B256, Self::Value)>> {
unimplemented!()
}
}

/// MDBX implementation of `OpProofsHashedCursor` for account state.
#[derive(Debug)]
pub struct MdbxAccountCursor {}

impl OpProofsHashedCursor for MdbxAccountCursor {
type Value = Account;

fn seek(&mut self, _key: B256) -> OpProofsStorageResult<Option<(B256, Self::Value)>> {
unimplemented!()
}

fn next(&mut self) -> OpProofsStorageResult<Option<(B256, Self::Value)>> {
unimplemented!()
}
}
15 changes: 15 additions & 0 deletions crates/exex/external-proofs/src/storage/mdbx/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! MDBX implementation of `OpProofsStorage`.
//!
//! This module provides a complete MDBX implementation of the
//! [`OpProofsStorage`](crate::storage::OpProofsStorage) trait. It uses the `reth_db` crate for
//! database interactions and defines the necessary tables and models for storing trie branches,
//! accounts, and storage leaves.

mod models;
pub use models::*;

mod store;
pub use store::MdbxProofsStorage;

mod cursor;
pub use cursor::{MdbxAccountCursor, MdbxStorageCursor, MdbxTrieCursor};
79 changes: 79 additions & 0 deletions crates/exex/external-proofs/src/storage/mdbx/models/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use bytes::BufMut;
use reth_db_api::table::{Compress, Decompress};
use serde::{Deserialize, Serialize};

/// Newtype wrapper for (u64, B256) to implement Compress/Decompress
///
/// Used for storing block metadata (number + hash).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockNumberHash(pub u64, pub alloy_primitives::B256);

impl From<(u64, alloy_primitives::B256)> for BlockNumberHash {
fn from((number, hash): (u64, alloy_primitives::B256)) -> Self {
Self(number, hash)
}
}

impl From<BlockNumberHash> for (u64, alloy_primitives::B256) {
fn from(bnh: BlockNumberHash) -> Self {
(bnh.0, bnh.1)
}
}

impl BlockNumberHash {
/// Create a new block number and hash pair
pub const fn new(block_number: u64, hash: alloy_primitives::B256) -> Self {
Self(block_number, hash)
}

/// Destructure into components
pub const fn into_components(self) -> (u64, alloy_primitives::B256) {
(self.0, self.1)
}
}

impl Compress for BlockNumberHash {
type Compressed = Vec<u8>;

fn compress_to_buf<B: BufMut + AsMut<[u8]>>(&self, buf: &mut B) {
// Encode block number (8 bytes, big-endian) + hash (32 bytes) = 40 bytes total
buf.put_u64(self.0);
buf.put_slice(self.1.as_slice());
}
}

impl Decompress for BlockNumberHash {
fn decompress(value: &[u8]) -> Result<Self, reth_db_api::DatabaseError> {
if value.len() != 40 {
return Err(reth_db_api::DatabaseError::Decode);
}

let block_number = u64::from_be_bytes(
value[..8].try_into().map_err(|_| reth_db_api::DatabaseError::Decode)?,
);
let hash = alloy_primitives::B256::from_slice(&value[8..40]);

Ok(Self(block_number, hash))
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::B256;

#[test]
fn test_block_number_hash_roundtrip() {
let test_cases = vec![
BlockNumberHash(0, B256::ZERO),
BlockNumberHash(42, B256::repeat_byte(0xaa)),
BlockNumberHash(u64::MAX, B256::repeat_byte(0xff)),
];

for original in test_cases {
let compressed = original.compress();
let decompressed = BlockNumberHash::decompress(&compressed).unwrap();
assert_eq!(original, decompressed);
}
}
}
68 changes: 68 additions & 0 deletions crates/exex/external-proofs/src/storage/mdbx/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! MDBX tables and models for external proofs storage.
use alloy_primitives::B256;
use reth_db_api::{
table::{DupSort, TableInfo},
tables, TableSet, TableType, TableViewer,
};
use reth_primitives_traits::Account;
use reth_trie::{BranchNodeCompact, StoredNibbles};
use std::fmt;

mod block;
pub use block::*;
mod version;
pub use version::*;

mod storage;
pub use storage::*;

tables! {
/// Stores historical branch nodes for the account state trie.
///
/// Each entry maps a compact-encoded trie path (`StoredNibbles`) to its versioned branch node.
/// Multiple versions of the same node are stored using the block number as a subkey.
table AccountTrieHistory {
type Key = StoredNibbles;
type Value = VersionedValue<BranchNodeCompact>;
type SubKey = u64; // block number
}

/// Stores historical branch nodes for the storage trie of each account.
///
/// Each entry is identified by a composite key combining the account’s hashed address and the
/// compact-encoded trie path. Versions are tracked using block numbers as subkeys.
table StorageTrieHistory {
type Key = StorageTrieSubKey;
type Value = VersionedValue<BranchNodeCompact>;
type SubKey = u64; // block number
}

/// Stores versioned account state across block history.
///
/// Each entry maps a hashed account address to its serialized account data (balance, nonce,
/// code hash, storage root).
table HashedAccountHistory {
type Key = B256;
type Value = VersionedValue<MaybeDeleted<Account>>;
type SubKey = u64; // block number
}

/// Stores versioned storage state across block history.
///
/// Each entry maps a composite key of (hashed address, storage key) to its stored value.
/// Used for reconstructing contract storage at any historical block height.
table HashedStorageHistory {
type Key = HashedStorageSubKey;
type Value = VersionedValue<B256>;
type SubKey = u64; // block number
}

/// Tracks the active proof window in the external historical storage.
///
/// Stores the earliest and latest block numbers (and corresponding hashes)
/// for which historical trie data is retained.
table ProofWindow {
type Key = ProofWindowKey;
type Value = BlockNumberHash;
}
}
Loading
Loading