1use crate::{
2 evm::FrameTr, item_or_result::FrameInitOrResult, precompile_provider::PrecompileProvider,
3 CallFrame, CreateFrame, FrameData, FrameResult, ItemOrResult,
4};
5use context::result::FromStringError;
6use context_interface::{
7 context::{take_error, ContextError},
8 journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalTr},
9 local::{FrameToken, OutFrame},
10 Cfg, ContextTr, Database,
11};
12use core::cmp::min;
13use derive_where::derive_where;
14use interpreter::{
15 interpreter::{EthInterpreter, ExtBytecode},
16 interpreter_action::FrameInit,
17 interpreter_types::ReturnData,
18 CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme,
19 FrameInput, Gas, GasTracker, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
20 InterpreterResult, InterpreterTypes, SharedMemory,
21};
22use primitives::{
23 constants::CALL_STACK_LIMIT,
24 hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
25 Address, Bytes, U256,
26};
27use state::Bytecode;
28use std::{borrow::ToOwned, boxed::Box, vec::Vec};
29
30#[derive_where(Clone, Debug; IW,
32 <IW as InterpreterTypes>::Stack,
33 <IW as InterpreterTypes>::Memory,
34 <IW as InterpreterTypes>::Bytecode,
35 <IW as InterpreterTypes>::ReturnData,
36 <IW as InterpreterTypes>::Input,
37 <IW as InterpreterTypes>::RuntimeFlag,
38 <IW as InterpreterTypes>::Extend,
39)]
40pub struct EthFrame<IW: InterpreterTypes = EthInterpreter> {
41 pub data: FrameData,
43 pub input: FrameInput,
45 pub depth: usize,
47 pub checkpoint: JournalCheckpoint,
49 pub interpreter: Interpreter<IW>,
51 pub is_finished: bool,
54}
55
56impl<IT: InterpreterTypes> FrameTr for EthFrame<IT> {
57 type FrameResult = FrameResult;
58 type FrameInit = FrameInit;
59}
60
61impl Default for EthFrame<EthInterpreter> {
62 fn default() -> Self {
63 Self::do_default(Interpreter::default())
64 }
65}
66
67impl EthFrame<EthInterpreter> {
68 pub fn invalid() -> Self {
70 Self::do_default(Interpreter::invalid())
71 }
72
73 fn do_default(interpreter: Interpreter<EthInterpreter>) -> Self {
74 Self {
75 data: FrameData::Call(CallFrame {
76 return_memory_range: 0..0,
77 }),
78 input: FrameInput::Empty,
79 depth: 0,
80 checkpoint: JournalCheckpoint::default(),
81 interpreter,
82 is_finished: false,
83 }
84 }
85
86 pub const fn is_finished(&self) -> bool {
88 self.is_finished
89 }
90
91 pub const fn set_finished(&mut self, finished: bool) {
93 self.is_finished = finished;
94 }
95}
96
97pub type ContextTrDbError<CTX> = <<CTX as ContextTr>::Db as Database>::Error;
99
100impl EthFrame<EthInterpreter> {
101 #[expect(clippy::too_many_arguments)]
103 #[inline(always)]
104 pub fn clear(
105 &mut self,
106 data: FrameData,
107 input: FrameInput,
108 depth: usize,
109 memory: SharedMemory,
110 bytecode: ExtBytecode,
111 inputs: InputsImpl,
112 is_static: bool,
113 spec_id: SpecId,
114 gas_limit: u64,
115 reservoir_remaining_gas: u64,
116 checkpoint: JournalCheckpoint,
117 ) {
118 let Self {
119 data: data_ref,
120 input: input_ref,
121 depth: depth_ref,
122 interpreter,
123 checkpoint: checkpoint_ref,
124 is_finished: is_finished_ref,
125 } = self;
126 *data_ref = data;
127 *input_ref = input;
128 *depth_ref = depth;
129 *is_finished_ref = false;
130 interpreter.clear(
131 memory,
132 bytecode,
133 inputs,
134 is_static,
135 spec_id,
136 gas_limit,
137 reservoir_remaining_gas,
138 );
139 *checkpoint_ref = checkpoint;
140 }
141
142 #[inline]
144 pub fn make_call_frame<
145 CTX: ContextTr,
146 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
147 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
148 >(
149 mut this: OutFrame<'_, Self>,
150 ctx: &mut CTX,
151 precompiles: &mut PRECOMPILES,
152 depth: usize,
153 memory: SharedMemory,
154 inputs: Box<CallInputs>,
155 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
156 let reservoir_remaining_gas = inputs.reservoir;
157 let charged_new_account_state_gas = inputs.charged_new_account_state_gas;
158 let gas =
159 Gas::new_with_regular_gas_and_reservoir(inputs.gas_limit, reservoir_remaining_gas);
160
161 let return_result = |instruction_result: InstructionResult| {
162 Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
163 result: InterpreterResult {
164 result: instruction_result,
165 gas,
166 output: Bytes::new(),
167 },
168 memory_offset: inputs.return_memory_offset.clone(),
169 was_precompile_called: false,
170 precompile_call_logs: Vec::new(),
171 charged_new_account_state_gas,
172 })))
173 };
174
175 if depth > CALL_STACK_LIMIT as usize {
177 return return_result(InstructionResult::CallTooDeep);
178 }
179
180 let checkpoint = ctx.journal_mut().checkpoint();
182
183 if let CallValue::Transfer(value) = inputs.value {
185 if let Some(i) =
188 ctx.journal_mut()
189 .transfer_loaded(inputs.caller, inputs.target_address, value)
190 {
191 ctx.journal_mut().checkpoint_revert(checkpoint);
192 return return_result(i.into());
193 }
194 }
195
196 let interpreter_input = InputsImpl {
197 target_address: inputs.target_address,
198 caller_address: inputs.caller,
199 bytecode_address: Some(inputs.bytecode_address),
200 input: inputs.input.clone(),
201 call_value: inputs.value.get(),
202 depth,
203 };
204 let is_static = inputs.is_static;
205 let gas_limit = inputs.gas_limit;
206
207 if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
208 let mut logs = Vec::new();
209 if result.result.is_ok() {
210 ctx.journal_mut().checkpoint_commit();
213 } else {
214 logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
217 ctx.journal_mut().checkpoint_revert(checkpoint);
218 }
219 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
220 result,
221 memory_offset: inputs.return_memory_offset.clone(),
222 was_precompile_called: true,
223 precompile_call_logs: logs,
224 charged_new_account_state_gas,
225 })));
226 }
227
228 let (bytecode_hash, bytecode) = inputs.known_bytecode.clone();
230
231 if bytecode.is_empty() {
233 ctx.journal_mut().checkpoint_commit();
234 return return_result(InstructionResult::Stop);
235 }
236
237 this.get(EthFrame::invalid).clear(
239 FrameData::Call(CallFrame {
240 return_memory_range: inputs.return_memory_offset.clone(),
241 }),
242 FrameInput::Call(inputs),
243 depth,
244 memory,
245 ExtBytecode::new_with_hash(bytecode, bytecode_hash),
246 interpreter_input,
247 is_static,
248 ctx.cfg().spec().into(),
249 gas_limit,
250 reservoir_remaining_gas,
251 checkpoint,
252 );
253
254 Ok(ItemOrResult::Item(this.consume()))
255 }
256
257 #[inline]
259 pub fn make_create_frame<
260 CTX: ContextTr,
261 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
262 >(
263 mut this: OutFrame<'_, Self>,
264 context: &mut CTX,
265 depth: usize,
266 memory: SharedMemory,
267 inputs: Box<CreateInputs>,
268 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
269 let reservoir_remaining_gas = inputs.reservoir();
270 let spec = context.cfg().spec().into();
271 let charged_create_state_gas = inputs.charged_create_state_gas();
276 let return_error = |e| {
277 Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
278 result: InterpreterResult {
279 result: e,
280 gas: Gas::new_with_regular_gas_and_reservoir(
281 inputs.gas_limit(),
282 reservoir_remaining_gas,
283 ),
284 output: Bytes::new(),
285 },
286 address: None,
287 charged_create_state_gas,
288 })))
289 };
290
291 if depth > CALL_STACK_LIMIT as usize {
293 return return_error(InstructionResult::CallTooDeep);
294 }
295
296 let journal = context.journal_mut();
298 let mut caller_info = journal.load_account_mut(inputs.caller())?;
299
300 if *caller_info.balance() < inputs.value() {
303 return return_error(InstructionResult::OutOfFunds);
304 }
305
306 let old_nonce = caller_info.nonce();
308 if !caller_info.bump_nonce() {
309 return return_error(InstructionResult::Return);
310 };
311
312 let created_address = inputs.created_address(old_nonce);
315 let init_code_hash = matches!(inputs.scheme(), CreateScheme::Create2 { .. })
316 .then(|| inputs.init_code_hash());
317
318 drop(caller_info); journal.load_account(created_address)?;
322
323 let checkpoint = match context.journal_mut().create_account_checkpoint(
325 inputs.caller(),
326 created_address,
327 inputs.value(),
328 spec,
329 ) {
330 Ok(checkpoint) => checkpoint,
331 Err(e) => return return_error(e.into()),
332 };
333
334 let bytecode = ExtBytecode::new_with_optional_hash(
335 Bytecode::new_legacy(inputs.init_code().clone()),
336 init_code_hash,
337 );
338
339 let interpreter_input = InputsImpl {
340 target_address: created_address,
341 caller_address: inputs.caller(),
342 bytecode_address: None,
343 input: CallInput::Bytes(Bytes::new()),
344 call_value: inputs.value(),
345 depth,
346 };
347 let gas_limit = inputs.gas_limit();
348
349 this.get(EthFrame::invalid).clear(
350 FrameData::Create(CreateFrame { created_address }),
351 FrameInput::Create(inputs),
352 depth,
353 memory,
354 bytecode,
355 interpreter_input,
356 false,
357 spec,
358 gas_limit,
359 reservoir_remaining_gas,
360 checkpoint,
361 );
362
363 Ok(ItemOrResult::Item(this.consume()))
364 }
365
366 pub fn init_with_context<
368 CTX: ContextTr,
369 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
370 >(
371 this: OutFrame<'_, Self>,
372 ctx: &mut CTX,
373 precompiles: &mut PRECOMPILES,
374 frame_init: FrameInit,
375 ) -> Result<
376 ItemOrResult<FrameToken, FrameResult>,
377 ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
378 > {
379 let FrameInit {
381 depth,
382 memory,
383 frame_input,
384 } = frame_init;
385
386 match frame_input {
387 FrameInput::Call(inputs) => {
388 Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
389 }
390 FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
391 FrameInput::Empty => unreachable!(),
392 }
393 }
394}
395
396impl EthFrame<EthInterpreter> {
397 pub fn process_next_action<
399 CTX: ContextTr,
400 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
401 >(
402 &mut self,
403 context: &mut CTX,
404 next_action: InterpreterAction,
405 ) -> Result<FrameInitOrResult<Self>, ERROR> {
406 let mut interpreter_result = match next_action {
409 InterpreterAction::NewFrame(frame_input) => {
410 let depth = self.depth + 1;
411 return Ok(ItemOrResult::Item(FrameInit {
412 frame_input,
413 depth,
414 memory: self.interpreter.memory.new_child_context(),
415 }));
416 }
417 InterpreterAction::Return(result) => result,
418 };
419
420 let result = match &self.data {
422 FrameData::Call(frame) => {
423 if interpreter_result.result.is_ok() {
426 context.journal_mut().checkpoint_commit();
427 } else {
428 context.journal_mut().checkpoint_revert(self.checkpoint);
429 }
430 let charged_new_account_state_gas = match &self.input {
434 FrameInput::Call(inputs) => inputs.charged_new_account_state_gas,
435 _ => false,
436 };
437 let mut outcome =
438 CallOutcome::new(interpreter_result, frame.return_memory_range.clone());
439 outcome.charged_new_account_state_gas = charged_new_account_state_gas;
440 ItemOrResult::Result(FrameResult::Call(outcome))
441 }
442 FrameData::Create(frame) => {
443 return_create(
444 context,
445 self.checkpoint,
446 &mut interpreter_result,
447 frame.created_address,
448 );
449
450 let mut create_outcome =
451 CreateOutcome::new(interpreter_result, Some(frame.created_address));
452 create_outcome.charged_create_state_gas = match &self.input {
453 FrameInput::Create(inputs) => inputs.charged_create_state_gas(),
454 _ => false,
455 };
456 ItemOrResult::Result(FrameResult::Create(create_outcome))
457 }
458 };
459
460 Ok(result)
461 }
462
463 pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
465 &mut self,
466 ctx: &mut CTX,
467 result: FrameResult,
468 ) -> Result<(), ERROR> {
469 self.interpreter.memory.free_child_context();
470 take_error::<ERROR, _>(ctx.error())?;
471
472 let refund_state_gas = result.refundable_state_gas(ctx.cfg().gas_params());
479
480 match result {
482 FrameResult::Call(outcome) => {
483 let mut out_gas = outcome.gas();
484 let ins_result = *outcome.instruction_result();
485 let returned_len = outcome.result.output.len();
486
487 let interpreter = &mut self.interpreter;
488 let mem_length = outcome.memory_length();
489 let mem_start = outcome.memory_start();
490 interpreter.return_data.set_buffer(outcome.result.output);
491
492 let target_len = min(mem_length, returned_len);
493
494 if ins_result == InstructionResult::FatalExternalError {
495 panic!("Fatal external error in insert_call_outcome");
496 }
497
498 let item = if ins_result.is_ok() {
499 U256::from(1)
500 } else {
501 U256::ZERO
502 };
503 let _ = interpreter.stack.push(item);
505
506 if ins_result.is_ok_or_revert() {
508 interpreter
509 .memory
510 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
511 }
512
513 handle_reservoir_remaining_gas(
517 ins_result,
518 interpreter.gas.tracker_mut(),
519 out_gas.tracker_mut(),
520 );
521 }
522 FrameResult::Create(outcome) => {
523 let instruction_result = *outcome.instruction_result();
524 let interpreter = &mut self.interpreter;
525
526 if instruction_result == InstructionResult::Revert {
527 interpreter
529 .return_data
530 .set_buffer(outcome.output().to_owned());
531 } else {
532 interpreter.return_data.clear();
534 };
535
536 assert_ne!(
537 instruction_result,
538 InstructionResult::FatalExternalError,
539 "Fatal external error in insert_eofcreate_outcome"
540 );
541
542 let mut create_gas = *outcome.gas();
543
544 handle_reservoir_remaining_gas(
548 instruction_result,
549 interpreter.gas.tracker_mut(),
550 create_gas.tracker_mut(),
551 );
552
553 let stack_item = if instruction_result.is_ok() {
554 outcome.address.unwrap_or_default().into_word().into()
555 } else {
556 U256::ZERO
557 };
558
559 let _ = interpreter.stack.push(stack_item);
561 }
562 }
563
564 if let Some(charge) = refund_state_gas {
567 self.interpreter.gas.refill_reservoir(charge);
568 }
569
570 Ok(())
571 }
572}
573
574#[inline]
594pub const fn handle_reservoir_remaining_gas(
595 instruction_result: InstructionResult,
596 parent_gas: &mut GasTracker,
597 child_gas: &mut GasTracker,
598) {
599 if !instruction_result.is_ok() {
601 child_gas.rollback_state_gas();
602 child_gas.set_refunded(0);
603 }
604 if instruction_result.is_halt() {
605 child_gas.spend_all();
609 }
610
611 if instruction_result.is_ok_or_revert() {
613 parent_gas.erase_cost(child_gas.remaining());
614 }
615 parent_gas.set_reservoir(child_gas.reservoir());
616 if instruction_result.is_ok() {
617 parent_gas.set_state_gas_spent(
623 parent_gas
624 .state_gas_spent()
625 .saturating_add(child_gas.state_gas_spent()),
626 );
627 parent_gas.add_state_gas_spilled(child_gas.state_gas_spilled());
628 parent_gas.record_refund(child_gas.refunded());
629 }
630}
631
632pub fn return_create<CTX: ContextTr>(
640 context: &mut CTX,
641 checkpoint: JournalCheckpoint,
642 interpreter_result: &mut InterpreterResult,
643 address: Address,
644) {
645 let (_, _, cfg, journal, _, _) = context.all_mut();
646
647 let max_code_size = cfg.max_code_size();
648 let is_eip3541_disabled = cfg.is_eip3541_disabled();
649 let spec_id = cfg.spec().into();
650 let is_amsterdam_eip8037 = cfg.is_amsterdam_eip8037_enabled();
651 let gas_params = cfg.gas_params();
652
653 if !interpreter_result.result.is_ok() {
655 journal.checkpoint_revert(checkpoint);
656 return;
657 }
658
659 if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
664 journal.checkpoint_revert(checkpoint);
665 interpreter_result.result = InstructionResult::CreateContractSizeLimit;
666 return;
667 }
668
669 if !is_eip3541_disabled
674 && spec_id.is_enabled_in(LONDON)
675 && interpreter_result.output.first() == Some(&0xEF)
676 {
677 journal.checkpoint_revert(checkpoint);
678 interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
679 return;
680 }
681
682 let gas_for_code = gas_params.code_deposit_cost(interpreter_result.output.len());
684 if !interpreter_result.gas.record_regular_cost(gas_for_code) {
685 if spec_id.is_enabled_in(HOMESTEAD) {
690 journal.checkpoint_revert(checkpoint);
691 interpreter_result.result = InstructionResult::OutOfGas;
692 return;
693 } else {
694 interpreter_result.output = Bytes::new();
695 }
696 }
697
698 if is_amsterdam_eip8037 {
705 let hash_cost = gas_params.keccak256_cost(interpreter_result.output.len());
706 if !interpreter_result.gas.record_regular_cost(hash_cost) {
707 journal.checkpoint_revert(checkpoint);
708 interpreter_result.result = InstructionResult::OutOfGas;
709 return;
710 }
711 let state_gas_for_code = gas_params.code_deposit_state_gas(interpreter_result.output.len());
717 if state_gas_for_code > 0 && !interpreter_result.gas.record_state_cost(state_gas_for_code) {
718 journal.checkpoint_revert(checkpoint);
719 interpreter_result.result = InstructionResult::OutOfGas;
720 return;
721 }
722 }
723
724 journal.checkpoint_commit();
726
727 let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
729
730 journal.set_code(address, bytecode);
732
733 interpreter_result.result = InstructionResult::Return;
734}