11package chain
22
33import (
4+ "math/big"
5+
46 "github.com/crytic/medusa/chain/types"
57 "github.com/ethereum/go-ethereum/common"
8+ "github.com/ethereum/go-ethereum/core/tracing"
9+ coretypes "github.com/ethereum/go-ethereum/core/types"
610 "github.com/ethereum/go-ethereum/core/vm"
7- "math/big "
11+ "github.com/ethereum/go-ethereum/eth/tracers "
812)
913
1014// cheatCodeTracer represents an EVM.Logger which tracks and patches EVM execution state to enable extended
@@ -18,13 +22,16 @@ type cheatCodeTracer struct {
1822 callDepth uint64
1923
2024 // evm refers to the EVM instance last captured.
21- evm * vm. EVM
25+ evmContext * tracing. VMContext
2226
2327 // callFrames represents per-call-frame data deployment information being captured by the tracer.
2428 callFrames []* cheatCodeTracerCallFrame
2529
2630 // results stores the tracer output after a transaction has concluded.
2731 results * cheatCodeTracerResults
32+
33+ // nativeTracer is the underlying tracer interface that the cheatcode tracer follows
34+ nativeTracer * TestChainTracer
2835}
2936
3037// cheatCodeTracerCallFrame represents per-call-frame data traced by a cheatCodeTracer.
@@ -57,13 +64,14 @@ type cheatCodeTracerCallFrame struct {
5764 // vmOp describes the current call frame's last instruction executed.
5865 vmOp vm.OpCode
5966 // vmScope describes the current call frame's scope context.
60- vmScope * vm. ScopeContext
67+ vmScope tracing. OpContext
6168 // vmReturnData describes the current call frame's return data (set on exit).
6269 vmReturnData []byte
6370 // vmErr describes the current call frame's returned error (set on exit), nil if no error.
6471 vmErr error
6572}
6673
74+ // cheatCodeTracerResults holds the hooks that need to be executed when the chain reverts.
6775type cheatCodeTracerResults struct {
6876 // onChainRevertHooks describes hooks which are to be executed when the chain reverts.
6977 onChainRevertHooks types.GenericHookFuncs
@@ -72,9 +80,25 @@ type cheatCodeTracerResults struct {
7280// newCheatCodeTracer creates a cheatCodeTracer and returns it.
7381func newCheatCodeTracer () * cheatCodeTracer {
7482 tracer := & cheatCodeTracer {}
83+ innerTracer := & tracers.Tracer {
84+ Hooks : & tracing.Hooks {
85+ OnTxStart : tracer .OnTxStart ,
86+ OnTxEnd : tracer .OnTxEnd ,
87+ OnEnter : tracer .OnEnter ,
88+ OnExit : tracer .OnExit ,
89+ OnOpcode : tracer .OnOpcode ,
90+ },
91+ }
92+ tracer .nativeTracer = & TestChainTracer {Tracer : innerTracer , CaptureTxEndSetAdditionalResults : tracer .CaptureTxEndSetAdditionalResults }
93+
7594 return tracer
7695}
7796
97+ // NativeTracer returns the underlying TestChainTracer.
98+ func (t * cheatCodeTracer ) NativeTracer () * TestChainTracer {
99+ return t .nativeTracer
100+ }
101+
78102// bindToChain is called by the TestChain which created the tracer to set its reference.
79103// Note: This is done because of the cheat code system's dependency on the genesis block, as well as chain's dependency
80104// on it, which prevents the chain being set in the tracer on initialization.
@@ -98,115 +122,105 @@ func (t *cheatCodeTracer) CurrentCallFrame() *cheatCodeTracerCallFrame {
98122 return t .callFrames [t .callDepth ]
99123}
100124
101- // CaptureTxStart is called upon the start of transaction execution, as defined by vm.EVMLogger .
102- func (t * cheatCodeTracer ) CaptureTxStart ( gasLimit uint64 ) {
125+ // OnTxStart is called upon the start of transaction execution, as defined by tracers.Tracer .
126+ func (t * cheatCodeTracer ) OnTxStart ( vm * tracing. VMContext , tx * coretypes. Transaction , from common. Address ) {
103127 // Reset our capture state
104128 t .callDepth = 0
105129 t .callFrames = make ([]* cheatCodeTracerCallFrame , 0 )
106130 t .results = & cheatCodeTracerResults {
107131 onChainRevertHooks : nil ,
108132 }
133+ // Store our evm reference
134+ t .evmContext = vm
109135}
110136
111- // CaptureTxEnd is called upon the end of transaction execution, as defined by vm.EVMLogger.
112- func (t * cheatCodeTracer ) CaptureTxEnd ( restGas uint64 ) {
137+ // OnTxEnd is called upon the end of transaction execution, as defined by tracers.Tracer
138+ func (t * cheatCodeTracer ) OnTxEnd ( * coretypes. Receipt , error ) {
113139
114140}
115141
116- // CaptureStart initializes the tracing operation for the top of a call frame, as defined by vm.EVMLogger.
117- func (t * cheatCodeTracer ) CaptureStart (env * vm.EVM , from common.Address , to common.Address , create bool , input []byte , gas uint64 , value * big.Int ) {
118- // Store our evm reference
119- t .evm = env
142+ // OnEnter initializes the tracing operation for the top of a call frame, as defined by tracers.Tracer.
143+ func (t * cheatCodeTracer ) OnEnter (depth int , typ byte , from common.Address , to common.Address , input []byte , gas uint64 , value * big.Int ) {
144+ // Check to see if this is the top level call frame
145+ isTopLevelFrame := depth == 0
146+ var callFrameData * cheatCodeTracerCallFrame
147+ if isTopLevelFrame {
148+ // Create our call frame struct to track data for this initial entry call frame.
149+ callFrameData = & cheatCodeTracerCallFrame {}
150+ } else {
151+ // We haven't updated our call depth yet, so obtain the "previous" call frame (current for now)
152+ previousCallFrame := t .CurrentCallFrame ()
153+
154+ // Create our call frame struct to track data for this initial entry call frame.
155+ // We forward our "next frame hooks" to this frame, then clear them from the previous frame.
156+ callFrameData = & cheatCodeTracerCallFrame {
157+ onFrameExitRestoreHooks : previousCallFrame .onNextFrameExitRestoreHooks ,
158+ }
159+ previousCallFrame .onNextFrameExitRestoreHooks = nil
160+
161+ // Increase our call depth now that we're entering a new call frame.
162+ t .callDepth ++
163+ }
120164
121- // Create our call frame struct to track data for this initial entry call frame.
122- callFrameData := & cheatCodeTracerCallFrame {}
165+ // Append our new call frame
123166 t .callFrames = append (t .callFrames , callFrameData )
167+
168+ // Note: We do not execute events for "next frame enter" here, as we do not yet have scope information.
169+ // Those events are executed when the first EVM instruction is executed in the new scope.
124170}
125171
126- // CaptureEnd is called after a call to finalize tracing completes for the top of a call frame, as defined by vm.EVMLogger .
127- func (t * cheatCodeTracer ) CaptureEnd ( output []byte , gasUsed uint64 , err error ) {
172+ // OnExit is called after a call to finalize tracing completes for the top of a call frame, as defined by tracers.Tracer .
173+ func (t * cheatCodeTracer ) OnExit ( depth int , output []byte , gasUsed uint64 , err error , reverted bool ) {
128174 // Execute all current call frame exit hooks
129175 exitingCallFrame := t .callFrames [t .callDepth ]
130176 exitingCallFrame .onFrameExitRestoreHooks .Execute (false , true )
131- exitingCallFrame .onTopFrameExitRestoreHooks .Execute (false , true )
132177
133- // If we didn't encounter an error in this call frame, we push our upward propagating revert events up one frame.
134- if err == nil {
135- // Store these revert hooks in our results.
136- t . results . onChainRevertHooks = append ( t . results . onChainRevertHooks , exitingCallFrame . onChainRevertRestoreHooks ... )
178+ var parentCallFrame * cheatCodeTracerCallFrame
179+ if depth == 0 {
180+ // If this is the top-level call frame, execute all of its exit hooks
181+ exitingCallFrame . onTopFrameExitRestoreHooks . Execute ( false , true )
137182 } else {
138- // We hit an error, so a revert occurred before this tx was committed.
139- exitingCallFrame . onChainRevertRestoreHooks . Execute ( false , true )
183+ // If not, retrieve the parent call frame
184+ parentCallFrame = t . callFrames [ t . callDepth - 1 ]
140185 }
141186
142187 // We're exiting the current frame, so remove our frame data.
143188 t .callFrames = t .callFrames [:t .callDepth ]
144- }
145-
146- // CaptureEnter is called upon entering of the call frame, as defined by vm.EVMLogger.
147- func (t * cheatCodeTracer ) CaptureEnter (typ vm.OpCode , from common.Address , to common.Address , input []byte , gas uint64 , value * big.Int ) {
148- // We haven't updated our call depth yet, so obtain the "previous" call frame (current for now)
149- previousCallFrame := t .CurrentCallFrame ()
150189
151- // Increase our call depth now that we're entering a new call frame.
152- t .callDepth ++
153-
154- // Create our call frame struct to track data for this initial entry call frame.
155- // We forward our "next frame hooks" to this frame, then clear them from the previous frame.
156- callFrameData := & cheatCodeTracerCallFrame {
157- onFrameExitRestoreHooks : previousCallFrame .onNextFrameExitRestoreHooks ,
158- }
159- previousCallFrame .onNextFrameExitRestoreHooks = nil
160- t .callFrames = append (t .callFrames , callFrameData )
161-
162- // Note: We do not execute events for "next frame enter" here, as we do not yet have scope information.
163- // Those events are executed when the first EVM instruction is executed in the new scope.
164- }
165-
166- // CaptureExit is called upon exiting of the call frame, as defined by vm.EVMLogger.
167- func (t * cheatCodeTracer ) CaptureExit (output []byte , gasUsed uint64 , err error ) {
168- // Execute all current call frame exit hooks
169- exitingCallFrame := t .callFrames [t .callDepth ]
170- exitingCallFrame .onFrameExitRestoreHooks .Execute (false , true )
171- parentCallFrame := t .callFrames [t .callDepth - 1 ]
172-
173- // If we didn't encounter an error in this call frame, we push our upward propagating revert events up one frame.
174- if err == nil {
190+ // If we didn't encounter an error in this call frame, we push our upward propagating restore events up one frame.
191+ if err == nil && depth == 0 {
192+ // Since this is the top call frame, we add the revert events to the results of the tracer and return early
193+ t .results .onChainRevertHooks = append (t .results .onChainRevertHooks , exitingCallFrame .onChainRevertRestoreHooks ... )
194+ return
195+ } else if err == nil {
196+ // Propagate hooks up to the parent call frame
175197 parentCallFrame .onTopFrameExitRestoreHooks = append (parentCallFrame .onTopFrameExitRestoreHooks , exitingCallFrame .onTopFrameExitRestoreHooks ... )
176198 parentCallFrame .onChainRevertRestoreHooks = append (parentCallFrame .onChainRevertRestoreHooks , exitingCallFrame .onChainRevertRestoreHooks ... )
177199 } else {
178200 // We hit an error, so a revert occurred before this tx was committed.
179201 exitingCallFrame .onChainRevertRestoreHooks .Execute (false , true )
180202 }
181203
182- // We're exiting the current frame, so remove our frame data.
183- t .callFrames = t .callFrames [:t .callDepth ]
184-
185204 // Decrease our call depth now that we've exited a call frame.
186205 t .callDepth --
187206}
188207
189- // CaptureState records data from an EVM state update, as defined by vm.EVMLogger .
190- func (t * cheatCodeTracer ) CaptureState (pc uint64 , op vm. OpCode , gas , cost uint64 , scope * vm. ScopeContext , rData []byte , depth int , vmErr error ) {
208+ // OnOpcode records data from an EVM state update, as defined by tracers.Tracer .
209+ func (t * cheatCodeTracer ) OnOpcode (pc uint64 , op byte , gas , cost uint64 , scope tracing. OpContext , rData []byte , depth int , err error ) {
191210 // Set our current frame information.
192211 currentCallFrame := t .CurrentCallFrame ()
193212 currentCallFrame .vmPc = pc
194- currentCallFrame .vmOp = op
213+ currentCallFrame .vmOp = vm . OpCode ( op )
195214 currentCallFrame .vmScope = scope
196215 currentCallFrame .vmReturnData = rData
197- currentCallFrame .vmErr = vmErr
216+ currentCallFrame .vmErr = err
198217
199218 // We execute our entered next frame hooks here (from our previous call frame), as we now have scope information.
200219 if t .callDepth > 0 {
201220 t .callFrames [t .callDepth - 1 ].onNextFrameEnterHooks .Execute (true , true )
202221 }
203222}
204223
205- // CaptureFault records an execution fault, as defined by vm.EVMLogger.
206- func (t * cheatCodeTracer ) CaptureFault (pc uint64 , op vm.OpCode , gas , cost uint64 , scope * vm.ScopeContext , depth int , err error ) {
207-
208- }
209-
210224// CaptureTxEndSetAdditionalResults can be used to set additional results captured from execution tracing. If this
211225// tracer is used during transaction execution (block creation), the results can later be queried from the block.
212226// This method will only be called on the added tracer if it implements the extended TestChainTracer interface.
0 commit comments