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 }