1use crate::call::Head;
2use crate::trace::{Event, Step};
3use serde::{Deserialize, Serialize};
4use yevm_base::math::lift;
5
6use crate::{Acc, Call, Int, Result, ops, state::State};
7
8const K: usize = 1024;
9
10#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
11pub enum HaltReason {
12 OutOfGas,
13 OutOfMemory,
14 BadCopyRange,
15 BadJump(usize),
16 BadOpcode(u8),
17 NonStatic,
18 StackUnderflow,
19 StackOverflow,
20 GasBelowStipend,
21}
22
23#[derive(Debug)]
24pub enum Fetch {
25 Code(Acc),
26 Nonce(Acc),
27 Balance(Acc),
28 Account(Acc),
29 BlockHash(u64),
30 StateCell(Acc, Int),
31}
32
33#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
34pub enum CallMode {
35 Call(usize, usize),
36 Static(usize, usize),
37 Delegate(usize, usize),
38 CallCode(usize, usize),
39 Create(Acc),
40 Create2(Acc),
41}
42
43impl CallMode {
44 pub fn target(&self) -> Option<(usize, usize)> {
45 match self {
46 Self::Call(offset, size) => Some((*offset, *size)),
47 Self::Static(offset, size) => Some((*offset, *size)),
48 Self::Delegate(offset, size) => Some((*offset, *size)),
49 Self::CallCode(offset, size) => Some((*offset, *size)),
50 _ => None,
51 }
52 }
53
54 pub fn created(&self) -> Option<Acc> {
55 match self {
56 Self::Create(acc) => Some(*acc),
57 Self::Create2(acc) => Some(*acc),
58 _ => None,
59 }
60 }
61}
62
63pub enum StepResult {
64 End,
65 Ok,
66 Call(Call, CallMode),
67 Return(Vec<u8>),
68 Revert(Vec<u8>),
69 Halt(HaltReason),
70 Fetch(Fetch),
71}
72
73impl From<HaltReason> for StepResult {
74 fn from(reason: HaltReason) -> Self {
75 StepResult::Halt(reason)
76 }
77}
78
79#[derive(Clone, Copy, Debug)]
80pub struct Gas {
81 pub limit: i64,
82 pub spent: i64,
83 pub refund: i64,
84 pub finalized: i64,
85}
86
87impl Gas {
88 pub fn new(gas: u64) -> Self {
89 Self {
90 limit: gas as i64,
91 spent: 0,
92 refund: 0,
93 finalized: 0,
94 }
95 }
96
97 pub fn remaining(&self) -> i64 {
98 self.limit - self.spent }
100
101 pub fn refund(&mut self, gas: i64) -> EvmResult<()> {
102 if self.refund + gas >= 0 {
103 self.refund += gas;
104 Ok(())
105 } else {
106 Err(EvmYield::Halt(HaltReason::OutOfGas))
107 }
108 }
109
110 pub fn charge(&mut self, gas: i64) -> EvmResult<i64> {
111 let rem = self.remaining();
112 if rem >= gas {
113 self.spent += gas;
114 Ok(rem - gas)
115 } else {
116 self.spent += rem;
117 Err(EvmYield::Halt(HaltReason::OutOfGas))
118 }
119 }
120
121 pub fn drain(&mut self) {
122 self.spent = self.limit;
123 self.refund = 0;
124 }
125}
126
127pub struct Context {
128 pub origin: Acc,
129 pub is_static: bool,
130 pub depth: usize,
131 pub this: Acc,
132}
133
134#[derive(Debug)]
135pub enum EvmYield {
136 Halt(HaltReason),
137 Fetch(Fetch),
138 Return(Vec<u8>),
139 Revert(Vec<u8>),
140 Call(Call, CallMode),
141}
142
143pub type EvmResult<T> = std::result::Result<T, EvmYield>;
144
145pub struct Evm {
146 pub pc: usize,
147 pub gas: Gas,
148 pub stack: Vec<Int>,
149 pub memory: Vec<u8>,
150 pub code: Vec<u8>,
151 pub head: Head,
152 pub ret: Vec<u8>,
153
154 pub chain_id: Int,
155 pub gas_price: Int,
156 pub blob_hashes: Vec<Int>,
157 pub(crate) pending_stack_pops: usize,
158 pub(crate) pending_stack_push: Vec<Int>,
159 pub(crate) pending_gas_charge: i64,
160 pub(crate) pending_gas_refund: i64,
161 pub(crate) pending_acc_warmup: [Acc; 2],
162 pub(crate) pending_acc_count: usize,
163 pub(crate) pending_key_warmup: Option<(Acc, Int)>,
164
165 pub(crate) step: Option<Step>,
166}
167
168impl Evm {
169 pub const STACK_SIZE_LIMIT: usize = 1024;
170 pub const MEMORY_SIZE_LIMIT: usize = (1_usize << 24);
173
174 pub fn new(
175 head: Head,
176 code: Vec<u8>,
177 gas: u64,
178 chain_id: Int,
179 gas_price: Int,
180 blob_hashes: Vec<Int>,
181 ) -> Self {
182 Self {
183 pc: 0,
184 gas: Gas::new(gas),
185 stack: Vec::with_capacity(Self::STACK_SIZE_LIMIT),
186 memory: Vec::with_capacity(4 * K),
187 code,
188 head,
189 ret: Vec::new(),
190 chain_id,
191 gas_price,
192 blob_hashes,
193 pending_stack_pops: 0,
194 pending_stack_push: Vec::new(),
195 pending_gas_charge: 0,
196 pending_gas_refund: 0,
197 pending_acc_warmup: [Acc::ZERO; 2],
198 pending_acc_count: 0,
199 pending_key_warmup: None,
200 step: None,
201 }
202 }
203
204 pub fn peek_usize<const N: usize>(&mut self) -> EvmResult<[usize; N]> {
205 let mut ret = [0usize; N];
206 let pop = self.peek::<N>()?;
207 for (i, item) in ret.iter_mut().enumerate() {
208 *item = pop[i].as_usize();
209 }
210 Ok(ret)
211 }
212
213 pub fn peek<const N: usize>(&mut self) -> EvmResult<[Int; N]> {
214 let mut ret = [Int::ZERO; N];
215 if self.stack.len() < N {
216 return Err(EvmYield::Halt(HaltReason::StackUnderflow));
217 }
218 for (slot, value) in ret.iter_mut().zip(self.stack.iter().rev()) {
219 *slot = *value;
220 }
221 self.pending_stack_pops = N;
222 Ok(ret)
223 }
224
225 pub fn apply(&mut self, state: &mut impl State) {
226 for _ in 0..self.pending_stack_pops {
227 let _ = self.stack.pop();
228 }
229 self.pending_stack_pops = 0;
230
231 for int in self.pending_stack_push.drain(..) {
232 self.stack.push(int);
233 }
234 assert!(self.pending_stack_push.is_empty());
235
236 self.gas.spent += self.pending_gas_charge;
237 self.pending_gas_charge = 0;
238
239 self.gas.refund += self.pending_gas_refund;
240 self.pending_gas_refund = 0;
241
242 for i in 0..self.pending_acc_count {
243 state.warm_acc(&self.pending_acc_warmup[i]);
244 }
245 self.pending_acc_count = 0;
246
247 if let Some((acc, key)) = self.pending_key_warmup.take() {
248 state.warm_key(&acc, &key);
249 }
250 }
251
252 pub fn reset(&mut self) {
253 self.pending_stack_pops = 0;
254 self.pending_stack_push.clear();
255 self.pending_gas_charge = 0;
256 self.pending_gas_refund = 0;
257 self.pending_acc_count = 0;
258 self.pending_key_warmup = None;
259 }
260
261 pub fn push(&mut self, int: Int) -> EvmResult<()> {
262 let effective = self
263 .stack
264 .len()
265 .saturating_sub(self.pending_stack_pops)
266 .saturating_add(self.pending_stack_push.len());
267 if effective >= Self::STACK_SIZE_LIMIT {
268 return Err(EvmYield::Halt(HaltReason::StackOverflow));
269 }
270 self.pending_stack_push.push(int);
271 Ok(())
272 }
273
274 pub fn warm_acc(&mut self, acc: &Acc) {
275 self.pending_acc_warmup[self.pending_acc_count] = *acc;
276 self.pending_acc_count += 1;
277 }
278
279 pub fn warm_key(&mut self, acc: &Acc, key: &Int) {
280 self.pending_key_warmup = Some((*acc, *key));
281 }
282
283 pub fn gas_remaining(&self) -> i64 {
284 self.gas.remaining() - self.pending_gas_charge
285 }
286
287 pub fn gas_charge(&mut self, gas: i64) -> EvmResult<()> {
288 if gas > self.gas_remaining() {
289 self.pending_gas_charge += self.gas.remaining();
290 return Err(EvmYield::Halt(HaltReason::OutOfGas));
291 }
292 self.pending_gas_charge += gas;
293 Ok(())
294 }
295
296 pub fn gas_refund(&mut self, gas: i64) -> EvmResult<()> {
297 self.pending_gas_refund += gas;
298 Ok(())
299 }
300
301 pub fn mem_expand(&mut self, offset: usize, size: usize) -> EvmResult<()> {
302 if size == 0 {
303 return Ok(());
304 }
305 mem_check(offset, size)?;
306 let len = self.memory.len();
307 let end = (offset + size).div_ceil(32) * 32;
308 if end > len {
309 let old_words = (len / 32) as i64;
310 let new_words = (end / 32) as i64;
311 let cost = (new_words * new_words / 512 + 3 * new_words)
312 - (old_words * old_words / 512 + 3 * old_words);
313 self.gas_charge(cost)?;
314 self.memory.resize(end, 0);
315 }
316 Ok(())
317 }
318
319 pub fn mem_expand_max(&mut self, regions: &[(usize, usize)]) -> EvmResult<()> {
322 let mut max_end = self.memory.len();
323 for (offset, size) in regions {
324 if *size > 0 {
325 mem_check(*offset, *size)?;
326 let end = (offset + size).div_ceil(32) * 32;
327 max_end = max_end.max(end);
328 }
329 }
330 let len = self.memory.len();
331 if max_end > len {
332 let old_words = (len / 32) as i64;
333 let new_words = (max_end / 32) as i64;
334 let cost = (new_words * new_words / 512 + 3 * new_words)
335 - (old_words * old_words / 512 + 3 * old_words);
336 self.gas_charge(cost)?;
337 self.memory.resize(max_end, 0);
338 }
339 Ok(())
340 }
341
342 pub fn mem_put(&mut self, offset: usize, size: usize, source: &[u8]) -> EvmResult<()> {
343 self.mem_expand(offset, size)?;
344 if size > 0 && !source.is_empty() {
345 let len = source.len().min(size);
346 self.memory[offset..offset + len].copy_from_slice(&source[..len]);
347 }
348 Ok(())
349 }
350
351 pub fn mem_get(&mut self, offset: usize, size: usize) -> EvmResult<Vec<u8>> {
352 self.mem_expand(offset, size)?;
353 let lo = offset.min(self.memory.len());
354 let hi = (offset + size).min(self.memory.len());
355 let mut ret = vec![0u8; size];
356 ret[..hi - lo].copy_from_slice(&self.memory[lo..hi]);
357 Ok(ret)
358 }
359
360 pub fn data(&self, pc: usize) -> Vec<u8> {
361 let op = self.code[pc];
362 let len = match op {
363 0x60..0x80 => op as usize - 0x60 + 1, _ => 0,
365 };
366 let lo = (pc + 1).min(self.code.len());
367 let hi = (pc + 1 + len).min(self.code.len());
368 let mut ret = vec![0; len];
369 let len = hi - lo;
370 ret[0..len].copy_from_slice(&self.code[lo..hi]);
371 ret
372 }
373
374 pub fn step(
375 &mut self,
376 ctx: &Context,
377 call: &Call,
378 state: &mut impl State,
379 ) -> Result<StepResult> {
380 let Some(op) = self.code.get(self.pc).copied() else {
381 return Ok(StepResult::End);
382 };
383 let name = ops::OPS[op as usize];
384
385 if state.is_tracing() {
386 let pc = self.pc;
387 let name = if name.starts_with("INVALID/") {
388 "INVALID".to_string()
389 } else {
390 name.to_string()
391 };
392 let data = self.data(pc);
393 let data = if data.is_empty() {
394 None
395 } else {
396 Some(data.into())
397 };
398 let gas = self.gas.remaining().max(0) as u64;
399 self.step = Some(Step {
400 pc,
401 op,
402 name,
403 data,
404 gas,
405 stack: self.stack.len(),
406 memory: self.memory.len(),
407 debug: vec![],
408 });
409 }
410
411 match ops::dispatch(op, self, ctx, call, state) {
418 Ok(()) => {
419 self.apply(state);
420 if !is_jump(op) {
421 self.pc += 1;
422 }
423 if let Some(mut step) = self.step.take() {
424 let cost = step.gas - self.gas.remaining().max(0) as u64;
425 step.gas = self.gas.remaining().max(0) as u64;
426 step.stack = self.stack.len();
427 step.memory = self.memory.len();
428 step.debug.push(format!("cost={cost}"));
429 if self.gas.refund != 0 {
430 step.debug.push(format!("gas_refund={}", self.gas.refund));
431 }
432 state.emit(Event::Step(step));
433 }
434 Ok(StepResult::Ok)
435 }
436 Err(EvmYield::Fetch(fetch)) => {
437 self.reset();
438 Ok(StepResult::Fetch(fetch))
439 }
440 Err(EvmYield::Halt(reason)) => {
441 self.apply(state);
442 if let Some(mut step) = self.step.take() {
443 step.gas = self.gas.remaining().max(0) as u64;
444 step.stack = self.stack.len();
445 step.memory = self.memory.len();
446 step.debug.push(format!("HALT:{:?}", reason));
447 state.emit(Event::Step(step));
448 }
449 Ok(StepResult::Halt(reason))
450 }
451 Err(EvmYield::Return(ret)) => {
452 self.apply(state);
453 if let Some(mut step) = self.step.take() {
454 step.gas = self.gas.remaining().max(0) as u64;
455 step.stack = self.stack.len();
456 step.memory = self.memory.len();
457 step.debug.push(format!("RETURN:size={}", ret.len()));
458 state.emit(Event::Step(step));
459 }
460 Ok(StepResult::Return(ret))
461 }
462 Err(EvmYield::Revert(ret)) => {
463 self.apply(state);
464 if let Some(mut step) = self.step.take() {
465 step.gas = self.gas.remaining().max(0) as u64;
466 step.stack = self.stack.len();
467 step.memory = self.memory.len();
468 step.debug.push(format!("REVERT:size={}", ret.len()));
469 state.emit(Event::Step(step));
470 }
471 Ok(StepResult::Revert(ret))
472 }
473 Err(EvmYield::Call(call, mode)) => {
474 self.apply(state);
475 if let Some(mut step) = self.step.take() {
476 step.gas = self.gas.remaining().max(0) as u64;
477 step.stack = self.stack.len();
478 step.memory = self.memory.len();
479 step.debug.push(format!(
480 "CALL:to={},gas={}",
481 call.to.unwrap_or_default(),
482 call.gas
483 ));
484 if !call.eth.is_zero() {
485 step.debug.push(format!("CALL:eth={}", call.eth));
486 }
487 step.debug.push(format!("CALL:mode={mode:?}"));
488 state.emit(Event::Step(step));
489 }
490 Ok(StepResult::Call(call, mode))
491 }
492 }
493 }
494}
495
496pub fn mem_check_int(offset: Int, size: Int) -> EvmResult<()> {
498 let limit = Int::from(Evm::MEMORY_SIZE_LIMIT);
499 let add = lift(|[a, b]| a + b);
500 let gt = lift(|[a, b]| {
501 if a > b {
502 yevm_base::math::U256::ONE
503 } else {
504 yevm_base::math::U256::ZERO
505 }
506 });
507 if !gt([size, limit]).is_zero() {
508 return Err(EvmYield::Halt(HaltReason::OutOfMemory));
509 }
510 let end = add([offset, size]);
511 if !gt([end, limit]).is_zero() {
512 return Err(EvmYield::Halt(HaltReason::OutOfMemory));
513 }
514 Ok(())
515}
516
517pub fn mem_check(offset: usize, size: usize) -> EvmResult<()> {
518 let limit = Evm::MEMORY_SIZE_LIMIT;
519 if size <= limit && offset <= limit.saturating_sub(size) {
520 return Ok(());
521 }
522 Err(EvmYield::Halt(HaltReason::OutOfMemory))
523}
524
525fn is_jump(op: u8) -> bool {
526 op == 0x56 || op == 0x57
527}