1use crate::{
2 evm::FrameTr, item_or_result::FrameInitOrResult, precompile_provider::PrecompileProvider,
3 CallFrame, CreateFrame, FrameData, FrameResult, ItemOrResult,
4};
5use context::{result::FromStringError, LocalContextTr};
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, 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 let return_result = |instruction_result: InstructionResult| {
161 Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
162 result: InterpreterResult {
163 result: instruction_result,
164 gas,
165 output: Bytes::new(),
166 },
167 memory_offset: inputs.return_memory_offset.clone(),
168 was_precompile_called: false,
169 precompile_call_logs: Vec::new(),
170 charged_new_account_state_gas,
171 })))
172 };
173
174 if depth > CALL_STACK_LIMIT as usize {
176 return return_result(InstructionResult::CallTooDeep);
177 }
178
179 let checkpoint = ctx.journal_mut().checkpoint();
181
182 if let CallValue::Transfer(value) = inputs.value {
184 if let Some(i) =
187 ctx.journal_mut()
188 .transfer_loaded(inputs.caller, inputs.target_address, value)
189 {
190 ctx.journal_mut().checkpoint_revert(checkpoint);
191 return return_result(i.into());
192 }
193 }
194
195 let interpreter_input = InputsImpl {
196 target_address: inputs.target_address,
197 caller_address: inputs.caller,
198 bytecode_address: Some(inputs.bytecode_address),
199 input: inputs.input.clone(),
200 call_value: inputs.value.get(),
201 };
202 let is_static = inputs.is_static;
203 let gas_limit = inputs.gas_limit;
204
205 if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
206 let mut logs = Vec::new();
207 if result.result.is_ok() {
208 ctx.journal_mut().checkpoint_commit();
211 } else {
212 logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
215 ctx.journal_mut().checkpoint_revert(checkpoint);
216 }
217 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
218 result,
219 memory_offset: inputs.return_memory_offset.clone(),
220 was_precompile_called: true,
221 precompile_call_logs: logs,
222 charged_new_account_state_gas,
223 })));
224 }
225
226 let (bytecode_hash, bytecode) = inputs.known_bytecode.clone();
228
229 if bytecode.is_empty() {
231 ctx.journal_mut().checkpoint_commit();
232 return return_result(InstructionResult::Stop);
233 }
234
235 this.get(EthFrame::invalid).clear(
237 FrameData::Call(CallFrame {
238 return_memory_range: inputs.return_memory_offset.clone(),
239 }),
240 FrameInput::Call(inputs),
241 depth,
242 memory,
243 ExtBytecode::new_with_hash(bytecode, bytecode_hash),
244 interpreter_input,
245 is_static,
246 ctx.cfg().spec().into(),
247 gas_limit,
248 reservoir_remaining_gas,
249 checkpoint,
250 );
251 Ok(ItemOrResult::Item(this.consume()))
252 }
253
254 #[inline]
256 pub fn make_create_frame<
257 CTX: ContextTr,
258 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
259 >(
260 mut this: OutFrame<'_, Self>,
261 context: &mut CTX,
262 depth: usize,
263 memory: SharedMemory,
264 inputs: Box<CreateInputs>,
265 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
266 let reservoir_remaining_gas = inputs.reservoir();
267 let spec = context.cfg().spec().into();
268 let return_error = |e| {
273 Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
274 result: InterpreterResult {
275 result: e,
276 gas: Gas::new_with_regular_gas_and_reservoir(
277 inputs.gas_limit(),
278 reservoir_remaining_gas,
279 ),
280 output: Bytes::new(),
281 },
282 address: None,
283 })))
284 };
285
286 if depth > CALL_STACK_LIMIT as usize {
288 return return_error(InstructionResult::CallTooDeep);
289 }
290
291 let journal = context.journal_mut();
293 let mut caller_info = journal.load_account_mut(inputs.caller())?;
294
295 if *caller_info.balance() < inputs.value() {
298 return return_error(InstructionResult::OutOfFunds);
299 }
300
301 let old_nonce = caller_info.nonce();
303 if !caller_info.bump_nonce() {
304 return return_error(InstructionResult::Return);
305 };
306
307 let created_address = inputs.created_address(old_nonce);
310 let init_code_hash = matches!(inputs.scheme(), CreateScheme::Create2 { .. })
311 .then(|| inputs.init_code_hash());
312
313 drop(caller_info); journal.load_account(created_address)?;
317
318 let checkpoint = match context.journal_mut().create_account_checkpoint(
320 inputs.caller(),
321 created_address,
322 inputs.value(),
323 spec,
324 ) {
325 Ok(checkpoint) => checkpoint,
326 Err(e) => return return_error(e.into()),
327 };
328
329 let bytecode = ExtBytecode::new_with_optional_hash(
330 Bytecode::new_legacy(inputs.init_code().clone()),
331 init_code_hash,
332 );
333
334 let interpreter_input = InputsImpl {
335 target_address: created_address,
336 caller_address: inputs.caller(),
337 bytecode_address: None,
338 input: CallInput::Bytes(Bytes::new()),
339 call_value: inputs.value(),
340 };
341 let gas_limit = inputs.gas_limit();
342
343 this.get(EthFrame::invalid).clear(
344 FrameData::Create(CreateFrame { created_address }),
345 FrameInput::Create(inputs),
346 depth,
347 memory,
348 bytecode,
349 interpreter_input,
350 false,
351 spec,
352 gas_limit,
353 reservoir_remaining_gas,
354 checkpoint,
355 );
356
357 Ok(ItemOrResult::Item(this.consume()))
358 }
359
360 pub fn init_with_context<
362 CTX: ContextTr,
363 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
364 >(
365 this: OutFrame<'_, Self>,
366 ctx: &mut CTX,
367 precompiles: &mut PRECOMPILES,
368 frame_init: FrameInit,
369 ) -> Result<
370 ItemOrResult<FrameToken, FrameResult>,
371 ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
372 > {
373 let FrameInit {
375 depth,
376 memory,
377 frame_input,
378 } = frame_init;
379
380 match frame_input {
381 FrameInput::Call(inputs) => {
382 Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
383 }
384 FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
385 FrameInput::Empty => unreachable!(),
386 }
387 }
388}
389
390impl EthFrame<EthInterpreter> {
391 pub fn process_next_action<
393 CTX: ContextTr,
394 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
395 >(
396 &mut self,
397 context: &mut CTX,
398 next_action: InterpreterAction,
399 ) -> Result<FrameInitOrResult<Self>, ERROR> {
400 let mut interpreter_result = match next_action {
403 InterpreterAction::NewFrame(frame_input) => {
404 let depth = self.depth + 1;
405 return Ok(ItemOrResult::Item(FrameInit {
406 frame_input,
407 depth,
408 memory: self.interpreter.memory.new_child_context(),
409 }));
410 }
411 InterpreterAction::Return(result) => result,
412 };
413
414 let result = match &self.data {
416 FrameData::Call(frame) => {
417 if interpreter_result.result.is_ok() {
420 context.journal_mut().checkpoint_commit();
421 } else {
422 context.journal_mut().checkpoint_revert(self.checkpoint);
423 }
424 let charged_new_account_state_gas = match &self.input {
428 FrameInput::Call(inputs) => inputs.charged_new_account_state_gas,
429 _ => false,
430 };
431 let mut outcome =
432 CallOutcome::new(interpreter_result, frame.return_memory_range.clone());
433 outcome.charged_new_account_state_gas = charged_new_account_state_gas;
434 ItemOrResult::Result(FrameResult::Call(outcome))
435 }
436 FrameData::Create(frame) => {
437 return_create(
438 context,
439 self.checkpoint,
440 &mut interpreter_result,
441 frame.created_address,
442 );
443
444 ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
445 interpreter_result,
446 Some(frame.created_address),
447 )))
448 }
449 };
450
451 Ok(result)
452 }
453
454 pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
456 &mut self,
457 ctx: &mut CTX,
458 result: FrameResult,
459 ) -> Result<(), ERROR> {
460 self.interpreter.memory.free_child_context();
461 take_error::<ERROR, _>(ctx.error())?;
462
463 match result {
465 FrameResult::Call(outcome) => {
466 let out_gas = outcome.gas();
467 let ins_result = *outcome.instruction_result();
468 let returned_len = outcome.result.output.len();
469
470 let interpreter = &mut self.interpreter;
471 let mem_length = outcome.memory_length();
472 let mem_start = outcome.memory_start();
473 interpreter.return_data.set_buffer(outcome.result.output);
474
475 let target_len = min(mem_length, returned_len);
476
477 if ins_result == InstructionResult::FatalExternalError {
478 panic!("Fatal external error in insert_call_outcome");
479 }
480
481 let item = if ins_result.is_ok() {
482 U256::from(1)
483 } else {
484 U256::ZERO
485 };
486 let _ = interpreter.stack.push(item);
488
489 if ins_result.is_ok_or_revert() {
491 interpreter.gas.erase_cost(out_gas.remaining());
492 interpreter
493 .memory
494 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
495 }
496
497 handle_reservoir_remaining_gas(ins_result, &mut interpreter.gas, &out_gas);
499
500 if ins_result.is_ok() {
501 interpreter.gas.record_refund(out_gas.refunded());
502 }
503 }
504 FrameResult::Create(outcome) => {
505 let instruction_result = *outcome.instruction_result();
506 let interpreter = &mut self.interpreter;
507
508 if instruction_result == InstructionResult::Revert {
509 interpreter
511 .return_data
512 .set_buffer(outcome.output().to_owned());
513 } else {
514 interpreter.return_data.clear();
516 };
517
518 assert_ne!(
519 instruction_result,
520 InstructionResult::FatalExternalError,
521 "Fatal external error in insert_eofcreate_outcome"
522 );
523
524 let this_gas = &mut interpreter.gas;
525 if instruction_result.is_ok_or_revert() {
527 this_gas.erase_cost(outcome.gas().remaining());
528 }
529
530 handle_reservoir_remaining_gas(instruction_result, this_gas, outcome.gas());
532
533 let create_failed = outcome.address.is_none() || !instruction_result.is_ok();
545
546 if create_failed && ctx.cfg().is_amsterdam_eip8037_enabled() {
547 let state_gas_charged =
548 ctx.cfg().gas_params().create_state_gas(ctx.local().cpsb());
549 this_gas.refill_reservoir(state_gas_charged);
550 }
551
552 let stack_item = if instruction_result.is_ok() {
553 this_gas.record_refund(outcome.gas().refunded());
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 Ok(())
565 }
566}
567
568#[inline]
570pub const fn handle_reservoir_remaining_gas(
571 instruction_result: InstructionResult,
572 parent_gas: &mut Gas,
573 child_gas: &Gas,
574) {
575 if instruction_result.is_ok() {
576 parent_gas.set_reservoir(child_gas.reservoir());
578 parent_gas.set_state_gas_spent(
587 parent_gas
588 .state_gas_spent()
589 .saturating_add(child_gas.state_gas_spent()),
590 );
591 parent_gas.set_refill_amount(
594 parent_gas
595 .refill_amount()
596 .saturating_add(child_gas.refill_amount()),
597 );
598 } else {
599 parent_gas.set_reservoir(
612 child_gas
613 .reservoir()
614 .saturating_add_signed(child_gas.state_gas_spent()),
615 );
616 }
617}
618
619pub fn return_create<CTX: ContextTr>(
627 context: &mut CTX,
628 checkpoint: JournalCheckpoint,
629 interpreter_result: &mut InterpreterResult,
630 address: Address,
631) {
632 let (_, _, cfg, journal, _, local) = context.all_mut();
633
634 let max_code_size = cfg.max_code_size();
635 let is_eip3541_disabled = cfg.is_eip3541_disabled();
636 let spec_id = cfg.spec().into();
637 let is_amsterdam_eip8037 = cfg.is_amsterdam_eip8037_enabled();
638 let cpsb = local.cpsb();
639 let gas_params = cfg.gas_params();
640
641 if !interpreter_result.result.is_ok() {
643 journal.checkpoint_revert(checkpoint);
644 return;
645 }
646
647 if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
652 journal.checkpoint_revert(checkpoint);
653 interpreter_result.result = InstructionResult::CreateContractSizeLimit;
654 return;
655 }
656
657 if !is_eip3541_disabled
662 && spec_id.is_enabled_in(LONDON)
663 && interpreter_result.output.first() == Some(&0xEF)
664 {
665 journal.checkpoint_revert(checkpoint);
666 interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
667 return;
668 }
669
670 let gas_for_code = gas_params.code_deposit_cost(interpreter_result.output.len());
672 if !interpreter_result.gas.record_regular_cost(gas_for_code) {
673 if spec_id.is_enabled_in(HOMESTEAD) {
678 journal.checkpoint_revert(checkpoint);
679 interpreter_result.result = InstructionResult::OutOfGas;
680 return;
681 } else {
682 interpreter_result.output = Bytes::new();
683 }
684 }
685
686 if is_amsterdam_eip8037 {
693 let hash_cost = gas_params.keccak256_cost(interpreter_result.output.len());
694 if !interpreter_result.gas.record_regular_cost(hash_cost) {
695 journal.checkpoint_revert(checkpoint);
696 interpreter_result.result = InstructionResult::OutOfGas;
697 return;
698 }
699 let state_gas_for_code =
705 gas_params.code_deposit_state_gas(interpreter_result.output.len(), cpsb);
706 if state_gas_for_code > 0 && !interpreter_result.gas.record_state_cost(state_gas_for_code) {
707 journal.checkpoint_revert(checkpoint);
708 interpreter_result.result = InstructionResult::OutOfGas;
709 return;
710 }
711 }
712
713 journal.checkpoint_commit();
715
716 let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
718
719 journal.set_code(address, bytecode);
721
722 interpreter_result.result = InstructionResult::Return;
723}