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}