Skip to content

Commit 70ab8b6

Browse files
authored
refactor(Inspector): add CallOutcome to call/call_end (bluealloy#985)
* rename main Evm structs, introduce wip external type * tests * Split evm and external context * continue previous commit * wip inspector handle register * add few more handlers for frame and host * Add instruction handle * add instruction handler registration and Inspector wrap * Rm Spec generic, more handlers, start factory * move towards the builder, allow EVM modify * wip on EvmBuilder and modify functionality * EvmBuilder with stages wip * Add wip stages for builer * make handle register simple function, add raw instruction table, split external data from registers * wip on simple builder functions and handler registry * Examples and cleanup * fix lifetime and fmt * Add more handlers, deduct caller, validate tx agains state * All handlers counted, started on docs, some cleanup * renaming and docs * Support all Inspector functionality with Handler * Handler restructured. Documentation added * more docs on registers * integrate builder, fmt, move optimism l1block * add utility builder stage functions * add precompiles, fix bugs with journal spec * spec to generic, optimism build * fix optimism test * fuck macros * clippy and fmt * fix trace block example * ci fixes * Flatten builder stages to generic and handler stage * EvmBuilder doc and refactor fn access * ignore rust code in book * make revme compile, will refactor this in future * Rename handles to Pre/Post Execution and ExecutionLoop * fix optimism clippy * small rename * FrameData and docs * check links mdbook * comments and cleanup * comment * Add initialize interepreter to first frame * clippy * clippy2 * feat: add callOutcome and added to inspector.call * added meaningful rust docs
1 parent 9d655c4 commit 70ab8b6

File tree

9 files changed

+152
-44
lines changed

9 files changed

+152
-44
lines changed
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use crate::{Gas, InstructionResult, InterpreterResult};
2+
use core::ops::Range;
3+
use revm_primitives::Bytes;
4+
5+
/// Represents the outcome of a call operation in a virtual machine.
6+
///
7+
/// This struct encapsulates the result of executing an instruction by an interpreter, including
8+
/// the result itself, gas usage information, and the memory offset where output data is stored.
9+
///
10+
/// # Fields
11+
///
12+
/// * `interpreter_result` - The result of the interpreter's execution, including output data and gas usage.
13+
/// * `memory_offset` - The range in memory where the output data is located.
14+
pub struct CallOutcome {
15+
pub interpreter_result: InterpreterResult,
16+
pub memory_offset: Range<usize>,
17+
}
18+
19+
impl CallOutcome {
20+
/// Constructs a new `CallOutcome`.
21+
///
22+
/// Creates an instance of `CallOutcome` with the given interpreter result and memory offset.
23+
///
24+
/// # Arguments
25+
///
26+
/// * `interpreter_result` - The result of the interpreter's execution.
27+
/// * `memory_offset` - The range in memory indicating where the output data is stored.
28+
pub fn new(interpreter_result: InterpreterResult, memory_offset: Range<usize>) -> Self {
29+
Self {
30+
interpreter_result,
31+
memory_offset,
32+
}
33+
}
34+
35+
/// Returns a reference to the instruction result.
36+
///
37+
/// Provides access to the result of the executed instruction.
38+
///
39+
/// # Returns
40+
///
41+
/// A reference to the `InstructionResult`.
42+
pub fn instruction_result(&self) -> &InstructionResult {
43+
&self.interpreter_result.result
44+
}
45+
46+
/// Returns the gas usage information.
47+
///
48+
/// Provides access to the gas usage details of the executed instruction.
49+
///
50+
/// # Returns
51+
///
52+
/// An instance of `Gas` representing the gas usage.
53+
pub fn gas(&self) -> Gas {
54+
self.interpreter_result.gas
55+
}
56+
57+
/// Returns a reference to the output data.
58+
///
59+
/// Provides access to the output data generated by the executed instruction.
60+
///
61+
/// # Returns
62+
///
63+
/// A reference to the output data as `Bytes`.
64+
pub fn output(&self) -> &Bytes {
65+
&self.interpreter_result.output
66+
}
67+
68+
/// Returns the start position of the memory offset.
69+
///
70+
/// Provides the starting index of the memory range where the output data is stored.
71+
///
72+
/// # Returns
73+
///
74+
/// The starting index of the memory offset as `usize`.
75+
pub fn memory_start(&self) -> usize {
76+
self.memory_offset.start
77+
}
78+
79+
/// Returns the length of the memory range.
80+
///
81+
/// Provides the length of the memory range where the output data is stored.
82+
///
83+
/// # Returns
84+
///
85+
/// The length of the memory range as `usize`.
86+
pub fn memory_length(&self) -> usize {
87+
self.memory_offset.len()
88+
}
89+
}

crates/interpreter/src/interpreter.rs

+34-15
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub use stack::{Stack, STACK_LIMIT};
1010

1111
use crate::alloc::borrow::ToOwned;
1212
use crate::{
13-
primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CreateInputs,
14-
CreateOutcome, Gas, Host, InstructionResult,
13+
primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CallOutcome,
14+
CreateInputs, CreateOutcome, Gas, Host, InstructionResult,
1515
};
1616
use alloc::boxed::Box;
1717
use core::cmp::min;
@@ -150,32 +150,51 @@ impl Interpreter {
150150
}
151151
}
152152

