github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/core/vm/logger.go (about)

     1  package vm
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"io"
     7  	"math/big"
     8  	"time"
     9  
    10  	"github.com/quickchainproject/quickchain/common"
    11  	"github.com/quickchainproject/quickchain/common/hexutil"
    12  	"github.com/quickchainproject/quickchain/common/math"
    13  	"github.com/quickchainproject/quickchain/core/types"
    14  )
    15  
    16  type Storage map[common.Hash]common.Hash
    17  
    18  func (s Storage) Copy() Storage {
    19  	cpy := make(Storage)
    20  	for key, value := range s {
    21  		cpy[key] = value
    22  	}
    23  
    24  	return cpy
    25  }
    26  
    27  // LogConfig are the configuration options for structured logger the EVM
    28  type LogConfig struct {
    29  	DisableMemory  bool // disable memory capture
    30  	DisableStack   bool // disable stack capture
    31  	DisableStorage bool // disable storage capture
    32  	Debug          bool // print output during capture end
    33  	Limit          int  // maximum length of output, but zero means unlimited
    34  }
    35  
    36  //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
    37  
    38  // StructLog is emitted to the EVM each cycle and lists information about the current internal state
    39  // prior to the execution of the statement.
    40  type StructLog struct {
    41  	Pc         uint64                      `json:"pc"`
    42  	Op         OpCode                      `json:"op"`
    43  	Gas        uint64                      `json:"gas"`
    44  	GasCost    uint64                      `json:"gasCost"`
    45  	Memory     []byte                      `json:"memory"`
    46  	MemorySize int                         `json:"memSize"`
    47  	Stack      []*big.Int                  `json:"stack"`
    48  	Storage    map[common.Hash]common.Hash `json:"-"`
    49  	Depth      int                         `json:"depth"`
    50  	Err        error                       `json:"-"`
    51  }
    52  
    53  // overrides for gencodec
    54  type structLogMarshaling struct {
    55  	Stack       []*math.HexOrDecimal256
    56  	Gas         math.HexOrDecimal64
    57  	GasCost     math.HexOrDecimal64
    58  	Memory      hexutil.Bytes
    59  	OpName      string `json:"opName"` // adds call to OpName() in MarshalJSON
    60  	ErrorString string `json:"error"`  // adds call to ErrorString() in MarshalJSON
    61  }
    62  
    63  func (s *StructLog) OpName() string {
    64  	return s.Op.String()
    65  }
    66  
    67  func (s *StructLog) ErrorString() string {
    68  	if s.Err != nil {
    69  		return s.Err.Error()
    70  	}
    71  	return ""
    72  }
    73  
    74  // Tracer is used to collect execution traces from an EVM transaction
    75  // execution. CaptureState is called for each step of the VM with the
    76  // current VM state.
    77  // Note that reference types are actual VM data structures; make copies
    78  // if you need to retain them beyond the current call.
    79  type Tracer interface {
    80  	CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
    81  	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
    82  	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
    83  	CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
    84  }
    85  
    86  // StructLogger is an EVM state logger and implements Tracer.
    87  //
    88  // StructLogger can capture state based on the given Log configuration and also keeps
    89  // a track record of modified storage which is used in reporting snapshots of the
    90  // contract their storage.
    91  type StructLogger struct {
    92  	cfg LogConfig
    93  
    94  	logs          []StructLog
    95  	changedValues map[common.Address]Storage
    96  	output        []byte
    97  	err           error
    98  }
    99  
   100  // NewStructLogger returns a new logger
   101  func NewStructLogger(cfg *LogConfig) *StructLogger {
   102  	logger := &StructLogger{
   103  		changedValues: make(map[common.Address]Storage),
   104  	}
   105  	if cfg != nil {
   106  		logger.cfg = *cfg
   107  	}
   108  	return logger
   109  }
   110  
   111  func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
   112  	return nil
   113  }
   114  
   115  // CaptureState logs a new structured log message and pushes it out to the environment
   116  //
   117  // CaptureState also tracks SSTORE ops to track dirty values.
   118  func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
   119  	// check if already accumulated the specified number of logs
   120  	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
   121  		return ErrTraceLimitReached
   122  	}
   123  
   124  	// initialise new changed values storage container for this contract
   125  	// if not present.
   126  	if l.changedValues[contract.Address()] == nil {
   127  		l.changedValues[contract.Address()] = make(Storage)
   128  	}
   129  
   130  	// capture SSTORE opcodes and determine the changed value and store
   131  	// it in the local storage container.
   132  	if op == SSTORE && stack.len() >= 2 {
   133  		var (
   134  			value   = common.BigToHash(stack.data[stack.len()-2])
   135  			address = common.BigToHash(stack.data[stack.len()-1])
   136  		)
   137  		l.changedValues[contract.Address()][address] = value
   138  	}
   139  	// Copy a snapstot of the current memory state to a new buffer
   140  	var mem []byte
   141  	if !l.cfg.DisableMemory {
   142  		mem = make([]byte, len(memory.Data()))
   143  		copy(mem, memory.Data())
   144  	}
   145  	// Copy a snapshot of the current stack state to a new buffer
   146  	var stck []*big.Int
   147  	if !l.cfg.DisableStack {
   148  		stck = make([]*big.Int, len(stack.Data()))
   149  		for i, item := range stack.Data() {
   150  			stck[i] = new(big.Int).Set(item)
   151  		}
   152  	}
   153  	// Copy a snapshot of the current storage to a new container
   154  	var storage Storage
   155  	if !l.cfg.DisableStorage {
   156  		storage = l.changedValues[contract.Address()].Copy()
   157  	}
   158  	// create a new snaptshot of the EVM.
   159  	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err}
   160  
   161  	l.logs = append(l.logs, log)
   162  	return nil
   163  }
   164  
   165  func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
   166  	return nil
   167  }
   168  
   169  func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
   170  	l.output = output
   171  	l.err = err
   172  	if l.cfg.Debug {
   173  		fmt.Printf("0x%x\n", output)
   174  		if err != nil {
   175  			fmt.Printf(" error: %v\n", err)
   176  		}
   177  	}
   178  	return nil
   179  }
   180  
   181  // StructLogs returns the captured log entries.
   182  func (l *StructLogger) StructLogs() []StructLog { return l.logs }
   183  
   184  // Error returns the VM error captured by the trace.
   185  func (l *StructLogger) Error() error { return l.err }
   186  
   187  // Output returns the VM return value captured by the trace.
   188  func (l *StructLogger) Output() []byte { return l.output }
   189  
   190  // WriteTrace writes a formatted trace to the given writer
   191  func WriteTrace(writer io.Writer, logs []StructLog) {
   192  	for _, log := range logs {
   193  		fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
   194  		if log.Err != nil {
   195  			fmt.Fprintf(writer, " ERROR: %v", log.Err)
   196  		}
   197  		fmt.Fprintln(writer)
   198  
   199  		if len(log.Stack) > 0 {
   200  			fmt.Fprintln(writer, "Stack:")
   201  			for i := len(log.Stack) - 1; i >= 0; i-- {
   202  				fmt.Fprintf(writer, "%08d  %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
   203  			}
   204  		}
   205  		if len(log.Memory) > 0 {
   206  			fmt.Fprintln(writer, "Memory:")
   207  			fmt.Fprint(writer, hex.Dump(log.Memory))
   208  		}
   209  		if len(log.Storage) > 0 {
   210  			fmt.Fprintln(writer, "Storage:")
   211  			for h, item := range log.Storage {
   212  				fmt.Fprintf(writer, "%x: %x\n", h, item)
   213  			}
   214  		}
   215  		fmt.Fprintln(writer)
   216  	}
   217  }
   218  
   219  // WriteLogs writes vm logs in a readable format to the given writer
   220  func WriteLogs(writer io.Writer, logs []*types.Log) {
   221  	for _, log := range logs {
   222  		fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
   223  
   224  		for i, topic := range log.Topics {
   225  			fmt.Fprintf(writer, "%08d  %x\n", i, topic)
   226  		}
   227  
   228  		fmt.Fprint(writer, hex.Dump(log.Data))
   229  		fmt.Fprintln(writer)
   230  	}
   231  }