Skip to content

fix: cache analyzed code #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 6, 2024
Merged
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
42 changes: 27 additions & 15 deletions crates/core/src/database.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::DatabaseError;
use revm::interpreter::analysis::to_analysed;
use revm::{
db::DatabaseRef,
primitives::{AccountInfo, Address, Bytecode, B256, U256},
Expand All @@ -23,6 +24,8 @@ type Result<T, E = DatabaseError> = std::result::Result<T, E>;
pub struct EvmDatabase<'a, CodeDb, ZkDb> {
/// Map of code hash to bytecode.
pub(crate) code_db: &'a mut CodeDb,
/// Cache of analyzed code
analyzed_code_cache: RefCell<HashMap<B256, Option<Bytecode>>>,
/// Storage root cache, avoid re-query account when storage root is needed
storage_root_caches: RefCell<HashMap<Address, ZkHash>>,
/// Storage trie cache, avoid re-creating trie for the same account.
Expand Down Expand Up @@ -56,6 +59,7 @@ impl<'a, CodeDb: KVDatabase, ZkDb: KVDatabase + 'static> EvmDatabase<'a, CodeDb,

Ok(EvmDatabase {
code_db,
analyzed_code_cache: Default::default(),
storage_root_caches: Default::default(),
storage_trie_caches: Default::default(),
committed_zktrie_root,
Expand Down Expand Up @@ -121,6 +125,21 @@ impl<'a, CodeDb: KVDatabase, ZkDb: KVDatabase + 'static> EvmDatabase<'a, CodeDb,
cycle_tracker_end!("insert CodeDB");
Ok(())
}

fn load_code(&self, hash: B256) -> Result<Option<Bytecode>> {
let mut code_cache = self.analyzed_code_cache.borrow_mut();
if let Some(code) = code_cache.get(&hash) {
Ok(code.clone())
} else {
let code = self
.code_db
.get(&hash)
.map_err(DatabaseError::code_db)?
.map(|v| to_analysed(Bytecode::new_legacy(v.into_bytes().into())));
code_cache.insert(hash, code.clone());
Ok(code)
}
}
}

impl<CodeDb: KVDatabase, ZkDb: KVDatabase + 'static> DatabaseRef for EvmDatabase<'_, CodeDb, ZkDb> {
Expand All @@ -142,11 +161,8 @@ impl<CodeDb: KVDatabase, ZkDb: KVDatabase + 'static> DatabaseRef for EvmDatabase
.insert(address, account.storage_root);

let mut info = AccountInfo::from(account);
info.code = self
.code_db
.get(&account.code_hash)
.map_err(DatabaseError::code_db)?
.map(|v| Bytecode::new_legacy(v.into_bytes().into()));
info.code = self.load_code(account.code_hash)?;

if let Some(ref code) = info.code {
debug_assert_eq!(
info.code_hash,
Expand All @@ -170,16 +186,12 @@ impl<CodeDb: KVDatabase, ZkDb: KVDatabase + 'static> DatabaseRef for EvmDatabase
// then the upcoming trace contains code (meaning the code is used in this new block),
// we can't directly update the CacheDB, so we offer the code by hash here.
// However, if the code still cannot be found, this is an error.
self.code_db
.get(&hash)
.map_err(DatabaseError::code_db)?
.map(|v| Bytecode::new_legacy(v.into_bytes().into()))
.ok_or_else(|| {
unreachable!(
"Code is either loaded or not needed (like EXTCODESIZE), code hash: {:?}",
hash
);
})
self.load_code(hash)?.ok_or_else(|| {
unreachable!(
"Code is either loaded or not needed (like EXTCODESIZE), code hash: {:?}",
hash
);
})
}

/// Get storage value of address at index.
Expand Down