-
Notifications
You must be signed in to change notification settings - Fork 29
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
perf(client, host): redundant state caching #59
Open
nhtyy
wants to merge
7
commits into
succinctlabs:main
Choose a base branch
from
nhtyy:n/rm-unused-cachedb
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
9606341
chore/perf: RefWrapper on witness, impl Database for rpc
nhtyy d26813a
nit: take
nhtyy 84163e8
fix: lint
nhtyy 49e18ca
fix: comment
nhtyy 5612173
fix: provider doesnt need to be clone
nhtyy 4a395a5
Merge branch 'succinctlabs:main' into n/rm-unused-cachedb
nhtyy dce3341
grasp
nhtyy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
use std::{ | ||
cell::RefCell, | ||
collections::{BTreeMap, BTreeSet}, | ||
marker::PhantomData, | ||
}; | ||
|
@@ -11,23 +10,28 @@ use reth_primitives::{ | |
revm_primitives::{AccountInfo, Bytecode}, | ||
Address, B256, U256, | ||
}; | ||
use reth_revm::DatabaseRef; | ||
use reth_revm::Database; | ||
use reth_storage_errors::{db::DatabaseError, provider::ProviderError}; | ||
use revm_primitives::HashMap; | ||
|
||
/// A database that fetches data from a [Provider] over a [Transport]. | ||
/// | ||
/// This type is very similar to a CacheDb as exposed by revm, except we have some extra fields | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can say that the caching part is similar, but the RpcDB actually uses an RPC to fetch the state, which is pretty different from the CacheDB which is just a wrapper. |
||
/// and the inner types are more conducive to the state we need to extract. | ||
#[derive(Debug, Clone)] | ||
pub struct RpcDb<T, P> { | ||
/// The provider which fetches data. | ||
pub provider: P, | ||
/// The block to fetch data from. | ||
pub block: BlockId, | ||
/// The cached accounts. | ||
pub accounts: RefCell<HashMap<Address, AccountInfo>>, | ||
pub accounts: HashMap<Address, AccountInfo>, | ||
/// The cached storage values. | ||
pub storage: RefCell<HashMap<Address, HashMap<U256, U256>>>, | ||
pub storage: HashMap<Address, HashMap<U256, U256>>, | ||
/// The cached block hashes | ||
pub block_hash_by_number: HashMap<u64, B256>, | ||
/// The oldest block whose header/hash has been requested. | ||
pub oldest_ancestor: RefCell<u64>, | ||
pub oldest_ancestor: u64, | ||
/// A phantom type to make the struct generic over the transport. | ||
pub _phantom: PhantomData<T>, | ||
} | ||
|
@@ -43,23 +47,35 @@ pub enum RpcDbError { | |
PreimageNotFound, | ||
} | ||
|
||
impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> { | ||
impl<T, P> RpcDb<T, P> | ||
where | ||
T: Transport + Clone, | ||
P: Provider<T, AnyNetwork>, | ||
{ | ||
/// Create a new [`RpcDb`]. | ||
pub fn new(provider: P, block: u64) -> Self { | ||
RpcDb { | ||
provider, | ||
block: block.into(), | ||
accounts: RefCell::new(HashMap::new()), | ||
storage: RefCell::new(HashMap::new()), | ||
oldest_ancestor: RefCell::new(block), | ||
accounts: HashMap::new(), | ||
storage: HashMap::new(), | ||
block_hash_by_number: HashMap::new(), | ||
oldest_ancestor: block, | ||
_phantom: PhantomData, | ||
} | ||
} | ||
|
||
/// Fetch the [AccountInfo] for an [Address]. | ||
pub async fn fetch_account_info(&self, address: Address) -> Result<AccountInfo, RpcDbError> { | ||
pub async fn fetch_account_info( | ||
&mut self, | ||
address: Address, | ||
) -> Result<AccountInfo, RpcDbError> { | ||
tracing::info!("fetching account info for address: {}", address); | ||
|
||
if let Some(account_info) = self.accounts.get(&address) { | ||
return Ok(account_info.clone()); | ||
} | ||
|
||
// Fetch the proof for the account. | ||
let proof = self | ||
.provider | ||
|
@@ -86,19 +102,25 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> { | |
}; | ||
|
||
// Record the account info to the state. | ||
self.accounts.borrow_mut().insert(address, account_info.clone()); | ||
self.accounts.insert(address, account_info.clone()); | ||
|
||
Ok(account_info) | ||
} | ||
|
||
/// Fetch the storage value at an [Address] and [U256] index. | ||
pub async fn fetch_storage_at( | ||
&self, | ||
&mut self, | ||
address: Address, | ||
index: U256, | ||
) -> Result<U256, RpcDbError> { | ||
tracing::info!("fetching storage value at address: {}, index: {}", address, index); | ||
|
||
if let Some(account) = self.storage.get(&address) { | ||
if let Some(value) = account.get(&index) { | ||
return Ok(*value); | ||
} | ||
} | ||
|
||
// Fetch the storage value. | ||
let value = self | ||
.provider | ||
|
@@ -108,17 +130,20 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> { | |
.map_err(|e| RpcDbError::RpcError(e.to_string()))?; | ||
|
||
// Record the storage value to the state. | ||
let mut storage_values = self.storage.borrow_mut(); | ||
let entry = storage_values.entry(address).or_default(); | ||
let entry = self.storage.entry(address).or_default(); | ||
entry.insert(index, value); | ||
|
||
Ok(value) | ||
} | ||
|
||
/// Fetch the block hash for a block number. | ||
pub async fn fetch_block_hash(&self, number: u64) -> Result<B256, RpcDbError> { | ||
pub async fn fetch_block_hash(&mut self, number: u64) -> Result<B256, RpcDbError> { | ||
tracing::info!("fetching block hash for block number: {}", number); | ||
|
||
if let Some(hash) = self.block_hash_by_number.get(&number) { | ||
return Ok(*hash); | ||
} | ||
|
||
// Fetch the block. | ||
let block = self | ||
.provider | ||
|
@@ -130,22 +155,20 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> { | |
let block = block.ok_or(RpcDbError::BlockNotFound)?; | ||
let hash = block.header.hash; | ||
|
||
let mut oldest_ancestor = self.oldest_ancestor.borrow_mut(); | ||
*oldest_ancestor = number.min(*oldest_ancestor); | ||
self.block_hash_by_number.insert(number, hash); | ||
self.oldest_ancestor = number.min(self.oldest_ancestor); | ||
|
||
Ok(hash) | ||
} | ||
|
||
/// Gets all the state keys used. The client uses this to read the actual state data from tries. | ||
pub fn get_state_requests(&self) -> HashMap<Address, Vec<U256>> { | ||
let accounts = self.accounts.borrow(); | ||
let storage = self.storage.borrow(); | ||
|
||
accounts | ||
self.accounts | ||
.keys() | ||
.chain(storage.keys()) | ||
.chain(self.storage.keys()) | ||
.map(|&address| { | ||
let storage_keys_for_address: BTreeSet<U256> = storage | ||
let storage_keys_for_address: BTreeSet<U256> = self | ||
.storage | ||
.get(&address) | ||
.map(|storage_map| storage_map.keys().cloned().collect()) | ||
.unwrap_or_default(); | ||
|
@@ -157,9 +180,7 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> { | |
|
||
/// Gets all account bytecodes. | ||
pub fn get_bytecodes(&self) -> Vec<Bytecode> { | ||
let accounts = self.accounts.borrow(); | ||
|
||
accounts | ||
self.accounts | ||
.values() | ||
.flat_map(|account| account.code.clone()) | ||
.map(|code| (code.hash_slow(), code)) | ||
|
@@ -169,10 +190,14 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> { | |
} | ||
} | ||
|
||
impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> DatabaseRef for RpcDb<T, P> { | ||
impl<T, P> Database for RpcDb<T, P> | ||
where | ||
T: Transport + Clone, | ||
P: Provider<T, AnyNetwork>, | ||
{ | ||
type Error = ProviderError; | ||
|
||
fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> { | ||
fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> { | ||
let handle = tokio::runtime::Handle::try_current().map_err(|_| { | ||
ProviderError::Database(DatabaseError::Other("no tokio runtime found".to_string())) | ||
})?; | ||
|
@@ -183,11 +208,11 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> DatabaseRef for R | |
Ok(Some(account_info)) | ||
} | ||
|
||
fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, Self::Error> { | ||
fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, Self::Error> { | ||
unimplemented!() | ||
} | ||
|
||
fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> { | ||
fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> { | ||
let handle = tokio::runtime::Handle::try_current().map_err(|_| { | ||
ProviderError::Database(DatabaseError::Other("no tokio runtime found".to_string())) | ||
})?; | ||
|
@@ -198,7 +223,7 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> DatabaseRef for R | |
Ok(value) | ||
} | ||
|
||
fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> { | ||
fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> { | ||
let handle = tokio::runtime::Handle::try_current().map_err(|_| { | ||
ProviderError::Database(DatabaseError::Other("no tokio runtime found".to_string())) | ||
})?; | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an unused import in this file according to the linter