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