github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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  }
    62  
    63  // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
    64  // into the provided stream.
    65  func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks {
    66  	l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
    67  	if l.cfg == nil {
    68  		l.cfg = &Config{}
    69  	}
    70  	return &tracing.Hooks{
    71  		OnTxStart: l.OnTxStart,
    72  		OnExit:    l.OnExit,
    73  		OnOpcode:  l.OnOpcode,
    74  		OnFault:   l.OnFault,
    75  	}
    76  }
    77  
    78  // NewJSONLoggerWithCallFrames creates a new EVM tracer that prints execution steps as JSON objects
    79  // into the provided stream. It also includes call frames in the output.
    80  func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) *tracing.Hooks {
    81  	l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
    82  	if l.cfg == nil {
    83  		l.cfg = &Config{}
    84  	}
    85  	return &tracing.Hooks{
    86  		OnTxStart: l.OnTxStart,
    87  		OnEnter:   l.OnEnter,
    88  		OnExit:    l.OnExit,
    89  		OnOpcode:  l.OnOpcode,
    90  		OnFault:   l.OnFault,
    91  	}
    92  }
    93  
    94  func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) {
    95  	// TODO: Add rData to this interface as well
    96  	l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err)
    97  }
    98  
    99  func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
   100  	memory := scope.MemoryData()
   101  	stack := scope.StackData()
   102  
   103  	log := StructLog{
   104  		Pc:            pc,
   105  		Op:            vm.OpCode(op),
   106  		Gas:           gas,
   107  		GasCost:       cost,
   108  		MemorySize:    len(memory),
   109  		Depth:         depth,
   110  		RefundCounter: l.env.StateDB.GetRefund(),
   111  		Err:           err,
   112  	}
   113  	if l.cfg.EnableMemory {
   114  		log.Memory = memory
   115  	}
   116  	if !l.cfg.DisableStack {
   117  		log.Stack = stack
   118  	}
   119  	if l.cfg.EnableReturnData {
   120  		log.ReturnData = rData
   121  	}
   122  	l.encoder.Encode(log)
   123  }
   124  
   125  // OnEnter is not enabled by default.
   126  func (l *jsonLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   127  	frame := callFrame{
   128  		op:    vm.OpCode(typ),
   129  		From:  from,
   130  		To:    to,
   131  		Gas:   gas,
   132  		Value: value,
   133  	}
   134  	if l.cfg.EnableMemory {
   135  		frame.Input = input
   136  	}
   137  	l.encoder.Encode(frame)
   138  }
   139  
   140  func (l *jsonLogger) OnEnd(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
   141  	if depth > 0 {
   142  		return
   143  	}
   144  	l.OnExit(depth, output, gasUsed, err, false)
   145  }
   146  
   147  func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
   148  	type endLog struct {
   149  		Output  string              `json:"output"`
   150  		GasUsed math.HexOrDecimal64 `json:"gasUsed"`
   151  		Err     string              `json:"error,omitempty"`
   152  	}
   153  	var errMsg string
   154  	if err != nil {
   155  		errMsg = err.Error()
   156  	}
   157  	l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
   158  }
   159  
   160  func (l *jsonLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
   161  	l.env = env
   162  }