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::ContextError,
8 journaled_state::{JournalCheckpoint, JournalTr},
9 local::{FrameToken, OutFrame},
10 Cfg, ContextTr, Database,
11};
12use core::{cmp::min, fmt::Debug};
13use derive_where::derive_where;
14use interpreter::{
15 gas,
16 interpreter::{EthInterpreter, ExtBytecode},
17 interpreter_action::FrameInit,
18 interpreter_types::ReturnData,
19 CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme,
20 FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
21 InterpreterResult, InterpreterTypes, SharedMemory,
22};
23use primitives::{
24 constants::CALL_STACK_LIMIT,
25 hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
26 keccak256, Address, Bytes, B256, U256,
27};
28use state::Bytecode;
29use std::{borrow::ToOwned, boxed::Box};
30
31#[derive_where(Clone, Debug; IW,
33 <IW as InterpreterTypes>::Stack,
34 <IW as InterpreterTypes>::Memory,
35 <IW as InterpreterTypes>::Bytecode,
36 <IW as InterpreterTypes>::ReturnData,
37 <IW as InterpreterTypes>::Input,
38 <IW as InterpreterTypes>::RuntimeFlag,
39 <IW as InterpreterTypes>::Extend,
40)]
41pub struct EthFrame<IW: InterpreterTypes = EthInterpreter, EXT: Clone + Debug = ()> {
42 pub data: FrameData,
44 pub input: FrameInput,
46 pub depth: usize,
48 pub checkpoint: JournalCheckpoint,
50 pub interpreter: Interpreter<IW>,
52 pub is_finished: bool,
55 pub interrupted_outcome: Option<EXT>,
57}
58
59impl<IT: InterpreterTypes, EXT: Clone + Debug> FrameTr for EthFrame<IT, EXT> {
60 type FrameResult = FrameResult;
61 type FrameInit = FrameInit;
62}
63
64impl<EXT: Clone + Debug> Default for EthFrame<EthInterpreter, EXT> {
65 fn default() -> Self {
66 Self::do_default(Interpreter::default())
67 }
68}
69
70impl<EXT: Clone + Debug> EthFrame<EthInterpreter, EXT> {
71 fn invalid() -> Self {
72 Self::do_default(Interpreter::invalid())
73 }
74
75 fn do_default(interpreter: Interpreter<EthInterpreter>) -> Self {
76 Self {
77 data: FrameData::Call(CallFrame {
78 return_memory_range: 0..0,
79 }),
80 input: FrameInput::Empty,
81 depth: 0,
82 checkpoint: JournalCheckpoint::default(),
83 interpreter,
84 is_finished: false,
85 interrupted_outcome: None,
86 }
87 }
88
89 pub fn insert_interrupted_outcome(&mut self, interrupted_outcome: EXT) {
91 self.interrupted_outcome = Some(interrupted_outcome);
92 }
93
94 pub fn is_interrupted_call(&self) -> bool {
96 self.interrupted_outcome.is_some()
97 }
98
99 pub fn take_interrupted_outcome(&mut self) -> Option<EXT> {
101 self.interrupted_outcome.take()
102 }
103
104 pub fn is_finished(&self) -> bool {
106 self.is_finished
107 }
108
109 pub fn set_finished(&mut self, finished: bool) {
111 self.is_finished = finished;
112 }
113}
114
115pub type ContextTrDbError<CTX> = <<CTX as ContextTr>::Db as Database>::Error;
117
118impl<EXT: Clone + Debug> EthFrame<EthInterpreter, EXT> {
119 #[allow(clippy::too_many_arguments)]
121 pub fn clear(
122 &mut self,
123 data: FrameData,
124 input: FrameInput,
125 depth: usize,
126 memory: SharedMemory,
127 bytecode: ExtBytecode,
128 inputs: InputsImpl,
129 is_static: bool,
130 spec_id: SpecId,
131 gas_limit: u64,
132 checkpoint: JournalCheckpoint,
133 ) {
134 let Self {
135 data: data_ref,
136 input: input_ref,
137 depth: depth_ref,
138 interpreter,
139 checkpoint: checkpoint_ref,
140 is_finished: is_finished_ref,
141 ..
142 } = self;
143 *data_ref = data;
144 *input_ref = input;
145 *depth_ref = depth;
146 *is_finished_ref = false;
147 interpreter.clear(memory, bytecode, inputs, is_static, spec_id, gas_limit);
148 *checkpoint_ref = checkpoint;
149 }
150
151 #[inline]
153 pub fn make_call_frame<
154 CTX: ContextTr,
155 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
156 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
157 >(
158 mut this: OutFrame<'_, Self>,
159 ctx: &mut CTX,
160 precompiles: &mut PRECOMPILES,
161 depth: usize,
162 memory: SharedMemory,
163 inputs: Box<CallInputs>,
164 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
165 let gas = Gas::new(inputs.gas_limit);
166 let return_result = |instruction_result: InstructionResult| {
167 Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
168 result: InterpreterResult {
169 result: instruction_result,
170 gas,
171 output: Bytes::new(),
172 },
173 memory_offset: inputs.return_memory_offset.clone(),
174 })))
175 };
176
177 if depth > CALL_STACK_LIMIT as usize {
179 return return_result(InstructionResult::CallTooDeep);
180 }
181
182 let _ = ctx
184 .journal_mut()
185 .load_account_delegated(inputs.bytecode_address)?;
186
187 let checkpoint = ctx.journal_mut().checkpoint();
189
190 if let CallValue::Transfer(value) = inputs.value {
192 if let Some(i) =
195 ctx.journal_mut()
196 .transfer(inputs.caller, inputs.target_address, value)?
197 {
198 ctx.journal_mut().checkpoint_revert(checkpoint);
199 return return_result(i.into());
200 }
201 }
202
203 let mut interpreter_input = InputsImpl {
204 target_address: inputs.target_address,
205 caller_address: inputs.caller,
206 bytecode_address: Some(inputs.bytecode_address),
207 input: inputs.input.clone(),
208 call_value: inputs.value.get(),
209 account_owner: None,
210 };
211 let is_static = inputs.is_static;
212 let gas_limit = inputs.gas_limit;
213
214 if let Some(result) = precompiles
215 .run(
216 ctx,
217 &inputs.bytecode_address,
218 &interpreter_input,
219 is_static,
220 gas_limit,
221 )
222 .map_err(ERROR::from_string)?
223 {
224 if result.result.is_ok() {
225 ctx.journal_mut().checkpoint_commit();
226 } else {
227 ctx.journal_mut().checkpoint_revert(checkpoint);
228 }
229 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
230 result,
231 memory_offset: inputs.return_memory_offset.clone(),
232 })));
233 }
234
235 let account = ctx
236 .journal_mut()
237 .load_account_code(inputs.bytecode_address)?;
238
239 let mut code_hash = account.info.code_hash();
240 let mut bytecode = account.info.code.clone().unwrap_or_default();
241
242 if let Bytecode::Eip7702(eip7702_bytecode) = bytecode {
243 let account = &ctx
244 .journal_mut()
245 .load_account_code(eip7702_bytecode.delegated_address)?
246 .info;
247 bytecode = account.code.clone().unwrap_or_default();
248 code_hash = account.code_hash();
249 } else if let Bytecode::OwnableAccount(ownable_account_bytecode) = bytecode {
250 let account = &ctx
251 .journal_mut()
252 .load_account_code(ownable_account_bytecode.owner_address)?
253 .info;
254 bytecode = account.code.clone().unwrap_or_default();
255 code_hash = account.code_hash();
256 interpreter_input.account_owner = Some(ownable_account_bytecode.owner_address);
258 }
259
260 if bytecode.is_empty() {
262 ctx.journal_mut().checkpoint_commit();
263 return return_result(InstructionResult::Stop);
264 }
265
266 this.get(EthFrame::invalid).clear(
268 FrameData::Call(CallFrame {
269 return_memory_range: inputs.return_memory_offset.clone(),
270 }),
271 FrameInput::Call(inputs),
272 depth,
273 memory,
274 ExtBytecode::new_with_hash(bytecode, code_hash),
275 interpreter_input,
276 is_static,
277 ctx.cfg().spec().into(),
278 gas_limit,
279 checkpoint,
280 );
281 Ok(ItemOrResult::Item(this.consume()))
282 }
283
284 #[inline]
286 pub fn make_create_frame<
287 CTX: ContextTr,
288 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
289 >(
290 mut this: OutFrame<'_, Self>,
291 context: &mut CTX,
292 depth: usize,
293 memory: SharedMemory,
294 inputs: Box<CreateInputs>,
295 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
296 let spec = context.cfg().spec().into();
297 let return_error = |e| {
298 Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
299 result: InterpreterResult {
300 result: e,
301 gas: Gas::new(inputs.gas_limit),
302 output: Bytes::new(),
303 },
304 address: None,
305 })))
306 };
307
308 if depth > CALL_STACK_LIMIT as usize {
310 return return_error(InstructionResult::CallTooDeep);
311 }
312
313 let caller_info = &mut context.journal_mut().load_account(inputs.caller)?.data.info;
321
322 if caller_info.balance < inputs.value {
324 return return_error(InstructionResult::OutOfFunds);
325 }
326
327 let old_nonce = caller_info.nonce;
329 let Some(new_nonce) = old_nonce.checked_add(1) else {
330 return return_error(InstructionResult::Return);
331 };
332 caller_info.nonce = new_nonce;
333 context
334 .journal_mut()
335 .nonce_bump_journal_entry(inputs.caller);
336
337 let mut init_code_hash = B256::ZERO;
339 let created_address = match inputs.scheme {
340 CreateScheme::Create => inputs.caller.create(old_nonce),
341 CreateScheme::Create2 { salt } => {
342 init_code_hash = keccak256(&inputs.init_code);
343 inputs.caller.create2(salt.to_be_bytes(), init_code_hash)
344 }
345 CreateScheme::Custom { address } => address,
346 };
347
348 context.journal_mut().load_account(created_address)?;
350
351 let checkpoint = match context.journal_mut().create_account_checkpoint(
353 inputs.caller,
354 created_address,
355 inputs.value,
356 spec,
357 ) {
358 Ok(checkpoint) => checkpoint,
359 Err(e) => return return_error(e.into()),
360 };
361
362 let bytecode = ExtBytecode::new_with_hash(
363 Bytecode::new_legacy(inputs.init_code.clone()),
364 init_code_hash,
365 );
366
367 let interpreter_input = InputsImpl {
368 target_address: created_address,
369 caller_address: inputs.caller,
370 bytecode_address: None,
371 input: CallInput::Bytes(Bytes::new()),
372 call_value: inputs.value,
373 account_owner: None,
374 };
375 let gas_limit = inputs.gas_limit;
376
377 this.get(EthFrame::invalid).clear(
378 FrameData::Create(CreateFrame { created_address }),
379 FrameInput::Create(inputs),
380 depth,
381 memory,
382 bytecode,
383 interpreter_input,
384 false,
385 spec,
386 gas_limit,
387 checkpoint,
388 );
389 Ok(ItemOrResult::Item(this.consume()))
390 }
391
392 pub fn init_with_context<
515 CTX: ContextTr,
516 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
517 >(
518 this: OutFrame<'_, Self>,
519 ctx: &mut CTX,
520 precompiles: &mut PRECOMPILES,
521 frame_init: FrameInit,
522 ) -> Result<
523 ItemOrResult<FrameToken, FrameResult>,
524 ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
525 > {
526 let FrameInit {
528 depth,
529 memory,
530 frame_input,
531 } = frame_init;
532
533 match frame_input {
534 FrameInput::Call(inputs) => {
535 Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
536 }
537 FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
538 FrameInput::Empty => unreachable!(),
539 }
540 }
541}
542
543impl<EXT: Clone + Debug> EthFrame<EthInterpreter, EXT> {
544 pub fn process_next_action<
546 CTX: ContextTr,
547 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
548 >(
549 &mut self,
550 context: &mut CTX,
551 next_action: InterpreterAction,
552 ) -> Result<FrameInitOrResult<Self>, ERROR> {
553 let spec = context.cfg().spec().into();
554
555 let mut interpreter_result = match next_action {
558 InterpreterAction::NewFrame(frame_input) => {
559 let depth = self.depth + 1;
560 return Ok(ItemOrResult::Item(FrameInit {
561 frame_input,
562 depth,
563 memory: self.interpreter.memory.new_child_context(),
564 }));
565 }
566 InterpreterAction::Return(result) => result,
567 InterpreterAction::SystemInterruption { .. } => unreachable!(),
568 };
569
570 let result = match &self.data {
572 FrameData::Call(frame) => {
573 if interpreter_result.result.is_ok() {
576 context.journal_mut().checkpoint_commit();
577 } else {
578 context.journal_mut().checkpoint_revert(self.checkpoint);
579 }
580 ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
581 interpreter_result,
582 frame.return_memory_range.clone(),
583 )))
584 }
585 FrameData::Create(frame) => {
586 let max_code_size = context.cfg().max_code_size();
587 let is_eip3541_disabled = context.cfg().is_eip3541_disabled();
588 return_create(
589 context.journal_mut(),
590 self.checkpoint,
591 &mut interpreter_result,
592 frame.created_address,
593 max_code_size,
594 is_eip3541_disabled,
595 spec,
596 );
597
598 ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
599 interpreter_result,
600 Some(frame.created_address),
601 )))
602 }
603 };
604
605 Ok(result)
606 }
607
608 pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
610 &mut self,
611 ctx: &mut CTX,
612 result: FrameResult,
613 ) -> Result<(), ERROR> {
614 self.interpreter.memory.free_child_context();
615 match core::mem::replace(ctx.error(), Ok(())) {
616 Err(ContextError::Db(e)) => return Err(e.into()),
617 Err(ContextError::Custom(e)) => return Err(ERROR::from_string(e)),
618 Ok(_) => (),
619 }
620
621 match result {
623 FrameResult::Call(outcome) => {
624 let out_gas = outcome.gas();
625 let ins_result = *outcome.instruction_result();
626 let returned_len = outcome.result.output.len();
627
628 let interpreter = &mut self.interpreter;
629 let mem_length = outcome.memory_length();
630 let mem_start = outcome.memory_start();
631 interpreter.return_data.set_buffer(outcome.result.output);
632
633 let target_len = min(mem_length, returned_len);
634
635 if ins_result == InstructionResult::FatalExternalError {
636 panic!("Fatal external error in insert_call_outcome");
637 }
638
639 let item = if ins_result.is_ok() {
640 U256::from(1)
641 } else {
642 U256::ZERO
643 };
644 let _ = interpreter.stack.push(item);
646
647 if ins_result.is_ok_or_revert() {
649 interpreter.gas.erase_cost(out_gas.remaining());
650 interpreter
651 .memory
652 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
653 }
654
655 if ins_result.is_ok() {
656 interpreter.gas.record_refund(out_gas.refunded());
657 }
658 }
659 FrameResult::Create(outcome) => {
660 let instruction_result = *outcome.instruction_result();
661 let interpreter = &mut self.interpreter;
662
663 if instruction_result == InstructionResult::Revert {
664 interpreter
666 .return_data
667 .set_buffer(outcome.output().to_owned());
668 } else {
669 interpreter.return_data.clear();
671 };
672
673 assert_ne!(
674 instruction_result,
675 InstructionResult::FatalExternalError,
676 "Fatal external error in insert_eofcreate_outcome"
677 );
678
679 let this_gas = &mut interpreter.gas;
680 if instruction_result.is_ok_or_revert() {
681 this_gas.erase_cost(outcome.gas().remaining());
682 }
683
684 let stack_item = if instruction_result.is_ok() {
685 this_gas.record_refund(outcome.gas().refunded());
686 outcome.address.unwrap_or_default().into_word().into()
687 } else {
688 U256::ZERO
689 };
690
691 let _ = interpreter.stack.push(stack_item);
693 }
694 }
695
696 Ok(())
697 }
698}
699
700pub fn return_create<JOURNAL: JournalTr>(
702 journal: &mut JOURNAL,
703 checkpoint: JournalCheckpoint,
704 interpreter_result: &mut InterpreterResult,
705 address: Address,
706 max_code_size: usize,
707 is_eip3541_disabled: bool,
708 spec_id: SpecId,
709) {
710 if !interpreter_result.result.is_ok() {
712 journal.checkpoint_revert(checkpoint);
713 return;
714 }
715 if !is_eip3541_disabled
720 && spec_id.is_enabled_in(LONDON)
721 && interpreter_result.output.first() == Some(&0xEF)
722 {
723 journal.checkpoint_revert(checkpoint);
724 interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
725 return;
726 }
727
728 if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
731 journal.checkpoint_revert(checkpoint);
732 interpreter_result.result = InstructionResult::CreateContractSizeLimit;
733 return;
734 }
735 let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
736 if !interpreter_result.gas.record_cost(gas_for_code) {
737 if spec_id.is_enabled_in(HOMESTEAD) {
742 journal.checkpoint_revert(checkpoint);
743 interpreter_result.result = InstructionResult::OutOfGas;
744 return;
745 } else {
746 interpreter_result.output = Bytes::new();
747 }
748 }
749 journal.checkpoint_commit();
751
752 if !interpreter_result.output.is_empty() {
754 let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
755 journal.set_code(address, bytecode);
756 }
757
758 interpreter_result.result = InstructionResult::Return;
759}
760
761