reth_optimism_evm/
config.rs

1use alloy_consensus::BlockHeader;
2use op_revm::OpSpecId;
3use reth_optimism_forks::OpHardforks;
4use revm::primitives::{Address, Bytes, B256};
5
6/// Context relevant for execution of a next block w.r.t OP.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct OpNextBlockEnvAttributes {
9    /// The timestamp of the next block.
10    pub timestamp: u64,
11    /// The suggested fee recipient for the next block.
12    pub suggested_fee_recipient: Address,
13    /// The randomness value for the next block.
14    pub prev_randao: B256,
15    /// Block gas limit.
16    pub gas_limit: u64,
17    /// The parent beacon block root.
18    pub parent_beacon_block_root: Option<B256>,
19    /// Encoded EIP-1559 parameters to include into block's `extra_data` field.
20    pub extra_data: Bytes,
21}
22
23/// Map the latest active hardfork at the given header to a revm [`OpSpecId`].
24pub fn revm_spec(chain_spec: impl OpHardforks, header: impl BlockHeader) -> OpSpecId {
25    revm_spec_by_timestamp_after_bedrock(chain_spec, header.timestamp())
26}
27
28/// Returns the revm [`OpSpecId`] at the given timestamp.
29///
30/// # Note
31///
32/// This is only intended to be used after the Bedrock, when hardforks are activated by
33/// timestamp.
34pub fn revm_spec_by_timestamp_after_bedrock(
35    chain_spec: impl OpHardforks,
36    timestamp: u64,
37) -> OpSpecId {
38    if chain_spec.is_interop_active_at_timestamp(timestamp) {
39        OpSpecId::INTEROP
40    } else if chain_spec.is_isthmus_active_at_timestamp(timestamp) {
41        OpSpecId::ISTHMUS
42    } else if chain_spec.is_holocene_active_at_timestamp(timestamp) {
43        OpSpecId::HOLOCENE
44    } else if chain_spec.is_granite_active_at_timestamp(timestamp) {
45        OpSpecId::GRANITE
46    } else if chain_spec.is_fjord_active_at_timestamp(timestamp) {
47        OpSpecId::FJORD
48    } else if chain_spec.is_ecotone_active_at_timestamp(timestamp) {
49        OpSpecId::ECOTONE
50    } else if chain_spec.is_canyon_active_at_timestamp(timestamp) {
51        OpSpecId::CANYON
52    } else if chain_spec.is_regolith_active_at_timestamp(timestamp) {
53        OpSpecId::REGOLITH
54    } else {
55        OpSpecId::BEDROCK
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use alloy_consensus::Header;
63    use reth_chainspec::ChainSpecBuilder;
64    use reth_optimism_chainspec::{OpChainSpec, OpChainSpecBuilder};
65
66    #[test]
67    fn test_revm_spec_by_timestamp_after_merge() {
68        #[inline(always)]
69        fn op_cs(f: impl FnOnce(OpChainSpecBuilder) -> OpChainSpecBuilder) -> OpChainSpec {
70            let cs = ChainSpecBuilder::mainnet().chain(reth_chainspec::Chain::from_id(10)).into();
71            f(cs).build()
72        }
73        assert_eq!(
74            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.interop_activated()), 0),
75            OpSpecId::INTEROP
76        );
77        assert_eq!(
78            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.isthmus_activated()), 0),
79            OpSpecId::ISTHMUS
80        );
81        assert_eq!(
82            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.holocene_activated()), 0),
83            OpSpecId::HOLOCENE
84        );
85        assert_eq!(
86            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.granite_activated()), 0),
87            OpSpecId::GRANITE
88        );
89        assert_eq!(
90            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.fjord_activated()), 0),
91            OpSpecId::FJORD
92        );
93        assert_eq!(
94            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.ecotone_activated()), 0),
95            OpSpecId::ECOTONE
96        );
97        assert_eq!(
98            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.canyon_activated()), 0),
99            OpSpecId::CANYON
100        );
101        assert_eq!(
102            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.bedrock_activated()), 0),
103            OpSpecId::BEDROCK
104        );
105        assert_eq!(
106            revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.regolith_activated()), 0),
107            OpSpecId::REGOLITH
108        );
109    }
110
111    #[test]
112    fn test_to_revm_spec() {
113        #[inline(always)]
114        fn op_cs(f: impl FnOnce(OpChainSpecBuilder) -> OpChainSpecBuilder) -> OpChainSpec {
115            let cs = ChainSpecBuilder::mainnet().chain(reth_chainspec::Chain::from_id(10)).into();
116            f(cs).build()
117        }
118        assert_eq!(
119            revm_spec(op_cs(|cs| cs.isthmus_activated()), Header::default()),
120            OpSpecId::ISTHMUS
121        );
122        assert_eq!(
123            revm_spec(op_cs(|cs| cs.holocene_activated()), Header::default()),
124            OpSpecId::HOLOCENE
125        );
126        assert_eq!(
127            revm_spec(op_cs(|cs| cs.granite_activated()), Header::default()),
128            OpSpecId::GRANITE
129        );
130        assert_eq!(revm_spec(op_cs(|cs| cs.fjord_activated()), Header::default()), OpSpecId::FJORD);
131        assert_eq!(
132            revm_spec(op_cs(|cs| cs.ecotone_activated()), Header::default()),
133            OpSpecId::ECOTONE
134        );
135        assert_eq!(
136            revm_spec(op_cs(|cs| cs.canyon_activated()), Header::default()),
137            OpSpecId::CANYON
138        );
139        assert_eq!(
140            revm_spec(op_cs(|cs| cs.bedrock_activated()), Header::default()),
141            OpSpecId::BEDROCK
142        );
143        assert_eq!(
144            revm_spec(op_cs(|cs| cs.regolith_activated()), Header::default()),
145            OpSpecId::REGOLITH
146        );
147    }
148}