1
1
package valuegenerationtracer
2
2
3
3
import (
4
+ "github.com/crytic/medusa/chain"
4
5
"github.com/crytic/medusa/chain/types"
5
6
"github.com/crytic/medusa/compilation/abiutils"
6
7
"github.com/crytic/medusa/fuzzing/contracts"
7
8
"github.com/crytic/medusa/utils"
8
9
"github.com/ethereum/go-ethereum/accounts/abi"
9
10
"github.com/ethereum/go-ethereum/common"
10
11
"github.com/ethereum/go-ethereum/core/state"
11
- coreTypes "github.com/ethereum/go-ethereum/core/types"
12
+ "github.com/ethereum/go-ethereum/core/tracing"
13
+ coretypes "github.com/ethereum/go-ethereum/core/types"
12
14
"github.com/ethereum/go-ethereum/core/vm"
15
+ "github.com/ethereum/go-ethereum/eth/tracers"
13
16
"golang.org/x/exp/slices"
14
17
"math/big"
15
18
)
@@ -31,7 +34,7 @@ type ValueGenerationTrace struct {
31
34
32
35
type ValueGenerationTracer struct {
33
36
// evm refers to the EVM instance last captured.
34
- evm * vm. EVM
37
+ evmContext * tracing. VMContext
35
38
36
39
// trace represents the current execution trace captured by this tracer.
37
40
trace * ValueGenerationTrace
@@ -47,39 +50,32 @@ type ValueGenerationTracer struct {
47
50
// after some state is captured, on the next state capture (e.g. detecting a log instruction, but
48
51
// using this structure to execute code later once the log is committed).
49
52
onNextCaptureState []func ()
53
+
54
+ // nativeTracer is the underlying tracer used to capture EVM execution.
55
+ nativeTracer * chain.TestChainTracer
50
56
}
51
57
58
+ // NativeTracer returns the underlying TestChainTracer.
59
+ func (t * ValueGenerationTracer ) NativeTracer () * chain.TestChainTracer {
60
+ return t .nativeTracer
61
+ }
52
62
func NewValueGenerationTracer (contractDefinitions contracts.Contracts ) * ValueGenerationTracer {
53
63
tracer := & ValueGenerationTracer {
54
64
contractDefinitions : contractDefinitions ,
55
65
}
56
- return tracer
57
- }
58
66
59
- func (v * ValueGenerationTracer ) CaptureTxStart (gasLimit uint64 ) {
60
- v .trace = newValueGenerationTrace (v .contractDefinitions )
61
- v .currentCallFrame = nil
62
- v .onNextCaptureState = nil
63
- }
64
-
65
- func (v * ValueGenerationTracer ) CaptureTxEnd (restGas uint64 ) {
66
- }
67
-
68
- func (v * ValueGenerationTracer ) CaptureStart (env * vm.EVM , from common.Address , to common.Address , create bool , input []byte , gas uint64 , value * big.Int ) {
69
- v .evm = env
70
- v .captureEnteredCallFrame (from , to , input , create , value )
71
- }
72
-
73
- func (v * ValueGenerationTracer ) CaptureEnd (output []byte , gasUsed uint64 , err error ) {
74
- v .trace .transactionOutputValues = append (v .trace .transactionOutputValues , v .captureExitedCallFrame (output , err ))
75
- }
76
-
77
- func (v * ValueGenerationTracer ) CaptureEnter (typ vm.OpCode , from common.Address , to common.Address , input []byte , gas uint64 , value * big.Int ) {
78
- v .captureEnteredCallFrame (from , to , input , typ == vm .CREATE || typ == vm .CREATE2 , value )
79
- }
80
-
81
- func (v * ValueGenerationTracer ) CaptureExit (output []byte , gasUsed uint64 , err error ) {
82
- v .trace .transactionOutputValues = append (v .trace .transactionOutputValues , v .captureExitedCallFrame (output , err ))
67
+ innerTracer := & tracers.Tracer {
68
+ Hooks : & tracing.Hooks {
69
+ OnTxStart : tracer .OnTxStart ,
70
+ OnEnter : tracer .OnEnter ,
71
+ OnTxEnd : tracer .OnTxEnd ,
72
+ OnExit : tracer .OnExit ,
73
+ OnOpcode : tracer .OnOpcode ,
74
+ OnLog : tracer .OnLog ,
75
+ },
76
+ }
77
+ tracer .nativeTracer = & chain.TestChainTracer {Tracer : innerTracer , CaptureTxEndSetAdditionalResults : nil }
78
+ return tracer
83
79
}
84
80
85
81
func newValueGenerationTrace (contracts contracts.Contracts ) * ValueGenerationTrace {
@@ -205,7 +201,7 @@ func (v *ValueGenerationTracer) captureExitedCallFrame(output []byte, err error)
205
201
if v .currentCallFrame .ToRuntimeBytecode == nil {
206
202
// As long as this isn't a failed contract creation, we should be able to fetch "to" byte code on exit.
207
203
if ! v .currentCallFrame .IsContractCreation () || err == nil {
208
- v .currentCallFrame .ToRuntimeBytecode = v .evm .StateDB .GetCode (v .currentCallFrame .ToAddress )
204
+ v .currentCallFrame .ToRuntimeBytecode = v .evmContext .StateDB .GetCode (v .currentCallFrame .ToAddress )
209
205
}
210
206
}
211
207
if v .currentCallFrame .CodeRuntimeBytecode == nil {
@@ -214,7 +210,7 @@ func (v *ValueGenerationTracer) captureExitedCallFrame(output []byte, err error)
214
210
if v .currentCallFrame .CodeAddress == v .currentCallFrame .ToAddress {
215
211
v .currentCallFrame .CodeRuntimeBytecode = v .currentCallFrame .ToRuntimeBytecode
216
212
} else {
217
- v .currentCallFrame .CodeRuntimeBytecode = v .evm .StateDB .GetCode (v .currentCallFrame .CodeAddress )
213
+ v .currentCallFrame .CodeRuntimeBytecode = v .evmContext .StateDB .GetCode (v .currentCallFrame .CodeAddress )
218
214
}
219
215
}
220
216
@@ -237,28 +233,6 @@ func (v *ValueGenerationTracer) captureExitedCallFrame(output []byte, err error)
237
233
return returnValue
238
234
}
239
235
240
- func (v * ValueGenerationTracer ) CaptureState (pc uint64 , op vm.OpCode , gas , cost uint64 , scope * vm.ScopeContext , rData []byte , depth int , err error ) {
241
- // TODO: look for RET opcode (for now try getting them from currentCallFrame.ReturnData)
242
- // Execute all "on next capture state" events and clear them.
243
- for _ , eventHandler := range v .onNextCaptureState {
244
- eventHandler ()
245
- }
246
- v .onNextCaptureState = nil
247
-
248
- // If a log operation occurred, add a deferred operation to capture it.
249
- if op == vm .LOG0 || op == vm .LOG1 || op == vm .LOG2 || op == vm .LOG3 || op == vm .LOG4 {
250
- v .onNextCaptureState = append (v .onNextCaptureState , func () {
251
- logs := v .evm .StateDB .(* state.StateDB ).Logs ()
252
- if len (logs ) > 0 {
253
- v .currentCallFrame .Operations = append (v .currentCallFrame .Operations , logs [len (logs )- 1 ])
254
- }
255
- })
256
- }
257
- }
258
-
259
- func (v * ValueGenerationTracer ) CaptureFault (pc uint64 , op vm.OpCode , gas , cost uint64 , scope * vm.ScopeContext , depth int , err error ) {
260
- }
261
-
262
236
// CaptureTxEndSetAdditionalResults can be used to set additional results captured from execution tracing. If this
263
237
// tracer is used during transaction execution (block creation), the results can later be queried from the block.
264
238
// This method will only be called on the added tracer if it implements the extended TestChainTracer interface.
@@ -275,12 +249,55 @@ func (v *ValueGenerationTracer) CaptureTxEndSetAdditionalResults(results *types.
275
249
276
250
}
277
251
252
+ // OnTxStart is called upon the start of transaction execution, as defined by tracers.Tracer.
253
+ func (t * ValueGenerationTracer ) OnTxStart (vm * tracing.VMContext , tx * coretypes.Transaction , from common.Address ) {
254
+ t .trace = newValueGenerationTrace (t .contractDefinitions )
255
+ t .currentCallFrame = nil
256
+ t .onNextCaptureState = nil
257
+ // Store our evm reference
258
+ t .evmContext = vm
259
+ }
260
+
261
+ func (t * ValueGenerationTracer ) OnEnter (depth int , typ byte , from common.Address , to common.Address , input []byte , gas uint64 , value * big.Int ) {
262
+ t .captureEnteredCallFrame (from , to , input , typ == byte (vm .CREATE ) || typ == byte (vm .CREATE2 ), value )
263
+ }
264
+
265
+ func (t * ValueGenerationTracer ) OnTxEnd (receipt * coretypes.Receipt , err error ) {
266
+
267
+ }
268
+
269
+ func (t * ValueGenerationTracer ) OnExit (depth int , output []byte , used uint64 , err error , reverted bool ) {
270
+ t .trace .transactionOutputValues = append (t .trace .transactionOutputValues , t .captureExitedCallFrame (output , err ))
271
+ }
272
+
273
+ func (t * ValueGenerationTracer ) OnOpcode (pc uint64 , op byte , gas uint64 , cost uint64 , scope tracing.OpContext , data []byte , depth int , err error ) {
274
+
275
+ // TODO: look for RET opcode (for now try getting them from currentCallFrame.ReturnData)
276
+ // Execute all "on next capture state" events and clear them.
277
+ for _ , eventHandler := range t .onNextCaptureState {
278
+ eventHandler ()
279
+ }
280
+ t .onNextCaptureState = nil
281
+
282
+ }
283
+
284
+ func (t * ValueGenerationTracer ) OnLog (log * coretypes.Log ) {
285
+
286
+ // If a log operation occurred, add a deferred operation to capture it.
287
+ t .onNextCaptureState = append (t .onNextCaptureState , func () {
288
+ logs := t .evmContext .StateDB .(* state.StateDB ).Logs ()
289
+ if len (logs ) > 0 {
290
+ t .currentCallFrame .Operations = append (t .currentCallFrame .Operations , logs [len (logs )- 1 ])
291
+ }
292
+ })
293
+ }
294
+
278
295
func (t * ValueGenerationTrace ) generateEvents (currentCallFrame * utils.CallFrame , events []any ) []any {
279
296
for _ , operation := range currentCallFrame .Operations {
280
297
if childCallFrame , ok := operation .(* utils.CallFrame ); ok {
281
298
// If this is a call frame being entered, generate information recursively.
282
299
t .generateEvents (childCallFrame , events )
283
- } else if eventLog , ok := operation .(* coreTypes .Log ); ok {
300
+ } else if eventLog , ok := operation .(* coretypes .Log ); ok {
284
301
// If an event log was emitted, add a message for it.
285
302
events = append (events , t .getEventsGenerated (currentCallFrame , eventLog )... )
286
303
//t.getEventsGenerated(currentCallFrame, eventLog)
@@ -290,7 +307,7 @@ func (t *ValueGenerationTrace) generateEvents(currentCallFrame *utils.CallFrame,
290
307
return events
291
308
}
292
309
293
- func (t * ValueGenerationTrace ) getEventsGenerated (callFrame * utils.CallFrame , eventLog * coreTypes .Log ) []any {
310
+ func (t * ValueGenerationTrace ) getEventsGenerated (callFrame * utils.CallFrame , eventLog * coretypes .Log ) []any {
294
311
// Try to unpack our event data
295
312
eventInputs := make ([]any , 0 )
296
313
event , eventInputValues := abiutils .UnpackEventAndValues (callFrame .CodeContractAbi , eventLog )
0 commit comments