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, 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 keccak256, 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 fn is_finished(&self) -> bool {
88 self.is_finished
89 }
90
91 pub 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 #[allow(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 gas =
158 Gas::new_with_regular_gas_and_reservoir(inputs.gas_limit, reservoir_remaining_gas);
159 let return_result = |instruction_result: InstructionResult| {
160 Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
161 result: InterpreterResult {
162 result: instruction_result,
163 gas,
164 output: Bytes::new(),
165 },
166 memory_offset: inputs.return_memory_offset.clone(),
167 was_precompile_called: false,
168 precompile_call_logs: Vec::new(),
169 })))
170 };
171
172 if depth > CALL_STACK_LIMIT as usize {
174 return return_result(InstructionResult::CallTooDeep);
175 }
176
177 let checkpoint = ctx.journal_mut().checkpoint();
179
180 if let CallValue::Transfer(value) = inputs.value {
182 if let Some(i) =
185 ctx.journal_mut()
186 .transfer_loaded(inputs.caller, inputs.target_address, value)
187 {
188 ctx.journal_mut().checkpoint_revert(checkpoint);
189 return return_result(i.into());
190 }
191 }
192
193 let interpreter_input = InputsImpl {
194 target_address: inputs.target_address,
195 caller_address: inputs.caller,
196 bytecode_address: Some(inputs.bytecode_address),
197 input: inputs.input.clone(),
198 call_value: inputs.value.get(),
199 };
200 let is_static = inputs.is_static;
201 let gas_limit = inputs.gas_limit;
202
203 if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
204 let mut logs = Vec::new();
205 if result.result.is_ok() {
206 ctx.journal_mut().checkpoint_commit();
209 } else {
210 logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
213 ctx.journal_mut().checkpoint_revert(checkpoint);
214 }
215 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
216 result,
217 memory_offset: inputs.return_memory_offset.clone(),
218 was_precompile_called: true,
219 precompile_call_logs: logs,
220 })));
221 }
222
223 let (bytecode_hash, bytecode) = inputs.known_bytecode.clone();
225
226 if bytecode.is_empty() {
228 ctx.journal_mut().checkpoint_commit();
229 return return_result(InstructionResult::Stop);
230 }
231
232 this.get(EthFrame::invalid).clear(
234 FrameData::Call(CallFrame {
235 return_memory_range: inputs.return_memory_offset.clone(),
236 }),
237 FrameInput::Call(inputs),
238 depth,
239 memory,
240 ExtBytecode::new_with_hash(bytecode, bytecode_hash),
241 interpreter_input,
242 is_static,
243 ctx.cfg().spec().into(),
244 gas_limit,
245 reservoir_remaining_gas,
246 checkpoint,
247 );
248 Ok(ItemOrResult::Item(this.consume()))
249 }
250
251 #[inline]
253 pub fn make_create_frame<
254 CTX: ContextTr,
255 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
256 >(
257 mut this: OutFrame<'_, Self>,
258 context: &mut CTX,
259 depth: usize,
260 memory: SharedMemory,
261 inputs: Box<CreateInputs>,
262 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
263 let reservoir_remaining_gas = inputs.reservoir();
264 let spec = context.cfg().spec().into();
265 let return_error = |e| {
266 Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
267 result: InterpreterResult {
268 result: e,
269 gas: Gas::new_with_regular_gas_and_reservoir(
270 inputs.gas_limit(),
271 reservoir_remaining_gas,
272 ),
273 output: Bytes::new(),
274 },
275 address: None,
276 })))
277 };
278
279 if depth > CALL_STACK_LIMIT as usize {
281 return return_error(InstructionResult::CallTooDeep);
282 }
283
284 let journal = context.journal_mut();
286 let mut caller_info = journal.load_account_mut(inputs.caller())?;
287
288 if *caller_info.balance() < inputs.value() {
291 return return_error(InstructionResult::OutOfFunds);
292 }
293
294 let old_nonce = caller_info.nonce();
296 if !caller_info.bump_nonce() {
297 return return_error(InstructionResult::Return);
298 };
299
300 let mut init_code_hash = None;
302 let created_address = match inputs.scheme() {
303 CreateScheme::Create => inputs.caller().create(old_nonce),
304 CreateScheme::Create2 { salt } => {
305 let init_code_hash = *init_code_hash.insert(keccak256(inputs.init_code()));
306 inputs.caller().create2(salt.to_be_bytes(), init_code_hash)
307 }
308 CreateScheme::Custom { address } => address,
309 };
310
311 drop(caller_info); journal.load_account(created_address)?;
315
316 let checkpoint = match context.journal_mut().create_account_checkpoint(
318 inputs.caller(),
319 created_address,
320 inputs.value(),
321 spec,
322 ) {
323 Ok(checkpoint) => checkpoint,
324 Err(e) => return return_error(e.into()),
325 };
326
327 let bytecode = ExtBytecode::new_with_optional_hash(
328 Bytecode::new_legacy(inputs.init_code().clone()),
329 init_code_hash,
330 );
331
332 let interpreter_input = InputsImpl {
333 target_address: created_address,
334 caller_address: inputs.caller(),
335 bytecode_address: None,
336 input: CallInput::Bytes(Bytes::new()),
337 call_value: inputs.value(),
338 };
339 let gas_limit = inputs.gas_limit();
340
341 this.get(EthFrame::invalid).clear(
342 FrameData::Create(CreateFrame { created_address }),
343 FrameInput::Create(inputs),
344 depth,
345 memory,
346 bytecode,
347 interpreter_input,
348 false,
349 spec,
350 gas_limit,
351 reservoir_remaining_gas,
352 checkpoint,
353 );
354
355 Ok(ItemOrResult::Item(this.consume()))
356 }
357
358 pub fn init_with_context<
360 CTX: ContextTr,
361 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
362 >(
363 this: OutFrame<'_, Self>,
364 ctx: &mut CTX,
365 precompiles: &mut PRECOMPILES,
366 frame_init: FrameInit,
367 ) -> Result<
368 ItemOrResult<FrameToken, FrameResult>,
369 ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
370 > {
371 let FrameInit {
373 depth,
374 memory,
375 frame_input,
376 } = frame_init;
377
378 match frame_input {
379 FrameInput::Call(inputs) => {
380 Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
381 }
382 FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
383 FrameInput::Empty => unreachable!(),
384 }
385 }
386}
387
388impl EthFrame<EthInterpreter> {
389 pub fn process_next_action<
391 CTX: ContextTr,
392 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
393 >(
394 &mut self,
395 context: &mut CTX,
396 next_action: InterpreterAction,
397 ) -> Result<FrameInitOrResult<Self>, ERROR> {
398 let mut interpreter_result = match next_action {
401 InterpreterAction::NewFrame(frame_input) => {
402 let depth = self.depth + 1;
403 return Ok(ItemOrResult::Item(FrameInit {
404 frame_input,
405 depth,
406 memory: self.interpreter.memory.new_child_context(),
407 }));
408 }
409 InterpreterAction::Return(result) => result,
410 };
411
412 let result = match &self.data {
414 FrameData::Call(frame) => {
415 if interpreter_result.result.is_ok() {
418 context.journal_mut().checkpoint_commit();
419 } else {
420 context.journal_mut().checkpoint_revert(self.checkpoint);
421 }
422 ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
423 interpreter_result,
424 frame.return_memory_range.clone(),
425 )))
426 }
427 FrameData::Create(frame) => {
428 let (cfg, journal) = context.cfg_journal_mut();
429 return_create(
430 journal,
431 cfg,
432 self.checkpoint,
433 &mut interpreter_result,
434 frame.created_address,
435 );
436
437 ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
438 interpreter_result,
439 Some(frame.created_address),
440 )))
441 }
442 };
443
444 Ok(result)
445 }
446
447 pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
449 &mut self,
450 ctx: &mut CTX,
451 result: FrameResult,
452 ) -> Result<(), ERROR> {
453 self.interpreter.memory.free_child_context();
454 take_error::<ERROR, _>(ctx.error())?;
455
456 match result {
458 FrameResult::Call(outcome) => {
459 let out_gas = outcome.gas();
460 let ins_result = *outcome.instruction_result();
461 let returned_len = outcome.result.output.len();
462
463 let interpreter = &mut self.interpreter;
464 let mem_length = outcome.memory_length();
465 let mem_start = outcome.memory_start();
466 interpreter.return_data.set_buffer(outcome.result.output);
467
468 let target_len = min(mem_length, returned_len);
469
470 if ins_result == InstructionResult::FatalExternalError {
471 panic!("Fatal external error in insert_call_outcome");
472 }
473
474 let item = if ins_result.is_ok() {
475 U256::from(1)
476 } else {
477 U256::ZERO
478 };
479 let _ = interpreter.stack.push(item);
481
482 if ins_result.is_ok_or_revert() {
484 interpreter.gas.erase_cost(out_gas.remaining());
485 interpreter
486 .memory
487 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
488 }
489
490 handle_reservoir_remaining_gas(&mut interpreter.gas, &out_gas, ins_result);
492
493 if ins_result.is_ok() {
494 interpreter.gas.record_refund(out_gas.refunded());
495 }
496 }
497 FrameResult::Create(outcome) => {
498 let instruction_result = *outcome.instruction_result();
499 let interpreter = &mut self.interpreter;
500
501 if instruction_result == InstructionResult::Revert {
502 interpreter
504 .return_data
505 .set_buffer(outcome.output().to_owned());
506 } else {
507 interpreter.return_data.clear();
509 };
510
511 assert_ne!(
512 instruction_result,
513 InstructionResult::FatalExternalError,
514 "Fatal external error in insert_eofcreate_outcome"
515 );
516
517 let this_gas = &mut interpreter.gas;
518 if instruction_result.is_ok_or_revert() {
520 this_gas.erase_cost(outcome.gas().remaining());
521 }
522
523 handle_reservoir_remaining_gas(this_gas, outcome.gas(), instruction_result);
525
526 let stack_item = if instruction_result.is_ok() {
527 this_gas.record_refund(outcome.gas().refunded());
528 outcome.address.unwrap_or_default().into_word().into()
529 } else {
530 U256::ZERO
531 };
532
533 let _ = interpreter.stack.push(stack_item);
535 }
536 }
537
538 Ok(())
539 }
540}
541
542#[inline]
544pub fn handle_reservoir_remaining_gas(
545 parent_gas: &mut Gas,
546 child_gas: &Gas,
547 result: InstructionResult,
548) {
549 if result.is_ok() {
550 parent_gas.set_reservoir(child_gas.reservoir());
552 parent_gas.set_state_gas_spent(parent_gas.state_gas_spent() + child_gas.state_gas_spent());
557 } else {
558 parent_gas.set_reservoir(child_gas.state_gas_spent() + child_gas.reservoir());
567 }
568}
569
570pub fn return_create<JOURNAL: JournalTr, CFG: Cfg>(
572 journal: &mut JOURNAL,
573 cfg: CFG,
574 checkpoint: JournalCheckpoint,
575 interpreter_result: &mut InterpreterResult,
576 address: Address,
577) {
578 let max_code_size = cfg.max_code_size();
579 let is_eip3541_disabled = cfg.is_eip3541_disabled();
580 let spec_id = cfg.spec().into();
581
582 if !interpreter_result.result.is_ok() {
584 journal.checkpoint_revert(checkpoint);
585 return;
586 }
587
588 if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
593 journal.checkpoint_revert(checkpoint);
594 interpreter_result.result = InstructionResult::CreateContractSizeLimit;
595 return;
596 }
597
598 if !is_eip3541_disabled
603 && spec_id.is_enabled_in(LONDON)
604 && interpreter_result.output.first() == Some(&0xEF)
605 {
606 journal.checkpoint_revert(checkpoint);
607 interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
608 return;
609 }
610
611 let gas_for_code = cfg
613 .gas_params()
614 .code_deposit_cost(interpreter_result.output.len());
615 if !interpreter_result.gas.record_regular_cost(gas_for_code) {
616 if spec_id.is_enabled_in(HOMESTEAD) {
621 journal.checkpoint_revert(checkpoint);
622 interpreter_result.result = InstructionResult::OutOfGas;
623 return;
624 } else {
625 interpreter_result.output = Bytes::new();
626 }
627 }
628
629 if cfg.is_amsterdam_eip8037_enabled() {
636 let hash_cost = cfg
637 .gas_params()
638 .keccak256_cost(interpreter_result.output.len());
639 if !interpreter_result.gas.record_regular_cost(hash_cost) {
640 journal.checkpoint_revert(checkpoint);
641 interpreter_result.result = InstructionResult::OutOfGas;
642 return;
643 }
644 let state_gas_for_code = cfg
650 .gas_params()
651 .code_deposit_state_gas(interpreter_result.output.len());
652 if state_gas_for_code > 0 && !interpreter_result.gas.record_state_cost(state_gas_for_code) {
653 journal.checkpoint_revert(checkpoint);
654 interpreter_result.result = InstructionResult::OutOfGas;
655 return;
656 }
657 }
658
659 journal.checkpoint_commit();
661
662 let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
664
665 journal.set_code(address, bytecode);
667
668 interpreter_result.result = InstructionResult::Return;
669}