example_block_traces/
main.rs1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
5
6use alloy_consensus::Transaction;
7use alloy_eips::{BlockId, BlockNumberOrTag};
8use alloy_provider::{network::primitives::BlockTransactions, Provider, ProviderBuilder};
9use indicatif::ProgressBar;
10use revm::{
11 context::TxEnv,
12 database::{AlloyDB, CacheDB, StateBuilder},
13 database_interface::WrapDatabaseAsync,
14 inspector::{inspectors::TracerEip3155, InspectEvm},
15 primitives::{TxKind, U256},
16 Context, MainBuilder, MainContext,
17};
18use std::fs::create_dir_all;
19use std::fs::OpenOptions;
20use std::io::BufWriter;
21use std::io::Write;
22use std::sync::Arc;
23use std::sync::Mutex;
24use std::time::Instant;
25
26struct FlushWriter {
27 writer: Arc<Mutex<BufWriter<std::fs::File>>>,
28}
29
30impl FlushWriter {
31 fn new(writer: Arc<Mutex<BufWriter<std::fs::File>>>) -> Self {
32 Self { writer }
33 }
34}
35
36impl Write for FlushWriter {
37 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
38 self.writer.lock().unwrap().write(buf)
39 }
40
41 fn flush(&mut self) -> std::io::Result<()> {
42 self.writer.lock().unwrap().flush()
43 }
44}
45
46#[tokio::main]
47async fn main() -> anyhow::Result<()> {
48 create_dir_all("traces")?;
49
50 let rpc_url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27".parse()?;
52
53 let client = ProviderBuilder::new().connect_http(rpc_url);
55
56 let chain_id: u64 = 1;
58 let block_number = 10889447;
59
60 let block = match client
62 .get_block_by_number(BlockNumberOrTag::Number(block_number))
63 .full()
64 .await
65 {
66 Ok(Some(block)) => block,
67 Ok(None) => anyhow::bail!("Block not found"),
68 Err(error) => anyhow::bail!("Error: {:?}", error),
69 };
70 println!("Fetched block number: {}", block.header.number);
71 let previous_block_number = block_number - 1;
72
73 let prev_id: BlockId = previous_block_number.into();
75 let state_db = WrapDatabaseAsync::new(AlloyDB::new(client, prev_id)).unwrap();
78 let cache_db: CacheDB<_> = CacheDB::new(state_db);
79 let mut state = StateBuilder::new_with_database(cache_db).build();
80 let ctx = Context::mainnet()
81 .with_db(&mut state)
82 .modify_block_chained(|b| {
83 b.number = U256::from(block.header.number);
84 b.beneficiary = block.header.beneficiary;
85 b.timestamp = U256::from(block.header.timestamp);
86
87 b.difficulty = block.header.difficulty;
88 b.gas_limit = block.header.gas_limit;
89 b.basefee = block.header.base_fee_per_gas.unwrap_or_default();
90 })
91 .modify_cfg_chained(|c| {
92 c.chain_id = chain_id;
93 });
94
95 let write = OpenOptions::new()
96 .write(true)
97 .create(true)
98 .truncate(true)
99 .open("traces/0.json");
100 let inner = Arc::new(Mutex::new(BufWriter::new(
101 write.expect("Failed to open file"),
102 )));
103 let writer = FlushWriter::new(Arc::clone(&inner));
104 let mut evm = ctx.build_mainnet_with_inspector(TracerEip3155::new(Box::new(writer)));
105
106 let txs = block.transactions.len();
107 println!("Found {txs} transactions.");
108
109 let console_bar = Arc::new(ProgressBar::new(txs as u64));
110 let start = Instant::now();
111
112 std::fs::create_dir_all("traces").expect("Failed to create traces directory");
114
115 let BlockTransactions::Full(transactions) = block.transactions else {
117 panic!("Wrong transaction type")
118 };
119
120 for tx in transactions {
121 let tx_number = tx.transaction_index.unwrap_or_default();
123
124 let tx = TxEnv::builder()
125 .caller(tx.inner.signer())
126 .gas_limit(tx.gas_limit())
127 .gas_price(tx.gas_price().unwrap_or(tx.inner.max_fee_per_gas()))
128 .value(tx.value())
129 .data(tx.input().to_owned())
130 .gas_priority_fee(tx.max_priority_fee_per_gas())
131 .chain_id(Some(chain_id))
132 .nonce(tx.nonce())
133 .access_list(tx.access_list().cloned().unwrap_or_default())
134 .kind(match tx.to() {
135 Some(to_address) => TxKind::Call(to_address),
136 None => TxKind::Create,
137 })
138 .build()
139 .unwrap();
140
141 let file_name = format!("traces/{tx_number}.json");
142 let write = OpenOptions::new()
143 .write(true)
144 .create(true)
145 .truncate(true)
146 .open(file_name);
147 let inner = Arc::new(Mutex::new(BufWriter::new(
148 write.expect("Failed to open file"),
149 )));
150 let writer = FlushWriter::new(Arc::clone(&inner));
151
152 let res: Result<_, _> = evm.inspect_one(tx, TracerEip3155::new(Box::new(writer)));
154
155 if let Err(error) = res {
156 println!("Got error: {error:?}");
157 }
158
159 inner.lock().unwrap().flush().expect("Failed to flush file");
161
162 console_bar.inc(1);
163 }
164
165 console_bar.finish_with_message("Finished all transactions.");
166
167 let elapsed = start.elapsed();
168 println!(
169 "Finished execution. Total CPU time: {:.6}s",
170 elapsed.as_secs_f64()
171 );
172
173 Ok(())
174}