1use crate::{
2 account::LevmAccount,
3 constants::STACK_LIMIT,
4 errors::{ExceptionalHalt, InternalError, VMError},
5 memory::Memory,
6 utils::restore_cache_state,
7 vm::VM,
8};
9use bytes::Bytes;
10use ethrex_common::types::block_access_list::BlockAccessListCheckpoint;
11use ethrex_common::{Address, H256, U256, types::Code};
12use rustc_hash::FxHashMap;
13use std::{
14 fmt,
15 hash::{Hash, Hasher},
16 hint::assert_unchecked,
17};
18
19const U64_PER_U256: usize = U256::MAX.0.len();
21
22#[derive(Clone, PartialEq, Eq)]
23pub struct Stack {
34 pub values: Box<[U256; STACK_LIMIT]>,
35 pub offset: usize,
36}
37
38impl Stack {
39 #[inline]
40 pub fn pop<const N: usize>(&mut self) -> Result<&[U256; N], ExceptionalHalt> {
41 const {
43 assert!(N <= STACK_LIMIT);
44 }
45
46 let next_offset = self.offset.wrapping_add(N);
49
50 #[expect(unsafe_code)]
54 let values = unsafe {
55 self.values
56 .get_unchecked(self.offset..)
57 .first_chunk::<N>()
58 .ok_or(ExceptionalHalt::StackUnderflow)?
59 };
60 self.offset = next_offset;
62
63 Ok(values)
64 }
65
66 #[inline]
67 pub fn pop1(&mut self) -> Result<U256, ExceptionalHalt> {
68 let value = *self
69 .values
70 .get(self.offset)
71 .ok_or(ExceptionalHalt::StackUnderflow)?;
72 self.offset = self.offset.wrapping_add(1);
75
76 Ok(value)
77 }
78
79 #[inline]
84 pub fn top_mut(&mut self) -> Result<&mut U256, ExceptionalHalt> {
85 self.values
86 .get_mut(self.offset)
87 .ok_or(ExceptionalHalt::StackUnderflow)
88 }
89
90 #[expect(unsafe_code, reason = "top validated by caller; depth unchanged")]
97 #[inline]
98 pub unsafe fn set_top_unchecked(&mut self, value: U256) {
99 unsafe {
100 *self.values.get_unchecked_mut(self.offset) = value;
101 }
102 }
103
104 #[inline]
109 pub fn pop1_and_top_mut(&mut self) -> Result<(U256, &mut U256), ExceptionalHalt> {
110 let a = self.pop1()?;
111 Ok((a, self.top_mut()?))
112 }
113
114 #[inline]
116 pub fn push(&mut self, value: U256) -> Result<(), ExceptionalHalt> {
117 let next_offset = self
120 .offset
121 .checked_sub(1)
122 .ok_or(ExceptionalHalt::StackOverflow)?;
123
124 #[expect(unsafe_code, reason = "next_offset == self.offset - 1 >= 0")]
131 unsafe {
132 let slot = self.values.get_unchecked_mut(next_offset);
133 slot.0[0] = value.0[0];
134 slot.0[1] = value.0[1];
135 slot.0[2] = value.0[2];
136 slot.0[3] = value.0[3];
137 }
138 self.offset = next_offset;
139
140 Ok(())
141 }
142
143 #[inline]
144 pub fn push_zero(&mut self) -> Result<(), ExceptionalHalt> {
145 let next_offset = self
148 .offset
149 .checked_sub(1)
150 .ok_or(ExceptionalHalt::StackOverflow)?;
151
152 #[expect(unsafe_code, reason = "next_offset == self.offset - 1 >= 0")]
155 unsafe {
156 *self
157 .values
158 .get_unchecked_mut(next_offset)
159 .0
160 .as_mut_ptr()
161 .cast() = [0u64; U64_PER_U256];
162 }
163 self.offset = next_offset;
164
165 Ok(())
166 }
167
168 pub fn len(&self) -> usize {
169 #[expect(clippy::arithmetic_side_effects)]
172 {
173 self.values.len() - self.offset
174 }
175 }
176
177 pub fn is_empty(&self) -> bool {
178 self.offset == self.values.len()
179 }
180
181 #[inline(always)]
182 pub fn swap<const N: usize>(&mut self) -> Result<(), ExceptionalHalt> {
183 const {
186 assert!(STACK_LIMIT.checked_add(N).is_some());
187 }
188 #[expect(clippy::arithmetic_side_effects)]
189 let index = self.offset + N;
190
191 if index >= self.values.len() {
192 return Err(ExceptionalHalt::StackUnderflow);
193 }
194
195 #[expect(unsafe_code, reason = "self.offset always < STACK_LIMIT")]
196 unsafe {
197 assert_unchecked(self.offset < STACK_LIMIT)
198 };
199
200 self.values.swap(self.offset, index);
201 Ok(())
202 }
203
204 pub fn clear(&mut self) {
205 self.offset = STACK_LIMIT;
206 }
207
208 #[inline]
210 pub fn dup<const N: usize>(&mut self) -> Result<(), ExceptionalHalt> {
211 const {
214 assert!(STACK_LIMIT.checked_add(N).is_some());
215 }
216 #[expect(clippy::arithmetic_side_effects)]
217 let index = self.offset + N;
218 if index >= self.values.len() {
219 return Err(ExceptionalHalt::StackUnderflow);
220 }
221
222 self.offset = self
223 .offset
224 .checked_sub(1)
225 .ok_or(ExceptionalHalt::StackOverflow)?;
226
227 #[expect(unsafe_code, reason = "index < size, offset-1 >= 0")]
228 unsafe {
229 std::ptr::copy_nonoverlapping(
230 self.values.get_unchecked_mut(index).0.as_mut_ptr(),
231 self.values.get_unchecked_mut(self.offset).0.as_mut_ptr(),
232 U64_PER_U256,
233 );
234 }
235 Ok(())
236 }
237}
238
239impl Default for Stack {
240 fn default() -> Self {
241 Self {
242 values: Box::new([U256::zero(); STACK_LIMIT]),
243 offset: STACK_LIMIT,
244 }
245 }
246}
247
248impl fmt::Debug for Stack {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 struct StackValues<'a>(&'a [U256]);
251
252 impl fmt::Debug for StackValues<'_> {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.debug_list().entries(self.0.iter().rev()).finish()
255 }
256 }
257
258 #[expect(clippy::indexing_slicing)]
259 f.debug_tuple("Stack")
260 .field(&StackValues(&self.values[self.offset..]))
261 .finish()
262 }
263}
264
265impl Hash for Stack {
266 #[expect(
267 clippy::indexing_slicing,
268 reason = "offset is always within bounds of values"
269 )]
270 fn hash<H: Hasher>(&self, state: &mut H) {
271 self.values[self.offset..].hash(state);
272 }
273}
274
275#[derive(Debug)]
276pub struct CallFrame {
281 pub gas_limit: u64,
283 pub gas_remaining: i64,
289 pub pc: usize,
291 pub msg_sender: Address,
293 pub to: Address,
295 pub code_address: Address,
297 pub bytecode: Code,
301 pub msg_value: U256,
303 pub stack: Stack,
304 pub memory: Memory,
305 pub calldata: Bytes,
307 pub output: Bytes,
309 pub sub_return_data: Bytes,
311 pub is_static: bool,
313 pub depth: usize,
315 pub is_create: bool,
317 pub call_frame_backup: CallFrameBackup,
319 pub ret_offset: usize,
321 pub ret_size: usize,
323 pub should_transfer_value: bool,
325 pub state_gas_used_at_entry: i64,
328 pub frame_state_gas_spilled: u64,
331 pub target_alive: bool,
336 pub new_account_state_gas_charged: bool,
340}
341
342#[derive(Debug, Clone, Eq, PartialEq, Default)]
343pub struct CallFrameBackup {
344 pub original_accounts_info: FxHashMap<Address, LevmAccount>,
345 pub original_account_storage_slots: FxHashMap<Address, FxHashMap<H256, U256>>,
346 pub bal_checkpoint: Option<BlockAccessListCheckpoint>,
349 pub inserted_code_hashes: Vec<H256>,
355}
356
357impl CallFrameBackup {
358 pub fn backup_account_info(
359 &mut self,
360 address: Address,
361 account: &LevmAccount,
362 ) -> Result<(), InternalError> {
363 self.original_accounts_info
364 .entry(address)
365 .or_insert_with(|| LevmAccount {
366 info: account.info.clone(),
367 storage: Default::default(),
368 status: account.status.clone(),
369 has_storage: account.has_storage,
370 exists: account.exists,
371 });
372
373 Ok(())
374 }
375
376 pub fn clear(&mut self) {
377 self.original_accounts_info.clear();
378 self.original_account_storage_slots.clear();
379 self.bal_checkpoint = None;
380 self.inserted_code_hashes.clear();
381 }
382
383 pub fn extend(&mut self, other: CallFrameBackup) {
388 for (address, other_storage) in other.original_account_storage_slots {
391 self.original_account_storage_slots
392 .entry(address)
393 .or_default()
394 .extend(other_storage);
395 }
396 self.original_accounts_info
397 .extend(other.original_accounts_info);
398 self.inserted_code_hashes.extend(other.inserted_code_hashes);
399 }
401
402 pub fn absorb(&mut self, other: &CallFrameBackup) {
411 for (address, account) in other.original_accounts_info.iter() {
412 self.original_accounts_info
413 .entry(*address)
414 .or_insert_with(|| account.clone());
415 }
416 for (address, storage) in other.original_account_storage_slots.iter() {
417 let slot = self
418 .original_account_storage_slots
419 .entry(*address)
420 .or_default();
421 for (key, value) in storage.iter() {
422 slot.entry(*key).or_insert(*value);
423 }
424 }
425 }
426}
427
428impl CallFrame {
429 #[expect(
430 clippy::too_many_arguments,
431 reason = "inlined constructor, many args needed for performance"
432 )]
433 #[inline(always)]
436 pub fn new(
437 msg_sender: Address,
438 to: Address,
439 code_address: Address,
440 bytecode: Code,
441 msg_value: U256,
442 calldata: Bytes,
443 is_static: bool,
444 gas_limit: u64,
445 depth: usize,
446 should_transfer_value: bool,
447 is_create: bool,
448 ret_offset: usize,
449 ret_size: usize,
450 stack: Stack,
451 memory: Memory,
452 ) -> Self {
453 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
456 Self {
457 gas_limit,
458 gas_remaining: gas_limit as i64,
459 msg_sender,
460 to,
461 code_address,
462 bytecode,
463 msg_value,
464 calldata,
465 is_static,
466 depth,
467 should_transfer_value,
468 is_create,
469 ret_offset,
470 ret_size,
471 stack,
472 memory,
473 call_frame_backup: CallFrameBackup::default(),
474 output: Bytes::default(),
475 pc: 0,
476 sub_return_data: Bytes::default(),
477 state_gas_used_at_entry: 0,
478 frame_state_gas_spilled: 0,
479 target_alive: false,
480 new_account_state_gas_charged: false,
481 }
482 }
483
484 #[inline(always)]
485 pub fn next_opcode(&self) -> u8 {
486 #[expect(unsafe_code, reason = "pc bounded by padded bytecode len")]
491 unsafe {
492 *self.bytecode.dispatch_buf().get_unchecked(self.pc)
493 }
494 }
495
496 pub fn pc(&self) -> usize {
497 self.pc
498 }
499
500 #[inline(always)]
502 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
503 #[expect(clippy::arithmetic_side_effects, reason = "arithmethic checked")]
504 pub fn increase_consumed_gas(&mut self, gas: u64) -> Result<(), ExceptionalHalt> {
505 self.gas_remaining -= gas as i64;
506
507 if self.gas_remaining < 0 {
508 return Err(ExceptionalHalt::OutOfGas);
509 }
510
511 Ok(())
512 }
513
514 #[inline(always)]
516 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
517 pub fn check_gas(&self, gas: u64) -> Result<(), ExceptionalHalt> {
518 if self.gas_remaining < 0 || (self.gas_remaining as u64) < gas {
519 return Err(ExceptionalHalt::OutOfGas);
520 }
521 Ok(())
522 }
523
524 pub fn set_code(&mut self, code: Code) -> Result<(), VMError> {
525 self.bytecode = code;
526 Ok(())
527 }
528}
529
530impl<'a> VM<'a> {
531 #[inline(always)]
533 pub fn add_callframe(&mut self, new_call_frame: CallFrame) {
534 if self.call_frames.is_empty() {
538 self.call_frames.reserve(8);
539 }
540 self.call_frames.push(new_call_frame);
541 #[allow(unsafe_code, reason = "just pushed, so the vec is not empty")]
542 unsafe {
543 std::mem::swap(
544 &mut self.current_call_frame,
545 self.call_frames.last_mut().unwrap_unchecked(),
546 );
547 }
548 }
549
550 #[inline(always)]
551 pub fn pop_call_frame(&mut self) -> Result<CallFrame, InternalError> {
552 let mut new = self.call_frames.pop().ok_or(InternalError::CallFrame)?;
553
554 std::mem::swap(&mut new, &mut self.current_call_frame);
555
556 Ok(new)
557 }
558
559 pub fn is_initial_call_frame(&self) -> bool {
560 self.call_frames.is_empty()
561 }
562
563 pub fn restore_cache_state(&mut self) -> Result<(), VMError> {
565 let callframe_backup = self.current_call_frame.call_frame_backup.clone();
566 restore_cache_state(self.db, callframe_backup)
567 }
568
569 pub fn restore_cache_state_consuming(&mut self) -> Result<(), VMError> {
578 let callframe_backup = std::mem::take(&mut self.current_call_frame.call_frame_backup);
579 restore_cache_state(self.db, callframe_backup)
580 }
581
582 pub fn merge_call_frame_backup_with_parent(
587 &mut self,
588 child_call_frame_backup: &CallFrameBackup,
589 ) -> Result<(), VMError> {
590 let parent_backup_accounts = &mut self
591 .current_call_frame
592 .call_frame_backup
593 .original_accounts_info;
594 for (address, account) in child_call_frame_backup.original_accounts_info.iter() {
595 if parent_backup_accounts.get(address).is_none() {
596 parent_backup_accounts.insert(*address, account.clone());
597 }
598 }
599
600 let parent_backup_storage = &mut self
601 .current_call_frame
602 .call_frame_backup
603 .original_account_storage_slots;
604 for (address, storage) in child_call_frame_backup
605 .original_account_storage_slots
606 .iter()
607 {
608 let parent_storage = parent_backup_storage.entry(*address).or_default();
609 for (key, value) in storage {
610 if parent_storage.get(key).is_none() {
611 parent_storage.insert(*key, *value);
612 }
613 }
614 }
615
616 self.current_call_frame
619 .call_frame_backup
620 .inserted_code_hashes
621 .extend(child_call_frame_backup.inserted_code_hashes.iter().copied());
622
623 Ok(())
624 }
625
626 #[inline(always)]
627 #[expect(
628 clippy::arithmetic_side_effects,
629 reason = "pc bounded by padded bytecode len"
630 )]
631 pub fn advance_pc(&mut self) {
632 self.current_call_frame.pc += 1;
633 }
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639 use ethrex_common::types::AccountInfo;
640
641 #[test]
642 fn absorb_keeps_earliest_original_value() {
643 let addr = Address::from_low_u64_be(1);
644 let mut acc = CallFrameBackup::default();
645
646 let mut first = CallFrameBackup::default();
647 first
648 .original_account_storage_slots
649 .entry(addr)
650 .or_default()
651 .insert(H256::zero(), U256::from(10));
652
653 let mut second = CallFrameBackup::default();
654 second
655 .original_account_storage_slots
656 .entry(addr)
657 .or_default()
658 .insert(H256::zero(), U256::from(20));
659
660 acc.absorb(&first);
661 acc.absorb(&second);
662
663 let value = acc
665 .original_account_storage_slots
666 .get(&addr)
667 .and_then(|slots| slots.get(&H256::zero()))
668 .copied();
669 assert_eq!(value, Some(U256::from(10)));
670 }
671
672 #[test]
673 fn absorb_keeps_earliest_account_info() {
674 let addr = Address::from_low_u64_be(2);
675 let mut acc = CallFrameBackup::default();
676
677 let first_acc = LevmAccount {
678 info: AccountInfo {
679 nonce: 1,
680 ..Default::default()
681 },
682 ..Default::default()
683 };
684 let mut first = CallFrameBackup::default();
685 first.original_accounts_info.insert(addr, first_acc);
686
687 let second_acc = LevmAccount {
688 info: AccountInfo {
689 nonce: 2,
690 ..Default::default()
691 },
692 ..Default::default()
693 };
694 let mut second = CallFrameBackup::default();
695 second.original_accounts_info.insert(addr, second_acc);
696
697 acc.absorb(&first);
698 acc.absorb(&second);
699
700 let nonce = acc.original_accounts_info.get(&addr).map(|a| a.info.nonce);
701 assert_eq!(nonce, Some(1));
702 }
703}