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 vm.env.config.fork,
62 )?)?;
63
64 let account_balance = vm.db.get_account(address)?.info.balance;
66
67 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
69 recorder.record_touched_address(address);
70 }
71
72 vm.current_call_frame.stack.push(account_balance)?;
73
74 Ok(OpcodeResult::Continue)
75 }
76}
77
78pub struct OpOriginHandler;
80impl OpcodeHandler for OpOriginHandler {
81 #[inline(always)]
82 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
83 vm.current_call_frame
84 .increase_consumed_gas(gas_cost::ORIGIN)?;
85
86 #[expect(unsafe_code, reason = "safe")]
87 vm.current_call_frame.stack.push(U256(unsafe {
88 let mut bytes: [u8; 32] = [0; 32];
89 bytes[12..].copy_from_slice(&vm.env.origin.0);
90 bytes.reverse();
91 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
92 }))?;
93
94 Ok(OpcodeResult::Continue)
95 }
96}
97
98pub struct OpGasPriceHandler;
100impl OpcodeHandler for OpGasPriceHandler {
101 #[inline(always)]
102 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
103 vm.current_call_frame
104 .increase_consumed_gas(gas_cost::GASPRICE)?;
105
106 vm.current_call_frame.stack.push(vm.env.gas_price)?;
107
108 Ok(OpcodeResult::Continue)
109 }
110}
111
112pub struct OpCallerHandler;
114impl OpcodeHandler for OpCallerHandler {
115 #[inline(always)]
116 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
117 vm.current_call_frame
118 .increase_consumed_gas(gas_cost::CALLER)?;
119
120 #[expect(unsafe_code, reason = "safe")]
121 vm.current_call_frame.stack.push(U256(unsafe {
122 let mut bytes: [u8; 32] = [0; 32];
123 bytes[12..].copy_from_slice(&vm.current_call_frame.msg_sender.0);
124 bytes.reverse();
125 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
126 }))?;
127
128 Ok(OpcodeResult::Continue)
129 }
130}
131
132pub struct OpCallValueHandler;
134impl OpcodeHandler for OpCallValueHandler {
135 #[inline(always)]
136 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
137 vm.current_call_frame
138 .increase_consumed_gas(gas_cost::CALLVALUE)?;
139
140 vm.current_call_frame
141 .stack
142 .push(vm.current_call_frame.msg_value)?;
143
144 Ok(OpcodeResult::Continue)
145 }
146}
147
148pub struct OpCallDataLoadHandler;
150impl OpcodeHandler for OpCallDataLoadHandler {
151 #[inline(always)]
152 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
153 vm.current_call_frame
154 .increase_consumed_gas(gas_cost::CALLDATALOAD)?;
155
156 let value_bytes = usize::try_from(vm.current_call_frame.stack.pop1()?)
157 .ok()
158 .and_then(|offset| vm.current_call_frame.calldata.get(offset..));
159 #[expect(clippy::indexing_slicing, reason = "length is checked in match guard")]
160 vm.current_call_frame.stack.push(match value_bytes {
161 Some(data) if data.len() >= 32 => U256::from_big_endian(&data[..32]),
162 Some(data) => {
163 let mut bytes = [0; 32];
164 bytes[..data.len()].copy_from_slice(data);
165 U256::from_big_endian(&bytes)
166 }
167 None => U256::zero(),
168 })?;
169
170 Ok(OpcodeResult::Continue)
171 }
172}
173
174pub struct OpCallDataSizeHandler;
176impl OpcodeHandler for OpCallDataSizeHandler {
177 #[inline(always)]
178 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
179 vm.current_call_frame
180 .increase_consumed_gas(gas_cost::CALLDATASIZE)?;
181
182 vm.current_call_frame
183 .stack
184 .push(U256::from(vm.current_call_frame.calldata.len()))?;
185
186 Ok(OpcodeResult::Continue)
187 }
188}
189
190pub struct OpCallDataCopyHandler;
192impl OpcodeHandler for OpCallDataCopyHandler {
193 #[inline(always)]
194 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
195 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
196 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
197 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
198
199 vm.current_call_frame
200 .increase_consumed_gas(gas_cost::calldatacopy(
201 calculate_memory_size(dst_offset, len)?,
202 vm.current_call_frame.memory.len(),
203 len,
204 )?)?;
205
206 if len > 0 {
207 let data = vm
208 .current_call_frame
209 .calldata
210 .get(src_offset..)
211 .unwrap_or_default();
212 let data = data.get(..len).unwrap_or(data);
213
214 vm.current_call_frame.memory.store_data(dst_offset, data)?;
215 if data.len() < len {
216 #[expect(
217 clippy::arithmetic_side_effects,
218 reason = "data.len() < len guard ensures no underflow"
219 )]
220 vm.current_call_frame
221 .memory
222 .store_zeros(dst_offset + data.len(), len - data.len())?;
223 }
224 }
225
226 Ok(OpcodeResult::Continue)
227 }
228}
229
230pub struct OpCodeSizeHandler;
232impl OpcodeHandler for OpCodeSizeHandler {
233 #[inline(always)]
234 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
235 vm.current_call_frame
236 .increase_consumed_gas(gas_cost::CODESIZE)?;
237
238 vm.current_call_frame
239 .stack
240 .push(vm.current_call_frame.bytecode.len().into())?;
241
242 Ok(OpcodeResult::Continue)
243 }
244}
245
246pub struct OpCodeCopyHandler;
248impl OpcodeHandler for OpCodeCopyHandler {
249 #[inline(always)]
250 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
251 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
252 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
253 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
254
255 vm.current_call_frame
256 .increase_consumed_gas(gas_cost::codecopy(
257 calculate_memory_size(dst_offset, len)?,
258 vm.current_call_frame.memory.len(),
259 len,
260 )?)?;
261
262 if len > 0 {
263 let data = vm
264 .current_call_frame
265 .bytecode
266 .dispatch_buf()
267 .get(src_offset..)
268 .unwrap_or_default();
269 let data = data.get(..len).unwrap_or(data);
270
271 vm.current_call_frame.memory.store_data(dst_offset, data)?;
272 if data.len() < len {
273 #[expect(
274 clippy::arithmetic_side_effects,
275 reason = "data.len() < len guard ensures no underflow"
276 )]
277 vm.current_call_frame
278 .memory
279 .store_zeros(dst_offset + data.len(), len - data.len())?;
280 }
281 }
282
283 Ok(OpcodeResult::Continue)
284 }
285}
286
287pub struct OpExtCodeSizeHandler;
289impl OpcodeHandler for OpExtCodeSizeHandler {
290 #[inline(always)]
291 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
292 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
293 vm.current_call_frame
294 .increase_consumed_gas(gas_cost::extcodesize(
295 vm.substate.add_accessed_address(address),
296 vm.env.config.fork,
297 )?)?;
298
299 if vm.validation_observer.active {
302 vm.validation_check_extcode_target(address)?;
303 }
304
305 let account_code_length = vm.db.get_code_length(address)?.into();
307
308 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
310 recorder.record_touched_address(address);
311 }
312
313 vm.current_call_frame.stack.push(account_code_length)?;
314
315 Ok(OpcodeResult::Continue)
316 }
317}
318
319pub struct OpExtCodeCopyHandler;
321impl OpcodeHandler for OpExtCodeCopyHandler {
322 #[inline(always)]
323 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
324 let [address, dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
325 let address = word_to_address(address);
326 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
327 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
328
329 vm.current_call_frame
330 .increase_consumed_gas(gas_cost::extcodecopy(
331 len,
332 calculate_memory_size(dst_offset, len)?,
333 vm.current_call_frame.memory.len(),
334 vm.substate.add_accessed_address(address),
335 vm.env.config.fork,
336 )?)?;
337
338 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
340 recorder.record_touched_address(address);
341 }
342
343 if vm.validation_observer.active {
346 vm.validation_check_extcode_target(address)?;
347 }
348
349 let code = vm.db.get_account_code(address)?;
353
354 if len > 0 {
355 let data = code.dispatch_buf().get(src_offset..).unwrap_or_default();
356 let data = data.get(..len).unwrap_or(data);
357
358 vm.current_call_frame.memory.store_data(dst_offset, data)?;
359 if data.len() < len {
360 #[expect(
361 clippy::arithmetic_side_effects,
362 reason = "data.len() < len guard ensures no underflow"
363 )]
364 vm.current_call_frame
365 .memory
366 .store_zeros(dst_offset + data.len(), len - data.len())?;
367 }
368 }
369
370 Ok(OpcodeResult::Continue)
371 }
372}
373
374pub struct OpExtCodeHashHandler;
376impl OpcodeHandler for OpExtCodeHashHandler {
377 #[inline(always)]
378 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
379 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
380 vm.current_call_frame
381 .increase_consumed_gas(gas_cost::extcodehash(
382 vm.substate.add_accessed_address(address),
383 vm.env.config.fork,
384 )?)?;
385
386 if vm.validation_observer.active {
389 vm.validation_check_extcode_target(address)?;
390 }
391
392 let account = vm.db.get_account(address)?;
393 let account_is_empty = account.is_empty();
394 let account_code_hash = account.info.code_hash.0;
395
396 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
398 recorder.record_touched_address(address);
399 }
400
401 if account_is_empty {
402 vm.current_call_frame.stack.push_zero()?;
403 } else {
404 #[expect(unsafe_code, reason = "safe")]
405 vm.current_call_frame.stack.push(U256(unsafe {
406 let mut bytes = account_code_hash;
407 bytes.reverse();
408 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
409 }))?;
410 }
411
412 Ok(OpcodeResult::Continue)
413 }
414}
415
416pub struct OpReturnDataSizeHandler;
418impl OpcodeHandler for OpReturnDataSizeHandler {
419 #[inline(always)]
420 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
421 vm.current_call_frame
422 .increase_consumed_gas(gas_cost::RETURNDATASIZE)?;
423
424 vm.current_call_frame
425 .stack
426 .push(vm.current_call_frame.sub_return_data.len().into())?;
427
428 Ok(OpcodeResult::Continue)
429 }
430}
431
432pub struct OpReturnDataCopyHandler;
434impl OpcodeHandler for OpReturnDataCopyHandler {
435 #[inline(always)]
436 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
437 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
438 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
439 let src_offset = u256_to_usize(src_offset)?;
440
441 vm.current_call_frame
442 .increase_consumed_gas(gas_cost::returndatacopy(
443 calculate_memory_size(dst_offset, len)?,
444 vm.current_call_frame.memory.len(),
445 len,
446 )?)?;
447
448 #[expect(
449 clippy::arithmetic_side_effects,
450 reason = "src_offset and len are validated by memory expansion"
451 )]
452 if src_offset + len > vm.current_call_frame.sub_return_data.len() {
453 return Err(ExceptionalHalt::OutOfBounds.into());
454 }
455
456 if len > 0 {
457 let data = vm
458 .current_call_frame
459 .sub_return_data
460 .get(src_offset..)
461 .unwrap_or_default();
462 let data = data.get(..len).unwrap_or(data);
463
464 vm.current_call_frame.memory.store_data(dst_offset, data)?;
465 if data.len() < len {
466 #[expect(
467 clippy::arithmetic_side_effects,
468 reason = "data.len() < len guard ensures no underflow"
469 )]
470 vm.current_call_frame
471 .memory
472 .store_zeros(dst_offset + data.len(), len - data.len())?;
473 }
474 }
475
476 Ok(OpcodeResult::Continue)
477 }
478}