1use yevm_base::math::U256;
2use yevm_base::math::lift;
3use yevm_misc::keccak256;
4
5use yevm_misc::buf::Buf;
6
7use crate::Fetch;
8use crate::Tx;
9use crate::evm::{CallMode, Context, Evm, Gas, StepResult};
10use crate::misc::{create_address, is_precompile};
11use crate::{Acc, Call, Error, Int, Result};
12use crate::{
13 call::Head,
14 chain::{Chain, fetch},
15 state::{Account, State},
16 trace::Event,
17};
18
19const MAX_CALL_DEPTH: usize = 1024;
20
21const BEACON_ROOTS: Acc = yevm_base::acc::acc("0x000f3df6d732807ef1319fb7b8bb8522d0beac02");
22const HISTORY_BUFFER_LENGTH: u64 = 8191;
23
24pub async fn pre_block(head: &Head, state: &mut impl State, chain: &impl Chain) -> Result<()> {
26 let Some(root) = head.parent_beacon_block_root else {
27 return Ok(());
28 };
29 fetch(Fetch::Account(BEACON_ROOTS), state, chain).await?;
30 let timestamp = head.timestamp.as_u64();
31 let slot = timestamp % HISTORY_BUFFER_LENGTH;
32 state.init(&BEACON_ROOTS, &Int::from(slot), Int::from(timestamp));
33 state.init(
34 &BEACON_ROOTS,
35 &Int::from(slot + HISTORY_BUFFER_LENGTH),
36 root,
37 );
38 Ok(())
39}
40const MAX_STEPS: u64 = 10_000_000;
41const MAX_CODE_SIZE: usize = 24_576;
42const CODE_DEPOSIT_GAS: i64 = 200;
43
44#[derive(Debug)]
45pub enum CallResult {
46 Done { status: Int, ret: Buf, gas: Gas },
47 Created { acc: Acc, code: Buf, gas: Gas },
48}
49
50impl CallResult {
51 pub fn gas(&self) -> &Gas {
52 match self {
53 Self::Done { gas, .. } => gas,
54 Self::Created { gas, .. } => gas,
55 }
56 }
57
58 pub fn gas_mut(&mut self) -> &mut Gas {
59 match self {
60 Self::Done { gas, .. } => gas,
61 Self::Created { gas, .. } => gas,
62 }
63 }
64}
65
66pub struct Executor {
67 pub call: Call,
68 pub callstack: Vec<CallFrame>,
69 effective_gas_price: Int,
71 pub fetches: usize,
72
73 #[cfg(not(target_arch = "wasm32"))]
74 pub fetching: std::time::Duration,
75}
76
77pub struct CallFrame {
78 pub call: Call,
79 pub evm: Evm,
80 pub ctx: Context,
81 pub checkpoint: usize,
82 pub target: (usize, usize),
84 pub is_create: bool,
87}
88
89pub fn intrinsic(
90 call: &Call,
91 tx: &Tx,
92 head: &Head,
93 state: &mut impl State,
94) -> Result<(i64, i64, Int)> {
95 let mut total = 21_000i64;
96 let mut floor = 21_000i64;
97 let has_code = call
98 .to
99 .and_then(|to| state.code(&to))
100 .is_some_and(|(c, _)| !c.0.is_empty());
101 let is_create = call.is_create() && !has_code;
102 if is_create {
103 total += 32_000;
104 let initcode_cost = 2 * ((call.data.0.len() as i64 + 31) / 32);
106 total += initcode_cost;
107 }
108 let zeroes = call.data.0.iter().filter(|b| **b == 0).count();
109 let non_zeroes = call.data.0.len() - zeroes;
110 total += (zeroes * 4 + non_zeroes * 16) as i64;
111 let tokens = (zeroes + non_zeroes * 4) as i64;
113 floor += 10 * tokens;
114
115 state.warm_acc(&call.by);
118 if let Some(to) = call.to {
119 state.warm_acc(&to);
120 }
121 state.warm_acc(&head.coinbase);
122 for i in 1u64..=0xa {
123 state.warm_acc(&Acc::from(i));
124 }
125 state.warm_acc(&Acc::from(0x100u64)); for (acc, keys) in tx
129 .access_list
130 .iter()
131 .map(|item| (item.address, &item.storage_keys))
132 {
133 let al_cost = 2_400 + 1_900 * keys.len() as i64;
134 total += al_cost;
135 state.warm_acc(&acc);
136 for key in keys {
137 state.warm_key(&acc, key);
138 }
139 }
140
141 let lt = lift(|[a, b]| if a < b { U256::ONE } else { U256::ZERO });
143 if tx.max_fee_per_gas.is_zero() {
144 if !lt([tx.gas_price, head.base_fee]).is_zero() {
146 return Err(Error::MaxFeeLessThanBaseFee);
147 }
148 } else {
149 if !lt([tx.max_fee_per_gas, head.base_fee]).is_zero() {
151 return Err(Error::MaxFeeLessThanBaseFee);
152 }
153 }
154
155 let effective_gas_price = if tx.max_fee_per_gas.is_zero() {
158 tx.gas_price
159 } else {
160 let min = lift(|[a, b]| a.min(b));
161 let sum = lift(|[a, b]| a + b);
162 min([
163 tx.max_fee_per_gas,
164 sum([head.base_fee, tx.max_priority_fee_per_gas]),
165 ])
166 };
167
168 let mul_overflows = lift(|[a, b]| {
170 if a.checked_mul(b).is_none() {
171 U256::ONE
172 } else {
173 U256::ZERO
174 }
175 });
176 if !mul_overflows([effective_gas_price, Int::from(call.gas)]).is_zero() {
177 return Err(Error::GasLimitPriceProductOverflow);
178 }
179
180 let mul = lift(|[a, b]| a * b);
183 let sub = lift(|[a, b]| a - b);
184 let add = lift(|[a, b]| a + b);
185 let gt = lift(|[a, b]| if a > b { U256::ONE } else { U256::ZERO });
186 let max_gas_price = if tx.max_fee_per_gas.is_zero() {
187 effective_gas_price
188 } else {
189 tx.max_fee_per_gas
190 };
191 let upfront_check = mul([Int::from(call.gas), max_gas_price]);
192 let upfront = mul([Int::from(call.gas), effective_gas_price]);
193
194 const GAS_PER_BLOB: u64 = 0x20000;
196 let blob_gas_cost = if let Some(_excess) = head.excess_blob_gas {
197 Int::from(tx.blob_versioned_hashes.len() as u64 * GAS_PER_BLOB)
201 } else {
202 Int::ZERO
203 };
204
205 let total_cost = add([upfront_check, call.eth]);
206 let balance = state.balance(&call.by).unwrap_or_default();
207 if !gt([total_cost, balance]).is_zero() {
208 }
211 state.set_value(&call.by, sub([balance, add([upfront, blob_gas_cost])]));
212
213 let auth_cost = 25_000 * tx.authorization_list.len() as i64;
218 total += auth_cost;
219 Ok((total, floor, effective_gas_price))
220}
221
222pub fn finalized(
223 call: &Call,
224 tx: &Tx,
225 head: &Head,
226 effective_gas_price: Int,
227 result: &CallResult,
228 state: &mut impl State,
229 floor: i64,
230) -> i64 {
231 let gas = result.gas();
233
234 let effective_refund = gas.refund.min(gas.spent / 5);
235 let final_gas = (gas.spent - effective_refund).max(0).max(floor) as u64;
237 let returned_gas = (gas.limit.max(0) as u64).saturating_sub(final_gas);
238 let mul = lift(|[a, b]| a * b);
239 let sub = lift(|[a, b]| a - b);
240 let add = lift(|[a, b]| a + b);
241
242 let returned_cost = mul([Int::from(returned_gas), effective_gas_price]);
244 let balance = state.balance(&call.by).unwrap_or_default();
245 state.set_value(&call.by, add([balance, returned_cost]));
246
247 let priority_fee = if tx.max_fee_per_gas.is_zero() {
249 sub([effective_gas_price, head.base_fee])
250 } else {
251 lift(|[a, b]| a.min(b))([
252 tx.max_priority_fee_per_gas,
253 sub([effective_gas_price, head.base_fee]),
254 ])
255 };
256
257 let tip = mul([Int::from(final_gas), priority_fee]);
258 if !tip.is_zero() {
259 let balance = state.balance(&head.coinbase).unwrap_or_default();
260 state.set_value(&head.coinbase, add([balance, tip]));
261 }
262
263 let base_burn = mul([Int::from(final_gas), head.base_fee]);
264 state.emit(Event::Fee(
265 call.by,
266 head.coinbase,
267 base_burn,
268 tip,
269 final_gas,
270 ));
271
272 final_gas as i64
273}
274
275pub fn transfer(call: &Call, mode: &CallMode, state: &mut impl State) {
276 if call.eth.is_zero() {
277 return;
278 }
279 let to = mode.created().or(call.to);
280 if let Some(to) = to {
281 let sub = lift(|[a, b]| a - b);
282 let add = lift(|[a, b]| a + b);
283 let by0 = state.balance(&call.by).unwrap_or_default();
284 state.set_value(&call.by, sub([by0, call.eth]));
285 let to0 = state.balance(&to).unwrap_or_default();
286 state.set_value(&to, add([to0, call.eth]));
287 state.emit(Event::Move(call.by, to, call.eth));
288 }
289}
290
291impl Executor {
292 pub fn new(call: Call) -> Self {
293 Self {
294 call,
295 callstack: vec![],
296 effective_gas_price: Int::ZERO,
297 fetches: 0,
298
299 #[cfg(not(target_arch = "wasm32"))]
300 fetching: std::time::Duration::ZERO,
301 }
302 }
303
304 pub async fn run(
305 &mut self,
306 mut tx: Tx,
307 head: Head,
308 state: &mut impl State,
309 chain: &impl Chain,
310 ) -> Result<CallResult> {
311 if !self.callstack.is_empty() {
312 return Err(eyre::eyre!("inconsistent state: call stack empty").into());
313 }
314 for acc in [&self.call.by, &head.coinbase] {
315 if state.acc(acc).is_none() {
316 fetch(Fetch::Account(*acc), state, chain).await?;
317 state.warm_acc(acc);
318 }
319 }
320 if let Some(acc) = self.call.to.as_ref()
321 && state.acc(acc).is_none()
322 {
323 fetch(Fetch::Account(*acc), state, chain).await?;
324 state.warm_acc(acc);
325 }
326
327 if tx.chain_id.is_zero() {
328 tx.chain_id = state.get_chain_id().into();
329 if tx.chain_id.is_zero() {
330 return Err(Error::UndefinedChainId);
331 }
332 }
333
334 if !tx.max_fee_per_gas.is_zero() && tx.max_fee_per_gas < head.base_fee {
338 return Err(Error::MaxFeeLessThanBaseFee);
339 }
340
341 if !tx.max_fee_per_gas.is_zero() && tx.max_priority_fee_per_gas > tx.max_fee_per_gas {
343 return Err(Error::PriorityGreaterThanMaxFee);
344 }
345
346 let gt = lift(|[a, b]| if a > b { U256::ONE } else { U256::ZERO });
348 if !gt([Int::from(self.call.gas), head.gas_limit]).is_zero() {
349 return Err(Error::GasAllowanceExceeded);
350 }
351
352 if let Some(acc) = state.acc(&self.call.by)
354 && !(state.auth(&self.call.by).is_some() || acc.code.0.0.is_empty())
355 {
356 }
359
360 let has_code = self
361 .call
362 .to
363 .and_then(|to| state.code(&to))
364 .is_some_and(|(c, _)| !c.0.is_empty())
365 && self.call.to.and_then(|to| state.auth(&to)).is_none();
366
367 let mode = if self.call.is_create() && !has_code {
369 let nonce = state.nonce(&self.call.by).unwrap_or_default();
370 let created = create_address(&self.call.by, nonce.as_u64());
371 CallMode::Create(created)
372 } else {
373 CallMode::Call(0, 0)
374 };
375
376 state.inc_nonce(&self.call.by, Int::ONE);
379
380 let eip7702_refund =
381 crate::eip7702::apply_authorization_list(&tx, tx.chain_id.as_u64(), state, chain)
382 .await?;
383
384 let (intrinsic, floor, effective_gas_price) = intrinsic(&self.call, &tx, &head, state)?;
385 self.effective_gas_price = effective_gas_price;
386 if (self.call.gas as i64) < intrinsic {
387 return Err(Error::GasTooLow {
388 have: self.call.gas,
389 want: intrinsic as u64,
390 });
391 }
392
393 let mut frame = prepare(
396 head.clone(),
397 self.call.clone(),
398 mode,
399 None,
400 tx.chain_id.to(),
401 effective_gas_price,
402 tx.blob_versioned_hashes.clone(),
403 state,
404 chain,
405 )
406 .await?;
407 if let CallMode::Create(created) = mode {
410 let existing_nonce = state.nonce(&created).unwrap_or(Int::ZERO);
411 let has_code = state.code(&created).is_some_and(|(c, _)| !c.0.is_empty());
412 if !existing_nonce.is_zero() || has_code {
413 frame.evm.gas.drain();
415 let gas = frame.evm.gas;
416 state.revert_to(frame.checkpoint);
417 let result = CallResult::Done {
418 status: Int::ZERO,
419 ret: vec![].into(),
420 gas,
421 };
422 let mut result = result;
423 result.gas_mut().refund += eip7702_refund;
424 let gas_final = finalized(
425 &self.call,
426 &tx,
427 &head,
428 effective_gas_price,
429 &result,
430 state,
431 floor,
432 );
433 result.gas_mut().finalized = gas_final;
434 state.apply();
435 return Ok(result);
436 }
437 let existing_balance = state.balance(&created).unwrap_or(Int::ZERO);
439 state.create(
440 created,
441 Account {
442 value: existing_balance,
443 nonce: Int::ONE,
444 code: (Buf::default(), Int::ZERO),
445 },
446 );
447 state.warm_acc(&created);
449 }
450 transfer(&self.call, &mode, state);
451 let _ = frame.evm.gas_charge(intrinsic);
452
453 let call_to_is_precompile = self
455 .call
456 .to
457 .map(|to| is_precompile(&to))
458 .unwrap_or_default();
459 if !frame.is_create && call_to_is_precompile {
460 let id = self.call.to.expect("verified precompile address").as_u64();
461 let (ok, out, gas_used) =
462 crate::pre::run(id, &self.call.data.0, frame.evm.gas_remaining());
463 let _ = frame.evm.gas_charge(gas_used);
464 frame.evm.apply(state);
465
466 let status = if ok { Int::ONE } else { Int::ZERO };
467 if ok {
468 state.emit(Event::Return(out.clone().into(), gas_used.max(0) as u64));
469 } else {
470 state.revert_to(frame.checkpoint);
471 state.emit(Event::Revert(vec![].into(), gas_used.max(0) as u64));
472 }
473
474 let mut result = CallResult::Done {
475 status,
476 ret: out.into(),
477 gas: frame.evm.gas,
478 };
479 result.gas_mut().refund += eip7702_refund;
480 let gas_final = finalized(
481 &self.call,
482 &tx,
483 &head,
484 effective_gas_price,
485 &result,
486 state,
487 floor,
488 );
489 result.gas_mut().finalized = gas_final;
490 state.apply();
491 return Ok(result);
492 }
493
494 state.emit(Event::Call(self.call.clone(), mode));
495 self.callstack.push(frame);
496
497 let mut result: Option<CallResult> = None;
498 let mut last_popped_checkpoint: Option<usize> = None;
499 let mut steps: u64 = 0;
500
501 while let Some(this) = self.callstack.last_mut() {
502 state.set_depth(this.ctx.depth + 1);
503 steps += 1;
504 if steps > MAX_STEPS {
505 this.evm.gas.drain();
506 let gas = this.evm.gas;
507 let checkpoint = this.checkpoint;
508 state.revert_to(checkpoint);
509 self.callstack.clear();
510 return Ok(CallResult::Done {
511 status: Int::ZERO,
512 ret: vec![].into(),
513 gas,
514 });
515 }
516 if let Some(call_result) = result.take() {
518 match call_result {
519 CallResult::Done { status, ret, gas } => {
520 if status.is_zero() {
522 if let Some(cp) = last_popped_checkpoint.take() {
523 state.revert_to(cp);
524 }
525 } else {
526 last_popped_checkpoint = None; }
528 let _ = this.evm.push(status);
529 this.evm.ret = ret.clone().into_vec();
530 let (offset, size) = this.target;
532 if size > 0 {
533 let _ = this.evm.mem_put(offset, size, ret.as_slice());
534 }
535 let return_gas = (gas.limit - gas.spent).max(0);
540 this.evm.gas.spent -= return_gas;
541 if !status.is_zero() {
543 this.evm.gas.refund += gas.refund;
544 }
545 this.evm.apply(state);
546 this.evm.pc += 1;
547 this.target = (0, 0);
548 result = None;
549 }
550 CallResult::Created {
551 acc: addr,
552 code,
553 gas,
554 } => {
555 last_popped_checkpoint = None; let hash = Int::from(keccak256(code.as_slice()).as_ref());
558 state.set_code(&addr, code, hash);
559
560 let _ = this.evm.push(addr.to());
561 let return_gas = (gas.limit - gas.spent).max(0);
562 this.evm.gas.spent -= return_gas;
565 this.evm.gas.refund += gas.refund;
566 this.evm.apply(state);
567 this.evm.pc += 1;
568 this.evm.ret.clear();
569 }
570 }
571 }
572
573 match this.evm.step(&this.ctx, &this.call, state)? {
574 StepResult::Ok => {
575 continue;
576 }
577 StepResult::End => {
578 this.evm.apply(state);
579
580 let is_create = this.is_create;
583 let gas = this.evm.gas;
584 result = Some(if is_create {
585 CallResult::Created {
586 acc: this.ctx.this,
587 code: vec![].into(),
588 gas,
589 }
590 } else {
591 CallResult::Done {
592 status: Int::ONE,
593 ret: vec![].into(),
594 gas,
595 }
596 });
597 last_popped_checkpoint = Some(this.checkpoint);
598 self.callstack.pop();
599 }
600 StepResult::Call(call, mode) => {
601 state.emit(Event::Call(call.clone(), mode));
602 this.evm.apply(state);
603 let call_to_is_precompile =
604 call.to.map(|to| is_precompile(&to)).unwrap_or_default();
605 if call_to_is_precompile {
606 if let Some(to) = call.to
609 && state.acc(&to).is_none()
610 {
611 fetch(Fetch::Account(to), state, chain).await?;
612 }
613
614 this.evm.ret.clear();
616
617 let id = call.to.expect("verified precompile address").as_u64();
620 let (ok, out, gas_used) =
621 crate::pre::run(id, &call.data.0, call.gas as i64);
622 this.evm.ret = out.clone();
623 this.evm.pending_gas_charge -= call.gas as i64;
624 this.evm.pending_gas_charge += gas_used;
625
626 if ok
629 && !call.eth.is_zero()
630 && matches!(mode, CallMode::Call(..))
631 && let Some(call_to) = call.to
632 {
633 let sub = lift(|[a, b]| a - b);
634 let add = lift(|[a, b]| a + b);
635 let by0 = state.balance(&call.by).unwrap_or_default();
636 let to0 = state.balance(&call_to).unwrap_or_default();
637 if call.by != call_to {
638 state.set_value(&call.by, sub([by0, call.eth]));
639 state.set_value(&call_to, add([to0, call.eth]));
640 }
641 }
642
643 let status = if ok { Int::ONE } else { Int::ZERO };
644 if ok {
645 let d = state.get_depth();
646 state.set_depth(d + 1);
647 state.emit(Event::Return(out.clone().into(), gas_used.max(0) as u64));
648 state.set_depth(d);
649 } else {
650 let d = state.get_depth();
651 state.set_depth(d + 1);
652 state.emit(Event::Revert(vec![].into(), gas_used.max(0) as u64));
653 state.set_depth(d);
654 }
655 let (ret_offset, ret_size) = mode.target().unwrap_or_default();
656 this.evm.apply(state);
657 let _ = this.evm.push(status);
658 if !status.is_zero() && ret_size > 0 {
659 let n = ret_size.min(out.len());
660 let _ = this.evm.mem_put(ret_offset, n, &out[..n]);
661 }
662 this.evm.apply(state);
663 this.evm.pc += 1;
664 continue;
665 }
666
667 let is_create = matches!(mode, CallMode::Create(_) | CallMode::Create2(_));
668
669 if let Some(created) = mode.created() {
673 let creator = call.by;
674
675 if this.ctx.depth + 1 > MAX_CALL_DEPTH {
677 this.evm.gas.spent -= call.gas as i64;
679 this.evm.apply(state);
680 let _ = this.evm.push(Int::ZERO);
681 this.evm.apply(state);
682 this.evm.ret = vec![];
683 this.evm.pc += 1;
684 this.target = (0, 0);
685 continue;
686 }
687
688 if !call.eth.is_zero() {
690 let gte = lift(|[a, b]| if a >= b { U256::ONE } else { U256::ZERO });
691 let by0 = state.balance(&creator).unwrap_or_default();
692 if gte([by0, call.eth]).is_zero() {
693 this.evm.gas.spent -= call.gas as i64;
695 this.evm.apply(state);
696 let _ = this.evm.push(Int::ZERO);
697 this.evm.apply(state);
698 this.evm.ret = vec![];
699 this.evm.pc += 1;
700 this.target = (0, 0);
701 continue;
702 }
703 }
704
705 let nonce_max = Int::from(u64::MAX);
707 let creator_nonce = state.nonce(&creator).unwrap_or(Int::ZERO);
708 if creator_nonce >= nonce_max {
709 this.evm.gas.spent -= call.gas as i64;
710 this.evm.apply(state);
711 let _ = this.evm.push(Int::ZERO);
712 this.evm.apply(state);
713 this.evm.ret = vec![];
714 this.evm.pc += 1;
715 this.target = (0, 0);
716 continue;
717 }
718
719 state.inc_nonce(&creator, Int::ONE);
721 state.warm_acc(&created);
723 }
724
725 let checkpoint = state.checkpoint();
726 this.target = mode.target().unwrap_or_default();
727
728 if let Some(created) = mode.created() {
729 let creator = call.by;
730
731 if state.acc(&created).is_none() {
733 fetch(Fetch::Account(created), state, chain).await?;
734 }
735
736 let existing_nonce = state.nonce(&created).unwrap_or(Int::ZERO);
738 let has_code = state.code(&created).is_some_and(|(c, _)| !c.0.is_empty());
739 if !existing_nonce.is_zero() || has_code {
740 state.revert_to(checkpoint);
741 this.evm.apply(state);
742 let _ = this.evm.push(Int::ZERO);
743 this.evm.apply(state);
744 this.evm.ret = vec![];
745 this.evm.pc += 1;
746 this.target = (0, 0);
747 continue;
748 }
749
750 let existing_balance = state.balance(&created).unwrap_or(Int::ZERO);
752 state.create(
753 created,
754 Account {
755 value: existing_balance,
756 nonce: Int::ONE,
757 code: (Buf::default(), Int::ZERO),
758 },
759 );
760
761 if !call.eth.is_zero() {
763 let sub = lift(|[a, b]| a - b);
764 let add = lift(|[a, b]| a + b);
765 let by0 = state.balance(&creator).unwrap_or_default();
766 state.set_value(&creator, sub([by0, call.eth]));
767 let to0 = state.balance(&created).unwrap_or_default();
768 state.set_value(&created, add([to0, call.eth]));
769 }
770 }
771
772 this.evm.ret.clear();
774
775 let mut frame = prepare(
776 head.clone(),
777 call.clone(),
778 mode,
779 Some(&this.ctx),
780 tx.chain_id.to(),
781 self.effective_gas_price,
782 tx.blob_versioned_hashes.clone(),
783 state,
784 chain,
785 )
786 .await?;
787 frame.checkpoint = checkpoint;
790 if frame.ctx.depth > MAX_CALL_DEPTH {
791 state.revert_to(checkpoint);
792 this.evm.gas.spent -= call.gas as i64;
794 this.evm.apply(state);
795 let _ = this.evm.push(Int::ZERO);
796 this.evm.apply(state);
797 this.evm.ret = vec![];
798 this.evm.pc += 1;
799 this.target = (0, 0);
800 continue;
801 }
802
803 if !is_create
805 && !call.eth.is_zero()
806 && matches!(mode, CallMode::Call(..) | CallMode::CallCode(..))
807 {
808 let by = call.by;
809 let by0 = state.balance(&by).unwrap_or_default();
810
811 let gte = lift(|[a, b]| if a >= b { U256::ONE } else { U256::ZERO });
812 if gte([by0, call.eth]).is_zero() {
813 state.revert_to(checkpoint);
814 this.evm.gas.spent -= call.gas as i64;
816 this.evm.apply(state);
817 let _ = this.evm.push(Int::ZERO);
818 this.evm.apply(state);
819 this.evm.ret = vec![];
820 this.evm.pc += 1;
821 this.target = (0, 0);
822 continue;
823 }
824
825 if matches!(mode, CallMode::Call(..))
828 && let Some(to) = call.to
829 {
830 let add = lift(|[a, b]| a + b);
831 let sub = lift(|[a, b]| a - b);
832 let to0 = state.balance(&to).unwrap_or_default();
833 if to != by {
834 state.set_value(&by, sub([by0, call.eth]));
835 state.set_value(&to, add([to0, call.eth]));
836 }
837 }
838 }
839 self.callstack.push(frame);
840 }
841 StepResult::Return(ret) => {
842 state.emit(Event::Return(
843 ret.clone().into(),
844 this.evm.gas.spent.max(0) as u64,
845 ));
846 let is_create = this.is_create;
847 result = Some(if is_create {
848 let deploy_cost = CODE_DEPOSIT_GAS * ret.len() as i64;
849 let starts_with_ef = ret.first() == Some(&0xEF);
851 if ret.len() > MAX_CODE_SIZE
852 || starts_with_ef
853 || this.evm.gas_remaining() < deploy_cost
854 {
855 this.evm.gas.drain();
856 state.revert_to(this.checkpoint);
857 CallResult::Done {
858 status: Int::ZERO,
859 ret: vec![].into(),
860 gas: this.evm.gas,
861 }
862 } else {
863 this.evm.gas.spent += deploy_cost;
864 CallResult::Created {
865 acc: this.ctx.this,
866 code: ret.into(),
867 gas: this.evm.gas,
868 }
869 }
870 } else {
871 CallResult::Done {
872 status: Int::ONE,
873 ret: ret.into(),
874 gas: this.evm.gas,
875 }
876 });
877 self.callstack.pop();
878 }
879 StepResult::Revert(ret) => {
880 state.emit(Event::Revert(
881 ret.clone().into(),
882 this.evm.gas.spent.max(0) as u64,
883 ));
884 state.revert_to(this.checkpoint);
885 let mut gas = this.evm.gas;
886 gas.refund = 0;
887 result = Some(CallResult::Done {
888 status: Int::ZERO,
889 ret: ret.into(),
890 gas,
891 });
892 self.callstack.pop();
893 }
894 StepResult::Halt(reason) => {
895 state.emit(Event::Halt(reason, this.evm.gas.limit.max(0) as u64));
896 this.evm.apply(state);
897 this.evm.gas.drain();
898 state.revert_to(this.checkpoint);
899 result = Some(CallResult::Done {
900 status: Int::ZERO,
901 ret: vec![].into(),
902 gas: this.evm.gas,
903 });
904 self.callstack.pop();
905 }
906 StepResult::Fetch(f) => {
907 #[cfg(not(target_arch = "wasm32"))]
908 let now = std::time::Instant::now();
909
910 fetch(f, state, chain).await?;
911
912 #[cfg(not(target_arch = "wasm32"))]
913 {
914 let elapsed = now.elapsed();
915 self.fetching += elapsed;
916 }
917
918 self.fetches += 1;
919 this.evm.reset();
920 }
921 }
922 }
923
924 let mut result =
925 result.ok_or::<Error>(eyre::eyre!("inconsistent state: call result missing").into())?;
926
927 let should_revert = match &result {
929 CallResult::Done { status, .. } => status.is_zero(),
930 CallResult::Created { acc, .. } => acc == &Acc::ZERO,
931 };
932 if should_revert && let Some(cp) = last_popped_checkpoint.take() {
933 state.revert_to(cp);
934 }
935
936 if let CallResult::Created {
938 acc: addr,
939 ref code,
940 ..
941 } = result
942 && !code.0.is_empty()
943 {
944 let hash = Int::from(keccak256(code.as_slice()).as_ref());
945 state.set_code(&addr, code.clone(), hash);
946 }
947
948 state.set_depth(0);
949
950 result.gas_mut().refund += eip7702_refund;
951 let gas_final = finalized(
952 &self.call,
953 &tx,
954 &head,
955 effective_gas_price,
956 &result,
957 state,
958 floor,
959 );
960 result.gas_mut().finalized = gas_final;
961
962 state.apply();
963 Ok(result)
964 }
965}
966
967#[allow(clippy::too_many_arguments)]
968async fn prepare(
969 head: Head,
970 mut call: Call,
971 mode: CallMode,
972 ctx: Option<&Context>,
973 chain_id: Int,
974 gas_price: Int,
975 blob_hashes: Vec<Int>,
976 state: &mut impl State,
977 chain: &impl Chain,
978) -> Result<CallFrame> {
979 let is_create = matches!(mode, CallMode::Create(_) | CallMode::Create2(_));
980 let code = if is_create {
981 std::mem::take(&mut call.data)
982 } else {
983 let call_to = call.to.expect("checked non-create call");
984 if let Some((code, _)) = state.code(&call_to) {
985 code
986 } else {
987 fetch(Fetch::Account(call_to), state, chain).await?;
988 if let Some(account) = state.acc(&call_to) {
989 account.code.0.clone()
990 } else {
991 Buf::default()
992 }
993 }
994 };
995 let code = if let Some(delegate) = call.to.and_then(|to| state.auth(&to)) {
1000 if let Some((code, _)) = state.code(&delegate) {
1001 state.warm_acc(&delegate);
1002 code
1003 } else {
1004 fetch(Fetch::Account(delegate), state, chain).await?;
1005 if let Some(account) = state.acc(&delegate) {
1006 state.warm_acc(&delegate);
1007 account.code.0.clone()
1008 } else {
1009 Buf::default()
1010 }
1011 }
1012 } else {
1013 code
1014 };
1015 let evm = Evm::new(
1017 head,
1018 code.into_vec(),
1019 call.gas,
1020 chain_id,
1021 gas_price,
1022 blob_hashes,
1023 );
1024 let is_static = matches!(mode, CallMode::Static(_, _));
1025 let this = match mode {
1026 CallMode::Create(acc) => acc,
1027 CallMode::Create2(acc) => acc,
1028 CallMode::Call(_, _) | CallMode::Static(_, _) => call.to.expect("CALL must have 'to' set"),
1029 CallMode::CallCode(_, _) | CallMode::Delegate(_, _) => {
1030 ctx.map(|c| c.this).unwrap_or(call.by)
1031 }
1032 };
1033 let ctx = if let Some(ctx) = ctx {
1034 Context {
1035 origin: ctx.origin,
1036 is_static: ctx.is_static || is_static,
1037 depth: ctx.depth + 1,
1038 this,
1039 }
1040 } else {
1041 Context {
1042 origin: call.by,
1043 is_static,
1044 depth: 0,
1045 this,
1046 }
1047 };
1048 let is_create = matches!(mode, CallMode::Create(_) | CallMode::Create2(_));
1049 let checkpoint = state.checkpoint();
1050 Ok(CallFrame {
1051 call,
1052 evm,
1053 ctx,
1054 checkpoint,
1055 target: (0, 0),
1056 is_create,
1057 })
1058}