@@ -19,10 +19,12 @@ package vm
19
19
import (
20
20
"bytes"
21
21
"encoding/hex"
22
+ "encoding/json"
22
23
"fmt"
23
24
"io"
24
25
"math/big"
25
26
"strings"
27
+ "sync/atomic"
26
28
"time"
27
29
28
30
"github.com/holiman/uint256"
@@ -127,6 +129,9 @@ func (s *StructLog) ErrorString() string {
127
129
// Note that reference types are actual VM data structures; make copies
128
130
// if you need to retain them beyond the current call.
129
131
type EVMLogger interface {
132
+ // Transaction level
133
+ CaptureTxStart (gasLimit uint64 )
134
+ CaptureTxEnd (restGas uint64 )
130
135
CaptureStart (env * EVM , from common.Address , to common.Address , create bool , input []byte , gas uint64 , value * big.Int )
131
136
CaptureState (pc uint64 , op OpCode , gas , cost uint64 , scope * ScopeContext , rData []byte , depth int , err error )
132
137
CaptureStateAfter (pc uint64 , op OpCode , gas , cost uint64 , scope * ScopeContext , rData []byte , depth int , err error )
@@ -162,6 +167,14 @@ type StructLogger struct {
162
167
logs []* StructLog
163
168
output []byte
164
169
err error
170
+
171
+ gasLimit uint64
172
+ usedGas uint64
173
+
174
+ interrupt atomic.Bool // Atomic flag to signal execution interruption
175
+ reason error // Textual reason for the interruption
176
+
177
+ ResultL1DataFee * big.Int
165
178
}
166
179
167
180
// NewStructLogger returns a new logger
@@ -214,6 +227,11 @@ func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Add
214
227
//
215
228
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
216
229
func (l * StructLogger ) CaptureState (pc uint64 , op OpCode , gas , cost uint64 , scope * ScopeContext , rData []byte , depth int , opErr error ) {
230
+ // If tracing was interrupted, set the error and stop
231
+ if l .interrupt .Load () {
232
+ return
233
+ }
234
+
217
235
memory := scope .Memory
218
236
stack := scope .Stack
219
237
contract := scope .Contract
@@ -340,6 +358,14 @@ func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
340
358
341
359
}
342
360
361
+ func (l * StructLogger ) CaptureTxStart (gasLimit uint64 ) {
362
+ l .gasLimit = gasLimit
363
+ }
364
+
365
+ func (l * StructLogger ) CaptureTxEnd (restGas uint64 ) {
366
+ l .usedGas = l .gasLimit - restGas
367
+ }
368
+
343
369
// UpdatedAccounts is used to collect all "touched" accounts
344
370
func (l * StructLogger ) UpdatedAccounts () map [common.Address ]struct {} {
345
371
return l .statesAffected
@@ -367,6 +393,33 @@ func (l *StructLogger) Error() error { return l.err }
367
393
// Output returns the VM return value captured by the trace.
368
394
func (l * StructLogger ) Output () []byte { return l .output }
369
395
396
+ func (l * StructLogger ) GetResult () (json.RawMessage , error ) {
397
+ // Tracing aborted
398
+ if l .reason != nil {
399
+ return nil , l .reason
400
+ }
401
+ failed := l .err != nil
402
+ returnData := common .CopyBytes (l .output )
403
+ // Return data when successful and revert reason when reverted, otherwise empty.
404
+ returnVal := fmt .Sprintf ("%x" , returnData )
405
+ if failed && l .err != ErrExecutionReverted {
406
+ returnVal = ""
407
+ }
408
+ return json .Marshal (& types.ExecutionResult {
409
+ Gas : l .usedGas ,
410
+ Failed : failed ,
411
+ ReturnValue : returnVal ,
412
+ StructLogs : formatLogs (l .StructLogs ()),
413
+ L1DataFee : (* hexutil .Big )(l .ResultL1DataFee ),
414
+ })
415
+ }
416
+
417
+ // Stop terminates execution of the tracer at the first opportune moment.
418
+ func (l * StructLogger ) Stop (err error ) {
419
+ l .reason = err
420
+ l .interrupt .Store (true )
421
+ }
422
+
370
423
// WriteTrace writes a formatted trace to the given writer
371
424
func WriteTrace (writer io.Writer , logs []* StructLog ) {
372
425
for _ , log := range logs {
@@ -487,6 +540,10 @@ func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Addre
487
540
488
541
func (t * mdLogger ) CaptureExit (output []byte , gasUsed uint64 , err error ) {}
489
542
543
+ func (t * mdLogger ) CaptureTxStart (gasLimit uint64 ) {}
544
+
545
+ func (t * mdLogger ) CaptureTxEnd (restGas uint64 ) {}
546
+
490
547
// FormatLogs formats EVM returned structured logs for json output
491
548
func FormatLogs (logs []* StructLog ) []* types.StructLogRes {
492
549
formatted := make ([]* types.StructLogRes , 0 , len (logs ))
@@ -511,3 +568,44 @@ func FormatLogs(logs []*StructLog) []*types.StructLogRes {
511
568
}
512
569
return formatted
513
570
}
571
+
572
+ // formatLogs formats EVM returned structured logs for json output
573
+ func formatLogs (logs []* StructLog ) []* types.StructLogRes {
574
+ formatted := make ([]* types.StructLogRes , len (logs ))
575
+ for index , trace := range logs {
576
+ formatted [index ] = & types.StructLogRes {
577
+ Pc : trace .Pc ,
578
+ Op : trace .Op .String (),
579
+ Gas : trace .Gas ,
580
+ GasCost : trace .GasCost ,
581
+ Depth : trace .Depth ,
582
+ Error : trace .ErrorString (),
583
+ RefundCounter : trace .RefundCounter ,
584
+ }
585
+ if trace .Stack != nil {
586
+ stack := make ([]string , len (trace .Stack ))
587
+ for i , stackValue := range trace .Stack {
588
+ stack [i ] = stackValue .Hex ()
589
+ }
590
+ formatted [index ].Stack = stack
591
+ }
592
+ if trace .ReturnData .Len () > 0 {
593
+ formatted [index ].ReturnData = hexutil .Bytes (trace .ReturnData .Bytes ()).String ()
594
+ }
595
+ if trace .Memory .Len () > 0 {
596
+ memory := make ([]string , 0 , (trace .Memory .Len ()+ 31 )/ 32 )
597
+ for i := 0 ; i + 32 <= trace .Memory .Len (); i += 32 {
598
+ memory = append (memory , fmt .Sprintf ("%x" , trace .Memory .Bytes ()[i :i + 32 ]))
599
+ }
600
+ formatted [index ].Memory = memory
601
+ }
602
+ if trace .Storage != nil {
603
+ storage := make (map [string ]string )
604
+ for i , storageValue := range trace .Storage {
605
+ storage [fmt .Sprintf ("%x" , i )] = fmt .Sprintf ("%x" , storageValue )
606
+ }
607
+ formatted [index ].Storage = storage
608
+ }
609
+ }
610
+ return formatted
611
+ }
0 commit comments