revm_statetest_types/test_unit.rs
1use crate::{AccountInfo, Env, SpecName, Test, TransactionParts};
2use revm::{
3 context::{block::BlockEnv, cfg::CfgEnv},
4 database::CacheState,
5 primitives::{hardfork::SpecId, keccak256, Address, Bytes, HashMap, B256},
6 state::Bytecode,
7};
8use serde::Deserialize;
9use std::collections::BTreeMap;
10
11/// Single test unit struct
12#[derive(Debug, PartialEq, Eq, Deserialize)]
13//#[serde(deny_unknown_fields)]
14// field config
15pub struct TestUnit {
16 /// Test info is optional.
17 #[serde(default, rename = "_info")]
18 pub info: Option<serde_json::Value>,
19
20 /// Test environment configuration.
21 ///
22 /// Contains the environmental information for executing the test, including
23 /// block information, coinbase address, difficulty, gas limit, and other
24 /// blockchain state parameters required for proper test execution.
25 pub env: Env,
26
27 /// Pre-execution state.
28 ///
29 /// A mapping of addresses to their account information before the transaction
30 /// is executed. This represents the initial state of all accounts involved
31 /// in the test, including their balances, nonces, code, and storage.
32 pub pre: HashMap<Address, AccountInfo>,
33
34 /// Post-execution expectations per specification.
35 ///
36 /// Maps each Ethereum specification name (hardfork) to a vector of expected
37 /// test results. This allows a single test to define different expected outcomes
38 /// for different protocol versions, enabling comprehensive testing across
39 /// multiple Ethereum upgrades.
40 pub post: BTreeMap<SpecName, Vec<Test>>,
41
42 /// Transaction details to be executed.
43 ///
44 /// Contains the transaction parameters that will be executed against the
45 /// pre-state. This includes sender, recipient, value, data, gas limits,
46 /// and other transaction fields that may vary based on indices.
47 pub transaction: TransactionParts,
48
49 /// Expected output data from the transaction execution.
50 ///
51 /// Optional field containing the expected return data from the transaction.
52 /// This is typically used for testing contract calls that return specific
53 /// values or for CREATE operations that return deployed contract addresses.
54 #[serde(default)]
55 pub out: Option<Bytes>,
56 //pub config
57}
58
59impl TestUnit {
60 /// Prepare the state from the test unit.
61 ///
62 /// This function uses [`TestUnit::pre`] to prepare the pre-state from the test unit.
63 /// It creates a new cache state and inserts the accounts from the test unit.
64 ///
65 /// # Returns
66 ///
67 /// A [`CacheState`] object containing the pre-state accounts and storages.
68 pub fn state(&self) -> CacheState {
69 let mut cache_state = CacheState::new(false);
70 for (address, info) in &self.pre {
71 let code_hash = keccak256(&info.code);
72 let bytecode = Bytecode::new_raw_checked(info.code.clone())
73 .unwrap_or(Bytecode::new_legacy(info.code.clone()));
74 let acc_info = revm::state::AccountInfo {
75 balance: info.balance,
76 code_hash,
77 code: Some(bytecode),
78 nonce: info.nonce,
79 };
80 cache_state.insert_account_with_storage(*address, acc_info, info.storage.clone());
81 }
82 cache_state
83 }
84
85 /// Create a block environment from the test unit.
86 ///
87 /// This function sets up the block environment using the current test unit's
88 /// environment settings and the provided configuration.
89 ///
90 /// # Arguments
91 ///
92 /// * `cfg` - The configuration environment containing spec and blob settings
93 ///
94 /// # Returns
95 ///
96 /// A configured [`BlockEnv`] ready for execution
97 pub fn block_env(&self, cfg: &CfgEnv) -> BlockEnv {
98 let mut block = BlockEnv {
99 number: self.env.current_number,
100 beneficiary: self.env.current_coinbase,
101 timestamp: self.env.current_timestamp,
102 gas_limit: self.env.current_gas_limit.try_into().unwrap_or(u64::MAX),
103 basefee: self
104 .env
105 .current_base_fee
106 .unwrap_or_default()
107 .try_into()
108 .unwrap_or(u64::MAX),
109 difficulty: self.env.current_difficulty,
110 prevrandao: self.env.current_random,
111 ..BlockEnv::default()
112 };
113
114 // Handle EIP-4844 blob gas
115 if let Some(current_excess_blob_gas) = self.env.current_excess_blob_gas {
116 block.set_blob_excess_gas_and_price(
117 current_excess_blob_gas.to(),
118 revm::primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN,
119 );
120 }
121
122 // Set default prevrandao for merge
123 if cfg.spec.is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() {
124 block.prevrandao = Some(B256::default());
125 }
126
127 block
128 }
129}