From 6b28e98e57a14fd7fd5a34a6bf39836c9c6db5e8 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Thu, 23 Oct 2025 19:35:53 +0530 Subject: [PATCH 01/12] fix: storage trie cursor --- crates/optimism/trie/src/db/cursor.rs | 70 ++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/crates/optimism/trie/src/db/cursor.rs b/crates/optimism/trie/src/db/cursor.rs index c2bcb0e8ba1..e38b12983a4 100644 --- a/crates/optimism/trie/src/db/cursor.rs +++ b/crates/optimism/trie/src/db/cursor.rs @@ -16,6 +16,7 @@ use reth_db::{ }; use reth_primitives_traits::Account; use reth_trie::{BranchNodeCompact, Nibbles, StoredNibbles}; +use tracing::info; /// Generic alias for dup cursor for T pub(crate) type Dup<'tx, T> = <::TX as DbTx>::DupCursor; @@ -286,11 +287,31 @@ where fn seek(&mut self, key: B256) -> OpProofsStorageResult> { let storage_key = HashedStorageKey::new(self.hashed_address, key); - self.inner.seek(storage_key).map(|opt| opt.map(|(k, v)| (k.hashed_storage_key, v.0))) + let result = self + .inner + .seek(storage_key) + .map(|opt| { + opt.and_then(|(k, v)| { + // Only return entries that belong to the bound address + (k.hashed_address == self.hashed_address).then_some((k.hashed_storage_key, v.0)) + }) + })?; + + Ok(result) } fn next(&mut self) -> OpProofsStorageResult> { - self.inner.next().map(|opt| opt.map(|(k, v)| (k.hashed_storage_key, v.0))) + let result = self + .inner + .next() + .map(|opt| { + opt.and_then(|(k, v)| { + // Only return entries that belong to the bound address + (k.hashed_address == self.hashed_address).then_some((k.hashed_storage_key, v.0)) + }) + })?; + + Ok(result) } } @@ -1145,6 +1166,51 @@ mod tests { assert_eq!((k2, v2), (s2, U256::from(22))); } + #[test] + fn hashed_storage_address_boundry() { + let db = setup_db(); + let addr1 = B256::from([0xAC; 32]); + let addr2 = B256::from([0xAD; 32]); + let s1 = B256::from([0x01; 32]); + let s2 = B256::from([0x02; 32]); + let s3 = B256::from([0x03; 32]); + + { + let wtx = db.tx_mut().expect("rw"); + append_hashed_storage(&wtx, addr1, s1, 10, Some(U256::from(11))); + append_hashed_storage(&wtx, addr1, s2, 10, Some(U256::from(22))); + wtx.commit().expect("commit"); + } + + { + let wtx = db.tx_mut().expect("rw"); + append_hashed_storage(&wtx, addr2, s1, 10, Some(U256::from(33))); + append_hashed_storage(&wtx, addr2, s2, 10, Some(U256::from(44))); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro"); + let mut cur = storage_cursor(&tx, 100, addr1); + + let (k1, v1) = OpProofsHashedCursorRO::next(&mut cur).expect("ok").expect("some"); + assert_eq!((k1, v1), (s1, U256::from(11))); + + let (k2, v2) = OpProofsHashedCursorRO::next(&mut cur).expect("ok").expect("some"); + assert_eq!((k2, v2), (s2, U256::from(22))); + + let out = OpProofsHashedCursorRO::next(&mut cur).expect("ok"); + assert!(out.is_none(), "should stop at address boundary"); + + let (k1, v1) = OpProofsHashedCursorRO::seek(&mut cur, s1).expect("ok").expect("some"); + assert_eq!((k1, v1), (s1, U256::from(11))); + + let (k2, v2) = OpProofsHashedCursorRO::seek(&mut cur, s2).expect("ok").expect("some"); + assert_eq!((k2, v2), (s2, U256::from(22))); + + let out = OpProofsHashedCursorRO::seek(&mut cur, s3).expect("ok"); + assert!(out.is_none(), "should not see keys from other address"); + } + #[test] fn hashed_account_seek_maps_key_and_value() { let db = setup_db(); From 41a68bf6a5aac27dc71f42db62e4819c98ac7e1a Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Thu, 23 Oct 2025 19:59:08 +0530 Subject: [PATCH 02/12] storage trie cursor fixed --- crates/optimism/trie/src/db/cursor.rs | 106 +++++++++++++++++++++----- 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/crates/optimism/trie/src/db/cursor.rs b/crates/optimism/trie/src/db/cursor.rs index e38b12983a4..666f1df3c01 100644 --- a/crates/optimism/trie/src/db/cursor.rs +++ b/crates/optimism/trie/src/db/cursor.rs @@ -16,7 +16,6 @@ use reth_db::{ }; use reth_primitives_traits::Account; use reth_trie::{BranchNodeCompact, Nibbles, StoredNibbles}; -use tracing::info; /// Generic alias for dup cursor for T pub(crate) type Dup<'tx, T> = <::TX as DbTx>::DupCursor; @@ -226,9 +225,14 @@ where ) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { let key = StorageTrieKey::new(address, StoredNibbles(path)); - return self.inner.seek_exact(key).map(|opt| { - opt.and_then(|(k, node)| (k.hashed_address == address).then_some((k.path.0, node))) - }) + return self + .inner + .seek_exact(key) + .map(|opt| { + opt.and_then(|(k, node)| { + (k.hashed_address == address).then_some((k.path.0, node)) + }) + }) } Ok(None) } @@ -239,26 +243,45 @@ where ) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { let key = StorageTrieKey::new(address, StoredNibbles(path)); - return self.inner.seek(key).map(|opt| opt.map(|(k, node)| (k.path.0, node))) + return self + .inner + .seek(key) + .map(|opt| { + opt.and_then(|(k, node)| { + (k.hashed_address == address).then_some((k.path.0, node)) + }) + }) } Ok(None) } fn next(&mut self) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { - return self.inner.next().map(|opt| { - opt.and_then(|(k, node)| (k.hashed_address == address).then_some((k.path.0, node))) - }) + return self + .inner + .next() + .map(|opt| { + opt.and_then(|(k, node)| { + (k.hashed_address == address).then_some((k.path.0, node)) + }) + }) } Ok(None) } fn current(&mut self) -> OpProofsStorageResult> { - self.inner - .cursor - .current() - .map_err(|e| OpProofsStorageError::Other(e.into())) - .map(|opt| opt.map(|(k, _)| k.path.0)) + if let Some(address) = self.hashed_address { + return self.inner + .cursor + .current() + .map_err(|e| OpProofsStorageError::Other(e.into())) + .map(|opt| { + opt.and_then(|(k, _)| { + (k.hashed_address == address).then_some(k.path.0) + }) + }) + } + Ok(None) } } @@ -1033,6 +1056,7 @@ mod tests { let p1 = Nibbles::from_nibbles([0x01]); let p2 = Nibbles::from_nibbles([0x02]); + let p3 = Nibbles::from_nibbles([0x03]); { let wtx = db.tx_mut().expect("rw tx"); @@ -1043,12 +1067,58 @@ mod tests { wtx.commit().expect("commit"); } - let tx = db.tx().expect("ro tx"); - let mut cur_a = storage_trie_cursor(&tx, 100, addr_a); + // test seek behaviour + { + let tx = db.tx().expect("ro tx"); + let mut cur_a = storage_trie_cursor(&tx, 100, addr_a); - // seek at p1: for A there is no p1; the next key >= p1 under A is p2 - let out = OpProofsTrieCursorRO::seek(&mut cur_a, p1).expect("ok").expect("some"); - assert_eq!(out.0, p2); + // seek at p1: for A there is no p1; the next key >= p1 under A is p2 + let out = OpProofsTrieCursorRO::seek(&mut cur_a, p1).expect("ok").expect("some"); + assert_eq!(out.0, p2); + + // seek at p2: exact match + let out = OpProofsTrieCursorRO::seek(&mut cur_a, p2).expect("ok").expect("some"); + assert_eq!(out.0, p2); + + // seek at p3: no p3 under A; no next key ≥ p3 under A → None + let out = OpProofsTrieCursorRO::seek(&mut cur_a, p3).expect("ok"); + assert!(out.is_none(), "no key ≥ p3 under A"); + } + + // test next behaviour + { + let tx = db.tx().expect("ro tx"); + let mut cur_a = storage_trie_cursor(&tx, 100, addr_a); + + let out = OpProofsTrieCursorRO::next(&mut cur_a).expect("ok").expect("some"); + assert_eq!(out.0, p2); + + // next should yield None as there is no further key under A + let out = OpProofsTrieCursorRO::next(&mut cur_a).expect("ok"); + assert!(out.is_none(), "no more keys under A"); + + // current should return None + let out = OpProofsTrieCursorRO::current(&mut cur_a).expect("ok"); + assert!(out.is_none(), "no current key after EOF"); + } + + // test seek_exact behaviour + { + let tx = db.tx().expect("ro tx"); + let mut cur_a = storage_trie_cursor(&tx, 100, addr_a); + + // seek_exact at p1: no exact match + let out = OpProofsTrieCursorRO::seek_exact(&mut cur_a, p1).expect("ok"); + assert!(out.is_none(), "no exact p1 under A"); + + // seek_exact at p2: exact match + let out = OpProofsTrieCursorRO::seek_exact(&mut cur_a, p2).expect("ok").expect("some"); + assert_eq!(out.0, p2); + + // seek_exact at p3: no exact match + let out = OpProofsTrieCursorRO::seek_exact(&mut cur_a, p3).expect("ok"); + assert!(out.is_none(), "no exact p3 under A"); + } } #[test] From cf741774ae6a31832f71d0b49aacb51940e02f2e Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Thu, 23 Oct 2025 20:02:38 +0530 Subject: [PATCH 03/12] lint fix --- crates/optimism/trie/src/db/cursor.rs | 74 +++++++++------------------ 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/crates/optimism/trie/src/db/cursor.rs b/crates/optimism/trie/src/db/cursor.rs index 666f1df3c01..9f9609a9e02 100644 --- a/crates/optimism/trie/src/db/cursor.rs +++ b/crates/optimism/trie/src/db/cursor.rs @@ -225,14 +225,9 @@ where ) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { let key = StorageTrieKey::new(address, StoredNibbles(path)); - return self - .inner - .seek_exact(key) - .map(|opt| { - opt.and_then(|(k, node)| { - (k.hashed_address == address).then_some((k.path.0, node)) - }) - }) + return self.inner.seek_exact(key).map(|opt| { + opt.and_then(|(k, node)| (k.hashed_address == address).then_some((k.path.0, node))) + }) } Ok(None) } @@ -243,43 +238,30 @@ where ) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { let key = StorageTrieKey::new(address, StoredNibbles(path)); - return self - .inner - .seek(key) - .map(|opt| { - opt.and_then(|(k, node)| { - (k.hashed_address == address).then_some((k.path.0, node)) - }) - }) + return self.inner.seek(key).map(|opt| { + opt.and_then(|(k, node)| (k.hashed_address == address).then_some((k.path.0, node))) + }) } Ok(None) } fn next(&mut self) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { - return self - .inner - .next() - .map(|opt| { - opt.and_then(|(k, node)| { - (k.hashed_address == address).then_some((k.path.0, node)) - }) - }) + return self.inner.next().map(|opt| { + opt.and_then(|(k, node)| (k.hashed_address == address).then_some((k.path.0, node))) + }) } Ok(None) } fn current(&mut self) -> OpProofsStorageResult> { if let Some(address) = self.hashed_address { - return self.inner + return self + .inner .cursor .current() .map_err(|e| OpProofsStorageError::Other(e.into())) - .map(|opt| { - opt.and_then(|(k, _)| { - (k.hashed_address == address).then_some(k.path.0) - }) - }) + .map(|opt| opt.and_then(|(k, _)| (k.hashed_address == address).then_some(k.path.0))) } Ok(None) } @@ -310,29 +292,23 @@ where fn seek(&mut self, key: B256) -> OpProofsStorageResult> { let storage_key = HashedStorageKey::new(self.hashed_address, key); - let result = self - .inner - .seek(storage_key) - .map(|opt| { - opt.and_then(|(k, v)| { - // Only return entries that belong to the bound address - (k.hashed_address == self.hashed_address).then_some((k.hashed_storage_key, v.0)) - }) - })?; + let result = self.inner.seek(storage_key).map(|opt| { + opt.and_then(|(k, v)| { + // Only return entries that belong to the bound address + (k.hashed_address == self.hashed_address).then_some((k.hashed_storage_key, v.0)) + }) + })?; Ok(result) } fn next(&mut self) -> OpProofsStorageResult> { - let result = self - .inner - .next() - .map(|opt| { - opt.and_then(|(k, v)| { - // Only return entries that belong to the bound address - (k.hashed_address == self.hashed_address).then_some((k.hashed_storage_key, v.0)) - }) - })?; + let result = self.inner.next().map(|opt| { + opt.and_then(|(k, v)| { + // Only return entries that belong to the bound address + (k.hashed_address == self.hashed_address).then_some((k.hashed_storage_key, v.0)) + }) + })?; Ok(result) } @@ -1237,7 +1213,7 @@ mod tests { } #[test] - fn hashed_storage_address_boundry() { + fn hashed_storage_address_boundary() { let db = setup_db(); let addr1 = B256::from([0xAC; 32]); let addr2 = B256::from([0xAD; 32]); From 528c6ce4ede552cdccfbf35a03bc05a29a63305e Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Fri, 24 Oct 2025 10:17:32 +0530 Subject: [PATCH 04/12] feat: update latest block in trie updates --- crates/optimism/trie/src/api.rs | 6 +- crates/optimism/trie/src/db/store.rs | 125 +++++++++++++++++++++----- crates/optimism/trie/src/in_memory.rs | 11 ++- crates/optimism/trie/src/live.rs | 23 +++-- crates/optimism/trie/src/metrics.rs | 5 +- crates/optimism/trie/tests/lib.rs | 82 +++++++++++++---- 6 files changed, 197 insertions(+), 55 deletions(-) diff --git a/crates/optimism/trie/src/api.rs b/crates/optimism/trie/src/api.rs index 2a9453007c3..62239e95b5b 100644 --- a/crates/optimism/trie/src/api.rs +++ b/crates/optimism/trie/src/api.rs @@ -1,6 +1,7 @@ //! Storage API for external storage of intermediary trie nodes. use alloy_primitives::{map::HashMap, B256, U256}; +use alloy_eips::eip1898::BlockWithParent; use auto_impl::auto_impl; use reth_db::DatabaseError; use reth_primitives_traits::Account; @@ -17,6 +18,9 @@ pub enum OpProofsStorageError { /// Parent block number is less than earliest stored block number #[error("Parent block number is less than earliest stored block number")] UnknownParent, + /// Block is out of order + #[error("Block {0} is out of order (parent: {1}, latest stored block number: {2})")] + OutOfOrder(u64, B256, B256), /// Block update failed since parent state #[error("Cannot execute block updates for block {0} without parent state {1} (latest stored block number: {2})")] BlockUpdateFailed(u64, u64, u64), @@ -187,7 +191,7 @@ pub trait OpProofsStore: Send + Sync + Debug { /// so should only happen for legacy reasons. fn store_trie_updates( &self, - block_number: u64, + block_ref: BlockWithParent, block_state_diff: BlockStateDiff, ) -> impl Future> + Send; diff --git a/crates/optimism/trie/src/db/store.rs b/crates/optimism/trie/src/db/store.rs index 893443dbce6..528582ed9d2 100644 --- a/crates/optimism/trie/src/db/store.rs +++ b/crates/optimism/trie/src/db/store.rs @@ -11,6 +11,7 @@ use crate::{ BlockStateDiff, OpProofsStorageError, OpProofsStorageResult, OpProofsStore, }; use alloy_primitives::{map::HashMap, B256, U256}; +use alloy_eips::eip1898::BlockWithParent; use itertools::Itertools; use reth_db::{ cursor::{DbCursorRO, DbCursorRW, DbDupCursorRW}, @@ -238,9 +239,10 @@ impl OpProofsStore for MdbxProofsStorage { async fn store_trie_updates( &self, - block_number: u64, + block_ref: BlockWithParent, block_state_diff: BlockStateDiff, ) -> OpProofsStorageResult<()> { + let block_number = block_ref.block.number; let sorted_trie_updates = block_state_diff.trie_updates.into_sorted(); let sorted_account_nodes = sorted_trie_updates.account_nodes; @@ -260,6 +262,21 @@ impl OpProofsStore for MdbxProofsStorage { .collect::>(); self.env.update(|tx| { + // check latest stored block is the parent of incoming block + let mut proof_window_cursor = tx.new_cursor::()?; + let latest_hash = proof_window_cursor + .seek_exact(ProofWindowKey::LatestBlock)? + .map(|(_, v)| *v.hash()) + .unwrap_or(B256::ZERO); + if latest_hash != block_ref.parent { + return Err(OpProofsStorageError::OutOfOrder( + block_number, + block_ref.parent, + latest_hash, + ) + .into()); + } + let mut account_trie_cursor = tx.new_cursor::()?; for (path, node) in sorted_account_nodes { let vv = VersionedValue { block_number, value: MaybeDeleted(node) }; @@ -296,6 +313,9 @@ impl OpProofsStore for MdbxProofsStorage { } } + // update proof window latest block + proof_window_cursor + .append(ProofWindowKey::LatestBlock, &BlockNumberHash::new(block_number, block_ref.block.hash))?; Ok(()) })? } @@ -340,6 +360,7 @@ mod tests { StorageTrieKey, }; use alloy_primitives::B256; + use alloy_eips::NumHash; use reth_db::{cursor::DbDupCursorRO, transaction::DbTx}; use reth_trie::{ updates::StorageTrieUpdates, BranchNodeCompact, HashedStorage, Nibbles, StoredNibbles, @@ -761,7 +782,7 @@ mod tests { let store = MdbxProofsStorage::new(dir.path()).expect("env"); // Sample block number - const BLOCK: u64 = 42; + const BLOCK: BlockWithParent = BlockWithParent::new(B256::ZERO, NumHash::new(42, B256::ZERO)); // Sample addresses and keys let addr1 = B256::from([0x11; 32]); @@ -830,22 +851,22 @@ mod tests { // Check first node let vv1 = - cur.seek_by_key_subkey(account_path1.into(), BLOCK).expect("seek").expect("exists"); - assert_eq!(vv1.block_number, BLOCK); + cur.seek_by_key_subkey(account_path1.into(), BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv1.block_number, BLOCK.block.number); assert!(vv1.value.0.is_some()); // Check second node let vv2 = - cur.seek_by_key_subkey(account_path2.into(), BLOCK).expect("seek").expect("exists"); - assert_eq!(vv2.block_number, BLOCK); + cur.seek_by_key_subkey(account_path2.into(), BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv2.block_number, BLOCK.block.number); assert!(vv2.value.0.is_some()); // Check removed node let vv3 = cur - .seek_by_key_subkey(removed_account_path.into(), BLOCK) + .seek_by_key_subkey(removed_account_path.into(), BLOCK.block.number) .expect("seek") .expect("exists"); - assert_eq!(vv3.block_number, BLOCK); + assert_eq!(vv3.block_number, BLOCK.block.number); assert!(vv3.value.0.is_none(), "Expected node deletion"); } @@ -856,14 +877,14 @@ mod tests { // Check node for addr1 let key1 = StorageTrieKey::new(addr1, storage_path1.into()); - let vv1 = cur.seek_by_key_subkey(key1, BLOCK).expect("seek").expect("exists"); - assert_eq!(vv1.block_number, BLOCK); + let vv1 = cur.seek_by_key_subkey(key1, BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv1.block_number, BLOCK.block.number); assert!(vv1.value.0.is_some()); // Check node for addr2 let key2 = StorageTrieKey::new(addr2, storage_path2.into()); - let vv2 = cur.seek_by_key_subkey(key2, BLOCK).expect("seek").expect("exists"); - assert_eq!(vv2.block_number, BLOCK); + let vv2 = cur.seek_by_key_subkey(key2, BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv2.block_number, BLOCK.block.number); assert!(vv2.value.0.is_some()); } @@ -873,13 +894,13 @@ mod tests { let mut cur = tx.new_cursor::().expect("cursor"); // Check account1 (exists) - let vv1 = cur.seek_by_key_subkey(addr1, BLOCK).expect("seek").expect("exists"); - assert_eq!(vv1.block_number, BLOCK); + let vv1 = cur.seek_by_key_subkey(addr1, BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv1.block_number, BLOCK.block.number); assert_eq!(vv1.value.0, Some(acc1)); // Check account2 (deletion) - let vv2 = cur.seek_by_key_subkey(addr2, BLOCK).expect("seek").expect("exists"); - assert_eq!(vv2.block_number, BLOCK); + let vv2 = cur.seek_by_key_subkey(addr2, BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv2.block_number, BLOCK.block.number); assert!(vv2.value.0.is_none(), "Expected account deletion"); } @@ -890,18 +911,80 @@ mod tests { // Check storage for addr1 let key1 = HashedStorageKey::new(addr1, slot1); - let vv1 = cur.seek_by_key_subkey(key1, BLOCK).expect("seek").expect("exists"); - assert_eq!(vv1.block_number, BLOCK); + let vv1 = cur.seek_by_key_subkey(key1, BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv1.block_number, BLOCK.block.number); let inner1 = vv1.value.0.as_ref().expect("Some(StorageValue)"); assert_eq!(inner1.0, val1); // Check storage for addr2 let key2 = HashedStorageKey::new(addr2, slot2); - let vv2 = cur.seek_by_key_subkey(key2, BLOCK).expect("seek").expect("exists"); - assert_eq!(vv2.block_number, BLOCK); + let vv2 = cur.seek_by_key_subkey(key2, BLOCK.block.number).expect("seek").expect("exists"); + assert_eq!(vv2.block_number, BLOCK.block.number); let inner2 = vv2.value.0.as_ref().expect("Some(StorageValue)"); assert_eq!(inner2.0, val2); } + + // check the latest block number in proof window + { + let tx = store.env.tx().expect("tx"); + let mut proof_window_cursor = tx.new_cursor::().expect("cursor"); + let latest_block = proof_window_cursor + .seek(ProofWindowKey::LatestBlock) + .expect("seek") + .expect("exists"); + assert_eq!(latest_block.1.number(), BLOCK.block.number); + assert_eq!(*latest_block.1.hash(), BLOCK.block.hash); + } + } + + #[tokio::test] + async fn store_trie_updates_out_of_order_rejects() { + let dir = tempfile::TempDir::new().unwrap(); + let store = MdbxProofsStorage::new(dir.path()).expect("env"); + + // set latest to some hash H1 + let existing_block = BlockWithParent::new(B256::random(), NumHash::new(1, B256::random())); + store.set_earliest_block_number(existing_block.block.number, existing_block.block.hash).await.expect("set"); + + // incoming block whose parent != existing latest + let bad_parent = B256::from([0xFF; 32]); + let bad_block: BlockWithParent = BlockWithParent::new(bad_parent, NumHash::new(2, B256::ZERO)); + let diff = BlockStateDiff::default(); + + let res = store.store_trie_updates(bad_block, diff).await; + assert!(matches!(res, Err(OpProofsStorageError::OutOfOrder(..) | _))); + // verify nothing written: proof window still unchanged + let latest = store.get_latest_block_number().await.expect("get latest"); + assert_eq!(latest.unwrap().1, existing_block.block.hash); + } + + #[tokio::test] + async fn store_trie_updates_multiple_blocks_append_versions() { + let dir = tempfile::TempDir::new().unwrap(); + let store = MdbxProofsStorage::new(dir.path()).expect("env"); + + let addr = B256::from([0x21; 32]); + // block A (parent = ZERO) + let block_a = BlockWithParent::new(B256::ZERO, NumHash::new(1, B256::random())); + let mut diff_a = BlockStateDiff::default(); + diff_a.post_state.accounts.insert(addr, Some(Account::default())); + + store.store_trie_updates(block_a, diff_a).await.expect("store A"); + + // block B (parent = hash of A) + let block_b = BlockWithParent::new(block_a.block.hash, NumHash::new(2, B256::random())); + let mut diff_b = BlockStateDiff::default(); + diff_b.post_state.accounts.insert(addr, Some(Account { nonce: 5, ..Default::default() })); + + store.store_trie_updates(block_b, diff_b).await.expect("store B"); + + // verify we can retrieve entries for both block numbers + let tx = store.env.tx().expect("tx"); + let mut cur = tx.new_cursor::().expect("cursor"); + let v_a = cur.seek_by_key_subkey(addr, block_a.block.number).expect("seek").expect("exists"); + let v_b = cur.seek_by_key_subkey(addr, block_b.block.number).expect("seek").expect("exists"); + assert_eq!(v_a.block_number, block_a.block.number); + assert_eq!(v_b.block_number, block_b.block.number); } #[tokio::test] @@ -909,7 +992,7 @@ mod tests { let dir = TempDir::new().unwrap(); let store = MdbxProofsStorage::new(dir.path()).expect("env"); - const BLOCK: u64 = 42; + const BLOCK: BlockWithParent = BlockWithParent::new(B256::ZERO, NumHash::new(42, B256::ZERO)); // Create BlockStateDiff with empty collections let block_state_diff = BlockStateDiff::default(); diff --git a/crates/optimism/trie/src/in_memory.rs b/crates/optimism/trie/src/in_memory.rs index c7c4b114395..2261e6dcdab 100644 --- a/crates/optimism/trie/src/in_memory.rs +++ b/crates/optimism/trie/src/in_memory.rs @@ -4,6 +4,7 @@ use crate::{ BlockStateDiff, OpProofsHashedCursorRO, OpProofsStorageError, OpProofsStorageResult, OpProofsStore, OpProofsTrieCursorRO, }; +use alloy_eips::eip1898::BlockWithParent; use alloy_primitives::{map::HashMap, B256, U256}; use reth_primitives_traits::Account; use reth_trie::{updates::TrieUpdates, BranchNodeCompact, HashedPostState, Nibbles}; @@ -506,12 +507,12 @@ impl OpProofsStore for InMemoryProofsStorage { async fn store_trie_updates( &self, - block_number: u64, + block_ref: BlockWithParent, block_state_diff: BlockStateDiff, ) -> OpProofsStorageResult<()> { let mut inner = self.inner.write().await; - inner.store_trie_updates(block_number, block_state_diff); + inner.store_trie_updates(block_ref.block.number, block_state_diff); Ok(()) } @@ -629,6 +630,7 @@ impl OpProofsStore for InMemoryProofsStorage { mod tests { use super::*; use alloy_primitives::U256; + use alloy_eips::NumHash; use reth_primitives_traits::Account; #[tokio::test] @@ -662,9 +664,10 @@ mod tests { let block_state_diff = BlockStateDiff { trie_updates: trie_updates.clone(), post_state: post_state.clone() }; - storage.store_trie_updates(5, block_state_diff).await?; + const BLOCK: BlockWithParent = BlockWithParent::new(B256::ZERO, NumHash::new(5, B256::ZERO)); + storage.store_trie_updates(BLOCK, block_state_diff).await?; - let retrieved_diff = storage.fetch_trie_updates(5).await?; + let retrieved_diff = storage.fetch_trie_updates(BLOCK.block.number).await?; assert_eq!(retrieved_diff.trie_updates, trie_updates); assert_eq!(retrieved_diff.post_state, post_state); diff --git a/crates/optimism/trie/src/live.rs b/crates/optimism/trie/src/live.rs index 597eac9b594..026bf3b4642 100644 --- a/crates/optimism/trie/src/live.rs +++ b/crates/optimism/trie/src/live.rs @@ -5,6 +5,7 @@ use crate::{ provider::OpProofsStateProviderRef, OpProofsStorage, }; +use alloy_eips::{NumHash, eip1898::BlockWithParent}; use derive_more::Constructor; use reth_evm::{execute::Executor, ConfigureEvm}; use reth_primitives_traits::{AlloyBlockHeader, BlockTy, RecoveredBlock}; @@ -14,7 +15,7 @@ use reth_provider::{ }; use reth_revm::database::StateProviderDatabase; use std::time::Instant; -use tracing::debug; +use tracing::info; /// Live trie collector for external proofs storage. #[derive(Debug, Constructor)] @@ -48,6 +49,7 @@ where return Err(OpProofsStorageError::NoBlocksFound.into()); }; + let fetch_block_duration = start.elapsed(); let parent_block_number = block.number() - 1; @@ -64,7 +66,10 @@ where .into()); } - let block_number = block.number(); + let block_ref = BlockWithParent::new( + block.parent_hash(), + NumHash::new(block.number(), block.hash()), + ); // TODO: should we check block hash here? @@ -101,19 +106,19 @@ where self.storage .store_trie_updates( - block_number, + block_ref, BlockStateDiff { trie_updates, post_state: hashed_state }, ) .await?; let write_trie_updates_duration = start.elapsed() - calculate_state_root_duration; + let execute_and_store_total_duration = start.elapsed(); - debug!("execute_and_store_block_updates duration: {:?}", start.elapsed()); - debug!("- fetch_block_duration: {:?}", fetch_block_duration); - debug!("- init_provider_duration: {:?}", init_provider_duration); - debug!("- execute_block_duration: {:?}", execute_block_duration); - debug!("- calculate_state_root_duration: {:?}", calculate_state_root_duration); - debug!("- write_trie_updates_duration: {:?}", write_trie_updates_duration); + info!( + block_number = block.number(), ?execute_and_store_total_duration, ?fetch_block_duration, + ?init_provider_duration, ?execute_block_duration, ?calculate_state_root_duration, + ?write_trie_updates_duration, "Stored trie updates", + ); Ok(()) } diff --git a/crates/optimism/trie/src/metrics.rs b/crates/optimism/trie/src/metrics.rs index e2f31e5c833..962a2da34ac 100644 --- a/crates/optimism/trie/src/metrics.rs +++ b/crates/optimism/trie/src/metrics.rs @@ -5,6 +5,7 @@ use crate::{ OpProofsTrieCursorRO, }; use alloy_primitives::{map::HashMap, B256, U256}; +use alloy_eips::eip1898::BlockWithParent; use derive_more::Constructor; use metrics::{Counter, Histogram}; use reth_metrics::Metrics; @@ -462,10 +463,10 @@ where #[inline] async fn store_trie_updates( &self, - block_number: u64, + block_ref: BlockWithParent, block_state_diff: BlockStateDiff, ) -> OpProofsStorageResult<()> { - self.storage.store_trie_updates(block_number, block_state_diff).await + self.storage.store_trie_updates(block_ref, block_state_diff).await } #[inline] diff --git a/crates/optimism/trie/tests/lib.rs b/crates/optimism/trie/tests/lib.rs index 0e3ab2561c3..7d02249830e 100644 --- a/crates/optimism/trie/tests/lib.rs +++ b/crates/optimism/trie/tests/lib.rs @@ -1,6 +1,7 @@ //! Common test suite for [`OpProofsStore`] implementations. use alloy_primitives::{map::HashMap, B256, U256}; +use alloy_eips::{NumHash, eip1898::BlockWithParent}; use reth_optimism_trie::{ BlockStateDiff, InMemoryProofsStorage, OpProofsHashedCursorRO, OpProofsStorageError, OpProofsStore, OpProofsTrieCursorRO, @@ -90,17 +91,20 @@ async fn test_earliest_block_operations( async fn test_trie_updates_operations( storage: S, ) -> Result<(), OpProofsStorageError> { - let block_number = 50; + let block_ref = BlockWithParent::new( + B256::ZERO, + NumHash::new(50, B256::repeat_byte(0x96)), + ); let trie_updates = TrieUpdates::default(); let post_state = HashedPostState::default(); let block_state_diff = BlockStateDiff { trie_updates: trie_updates.clone(), post_state: post_state.clone() }; // Store trie updates - storage.store_trie_updates(block_number, block_state_diff).await?; + storage.store_trie_updates(block_ref, block_state_diff).await?; // Retrieve and verify - let retrieved_diff = storage.fetch_trie_updates(block_number).await?; + let retrieved_diff = storage.fetch_trie_updates(block_ref.block.number).await?; assert_eq!(retrieved_diff.trie_updates, trie_updates); assert_eq!(retrieved_diff.post_state, post_state); @@ -1115,6 +1119,10 @@ async fn test_store_trie_updates_with_wiped_storage( use reth_trie::HashedStorage; let hashed_address = B256::repeat_byte(0x01); + let block_ref = BlockWithParent::new( + B256::ZERO, + NumHash::new(100, B256::repeat_byte(0x96)), + ); // First, store some storage values at block 50 let storage_slots = vec![ @@ -1146,7 +1154,7 @@ async fn test_store_trie_updates_with_wiped_storage( let block_state_diff = BlockStateDiff { trie_updates: TrieUpdates::default(), post_state }; // Store the wiped state - storage.store_trie_updates(100, block_state_diff).await?; + storage.store_trie_updates(block_ref, block_state_diff).await?; // After wiping, cursor at block 150 should see NO storage values let mut cursor150 = storage.storage_hashed_cursor(hashed_address, 150)?; @@ -1199,7 +1207,10 @@ async fn test_store_trie_updates_comprehensive( ) -> Result<(), OpProofsStorageError> { use reth_trie::{updates::StorageTrieUpdates, HashedStorage}; - let block_number = 100; + let block_ref = BlockWithParent::new( + B256::ZERO, + NumHash::new(100, B256::repeat_byte(0x96)), + ); // Create comprehensive trie updates with branches, leaves, and removals let mut trie_updates = TrieUpdates::default(); @@ -1260,10 +1271,10 @@ async fn test_store_trie_updates_comprehensive( let block_state_diff = BlockStateDiff { trie_updates, post_state }; // Store the updates - storage.store_trie_updates(block_number, block_state_diff).await?; + storage.store_trie_updates(block_ref, block_state_diff).await?; // ========== Verify Account Branch Nodes ========== - let mut account_trie_cursor = storage.account_trie_cursor(block_number + 10)?; + let mut account_trie_cursor = storage.account_trie_cursor(block_ref.block.number + 10)?; // Should find the added branches let result1 = account_trie_cursor.seek_exact(account_path1)?; @@ -1279,7 +1290,7 @@ async fn test_store_trie_updates_comprehensive( assert!(removed_result.is_none(), "Removed account node should not be found"); // ========== Verify Storage Branch Nodes ========== - let mut storage_trie_cursor = storage.storage_trie_cursor(hashed_address, block_number + 10)?; + let mut storage_trie_cursor = storage.storage_trie_cursor(hashed_address, block_ref.block.number + 10)?; let storage_result1 = storage_trie_cursor.seek_exact(storage_path1)?; assert!(storage_result1.is_some(), "Storage branch node 1 should be found"); @@ -1292,7 +1303,7 @@ async fn test_store_trie_updates_comprehensive( assert!(removed_storage_result.is_none(), "Removed storage node should not be found"); // ========== Verify Account Leaves ========== - let mut account_cursor = storage.account_hashed_cursor(block_number + 10)?; + let mut account_cursor = storage.account_hashed_cursor(block_ref.block.number + 10)?; let acc1_result = account_cursor.seek(account1_addr)?; assert!(acc1_result.is_some(), "Account 1 should be found"); @@ -1312,7 +1323,7 @@ async fn test_store_trie_updates_comprehensive( ); // ========== Verify Storage Leaves ========== - let mut storage_cursor = storage.storage_hashed_cursor(storage_addr, block_number + 10)?; + let mut storage_cursor = storage.storage_hashed_cursor(storage_addr, block_ref.block.number + 10)?; let slot1_result = storage_cursor.seek(B256::repeat_byte(0x01))?; assert!(slot1_result.is_some(), "Storage slot 1 should be found"); @@ -1330,7 +1341,7 @@ async fn test_store_trie_updates_comprehensive( ); // ========== Verify fetch_trie_updates can retrieve the data ========== - let fetched_diff = storage.fetch_trie_updates(block_number).await?; + let fetched_diff = storage.fetch_trie_updates(block_ref.block.number).await?; // Check that trie updates are stored assert_eq!( @@ -1367,6 +1378,11 @@ async fn test_replace_updates_applies_all_updates( ) -> Result<(), OpProofsStorageError> { use reth_trie::{updates::StorageTrieUpdates, HashedStorage}; + let block_ref_50 = BlockWithParent::new( + B256::ZERO, + NumHash::new(50, B256::repeat_byte(0x96)), + ); + // ========== Setup: Store initial state at blocks 50, 100, 101 ========== let initial_account_addr = B256::repeat_byte(0x10); let initial_account = create_test_account_with_values(1, 1000, 0xAA); @@ -1387,7 +1403,7 @@ async fn test_replace_updates_applies_all_updates( let initial_diff_50 = BlockStateDiff { trie_updates: initial_trie_updates_50, post_state: initial_post_state_50 }; - storage.store_trie_updates(50, initial_diff_50).await?; + storage.store_trie_updates(block_ref_50, initial_diff_50).await?; // Store data at block 100 (common block) let mut initial_trie_updates_100 = TrieUpdates::default(); @@ -1403,7 +1419,13 @@ async fn test_replace_updates_applies_all_updates( trie_updates: initial_trie_updates_100, post_state: initial_post_state_100, }; - storage.store_trie_updates(100, initial_diff_100).await?; + + let block_ref_100 = BlockWithParent::new( + B256::ZERO, + NumHash::new(100, B256::repeat_byte(0x97)), + ); + + storage.store_trie_updates(block_ref_100, initial_diff_100).await?; // Store data at block 101 (will be replaced) let mut initial_trie_updates_101 = TrieUpdates::default(); @@ -1419,7 +1441,11 @@ async fn test_replace_updates_applies_all_updates( trie_updates: initial_trie_updates_101, post_state: initial_post_state_101, }; - storage.store_trie_updates(101, initial_diff_101).await?; + let block_ref_101 = BlockWithParent::new( + B256::ZERO, + NumHash::new(101, B256::repeat_byte(0x98)), + ); + storage.store_trie_updates(block_ref_101, initial_diff_101).await?; // ========== Verify initial state exists ========== // Verify block 50 data exists @@ -1626,7 +1652,12 @@ async fn test_pure_deletions_stored_correctly( post_state: HashedPostState::default(), }; - storage.store_trie_updates(50, initial_diff).await?; + let block_ref_50 = BlockWithParent::new( + B256::ZERO, + NumHash::new(50, B256::repeat_byte(0x96)), + ); + + storage.store_trie_updates(block_ref_50, initial_diff).await?; // Verify initial state exists at block 75 let mut cursor_75 = storage.account_trie_cursor(75)?; @@ -1665,7 +1696,12 @@ async fn test_pure_deletions_stored_correctly( post_state: HashedPostState::default(), }; - storage.store_trie_updates(100, deletion_diff).await?; + let block_ref_100 = BlockWithParent::new( + B256::ZERO, + NumHash::new(100, B256::repeat_byte(0x97)), + ); + + storage.store_trie_updates(block_ref_100, deletion_diff).await?; // ========== Verify that deleted nodes return None at block 150 ========== @@ -1750,7 +1786,12 @@ async fn test_updates_take_precedence_over_removals( post_state: HashedPostState::default(), }; - storage.store_trie_updates(50, initial_diff).await?; + let block_ref_50 = BlockWithParent::new( + B256::ZERO, + NumHash::new(50, B256::repeat_byte(0x96)), + ); + + storage.store_trie_updates(block_ref_50, initial_diff).await?; // Verify initial state exists at block 75 let mut cursor_75 = storage.account_trie_cursor(75)?; @@ -1789,7 +1830,12 @@ async fn test_updates_take_precedence_over_removals( post_state: HashedPostState::default(), }; - storage.store_trie_updates(100, conflicting_diff).await?; + let block_ref_100 = BlockWithParent::new( + B256::ZERO, + NumHash::new(100, B256::repeat_byte(0x97)), + ); + + storage.store_trie_updates(block_ref_100, conflicting_diff).await?; // ========== Verify that updates took precedence at block 150 ========== From 397c0c77a315b8e1415996a2247b51af66c2f5b8 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Fri, 24 Oct 2025 11:23:20 +0530 Subject: [PATCH 05/12] lintfixes --- crates/optimism/trie/src/api.rs | 2 +- crates/optimism/trie/src/db/store.rs | 60 +++++++++++++++++--------- crates/optimism/trie/src/in_memory.rs | 5 ++- crates/optimism/trie/src/live.rs | 20 +++++---- crates/optimism/trie/src/metrics.rs | 2 +- crates/optimism/trie/tests/lib.rs | 62 ++++++++------------------- 6 files changed, 75 insertions(+), 76 deletions(-) diff --git a/crates/optimism/trie/src/api.rs b/crates/optimism/trie/src/api.rs index 62239e95b5b..6d0171a6bdb 100644 --- a/crates/optimism/trie/src/api.rs +++ b/crates/optimism/trie/src/api.rs @@ -1,7 +1,7 @@ //! Storage API for external storage of intermediary trie nodes. -use alloy_primitives::{map::HashMap, B256, U256}; use alloy_eips::eip1898::BlockWithParent; +use alloy_primitives::{map::HashMap, B256, U256}; use auto_impl::auto_impl; use reth_db::DatabaseError; use reth_primitives_traits::Account; diff --git a/crates/optimism/trie/src/db/store.rs b/crates/optimism/trie/src/db/store.rs index 528582ed9d2..fb14a6ecc1f 100644 --- a/crates/optimism/trie/src/db/store.rs +++ b/crates/optimism/trie/src/db/store.rs @@ -10,8 +10,8 @@ use crate::{ }, BlockStateDiff, OpProofsStorageError, OpProofsStorageResult, OpProofsStore, }; -use alloy_primitives::{map::HashMap, B256, U256}; use alloy_eips::eip1898::BlockWithParent; +use alloy_primitives::{map::HashMap, B256, U256}; use itertools::Itertools; use reth_db::{ cursor::{DbCursorRO, DbCursorRW, DbDupCursorRW}, @@ -314,8 +314,10 @@ impl OpProofsStore for MdbxProofsStorage { } // update proof window latest block - proof_window_cursor - .append(ProofWindowKey::LatestBlock, &BlockNumberHash::new(block_number, block_ref.block.hash))?; + proof_window_cursor.append( + ProofWindowKey::LatestBlock, + &BlockNumberHash::new(block_number, block_ref.block.hash), + )?; Ok(()) })? } @@ -359,8 +361,8 @@ mod tests { models::{AccountTrieHistory, StorageTrieHistory}, StorageTrieKey, }; - use alloy_primitives::B256; use alloy_eips::NumHash; + use alloy_primitives::B256; use reth_db::{cursor::DbDupCursorRO, transaction::DbTx}; use reth_trie::{ updates::StorageTrieUpdates, BranchNodeCompact, HashedStorage, Nibbles, StoredNibbles, @@ -782,7 +784,8 @@ mod tests { let store = MdbxProofsStorage::new(dir.path()).expect("env"); // Sample block number - const BLOCK: BlockWithParent = BlockWithParent::new(B256::ZERO, NumHash::new(42, B256::ZERO)); + const BLOCK: BlockWithParent = + BlockWithParent::new(B256::ZERO, NumHash::new(42, B256::ZERO)); // Sample addresses and keys let addr1 = B256::from([0x11; 32]); @@ -850,14 +853,18 @@ mod tests { let mut cur = tx.new_cursor::().expect("cursor"); // Check first node - let vv1 = - cur.seek_by_key_subkey(account_path1.into(), BLOCK.block.number).expect("seek").expect("exists"); + let vv1 = cur + .seek_by_key_subkey(account_path1.into(), BLOCK.block.number) + .expect("seek") + .expect("exists"); assert_eq!(vv1.block_number, BLOCK.block.number); assert!(vv1.value.0.is_some()); // Check second node - let vv2 = - cur.seek_by_key_subkey(account_path2.into(), BLOCK.block.number).expect("seek").expect("exists"); + let vv2 = cur + .seek_by_key_subkey(account_path2.into(), BLOCK.block.number) + .expect("seek") + .expect("exists"); assert_eq!(vv2.block_number, BLOCK.block.number); assert!(vv2.value.0.is_some()); @@ -877,13 +884,15 @@ mod tests { // Check node for addr1 let key1 = StorageTrieKey::new(addr1, storage_path1.into()); - let vv1 = cur.seek_by_key_subkey(key1, BLOCK.block.number).expect("seek").expect("exists"); + let vv1 = + cur.seek_by_key_subkey(key1, BLOCK.block.number).expect("seek").expect("exists"); assert_eq!(vv1.block_number, BLOCK.block.number); assert!(vv1.value.0.is_some()); // Check node for addr2 let key2 = StorageTrieKey::new(addr2, storage_path2.into()); - let vv2 = cur.seek_by_key_subkey(key2, BLOCK.block.number).expect("seek").expect("exists"); + let vv2 = + cur.seek_by_key_subkey(key2, BLOCK.block.number).expect("seek").expect("exists"); assert_eq!(vv2.block_number, BLOCK.block.number); assert!(vv2.value.0.is_some()); } @@ -894,12 +903,14 @@ mod tests { let mut cur = tx.new_cursor::().expect("cursor"); // Check account1 (exists) - let vv1 = cur.seek_by_key_subkey(addr1, BLOCK.block.number).expect("seek").expect("exists"); + let vv1 = + cur.seek_by_key_subkey(addr1, BLOCK.block.number).expect("seek").expect("exists"); assert_eq!(vv1.block_number, BLOCK.block.number); assert_eq!(vv1.value.0, Some(acc1)); // Check account2 (deletion) - let vv2 = cur.seek_by_key_subkey(addr2, BLOCK.block.number).expect("seek").expect("exists"); + let vv2 = + cur.seek_by_key_subkey(addr2, BLOCK.block.number).expect("seek").expect("exists"); assert_eq!(vv2.block_number, BLOCK.block.number); assert!(vv2.value.0.is_none(), "Expected account deletion"); } @@ -911,14 +922,16 @@ mod tests { // Check storage for addr1 let key1 = HashedStorageKey::new(addr1, slot1); - let vv1 = cur.seek_by_key_subkey(key1, BLOCK.block.number).expect("seek").expect("exists"); + let vv1 = + cur.seek_by_key_subkey(key1, BLOCK.block.number).expect("seek").expect("exists"); assert_eq!(vv1.block_number, BLOCK.block.number); let inner1 = vv1.value.0.as_ref().expect("Some(StorageValue)"); assert_eq!(inner1.0, val1); // Check storage for addr2 let key2 = HashedStorageKey::new(addr2, slot2); - let vv2 = cur.seek_by_key_subkey(key2, BLOCK.block.number).expect("seek").expect("exists"); + let vv2 = + cur.seek_by_key_subkey(key2, BLOCK.block.number).expect("seek").expect("exists"); assert_eq!(vv2.block_number, BLOCK.block.number); let inner2 = vv2.value.0.as_ref().expect("Some(StorageValue)"); assert_eq!(inner2.0, val2); @@ -944,11 +957,15 @@ mod tests { // set latest to some hash H1 let existing_block = BlockWithParent::new(B256::random(), NumHash::new(1, B256::random())); - store.set_earliest_block_number(existing_block.block.number, existing_block.block.hash).await.expect("set"); + store + .set_earliest_block_number(existing_block.block.number, existing_block.block.hash) + .await + .expect("set"); // incoming block whose parent != existing latest let bad_parent = B256::from([0xFF; 32]); - let bad_block: BlockWithParent = BlockWithParent::new(bad_parent, NumHash::new(2, B256::ZERO)); + let bad_block: BlockWithParent = + BlockWithParent::new(bad_parent, NumHash::new(2, B256::ZERO)); let diff = BlockStateDiff::default(); let res = store.store_trie_updates(bad_block, diff).await; @@ -981,8 +998,10 @@ mod tests { // verify we can retrieve entries for both block numbers let tx = store.env.tx().expect("tx"); let mut cur = tx.new_cursor::().expect("cursor"); - let v_a = cur.seek_by_key_subkey(addr, block_a.block.number).expect("seek").expect("exists"); - let v_b = cur.seek_by_key_subkey(addr, block_b.block.number).expect("seek").expect("exists"); + let v_a = + cur.seek_by_key_subkey(addr, block_a.block.number).expect("seek").expect("exists"); + let v_b = + cur.seek_by_key_subkey(addr, block_b.block.number).expect("seek").expect("exists"); assert_eq!(v_a.block_number, block_a.block.number); assert_eq!(v_b.block_number, block_b.block.number); } @@ -992,7 +1011,8 @@ mod tests { let dir = TempDir::new().unwrap(); let store = MdbxProofsStorage::new(dir.path()).expect("env"); - const BLOCK: BlockWithParent = BlockWithParent::new(B256::ZERO, NumHash::new(42, B256::ZERO)); + const BLOCK: BlockWithParent = + BlockWithParent::new(B256::ZERO, NumHash::new(42, B256::ZERO)); // Create BlockStateDiff with empty collections let block_state_diff = BlockStateDiff::default(); diff --git a/crates/optimism/trie/src/in_memory.rs b/crates/optimism/trie/src/in_memory.rs index 2261e6dcdab..e97241bd360 100644 --- a/crates/optimism/trie/src/in_memory.rs +++ b/crates/optimism/trie/src/in_memory.rs @@ -629,8 +629,8 @@ impl OpProofsStore for InMemoryProofsStorage { #[cfg(test)] mod tests { use super::*; - use alloy_primitives::U256; use alloy_eips::NumHash; + use alloy_primitives::U256; use reth_primitives_traits::Account; #[tokio::test] @@ -664,7 +664,8 @@ mod tests { let block_state_diff = BlockStateDiff { trie_updates: trie_updates.clone(), post_state: post_state.clone() }; - const BLOCK: BlockWithParent = BlockWithParent::new(B256::ZERO, NumHash::new(5, B256::ZERO)); + const BLOCK: BlockWithParent = + BlockWithParent::new(B256::ZERO, NumHash::new(5, B256::ZERO)); storage.store_trie_updates(BLOCK, block_state_diff).await?; let retrieved_diff = storage.fetch_trie_updates(BLOCK.block.number).await?; diff --git a/crates/optimism/trie/src/live.rs b/crates/optimism/trie/src/live.rs index 026bf3b4642..1329f2bd203 100644 --- a/crates/optimism/trie/src/live.rs +++ b/crates/optimism/trie/src/live.rs @@ -5,7 +5,7 @@ use crate::{ provider::OpProofsStateProviderRef, OpProofsStorage, }; -use alloy_eips::{NumHash, eip1898::BlockWithParent}; +use alloy_eips::{eip1898::BlockWithParent, NumHash}; use derive_more::Constructor; use reth_evm::{execute::Executor, ConfigureEvm}; use reth_primitives_traits::{AlloyBlockHeader, BlockTy, RecoveredBlock}; @@ -49,7 +49,6 @@ where return Err(OpProofsStorageError::NoBlocksFound.into()); }; - let fetch_block_duration = start.elapsed(); let parent_block_number = block.number() - 1; @@ -66,10 +65,8 @@ where .into()); } - let block_ref = BlockWithParent::new( - block.parent_hash(), - NumHash::new(block.number(), block.hash()), - ); + let block_ref = + BlockWithParent::new(block.parent_hash(), NumHash::new(block.number(), block.hash())); // TODO: should we check block hash here? @@ -115,9 +112,14 @@ where let execute_and_store_total_duration = start.elapsed(); info!( - block_number = block.number(), ?execute_and_store_total_duration, ?fetch_block_duration, - ?init_provider_duration, ?execute_block_duration, ?calculate_state_root_duration, - ?write_trie_updates_duration, "Stored trie updates", + block_number = block.number(), + ?execute_and_store_total_duration, + ?fetch_block_duration, + ?init_provider_duration, + ?execute_block_duration, + ?calculate_state_root_duration, + ?write_trie_updates_duration, + "Stored trie updates", ); Ok(()) diff --git a/crates/optimism/trie/src/metrics.rs b/crates/optimism/trie/src/metrics.rs index 962a2da34ac..8609640f939 100644 --- a/crates/optimism/trie/src/metrics.rs +++ b/crates/optimism/trie/src/metrics.rs @@ -4,8 +4,8 @@ use crate::{ cursor, BlockStateDiff, OpProofsHashedCursorRO, OpProofsStorageResult, OpProofsStore, OpProofsTrieCursorRO, }; -use alloy_primitives::{map::HashMap, B256, U256}; use alloy_eips::eip1898::BlockWithParent; +use alloy_primitives::{map::HashMap, B256, U256}; use derive_more::Constructor; use metrics::{Counter, Histogram}; use reth_metrics::Metrics; diff --git a/crates/optimism/trie/tests/lib.rs b/crates/optimism/trie/tests/lib.rs index 7d02249830e..d56605a6bb8 100644 --- a/crates/optimism/trie/tests/lib.rs +++ b/crates/optimism/trie/tests/lib.rs @@ -1,7 +1,7 @@ //! Common test suite for [`OpProofsStore`] implementations. +use alloy_eips::{eip1898::BlockWithParent, NumHash}; use alloy_primitives::{map::HashMap, B256, U256}; -use alloy_eips::{NumHash, eip1898::BlockWithParent}; use reth_optimism_trie::{ BlockStateDiff, InMemoryProofsStorage, OpProofsHashedCursorRO, OpProofsStorageError, OpProofsStore, OpProofsTrieCursorRO, @@ -91,10 +91,7 @@ async fn test_earliest_block_operations( async fn test_trie_updates_operations( storage: S, ) -> Result<(), OpProofsStorageError> { - let block_ref = BlockWithParent::new( - B256::ZERO, - NumHash::new(50, B256::repeat_byte(0x96)), - ); + let block_ref = BlockWithParent::new(B256::ZERO, NumHash::new(50, B256::repeat_byte(0x96))); let trie_updates = TrieUpdates::default(); let post_state = HashedPostState::default(); let block_state_diff = @@ -1119,10 +1116,7 @@ async fn test_store_trie_updates_with_wiped_storage( use reth_trie::HashedStorage; let hashed_address = B256::repeat_byte(0x01); - let block_ref = BlockWithParent::new( - B256::ZERO, - NumHash::new(100, B256::repeat_byte(0x96)), - ); + let block_ref = BlockWithParent::new(B256::ZERO, NumHash::new(100, B256::repeat_byte(0x96))); // First, store some storage values at block 50 let storage_slots = vec![ @@ -1207,10 +1201,7 @@ async fn test_store_trie_updates_comprehensive( ) -> Result<(), OpProofsStorageError> { use reth_trie::{updates::StorageTrieUpdates, HashedStorage}; - let block_ref = BlockWithParent::new( - B256::ZERO, - NumHash::new(100, B256::repeat_byte(0x96)), - ); + let block_ref = BlockWithParent::new(B256::ZERO, NumHash::new(100, B256::repeat_byte(0x96))); // Create comprehensive trie updates with branches, leaves, and removals let mut trie_updates = TrieUpdates::default(); @@ -1290,7 +1281,8 @@ async fn test_store_trie_updates_comprehensive( assert!(removed_result.is_none(), "Removed account node should not be found"); // ========== Verify Storage Branch Nodes ========== - let mut storage_trie_cursor = storage.storage_trie_cursor(hashed_address, block_ref.block.number + 10)?; + let mut storage_trie_cursor = + storage.storage_trie_cursor(hashed_address, block_ref.block.number + 10)?; let storage_result1 = storage_trie_cursor.seek_exact(storage_path1)?; assert!(storage_result1.is_some(), "Storage branch node 1 should be found"); @@ -1323,7 +1315,8 @@ async fn test_store_trie_updates_comprehensive( ); // ========== Verify Storage Leaves ========== - let mut storage_cursor = storage.storage_hashed_cursor(storage_addr, block_ref.block.number + 10)?; + let mut storage_cursor = + storage.storage_hashed_cursor(storage_addr, block_ref.block.number + 10)?; let slot1_result = storage_cursor.seek(B256::repeat_byte(0x01))?; assert!(slot1_result.is_some(), "Storage slot 1 should be found"); @@ -1378,10 +1371,7 @@ async fn test_replace_updates_applies_all_updates( ) -> Result<(), OpProofsStorageError> { use reth_trie::{updates::StorageTrieUpdates, HashedStorage}; - let block_ref_50 = BlockWithParent::new( - B256::ZERO, - NumHash::new(50, B256::repeat_byte(0x96)), - ); + let block_ref_50 = BlockWithParent::new(B256::ZERO, NumHash::new(50, B256::repeat_byte(0x96))); // ========== Setup: Store initial state at blocks 50, 100, 101 ========== let initial_account_addr = B256::repeat_byte(0x10); @@ -1420,10 +1410,8 @@ async fn test_replace_updates_applies_all_updates( post_state: initial_post_state_100, }; - let block_ref_100 = BlockWithParent::new( - B256::ZERO, - NumHash::new(100, B256::repeat_byte(0x97)), - ); + let block_ref_100 = + BlockWithParent::new(B256::ZERO, NumHash::new(100, B256::repeat_byte(0x97))); storage.store_trie_updates(block_ref_100, initial_diff_100).await?; @@ -1441,10 +1429,8 @@ async fn test_replace_updates_applies_all_updates( trie_updates: initial_trie_updates_101, post_state: initial_post_state_101, }; - let block_ref_101 = BlockWithParent::new( - B256::ZERO, - NumHash::new(101, B256::repeat_byte(0x98)), - ); + let block_ref_101 = + BlockWithParent::new(B256::ZERO, NumHash::new(101, B256::repeat_byte(0x98))); storage.store_trie_updates(block_ref_101, initial_diff_101).await?; // ========== Verify initial state exists ========== @@ -1652,10 +1638,7 @@ async fn test_pure_deletions_stored_correctly( post_state: HashedPostState::default(), }; - let block_ref_50 = BlockWithParent::new( - B256::ZERO, - NumHash::new(50, B256::repeat_byte(0x96)), - ); + let block_ref_50 = BlockWithParent::new(B256::ZERO, NumHash::new(50, B256::repeat_byte(0x96))); storage.store_trie_updates(block_ref_50, initial_diff).await?; @@ -1696,10 +1679,8 @@ async fn test_pure_deletions_stored_correctly( post_state: HashedPostState::default(), }; - let block_ref_100 = BlockWithParent::new( - B256::ZERO, - NumHash::new(100, B256::repeat_byte(0x97)), - ); + let block_ref_100 = + BlockWithParent::new(B256::ZERO, NumHash::new(100, B256::repeat_byte(0x97))); storage.store_trie_updates(block_ref_100, deletion_diff).await?; @@ -1786,10 +1767,7 @@ async fn test_updates_take_precedence_over_removals( post_state: HashedPostState::default(), }; - let block_ref_50 = BlockWithParent::new( - B256::ZERO, - NumHash::new(50, B256::repeat_byte(0x96)), - ); + let block_ref_50 = BlockWithParent::new(B256::ZERO, NumHash::new(50, B256::repeat_byte(0x96))); storage.store_trie_updates(block_ref_50, initial_diff).await?; @@ -1830,10 +1808,8 @@ async fn test_updates_take_precedence_over_removals( post_state: HashedPostState::default(), }; - let block_ref_100 = BlockWithParent::new( - B256::ZERO, - NumHash::new(100, B256::repeat_byte(0x97)), - ); + let block_ref_100 = + BlockWithParent::new(B256::ZERO, NumHash::new(100, B256::repeat_byte(0x97))); storage.store_trie_updates(block_ref_100, conflicting_diff).await?; From 8846dee7c0ece150e62df01475da50b3ede0d661 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Fri, 24 Oct 2025 11:24:33 +0530 Subject: [PATCH 06/12] bugfix --- crates/optimism/trie/src/db/store.rs | 31 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/crates/optimism/trie/src/db/store.rs b/crates/optimism/trie/src/db/store.rs index fb14a6ecc1f..81aba13a180 100644 --- a/crates/optimism/trie/src/db/store.rs +++ b/crates/optimism/trie/src/db/store.rs @@ -261,22 +261,22 @@ impl OpProofsStore for MdbxProofsStorage { .sorted_by_key(|(hashed_address, _)| *hashed_address) .collect::>(); - self.env.update(|tx| { - // check latest stored block is the parent of incoming block - let mut proof_window_cursor = tx.new_cursor::()?; - let latest_hash = proof_window_cursor - .seek_exact(ProofWindowKey::LatestBlock)? - .map(|(_, v)| *v.hash()) - .unwrap_or(B256::ZERO); - if latest_hash != block_ref.parent { - return Err(OpProofsStorageError::OutOfOrder( - block_number, - block_ref.parent, - latest_hash, - ) - .into()); - } + // check latest stored block is the parent of incoming block + // todo: move this check inside the update transaction + let latest_hash = self + .get_latest_block_number() + .await? + .map(|(_, hash)| hash) + .unwrap_or(B256::ZERO); + if latest_hash != block_ref.parent { + return Err(OpProofsStorageError::OutOfOrder( + block_number, + block_ref.parent, + latest_hash, + )); + } + self.env.update(|tx| { let mut account_trie_cursor = tx.new_cursor::()?; for (path, node) in sorted_account_nodes { let vv = VersionedValue { block_number, value: MaybeDeleted(node) }; @@ -314,6 +314,7 @@ impl OpProofsStore for MdbxProofsStorage { } // update proof window latest block + let mut proof_window_cursor = tx.new_cursor::()?; proof_window_cursor.append( ProofWindowKey::LatestBlock, &BlockNumberHash::new(block_number, block_ref.block.hash), From f2fe8cb9c976f71b48f7597243014e69ac6d590d Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Fri, 24 Oct 2025 11:25:32 +0530 Subject: [PATCH 07/12] lintfix --- crates/optimism/trie/src/db/store.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/optimism/trie/src/db/store.rs b/crates/optimism/trie/src/db/store.rs index 81aba13a180..f56fe0b0a8e 100644 --- a/crates/optimism/trie/src/db/store.rs +++ b/crates/optimism/trie/src/db/store.rs @@ -263,11 +263,8 @@ impl OpProofsStore for MdbxProofsStorage { // check latest stored block is the parent of incoming block // todo: move this check inside the update transaction - let latest_hash = self - .get_latest_block_number() - .await? - .map(|(_, hash)| hash) - .unwrap_or(B256::ZERO); + let latest_hash = + self.get_latest_block_number().await?.map(|(_, hash)| hash).unwrap_or(B256::ZERO); if latest_hash != block_ref.parent { return Err(OpProofsStorageError::OutOfOrder( block_number, From 8498c424a166470e5fbddad2c2f9deb9f8ceb19c Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Fri, 24 Oct 2025 12:17:33 +0530 Subject: [PATCH 08/12] review fixes --- crates/optimism/trie/src/db/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/optimism/trie/src/db/store.rs b/crates/optimism/trie/src/db/store.rs index f56fe0b0a8e..8775525257b 100644 --- a/crates/optimism/trie/src/db/store.rs +++ b/crates/optimism/trie/src/db/store.rs @@ -967,7 +967,7 @@ mod tests { let diff = BlockStateDiff::default(); let res = store.store_trie_updates(bad_block, diff).await; - assert!(matches!(res, Err(OpProofsStorageError::OutOfOrder(..) | _))); + assert!(matches!(res, Err(OpProofsStorageError::OutOfOrder(..)))); // verify nothing written: proof window still unchanged let latest = store.get_latest_block_number().await.expect("get latest"); assert_eq!(latest.unwrap().1, existing_block.block.hash); From ba0f0db05e12d9dcf292dec112e8141d7d569a76 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Fri, 24 Oct 2025 13:04:38 +0530 Subject: [PATCH 09/12] feat: live collector integration --- Cargo.lock | 2 + crates/optimism/bin/src/main.rs | 11 ++-- crates/optimism/exex/Cargo.toml | 4 ++ crates/optimism/exex/src/lib.rs | 95 ++++++++++++++++++++++++++++---- crates/optimism/trie/src/live.rs | 2 +- 5 files changed, 99 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4983ebc9d34..7d13fff40b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9337,6 +9337,7 @@ dependencies = [ name = "reth-optimism-exex" version = "1.8.2" dependencies = [ + "alloy-consensus", "derive_more", "eyre", "futures", @@ -9349,6 +9350,7 @@ dependencies = [ "reth-optimism-trie", "reth-provider", "tokio", + "tracing", ] [[package]] diff --git a/crates/optimism/bin/src/main.rs b/crates/optimism/bin/src/main.rs index 030ff130ea5..9ca94915e7c 100644 --- a/crates/optimism/bin/src/main.rs +++ b/crates/optimism/bin/src/main.rs @@ -77,20 +77,23 @@ async fn launch_node_with_storage( where S: OpProofsStore + Clone + 'static, { - let storage_clone = storage.clone(); + let storage_exec = storage.clone(); + let storage_rpc = storage.clone(); let proofs_history_enabled = args.proofs_history; let handle = builder .node(OpNode::new(args.rollup_args)) .install_exex_if(proofs_history_enabled, "proofs-history", async move |exex_context| { - Ok(OpProofsExEx::new(exex_context, storage, args.proofs_history_window).run().boxed()) + Ok(OpProofsExEx::new(exex_context, storage_exec, args.proofs_history_window) + .run() + .boxed()) }) .extend_rpc_modules(move |ctx| { if proofs_history_enabled { - let api_ext = EthApiExt::new(ctx.registry.eth_api().clone(), storage_clone.clone()); + let api_ext = EthApiExt::new(ctx.registry.eth_api().clone(), storage_rpc.clone()); let debug_ext = DebugApiExt::new( ctx.node().provider().clone(), ctx.registry.eth_api().clone(), - storage_clone, + storage_rpc, Box::new(ctx.node().task_executor().clone()), ctx.node().evm_config().clone(), ); diff --git a/crates/optimism/exex/Cargo.toml b/crates/optimism/exex/Cargo.toml index 753932cfb2c..8ee85ffc20a 100644 --- a/crates/optimism/exex/Cargo.toml +++ b/crates/optimism/exex/Cargo.toml @@ -23,10 +23,14 @@ reth-chainspec.workspace = true # proofs exex handles `TrieUpdates` in notifications reth-optimism-trie = { workspace = true, features = ["serde-bincode-compat"] } +# alloy +alloy-consensus.workspace = true + # misc eyre.workspace = true futures-util.workspace = true derive_more.workspace = true +tracing.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["test-util", "rt-multi-thread", "macros"] } diff --git a/crates/optimism/exex/src/lib.rs b/crates/optimism/exex/src/lib.rs index 2ba70468e00..a43699d9198 100644 --- a/crates/optimism/exex/src/lib.rs +++ b/crates/optimism/exex/src/lib.rs @@ -8,14 +8,16 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(not(test), warn(unused_crate_dependencies))] +use alloy_consensus::BlockHeader; use derive_more::Constructor; use futures_util::TryStreamExt; use reth_chainspec::ChainInfo; -use reth_exex::{ExExContext, ExExEvent}; +use reth_exex::{ExExContext, ExExEvent, ExExNotification}; use reth_node_api::{FullNodeComponents, NodePrimitives}; use reth_node_types::NodeTypes; -use reth_optimism_trie::{BackfillJob, OpProofsStore}; +use reth_optimism_trie::{live::LiveTrieCollector, BackfillJob, OpProofsStorage, OpProofsStore}; use reth_provider::{BlockNumReader, DBProvider, DatabaseProviderFactory}; +use tracing::{debug, error}; /// OP Proofs ExEx - processes blocks and tracks state changes within fault proof window. /// @@ -23,27 +25,26 @@ use reth_provider::{BlockNumReader, DBProvider, DatabaseProviderFactory}; /// saving the current state, new blocks as they're added, and serving proof RPCs /// based on the saved data. #[derive(Debug, Constructor)] -pub struct OpProofsExEx +pub struct OpProofsExEx where Node: FullNodeComponents, - S: OpProofsStore + Clone, { /// The ExEx context containing the node related utilities e.g. provider, notifications, /// events. ctx: ExExContext, /// The type of storage DB. - storage: S, + storage: OpProofsStorage, /// The window to span blocks for proofs history. Value is the number of blocks, received as /// cli arg. #[expect(dead_code)] proofs_history_window: u64, } -impl OpProofsExEx +impl OpProofsExEx where Node: FullNodeComponents>, Primitives: NodePrimitives, - S: OpProofsStore + Clone, + Storage: OpProofsStore + Clone + 'static, { /// Main execution loop for the ExEx pub async fn run(mut self) -> eyre::Result<()> { @@ -53,10 +54,84 @@ where let ChainInfo { best_number, best_hash } = self.ctx.provider().chain_info()?; BackfillJob::new(self.storage.clone(), &db_tx).run(best_number, best_hash).await?; + let collector = LiveTrieCollector::new( + self.ctx.evm_config().clone(), + self.ctx.provider().clone(), + &self.storage, + ); + while let Some(notification) = self.ctx.notifications.try_next().await? { - // match ¬ification { - // _ => {} - // }; + match ¬ification { + ExExNotification::ChainCommitted { new } => { + debug!( + block_number = new.tip().number(), + block_hash = ?new.tip().hash(), + "ChainCommitted notification received", + ); + + // Get latest stored number (ignore stored hash for now) + let latest_stored_block_number = + match self.storage.get_latest_block_number().await? { + Some((n, _)) => n, + None => { + return Err(eyre::eyre!("No blocks stored in proofs storage")); + } + }; + + // If tip is not newer than what we have, nothing to do. + if new.tip().number() <= latest_stored_block_number { + debug!( + block_number = new.tip().number(), + latest_stored = latest_stored_block_number, + "Tip number is less than or equal to latest stored, skipping" + ); + continue; + } + + // Start from the next block after the latest stored one. + let start = latest_stored_block_number.saturating_add(1); + debug!( + start, + end = new.tip().number(), + "Applying updates for blocks in committed chain" + ); + for block_number in start..=new.tip().number() { + match new.blocks().get(&block_number) { + Some(block) => { + collector.execute_and_store_block_updates(block).await?; + } + None => { + error!( + block_number, + "Missing block in committed chain; stopping incremental application", + ); + return Err(eyre::eyre!( + "Missing block {} in committed chain", + block_number + )); + } + } + } + } + ExExNotification::ChainReorged { old, new } => { + debug!( + old_block_number = old.tip().number(), + old_block_hash = ?old.tip().hash(), + new_block_number = new.tip().number(), + new_block_hash = ?new.tip().hash(), + "ChainReorged notification received", + ); + // handle reorg logic here + } + ExExNotification::ChainReverted { old } => { + debug!( + old_block_number = old.tip().number(), + old_block_hash = ?old.tip().hash(), + "ChainReverted notification received", + ); + // handle revert logic here + } + }; if let Some(committed_chain) = notification.committed_chain() { self.ctx diff --git a/crates/optimism/trie/src/live.rs b/crates/optimism/trie/src/live.rs index 1329f2bd203..c2ac5fc3fb4 100644 --- a/crates/optimism/trie/src/live.rs +++ b/crates/optimism/trie/src/live.rs @@ -119,7 +119,7 @@ where ?execute_block_duration, ?calculate_state_root_duration, ?write_trie_updates_duration, - "Stored trie updates", + "Trie updates stored successfully", ); Ok(()) From 886f5770e0e703094440edb82cff0cabb4552252 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Mon, 27 Oct 2025 11:09:57 +0530 Subject: [PATCH 10/12] Update crates/optimism/exex/src/lib.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- crates/optimism/exex/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/optimism/exex/src/lib.rs b/crates/optimism/exex/src/lib.rs index 86255e66ed4..df8607fdb06 100644 --- a/crates/optimism/exex/src/lib.rs +++ b/crates/optimism/exex/src/lib.rs @@ -121,7 +121,7 @@ where new_block_hash = ?new.tip().hash(), "ChainReorged notification received", ); - unimplemented!(); + unimplemented!("Chain reorg handling not yet implemented in OpProofsExEx"); } ExExNotification::ChainReverted { old } => { debug!( From c7ac73cde1a0520ac382e7cb194a87257c2b6f31 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Mon, 27 Oct 2025 11:10:09 +0530 Subject: [PATCH 11/12] Update crates/optimism/exex/src/lib.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- crates/optimism/exex/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/optimism/exex/src/lib.rs b/crates/optimism/exex/src/lib.rs index df8607fdb06..4374ab31359 100644 --- a/crates/optimism/exex/src/lib.rs +++ b/crates/optimism/exex/src/lib.rs @@ -129,7 +129,7 @@ where old_block_hash = ?old.tip().hash(), "ChainReverted notification received", ); - unimplemented!(); + unimplemented!("Chain revert handling not yet implemented"); } }; From aafbfb6050383eaefdba7bd69cd0f47b2d9dd0ba Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Mon, 27 Oct 2025 18:19:29 +0530 Subject: [PATCH 12/12] Update crates/optimism/exex/src/lib.rs Co-authored-by: Emilia Hane --- crates/optimism/exex/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/optimism/exex/src/lib.rs b/crates/optimism/exex/src/lib.rs index 4374ab31359..98b9990c7e7 100644 --- a/crates/optimism/exex/src/lib.rs +++ b/crates/optimism/exex/src/lib.rs @@ -103,7 +103,7 @@ where None => { error!( block_number, - "Missing block in committed chain; stopping incremental application", + "Missing block in committed chain, stopping incremental application", ); return Err(eyre::eyre!( "Missing block {} in committed chain",