Skip to main content

revm_statetest_types/
transaction.rs

1use crate::{deserializer::deserialize_maybe_empty, TestAuthorization};
2use context::TransactionType;
3use context_interface::transaction::AccessList;
4use primitives::{Address, Bytes, B256, U256};
5use serde::{Deserialize, Serialize};
6
7/// Transaction parts.
8#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct TransactionParts {
11    /// Transaction type (0=Legacy, 1=EIP-2930, 2=EIP-1559, 3=EIP-4844, 4=EIP-7702)
12    #[serde(rename = "type")]
13    pub tx_type: Option<u8>,
14    /// Chain id of the transaction. If it differs from the network chain id the
15    /// transaction is expected to be rejected with `INVALID_CHAINID`.
16    pub chain_id: Option<U256>,
17    /// Transaction data/input (multiple variants for different test cases)
18    pub data: Vec<Bytes>,
19    /// Gas limit values (multiple variants for different test cases)
20    pub gas_limit: Vec<U256>,
21    /// Gas price (for legacy and EIP-2930 transactions)
22    pub gas_price: Option<U256>,
23    /// Transaction nonce
24    pub nonce: U256,
25    /// Private key for signing the transaction.
26    ///
27    /// State tests are unsigned; the secret key stands in for a valid
28    /// signature. A fixture without one models an invalidly-signed
29    /// transaction (e.g. `test_bad_v_r_s`) and is expected to be rejected.
30    pub secret_key: Option<B256>,
31    /// if sender is not present we need to derive it from secret key.
32    #[serde(default)]
33    pub sender: Option<Address>,
34    /// Recipient address (None for contract creation)
35    #[serde(default, deserialize_with = "deserialize_maybe_empty")]
36    pub to: Option<Address>,
37    /// Ether value to transfer (multiple variants for different test cases)
38    pub value: Vec<U256>,
39    /// Maximum fee per gas (EIP-1559 transactions)
40    pub max_fee_per_gas: Option<U256>,
41    /// Maximum priority fee per gas (EIP-1559 transactions)
42    pub max_priority_fee_per_gas: Option<U256>,
43    /// Initcodes for contract creation (EIP-7873)
44    pub initcodes: Option<Vec<Bytes>>,
45    /// Access lists for different test cases (EIP-2930)
46    #[serde(default)]
47    pub access_lists: Vec<Option<AccessList>>,
48    /// Authorization list (EIP-7702)
49    pub authorization_list: Option<Vec<TestAuthorization>>,
50    /// Blob versioned hashes (EIP-4844)
51    #[serde(default)]
52    pub blob_versioned_hashes: Vec<B256>,
53    /// Maximum fee per blob gas (EIP-4844)
54    pub max_fee_per_blob_gas: Option<U256>,
55}
56
57impl TransactionParts {
58    /// Returns the transaction type.   
59    ///
60    /// As this information is derived from the fields it is not stored in the struct.
61    ///
62    /// Returns `None` if the transaction is invalid:
63    ///   * It has both blob gas and no destination.
64    ///   * It has authorization list and no destination.
65    pub fn tx_type(&self, access_list_index: usize) -> Option<TransactionType> {
66        if let Some(tx_type) = self.tx_type {
67            return Some(TransactionType::from(tx_type));
68        }
69
70        let mut tx_type = TransactionType::Legacy;
71
72        // If it has access list it is EIP-2930 tx
73        if let Some(access_list) = self.access_lists.get(access_list_index) {
74            if access_list.is_some() {
75                tx_type = TransactionType::Eip2930;
76            }
77        }
78
79        // If there is max_fee it is EIP-1559 tx
80        if self.max_fee_per_gas.is_some() {
81            tx_type = TransactionType::Eip1559;
82        }
83
84        // If it has max_fee_per_blob_gas it is EIP-4844 tx
85        if self.max_fee_per_blob_gas.is_some() {
86            // target need to be present for EIP-4844 tx
87            self.to?;
88            return Some(TransactionType::Eip4844);
89        }
90
91        // And if it has authorization list it is EIP-7702 tx
92        if self.authorization_list.is_some() {
93            // Target need to be present for EIP-7702 tx
94            self.to?;
95            return Some(TransactionType::Eip7702);
96        }
97
98        Some(tx_type)
99    }
100}
101
102/// Transaction part indices.
103#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase", deny_unknown_fields)]
105pub struct TxPartIndices {
106    /// Index into the data array
107    pub data: usize,
108    /// Index into the gas_limit array
109    pub gas: usize,
110    /// Index into the value array
111    pub value: usize,
112}
113
114#[cfg(test)]
115mod test {
116
117    use super::*;
118
119    #[test]
120    fn decode_tx_parts() {
121        let tx = r#"{
122            "nonce": "0x00",
123            "maxPriorityFeePerGas": "0x00",
124            "maxFeePerGas": "0x07",
125            "gasLimit": [
126                "0x0423ff"
127            ],
128            "to": "0x0000000000000000000000000000000000001000",
129            "value": [
130                "0x00"
131            ],
132            "data": [
133                "0x"
134            ],
135            "accessLists": [
136                [
137                    {
138                        "address": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71",
139                        "storageKeys": [
140                            "0x0000000000000000000000000000000000000000000000000000000000000000"
141                        ]
142                    }
143                ]
144            ],
145            "authorizationList": [
146                {
147                    "chainId": "0x00",
148                    "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
149                    "nonce": "0x00",
150                    "v": "0x01",
151                    "r": "0x5a8cac98fd240d8ef83c22db4a061ffa0facb1801245283cc05fc809d8b92837",
152                    "s": "0x1c3162fe11d91bc24d4fa00fb19ca34531e0eacdf8142c804be44058d5b8244f",
153                    "signer": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71"
154                }
155            ],
156            "sender": "0x8a0a19589531694250d570040a0c4b74576919b8",
157            "secretKey": "0x9e7645d0cfd9c3a04eb7a9db59a4eb7d359f2e75c9164a9d6b9a7d54e1b6a36f"
158        }"#;
159
160        let _: TransactionParts = serde_json::from_str(tx).unwrap();
161    }
162}