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