github.com/klaytn/klaytn@v1.12.1/blockchain/vm/logger.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from core/vm/logger.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package vm
    22  
    23  import (
    24  	"encoding/hex"
    25  	"fmt"
    26  	"io"
    27  	"math/big"
    28  
    29  	"github.com/klaytn/klaytn/blockchain/types"
    30  	"github.com/klaytn/klaytn/common"
    31  	"github.com/klaytn/klaytn/common/hexutil"
    32  	"github.com/klaytn/klaytn/common/math"
    33  )
    34  
    35  // Storage represents a contract's storage.
    36  type Storage map[common.Hash]common.Hash
    37  
    38  // Copy duplicates the current storage.
    39  func (s Storage) Copy() Storage {
    40  	cpy := make(Storage)
    41  	for key, value := range s {
    42  		cpy[key] = value
    43  	}
    44  
    45  	return cpy
    46  }
    47  
    48  // LogConfig are the configuration options for structured logger the EVM
    49  type LogConfig struct {
    50  	DisableMemory  bool // disable memory capture
    51  	DisableStack   bool // disable stack capture
    52  	DisableStorage bool // disable storage capture
    53  	Debug          bool // print output during capture end
    54  	Limit          int  // maximum length of output, but zero means unlimited
    55  }
    56  
    57  //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
    58  
    59  // StructLog is emitted to the EVM each cycle and lists information about the current internal state
    60  // prior to the execution of the statement.
    61  type StructLog struct {
    62  	Pc              uint64                      `json:"pc"`
    63  	Op              OpCode                      `json:"op"`
    64  	Gas             uint64                      `json:"gas"`
    65  	GasCost         uint64                      `json:"gasCost"`
    66  	Memory          []byte                      `json:"memory"`
    67  	MemorySize      int                         `json:"memSize"`
    68  	Stack           []*big.Int                  `json:"stack"`
    69  	Storage         map[common.Hash]common.Hash `json:"-"`
    70  	Depth           int                         `json:"depth"`
    71  	RefundCounter   uint64                      `json:"refund"`
    72  	Computation     uint64                      `json:"computation"`
    73  	ComputationCost uint64                      `json:"computationCost"`
    74  	Err             error                       `json:"-"`
    75  }
    76  
    77  // overrides for gencodec
    78  type structLogMarshaling struct {
    79  	Stack       []*math.HexOrDecimal256
    80  	Gas         math.HexOrDecimal64
    81  	GasCost     math.HexOrDecimal64
    82  	Memory      hexutil.Bytes
    83  	OpName      string `json:"opName"` // adds call to OpName() in MarshalJSON
    84  	ErrorString string `json:"error"`  // adds call to ErrorString() in MarshalJSON
    85  }
    86  
    87  // OpName formats the operand name in a human-readable format.
    88  func (s *StructLog) OpName() string {
    89  	return s.Op.String()
    90  }
    91  
    92  // ErrorString formats the log's error as a string.
    93  func (s *StructLog) ErrorString() string {
    94  	if s.Err != nil {
    95  		return s.Err.Error()
    96  	}
    97  	return ""
    98  }
    99  
   100  // Tracer is used to collect execution traces from an EVM transaction
   101  // execution. CaptureState is called for each step of the VM with the
   102  // current VM state.
   103  // Note that reference types are actual VM data structures; make copies
   104  // if you need to retain them beyond the current call.
   105  type Tracer interface {
   106  	// Transaction level
   107  	CaptureTxStart(gasLimit uint64)
   108  	CaptureTxEnd(restGas uint64)
   109  	// Top call frame
   110  	CaptureStart(env *EVM, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int)
   111  	CaptureEnd(output []byte, gasUsed uint64, err error)
   112  	// Rest of call frames
   113  	CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
   114  	CaptureExit(output []byte, gasUsed uint64, err error)
   115  	// Opcode level
   116  	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
   117  	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
   118  }
   119  
   120  // StructLogger is an EVM state logger and implements Tracer.
   121  //
   122  // StructLogger can capture state based on the given Log configuration and also keeps
   123  // a track record of modified storage which is used in reporting snapshots of the
   124  // contract their storage.
   125  type StructLogger struct {
   126  	cfg LogConfig
   127  
   128  	logs          []StructLog
   129  	changedValues map[common.Address]Storage
   130  	output        []byte
   131  	err           error
   132  }
   133  
   134  // NewStructLogger returns a new logger
   135  func NewStructLogger(cfg *LogConfig) *StructLogger {
   136  	logger := &StructLogger{
   137  		changedValues: make(map[common.Address]Storage),
   138  	}
   139  	if cfg != nil {
   140  		logger.cfg = *cfg
   141  	}
   142  	return logger
   143  }
   144  
   145  // CaptureStart implements the Tracer interface to initialize the tracing operation.
   146  func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
   147  }
   148  
   149  // CaptureState logs a new structured log message and pushes it out to the environment
   150  //
   151  // CaptureState also tracks SSTORE ops to track dirty values.
   152  func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
   153  	memory := scope.Memory
   154  	stack := scope.Stack
   155  	contract := scope.Contract
   156  	// check if already accumulated the specified number of logs
   157  	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
   158  		return
   159  	}
   160  
   161  	// initialise new changed values storage container for this contract
   162  	// if not present.
   163  	if l.changedValues[contract.Address()] == nil {
   164  		l.changedValues[contract.Address()] = make(Storage)
   165  	}
   166  
   167  	// capture SSTORE opcodes and determine the changed value and store
   168  	// it in the local storage container.
   169  	if op == SSTORE && stack.len() >= 2 {
   170  		var (
   171  			value   = common.Hash(stack.data[stack.len()-2].Bytes32())
   172  			address = common.Hash(stack.data[stack.len()-1].Bytes32())
   173  		)
   174  		l.changedValues[contract.Address()][address] = value
   175  	}
   176  	// Copy a snapshot of the current memory state to a new buffer
   177  	var mem []byte
   178  	if !l.cfg.DisableMemory {
   179  		mem = make([]byte, len(memory.Data()))
   180  		copy(mem, memory.Data())
   181  	}
   182  	// Copy a snapshot of the current stack state to a new buffer
   183  	var stck []*big.Int
   184  	if !l.cfg.DisableStack {
   185  		stck = make([]*big.Int, len(stack.Data()))
   186  		for i, item := range stack.Data() {
   187  			stck[i] = new(big.Int).Set(item.ToBig())
   188  		}
   189  	}
   190  	// Copy a snapshot of the current storage to a new container
   191  	var storage Storage
   192  	if !l.cfg.DisableStorage {
   193  		storage = l.changedValues[contract.Address()].Copy()
   194  	}
   195  	// create a new snapshot of the EVM.
   196  	var (
   197  		remainingComputationCost = env.Config.ComputationCostLimit - env.opcodeComputationCostSum
   198  		opcodeComputationCost    = env.interpreter.cfg.JumpTable[op].computationCost
   199  	)
   200  	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), remainingComputationCost, opcodeComputationCost, err}
   201  
   202  	l.logs = append(l.logs, log)
   203  }
   204  
   205  // CaptureFault implements the Tracer interface to trace an execution fault
   206  // while running an opcode.
   207  func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
   208  }
   209  
   210  // CaptureEnd is called after the call finishes to finalize the tracing.
   211  func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
   212  	l.output = output
   213  	l.err = err
   214  	if l.cfg.Debug {
   215  		fmt.Printf("0x%x\n", output)
   216  		if err != nil {
   217  			fmt.Printf(" error: %v\n", err)
   218  		}
   219  	}
   220  }
   221  
   222  func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   223  }
   224  
   225  func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
   226  
   227  func (l *StructLogger) CaptureTxStart(gasLimit uint64) {}
   228  
   229  func (l *StructLogger) CaptureTxEnd(restGas uint64) {}
   230  
   231  // StructLogs returns the captured log entries.
   232  func (l *StructLogger) StructLogs() []StructLog { return l.logs }
   233  
   234  // Error returns the VM error captured by the trace.
   235  func (l *StructLogger) Error() error { return l.err }
   236  
   237  // Output returns the VM return value captured by the trace.
   238  func (l *StructLogger) Output() []byte { return l.output }
   239  
   240  // WriteTrace writes a formatted trace to the given writer
   241  func WriteTrace(writer io.Writer, logs []StructLog) {
   242  	for _, log := range logs {
   243  		fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
   244  		if log.Err != nil {
   245  			fmt.Fprintf(writer, " ERROR: %v", log.Err)
   246  		}
   247  		fmt.Fprintln(writer)
   248  
   249  		if len(log.Stack) > 0 {
   250  			fmt.Fprintln(writer, "Stack:")
   251  			for i := len(log.Stack) - 1; i >= 0; i-- {
   252  				fmt.Fprintf(writer, "%08d  %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
   253  			}
   254  		}
   255  		if len(log.Memory) > 0 {
   256  			fmt.Fprintln(writer, "Memory:")
   257  			fmt.Fprint(writer, hex.Dump(log.Memory))
   258  		}
   259  		if len(log.Storage) > 0 {
   260  			fmt.Fprintln(writer, "Storage:")
   261  			for h, item := range log.Storage {
   262  				fmt.Fprintf(writer, "%x: %x\n", h, item)
   263  			}
   264  		}
   265  		fmt.Fprintln(writer)
   266  	}
   267  }
   268  
   269  // WriteLogs writes vm logs in a readable format to the given writer
   270  func WriteLogs(writer io.Writer, logs []*types.Log) {
   271  	for _, log := range logs {
   272  		fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
   273  
   274  		for i, topic := range log.Topics {
   275  			fmt.Fprintf(writer, "%08d  %x\n", i, topic)
   276  		}
   277  
   278  		fmt.Fprint(writer, hex.Dump(log.Data))
   279  		fmt.Fprintln(writer)
   280  	}
   281  }