ethrex_levm/opcode_handlers/
environment.rs1use crate::{
22 errors::{ExceptionalHalt, OpcodeResult, VMError},
23 gas_cost::{self},
24 memory::calculate_memory_size,
25 opcode_handlers::OpcodeHandler,
26 utils::{size_offset_to_usize, u256_to_usize, word_to_address},
27 vm::VM,
28};
29use ethrex_common::U256;
30use std::mem;
31
32pub struct OpAddressHandler;
34impl OpcodeHandler for OpAddressHandler {
35 #[inline(always)]
36 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
37 vm.current_call_frame
38 .increase_consumed_gas(gas_cost::ADDRESS)?;
39
40 #[expect(unsafe_code, reason = "safe")]
41 vm.current_call_frame.stack.push(U256(unsafe {
42 let mut bytes: [u8; 32] = [0; 32];
43 bytes[12..].copy_from_slice(&vm.current_call_frame.to.0);
44 bytes.reverse();
45 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
46 }))?;
47
48 Ok(OpcodeResult::Continue)
49 }
50}
51
52pub struct OpBalanceHandler;
54impl OpcodeHandler for OpBalanceHandler {
55 #[inline(always)]
56 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
57 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
58 vm.current_call_frame
59 .increase_consumed_gas(gas_cost::balance(
60 vm.substate.add_accessed_address(address),
61 )?)?;
62
63 let account_balance = vm.db.get_account(address)?.info.balance;
65
66 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
68 recorder.record_touched_address(address);
69 }
70
71 vm.current_call_frame.stack.push(account_balance)?;
72
73 Ok(OpcodeResult::Continue)
74 }
75}
76
77pub struct OpOriginHandler;
79impl OpcodeHandler for OpOriginHandler {
80 #[inline(always)]
81 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
82 vm.current_call_frame
83 .increase_consumed_gas(gas_cost::ORIGIN)?;
84
85 #[expect(unsafe_code, reason = "safe")]
86 vm.current_call_frame.stack.push(U256(unsafe {
87 let mut bytes: [u8; 32] = [0; 32];
88 bytes[12..].copy_from_slice(&vm.env.origin.0);
89 bytes.reverse();
90 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
91 }))?;
92
93 Ok(OpcodeResult::Continue)
94 }
95}
96
97pub struct OpGasPriceHandler;
99impl OpcodeHandler for OpGasPriceHandler {
100 #[inline(always)]
101 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
102 vm.current_call_frame
103 .increase_consumed_gas(gas_cost::GASPRICE)?;
104
105 vm.current_call_frame.stack.push(vm.env.gas_price)?;
106
107 Ok(OpcodeResult::Continue)
108 }
109}
110
111pub struct OpCallerHandler;
113impl OpcodeHandler for OpCallerHandler {
114 #[inline(always)]
115 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
116 vm.current_call_frame
117 .increase_consumed_gas(gas_cost::CALLER)?;
118
119 #[expect(unsafe_code, reason = "safe")]
120 vm.current_call_frame.stack.push(U256(unsafe {
121 let mut bytes: [u8; 32] = [0; 32];
122 bytes[12..].copy_from_slice(&vm.current_call_frame.msg_sender.0);
123 bytes.reverse();
124 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
125 }))?;
126
127 Ok(OpcodeResult::Continue)
128 }
129}
130
131pub struct OpCallValueHandler;
133impl OpcodeHandler for OpCallValueHandler {
134 #[inline(always)]
135 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
136 vm.current_call_frame
137 .increase_consumed_gas(gas_cost::CALLVALUE)?;
138
139 vm.current_call_frame
140 .stack
141 .push(vm.current_call_frame.msg_value)?;
142
143 Ok(OpcodeResult::Continue)
144 }
145}
146
147pub struct OpCallDataLoadHandler;
149impl OpcodeHandler for OpCallDataLoadHandler {
150 #[inline(always)]
151 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
152 vm.current_call_frame
153 .increase_consumed_gas(gas_cost::CALLDATALOAD)?;
154
155 let value_bytes = usize::try_from(vm.current_call_frame.stack.pop1()?)
156 .ok()
157 .and_then(|offset| vm.current_call_frame.calldata.get(offset..));
158 #[expect(clippy::indexing_slicing, reason = "length is checked in match guard")]
159 vm.current_call_frame.stack.push(match value_bytes {
160 Some(data) if data.len() >= 32 => U256::from_big_endian(&data[..32]),
161 Some(data) => {
162 let mut bytes = [0; 32];
163 bytes[..data.len()].copy_from_slice(data);
164 U256::from_big_endian(&bytes)
165 }
166 None => U256::zero(),
167 })?;
168
169 Ok(OpcodeResult::Continue)
170 }
171}
172
173pub struct OpCallDataSizeHandler;
175impl OpcodeHandler for OpCallDataSizeHandler {
176 #[inline(always)]
177 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
178 vm.current_call_frame
179 .increase_consumed_gas(gas_cost::CALLDATASIZE)?;
180
181 vm.current_call_frame
182 .stack
183 .push(U256::from(vm.current_call_frame.calldata.len()))?;
184
185 Ok(OpcodeResult::Continue)
186 }
187}
188
189pub struct OpCallDataCopyHandler;
191impl OpcodeHandler for OpCallDataCopyHandler {
192 #[inline(always)]
193 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
194 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
195 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
196 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
197
198 vm.current_call_frame
199 .increase_consumed_gas(gas_cost::calldatacopy(
200 calculate_memory_size(dst_offset, len)?,
201 vm.current_call_frame.memory.len(),
202 len,
203 )?)?;
204
205 if len > 0 {
206 let data = vm
207 .current_call_frame
208 .calldata
209 .get(src_offset..)
210 .unwrap_or_default();
211 let data = data.get(..len).unwrap_or(data);
212
213 vm.current_call_frame.memory.store_data(dst_offset, data)?;
214 if data.len() < len {
215 #[expect(
216 clippy::arithmetic_side_effects,
217 reason = "data.len() < len guard ensures no underflow"
218 )]
219 vm.current_call_frame
220 .memory
221 .store_zeros(dst_offset + data.len(), len - data.len())?;
222 }
223 }
224
225 Ok(OpcodeResult::Continue)
226 }
227}
228
229pub struct OpCodeSizeHandler;
231impl OpcodeHandler for OpCodeSizeHandler {
232 #[inline(always)]
233 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
234 vm.current_call_frame
235 .increase_consumed_gas(gas_cost::CODESIZE)?;
236
237 vm.current_call_frame
238 .stack
239 .push(vm.current_call_frame.bytecode.len().into())?;
240
241 Ok(OpcodeResult::Continue)
242 }
243}
244
245pub struct OpCodeCopyHandler;
247impl OpcodeHandler for OpCodeCopyHandler {
248 #[inline(always)]
249 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
250 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
251 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
252 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
253
254 vm.current_call_frame
255 .increase_consumed_gas(gas_cost::codecopy(
256 calculate_memory_size(dst_offset, len)?,
257 vm.current_call_frame.memory.len(),
258 len,
259 )?)?;
260
261 if len > 0 {
262 let data = vm
263 .current_call_frame
264 .bytecode
265 .dispatch_buf()
266 .get(src_offset..)
267 .unwrap_or_default();
268 let data = data.get(..len).unwrap_or(data);
269
270 vm.current_call_frame.memory.store_data(dst_offset, data)?;
271 if data.len() < len {
272 #[expect(
273 clippy::arithmetic_side_effects,
274 reason = "data.len() < len guard ensures no underflow"
275 )]
276 vm.current_call_frame
277 .memory
278 .store_zeros(dst_offset + data.len(), len - data.len())?;
279 }
280 }
281
282 Ok(OpcodeResult::Continue)
283 }
284}
285
286pub struct OpExtCodeSizeHandler;
288impl OpcodeHandler for OpExtCodeSizeHandler {
289 #[inline(always)]
290 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
291 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
292 vm.current_call_frame
293 .increase_consumed_gas(gas_cost::extcodesize(
294 vm.substate.add_accessed_address(address),
295 )?)?;
296
297 let account_code_length = vm.db.get_code_length(address)?.into();
299
300 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
302 recorder.record_touched_address(address);
303 }
304
305 vm.current_call_frame.stack.push(account_code_length)?;
306
307 Ok(OpcodeResult::Continue)
308 }
309}
310
311pub struct OpExtCodeCopyHandler;
313impl OpcodeHandler for OpExtCodeCopyHandler {
314 #[inline(always)]
315 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
316 let [address, dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
317 let address = word_to_address(address);
318 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
319 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
320
321 vm.current_call_frame
322 .increase_consumed_gas(gas_cost::extcodecopy(
323 len,
324 calculate_memory_size(dst_offset, len)?,
325 vm.current_call_frame.memory.len(),
326 vm.substate.add_accessed_address(address),
327 )?)?;
328
329 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
331 recorder.record_touched_address(address);
332 }
333
334 let code = vm.db.get_account_code(address)?;
338
339 if len > 0 {
340 let data = code.dispatch_buf().get(src_offset..).unwrap_or_default();
341 let data = data.get(..len).unwrap_or(data);
342
343 vm.current_call_frame.memory.store_data(dst_offset, data)?;
344 if data.len() < len {
345 #[expect(
346 clippy::arithmetic_side_effects,
347 reason = "data.len() < len guard ensures no underflow"
348 )]
349 vm.current_call_frame
350 .memory
351 .store_zeros(dst_offset + data.len(), len - data.len())?;
352 }
353 }
354
355 Ok(OpcodeResult::Continue)
356 }
357}
358
359pub struct OpExtCodeHashHandler;
361impl OpcodeHandler for OpExtCodeHashHandler {
362 #[inline(always)]
363 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
364 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
365 vm.current_call_frame
366 .increase_consumed_gas(gas_cost::extcodehash(
367 vm.substate.add_accessed_address(address),
368 )?)?;
369
370 let account = vm.db.get_account(address)?;
371 let account_is_empty = account.is_empty();
372 let account_code_hash = account.info.code_hash.0;
373
374 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
376 recorder.record_touched_address(address);
377 }
378
379 if account_is_empty {
380 vm.current_call_frame.stack.push_zero()?;
381 } else {
382 #[expect(unsafe_code, reason = "safe")]
383 vm.current_call_frame.stack.push(U256(unsafe {
384 let mut bytes = account_code_hash;
385 bytes.reverse();
386 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
387 }))?;
388 }
389
390 Ok(OpcodeResult::Continue)
391 }
392}
393
394pub struct OpReturnDataSizeHandler;
396impl OpcodeHandler for OpReturnDataSizeHandler {
397 #[inline(always)]
398 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
399 vm.current_call_frame
400 .increase_consumed_gas(gas_cost::RETURNDATASIZE)?;
401
402 vm.current_call_frame
403 .stack
404 .push(vm.current_call_frame.sub_return_data.len().into())?;
405
406 Ok(OpcodeResult::Continue)
407 }
408}
409
410pub struct OpReturnDataCopyHandler;
412impl OpcodeHandler for OpReturnDataCopyHandler {
413 #[inline(always)]
414 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
415 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
416 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
417 let src_offset = u256_to_usize(src_offset)?;
418
419 vm.current_call_frame
420 .increase_consumed_gas(gas_cost::returndatacopy(
421 calculate_memory_size(dst_offset, len)?,
422 vm.current_call_frame.memory.len(),
423 len,
424 )?)?;
425
426 #[expect(
427 clippy::arithmetic_side_effects,
428 reason = "src_offset and len are validated by memory expansion"
429 )]
430 if src_offset + len > vm.current_call_frame.sub_return_data.len() {
431 return Err(ExceptionalHalt::OutOfBounds.into());
432 }
433
434 if len > 0 {
435 let data = vm
436 .current_call_frame
437 .sub_return_data
438 .get(src_offset..)
439 .unwrap_or_default();
440 let data = data.get(..len).unwrap_or(data);
441
442 vm.current_call_frame.memory.store_data(dst_offset, data)?;
443 if data.len() < len {
444 #[expect(
445 clippy::arithmetic_side_effects,
446 reason = "data.len() < len guard ensures no underflow"
447 )]
448 vm.current_call_frame
449 .memory
450 .store_zeros(dst_offset + data.len(), len - data.len())?;
451 }
452 }
453
454 Ok(OpcodeResult::Continue)
455 }
456}