revm_statetest_types/
test_unit.rs

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