Skip to content

Commit 5a47ae0

Browse files
authored
feat(examples): generate block traces (#895)
1 parent 75a3cc4 commit 5a47ae0

File tree

5 files changed

+193
-2
lines changed

5 files changed

+193
-2
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ target
66
.idea
77
pkg/
88

9-
tests
109
bins/revme/temp_folder
1110
bins/revme/tests
1211
ethereumjs-util.js
1312
book
13+
14+
# Generated by the block traces example
15+
traces

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,19 @@ cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm-test --bin
8383
This command will produce a flamegraph image output to `flamegraph.svg`.
8484
Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will prompt you for your password if not in sudo mode already.
8585

86-
## Running example
86+
## Running examples
8787

8888
```shell
8989
cargo run -p revm --features ethersdb --example fork_ref_transact
9090
```
9191

92+
Generate block traces and write them to json files in a new `traces/` directory.
93+
Each file corresponds to a transaction in the block and is named as such: `<tx index>.json`.
94+
95+
```shell
96+
cargo run -p revm --features std,serde,ethersdb --example generate_block_traces
97+
```
98+
9299
# Used by:
93100

94101
* [Foundry](https://github.com/foundry-rs/foundry) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.

crates/revm/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ futures = { version = "0.3.29", optional = true }
3737
ethers-contract = { version = "2.0.11", default-features = false }
3838
anyhow = "1.0.75"
3939
criterion = "0.5"
40+
indicatif = "0.17"
4041

4142
[features]
4243
default = ["std", "c-kzg", "secp256k1"]
@@ -80,6 +81,11 @@ name = "fork_ref_transact"
8081
path = "../../examples/fork_ref_transact.rs"
8182
required-features = ["ethersdb"]
8283

84+
[[example]]
85+
name = "generate_block_traces"
86+
path = "../../examples/generate_block_traces.rs"
87+
required-features = ["std", "serde", "ethersdb"]
88+
8389
[[bench]]
8490
name = "bench"
8591
path = "benches/bench.rs"

examples/generate_block_traces.rs

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Example Adapted From: https://github.com/bluealloy/revm/issues/672
2+
3+
use ethers_core::types::BlockId;
4+
use ethers_providers::Middleware;
5+
use ethers_providers::{Http, Provider};
6+
use indicatif::ProgressBar;
7+
use revm::db::{CacheDB, EthersDB, StateBuilder};
8+
use revm::inspectors::TracerEip3155;
9+
use revm::primitives::{Address, Env, TransactTo, U256};
10+
use revm::EVM;
11+
use std::fs::OpenOptions;
12+
use std::io::BufWriter;
13+
use std::io::Write;
14+
use std::sync::Arc;
15+
use std::sync::Mutex;
16+
17+
macro_rules! local_fill {
18+
($left:expr, $right:expr, $fun:expr) => {
19+
if let Some(right) = $right {
20+
$left = $fun(right.0)
21+
}
22+
};
23+
($left:expr, $right:expr) => {
24+
if let Some(right) = $right {
25+
$left = Address::from(right.as_fixed_bytes())
26+
}
27+
};
28+
}
29+
30+
struct FlushWriter {
31+
writer: Arc<Mutex<BufWriter<std::fs::File>>>,
32+
}
33+
34+
impl FlushWriter {
35+
fn new(writer: Arc<Mutex<BufWriter<std::fs::File>>>) -> Self {
36+
Self { writer }
37+
}
38+
}
39+
40+
impl Write for FlushWriter {
41+
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
42+
self.writer.lock().unwrap().write(buf)
43+
}
44+
45+
fn flush(&mut self) -> std::io::Result<()> {
46+
self.writer.lock().unwrap().flush()
47+
}
48+
}
49+
50+
#[tokio::main]
51+
async fn main() -> anyhow::Result<()> {
52+
// Create ethers client and wrap it in Arc<M>
53+
let client = Provider::<Http>::try_from(
54+
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
55+
)?;
56+
let client = Arc::new(client);
57+
58+
// Params
59+
let chain_id: u64 = 1;
60+
let block_number = 10889447;
61+
62+
// Fetch the transaction-rich block
63+
let block = match client.get_block_with_txs(block_number).await {
64+
Ok(Some(block)) => block,
65+
Ok(None) => anyhow::bail!("Block not found"),
66+
Err(error) => anyhow::bail!("Error: {:?}", error),
67+
};
68+
println!("Fetched block number: {}", block.number.unwrap().0[0]);
69+
let previous_block_number = block_number - 1;
70+
71+
// Use the previous block state as the db with caching
72+
let prev_id: BlockId = previous_block_number.into();
73+
// SAFETY: This cannot fail since this is in the top-level tokio runtime
74+
let state_db = EthersDB::new(Arc::clone(&client), Some(prev_id)).expect("panic");
75+
let cache_db: CacheDB<EthersDB<Provider<Http>>> = CacheDB::new(state_db);
76+
let mut state = StateBuilder::new_with_database(cache_db).build();
77+
let mut evm = EVM::new();
78+
evm.database(&mut state);
79+
80+
let mut env = Env::default();
81+
if let Some(number) = block.number {
82+
let nn = number.0[0];
83+
env.block.number = U256::from(nn);
84+
}
85+
local_fill!(env.block.coinbase, block.author);
86+
local_fill!(env.block.timestamp, Some(block.timestamp), U256::from_limbs);
87+
local_fill!(
88+
env.block.difficulty,
89+
Some(block.difficulty),
90+
U256::from_limbs
91+
);
92+
local_fill!(env.block.gas_limit, Some(block.gas_limit), U256::from_limbs);
93+
if let Some(base_fee) = block.base_fee_per_gas {
94+
local_fill!(env.block.basefee, Some(base_fee), U256::from_limbs);
95+
}
96+
97+
let txs = block.transactions.len();
98+
println!("Found {txs} transactions.");
99+
100+
let console_bar = Arc::new(ProgressBar::new(txs as u64));
101+
let elapsed = std::time::Duration::ZERO;
102+
103+
// Create the traces directory if it doesn't exist
104+
std::fs::create_dir_all("traces").expect("Failed to create traces directory");
105+
106+
// Fill in CfgEnv
107+
env.cfg.chain_id = chain_id;
108+
for tx in block.transactions {
109+
env.tx.caller = Address::from(tx.from.as_fixed_bytes());
110+
env.tx.gas_limit = tx.gas.as_u64();
111+
local_fill!(env.tx.gas_price, tx.gas_price, U256::from_limbs);
112+
local_fill!(env.tx.value, Some(tx.value), U256::from_limbs);
113+
env.tx.data = tx.input.0.into();
114+
let mut gas_priority_fee = U256::ZERO;
115+
local_fill!(
116+
gas_priority_fee,
117+
tx.max_priority_fee_per_gas,
118+
U256::from_limbs
119+
);
120+
env.tx.gas_priority_fee = Some(gas_priority_fee);
121+
env.tx.chain_id = Some(chain_id);
122+
env.tx.nonce = Some(tx.nonce.as_u64());
123+
if let Some(access_list) = tx.access_list {
124+
env.tx.access_list = access_list
125+
.0
126+
.into_iter()
127+
.map(|item| {
128+
let new_keys: Vec<U256> = item
129+
.storage_keys
130+
.into_iter()
131+
.map(|h256| U256::from_le_bytes(h256.0))
132+
.collect();
133+
(Address::from(item.address.as_fixed_bytes()), new_keys)
134+
})
135+
.collect();
136+
} else {
137+
env.tx.access_list = Default::default();
138+
}
139+
140+
env.tx.transact_to = match tx.to {
141+
Some(to_address) => TransactTo::Call(Address::from(to_address.as_fixed_bytes())),
142+
None => TransactTo::create(),
143+
};
144+
145+
evm.env = env.clone();
146+
147+
// Construct the file writer to write the trace to
148+
let tx_number = tx.transaction_index.unwrap().0[0];
149+
let file_name = format!("traces/{}.json", tx_number);
150+
let write = OpenOptions::new().write(true).create(true).open(file_name);
151+
let inner = Arc::new(Mutex::new(BufWriter::new(
152+
write.expect("Failed to open file"),
153+
)));
154+
let writer = FlushWriter::new(Arc::clone(&inner));
155+
156+
// Inspect and commit the transaction to the EVM
157+
let inspector = TracerEip3155::new(Box::new(writer), true, true);
158+
if let Err(error) = evm.inspect_commit(inspector) {
159+
println!("Got error: {:?}", error);
160+
}
161+
162+
// Flush the file writer
163+
inner.lock().unwrap().flush().expect("Failed to flush file");
164+
165+
console_bar.inc(1);
166+
}
167+
168+
console_bar.finish_with_message("Finished all transactions.");
169+
println!(
170+
"Finished execution. Total CPU time: {:.6}s",
171+
elapsed.as_secs_f64()
172+
);
173+
174+
Ok(())
175+
}

0 commit comments

Comments
 (0)