evm_core/
lib.rs

1//! Core layer for EVM.
2
3#![deny(warnings)]
4#![forbid(unsafe_code, unused_variables, unused_imports)]
5#![cfg_attr(not(feature = "std"), no_std)]
6
7extern crate alloc;
8
9mod error;
10mod eval;
11mod external;
12mod memory;
13mod opcode;
14mod stack;
15mod utils;
16mod valids;
17
18pub use crate::error::{Capture, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Trap};
19pub use crate::external::ExternalOperation;
20pub use crate::memory::Memory;
21pub use crate::opcode::Opcode;
22pub use crate::stack::Stack;
23pub use crate::valids::Valids;
24
25use crate::eval::{eval, Control};
26use alloc::rc::Rc;
27use alloc::vec::Vec;
28use core::ops::Range;
29use primitive_types::{H160, U256};
30
31/// EIP-7702 delegation designator prefix
32pub const EIP_7702_DELEGATION_PREFIX: &[u8] = &[0xef, 0x01, 0x00];
33
34/// EIP-7702 delegation designator full length (prefix + address)
35pub const EIP_7702_DELEGATION_SIZE: usize = 23; // 3 bytes prefix + 20 bytes address
36
37/// Core execution layer for EVM.
38pub struct Machine {
39	/// Program data.
40	data: Rc<Vec<u8>>,
41	/// Program code.
42	code: Rc<Vec<u8>>,
43	/// Program counter.
44	position: Result<usize, ExitReason>,
45	/// Return value.
46	return_range: Range<U256>,
47	/// Code validity maps.
48	valids: Valids,
49	/// Memory.
50	memory: Memory,
51	/// Stack.
52	stack: Stack,
53}
54
55impl Machine {
56	/// Reference of machine stack.
57	pub fn stack(&self) -> &Stack {
58		&self.stack
59	}
60	/// Mutable reference of machine stack.
61	pub fn stack_mut(&mut self) -> &mut Stack {
62		&mut self.stack
63	}
64	/// Reference of machine memory.
65	pub fn memory(&self) -> &Memory {
66		&self.memory
67	}
68	/// Mutable reference of machine memory.
69	pub fn memory_mut(&mut self) -> &mut Memory {
70		&mut self.memory
71	}
72	/// Return a reference of the program counter.
73	pub fn position(&self) -> &Result<usize, ExitReason> {
74		&self.position
75	}
76
77	/// Create a new machine with given code and data.
78	pub fn new(
79		code: Rc<Vec<u8>>,
80		data: Rc<Vec<u8>>,
81		stack_limit: usize,
82		memory_limit: usize,
83	) -> Self {
84		let valids = Valids::new(&code[..]);
85
86		Self {
87			data,
88			code,
89			position: Ok(0),
90			return_range: U256::zero()..U256::zero(),
91			valids,
92			memory: Memory::new(memory_limit),
93			stack: Stack::new(stack_limit),
94		}
95	}
96
97	/// Explicit exit of the machine. Further step will return error.
98	pub fn exit(&mut self, reason: ExitReason) {
99		self.position = Err(reason);
100	}
101
102	/// Inspect the machine's next opcode and current stack.
103	pub fn inspect(&self) -> Option<(Opcode, &Stack)> {
104		let position = match self.position {
105			Ok(position) => position,
106			Err(_) => return None,
107		};
108		self.code.get(position).map(|v| (Opcode(*v), &self.stack))
109	}
110
111	/// Copy and get the return value of the machine, if any.
112	#[allow(clippy::slow_vector_initialization)]
113	// Clippy complains about not using `no_std`. However, we need to support
114	// `no_std` and we can't use that.
115	pub fn return_value(&self) -> Vec<u8> {
116		if self.return_range.start > U256::from(usize::MAX) {
117			let mut ret = Vec::new();
118			ret.resize(
119				(self.return_range.end - self.return_range.start).as_usize(),
120				0,
121			);
122			ret
123		} else if self.return_range.end > U256::from(usize::MAX) {
124			let mut ret = self.memory.get(
125				self.return_range.start.as_usize(),
126				usize::MAX - self.return_range.start.as_usize(),
127			);
128			while ret.len() < (self.return_range.end - self.return_range.start).as_usize() {
129				ret.push(0);
130			}
131			ret
132		} else {
133			self.memory.get(
134				self.return_range.start.as_usize(),
135				(self.return_range.end - self.return_range.start).as_usize(),
136			)
137		}
138	}
139
140	/// Loop stepping the machine, until it stops.
141	pub fn run(&mut self) -> Capture<ExitReason, Trap> {
142		loop {
143			match self.step() {
144				Ok(()) => (),
145				Err(res) => return res,
146			}
147		}
148	}
149
150	#[inline]
151	/// Step the machine, executing one opcode. It then returns.
152	pub fn step(&mut self) -> Result<(), Capture<ExitReason, Trap>> {
153		let position = *self
154			.position
155			.as_ref()
156			.map_err(|reason| Capture::Exit(reason.clone()))?;
157
158		match self.code.get(position).map(|v| Opcode(*v)) {
159			Some(opcode) => match eval(self, opcode, position) {
160				Control::Continue(p) => {
161					self.position = Ok(position + p);
162					Ok(())
163				}
164				Control::Exit(e) => {
165					self.position = Err(e.clone());
166					Err(Capture::Exit(e))
167				}
168				Control::Jump(p) => {
169					self.position = Ok(p);
170					Ok(())
171				}
172				Control::Trap(opcode) => {
173					#[cfg(feature = "force-debug")]
174					log::trace!(target: "evm", "OpCode Trap: {:?}", opcode);
175
176					self.position = Ok(position + 1);
177					Err(Capture::Trap(opcode))
178				}
179			},
180			None => {
181				self.position = Err(ExitSucceed::Stopped.into());
182				Err(Capture::Exit(ExitSucceed::Stopped.into()))
183			}
184		}
185	}
186}
187
188/// Check if code is an EIP-7702 delegation designator
189pub fn is_delegation_designator(code: &[u8]) -> bool {
190	code.len() == EIP_7702_DELEGATION_SIZE && code.starts_with(EIP_7702_DELEGATION_PREFIX)
191}
192
193/// Extract the delegated address from EIP-7702 delegation designator
194pub fn extract_delegation_address(code: &[u8]) -> Option<H160> {
195	if is_delegation_designator(code) {
196		let mut address_bytes = [0u8; 20];
197		address_bytes.copy_from_slice(&code[3..23]);
198		Some(H160::from(address_bytes))
199	} else {
200		None
201	}
202}
203
204/// Create EIP-7702 delegation designator
205pub fn create_delegation_designator(address: H160) -> Vec<u8> {
206	let mut designator = Vec::with_capacity(EIP_7702_DELEGATION_SIZE);
207	designator.extend_from_slice(EIP_7702_DELEGATION_PREFIX);
208	designator.extend_from_slice(address.as_bytes());
209	designator
210}
211
212#[cfg(test)]
213mod tests {
214	use super::*;
215
216	#[test]
217	fn test_delegation_designator_creation() {
218		let address = H160::from_slice(&[1u8; 20]);
219		let designator = create_delegation_designator(address);
220
221		assert_eq!(designator.len(), EIP_7702_DELEGATION_SIZE);
222		assert_eq!(&designator[0..3], EIP_7702_DELEGATION_PREFIX);
223		assert_eq!(&designator[3..23], address.as_bytes());
224	}
225
226	#[test]
227	fn test_delegation_designator_detection() {
228		let address = H160::from_slice(&[1u8; 20]);
229		let designator = create_delegation_designator(address);
230
231		assert!(is_delegation_designator(&designator));
232		assert_eq!(extract_delegation_address(&designator), Some(address));
233	}
234
235	#[test]
236	fn test_non_delegation_code() {
237		let regular_code = vec![0x60, 0x00]; // PUSH1 0
238		assert!(!is_delegation_designator(&regular_code));
239		assert_eq!(extract_delegation_address(&regular_code), None);
240	}
241}