github.com/ethereum/go-ethereum@v1.16.1/eth/tracers/logger/logger_json.go (about)

     1  // Copyright 2021 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package logger
    18  
    19  import (
    20  	"encoding/json"
    21  	"io"
    22  	"math/big"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/common/hexutil"
    26  	"github.com/ethereum/go-ethereum/common/math"
    27  	"github.com/ethereum/go-ethereum/core/tracing"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/core/vm"
    30  )
    31  
    32  //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe.go
    33  
    34  // overrides for gencodec
    35  type callFrameMarshaling struct {
    36  	Input hexutil.Bytes
    37  	Gas   math.HexOrDecimal64
    38  	Value *hexutil.Big
    39  	Type  string `json:"type"` // adds call to Type() in MarshalJSON
    40  }
    41  
    42  // callFrame is emitted every call frame entered.
    43  type callFrame struct {
    44  	op    vm.OpCode
    45  	From  common.Address `json:"from"`
    46  	To    common.Address `json:"to"`
    47  	Input []byte         `json:"input,omitempty"`
    48  	Gas   uint64         `json:"gas"`
    49  	Value *big.Int       `json:"value"`
    50  }
    51  
    52  // Type formats the call type in a human-readable format.
    53  func (c *callFrame) Type() string {
    54  	return c.op.String()
    55  }
    56  
    57  type jsonLogger struct {
    58  	encoder *json.Encoder
    59  	cfg     *Config
    60  	env     *tracing.VMContext
    61  	hooks   *tracing.Hooks
    62  }
    63  
    64  // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
    65  // into the provided stream.
    66  func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks {
    67  	l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
    68  	if l.cfg == nil {
    69  		l.cfg = &Config{}
    70  	}
    71  	l.hooks = &tracing.Hooks{
    72  		OnTxStart:         l.OnTxStart,
    73  		OnSystemCallStart: l.onSystemCallStart,
    74  		OnExit:            l.OnExit,
    75  		OnOpcode:          l.OnOpcode,
    76  		OnFault:           l.OnFault,
    77  	}
    78  	return l.hooks
    79  }
    80  
    81  // NewJSONLoggerWithCallFrames creates a new EVM tracer that prints execution steps as JSON objects
    82  // into the provided stream. It also includes call frames in the output.
    83  func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) *tracing.Hooks {
    84  	l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
    85  	if l.cfg == nil {
    86  		l.cfg = &Config{}
    87  	}
    88  	l.hooks = &tracing.Hooks{
    89  		OnTxStart:         l.OnTxStart,
    90  		OnSystemCallStart: l.onSystemCallStart,
    91  		OnEnter:           l.OnEnter,
    92  		OnExit:            l.OnExit,
    93  		OnOpcode:          l.OnOpcode,
    94  		OnFault:           l.OnFault,
    95  	}
    96  	return l.hooks
    97  }
    98  
    99  func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) {
   100  	// TODO: Add rData to this interface as well
   101  	l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err)
   102  }
   103  
   104  func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
   105  	memory := scope.MemoryData()
   106  	stack := scope.StackData()
   107  
   108  	log := StructLog{
   109  		Pc:            pc,
   110  		Op:            vm.OpCode(op),
   111  		Gas:           gas,
   112  		GasCost:       cost,
   113  		MemorySize:    len(memory),
   114  		Depth:         depth,
   115  		RefundCounter: l.env.StateDB.GetRefund(),
   116  		Err:           err,
   117  	}
   118  	if l.cfg.EnableMemory {
   119  		log.Memory = memory
   120  	}
   121  	if !l.cfg.DisableStack {
   122  		log.Stack = stack
   123  	}
   124  	if l.cfg.EnableReturnData {
   125  		log.ReturnData = rData
   126  	}
   127  	l.encoder.Encode(log)
   128  }
   129  
   130  func (l *jsonLogger) onSystemCallStart() {
   131  	// Process no events while in system call.
   132  	hooks := *l.hooks
   133  	*l.hooks = tracing.Hooks{
   134  		OnSystemCallEnd: func() {
   135  			*l.hooks = hooks
   136  		},
   137  	}
   138  }
   139  
   140  // OnEnter is not enabled by default.
   141  func (l *jsonLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   142  	frame := callFrame{
   143  		op:    vm.OpCode(typ),
   144  		From:  from,
   145  		To:    to,
   146  		Gas:   gas,
   147  		Value: value,
   148  	}
   149  	if l.cfg.EnableMemory {
   150  		frame.Input = input
   151  	}
   152  	l.encoder.Encode(frame)
   153  }
   154  
   155  func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
   156  	type endLog struct {
   157  		Output  string              `json:"output"`
   158  		GasUsed math.HexOrDecimal64 `json:"gasUsed"`
   159  		Err     string              `json:"error,omitempty"`
   160  	}
   161  	var errMsg string
   162  	if err != nil {
   163  		errMsg = err.Error()
   164  	}
   165  	l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
   166  }
   167  
   168  func (l *jsonLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
   169  	l.env = env
   170  }