1use alloy::{
16 network::{primitives::HeaderResponse, BlockResponse, Network},
17 providers::Provider,
18 rpc::types::EIP1186AccountProofResponse,
19 transports::TransportError,
20};
21use alloy_primitives::{map::B256HashMap, Address, BlockHash, Log, StorageKey, B256, U256};
22use alloy_rpc_types::Filter;
23use revm::{
24 database::DBErrorMarker,
25 primitives::KECCAK_EMPTY,
26 state::{AccountInfo, Bytecode},
27 Database as RevmDatabase,
28};
29use std::{future::IntoFuture, marker::PhantomData};
30use tokio::runtime::Handle;
31
32#[derive(Debug, thiserror::Error)]
34pub enum Error {
35 #[error("{0} failed")]
36 Rpc(&'static str, #[source] TransportError),
37 #[error("block not found")]
38 BlockNotFound,
39 #[error("inconsistent RPC response: {0}")]
40 InconsistentResponse(&'static str),
41}
42
43impl DBErrorMarker for Error {}
44
45pub struct ProviderDb<N: Network, P: Provider<N>> {
53 provider: P,
55 provider_config: ProviderConfig,
57 block: BlockHash,
59 handle: Handle,
61 contracts: B256HashMap<Bytecode>,
63
64 phantom: PhantomData<N>,
65}
66
67#[derive(Clone, Debug)]
69#[non_exhaustive]
70pub(crate) struct ProviderConfig {
71 pub eip1186_proof_chunk_size: usize,
73}
74
75impl Default for ProviderConfig {
76 fn default() -> Self {
77 Self {
78 eip1186_proof_chunk_size: 1000,
79 }
80 }
81}
82
83impl<N: Network, P: Provider<N>> ProviderDb<N, P> {
84 pub(crate) fn new(provider: P, config: ProviderConfig, block_hash: BlockHash) -> Self {
88 Self {
89 provider,
90 provider_config: config,
91 block: block_hash,
92 handle: Handle::current(),
93 contracts: Default::default(),
94 phantom: PhantomData,
95 }
96 }
97
98 pub(crate) fn provider(&self) -> &P {
100 &self.provider
101 }
102
103 pub(crate) fn block(&self) -> BlockHash {
105 self.block
106 }
107
108 pub(crate) async fn get_proof(
110 &self,
111 address: Address,
112 mut keys: Vec<StorageKey>,
113 ) -> Result<EIP1186AccountProofResponse, Error> {
114 let block = self.block();
115
116 keys.sort_unstable();
118
119 let mut iter = keys.chunks(self.provider_config.eip1186_proof_chunk_size);
120 let mut account_proof = self
122 .provider()
123 .get_proof(address, iter.next().unwrap_or_default().into())
124 .hash(block)
125 .await
126 .map_err(|err| Error::Rpc("eth_getProof", err))?;
127 for keys in iter {
128 let proof = self
129 .provider()
130 .get_proof(address, keys.into())
131 .hash(block)
132 .await
133 .map_err(|err| Error::Rpc("eth_getProof", err))?;
134 if proof.account_proof != account_proof.account_proof {
136 return Err(Error::InconsistentResponse(
137 "account_proof not consistent between calls",
138 ));
139 }
140
141 account_proof.storage_proof.extend(proof.storage_proof);
142 }
143
144 Ok(account_proof)
145 }
146}
147
148impl<N: Network, P: Provider<N>> RevmDatabase for ProviderDb<N, P> {
149 type Error = Error;
150
151 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
152 let f = async {
153 let get_nonce = self
154 .provider
155 .get_transaction_count(address)
156 .hash(self.block);
157 let get_balance = self.provider.get_balance(address).hash(self.block);
158 let get_code = self.provider.get_code_at(address).hash(self.block);
159
160 tokio::join!(
161 get_nonce.into_future(),
162 get_balance.into_future(),
163 get_code.into_future()
164 )
165 };
166 let (nonce, balance, code) = self.handle.block_on(f);
167
168 let nonce = nonce.map_err(|err| Error::Rpc("eth_getTransactionCount", err))?;
169 let balance = balance.map_err(|err| Error::Rpc("eth_getBalance", err))?;
170 let code = code.map_err(|err| Error::Rpc("eth_getCode", err))?;
171 let bytecode = Bytecode::new_raw(code.0.into());
172
173 if nonce == 0 && balance.is_zero() && bytecode.is_empty() {
176 return Ok(None);
177 }
178
179 let code_hash = bytecode.hash_slow();
181 self.contracts.insert(code_hash, bytecode);
182
183 Ok(Some(AccountInfo {
184 nonce,
185 balance,
186 code_hash,
187 code: None, }))
189 }
190
191 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
192 if code_hash == KECCAK_EMPTY {
193 return Ok(Bytecode::new());
194 }
195
196 let code = self
198 .contracts
199 .get(&code_hash)
200 .expect("`basic` must be called first for the corresponding account");
201
202 Ok(code.clone())
203 }
204
205 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
206 let storage = self
207 .handle
208 .block_on(
209 self.provider
210 .get_storage_at(address, index)
211 .hash(self.block)
212 .into_future(),
213 )
214 .map_err(|err| Error::Rpc("eth_getStorageAt", err))?;
215
216 Ok(storage)
217 }
218
219 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
220 let block_response = self
221 .handle
222 .block_on(
223 self.provider
224 .get_block_by_number(number.into())
225 .into_future(),
226 )
227 .map_err(|err| Error::Rpc("eth_getBlockByNumber", err))?;
228 let block = block_response.ok_or(Error::BlockNotFound)?;
229
230 Ok(block.header().hash())
231 }
232}
233
234impl<N: Network, P: Provider<N>> crate::EvmDatabase for ProviderDb<N, P> {
235 fn logs(&mut self, filter: Filter) -> Result<Vec<Log>, <Self as RevmDatabase>::Error> {
236 assert_eq!(filter.get_block_hash(), Some(self.block()));
237 let rpc_logs = self
238 .handle
239 .block_on(self.provider.get_logs(&filter))
240 .map_err(|err| Error::Rpc("eth_getLogs", err))?;
241
242 Ok(rpc_logs.into_iter().map(|log| log.inner).collect())
243 }
244}