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