Skip to main content

ethrex_levm/opcode_handlers/
block.rs

1//! # Block operations
2//!
3//! Includes the following opcodes:
4//!   - `BLOCKHASH`
5//!   - `COINBASE`
6//!   - `TIMESTAMP`
7//!   - `NUMBER`
8//!   - `PREVRANDAO`
9//!   - `GASLIMIT`
10//!   - `CHAINID`
11//!   - `SELFBALANCE`
12//!   - `BASEFEE`
13//!   - `BLOBHASH`
14//!   - `BLOBBASEFEE`
15
16use std::mem;
17
18use crate::{
19    constants::LAST_AVAILABLE_BLOCK_LIMIT,
20    errors::{OpcodeResult, VMError},
21    gas_cost,
22    opcode_handlers::OpcodeHandler,
23    utils::*,
24    vm::VM,
25};
26use ethrex_common::U256;
27
28/// Implementation for the `BLOCKHASH` opcode.
29pub struct OpBlockHashHandler;
30impl OpcodeHandler for OpBlockHashHandler {
31    #[inline(always)]
32    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
33        vm.current_call_frame
34            .increase_consumed_gas(gas_cost::BLOCKHASH)?;
35
36        // Some(_) if
37        //   - is u64
38        //   - 0 < current_number - block_number <= LAST_AVAILABLE_BLOCK_LIMIT
39        #[expect(
40            clippy::arithmetic_side_effects,
41            reason = "subtraction is guarded by take_if range check"
42        )]
43        if let Some(block_number) = u64::try_from(vm.current_call_frame.stack.pop1()?)
44            .ok()
45            .take_if(|&mut block_number| {
46                block_number < vm.env.block_number
47                    && vm.env.block_number - block_number <= LAST_AVAILABLE_BLOCK_LIMIT
48            })
49        {
50            #[expect(unsafe_code, reason = "safe")]
51            vm.current_call_frame.stack.push(unsafe {
52                let mut bytes = vm.db.store.get_block_hash(block_number)?.0;
53                bytes.reverse();
54                U256(mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes))
55            })?;
56        } else {
57            vm.current_call_frame.stack.push_zero()?;
58        }
59
60        Ok(OpcodeResult::Continue)
61    }
62}
63
64/// Implementation for the `COINBASE` opcode.
65pub struct OpCoinbaseHandler;
66impl OpcodeHandler for OpCoinbaseHandler {
67    #[inline(always)]
68    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
69        vm.current_call_frame
70            .increase_consumed_gas(gas_cost::COINBASE)?;
71
72        vm.current_call_frame
73            .stack
74            .push(address_to_word(vm.env.coinbase))?;
75
76        Ok(OpcodeResult::Continue)
77    }
78}
79
80/// Implementation for the `TIMESTAMP` opcode.
81pub struct OpTimestampHandler;
82impl OpcodeHandler for OpTimestampHandler {
83    #[inline(always)]
84    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
85        vm.current_call_frame
86            .increase_consumed_gas(gas_cost::TIMESTAMP)?;
87
88        vm.current_call_frame.stack.push(vm.env.timestamp.into())?;
89
90        Ok(OpcodeResult::Continue)
91    }
92}
93
94/// Implementation for the `NUMBER` opcode.
95pub struct OpNumberHandler;
96impl OpcodeHandler for OpNumberHandler {
97    #[inline(always)]
98    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
99        vm.current_call_frame
100            .increase_consumed_gas(gas_cost::NUMBER)?;
101
102        vm.current_call_frame
103            .stack
104            .push(vm.env.block_number.into())?;
105
106        Ok(OpcodeResult::Continue)
107    }
108}
109
110/// Implementation for the `PREVRANDAO` opcode.
111pub struct OpPrevRandaoHandler;
112impl OpcodeHandler for OpPrevRandaoHandler {
113    #[inline(always)]
114    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
115        vm.current_call_frame
116            .increase_consumed_gas(gas_cost::PREVRANDAO)?;
117
118        // After Paris, `PREVRANDAO` is the prev_randao (or current_random) field.
119        // Source: https://eips.ethereum.org/EIPS/eip-4399
120        #[expect(unsafe_code, reason = "safe")]
121        vm.current_call_frame.stack.push(U256(unsafe {
122            let mut bytes = vm.env.prev_randao.unwrap_or_default().0;
123            bytes.reverse();
124            mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
125        }))?;
126
127        Ok(OpcodeResult::Continue)
128    }
129}
130
131/// Implementation for the `GASLIMIT` opcode.
132pub struct OpGasLimitHandler;
133impl OpcodeHandler for OpGasLimitHandler {
134    #[inline(always)]
135    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
136        vm.current_call_frame
137            .increase_consumed_gas(gas_cost::GASLIMIT)?;
138
139        vm.current_call_frame
140            .stack
141            .push(vm.env.block_gas_limit.into())?;
142
143        Ok(OpcodeResult::Continue)
144    }
145}
146
147/// Implementation for the `CHAINID` opcode.
148pub struct OpChainIdHandler;
149impl OpcodeHandler for OpChainIdHandler {
150    #[inline(always)]
151    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
152        vm.current_call_frame
153            .increase_consumed_gas(gas_cost::CHAINID)?;
154
155        vm.current_call_frame.stack.push(vm.env.chain_id)?;
156
157        Ok(OpcodeResult::Continue)
158    }
159}
160
161/// Implementation for the `SELFBALANCE` opcode.
162pub struct OpSelfBalanceHandler;
163impl OpcodeHandler for OpSelfBalanceHandler {
164    #[inline(always)]
165    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
166        vm.current_call_frame
167            .increase_consumed_gas(gas_cost::SELFBALANCE)?;
168
169        let address = vm.current_call_frame.to;
170        let balance = vm.db.get_account(address)?.info.balance;
171
172        // Record address touch for BAL per EIP-7928
173        // SELFBALANCE has "Pre-state Cost: None" so always succeeds
174        if let Some(recorder) = vm.db.bal_recorder.as_mut() {
175            recorder.record_touched_address(address);
176        }
177
178        vm.current_call_frame.stack.push(balance)?;
179
180        Ok(OpcodeResult::Continue)
181    }
182}
183
184/// Implementation for the `BASEFEE` opcode.
185pub struct OpBaseFeeHandler;
186impl OpcodeHandler for OpBaseFeeHandler {
187    #[inline(always)]
188    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
189        vm.current_call_frame
190            .increase_consumed_gas(gas_cost::BASEFEE)?;
191
192        // https://eips.ethereum.org/EIPS/eip-3198
193        vm.current_call_frame.stack.push(vm.env.base_fee_per_gas)?;
194
195        Ok(OpcodeResult::Continue)
196    }
197}
198
199/// Implementation for the `BLOBHASH` opcode.
200pub struct OpBlobHashHandler;
201impl OpcodeHandler for OpBlobHashHandler {
202    #[inline(always)]
203    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
204        vm.current_call_frame
205            .increase_consumed_gas(gas_cost::BLOBHASH)?;
206
207        match usize::try_from(vm.current_call_frame.stack.pop1()?)
208            .ok()
209            .and_then(|index| vm.env.tx_blob_hashes.get(index))
210        {
211            Some(hash) =>
212            {
213                #[expect(unsafe_code, reason = "safe")]
214                vm.current_call_frame.stack.push(U256(unsafe {
215                    let mut bytes = hash.0;
216                    bytes.reverse();
217                    mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
218                }))?
219            }
220            None => vm.current_call_frame.stack.push_zero()?,
221        }
222
223        Ok(OpcodeResult::Continue)
224    }
225}
226
227/// Implementation for the `BLOBBASEFEE` opcode.
228pub struct OpBlobBaseFeeHandler;
229impl OpcodeHandler for OpBlobBaseFeeHandler {
230    #[inline(always)]
231    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
232        vm.current_call_frame
233            .increase_consumed_gas(gas_cost::BLOBBASEFEE)?;
234
235        vm.current_call_frame
236            .stack
237            .push(vm.env.base_blob_fee_per_gas)?;
238
239        Ok(OpcodeResult::Continue)
240    }
241}
242
243/// Implementation for the `SLOTNUM` opcode.
244pub struct OpSlotNumHandler;
245impl OpcodeHandler for OpSlotNumHandler {
246    #[inline(always)]
247    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
248        // EIP-7843: Returns the slot number of the current block
249        vm.current_call_frame
250            .increase_consumed_gas(gas_cost::SLOTNUM)?;
251
252        vm.current_call_frame.stack.push(vm.env.slot_number)?;
253
254        Ok(OpcodeResult::Continue)
255    }
256}