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 checkpoint: JournalCheckpoint,
116 ) {
117 let Self {
118 data: data_ref,
119 input: input_ref,
120 depth: depth_ref,
121 interpreter,
122 checkpoint: checkpoint_ref,
123 is_finished: is_finished_ref,
124 } = self;
125 *data_ref = data;
126 *input_ref = input;
127 *depth_ref = depth;
128 *is_finished_ref = false;
129 interpreter.clear(memory, bytecode, inputs, is_static, spec_id, gas_limit);
130 *checkpoint_ref = checkpoint;
131 }
132
133 #[inline]
135 pub fn make_call_frame<
136 CTX: ContextTr,
137 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
138 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
139 >(
140 mut this: OutFrame<'_, Self>,
141 ctx: &mut CTX,
142 precompiles: &mut PRECOMPILES,
143 depth: usize,
144 memory: SharedMemory,
145 inputs: Box<CallInputs>,
146 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
147 let gas = Gas::new(inputs.gas_limit);
148 let return_result = |instruction_result: InstructionResult| {
149 Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
150 result: InterpreterResult {
151 result: instruction_result,
152 gas,
153 output: Bytes::new(),
154 },
155 memory_offset: inputs.return_memory_offset.clone(),
156 was_precompile_called: false,
157 precompile_call_logs: Vec::new(),
158 })))
159 };
160
161 if depth > CALL_STACK_LIMIT as usize {
163 return return_result(InstructionResult::CallTooDeep);
164 }
165
166 let checkpoint = ctx.journal_mut().checkpoint();
168
169 if let CallValue::Transfer(value) = inputs.value {
171 if let Some(i) =
174 ctx.journal_mut()
175 .transfer_loaded(inputs.caller, inputs.target_address, value)
176 {
177 ctx.journal_mut().checkpoint_revert(checkpoint);
178 return return_result(i.into());
179 }
180 }
181
182 let interpreter_input = InputsImpl {
183 target_address: inputs.target_address,
184 caller_address: inputs.caller,
185 bytecode_address: Some(inputs.bytecode_address),
186 input: inputs.input.clone(),
187 call_value: inputs.value.get(),
188 };
189 let is_static = inputs.is_static;
190 let gas_limit = inputs.gas_limit;
191
192 if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
193 let mut logs = Vec::new();
194 if result.result.is_ok() {
195 ctx.journal_mut().checkpoint_commit();
196 } else {
197 logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
200 ctx.journal_mut().checkpoint_revert(checkpoint);
201 }
202 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
203 result,
204 memory_offset: inputs.return_memory_offset.clone(),
205 was_precompile_called: true,
206 precompile_call_logs: logs,
207 })));
208 }
209
210 let (bytecode_hash, bytecode) = inputs.known_bytecode.clone();
212
213 if bytecode.is_empty() {
215 ctx.journal_mut().checkpoint_commit();
216 return return_result(InstructionResult::Stop);
217 }
218
219 this.get(EthFrame::invalid).clear(
221 FrameData::Call(CallFrame {
222 return_memory_range: inputs.return_memory_offset.clone(),
223 }),
224 FrameInput::Call(inputs),
225 depth,
226 memory,
227 ExtBytecode::new_with_hash(bytecode, bytecode_hash),
228 interpreter_input,
229 is_static,
230 ctx.cfg().spec().into(),
231 gas_limit,
232 checkpoint,
233 );
234 Ok(ItemOrResult::Item(this.consume()))
235 }
236
237 #[inline]
239 pub fn make_create_frame<
240 CTX: ContextTr,
241 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
242 >(
243 mut this: OutFrame<'_, Self>,
244 context: &mut CTX,
245 depth: usize,
246 memory: SharedMemory,
247 inputs: Box<CreateInputs>,
248 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
249 let spec = context.cfg().spec().into();
250 let return_error = |e| {
251 Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
252 result: InterpreterResult {
253 result: e,
254 gas: Gas::new(inputs.gas_limit()),
255 output: Bytes::new(),
256 },
257 address: None,
258 })))
259 };
260
261 if depth > CALL_STACK_LIMIT as usize {
263 return return_error(InstructionResult::CallTooDeep);
264 }
265
266 let journal = context.journal_mut();
268 let mut caller_info = journal.load_account_mut(inputs.caller())?;
269
270 if *caller_info.balance() < inputs.value() {
273 return return_error(InstructionResult::OutOfFunds);
274 }
275
276 let old_nonce = caller_info.nonce();
278 if !caller_info.bump_nonce() {
279 return return_error(InstructionResult::Return);
280 };
281
282 let mut init_code_hash = None;
284 let created_address = match inputs.scheme() {
285 CreateScheme::Create => inputs.caller().create(old_nonce),
286 CreateScheme::Create2 { salt } => {
287 let init_code_hash = *init_code_hash.insert(keccak256(inputs.init_code()));
288 inputs.caller().create2(salt.to_be_bytes(), init_code_hash)
289 }
290 CreateScheme::Custom { address } => address,
291 };
292
293 drop(caller_info); journal.load_account(created_address)?;
297
298 let checkpoint = match context.journal_mut().create_account_checkpoint(
300 inputs.caller(),
301 created_address,
302 inputs.value(),
303 spec,
304 ) {
305 Ok(checkpoint) => checkpoint,
306 Err(e) => return return_error(e.into()),
307 };
308
309 let bytecode = ExtBytecode::new_with_optional_hash(
310 Bytecode::new_legacy(inputs.init_code().clone()),
311 init_code_hash,
312 );
313
314 let interpreter_input = InputsImpl {
315 target_address: created_address,
316 caller_address: inputs.caller(),
317 bytecode_address: None,
318 input: CallInput::Bytes(Bytes::new()),
319 call_value: inputs.value(),
320 };
321 let gas_limit = inputs.gas_limit();
322
323 this.get(EthFrame::invalid).clear(
324 FrameData::Create(CreateFrame { created_address }),
325 FrameInput::Create(inputs),
326 depth,
327 memory,
328 bytecode,
329 interpreter_input,
330 false,
331 spec,
332 gas_limit,
333 checkpoint,
334 );
335 Ok(ItemOrResult::Item(this.consume()))
336 }
337
338 pub fn init_with_context<
340 CTX: ContextTr,
341 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
342 >(
343 this: OutFrame<'_, Self>,
344 ctx: &mut CTX,
345 precompiles: &mut PRECOMPILES,
346 frame_init: FrameInit,
347 ) -> Result<
348 ItemOrResult<FrameToken, FrameResult>,
349 ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
350 > {
351 let FrameInit {
353 depth,
354 memory,
355 frame_input,
356 } = frame_init;
357
358 match frame_input {
359 FrameInput::Call(inputs) => {
360 Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
361 }
362 FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
363 FrameInput::Empty => unreachable!(),
364 }
365 }
366}
367
368impl EthFrame<EthInterpreter> {
369 pub fn process_next_action<
371 CTX: ContextTr,
372 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
373 >(
374 &mut self,
375 context: &mut CTX,
376 next_action: InterpreterAction,
377 ) -> Result<FrameInitOrResult<Self>, ERROR> {
378 let mut interpreter_result = match next_action {
381 InterpreterAction::NewFrame(frame_input) => {
382 let depth = self.depth + 1;
383 return Ok(ItemOrResult::Item(FrameInit {
384 frame_input,
385 depth,
386 memory: self.interpreter.memory.new_child_context(),
387 }));
388 }
389 InterpreterAction::Return(result) => result,
390 };
391
392 let result = match &self.data {
394 FrameData::Call(frame) => {
395 if interpreter_result.result.is_ok() {
398 context.journal_mut().checkpoint_commit();
399 } else {
400 context.journal_mut().checkpoint_revert(self.checkpoint);
401 }
402 ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
403 interpreter_result,
404 frame.return_memory_range.clone(),
405 )))
406 }
407 FrameData::Create(frame) => {
408 let (cfg, journal) = context.cfg_journal_mut();
409 return_create(
410 journal,
411 cfg,
412 self.checkpoint,
413 &mut interpreter_result,
414 frame.created_address,
415 );
416
417 ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
418 interpreter_result,
419 Some(frame.created_address),
420 )))
421 }
422 };
423
424 Ok(result)
425 }
426
427 pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
429 &mut self,
430 ctx: &mut CTX,
431 result: FrameResult,
432 ) -> Result<(), ERROR> {
433 self.interpreter.memory.free_child_context();
434 take_error::<ERROR, _>(ctx.error())?;
435
436 match result {
438 FrameResult::Call(outcome) => {
439 let out_gas = outcome.gas();
440 let ins_result = *outcome.instruction_result();
441 let returned_len = outcome.result.output.len();
442
443 let interpreter = &mut self.interpreter;
444 let mem_length = outcome.memory_length();
445 let mem_start = outcome.memory_start();
446 interpreter.return_data.set_buffer(outcome.result.output);
447
448 let target_len = min(mem_length, returned_len);
449
450 if ins_result == InstructionResult::FatalExternalError {
451 panic!("Fatal external error in insert_call_outcome");
452 }
453
454 let item = if ins_result.is_ok() {
455 U256::from(1)
456 } else {
457 U256::ZERO
458 };
459 let _ = interpreter.stack.push(item);
461
462 if ins_result.is_ok_or_revert() {
464 interpreter.gas.erase_cost(out_gas.remaining());
465 interpreter
466 .memory
467 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
468 }
469
470 if ins_result.is_ok() {
471 interpreter.gas.record_refund(out_gas.refunded());
472 }
473 }
474 FrameResult::Create(outcome) => {
475 let instruction_result = *outcome.instruction_result();
476 let interpreter = &mut self.interpreter;
477
478 if instruction_result == InstructionResult::Revert {
479 interpreter
481 .return_data
482 .set_buffer(outcome.output().to_owned());
483 } else {
484 interpreter.return_data.clear();
486 };
487
488 assert_ne!(
489 instruction_result,
490 InstructionResult::FatalExternalError,
491 "Fatal external error in insert_eofcreate_outcome"
492 );
493
494 let this_gas = &mut interpreter.gas;
495 if instruction_result.is_ok_or_revert() {
496 this_gas.erase_cost(outcome.gas().remaining());
497 }
498
499 let stack_item = if instruction_result.is_ok() {
500 this_gas.record_refund(outcome.gas().refunded());
501 outcome.address.unwrap_or_default().into_word().into()
502 } else {
503 U256::ZERO
504 };
505
506 let _ = interpreter.stack.push(stack_item);
508 }
509 }
510
511 Ok(())
512 }
513}
514
515pub fn return_create<JOURNAL: JournalTr, CFG: Cfg>(
517 journal: &mut JOURNAL,
518 cfg: CFG,
519 checkpoint: JournalCheckpoint,
520 interpreter_result: &mut InterpreterResult,
521 address: Address,
522) {
523 let max_code_size = cfg.max_code_size();
524 let is_eip3541_disabled = cfg.is_eip3541_disabled();
525 let spec_id = cfg.spec().into();
526
527 if !interpreter_result.result.is_ok() {
529 journal.checkpoint_revert(checkpoint);
530 return;
531 }
532 if !is_eip3541_disabled
537 && spec_id.is_enabled_in(LONDON)
538 && interpreter_result.output.first() == Some(&0xEF)
539 {
540 journal.checkpoint_revert(checkpoint);
541 interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
542 return;
543 }
544
545 if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
548 journal.checkpoint_revert(checkpoint);
549 interpreter_result.result = InstructionResult::CreateContractSizeLimit;
550 return;
551 }
552 let gas_for_code = cfg
553 .gas_params()
554 .code_deposit_cost(interpreter_result.output.len());
555 if !interpreter_result.gas.record_cost(gas_for_code) {
556 if spec_id.is_enabled_in(HOMESTEAD) {
561 journal.checkpoint_revert(checkpoint);
562 interpreter_result.result = InstructionResult::OutOfGas;
563 return;
564 } else {
565 interpreter_result.output = Bytes::new();
566 }
567 }
568 journal.checkpoint_commit();
570
571 let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
573
574 journal.set_code(address, bytecode);
576
577 interpreter_result.result = InstructionResult::Return;
578}