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 let account_code_length = vm.db.get_code_length(address)?.into();
301
302 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
304 recorder.record_touched_address(address);
305 }
306
307 vm.current_call_frame.stack.push(account_code_length)?;
308
309 Ok(OpcodeResult::Continue)
310 }
311}
312
313pub struct OpExtCodeCopyHandler;
315impl OpcodeHandler for OpExtCodeCopyHandler {
316 #[inline(always)]
317 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
318 let [address, dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
319 let address = word_to_address(address);
320 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
321 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
322
323 vm.current_call_frame
324 .increase_consumed_gas(gas_cost::extcodecopy(
325 len,
326 calculate_memory_size(dst_offset, len)?,
327 vm.current_call_frame.memory.len(),
328 vm.substate.add_accessed_address(address),
329 vm.env.config.fork,
330 )?)?;
331
332 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
334 recorder.record_touched_address(address);
335 }
336
337 let code = vm.db.get_account_code(address)?;
341
342 if len > 0 {
343 let data = code.dispatch_buf().get(src_offset..).unwrap_or_default();
344 let data = data.get(..len).unwrap_or(data);
345
346 vm.current_call_frame.memory.store_data(dst_offset, data)?;
347 if data.len() < len {
348 #[expect(
349 clippy::arithmetic_side_effects,
350 reason = "data.len() < len guard ensures no underflow"
351 )]
352 vm.current_call_frame
353 .memory
354 .store_zeros(dst_offset + data.len(), len - data.len())?;
355 }
356 }
357
358 Ok(OpcodeResult::Continue)
359 }
360}
361
362pub struct OpExtCodeHashHandler;
364impl OpcodeHandler for OpExtCodeHashHandler {
365 #[inline(always)]
366 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
367 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
368 vm.current_call_frame
369 .increase_consumed_gas(gas_cost::extcodehash(
370 vm.substate.add_accessed_address(address),
371 vm.env.config.fork,
372 )?)?;
373
374 let account = vm.db.get_account(address)?;
375 let account_is_empty = account.is_empty();
376 let account_code_hash = account.info.code_hash.0;
377
378 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
380 recorder.record_touched_address(address);
381 }
382
383 if account_is_empty {
384 vm.current_call_frame.stack.push_zero()?;
385 } else {
386 #[expect(unsafe_code, reason = "safe")]
387 vm.current_call_frame.stack.push(U256(unsafe {
388 let mut bytes = account_code_hash;
389 bytes.reverse();
390 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
391 }))?;
392 }
393
394 Ok(OpcodeResult::Continue)
395 }
396}
397
398pub struct OpReturnDataSizeHandler;
400impl OpcodeHandler for OpReturnDataSizeHandler {
401 #[inline(always)]
402 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
403 vm.current_call_frame
404 .increase_consumed_gas(gas_cost::RETURNDATASIZE)?;
405
406 vm.current_call_frame
407 .stack
408 .push(vm.current_call_frame.sub_return_data.len().into())?;
409
410 Ok(OpcodeResult::Continue)
411 }
412}
413
414pub struct OpReturnDataCopyHandler;
416impl OpcodeHandler for OpReturnDataCopyHandler {
417 #[inline(always)]
418 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
419 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
420 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
421 let src_offset = u256_to_usize(src_offset)?;
422
423 vm.current_call_frame
424 .increase_consumed_gas(gas_cost::returndatacopy(
425 calculate_memory_size(dst_offset, len)?,
426 vm.current_call_frame.memory.len(),
427 len,
428 )?)?;
429
430 #[expect(
431 clippy::arithmetic_side_effects,
432 reason = "src_offset and len are validated by memory expansion"
433 )]
434 if src_offset + len > vm.current_call_frame.sub_return_data.len() {
435 return Err(ExceptionalHalt::OutOfBounds.into());
436 }
437
438 if len > 0 {
439 let data = vm
440 .current_call_frame
441 .sub_return_data
442 .get(src_offset..)
443 .unwrap_or_default();
444 let data = data.get(..len).unwrap_or(data);
445
446 vm.current_call_frame.memory.store_data(dst_offset, data)?;
447 if data.len() < len {
448 #[expect(
449 clippy::arithmetic_side_effects,
450 reason = "data.len() < len guard ensures no underflow"
451 )]
452 vm.current_call_frame
453 .memory
454 .store_zeros(dst_offset + data.len(), len - data.len())?;
455 }
456 }
457
458 Ok(OpcodeResult::Continue)
459 }
460}