revm_fork_db/
fork_db.rs

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, &params);
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}