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 = match usize::try_from(vm.current_call_frame.stack.pop1()?) {
157 Ok(offset) => vm.current_call_frame.calldata.load_word(offset)?,
158 Err(_) => U256::zero(),
159 };
160 vm.current_call_frame.stack.push(value)?;
161
162 Ok(OpcodeResult::Continue)
163 }
164}
165
166pub struct OpCallDataSizeHandler;
168impl OpcodeHandler for OpCallDataSizeHandler {
169 #[inline(always)]
170 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
171 vm.current_call_frame
172 .increase_consumed_gas(gas_cost::CALLDATASIZE)?;
173
174 vm.current_call_frame
175 .stack
176 .push(U256::from(vm.current_call_frame.calldata.len()))?;
177
178 Ok(OpcodeResult::Continue)
179 }
180}
181
182pub struct OpCallDataCopyHandler;
184impl OpcodeHandler for OpCallDataCopyHandler {
185 #[inline(always)]
186 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
187 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
188 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
189 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
190
191 vm.current_call_frame
192 .increase_consumed_gas(gas_cost::calldatacopy(
193 calculate_memory_size(dst_offset, len)?,
194 vm.current_call_frame.memory.len(),
195 len,
196 )?)?;
197
198 let call_frame = &mut vm.current_call_frame;
199 call_frame
200 .memory
201 .store_calldata(dst_offset, &call_frame.calldata, src_offset, len)?;
202
203 Ok(OpcodeResult::Continue)
204 }
205}
206
207pub struct OpCodeSizeHandler;
209impl OpcodeHandler for OpCodeSizeHandler {
210 #[inline(always)]
211 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
212 vm.current_call_frame
213 .increase_consumed_gas(gas_cost::CODESIZE)?;
214
215 vm.current_call_frame
216 .stack
217 .push(vm.current_call_frame.bytecode.len().into())?;
218
219 Ok(OpcodeResult::Continue)
220 }
221}
222
223pub struct OpCodeCopyHandler;
225impl OpcodeHandler for OpCodeCopyHandler {
226 #[inline(always)]
227 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
228 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
229 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
230 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
231
232 vm.current_call_frame
233 .increase_consumed_gas(gas_cost::codecopy(
234 calculate_memory_size(dst_offset, len)?,
235 vm.current_call_frame.memory.len(),
236 len,
237 )?)?;
238
239 if len > 0 {
240 let data = vm
241 .current_call_frame
242 .bytecode
243 .dispatch_buf()
244 .get(src_offset..)
245 .unwrap_or_default();
246 let data = data.get(..len).unwrap_or(data);
247
248 vm.current_call_frame.memory.store_data(dst_offset, data)?;
249 if data.len() < len {
250 #[expect(
251 clippy::arithmetic_side_effects,
252 reason = "data.len() < len guard ensures no underflow"
253 )]
254 vm.current_call_frame
255 .memory
256 .store_zeros(dst_offset + data.len(), len - data.len())?;
257 }
258 }
259
260 Ok(OpcodeResult::Continue)
261 }
262}
263
264pub struct OpExtCodeSizeHandler;
266impl OpcodeHandler for OpExtCodeSizeHandler {
267 #[inline(always)]
268 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
269 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
270 vm.current_call_frame
271 .increase_consumed_gas(gas_cost::extcodesize(
272 vm.substate.add_accessed_address(address),
273 vm.env.config.fork,
274 )?)?;
275
276 if vm.validation_observer.active {
279 vm.validation_check_extcode_target(address)?;
280 }
281
282 let account_code_length = vm.db.get_code_length(address)?.into();
284
285 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
287 recorder.record_touched_address(address);
288 }
289
290 vm.current_call_frame.stack.push(account_code_length)?;
291
292 Ok(OpcodeResult::Continue)
293 }
294}
295
296pub struct OpExtCodeCopyHandler;
298impl OpcodeHandler for OpExtCodeCopyHandler {
299 #[inline(always)]
300 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
301 let [address, dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
302 let address = word_to_address(address);
303 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
304 let src_offset = u256_to_usize(src_offset).unwrap_or(usize::MAX);
305
306 vm.current_call_frame
307 .increase_consumed_gas(gas_cost::extcodecopy(
308 len,
309 calculate_memory_size(dst_offset, len)?,
310 vm.current_call_frame.memory.len(),
311 vm.substate.add_accessed_address(address),
312 vm.env.config.fork,
313 )?)?;
314
315 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
317 recorder.record_touched_address(address);
318 }
319
320 if vm.validation_observer.active {
323 vm.validation_check_extcode_target(address)?;
324 }
325
326 let code = vm.db.get_account_code(address)?;
330
331 if len > 0 {
332 let data = code.dispatch_buf().get(src_offset..).unwrap_or_default();
333 let data = data.get(..len).unwrap_or(data);
334
335 vm.current_call_frame.memory.store_data(dst_offset, data)?;
336 if data.len() < len {
337 #[expect(
338 clippy::arithmetic_side_effects,
339 reason = "data.len() < len guard ensures no underflow"
340 )]
341 vm.current_call_frame
342 .memory
343 .store_zeros(dst_offset + data.len(), len - data.len())?;
344 }
345 }
346
347 Ok(OpcodeResult::Continue)
348 }
349}
350
351pub struct OpExtCodeHashHandler;
353impl OpcodeHandler for OpExtCodeHashHandler {
354 #[inline(always)]
355 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
356 let address = word_to_address(vm.current_call_frame.stack.pop1()?);
357 vm.current_call_frame
358 .increase_consumed_gas(gas_cost::extcodehash(
359 vm.substate.add_accessed_address(address),
360 vm.env.config.fork,
361 )?)?;
362
363 if vm.validation_observer.active {
366 vm.validation_check_extcode_target(address)?;
367 }
368
369 let account = vm.db.get_account(address)?;
370 let account_is_empty = account.is_empty();
371 let account_code_hash = account.info.code_hash.0;
372
373 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
375 recorder.record_touched_address(address);
376 }
377
378 if account_is_empty {
379 vm.current_call_frame.stack.push_zero()?;
380 } else {
381 #[expect(unsafe_code, reason = "safe")]
382 vm.current_call_frame.stack.push(U256(unsafe {
383 let mut bytes = account_code_hash;
384 bytes.reverse();
385 mem::transmute_copy::<[u8; 32], [u64; 4]>(&bytes)
386 }))?;
387 }
388
389 Ok(OpcodeResult::Continue)
390 }
391}
392
393pub struct OpReturnDataSizeHandler;
395impl OpcodeHandler for OpReturnDataSizeHandler {
396 #[inline(always)]
397 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
398 vm.current_call_frame
399 .increase_consumed_gas(gas_cost::RETURNDATASIZE)?;
400
401 vm.current_call_frame
402 .stack
403 .push(vm.current_call_frame.sub_return_data.len().into())?;
404
405 Ok(OpcodeResult::Continue)
406 }
407}
408
409pub struct OpReturnDataCopyHandler;
411impl OpcodeHandler for OpReturnDataCopyHandler {
412 #[inline(always)]
413 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
414 let [dst_offset, src_offset, len] = *vm.current_call_frame.stack.pop()?;
415 let (len, dst_offset) = size_offset_to_usize(len, dst_offset)?;
416 let src_offset = u256_to_usize(src_offset)?;
417
418 vm.current_call_frame
419 .increase_consumed_gas(gas_cost::returndatacopy(
420 calculate_memory_size(dst_offset, len)?,
421 vm.current_call_frame.memory.len(),
422 len,
423 )?)?;
424
425 #[expect(
426 clippy::arithmetic_side_effects,
427 reason = "src_offset and len are validated by memory expansion"
428 )]
429 if src_offset + len > vm.current_call_frame.sub_return_data.len() {
430 return Err(ExceptionalHalt::OutOfBounds.into());
431 }
432
433 if len > 0 {
434 let data = vm
435 .current_call_frame
436 .sub_return_data
437 .get(src_offset..)
438 .unwrap_or_default();
439 let data = data.get(..len).unwrap_or(data);
440
441 vm.current_call_frame.memory.store_data(dst_offset, data)?;
442 if data.len() < len {
443 #[expect(
444 clippy::arithmetic_side_effects,
445 reason = "data.len() < len guard ensures no underflow"
446 )]
447 vm.current_call_frame
448 .memory
449 .store_zeros(dst_offset + data.len(), len - data.len())?;
450 }
451 }
452
453 Ok(OpcodeResult::Continue)
454 }
455}