Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 7 additions & 1 deletion crates/optimism/trie/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::OpProofsStorageResult;
use alloy_eips::eip1898::BlockWithParent;
use alloy_primitives::{map::HashMap, B256, U256};
use alloy_primitives::{map::HashMap, Address, B256, U256};
use auto_impl::auto_impl;
use derive_more::{AddAssign, Constructor};
use reth_primitives_traits::Account;
Expand Down Expand Up @@ -110,6 +110,12 @@ pub trait OpProofsStore: Send + Sync + Debug {
storages: Vec<(B256, U256)>,
) -> impl Future<Output = OpProofsStorageResult<()>> + Send;

/// Store a batch of address mappings from hashed to original addresses.
fn store_address_mappings(
&self,
mappings: Vec<(B256, Address)>,
) -> impl Future<Output = OpProofsStorageResult<()>> + Send;

/// Get the earliest block number and hash that has been stored
///
/// This is used to determine the block number of trie nodes with block number 0.
Expand Down
55 changes: 54 additions & 1 deletion crates/optimism/trie/src/backfill.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Backfill job for proofs storage. Handles storing the existing state into the proofs storage.

use crate::OpProofsStore;
use alloy_primitives::B256;
use alloy_primitives::{keccak256, Address, B256};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
tables,
Expand Down Expand Up @@ -82,6 +82,7 @@ macro_rules! define_dup_cursor_iter {

// Generate iterators for all 4 table types
define_simple_cursor_iter!(HashedAccountsIter, tables::HashedAccounts, B256, Account);
define_simple_cursor_iter!(AddressLookupIter, tables::PlainAccountState, Address, Account);
define_dup_cursor_iter!(HashedStoragesIter, tables::HashedStorages, B256, StorageEntry);
define_simple_cursor_iter!(
AccountsTrieIter,
Expand Down Expand Up @@ -263,6 +264,29 @@ impl<'a, Tx: DbTx, S: OpProofsStore + Send> BackfillJob<'a, Tx, S> {
Ok(())
}

/// Backfill address mappings data
async fn backfill_address_mappings(&self) -> eyre::Result<()> {
let start_cursor = self.tx.cursor_read::<tables::PlainAccountState>()?;

let source = AddressLookupIter::new(start_cursor)
.map(|res| res.map(|(addr, _)| (keccak256(addr), addr)));
let save_fn = async |entries: Vec<(B256, Address)>| -> eyre::Result<()> {
self.storage.store_address_mappings(entries).await?;
Ok(())
};

backfill(
"address mappings",
source,
BACKFILL_STORAGE_THRESHOLD,
BACKFILL_LOG_THRESHOLD,
save_fn,
)
.await?;

Ok(())
}

/// Backfill accounts trie data
async fn backfill_accounts_trie(&self) -> eyre::Result<()> {
let start_cursor = self.tx.cursor_read::<tables::AccountsTrie>()?;
Expand Down Expand Up @@ -328,6 +352,7 @@ impl<'a, Tx: DbTx, S: OpProofsStore + Send> BackfillJob<'a, Tx, S> {
async fn backfill_trie(&self) -> eyre::Result<()> {
self.backfill_hashed_accounts().await?;
self.backfill_hashed_storages().await?;
self.backfill_address_mappings().await?;
self.backfill_storages_trie().await?;
self.backfill_accounts_trie().await?;

Expand Down Expand Up @@ -520,6 +545,34 @@ mod tests {
assert_eq!(count, 3);
}

#[tokio::test]
async fn test_backfill_address_mappings() {
let db = create_test_rw_db();
let storage = InMemoryProofsStorage::new();

// Insert test address mappings into database
let tx = db.tx_mut().unwrap();
let mut cursor = tx.cursor_write::<tables::PlainAccountState>().unwrap();

let accounts = vec![
(Address::repeat_byte(0x01), Account { nonce: 1, ..Default::default() }),
(Address::repeat_byte(0x02), Account { nonce: 2, ..Default::default() }),
];

for (addr, account) in &accounts {
cursor.append(*addr, account).unwrap();
}
drop(cursor);
tx.commit().unwrap();

// Run backfill
let tx = db.tx().unwrap();
let job = BackfillJob::new(storage.clone(), &tx);
job.backfill_address_mappings().await.unwrap();

// Todo: Verify data was stored
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains a capitalization error. "Todo" should be "TODO" to follow the standard convention for code comments indicating incomplete work.

Suggested change
// Todo: Verify data was stored
// TODO: Verify data was stored

Copilot uses AI. Check for mistakes.
}
Comment on lines +573 to +574
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is incomplete with a Todo comment indicating that data verification is missing. Unlike other backfill tests in this file (e.g., test_backfill_hashed_accounts, test_backfill_accounts_trie), this test doesn't verify that the address mappings were actually stored correctly. Consider adding verification code that reads back the stored mappings and asserts they match the expected values.

Copilot uses AI. Check for mistakes.

#[tokio::test]
async fn test_backfill_storages_trie() {
let db = create_test_rw_db();
Expand Down
8 changes: 7 additions & 1 deletion crates/optimism/trie/src/db/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub(crate) mod kv;
pub use change_set::*;
pub use kv::*;

use alloy_primitives::B256;
use alloy_primitives::{Address, B256};
use reth_db::{
table::{DupSort, TableInfo},
tables, TableSet, TableType, TableViewer,
Expand Down Expand Up @@ -81,4 +81,10 @@ tables! {
type Key = u64; // Block number
type Value = ChangeSet;
}

/// A mapping table from hashed addresses to their original addresses.
table AddressLookup {
type Key = B256;
type Value = Address;
}
}
65 changes: 60 additions & 5 deletions crates/optimism/trie/src/db/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ use crate::{
db::{
cursor::Dup,
models::{
kv::IntoKV, AccountTrieHistory, BlockChangeSet, ChangeSet, HashedAccountHistory,
HashedStorageHistory, HashedStorageKey, MaybeDeleted, StorageTrieHistory,
StorageTrieKey, StorageValue, VersionedValue,
kv::IntoKV, AccountTrieHistory, AddressLookup, BlockChangeSet, ChangeSet,
HashedAccountHistory, HashedStorageHistory, HashedStorageKey, MaybeDeleted,
StorageTrieHistory, StorageTrieKey, StorageValue, VersionedValue,
},
MdbxAccountCursor, MdbxStorageCursor, MdbxTrieCursor,
},
BlockStateDiff, OpProofsStorageError, OpProofsStorageResult, OpProofsStore,
};
use alloy_eips::{eip1898::BlockWithParent, NumHash};
use alloy_primitives::{map::HashMap, B256, U256};
use alloy_primitives::{map::HashMap, Address, B256, U256};
#[cfg(feature = "metrics")]
use metrics::{gauge, Label};
use reth_db::{
Expand Down Expand Up @@ -514,6 +514,27 @@ impl OpProofsStore for MdbxProofsStorage {
})?
}

async fn store_address_mappings(
&self,
mappings: Vec<(B256, Address)>,
) -> OpProofsStorageResult<()> {
let mut mappings = mappings;
if mappings.is_empty() {
return Ok(());
}

// sort the mappings by key to ensure insertion is efficient
mappings.sort_by_key(|(key, _)| *key);

self.env.update(|tx| {
let mut cur = tx.cursor_write::<AddressLookup>()?;
for (k, v) in mappings {
cur.append(k, &v)?;
}
Ok(())
})?
}

async fn get_earliest_block_number(&self) -> OpProofsStorageResult<Option<(u64, B256)>> {
self.env.view(|tx| self.inner_get_block_number_hash(tx, ProofWindowKey::EarliestBlock))?
}
Expand Down Expand Up @@ -901,7 +922,7 @@ mod tests {
StorageTrieKey,
};
use alloy_eips::NumHash;
use alloy_primitives::B256;
use alloy_primitives::{keccak256, B256};
use reth_db::{
cursor::DbDupCursorRO,
transaction::{DbTx, DbTxMut},
Expand Down Expand Up @@ -1322,6 +1343,40 @@ mod tests {
}
}

#[tokio::test]
async fn test_store_address_mappings() {
let dir = TempDir::new().unwrap();
let store = MdbxProofsStorage::new(dir.path()).expect("env");

let a1 = Address::random();
let h1 = keccak256(a1);
let a2 = Address::random();
let h2 = keccak256(a2);
let a3 = Address::random();
let h3 = keccak256(a3);

// Input is unsorted to verify the method sorts them before appending
// (MDBX append requires sorted keys)
let mappings = vec![(h3, a3), (h1, a1), (h2, a2)];

store.store_address_mappings(mappings).await.expect("store");

let tx = store.env.tx().expect("ro tx");
let mut cur = tx.cursor_read::<AddressLookup>().expect("cursor");

// Verify h1
let v1 = cur.seek_exact(h1).expect("seek").expect("exists");
assert_eq!(v1, (h1, a1));

// Verify h2
let v2 = cur.seek_exact(h2).expect("seek").expect("exists");
assert_eq!(v2, (h2, a2));

// Verify h3
let v3 = cur.seek_exact(h3).expect("seek").expect("exists");
assert_eq!(v3, (h3, a3));
}

#[tokio::test]
async fn test_store_trie_updates_comprehensive() {
let dir = TempDir::new().unwrap();
Expand Down
18 changes: 17 additions & 1 deletion crates/optimism/trie/src/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
api::WriteCounts, BlockStateDiff, OpProofsStorageError, OpProofsStorageResult, OpProofsStore,
};
use alloy_eips::eip1898::BlockWithParent;
use alloy_primitives::{map::HashMap, B256, U256};
use alloy_primitives::{map::HashMap, Address, B256, U256};
use reth_db::DatabaseError;
use reth_primitives_traits::Account;
use reth_trie::{
Expand Down Expand Up @@ -37,6 +37,9 @@ struct InMemoryStorageInner {
/// Hashed storages: (`block_number`, `hashed_address`, `hashed_slot`) -> value
hashed_storages: BTreeMap<(u64, B256, B256), U256>,

/// Address mappings: (`block_number`, `hashed_address`) -> original address
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment incorrectly states the key structure includes block_number. According to the field definition on line 41, the BTreeMap key is just B256 (hashed_address), not a tuple including block_number. The comment should be updated to reflect the actual key structure.

Suggested change
/// Address mappings: (`block_number`, `hashed_address`) -> original address
/// Address mappings: `hashed_address` -> original address

Copilot uses AI. Check for mistakes.
address_mappings: BTreeMap<B256, Address>,

/// Trie updates by block number
trie_updates: BTreeMap<u64, TrieUpdatesSorted>,

Expand Down Expand Up @@ -538,6 +541,19 @@ impl OpProofsStore for InMemoryProofsStorage {
Ok(())
}

async fn store_address_mappings(
&self,
mappings: Vec<(B256, alloy_primitives::Address)>,
) -> OpProofsStorageResult<()> {
let mut inner = self.inner.write().await;

for (hashed_address, original_address) in mappings {
inner.address_mappings.insert(hashed_address, original_address);
}

Ok(())
}

async fn get_earliest_block_number(&self) -> OpProofsStorageResult<Option<(u64, B256)>> {
let inner = self.inner.read().await;
Ok(inner.earliest_block)
Expand Down
24 changes: 24 additions & 0 deletions crates/optimism/trie/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum StorageOperation {
StoreHashedAccount,
/// Store hashed storage
StoreHashedStorage,
/// Store address mapping
StoreAddressMapping,
/// Trie cursor seek exact operation
TrieCursorSeekExact,
/// Trie cursor seek
Expand All @@ -73,6 +75,7 @@ impl StorageOperation {
Self::StoreStorageBranch => "store_storage_branch",
Self::StoreHashedAccount => "store_hashed_account",
Self::StoreHashedStorage => "store_hashed_storage",
Self::StoreAddressMapping => "store_address_mapping",
Self::TrieCursorSeekExact => "trie_cursor_seek_exact",
Self::TrieCursorSeek => "trie_cursor_seek",
Self::TrieCursorNext => "trie_cursor_next",
Expand Down Expand Up @@ -469,6 +472,27 @@ where
result
}

async fn store_address_mappings(
&self,
mappings: Vec<(B256, alloy_primitives::Address)>,
) -> OpProofsStorageResult<()> {
let count = mappings.len();
let start = Instant::now();
let result = self.storage.store_address_mappings(mappings).await;
let duration = start.elapsed();

// Record per-item duration
if count > 0 {
self.metrics.record_duration_per_item(
StorageOperation::StoreAddressMapping,
duration,
count,
);
}

result
}

#[inline]
async fn get_earliest_block_number(&self) -> OpProofsStorageResult<Option<(u64, B256)>> {
self.storage.get_earliest_block_number().await
Expand Down
Loading