ef_tests/
models.rs

1//! Shared models for <https://github.com/ethereum/tests>
2
3use crate::{assert::assert_equal, Error};
4use alloy_consensus::Header as RethHeader;
5use alloy_eips::eip4895::Withdrawals;
6use alloy_primitives::{keccak256, Address, Bloom, Bytes, B256, B64, U256};
7use reth_chainspec::{ChainSpec, ChainSpecBuilder};
8use reth_db::tables;
9use reth_db_api::{
10    cursor::DbDupCursorRO,
11    transaction::{DbTx, DbTxMut},
12};
13use reth_primitives::{Account as RethAccount, Bytecode, SealedHeader, StorageEntry};
14use serde::Deserialize;
15use std::{collections::BTreeMap, ops::Deref};
16
17/// The definition of a blockchain test.
18#[derive(Debug, PartialEq, Eq, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct BlockchainTest {
21    /// Genesis block header.
22    pub genesis_block_header: Header,
23    /// RLP encoded genesis block.
24    #[serde(rename = "genesisRLP")]
25    pub genesis_rlp: Option<Bytes>,
26    /// Block data.
27    pub blocks: Vec<Block>,
28    /// The expected post state.
29    pub post_state: Option<BTreeMap<Address, Account>>,
30    /// The expected post state merkle root.
31    pub post_state_hash: Option<B256>,
32    /// The test pre-state.
33    pub pre: State,
34    /// Hash of the best block.
35    pub lastblockhash: B256,
36    /// Network spec.
37    pub network: ForkSpec,
38    #[serde(default)]
39    /// Engine spec.
40    pub seal_engine: SealEngine,
41}
42
43/// A block header in an Ethereum blockchain test.
44#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Default)]
45#[serde(rename_all = "camelCase")]
46pub struct Header {
47    /// Bloom filter.
48    pub bloom: Bloom,
49    /// Coinbase.
50    pub coinbase: Address,
51    /// Difficulty.
52    pub difficulty: U256,
53    /// Extra data.
54    pub extra_data: Bytes,
55    /// Gas limit.
56    pub gas_limit: U256,
57    /// Gas used.
58    pub gas_used: U256,
59    /// Block Hash.
60    pub hash: B256,
61    /// Mix hash.
62    pub mix_hash: B256,
63    /// Seal nonce.
64    pub nonce: B64,
65    /// Block number.
66    pub number: U256,
67    /// Parent hash.
68    pub parent_hash: B256,
69    /// Receipt trie.
70    pub receipt_trie: B256,
71    /// State root.
72    pub state_root: B256,
73    /// Timestamp.
74    pub timestamp: U256,
75    /// Transactions trie.
76    pub transactions_trie: B256,
77    /// Uncle hash.
78    pub uncle_hash: B256,
79    /// Base fee per gas.
80    pub base_fee_per_gas: Option<U256>,
81    /// Withdrawals root.
82    pub withdrawals_root: Option<B256>,
83    /// Blob gas used.
84    pub blob_gas_used: Option<U256>,
85    /// Excess blob gas.
86    pub excess_blob_gas: Option<U256>,
87    /// Parent beacon block root.
88    pub parent_beacon_block_root: Option<B256>,
89    /// Requests root.
90    pub requests_hash: Option<B256>,
91    /// Target blobs per block.
92    pub target_blobs_per_block: Option<U256>,
93}
94
95impl From<Header> for SealedHeader {
96    fn from(value: Header) -> Self {
97        let header = RethHeader {
98            base_fee_per_gas: value.base_fee_per_gas.map(|v| v.to::<u64>()),
99            beneficiary: value.coinbase,
100            difficulty: value.difficulty,
101            extra_data: value.extra_data,
102            gas_limit: value.gas_limit.to::<u64>(),
103            gas_used: value.gas_used.to::<u64>(),
104            mix_hash: value.mix_hash,
105            nonce: u64::from_be_bytes(value.nonce.0).into(),
106            number: value.number.to::<u64>(),
107            timestamp: value.timestamp.to::<u64>(),
108            transactions_root: value.transactions_trie,
109            receipts_root: value.receipt_trie,
110            ommers_hash: value.uncle_hash,
111            state_root: value.state_root,
112            parent_hash: value.parent_hash,
113            logs_bloom: value.bloom,
114            withdrawals_root: value.withdrawals_root,
115            blob_gas_used: value.blob_gas_used.map(|v| v.to::<u64>()),
116            excess_blob_gas: value.excess_blob_gas.map(|v| v.to::<u64>()),
117            parent_beacon_block_root: value.parent_beacon_block_root,
118            requests_hash: value.requests_hash,
119            target_blobs_per_block: value.target_blobs_per_block.map(|v| v.to::<u64>()),
120        };
121        Self::new(header, value.hash)
122    }
123}
124
125/// A block in an Ethereum blockchain test.
126#[derive(Debug, PartialEq, Eq, Deserialize, Default)]
127#[serde(rename_all = "camelCase")]
128pub struct Block {
129    /// Block header.
130    pub block_header: Option<Header>,
131    /// RLP encoded block bytes
132    pub rlp: Bytes,
133    /// Transactions
134    pub transactions: Option<Vec<Transaction>>,
135    /// Uncle/ommer headers
136    pub uncle_headers: Option<Vec<Header>>,
137    /// Transaction Sequence
138    pub transaction_sequence: Option<Vec<TransactionSequence>>,
139    /// Withdrawals
140    pub withdrawals: Option<Withdrawals>,
141}
142
143/// Transaction sequence in block
144#[derive(Debug, PartialEq, Eq, Deserialize, Default)]
145#[serde(deny_unknown_fields)]
146#[serde(rename_all = "camelCase")]
147pub struct TransactionSequence {
148    exception: String,
149    raw_bytes: Bytes,
150    valid: String,
151}
152
153/// Ethereum blockchain test data state.
154#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Default)]
155pub struct State(BTreeMap<Address, Account>);
156
157impl State {
158    /// Write the state to the database.
159    pub fn write_to_db(&self, tx: &impl DbTxMut) -> Result<(), Error> {
160        for (&address, account) in &self.0 {
161            let hashed_address = keccak256(address);
162            let has_code = !account.code.is_empty();
163            let code_hash = has_code.then(|| keccak256(&account.code));
164            let reth_account = RethAccount {
165                balance: account.balance,
166                nonce: account.nonce.to::<u64>(),
167                bytecode_hash: code_hash,
168            };
169            tx.put::<tables::PlainAccountState>(address, reth_account)?;
170            tx.put::<tables::HashedAccounts>(hashed_address, reth_account)?;
171
172            if let Some(code_hash) = code_hash {
173                tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(account.code.clone()))?;
174            }
175
176            for (k, v) in &account.storage {
177                if v.is_zero() {
178                    continue
179                }
180                let storage_key = B256::from_slice(&k.to_be_bytes::<32>());
181                tx.put::<tables::PlainStorageState>(
182                    address,
183                    StorageEntry { key: storage_key, value: *v, ..Default::default() },
184                )?;
185                tx.put::<tables::HashedStorages>(
186                    hashed_address,
187                    StorageEntry { key: keccak256(storage_key), value: *v, ..Default::default() },
188                )?;
189            }
190        }
191        Ok(())
192    }
193}
194
195impl Deref for State {
196    type Target = BTreeMap<Address, Account>;
197
198    fn deref(&self) -> &Self::Target {
199        &self.0
200    }
201}
202
203/// An account.
204#[derive(Debug, PartialEq, Eq, Deserialize, Clone, Default)]
205#[serde(deny_unknown_fields)]
206pub struct Account {
207    /// Balance.
208    pub balance: U256,
209    /// Code.
210    pub code: Bytes,
211    /// Nonce.
212    pub nonce: U256,
213    /// Storage.
214    pub storage: BTreeMap<U256, U256>,
215}
216
217impl Account {
218    /// Check that the account matches what is in the database.
219    ///
220    /// In case of a mismatch, `Err(Error::Assertion)` is returned.
221    pub fn assert_db(&self, address: Address, tx: &impl DbTx) -> Result<(), Error> {
222        let account =
223            tx.get_by_encoded_key::<tables::PlainAccountState>(&address)?.ok_or_else(|| {
224                Error::Assertion(format!(
225                    "Expected account ({address}) is missing from DB: {self:?}"
226                ))
227            })?;
228
229        assert_equal(self.balance, account.balance, "Balance does not match")?;
230        assert_equal(self.nonce.to(), account.nonce, "Nonce does not match")?;
231
232        if let Some(bytecode_hash) = account.bytecode_hash {
233            assert_equal(keccak256(&self.code), bytecode_hash, "Bytecode does not match")?;
234        } else {
235            assert_equal(
236                self.code.is_empty(),
237                true,
238                "Expected empty bytecode, got bytecode in db.",
239            )?;
240        }
241
242        let mut storage_cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
243        for (slot, value) in &self.storage {
244            if let Some(entry) =
245                storage_cursor.seek_by_key_subkey(address, B256::new(slot.to_be_bytes()))?
246            {
247                if U256::from_be_bytes(entry.key.0) == *slot {
248                    assert_equal(
249                        *value,
250                        entry.value,
251                        &format!("Storage for slot {slot:?} does not match"),
252                    )?;
253                } else {
254                    return Err(Error::Assertion(format!(
255                        "Slot {slot:?} is missing from the database. Expected {value:?}"
256                    )))
257                }
258            } else {
259                return Err(Error::Assertion(format!(
260                    "Slot {slot:?} is missing from the database. Expected {value:?}"
261                )))
262            }
263        }
264
265        Ok(())
266    }
267}
268
269/// Fork specification.
270#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Ord, Clone, Copy, Deserialize)]
271pub enum ForkSpec {
272    /// Frontier
273    Frontier,
274    /// Frontier to Homestead
275    FrontierToHomesteadAt5,
276    /// Homestead
277    Homestead,
278    /// Homestead to Tangerine
279    HomesteadToDaoAt5,
280    /// Homestead to Tangerine
281    HomesteadToEIP150At5,
282    /// Tangerine
283    EIP150,
284    /// Spurious Dragon
285    EIP158, // EIP-161: State trie clearing
286    /// Spurious Dragon to Byzantium
287    EIP158ToByzantiumAt5,
288    /// Byzantium
289    Byzantium,
290    /// Byzantium to Constantinople
291    ByzantiumToConstantinopleAt5, // SKIPPED
292    /// Byzantium to Constantinople
293    ByzantiumToConstantinopleFixAt5,
294    /// Constantinople
295    Constantinople, // SKIPPED
296    /// Constantinople fix
297    ConstantinopleFix,
298    /// Istanbul
299    Istanbul,
300    /// Berlin
301    Berlin,
302    /// Berlin to London
303    BerlinToLondonAt5,
304    /// London
305    London,
306    /// Paris aka The Merge
307    Merge,
308    /// Shanghai
309    Shanghai,
310    /// Merge EOF test
311    #[serde(alias = "Merge+3540+3670")]
312    MergeEOF,
313    /// After Merge Init Code test
314    #[serde(alias = "Merge+3860")]
315    MergeMeterInitCode,
316    /// After Merge plus new PUSH0 opcode
317    #[serde(alias = "Merge+3855")]
318    MergePush0,
319    /// Cancun
320    Cancun,
321    /// Fork Spec which is unknown to us
322    #[serde(other)]
323    Unknown,
324}
325
326impl From<ForkSpec> for ChainSpec {
327    fn from(fork_spec: ForkSpec) -> Self {
328        let spec_builder = ChainSpecBuilder::mainnet();
329
330        match fork_spec {
331            ForkSpec::Frontier => spec_builder.frontier_activated(),
332            ForkSpec::Homestead | ForkSpec::FrontierToHomesteadAt5 => {
333                spec_builder.homestead_activated()
334            }
335            ForkSpec::EIP150 | ForkSpec::HomesteadToDaoAt5 | ForkSpec::HomesteadToEIP150At5 => {
336                spec_builder.tangerine_whistle_activated()
337            }
338            ForkSpec::EIP158 => spec_builder.spurious_dragon_activated(),
339            ForkSpec::Byzantium |
340            ForkSpec::EIP158ToByzantiumAt5 |
341            ForkSpec::ConstantinopleFix |
342            ForkSpec::ByzantiumToConstantinopleFixAt5 => spec_builder.byzantium_activated(),
343            ForkSpec::Istanbul => spec_builder.istanbul_activated(),
344            ForkSpec::Berlin => spec_builder.berlin_activated(),
345            ForkSpec::London | ForkSpec::BerlinToLondonAt5 => spec_builder.london_activated(),
346            ForkSpec::Merge |
347            ForkSpec::MergeEOF |
348            ForkSpec::MergeMeterInitCode |
349            ForkSpec::MergePush0 => spec_builder.paris_activated(),
350            ForkSpec::Shanghai => spec_builder.shanghai_activated(),
351            ForkSpec::Cancun => spec_builder.cancun_activated(),
352            ForkSpec::ByzantiumToConstantinopleAt5 | ForkSpec::Constantinople => {
353                panic!("Overridden with PETERSBURG")
354            }
355            ForkSpec::Unknown => {
356                panic!("Unknown fork");
357            }
358        }
359        .build()
360    }
361}
362
363/// Possible seal engines.
364#[derive(Debug, PartialEq, Eq, Default, Deserialize)]
365pub enum SealEngine {
366    /// No consensus checks.
367    #[default]
368    NoProof,
369}
370
371/// Ethereum blockchain test transaction data.
372#[derive(Debug, PartialEq, Eq, Deserialize)]
373#[serde(rename_all = "camelCase")]
374pub struct Transaction {
375    /// Transaction type
376    #[serde(rename = "type")]
377    pub transaction_type: Option<U256>,
378    /// Data.
379    pub data: Bytes,
380    /// Gas limit.
381    pub gas_limit: U256,
382    /// Gas price.
383    pub gas_price: Option<U256>,
384    /// Nonce.
385    pub nonce: U256,
386    /// Signature r part.
387    pub r: U256,
388    /// Signature s part.
389    pub s: U256,
390    /// Parity bit.
391    pub v: U256,
392    /// Transaction value.
393    pub value: U256,
394    /// Chain ID.
395    pub chain_id: Option<U256>,
396    /// Access list.
397    pub access_list: Option<AccessList>,
398    /// Max fee per gas.
399    pub max_fee_per_gas: Option<U256>,
400    /// Max priority fee per gas
401    pub max_priority_fee_per_gas: Option<U256>,
402    /// Transaction hash.
403    pub hash: Option<B256>,
404}
405
406/// Access list item
407#[derive(Debug, PartialEq, Eq, Deserialize, Clone)]
408#[serde(rename_all = "camelCase")]
409pub struct AccessListItem {
410    /// Account address
411    pub address: Address,
412    /// Storage key.
413    pub storage_keys: Vec<B256>,
414}
415
416/// Access list.
417pub type AccessList = Vec<AccessListItem>;
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422
423    #[test]
424    fn header_deserialize() {
425        let test = r#"{
426            "baseFeePerGas" : "0x0a",
427            "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
428            "coinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
429            "difficulty" : "0x020000",
430            "extraData" : "0x00",
431            "gasLimit" : "0x10000000000000",
432            "gasUsed" : "0x10000000000000",
433            "hash" : "0x7ebfee2a2c785fef181b8ffd92d4a48a0660ec000f465f309757e3f092d13882",
434            "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
435            "nonce" : "0x0000000000000000",
436            "number" : "0x01",
437            "parentHash" : "0xa8f2eb2ea9dccbf725801eef5a31ce59bada431e888dfd5501677cc4365dc3be",
438            "receiptTrie" : "0xbdd943f5c62ae0299324244a0f65524337ada9817e18e1764631cc1424f3a293",
439            "stateRoot" : "0xc9c6306ee3e5acbaabe8e2fa28a10c12e27bad1d1aacc271665149f70519f8b0",
440            "timestamp" : "0x03e8",
441            "transactionsTrie" : "0xf5893b055ca05e4f14d1792745586a1376e218180bd56bd96b2b024e1dc78300",
442            "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
443        }"#;
444        let res = serde_json::from_str::<Header>(test);
445        assert!(res.is_ok(), "Failed to deserialize Header with error: {res:?}");
446    }
447
448    #[test]
449    fn transaction_deserialize() {
450        let test = r#"[
451            {
452                "accessList" : [
453                ],
454                "chainId" : "0x01",
455                "data" : "0x693c61390000000000000000000000000000000000000000000000000000000000000000",
456                "gasLimit" : "0x10000000000000",
457                "maxFeePerGas" : "0x07d0",
458                "maxPriorityFeePerGas" : "0x00",
459                "nonce" : "0x01",
460                "r" : "0x5fecc3972a35c9e341b41b0c269d9a7325e13269fb01c2f64cbce1046b3441c8",
461                "s" : "0x7d4d0eda0e4ebd53c5d0b6fc35c600b317f8fa873b3963ab623ec9cec7d969bd",
462                "sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
463                "to" : "0xcccccccccccccccccccccccccccccccccccccccc",
464                "type" : "0x02",
465                "v" : "0x01",
466                "value" : "0x00"
467            }
468        ]"#;
469
470        let res = serde_json::from_str::<Vec<Transaction>>(test);
471        assert!(res.is_ok(), "Failed to deserialize transaction with error: {res:?}");
472    }
473}