1use crate::evm::FrameTr;
2use crate::item_or_result::FrameInitOrResult;
3use crate::{precompile_provider::PrecompileProvider, ItemOrResult};
4use crate::{CallFrame, CreateFrame, FrameData, FrameResult};
5use context::result::FromStringError;
6use context_interface::context::ContextError;
7use context_interface::local::{FrameToken, OutFrame};
8use context_interface::ContextTr;
9use context_interface::{
10 journaled_state::{JournalCheckpoint, JournalTr},
11 Cfg, Database,
12};
13use core::cmp::min;
14use derive_where::derive_where;
15use interpreter::interpreter_action::FrameInit;
16use interpreter::{
17 gas,
18 interpreter::{EthInterpreter, ExtBytecode},
19 interpreter_types::ReturnData,
20 CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme,
21 FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
22 InterpreterResult, InterpreterTypes, SharedMemory,
23};
24use primitives::{
25 constants::CALL_STACK_LIMIT,
26 hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
27};
28use primitives::{keccak256, Address, Bytes, U256};
29use state::Bytecode;
30use std::borrow::ToOwned;
31use std::boxed::Box;
32
33#[derive_where(Clone, Debug; IW,
35 <IW as InterpreterTypes>::Stack,
36 <IW as InterpreterTypes>::Memory,
37 <IW as InterpreterTypes>::Bytecode,
38 <IW as InterpreterTypes>::ReturnData,
39 <IW as InterpreterTypes>::Input,
40 <IW as InterpreterTypes>::RuntimeFlag,
41 <IW as InterpreterTypes>::Extend,
42)]
43pub struct EthFrame<IW: InterpreterTypes = EthInterpreter> {
44 pub data: FrameData,
46 pub input: FrameInput,
48 pub depth: usize,
50 pub checkpoint: JournalCheckpoint,
52 pub interpreter: Interpreter<IW>,
54 pub is_finished: bool,
57}
58
59impl<IT: InterpreterTypes> FrameTr for EthFrame<IT> {
60 type FrameResult = FrameResult;
61 type FrameInit = FrameInit;
62}
63
64impl Default for EthFrame<EthInterpreter> {
65 fn default() -> Self {
66 Self::do_default(Interpreter::default())
67 }
68}
69
70impl EthFrame<EthInterpreter> {
71 pub fn invalid() -> Self {
73 Self::do_default(Interpreter::invalid())
74 }
75
76 fn do_default(interpreter: Interpreter<EthInterpreter>) -> Self {
77 Self {
78 data: FrameData::Call(CallFrame {
79 return_memory_range: 0..0,
80 }),
81 input: FrameInput::Empty,
82 depth: 0,
83 checkpoint: JournalCheckpoint::default(),
84 interpreter,
85 is_finished: false,
86 }
87 }
88
89 pub fn is_finished(&self) -> bool {
91 self.is_finished
92 }
93
94 pub fn set_finished(&mut self, finished: bool) {
96 self.is_finished = finished;
97 }
98}
99
100pub type ContextTrDbError<CTX> = <<CTX as ContextTr>::Db as Database>::Error;
102
103impl EthFrame<EthInterpreter> {
104 #[allow(clippy::too_many_arguments)]
106 pub fn clear(
107 &mut self,
108 data: FrameData,
109 input: FrameInput,
110 depth: usize,
111 memory: SharedMemory,
112 bytecode: ExtBytecode,
113 inputs: InputsImpl,
114 is_static: bool,
115 spec_id: SpecId,
116 gas_limit: u64,
117 checkpoint: JournalCheckpoint,
118 ) {
119 let Self {
120 data: data_ref,
121 input: input_ref,
122 depth: depth_ref,
123 interpreter,
124 checkpoint: checkpoint_ref,
125 is_finished: is_finished_ref,
126 } = self;
127 *data_ref = data;
128 *input_ref = input;
129 *depth_ref = depth;
130 *is_finished_ref = false;
131 interpreter.clear(memory, bytecode, inputs, is_static, spec_id, gas_limit);
132 *checkpoint_ref = checkpoint;
133 }
134
135 #[inline]
137 pub fn make_call_frame<
138 CTX: ContextTr,
139 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
140 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
141 >(
142 mut this: OutFrame<'_, Self>,
143 ctx: &mut CTX,
144 precompiles: &mut PRECOMPILES,
145 depth: usize,
146 memory: SharedMemory,
147 inputs: Box<CallInputs>,
148 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
149 let gas = Gas::new(inputs.gas_limit);
150 let return_result = |instruction_result: InstructionResult| {
151 Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
152 result: InterpreterResult {
153 result: instruction_result,
154 gas,
155 output: Bytes::new(),
156 },
157 memory_offset: inputs.return_memory_offset.clone(),
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 if result.result.is_ok() {
194 ctx.journal_mut().checkpoint_commit();
195 } else {
196 ctx.journal_mut().checkpoint_revert(checkpoint);
197 }
198 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
199 result,
200 memory_offset: inputs.return_memory_offset.clone(),
201 })));
202 }
203
204 let (bytecode, bytecode_hash) = if let Some((hash, code)) = inputs.known_bytecode.clone() {
206 (code, hash)
208 } else {
209 let account = ctx
211 .journal_mut()
212 .load_account_code(inputs.bytecode_address)?;
213 (
214 account.info.code.clone().unwrap_or_default(),
215 account.info.code_hash,
216 )
217 };
218
219 if bytecode.is_empty() {
221 ctx.journal_mut().checkpoint_commit();
222 return return_result(InstructionResult::Stop);
223 }
224
225 this.get(EthFrame::invalid).clear(
227 FrameData::Call(CallFrame {
228 return_memory_range: inputs.return_memory_offset.clone(),
229 }),
230 FrameInput::Call(inputs),
231 depth,
232 memory,
233 ExtBytecode::new_with_hash(bytecode, bytecode_hash),
234 interpreter_input,
235 is_static,
236 ctx.cfg().spec().into(),
237 gas_limit,
238 checkpoint,
239 );
240 Ok(ItemOrResult::Item(this.consume()))
241 }
242
243 #[inline]
245 pub fn make_create_frame<
246 CTX: ContextTr,
247 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
248 >(
249 mut this: OutFrame<'_, Self>,
250 context: &mut CTX,
251 depth: usize,
252 memory: SharedMemory,
253 inputs: Box<CreateInputs>,
254 ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
255 let spec = context.cfg().spec().into();
256 let return_error = |e| {
257 Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
258 result: InterpreterResult {
259 result: e,
260 gas: Gas::new(inputs.gas_limit),
261 output: Bytes::new(),
262 },
263 address: None,
264 })))
265 };
266
267 if depth > CALL_STACK_LIMIT as usize {
269 return return_error(InstructionResult::CallTooDeep);
270 }
271
272 let caller_info = &mut context.journal_mut().load_account(inputs.caller)?.data.info;
274
275 if caller_info.balance < inputs.value {
277 return return_error(InstructionResult::OutOfFunds);
278 }
279
280 let old_nonce = caller_info.nonce;
282 let Some(new_nonce) = old_nonce.checked_add(1) else {
283 return return_error(InstructionResult::Return);
284 };
285 caller_info.nonce = new_nonce;
286 context
287 .journal_mut()
288 .nonce_bump_journal_entry(inputs.caller);
289
290 let mut init_code_hash = None;
292 let created_address = match inputs.scheme {
293 CreateScheme::Create => inputs.caller.create(old_nonce),
294 CreateScheme::Create2 { salt } => {
295 let init_code_hash = *init_code_hash.insert(keccak256(&inputs.init_code));
296 inputs.caller.create2(salt.to_be_bytes(), init_code_hash)
297 }
298 CreateScheme::Custom { address } => address,
299 };
300
301 context.journal_mut().load_account(created_address)?;
303
304 let checkpoint = match context.journal_mut().create_account_checkpoint(
306 inputs.caller,
307 created_address,
308 inputs.value,
309 spec,
310 ) {
311 Ok(checkpoint) => checkpoint,
312 Err(e) => return return_error(e.into()),
313 };
314
315 let bytecode = ExtBytecode::new_with_optional_hash(
316 Bytecode::new_legacy(inputs.init_code.clone()),
317 init_code_hash,
318 );
319
320 let interpreter_input = InputsImpl {
321 target_address: created_address,
322 caller_address: inputs.caller,
323 bytecode_address: None,
324 input: CallInput::Bytes(Bytes::new()),
325 call_value: inputs.value,
326 };
327 let gas_limit = inputs.gas_limit;
328
329 this.get(EthFrame::invalid).clear(
330 FrameData::Create(CreateFrame { created_address }),
331 FrameInput::Create(inputs),
332 depth,
333 memory,
334 bytecode,
335 interpreter_input,
336 false,
337 spec,
338 gas_limit,
339 checkpoint,
340 );
341 Ok(ItemOrResult::Item(this.consume()))
342 }
343
344 pub fn init_with_context<
346 CTX: ContextTr,
347 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
348 >(
349 this: OutFrame<'_, Self>,
350 ctx: &mut CTX,
351 precompiles: &mut PRECOMPILES,
352 frame_init: FrameInit,
353 ) -> Result<
354 ItemOrResult<FrameToken, FrameResult>,
355 ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
356 > {
357 let FrameInit {
359 depth,
360 memory,
361 frame_input,
362 } = frame_init;
363
364 match frame_input {
365 FrameInput::Call(inputs) => {
366 Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
367 }
368 FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
369 FrameInput::Empty => unreachable!(),
370 }
371 }
372}
373
374impl EthFrame<EthInterpreter> {
375 pub fn process_next_action<
377 CTX: ContextTr,
378 ERROR: From<ContextTrDbError<CTX>> + FromStringError,
379 >(
380 &mut self,
381 context: &mut CTX,
382 next_action: InterpreterAction,
383 ) -> Result<FrameInitOrResult<Self>, ERROR> {
384 let spec = context.cfg().spec().into();
385
386 let mut interpreter_result = match next_action {
389 InterpreterAction::NewFrame(frame_input) => {
390 let depth = self.depth + 1;
391 return Ok(ItemOrResult::Item(FrameInit {
392 frame_input,
393 depth,
394 memory: self.interpreter.memory.new_child_context(),
395 }));
396 }
397 InterpreterAction::Return(result) => result,
398 };
399
400 let result = match &self.data {
402 FrameData::Call(frame) => {
403 if interpreter_result.result.is_ok() {
406 context.journal_mut().checkpoint_commit();
407 } else {
408 context.journal_mut().checkpoint_revert(self.checkpoint);
409 }
410 ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
411 interpreter_result,
412 frame.return_memory_range.clone(),
413 )))
414 }
415 FrameData::Create(frame) => {
416 let max_code_size = context.cfg().max_code_size();
417 let is_eip3541_disabled = context.cfg().is_eip3541_disabled();
418 return_create(
419 context.journal_mut(),
420 self.checkpoint,
421 &mut interpreter_result,
422 frame.created_address,
423 max_code_size,
424 is_eip3541_disabled,
425 spec,
426 );
427
428 ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
429 interpreter_result,
430 Some(frame.created_address),
431 )))
432 }
433 };
434
435 Ok(result)
436 }
437
438 pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
440 &mut self,
441 ctx: &mut CTX,
442 result: FrameResult,
443 ) -> Result<(), ERROR> {
444 self.interpreter.memory.free_child_context();
445 match core::mem::replace(ctx.error(), Ok(())) {
446 Err(ContextError::Db(e)) => return Err(e.into()),
447 Err(ContextError::Custom(e)) => return Err(ERROR::from_string(e)),
448 Ok(_) => (),
449 }
450
451 match result {
453 FrameResult::Call(outcome) => {
454 let out_gas = outcome.gas();
455 let ins_result = *outcome.instruction_result();
456 let returned_len = outcome.result.output.len();
457
458 let interpreter = &mut self.interpreter;
459 let mem_length = outcome.memory_length();
460 let mem_start = outcome.memory_start();
461 interpreter.return_data.set_buffer(outcome.result.output);
462
463 let target_len = min(mem_length, returned_len);
464
465 if ins_result == InstructionResult::FatalExternalError {
466 panic!("Fatal external error in insert_call_outcome");
467 }
468
469 let item = if ins_result.is_ok() {
470 U256::from(1)
471 } else {
472 U256::ZERO
473 };
474 let _ = interpreter.stack.push(item);
476
477 if ins_result.is_ok_or_revert() {
479 interpreter.gas.erase_cost(out_gas.remaining());
480 interpreter
481 .memory
482 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
483 }
484
485 if ins_result.is_ok() {
486 interpreter.gas.record_refund(out_gas.refunded());
487 }
488 }
489 FrameResult::Create(outcome) => {
490 let instruction_result = *outcome.instruction_result();
491 let interpreter = &mut self.interpreter;
492
493 if instruction_result == InstructionResult::Revert {
494 interpreter
496 .return_data
497 .set_buffer(outcome.output().to_owned());
498 } else {
499 interpreter.return_data.clear();
501 };
502
503 assert_ne!(
504 instruction_result,
505 InstructionResult::FatalExternalError,
506 "Fatal external error in insert_eofcreate_outcome"
507 );
508
509 let this_gas = &mut interpreter.gas;
510 if instruction_result.is_ok_or_revert() {
511 this_gas.erase_cost(outcome.gas().remaining());
512 }
513
514 let stack_item = if instruction_result.is_ok() {
515 this_gas.record_refund(outcome.gas().refunded());
516 outcome.address.unwrap_or_default().into_word().into()
517 } else {
518 U256::ZERO
519 };
520
521 let _ = interpreter.stack.push(stack_item);
523 }
524 }
525
526 Ok(())
527 }
528}
529
530pub fn return_create<JOURNAL: JournalTr>(
532 journal: &mut JOURNAL,
533 checkpoint: JournalCheckpoint,
534 interpreter_result: &mut InterpreterResult,
535 address: Address,
536 max_code_size: usize,
537 is_eip3541_disabled: bool,
538 spec_id: SpecId,
539) {
540 if !interpreter_result.result.is_ok() {
542 journal.checkpoint_revert(checkpoint);
543 return;
544 }
545 if !is_eip3541_disabled
550 && spec_id.is_enabled_in(LONDON)
551 && interpreter_result.output.first() == Some(&0xEF)
552 {
553 journal.checkpoint_revert(checkpoint);
554 interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
555 return;
556 }
557
558 if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
561 journal.checkpoint_revert(checkpoint);
562 interpreter_result.result = InstructionResult::CreateContractSizeLimit;
563 return;
564 }
565 let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
566 if !interpreter_result.gas.record_cost(gas_for_code) {
567 if spec_id.is_enabled_in(HOMESTEAD) {
572 journal.checkpoint_revert(checkpoint);
573 interpreter_result.result = InstructionResult::OutOfGas;
574 return;
575 } else {
576 interpreter_result.output = Bytes::new();
577 }
578 }
579 journal.checkpoint_commit();
581
582 let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
584
585 journal.set_code(address, bytecode);
587
588 interpreter_result.result = InstructionResult::Return;
589}