github.com/codingfuture/orig-energi3@v0.8.4/core/vm/logger.go (about)

     1  // Copyright 2018 The Energi Core Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the Energi Core library.
     4  //
     5  // The Energi Core 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 Energi Core 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 Energi Core library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package vm
    19  
    20  import (
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	"math/big"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/common/math"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  )
    32  
    33  // Storage represents a contract's storage.
    34  type Storage map[common.Hash]common.Hash
    35  
    36  // Copy duplicates the current storage.
    37  func (s Storage) Copy() Storage {
    38  	cpy := make(Storage)
    39  	for key, value := range s {
    40  		cpy[key] = value
    41  	}
    42  
    43  	return cpy
    44  }
    45  
    46  // LogConfig are the configuration options for structured logger the EVM
    47  type LogConfig struct {
    48  	DisableMemory  bool // disable memory capture
    49  	DisableStack   bool // disable stack capture
    50  	DisableStorage bool // disable storage capture
    51  	OnlyClosures   bool // capture only call related opcodes
    52  	Debug          bool // print output during capture end
    53  	Limit          int  // maximum length of output, but zero means unlimited
    54  }
    55  
    56  //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
    57  
    58  // StructLog is emitted to the EVM each cycle and lists information about the current internal state
    59  // prior to the execution of the statement.
    60  type StructLog struct {
    61  	Pc            uint64                      `json:"pc"`
    62  	Op            OpCode                      `json:"op"`
    63  	Gas           uint64                      `json:"gas"`
    64  	GasCost       uint64                      `json:"gasCost"`
    65  	Memory        []byte                      `json:"memory"`
    66  	MemorySize    int                         `json:"memSize"`
    67  	Stack         []*big.Int                  `json:"stack"`
    68  	Storage       map[common.Hash]common.Hash `json:"-"`
    69  	Depth         int                         `json:"depth"`
    70  	RefundCounter uint64                      `json:"refund"`
    71  	Err           error                       `json:"-"`
    72  }
    73  
    74  // overrides for gencodec
    75  type structLogMarshaling struct {
    76  	Stack       []*math.HexOrDecimal256
    77  	Gas         math.HexOrDecimal64
    78  	GasCost     math.HexOrDecimal64
    79  	Memory      hexutil.Bytes
    80  	OpName      string `json:"opName"` // adds call to OpName() in MarshalJSON
    81  	ErrorString string `json:"error"`  // adds call to ErrorString() in MarshalJSON
    82  }
    83  
    84  // OpName formats the operand name in a human-readable format.
    85  func (s *StructLog) OpName() string {
    86  	return s.Op.String()
    87  }
    88  
    89  // ErrorString formats the log's error as a string.
    90  func (s *StructLog) ErrorString() string {
    91  	if s.Err != nil {
    92  		return s.Err.Error()
    93  	}
    94  	return ""
    95  }
    96  
    97  // Tracer is used to collect execution traces from an EVM transaction
    98  // execution. CaptureState is called for each step of the VM with the
    99  // current VM state.
   100  // Note that reference types are actual VM data structures; make copies
   101  // if you need to retain them beyond the current call.
   102  type Tracer interface {
   103  	CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
   104  	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
   105  	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
   106  	CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
   107  }
   108  
   109  // StructLogger is an EVM state logger and implements Tracer.
   110  //
   111  // StructLogger can capture state based on the given Log configuration and also keeps
   112  // a track record of modified storage which is used in reporting snapshots of the
   113  // contract their storage.
   114  type StructLogger struct {
   115  	cfg LogConfig
   116  
   117  	logs          []StructLog
   118  	changedValues map[common.Address]Storage
   119  	output        []byte
   120  	err           error
   121  }
   122  
   123  // NewStructLogger returns a new logger
   124  func NewStructLogger(cfg *LogConfig) *StructLogger {
   125  	logger := &StructLogger{
   126  		changedValues: make(map[common.Address]Storage),
   127  	}
   128  	if cfg != nil {
   129  		logger.cfg = *cfg
   130  	}
   131  	return logger
   132  }
   133  
   134  // CaptureStart implements the Tracer interface to initialize the tracing operation.
   135  func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
   136  	return nil
   137  }
   138  
   139  // CaptureState logs a new structured log message and pushes it out to the environment
   140  //
   141  // CaptureState also tracks SSTORE ops to track dirty values.
   142  func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
   143  	// check if already accumulated the specified number of logs
   144  	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
   145  		return ErrTraceLimitReached
   146  	}
   147  
   148  	if l.cfg.OnlyClosures {
   149  		arg_count := 0
   150  		mem_off_pos := -1
   151  		mem_len_pos := -1
   152  
   153  		switch op {
   154  		case CREATE:
   155  			arg_count = 3
   156  			mem_len_pos = 0
   157  			mem_off_pos = 1
   158  			break
   159  		case CALL, CALLCODE:
   160  			arg_count = 7
   161  			mem_len_pos = 2
   162  			mem_off_pos = 3
   163  			break
   164  		case RETURN, REVERT:
   165  			arg_count = 2
   166  			mem_len_pos = 0
   167  			mem_off_pos = 1
   168  			break
   169  		case DELEGATECALL, STATICCALL:
   170  			arg_count = 6
   171  			mem_len_pos = 2
   172  			mem_off_pos = 3
   173  			break
   174  		case CREATE2:
   175  			arg_count = 4
   176  			mem_len_pos = 0
   177  			mem_off_pos = 1
   178  			break
   179  		case SELFDESTRUCT:
   180  			arg_count = 1
   181  			break
   182  		default:
   183  			return nil
   184  		}
   185  
   186  		var stck []*big.Int
   187  		var mem []byte
   188  
   189  		if stack_len := len(stack.Data()); arg_count > 0 && arg_count <= stack_len {
   190  			stck = make([]*big.Int, arg_count)
   191  			for i, item := range stack.Data()[stack_len-arg_count:] {
   192  				stck[i] = new(big.Int).Set(item)
   193  			}
   194  
   195  			if mem_off_pos >= 0 {
   196  				mem_start := stck[mem_off_pos].Int64()
   197  				mem_end := stck[mem_off_pos].Int64() + stck[mem_len_pos].Int64()
   198  
   199  				if mem_len := mem_end - mem_start; mem_len > 0 {
   200  					if ml := int64(len(memory.Data())); mem_len > ml {
   201  						mem_len = ml
   202  						mem_end = mem_start + mem_len
   203  					}
   204  					mem = memory.Data()[mem_start:mem_end]
   205  				}
   206  
   207  				// Adjust stack
   208  				stck[mem_off_pos].SetInt64(stck[mem_off_pos].Int64() - mem_start)
   209  			}
   210  		}
   211  
   212  		memory = &Memory{mem, memory.lastGasCost}
   213  		stack = &Stack{stck}
   214  	}
   215  
   216  	// initialise new changed values storage container for this contract
   217  	// if not present.
   218  	if l.changedValues[contract.Address()] == nil {
   219  		l.changedValues[contract.Address()] = make(Storage)
   220  	}
   221  
   222  	// capture SSTORE opcodes and determine the changed value and store
   223  	// it in the local storage container.
   224  	if op == SSTORE && stack.len() >= 2 {
   225  		var (
   226  			value   = common.BigToHash(stack.data[stack.len()-2])
   227  			address = common.BigToHash(stack.data[stack.len()-1])
   228  		)
   229  		l.changedValues[contract.Address()][address] = value
   230  	}
   231  	// Copy a snapstot of the current memory state to a new buffer
   232  	var mem []byte
   233  	if !l.cfg.DisableMemory {
   234  		mem = make([]byte, len(memory.Data()))
   235  		copy(mem, memory.Data())
   236  	}
   237  	// Copy a snapshot of the current stack state to a new buffer
   238  	var stck []*big.Int
   239  	if !l.cfg.DisableStack {
   240  		stck = make([]*big.Int, len(stack.Data()))
   241  		for i, item := range stack.Data() {
   242  			stck[i] = new(big.Int).Set(item)
   243  		}
   244  	}
   245  	// Copy a snapshot of the current storage to a new container
   246  	var storage Storage
   247  	if !l.cfg.DisableStorage {
   248  		storage = l.changedValues[contract.Address()].Copy()
   249  	}
   250  	// create a new snaptshot of the EVM.
   251  	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
   252  
   253  	l.logs = append(l.logs, log)
   254  	return nil
   255  }
   256  
   257  // CaptureFault implements the Tracer interface to trace an execution fault
   258  // while running an opcode.
   259  func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
   260  	return nil
   261  }
   262  
   263  // CaptureEnd is called after the call finishes to finalize the tracing.
   264  func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
   265  	l.output = output
   266  	l.err = err
   267  	if l.cfg.Debug {
   268  		fmt.Printf("0x%x\n", output)
   269  		if err != nil {
   270  			fmt.Printf(" error: %v\n", err)
   271  		}
   272  	}
   273  	return nil
   274  }
   275  
   276  // StructLogs returns the captured log entries.
   277  func (l *StructLogger) StructLogs() []StructLog { return l.logs }
   278  
   279  // Error returns the VM error captured by the trace.
   280  func (l *StructLogger) Error() error { return l.err }
   281  
   282  // Output returns the VM return value captured by the trace.
   283  func (l *StructLogger) Output() []byte { return l.output }
   284  
   285  // WriteTrace writes a formatted trace to the given writer
   286  func WriteTrace(writer io.Writer, logs []StructLog) {
   287  	for _, log := range logs {
   288  		fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
   289  		if log.Err != nil {
   290  			fmt.Fprintf(writer, " ERROR: %v", log.Err)
   291  		}
   292  		fmt.Fprintln(writer)
   293  
   294  		if len(log.Stack) > 0 {
   295  			fmt.Fprintln(writer, "Stack:")
   296  			for i := len(log.Stack) - 1; i >= 0; i-- {
   297  				fmt.Fprintf(writer, "%08d  %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
   298  			}
   299  		}
   300  		if len(log.Memory) > 0 {
   301  			fmt.Fprintln(writer, "Memory:")
   302  			fmt.Fprint(writer, hex.Dump(log.Memory))
   303  		}
   304  		if len(log.Storage) > 0 {
   305  			fmt.Fprintln(writer, "Storage:")
   306  			for h, item := range log.Storage {
   307  				fmt.Fprintf(writer, "%x: %x\n", h, item)
   308  			}
   309  		}
   310  		fmt.Fprintln(writer)
   311  	}
   312  }
   313  
   314  // WriteLogs writes vm logs in a readable format to the given writer
   315  func WriteLogs(writer io.Writer, logs []*types.Log) {
   316  	for _, log := range logs {
   317  		fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
   318  
   319  		for i, topic := range log.Topics {
   320  			fmt.Fprintf(writer, "%08d  %x\n", i, topic)
   321  		}
   322  
   323  		fmt.Fprint(writer, hex.Dump(log.Data))
   324  		fmt.Fprintln(writer)
   325  	}
   326  }