revm/handler/mainnet/
execution.rs

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/// Execute frame
19#[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    // Take the shared memory back.
33    *shared_memory = interpreter.take_memory();
34
35    Ok(next_action)
36}
37
38/// Handle output of the transaction
39#[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    // Spend the gas limit. Gas is reimbursed when the tx returns successfully.
50    *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/// Handle frame sub call.
66#[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/// Handle frame sub create.
105#[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/// Handle frame sub create.
145#[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    /// Creates frame result.
192    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}