1use crate::{
2 db::Database,
3 frame::EOFCreateFrame,
4 interpreter::{
5 return_ok, return_revert, CallInputs, CreateInputs, CreateOutcome, Gas, InstructionResult,
6 SharedMemory,
7 },
8 primitives::{EVMError, Spec},
9 CallFrame, Context, CreateFrame, Frame, FrameOrResult, FrameResult,
10};
11use core::mem;
12use revm_interpreter::{
13 opcode::InstructionTables, CallOutcome, EOFCreateInputs, InterpreterAction, InterpreterResult,
14 EMPTY_SHARED_MEMORY,
15};
16use std::boxed::Box;
17
18#[inline]
20pub fn execute_frame<SPEC: Spec, EXT, DB: Database>(
21 frame: &mut Frame,
22 shared_memory: &mut SharedMemory,
23 instruction_tables: &InstructionTables<'_, Context<EXT, DB>>,
24 context: &mut Context<EXT, DB>,
25) -> Result<InterpreterAction, EVMError<DB::Error>> {
26 let interpreter = frame.interpreter_mut();
27 let memory = mem::replace(shared_memory, EMPTY_SHARED_MEMORY);
28 let next_action = match instruction_tables {
29 InstructionTables::Plain(table) => interpreter.run(memory, table, context),
30 InstructionTables::Boxed(table) => interpreter.run(memory, table, context),
31 };
32 *shared_memory = interpreter.take_memory();
34
35 Ok(next_action)
36}
37
38#[inline]
40pub fn last_frame_return<SPEC: Spec, EXT, DB: Database>(
41 context: &mut Context<EXT, DB>,
42 frame_result: &mut FrameResult,
43) -> Result<(), EVMError<DB::Error>> {
44 let instruction_result = frame_result.interpreter_result().result;
45 let gas = frame_result.gas_mut();
46 let remaining = gas.remaining();
47 let refunded = gas.refunded();
48
49 *gas = Gas::new_spent(context.evm.env.tx.gas_limit);
51
52 match instruction_result {
53 return_ok!() => {
54 gas.erase_cost(remaining);
55 gas.record_refund(refunded);
56 }
57 return_revert!() => {
58 gas.erase_cost(remaining);
59 }
60 _ => {}
61 }
62 Ok(())
63}
64
65#[inline]
67pub fn call<SPEC: Spec, EXT, DB: Database>(
68 context: &mut Context<EXT, DB>,
69 inputs: Box<CallInputs>,
70) -> Result<FrameOrResult, EVMError<DB::Error>> {
71 context.evm.make_call_frame(&inputs)
72}
73
74#[inline]
75pub fn call_return<EXT, DB: Database>(
76 context: &mut Context<EXT, DB>,
77 frame: Box<CallFrame>,
78 interpreter_result: InterpreterResult,
79) -> Result<CallOutcome, EVMError<DB::Error>> {
80 context
81 .evm
82 .call_return(&interpreter_result, frame.frame_data.checkpoint);
83 Ok(CallOutcome::new(
84 interpreter_result,
85 frame.return_memory_range,
86 ))
87}
88
89#[inline]
90pub fn insert_call_outcome<EXT, DB: Database>(
91 context: &mut Context<EXT, DB>,
92 frame: &mut Frame,
93 shared_memory: &mut SharedMemory,
94 outcome: CallOutcome,
95) -> Result<(), EVMError<DB::Error>> {
96 context.evm.take_error()?;
97 frame
98 .frame_data_mut()
99 .interpreter
100 .insert_call_outcome(shared_memory, outcome);
101 Ok(())
102}
103
104#[inline]
106pub fn create<SPEC: Spec, EXT, DB: Database>(
107 context: &mut Context<EXT, DB>,
108 inputs: Box<CreateInputs>,
109) -> Result<FrameOrResult, EVMError<DB::Error>> {
110 context.evm.make_create_frame(SPEC::SPEC_ID, &inputs)
111}
112
113#[inline]
114pub fn create_return<SPEC: Spec, EXT, DB: Database>(
115 context: &mut Context<EXT, DB>,
116 frame: Box<CreateFrame>,
117 mut interpreter_result: InterpreterResult,
118) -> Result<CreateOutcome, EVMError<DB::Error>> {
119 context.evm.create_return::<SPEC>(
120 &mut interpreter_result,
121 frame.created_address,
122 frame.frame_data.checkpoint,
123 );
124 Ok(CreateOutcome::new(
125 interpreter_result,
126 Some(frame.created_address),
127 ))
128}
129
130#[inline]
131pub fn insert_create_outcome<EXT, DB: Database>(
132 context: &mut Context<EXT, DB>,
133 frame: &mut Frame,
134 outcome: CreateOutcome,
135) -> Result<(), EVMError<DB::Error>> {
136 context.evm.take_error()?;
137 frame
138 .frame_data_mut()
139 .interpreter
140 .insert_create_outcome(outcome);
141 Ok(())
142}
143
144#[inline]
146pub fn eofcreate<SPEC: Spec, EXT, DB: Database>(
147 context: &mut Context<EXT, DB>,
148 inputs: Box<EOFCreateInputs>,
149) -> Result<FrameOrResult, EVMError<DB::Error>> {
150 context.evm.make_eofcreate_frame(SPEC::SPEC_ID, &inputs)
151}
152
153#[inline]
154pub fn eofcreate_return<SPEC: Spec, EXT, DB: Database>(
155 context: &mut Context<EXT, DB>,
156 frame: Box<EOFCreateFrame>,
157 mut interpreter_result: InterpreterResult,
158) -> Result<CreateOutcome, EVMError<DB::Error>> {
159 context.evm.eofcreate_return::<SPEC>(
160 &mut interpreter_result,
161 frame.created_address,
162 frame.frame_data.checkpoint,
163 );
164 Ok(CreateOutcome::new(
165 interpreter_result,
166 Some(frame.created_address),
167 ))
168}
169
170#[inline]
171pub fn insert_eofcreate_outcome<EXT, DB: Database>(
172 context: &mut Context<EXT, DB>,
173 frame: &mut Frame,
174 outcome: CreateOutcome,
175) -> Result<(), EVMError<DB::Error>> {
176 core::mem::replace(&mut context.evm.error, Ok(()))?;
177 frame
178 .frame_data_mut()
179 .interpreter
180 .insert_eofcreate_outcome(outcome);
181 Ok(())
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::handler::mainnet::refund;
188 use crate::primitives::{CancunSpec, Env};
189 use revm_precompile::Bytes;
190
191 fn call_last_frame_return(instruction_result: InstructionResult, gas: Gas) -> Gas {
193 let mut env = Env::default();
194 env.tx.gas_limit = 100;
195
196 let mut ctx = Context::new_empty();
197 ctx.evm.inner.env = Box::new(env);
198 let mut first_frame = FrameResult::Call(CallOutcome::new(
199 InterpreterResult {
200 result: instruction_result,
201 output: Bytes::new(),
202 gas,
203 },
204 0..0,
205 ));
206 last_frame_return::<CancunSpec, _, _>(&mut ctx, &mut first_frame).unwrap();
207 refund::<CancunSpec, _, _>(&mut ctx, first_frame.gas_mut(), 0);
208 *first_frame.gas()
209 }
210
211 #[test]
212 fn test_consume_gas() {
213 let gas = call_last_frame_return(InstructionResult::Stop, Gas::new(90));
214 assert_eq!(gas.remaining(), 90);
215 assert_eq!(gas.spent(), 10);
216 assert_eq!(gas.refunded(), 0);
217 }
218
219 #[test]
220 fn test_consume_gas_with_refund() {
221 let mut return_gas = Gas::new(90);
222 return_gas.record_refund(30);
223
224 let gas = call_last_frame_return(InstructionResult::Stop, return_gas);
225 assert_eq!(gas.remaining(), 90);
226 assert_eq!(gas.spent(), 10);
227 assert_eq!(gas.refunded(), 2);
228
229 let gas = call_last_frame_return(InstructionResult::Revert, return_gas);
230 assert_eq!(gas.remaining(), 90);
231 assert_eq!(gas.spent(), 10);
232 assert_eq!(gas.refunded(), 0);
233 }
234
235 #[test]
236 fn test_revert_gas() {
237 let gas = call_last_frame_return(InstructionResult::Revert, Gas::new(90));
238 assert_eq!(gas.remaining(), 90);
239 assert_eq!(gas.spent(), 10);
240 assert_eq!(gas.refunded(), 0);
241 }
242}