1use crate::utils;
2use revm::{
3 db::{CacheDB, DatabaseRef},
4 interpreter,
5 precompile::Bytes,
6 primitives::{alloy_primitives::U64, keccak256, AccountInfo, Address, Bytecode, B256, U256},
7 Database,
8};
9use serde::{de::DeserializeOwned, Deserialize};
10use serde_json::{json, Value};
11use std::time::Duration;
12use std::{
13 sync::atomic::{AtomicU64, Ordering},
14 time,
15};
16use eyre::{Context, ContextCompat, eyre};
17
18pub struct ForkDB {
19 rpc_client: MiniRpcClient,
20 block_number: u64,
21 measure_rpc_time: bool,
22 cumulative_rpc_time: AtomicU64,
23}
24
25impl ForkDB {
26 pub fn new<T: Into<String>, B: Into<u64>>(rpc_url: T, block_number: B) -> Self {
27 ForkDB {
28 rpc_client: MiniRpcClient::new(rpc_url),
29 block_number: block_number.into(),
30 measure_rpc_time: false,
31 cumulative_rpc_time: AtomicU64::new(0),
32 }
33 }
34
35 pub fn new_as_cache_db<T: Into<String>, B: Into<u64>>(
36 rpc_url: T,
37 block_number: B,
38 ) -> CacheDB<Self> {
39 CacheDB::new(ForkDB::new(rpc_url, block_number))
40 }
41
42 pub fn set_measure_rpc_time(&mut self, enable: bool) {
43 self.measure_rpc_time = enable;
44 }
45
46 pub fn get_rpc_time(&self) -> Duration {
47 Duration::from_nanos(self.cumulative_rpc_time.load(Ordering::Relaxed))
48 }
49
50 pub fn reset_rpc_time(&mut self) {
51 self.cumulative_rpc_time.store(0, Ordering::Relaxed);
52 }
53
54 pub fn make_request<F, R>(&self, f: F) -> eyre::Result<R>
55 where
56 F: FnOnce(&MiniRpcClient) -> eyre::Result<R>,
57 {
58 if self.measure_rpc_time {
59 let start = time::Instant::now();
60 let result = f(&self.rpc_client)?;
61 let elapsed = start.elapsed().as_nanos() as u64;
62 self.cumulative_rpc_time
63 .fetch_add(elapsed, Ordering::Relaxed);
64 Ok(result)
65 } else {
66 f(&self.rpc_client)
67 }
68 }
69}
70
71impl Database for ForkDB {
72 type Error = eyre::Error;
73
74 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
75 DatabaseRef::basic_ref(self, address)
76 }
77
78 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
79 DatabaseRef::code_by_hash_ref(self, code_hash)
80 }
81
82 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
83 DatabaseRef::storage_ref(self, address, index)
84 }
85
86 fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
87 DatabaseRef::block_hash_ref(self, number)
88 }
89}
90
91impl DatabaseRef for ForkDB {
92 type Error = eyre::Error;
93
94 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
95 let (balance, nonce, code) = self
96 .make_request(|rpc_client| rpc_client.get_account_basic(&address, self.block_number))?;
97
98 let code_hash = keccak256(&code);
99 let bytecode = interpreter::analysis::to_analysed(Bytecode::new_raw(code));
100
101 Ok(Some(AccountInfo::new(balance, nonce, code_hash, bytecode)))
102 }
103
104 fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
105 unreachable!("code_by_hash should not be called")
106 }
107
108 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
109 let result = self.make_request(|rpc_client| {
110 rpc_client.get_storage_at(&address, &index.into(), self.block_number)
111 })?;
112
113 Ok(result.into())
114 }
115
116 fn block_hash_ref(&self, number: U256) -> Result<B256, Self::Error> {
117 let number = match utils::u256_to_u64(&number) {
118 Some(number) => number,
119 None => eyre::bail!("block number too large"),
120 };
121
122 let block = self
123 .make_request(|rpc_client| rpc_client.get_blockhash(number))?
124 .context("block not found")?;
125
126 block.hash.context("block not finalized")
127 }
128}
129
130pub struct MiniRpcClient {
131 client: ureq::Agent,
132 rpc_url: String,
133}
134
135impl MiniRpcClient {
136 pub fn new<T: Into<String>>(rpc_url: T) -> Self {
137 MiniRpcClient {
138 rpc_url: rpc_url.into(),
139 client: ureq::Agent::new(),
140 }
141 }
142
143 fn format_block_tag(block_number: u64) -> Value {
144 json!(format!("0x{:x}", block_number))
145 }
146
147 fn make_request(id: u64, method: &str, params: &Value) -> Value {
148 json!({
149 "id": id,
150 "jsonrpc": "2.0",
151 "method": method,
152 "params": params,
153 })
154 }
155
156 fn handle_response<T: DeserializeOwned>(mut response: Value) -> eyre::Result<T> {
157 if let Some(error) = response.get("error") {
158 eyre::bail!("rpc error: {error}");
159 } else if response.get("result").is_some() {
160 let value = response["result"].take();
161 return serde_json::from_value(value).context("fail to deserialize result");
162 } else {
163 eyre::bail!("rpc response missing result")
164 }
165 }
166
167 fn do_request<T: DeserializeOwned>(&self, method: &str, params: Value) -> eyre::Result<T> {
168 let request = Self::make_request(1, method, ¶ms);
169
170 let response = self
171 .client
172 .post(&self.rpc_url)
173 .send_json(request)
174 .context("fail to send request")?
175 .into_json::<Value>()
176 .context("fail to read response")?;
177
178 tracing::trace!(
179 method,
180 params = format_args!("{}", params),
181 "response: {response}",
182 );
183
184 Self::handle_response(response)
185 }
186
187 fn get_storage_at(
188 &self,
189 address: &Address,
190 index: &B256,
191 block_number: u64,
192 ) -> eyre::Result<B256> {
193 let response = self.do_request::<B256>(
194 "eth_getStorageAt",
195 json!([
196 address.to_string(),
197 index.to_string(),
198 Self::format_block_tag(block_number),
199 ]),
200 )?;
201
202 Ok(response)
203 }
204
205 fn get_blockhash(&self, block_number: u64) -> eyre::Result<Option<Block>> {
206 let response = self.do_request::<Option<Block>>(
207 "eth_getBlockByNumber",
208 json!([Self::format_block_tag(block_number), false]),
209 )?;
210
211 Ok(response)
212 }
213
214 fn get_account_basic(
215 &self,
216 address: &Address,
217 block_number: u64,
218 ) -> eyre::Result<(U256, u64, Bytes)> {
219 let requests = json!([
220 Self::make_request(
221 1,
222 "eth_getBalance",
223 &json!([address.to_string(), Self::format_block_tag(block_number)]),
224 ),
225 Self::make_request(
226 2,
227 "eth_getTransactionCount",
228 &json!([address.to_string(), Self::format_block_tag(block_number)]),
229 ),
230 Self::make_request(
231 3,
232 "eth_getCode",
233 &json!([address.to_string(), Self::format_block_tag(block_number)]),
234 ),
235 ]);
236
237 let mut response = self
238 .client
239 .post(&self.rpc_url)
240 .send_json(requests)
241 .context("fail to send request")?
242 .into_json::<Value>()?;
243
244 tracing::trace!(
245 account = address.to_string(),
246 "get account basic info. response: {response}"
247 );
248
249 let results = response
250 .as_array_mut()
251 .ok_or(eyre!("expect array response"))?;
252
253 if results.len() != 3 {
254 eyre::bail!("expect 3 responses, got {}", results.len());
255 }
256
257 let balance =
258 Self::handle_response::<U256>(results[0].take()).context("fail to parse balance")?;
259 let nonce =
260 Self::handle_response::<U64>(results[1].take()).context("fail to parse nonce")?;
261 let code =
262 Self::handle_response::<Bytes>(results[2].take()).context("fail to parse code")?;
263
264 Ok((balance, nonce.as_limbs()[0], code))
265 }
266}
267
268#[derive(Debug, Deserialize)]
269struct Block {
270 pub hash: Option<B256>,
271}