Skip to content

Commit fa5429a

Browse files
authored
fix(evm): query state_snapshot.storage in ForkDbStateSnapshot::storage_ref (#14007)
* fix(evm): query `state_snapshot.storage` in `ForkDbStateSnapshot::storage_ref` * test(evm): cover `ForkDbStateSnapshot::storage_ref` snapshot lookup
1 parent a70d4aa commit fa5429a

1 file changed

Lines changed: 38 additions & 15 deletions

File tree

crates/evm/core/src/fork/database.rs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,18 @@ pub struct ForkDbStateSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
212212
}
213213

214214
impl<N: Network, B: ForkBlockEnv> ForkDbStateSnapshot<N, B> {
215-
fn get_storage(&self, address: Address, index: U256) -> Option<U256> {
216-
self.local
217-
.cache
218-
.accounts
219-
.get(&address)
220-
.and_then(|account| account.storage.get(&index))
221-
.copied()
215+
/// Lookup storage in `state_snapshot`, then fall back to the backend (remote RPC).
216+
fn storage_from_snapshot_or_backend(
217+
&self,
218+
address: Address,
219+
index: U256,
220+
) -> Result<U256, DatabaseError> {
221+
// Check state_snapshot.storage first (data fetched by SharedBackend / disk cache).
222+
if let Some(val) = self.state_snapshot.storage.get(&address).and_then(|s| s.get(&index)) {
223+
return Ok(*val);
224+
}
225+
// Fall back to the underlying backend (SharedBackend → remote RPC).
226+
DatabaseRef::storage_ref(&self.local, address, index)
222227
}
223228
}
224229

@@ -250,15 +255,9 @@ impl<N: Network, B: ForkBlockEnv> DatabaseRef for ForkDbStateSnapshot<N, B> {
250255
match self.local.cache.accounts.get(&address) {
251256
Some(account) => match account.storage.get(&index) {
252257
Some(entry) => Ok(*entry),
253-
None => match self.get_storage(address, index) {
254-
None => DatabaseRef::storage_ref(&self.local, address, index),
255-
Some(storage) => Ok(storage),
256-
},
257-
},
258-
None => match self.get_storage(address, index) {
259-
None => DatabaseRef::storage_ref(&self.local, address, index),
260-
Some(storage) => Ok(storage),
258+
None => self.storage_from_snapshot_or_backend(address, index),
261259
},
260+
None => self.storage_from_snapshot_or_backend(address, index),
262261
}
263262
}
264263

@@ -303,4 +302,28 @@ mod tests {
303302
assert!(loaded.is_some());
304303
assert_eq!(loaded.unwrap(), info);
305304
}
305+
306+
/// Verifies that `ForkDbStateSnapshot::storage_ref` reads from `state_snapshot.storage`
307+
/// when the slot is missing from `local.cache.accounts`. Without this lookup the call
308+
/// would fall through to the backend and return the unrelated remote value.
309+
#[tokio::test(flavor = "multi_thread")]
310+
async fn fork_db_state_snapshot_reads_storage_from_snapshot() {
311+
let rpc = foundry_test_utils::rpc::next_http_rpc_endpoint();
312+
let provider = get_http_provider(rpc.clone());
313+
let meta = BlockchainDbMeta::new(BlockEnv::default(), rpc);
314+
let db = BlockchainDb::new(meta, None);
315+
let backend = SharedBackend::spawn_backend(Arc::new(provider), db, None).await;
316+
317+
let address = Address::random();
318+
let slot = U256::from(42u64);
319+
let expected = U256::from(0xdeadbeefu64);
320+
321+
let mut state_snapshot = StateSnapshot::default();
322+
state_snapshot.storage.entry(address).or_default().insert(slot, expected);
323+
324+
let snapshot = ForkDbStateSnapshot { local: CacheDB::new(backend), state_snapshot };
325+
326+
let got = DatabaseRef::storage_ref(&snapshot, address, slot).unwrap();
327+
assert_eq!(got, expected);
328+
}
306329
}

0 commit comments

Comments
 (0)