revme/cmd/bench/
burntpix.rs

1pub mod static_data;
2
3use context::TxEnv;
4use criterion::Criterion;
5use primitives::{StorageKey, StorageValue};
6use static_data::{
7    BURNTPIX_ADDRESS_ONE, BURNTPIX_ADDRESS_THREE, BURNTPIX_ADDRESS_TWO, BURNTPIX_BYTECODE_FOUR,
8    BURNTPIX_BYTECODE_ONE, BURNTPIX_BYTECODE_THREE, BURNTPIX_BYTECODE_TWO, BURNTPIX_MAIN_ADDRESS,
9    STORAGE_ONE, STORAGE_TWO, STORAGE_ZERO,
10};
11
12use alloy_sol_types::{sol, SolCall};
13use database::{CacheDB, BENCH_CALLER};
14use revm::{
15    database_interface::EmptyDB,
16    primitives::{hex, keccak256, Address, Bytes, TxKind, B256, U256},
17    state::{AccountInfo, Bytecode},
18    Context, ExecuteEvm, MainBuilder, MainContext,
19};
20
21use std::{error::Error, fs::File, io::Write};
22
23use std::str::FromStr;
24
25sol! {
26    #[derive(Debug, PartialEq, Eq)]
27    interface IBURNTPIX {
28        function run( uint32 seed, uint256 iterations) returns (string);
29    }
30}
31
32pub fn run(criterion: &mut Criterion) {
33    let (seed, iterations) = try_init_env_vars().expect("Failed to parse env vars");
34
35    let run_call_data = IBURNTPIX::runCall { seed, iterations }.abi_encode();
36
37    let db = init_db();
38
39    let mut evm = Context::mainnet()
40        .with_db(db)
41        .modify_cfg_chained(|c| c.disable_nonce_check = true)
42        .build_mainnet();
43
44    let tx = TxEnv::builder()
45        .caller(BENCH_CALLER)
46        .kind(TxKind::Call(BURNTPIX_MAIN_ADDRESS))
47        .data(run_call_data.clone().into())
48        .gas_limit(u64::MAX)
49        .build()
50        .unwrap();
51
52    criterion.bench_function("burntpix", |b| {
53        b.iter_batched(
54            || tx.clone(),
55            |input| evm.transact_one(input).unwrap(),
56            criterion::BatchSize::SmallInput,
57        );
58    });
59
60    //Collects the data and uses it to generate the svg after running the benchmark
61    /*
62    let tx_result = evm.replay().unwrap();
63    let return_data = match tx_result.result {
64        context::result::ExecutionResult::Success {
65            output, gas_used, ..
66        } => {
67            println!("Gas used: {:?}", gas_used);
68            match output {
69                context::result::Output::Call(value) => value,
70                _ => unreachable!("Unexpected output type"),
71            }
72        }
73        _ => unreachable!("Execution failed: {:?}", tx_result),
74    };
75
76    // Remove returndata offset and length from output
77    let returndata_offset = 64;
78    let data = &return_data[returndata_offset..];
79
80    // Remove trailing zeros
81    let trimmed_data = data
82        .split_at(data.len() - data.iter().rev().filter(|&x| *x == 0).count())
83        .0;
84    let file_name = format!("{}_{}", seed, iterations);
85
86    svg(file_name, trimmed_data).expect("Failed to store svg");
87    */
88}
89
90/// Actually generates the svg
91pub fn svg(filename: String, svg_data: &[u8]) -> Result<(), Box<dyn Error>> {
92    let current_dir = std::env::current_dir()?;
93    let svg_dir = current_dir.join("burntpix").join("svgs");
94    std::fs::create_dir_all(&svg_dir)?;
95
96    let file_path = svg_dir.join(format!("{filename}.svg"));
97    let mut file = File::create(file_path)?;
98    file.write_all(svg_data)?;
99
100    Ok(())
101}
102
103const DEFAULT_SEED: &str = "0";
104const DEFAULT_ITERATIONS: &str = "0x4E20"; // 20_000 iterations
105fn try_init_env_vars() -> Result<(u32, U256), Box<dyn Error>> {
106    let seed_from_env = std::env::var("SEED").unwrap_or(DEFAULT_SEED.to_string());
107    let seed: u32 = try_from_hex_to_u32(&seed_from_env)?;
108    let iterations_from_env = std::env::var("ITERATIONS").unwrap_or(DEFAULT_ITERATIONS.to_string());
109    let iterations = U256::from_str(&iterations_from_env)?;
110    Ok((seed, iterations))
111}
112
113fn try_from_hex_to_u32(hex: &str) -> Result<u32, Box<dyn Error>> {
114    let trimmed = hex.strip_prefix("0x").unwrap_or(hex);
115    u32::from_str_radix(trimmed, 16).map_err(|e| format!("Failed to parse hex: {e}").into())
116}
117
118fn insert_account_info(cache_db: &mut CacheDB<EmptyDB>, addr: Address, code: &str) {
119    let code = Bytes::from(hex::decode(code).unwrap());
120    let code_hash = hex::encode(keccak256(&code));
121    let account_info = AccountInfo::new(
122        U256::from(0),
123        0,
124        B256::from_str(&code_hash).unwrap(),
125        Bytecode::new_raw(code),
126    );
127    cache_db.insert_account_info(addr, account_info);
128}
129
130fn init_db() -> CacheDB<EmptyDB> {
131    let mut cache_db = CacheDB::new(EmptyDB::default());
132
133    insert_account_info(&mut cache_db, BURNTPIX_ADDRESS_ONE, BURNTPIX_BYTECODE_ONE);
134    insert_account_info(&mut cache_db, BURNTPIX_MAIN_ADDRESS, BURNTPIX_BYTECODE_TWO);
135    insert_account_info(&mut cache_db, BURNTPIX_ADDRESS_TWO, BURNTPIX_BYTECODE_THREE);
136    insert_account_info(
137        &mut cache_db,
138        BURNTPIX_ADDRESS_THREE,
139        BURNTPIX_BYTECODE_FOUR,
140    );
141
142    cache_db
143        .insert_account_storage(
144            BURNTPIX_MAIN_ADDRESS,
145            StorageKey::from(0),
146            StorageValue::from_be_bytes(*STORAGE_ZERO),
147        )
148        .unwrap();
149
150    cache_db
151        .insert_account_storage(
152            BURNTPIX_MAIN_ADDRESS,
153            StorageKey::from(1),
154            StorageValue::from_be_bytes(*STORAGE_ONE),
155        )
156        .unwrap();
157
158    cache_db
159        .insert_account_storage(
160            BURNTPIX_MAIN_ADDRESS,
161            StorageKey::from(2),
162            StorageValue::from_be_bytes(*STORAGE_TWO),
163        )
164        .unwrap();
165
166    cache_db
167}