1use std::str::FromStr;
4
5use alloy_primitives::{Address, B256, U256};
6use clap::{Args, Parser};
7use std::convert::Infallible;
8
9use mega_evm::{
10 alloy_evm::Database,
11 revm::{
12 context::{block::BlockEnv, cfg::CfgEnv},
13 primitives::eip4844,
14 },
15 MegaContext, MegaSpecId, TestExternalEnvs,
16};
17
18use crate::hasher::AHashBucketHasher;
19
20pub type EvmeExternalEnvs = TestExternalEnvs<Infallible, AHashBucketHasher>;
22use tracing::{debug, trace};
23
24use super::{EvmeError, Result};
25
26#[derive(Args, Debug, Clone)]
28#[command(next_help_heading = "Chain Options")]
29pub struct ChainArgs {
30 #[arg(long = "spec", default_value = "Rex5")]
33 pub spec: String,
34
35 #[arg(long = "chain-id", visible_aliases = ["chainid"], default_value = "6342")]
37 pub chain_id: u64,
38}
39
40impl ChainArgs {
41 pub fn spec_id(&self) -> Result<MegaSpecId> {
43 MegaSpecId::from_str(&self.spec)
44 .map_err(|e| EvmeError::InvalidInput(format!("Invalid spec name: {:?}", e)))
45 }
46
47 pub fn create_cfg_env(&self) -> Result<CfgEnv<MegaSpecId>> {
49 let mut cfg = CfgEnv::default();
50 cfg.chain_id = self.chain_id;
51 cfg.spec = self.spec_id()?;
52 debug!(cfg = ?cfg, "Evm CfgEnv created");
53 Ok(cfg)
54 }
55}
56
57#[derive(Args, Debug, Clone)]
59#[command(next_help_heading = "Block Options")]
60pub struct BlockEnvArgs {
61 #[arg(long = "block.number", default_value = "1")]
63 pub block_number: u64,
64
65 #[arg(long = "block.coinbase", visible_aliases = ["block.beneficiary"], default_value = "0x0000000000000000000000000000000000000000")]
67 pub block_coinbase: Address,
68
69 #[arg(long = "block.timestamp", default_value = "1")]
71 pub block_timestamp: u64,
72
73 #[arg(long = "block.gaslimit", visible_aliases = ["block.gas-limit", "block.gas"], default_value = "10000000000")]
75 pub block_gas_limit: u64,
76
77 #[arg(long = "block.basefee", visible_aliases = ["block.base-fee"], default_value = "0")]
79 pub block_basefee: u64,
80
81 #[arg(long = "block.difficulty", default_value = "0")]
83 pub block_difficulty: U256,
84
85 #[arg(
87 long = "block.prevrandao",
88 visible_aliases = ["block.random"],
89 default_value = "0x0000000000000000000000000000000000000000000000000000000000000000"
90 )]
91 pub block_prevrandao: B256,
92
93 #[arg(long = "block.blobexcessgas", visible_aliases = ["block.blob-excess-gas"], default_value = "0")]
95 pub block_blob_excess_gas: Option<u64>,
96}
97
98impl BlockEnvArgs {
99 pub fn create_block_env(&self) -> Result<BlockEnv> {
101 let mut block = BlockEnv {
102 number: U256::from(self.block_number),
103 beneficiary: self.block_coinbase,
104 timestamp: U256::from(self.block_timestamp),
105 gas_limit: self.block_gas_limit,
106 basefee: self.block_basefee,
107 difficulty: self.block_difficulty,
108 prevrandao: Some(self.block_prevrandao),
109 blob_excess_gas_and_price: None,
110 };
111
112 if let Some(excess_gas) = self.block_blob_excess_gas {
114 block.set_blob_excess_gas_and_price(
115 excess_gas,
116 eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN,
117 );
118 }
119 debug!(block = ?block, "Evm BlockEnv created");
120
121 Ok(block)
122 }
123}
124
125#[derive(Args, Debug, Clone)]
127#[command(next_help_heading = "External Environment Options")]
128pub struct ExtEnvArgs {
129 #[arg(long = "bucket-capacity", value_name = "BUCKET_ID:CAPACITY")]
133 pub bucket_capacity: Vec<String>,
134}
135
136impl ExtEnvArgs {
137 pub fn create_external_envs(&self) -> Result<EvmeExternalEnvs> {
139 let mut external_envs = EvmeExternalEnvs::new();
140
141 for bucket_capacity_str in &self.bucket_capacity {
143 let (bucket_id, capacity) = parse_bucket_capacity(bucket_capacity_str)?;
144 external_envs = external_envs.with_bucket_capacity(bucket_id, capacity);
145 }
146 debug!(external_envs = ?external_envs, "Evm EvmeExternalEnvs created");
147
148 Ok(external_envs)
149 }
150}
151
152#[derive(Parser, Debug, Clone)]
154pub struct EnvArgs {
155 #[command(flatten)]
157 pub chain: ChainArgs,
158
159 #[command(flatten)]
161 pub block: BlockEnvArgs,
162
163 #[command(flatten)]
165 pub ext: ExtEnvArgs,
166}
167
168impl EnvArgs {
169 pub fn spec_id(&self) -> Result<MegaSpecId> {
171 self.chain.spec_id()
172 }
173
174 pub fn create_cfg_env(&self) -> Result<CfgEnv<MegaSpecId>> {
176 self.chain.create_cfg_env()
177 }
178
179 pub fn create_block_env(&self) -> Result<BlockEnv> {
181 self.block.create_block_env()
182 }
183
184 pub fn create_external_envs(&self) -> Result<EvmeExternalEnvs> {
186 self.ext.create_external_envs()
187 }
188
189 pub fn create_evm_context<DB: Database>(
196 &self,
197 db: DB,
198 ) -> Result<MegaContext<DB, EvmeExternalEnvs>> {
199 let cfg = self.create_cfg_env()?;
200 let block = self.create_block_env()?;
201 let external_envs = self.create_external_envs()?;
202
203 Ok(MegaContext::new(db, cfg.spec)
204 .with_cfg(cfg)
205 .with_block(block)
206 .with_external_envs(external_envs.into()))
207 }
208}
209
210pub fn parse_bucket_capacity(s: &str) -> Result<(u32, u64)> {
213 let parts: Vec<&str> = s.split(':').collect();
214 if parts.len() != 2 {
215 return Err(EvmeError::InvalidInput(format!(
216 "Invalid bucket capacity format: '{}'. Expected format: 'bucket_id:capacity'",
217 s
218 )));
219 }
220
221 let bucket_id = parts[0]
222 .parse::<u32>()
223 .map_err(|e| EvmeError::InvalidInput(format!("Invalid bucket ID '{}': {}", parts[0], e)))?;
224
225 let capacity = parts[1]
226 .parse::<u64>()
227 .map_err(|e| EvmeError::InvalidInput(format!("Invalid capacity '{}': {}", parts[1], e)))?;
228
229 trace!(string = %s, bucket_id = %bucket_id, capacity = %capacity, "Parsed bucket capacity");
230 Ok((bucket_id, capacity))
231}