153-
/// When sub call returns we can insert output of that call into this interpreter.
153+
/// Inserts the outcome of a call into the virtual machine's state.
154154
///
155-
/// Note that shared memory is required as a input field.
156-
/// As SharedMemory inside Interpreter is taken and replaced with empty (not valid) memory.
157-
pub fn insert_call_output(
155+
/// This function takes the result of a call, represented by `CallOutcome`,
156+
/// and updates the virtual machine's state accordingly. It involves updating
157+
/// the return data buffer, handling gas accounting, and setting the memory
158+
/// in shared storage based on the outcome of the call.
159+
///
160+
/// # Arguments
161+
///
162+
/// * `shared_memory` - A mutable reference to the shared memory used by the virtual machine.
163+
/// * `call_outcome` - The outcome of the call to be processed, containing details such as
164+
/// instruction result, gas information, and output data.
165+
///
166+
/// # Behavior
167+
///
168+
/// The function first copies the output data from the call outcome to the virtual machine's
169+
/// return data buffer. It then checks the instruction result from the call outcome:
170+
///
171+
/// - `return_ok!()`: Processes successful execution, refunds gas, and updates shared memory.
172+
/// - `return_revert!()`: Handles a revert by only updating the gas usage and shared memory.
173+
/// - `InstructionResult::FatalExternalError`: Sets the instruction result to a fatal external error.
174+
/// - Any other result: No specific action is taken.
175+
pub fn insert_call_outcome(
158176
&mut self,
159177
shared_memory: &mut SharedMemory,
160-
result: InterpreterResult,
161-
memory_return_offset: Range<usize>,
178+
call_outcome: CallOutcome,
162179
) {
163-
let out_offset = memory_return_offset.start;
164-
let out_len = memory_return_offset.len();
180+
let out_offset = call_outcome.memory_start();
181+
let out_len = call_outcome.memory_length();
165182

166-
self.return_data_buffer = result.output;
183+
self.return_data_buffer = call_outcome.output().to_owned();
167184
let target_len = min(out_len, self.return_data_buffer.len());
168185

169-
match result.result {
186+
match call_outcome.instruction_result() {
170187
return_ok!() => {
171188
// return unspend gas.
172-
self.gas.erase_cost(result.gas.remaining());
173-
self.gas.record_refund(result.gas.refunded());
189+
let remaining = call_outcome.gas().remaining();
190+
let refunded = call_outcome.gas().refunded();
191+
self.gas.erase_cost(remaining);
192+
self.gas.record_refund(refunded);
174193
shared_memory.set(out_offset, &self.return_data_buffer[..target_len]);
175194
push!(self, U256::from(1));
176195
}
177196
return_revert!() => {
178-
self.gas.erase_cost(result.gas.remaining());
197+
self.gas.erase_cost(call_outcome.gas().remaining());
179198
shared_memory.set(out_offset, &self.return_data_buffer[..target_len]);
180199
push!(self, U256::ZERO);
181200
}

crates/interpreter/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern crate alloc;
1212
#[macro_use]
1313
mod macros;
1414

15+
mod call_outcome;
1516
mod create_outcome;
1617
pub mod gas;
1718
mod host;
@@ -21,6 +22,7 @@ pub mod instructions;
2122
mod interpreter;
2223

2324
// Reexport primary types.
25+
pub use call_outcome::CallOutcome;
2426
pub use create_outcome::CreateOutcome;
2527
pub use gas::Gas;
2628
pub use host::{DummyHost, Host};

crates/revm/src/handler/mainnet/execution_loop.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
};
1010
use alloc::boxed::Box;
1111
use core::ops::Range;
12+
use revm_interpreter::CallOutcome;
1213

1314
/// Creates first frame.
1415
#[inline]
@@ -106,12 +107,11 @@ pub fn frame_return<SPEC: Spec, EXT, DB: Database>(
106107
let Some(parent_stack_frame) = parent_stack_frame else {
107108
return Some(result);
108109
};
110+
let call_outcome = CallOutcome::new(result, return_memory_range);
109111

110-
parent_stack_frame.interpreter.insert_call_output(
111-
shared_memory,
112-
result,
113-
return_memory_range,
114-
)
112+
parent_stack_frame
113+
.interpreter
114+
.insert_call_outcome(shared_memory, call_outcome)
115115
}
116116
}
117117
None
@@ -132,11 +132,10 @@ pub fn sub_call<SPEC: Spec, EXT, DB: Database>(
132132
{
133133
FrameOrResult::Frame(new_frame) => Some(new_frame),
134134
FrameOrResult::Result(result) => {
135-
curent_stack_frame.interpreter.insert_call_output(
136-
shared_memory,
137-
result,
138-
return_memory_offset,
139-
);
135+
let call_outcome = CallOutcome::new(result, return_memory_offset);
136+
curent_stack_frame
137+
.interpreter
138+
.insert_call_outcome(shared_memory, call_outcome);
140139
None
141140
}
142141
}

crates/revm/src/inspector.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use core::ops::Range;
2-
31
use crate::{
42
interpreter::{CallInputs, CreateInputs, Interpreter},
53
primitives::{db::Database, Address, Log, U256},
@@ -18,7 +16,8 @@ mod noop;
1816
// Exports.
1917

2018
pub use handler_register::{inspector_handle_register, inspector_instruction, GetInspector};
21-
use revm_interpreter::{CreateOutcome, InterpreterResult};
19+
use revm_interpreter::{CallOutcome, CreateOutcome, InterpreterResult};
20+
2221
/// [Inspector] implementations.
2322
pub mod inspectors {
2423
#[cfg(feature = "std")]
@@ -81,7 +80,7 @@ pub trait Inspector<DB: Database> {
8180
&mut self,
8281
context: &mut EvmContext<DB>,
8382
inputs: &mut CallInputs,
84-
) -> Option<(InterpreterResult, Range<usize>)> {
83+
) -> Option<CallOutcome> {
8584
let _ = context;
8685
let _ = inputs;
8786
None

crates/revm/src/inspector/customprinter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Custom print inspector, it has step level information of execution.
22
//! It is a great tool if some debugging is needed.
33
4-
use core::ops::Range;
4+
use revm_interpreter::CallOutcome;
55
use revm_interpreter::CreateOutcome;
66

77
use crate::{
@@ -82,7 +82,7 @@ impl<DB: Database> Inspector<DB> for CustomPrintTracer {
8282
&mut self,
8383
_context: &mut EvmContext<DB>,
8484
inputs: &mut CallInputs,
85-
) -> Option<(InterpreterResult, Range<usize>)> {
85+
) -> Option<CallOutcome> {
8686
println!(
8787
"SM CALL: {:?}, context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}",
8888
inputs.contract,

crates/revm/src/inspector/eip3155.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use crate::{
44
primitives::{db::Database, hex, Address, U256},
55
EvmContext, GetInspector, Inspector,
66
};
7-
use core::ops::Range;
7+
8+
use revm_interpreter::CallOutcome;
89
use revm_interpreter::CreateOutcome;
910
use serde_json::json;
1011
use std::io::Write;
@@ -90,7 +91,7 @@ impl<DB: Database> Inspector<DB> for TracerEip3155 {
9091
&mut self,
9192
_context: &mut EvmContext<DB>,
9293
_inputs: &mut CallInputs,
93-
) -> Option<(InterpreterResult, Range<usize>)> {
94+
) -> Option<CallOutcome> {
9495
None
9596
}
9697

crates/revm/src/inspector/gas.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ impl<DB: Database> Inspector<DB> for GasInspector {
6868

6969
#[cfg(test)]
7070
mod tests {
71+
use revm_interpreter::CallOutcome;
7172
use revm_interpreter::CreateOutcome;
7273

7374
use crate::{
@@ -77,7 +78,6 @@ mod tests {
7778
primitives::{Address, Log},
7879
Database, EvmContext, Inspector,
7980
};
80-
use core::ops::Range;
8181

8282
#[derive(Default, Debug)]
8383
struct StackInspector {
@@ -116,7 +116,7 @@ mod tests {
116116
&mut self,
117117
context: &mut EvmContext<DB>,
118118
call: &mut CallInputs,
119-
) -> Option<(InterpreterResult, Range<usize>)> {
119+
) -> Option<CallOutcome> {
120120
self.gas_inspector.call(context, call)
121121
}
122122

crates/revm/src/inspector/handler_register.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
CallStackFrame, Evm, FrameData, FrameOrResult, Inspector, JournalEntry,
1010
};
1111
use alloc::{boxed::Box, sync::Arc, vec::Vec};
12-
use revm_interpreter::CreateInputs;
12+
use revm_interpreter::{CallOutcome, CreateInputs};
1313

1414
pub trait GetInspector<'a, DB: Database> {
1515
fn get_inspector(&mut self) -> &mut dyn Inspector<DB>;
@@ -106,7 +106,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>(
106106
.get_inspector()
107107
.call(&mut context.evm, &mut call_inputs)
108108
{
109-
return FrameOrResult::Result(output.0);
109+
return FrameOrResult::Result(output.interpreter_result);
110110
}
111111
// first call frame does not have return range.
112112
context.evm.make_call_frame(&call_inputs, 0..0)
@@ -203,8 +203,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>(
203203
move |context, mut inputs, frame, memory, return_memory_offset| -> Option<Box<_>> {
204204
// inspector handle
205205
let inspector = context.external.get_inspector();
206-
if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) {
207-
frame.interpreter.insert_call_output(memory, result, range);
206+
if let Some(call_outcome) = inspector.call(&mut context.evm, &mut inputs) {
207+
frame.interpreter.insert_call_outcome(memory, call_outcome);
208208
return None;
209209
}
210210
match context
@@ -218,9 +218,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>(
218218
FrameOrResult::Result(result) => {
219219
// inspector handle
220220
let result = inspector.call_end(&mut context.evm, result);
221-
frame
222-
.interpreter
223-
.insert_call_output(memory, result, return_memory_offset);
221+
let call_outcome = CallOutcome::new(result, return_memory_offset);
222+
frame.interpreter.insert_call_outcome(memory, call_outcome);
224223
None
225224
}
226225
}
@@ -299,7 +298,7 @@ mod tests {
299298
primitives::{Address, BerlinSpec},
300299
Database, Evm, EvmContext, Inspector,
301300
};
302-
use core::ops::Range;
301+
303302
use revm_interpreter::CreateOutcome;
304303

305304
#[test]
@@ -349,7 +348,7 @@ mod tests {
349348
&mut self,
350349
_context: &mut EvmContext<DB>,
351350
_call: &mut CallInputs,
352-
) -> Option<(InterpreterResult, Range<usize>)> {
351+
) -> Option<CallOutcome> {
353352
if self.call {
354353
unreachable!("call should not be called twice")
355354
}

0 commit comments

Comments
 (0)