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::U256;
30
31/// Core execution layer for EVM.
32pub struct Machine {
33	/// Program data.
34	data: Rc<Vec<u8>>,
35	/// Program code.
36	code: Rc<Vec<u8>>,
37	/// Program counter.
38	position: Result<usize, ExitReason>,
39	/// Return value.
40	return_range: Range<U256>,
41	/// Code validity maps.
42	valids: Valids,
43	/// Memory.
44	memory: Memory,
45	/// Stack.
46	stack: Stack,
47}
48
49impl Machine {
50	/// Reference of machine stack.
51	pub fn stack(&self) -> &Stack {
52		&self.stack
53	}
54	/// Mutable reference of machine stack.
55	pub fn stack_mut(&mut self) -> &mut Stack {
56		&mut self.stack
57	}
58	/// Reference of machine memory.
59	pub fn memory(&self) -> &Memory {
60		&self.memory
61	}
62	/// Mutable reference of machine memory.
63	pub fn memory_mut(&mut self) -> &mut Memory {
64		&mut self.memory
65	}
66	/// Return a reference of the program counter.
67	pub fn position(&self) -> &Result<usize, ExitReason> {
68		&self.position
69	}
70
71	/// Create a new machine with given code and data.
72	pub fn new(
73		code: Rc<Vec<u8>>,
74		data: Rc<Vec<u8>>,
75		stack_limit: usize,
76		memory_limit: usize,
77	) -> Self {
78		let valids = Valids::new(&code[..]);
79
80		Self {
81			data,
82			code,
83			position: Ok(0),
84			return_range: U256::zero()..U256::zero(),
85			valids,
86			memory: Memory::new(memory_limit),
87			stack: Stack::new(stack_limit),
88		}
89	}
90
91	/// Explicit exit of the machine. Further step will return error.
92	pub fn exit(&mut self, reason: ExitReason) {
93		self.position = Err(reason);
94	}
95
96	/// Inspect the machine's next opcode and current stack.
97	pub fn inspect(&self) -> Option<(Opcode, &Stack)> {
98		let position = match self.position {
99			Ok(position) => position,
100			Err(_) => return None,
101		};
102		self.code.get(position).map(|v| (Opcode(*v), &self.stack))
103	}
104
105	/// Copy and get the return value of the machine, if any.
106	#[allow(clippy::slow_vector_initialization)]
107	// Clippy complains about not using `no_std`. However, we need to support
108	// `no_std` and we can't use that.
109	pub fn return_value(&self) -> Vec<u8> {
110		if self.return_range.start > U256::from(usize::MAX) {
111			let mut ret = Vec::new();
112			ret.resize(
113				(self.return_range.end - self.return_range.start).as_usize(),
114				0,
115			);
116			ret
117		} else if self.return_range.end > U256::from(usize::MAX) {
118			let mut ret = self.memory.get(
119				self.return_range.start.as_usize(),
120				usize::MAX - self.return_range.start.as_usize(),
121			);
122			while ret.len() < (self.return_range.end - self.return_range.start).as_usize() {
123				ret.push(0);
124			}
125			ret
126		} else {
127			self.memory.get(
128				self.return_range.start.as_usize(),
129				(self.return_range.end - self.return_range.start).as_usize(),
130			)
131		}
132	}
133
134	/// Loop stepping the machine, until it stops.
135	pub fn run(&mut self) -> Capture<ExitReason, Trap> {
136		loop {
137			match self.step() {
138				Ok(()) => (),
139				Err(res) => return res,
140			}
141		}
142	}
143
144	#[inline]
145	/// Step the machine, executing one opcode. It then returns.
146	pub fn step(&mut self) -> Result<(), Capture<ExitReason, Trap>> {
147		let position = *self
148			.position
149			.as_ref()
150			.map_err(|reason| Capture::Exit(reason.clone()))?;
151
152		match self.code.get(position).map(|v| Opcode(*v)) {
153			Some(opcode) => match eval(self, opcode, position) {
154				Control::Continue(p) => {
155					self.position = Ok(position + p);
156					Ok(())
157				}
158				Control::Exit(e) => {
159					self.position = Err(e.clone());
160					Err(Capture::Exit(e))
161				}
162				Control::Jump(p) => {
163					self.position = Ok(p);
164					Ok(())
165				}
166				Control::Trap(opcode) => {
167					#[cfg(feature = "force-debug")]
168					log::trace!(target: "evm", "OpCode Trap: {:?}", opcode);
169
170					self.position = Ok(position + 1);
171					Err(Capture::Trap(opcode))
172				}
173			},
174			None => {
175				self.position = Err(ExitSucceed::Stopped.into());
176				Err(Capture::Exit(ExitSucceed::Stopped.into()))
177			}
178		}
179	}
180}