Skip to main content

revm_statetest_types/
test.rs

1use context::tx::TxEnv;
2use primitives::{AddressMap, Bytes, TxKind, B256};
3use serde::Deserialize;
4
5use crate::{
6    error::TestError, transaction::TxPartIndices, utils::recover_address, AccountInfo, TestUnit,
7};
8
9/// State test indexed state result deserialization.
10#[derive(Debug, PartialEq, Eq, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct Test {
13    /// Expected exception for this test case, if any.
14    ///
15    /// This field contains an optional string describing an expected error or exception
16    /// that should occur during the execution of this state test. If present, the test
17    /// is expected to fail with this specific error message or exception type.
18    pub expect_exception: Option<String>,
19
20    /// Indexes
21    pub indexes: TxPartIndices,
22    /// Post state hash
23    pub hash: B256,
24    /// Post state
25    #[serde(default)]
26    pub post_state: AddressMap<AccountInfo>,
27
28    /// Logs root
29    pub logs: B256,
30
31    /// Output state.
32    ///
33    /// Note: Not used.
34    #[serde(default)]
35    state: AddressMap<AccountInfo>,
36
37    /// Tx bytes
38    pub txbytes: Option<Bytes>,
39}
40
41impl Test {
42    /// Create a transaction environment from this test and the test unit.
43    ///
44    /// This function sets up the transaction environment using the test's
45    /// indices to select the appropriate transaction parameters from the
46    /// test unit.
47    ///
48    /// # Arguments
49    ///
50    /// * `unit` - The test unit containing transaction parts
51    ///
52    /// # Returns
53    ///
54    /// A configured [`TxEnv`] ready for execution, or an error if setup fails
55    ///
56    /// # Errors
57    ///
58    /// Returns an error if:
59    /// - The private key cannot be used to recover the sender address
60    /// - The transaction type is invalid and no exception is expected
61    pub fn tx_env(&self, unit: &TestUnit) -> Result<TxEnv, TestError> {
62        // State tests are unsigned; the secret key stands in for a valid
63        // signature. A fixture without one models an invalidly-signed
64        // transaction (e.g. bad v/r/s values) and must be rejected.
65        let Some(secret_key) = unit.transaction.secret_key else {
66            return Err(TestError::UnexpectedException {
67                expected_exception: self.expect_exception.clone(),
68                got_exception: Some("Missing secret key".to_string()),
69            });
70        };
71
72        // Setup sender
73        let caller = if let Some(address) = unit.transaction.sender {
74            address
75        } else {
76            recover_address(secret_key.as_slice())
77                .ok_or(TestError::UnknownPrivateKey(secret_key))?
78        };
79
80        // Transaction specific fields
81        let tx_type = unit.transaction.tx_type(self.indexes.data).ok_or_else(|| {
82            if self.expect_exception.is_some() {
83                TestError::UnexpectedException {
84                    expected_exception: self.expect_exception.clone(),
85                    got_exception: Some("Invalid transaction type".to_string()),
86                }
87            } else {
88                TestError::InvalidTransactionType
89            }
90        })?;
91
92        let tx = TxEnv {
93            caller,
94            gas_price: unit
95                .transaction
96                .gas_price
97                .or(unit.transaction.max_fee_per_gas)
98                .unwrap_or_default()
99                .try_into()
100                .unwrap_or(u128::MAX),
101            gas_priority_fee: unit
102                .transaction
103                .max_priority_fee_per_gas
104                .map(|b| u128::try_from(b).expect("max priority fee less than u128::MAX")),
105            blob_hashes: unit.transaction.blob_versioned_hashes.clone(),
106            max_fee_per_blob_gas: unit
107                .transaction
108                .max_fee_per_blob_gas
109                .map(|b| u128::try_from(b).expect("max fee less than u128::MAX"))
110                .unwrap_or(u128::MAX),
111            tx_type: tx_type as u8,
112            gas_limit: unit.transaction.gas_limit[self.indexes.gas].saturating_to(),
113            data: unit.transaction.data[self.indexes.data].clone(),
114            nonce: u64::try_from(unit.transaction.nonce).map_err(|_| {
115                TestError::UnexpectedException {
116                    expected_exception: self.expect_exception.clone(),
117                    got_exception: Some("Nonce overflow".to_string()),
118                }
119            })?,
120            chain_id: Some(
121                unit.transaction
122                    .chain_id
123                    .map(|id| id.try_into().unwrap_or(u64::MAX))
124                    .unwrap_or(1),
125            ),
126            value: unit.transaction.value[self.indexes.value],
127            access_list: unit
128                .transaction
129                .access_lists
130                .get(self.indexes.data)
131                .cloned()
132                .flatten()
133                .unwrap_or_default(),
134            authorization_list: unit
135                .transaction
136                .authorization_list
137                .clone()
138                .map(|auth_list| {
139                    auth_list
140                        .into_iter()
141                        .map(|i| context::either::Either::Left(i.into()))
142                        .collect::<Vec<_>>()
143                })
144                .unwrap_or_default(),
145            kind: match unit.transaction.to {
146                Some(add) => TxKind::Call(add),
147                None => TxKind::Create,
148            },
149        };
150
151        Ok(tx)
152    }
153}