pub struct LevmOpcodeTracer {
pub active: bool,
pub cfg: OpcodeTracerConfig,
pub logs: Vec<OpcodeStep>,
pub output: Bytes,
pub error: Option<String>,
pub gas_used: u64,
pub last_opcode_gas_cost: Option<u64>,
pub last_step_index: Option<usize>,
pub cumulative_storage: BTreeMap<H256, H256>,
}Expand description
Per-opcode (EIP-3155) tracer, emitted under the de-facto cross-client
structLogger wrapper shape.
Use LevmOpcodeTracer::disabled() when tracing is not wanted;
the dispatch-loop guard is a single if self.opcode_tracer.active branch
with no other overhead on the fast path.
Fields§
§active: boolWhether this tracer is active.
cfg: OpcodeTracerConfigConfiguration.
logs: Vec<OpcodeStep>Collected per-step entries.
output: BytesFinal output bytes (from RETURN / REVERT).
error: Option<String>Top-level error string, if the transaction reverted.
gas_used: u64Gas used by the transaction.
last_opcode_gas_cost: Option<u64>Explicit gas cost written by CALL/CALLCODE/DELEGATECALL/STATICCALL/CREATE/CREATE2
handlers before invoking the child frame, and by jump() when JUMP/JUMPI is
fused with JUMPDEST under active tracing. The dispatch loop prefers this value
over the (incorrect) gas-diff that would include forwarded gas.
last_step_index: Option<usize>Index in logs of the entry that the next finalize_step should patch.
Some(i) is set by pre_step_capture after a push; None after the
limit cap is reached (so finalize_step is a no-op). Synthesized
steps (e.g. fused JUMPDEST) push directly without touching this index,
preserving the parent opcode’s pending finalize target.
cumulative_storage: BTreeMap<H256, H256>Cumulative map of every storage slot touched by an SLOAD/SSTORE so far in
this transaction, with the most recent value observed. Each
SLOAD/SSTORE-bearing step embeds a snapshot of this map under its
storage field, matching geth’s structLogger behavior of accumulating
touched slots across the trace rather than emitting only the slot just
accessed. Empty until the first SLOAD/SSTORE; not reset between call
frames (consistent with how slot keys are indexed — by slot only, not by
(address, slot) — so cross-frame frame isolation is a separate concern).
Implementations§
Source§impl LevmOpcodeTracer
impl LevmOpcodeTracer
Sourcepub fn disabled() -> Self
pub fn disabled() -> Self
Returns an inactive tracer. No allocations; zero overhead on the hot path.
Sourcepub fn new(cfg: OpcodeTracerConfig) -> Self
pub fn new(cfg: OpcodeTracerConfig) -> Self
Returns an active tracer with the given config.
Sourcepub fn pre_step_capture(
&mut self,
pc: u64,
opcode: u8,
gas: u64,
depth: u32,
refund: u64,
stack_view: &[U256],
memory_view: &[u8],
mem_size: u64,
return_data: &Bytes,
storage_kv: Option<(H256, H256)>,
)
pub fn pre_step_capture( &mut self, pc: u64, opcode: u8, gas: u64, depth: u32, refund: u64, stack_view: &[U256], memory_view: &[u8], mem_size: u64, return_data: &Bytes, storage_kv: Option<(H256, H256)>, )
Captures pre-step state, building and buffering an OpcodeStep entry.
Called BEFORE the opcode executes. pc must be the address of the
current opcode (before advance_pc()).
stack_view must already be bottom-first (caller reverses LEVM’s top-first
layout) and empty when cfg.disable_stack is true.
memory_view is the live byte slice for the current frame (caller provides
this only when cfg.enable_memory is true; otherwise pass &[]).
storage_kv is pre-fetched by the caller via read_storage_for_trace; it is
None for all opcodes except SLOAD/SSTORE (or when storage capture is disabled).
Sourcepub fn finalize_step(
&mut self,
gas_cost: u64,
refund_after: u64,
error: Option<&str>,
)
pub fn finalize_step( &mut self, gas_cost: u64, refund_after: u64, error: Option<&str>, )
Patches the entry recorded by the most recent pre_step_capture with the
actual gas cost, the post-execution refund counter, and any step-level
error string. Called immediately after the opcode handler returns.
refund_after matches geth’s structLogger timing: the refund counter
shown on an opcode’s step is the value after the opcode’s gas+refund
accounting has been applied. For opcodes that don’t mutate the refund
counter (every opcode except SSTORE and pre-London SELFDESTRUCT) this is
a no-op since the captured pre-op refund already equals the post-op one.
No-op when the most recent pre_step_capture did not push (limit reached).
Synthesized entries (e.g. fused JUMPDEST) push directly into logs without
updating last_step_index, so this still patches the correct parent entry.
Sourcepub fn synthesize_step(&mut self, step: OpcodeStep)
pub fn synthesize_step(&mut self, step: OpcodeStep)
Pushes a fully-formed synthetic step (used for fused JUMPDEST under JUMP/JUMPI).
Does not update last_step_index, so the pending finalize_step for the
parent opcode continues to patch the parent’s entry. The limit cap is honored
— synthetic pushes are dropped once cfg.limit is reached.
Sourcepub fn take_result(&mut self) -> OpcodeTraceResult
pub fn take_result(&mut self) -> OpcodeTraceResult
Assembles the final OpcodeTraceResult after the transaction finishes.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for LevmOpcodeTracer
impl RefUnwindSafe for LevmOpcodeTracer
impl Send for LevmOpcodeTracer
impl Sync for LevmOpcodeTracer
impl Unpin for LevmOpcodeTracer
impl UnsafeUnpin for LevmOpcodeTracer
impl UnwindSafe for LevmOpcodeTracer
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.