Skip to content

Commit a0d4b2c

Browse files
Revert "remove caching provider, handle cache directly in RPC provider"
This reverts commit ebc5524.
1 parent ebc5524 commit a0d4b2c

File tree

8 files changed

+290
-127
lines changed

8 files changed

+290
-127
lines changed

core/src/execution/cache/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod provider;
12
mod state;
23

4+
pub use provider::CachingProvider;
35
pub(crate) use state::Cache;
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
use std::{collections::HashMap, sync::Arc};
2+
3+
use alloy::{
4+
eips::BlockId,
5+
network::{primitives::HeaderResponse, BlockResponse},
6+
primitives::{Address, B256},
7+
rpc::types::{EIP1186AccountProofResponse, Filter, Log},
8+
};
9+
use async_trait::async_trait;
10+
use eyre::{eyre, Result};
11+
12+
use helios_common::{
13+
execution_provider::{
14+
AccountProvider, BlockProvider, ExecutionHintProvider, ExecutionProvider, LogProvider,
15+
ReceiptProvider, TransactionProvider,
16+
},
17+
network_spec::NetworkSpec,
18+
types::Account,
19+
};
20+
21+
use super::Cache;
22+
23+
pub struct CachingProvider<P> {
24+
inner: P,
25+
cache: Arc<Cache>,
26+
}
27+
28+
impl<P> CachingProvider<P> {
29+
pub fn new(inner: P) -> Self {
30+
Self {
31+
inner,
32+
cache: Arc::new(Cache::new()),
33+
}
34+
}
35+
}
36+
37+
impl<N, P> ExecutionProvider<N> for CachingProvider<P>
38+
where
39+
N: NetworkSpec,
40+
P: ExecutionProvider<N>,
41+
{
42+
}
43+
44+
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
45+
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
46+
impl<N, P> AccountProvider<N> for CachingProvider<P>
47+
where
48+
N: NetworkSpec,
49+
P: ExecutionProvider<N>,
50+
{
51+
async fn get_account(
52+
&self,
53+
address: Address,
54+
slots: &[B256],
55+
with_code: bool,
56+
block_id: BlockId,
57+
) -> Result<Account> {
58+
let block_hash = match block_id {
59+
BlockId::Hash(hash) => {
60+
let hash: B256 = hash.into();
61+
hash
62+
}
63+
block_id => {
64+
let block = self
65+
.inner
66+
.get_block(block_id, false)
67+
.await?
68+
.ok_or(eyre!("block not found"))?;
69+
block.header().hash()
70+
}
71+
};
72+
73+
let slots_left_to_fetch = match self.cache.get_account(address, slots, block_hash) {
74+
Some((cached, missing_slots)) => {
75+
if missing_slots.is_empty() {
76+
if with_code && cached.code.is_none() {
77+
// All account data is cached, but code is missing - fetch code directly
78+
let expected = cached.account.code_hash;
79+
let acc_with_code = self
80+
.inner
81+
.get_account(address, &[], true, block_hash.into())
82+
.await?;
83+
let code = acc_with_code
84+
.code
85+
.ok_or_else(|| eyre!("provider did not return code"))?;
86+
if acc_with_code.account.code_hash != expected {
87+
return Err(eyre!("code_hash changed while fetching code"));
88+
}
89+
self.cache.insert_code(expected, code.clone());
90+
return Ok(Account {
91+
code: Some(code),
92+
..cached
93+
});
94+
}
95+
return Ok(cached);
96+
}
97+
missing_slots
98+
}
99+
None => slots.to_vec(),
100+
};
101+
102+
// Always fetch account proof without full code because it might be already in cache
103+
let account = self
104+
.inner
105+
.get_account(address, &slots_left_to_fetch, false, block_hash.into())
106+
.await?;
107+
108+
let response = EIP1186AccountProofResponse {
109+
address,
110+
balance: account.account.balance,
111+
code_hash: account.account.code_hash,
112+
nonce: account.account.nonce,
113+
storage_hash: account.account.storage_root,
114+
account_proof: account.account_proof.clone(),
115+
storage_proof: account.storage_proof.clone(),
116+
};
117+
118+
self.cache.insert(response, None, block_hash);
119+
120+
// Retrieve from cache again to get the full merged result
121+
let (mut full_account, missing_after) = self
122+
.cache
123+
.get_account(address, slots, block_hash)
124+
.ok_or(eyre!("Cache inconsistency after write"))?;
125+
126+
if !missing_after.is_empty() {
127+
return Err(eyre!("Failed to fetch all slots"));
128+
}
129+
130+
// If code is still missing, fetch via get_account with with_code=true
131+
if with_code && full_account.code.is_none() {
132+
let expected = full_account.account.code_hash;
133+
let acc_with_code = self
134+
.inner
135+
.get_account(address, &[], true, block_hash.into())
136+
.await?;
137+
let code = acc_with_code
138+
.code
139+
.ok_or_else(|| eyre!("provider did not return code"))?;
140+
if acc_with_code.account.code_hash != expected {
141+
return Err(eyre!("code_hash changed while fetching code"));
142+
}
143+
self.cache.insert_code(expected, code.clone());
144+
full_account.code = Some(code);
145+
}
146+
147+
Ok(full_account)
148+
}
149+
}
150+
151+
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
152+
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
153+
impl<N, P> BlockProvider<N> for CachingProvider<P>
154+
where
155+
N: NetworkSpec,
156+
P: ExecutionProvider<N>,
157+
{
158+
async fn push_block(&self, block: N::BlockResponse, block_id: BlockId) {
159+
self.inner.push_block(block, block_id).await
160+
}
161+
162+
async fn get_block(
163+
&self,
164+
block_id: BlockId,
165+
full_tx: bool,
166+
) -> Result<Option<N::BlockResponse>> {
167+
self.inner.get_block(block_id, full_tx).await
168+
}
169+
170+
async fn get_untrusted_block(
171+
&self,
172+
block_id: BlockId,
173+
full_tx: bool,
174+
) -> Result<Option<N::BlockResponse>> {
175+
self.inner.get_untrusted_block(block_id, full_tx).await
176+
}
177+
}
178+
179+
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
180+
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
181+
impl<N, P> TransactionProvider<N> for CachingProvider<P>
182+
where
183+
N: NetworkSpec,
184+
P: ExecutionProvider<N>,
185+
{
186+
async fn send_raw_transaction(&self, bytes: &[u8]) -> Result<B256> {
187+
self.inner.send_raw_transaction(bytes).await
188+
}
189+
190+
async fn get_transaction(&self, hash: B256) -> Result<Option<N::TransactionResponse>> {
191+
self.inner.get_transaction(hash).await
192+
}
193+
194+
async fn get_transaction_by_location(
195+
&self,
196+
block_id: BlockId,
197+
index: u64,
198+
) -> Result<Option<N::TransactionResponse>> {
199+
self.inner
200+
.get_transaction_by_location(block_id, index)
201+
.await
202+
}
203+
}
204+
205+
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
206+
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
207+
impl<N, P> ReceiptProvider<N> for CachingProvider<P>
208+
where
209+
N: NetworkSpec,
210+
P: ExecutionProvider<N>,
211+
{
212+
async fn get_receipt(&self, hash: B256) -> Result<Option<N::ReceiptResponse>> {
213+
self.inner.get_receipt(hash).await
214+
}
215+
216+
async fn get_block_receipts(&self, block: BlockId) -> Result<Option<Vec<N::ReceiptResponse>>> {
217+
self.inner.get_block_receipts(block).await
218+
}
219+
}
220+
221+
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
222+
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
223+
impl<N, P> LogProvider<N> for CachingProvider<P>
224+
where
225+
N: NetworkSpec,
226+
P: ExecutionProvider<N>,
227+
{
228+
async fn get_logs(&self, filter: &Filter) -> Result<Vec<Log>> {
229+
self.inner.get_logs(filter).await
230+
}
231+
}
232+
233+
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
234+
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
235+
impl<N, P> ExecutionHintProvider<N> for CachingProvider<P>
236+
where
237+
N: NetworkSpec,
238+
P: ExecutionProvider<N>,
239+
{
240+
async fn get_execution_hint(
241+
&self,
242+
call: &N::TransactionRequest,
243+
validate: bool,
244+
block_id: BlockId,
245+
) -> Result<HashMap<Address, Account>> {
246+
self.inner
247+
.get_execution_hint(call, validate, block_id)
248+
.await
249+
}
250+
}

0 commit comments

Comments
 (0)