github.com/ontio/ontology@v1.14.4/vm/evm/logger.go (about) 1 // Copyright (C) 2021 The Ontology 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 package evm 19 20 import ( 21 "encoding/hex" 22 "errors" 23 "fmt" 24 "io" 25 "math/big" 26 "strings" 27 "time" 28 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/common/hexutil" 31 "github.com/ethereum/go-ethereum/common/math" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ontio/ontology/vm/evm/params" 34 ) 35 36 var errTraceLimitReached = errors.New("the number of logs reached the specified limit") 37 38 // Storage represents a contract's storage. 39 type Storage map[common.Hash]common.Hash 40 41 // Copy duplicates the current storage. 42 func (s Storage) Copy() Storage { 43 cpy := make(Storage) 44 for key, value := range s { 45 cpy[key] = value 46 } 47 return cpy 48 } 49 50 // LogConfig are the configuration options for structured logger the EVM 51 type LogConfig struct { 52 DisableMemory bool // disable memory capture 53 DisableStack bool // disable stack capture 54 DisableStorage bool // disable storage capture 55 DisableReturnData bool // disable return data capture 56 Debug bool // print output during capture end 57 Limit int // maximum length of output, but zero means unlimited 58 // Chain overrides, can be used to execute a trace using future fork rules 59 Overrides *params.ChainConfig `json:"overrides,omitempty"` 60 } 61 62 //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go 63 64 // StructLog is emitted to the EVM each cycle and lists information about the current internal state 65 // prior to the execution of the statement. 66 type StructLog struct { 67 Pc uint64 `json:"pc"` 68 Op OpCode `json:"op"` 69 Gas uint64 `json:"gas"` 70 GasCost uint64 `json:"gasCost"` 71 Memory []byte `json:"memory"` 72 MemorySize int `json:"memSize"` 73 Stack []*big.Int `json:"stack"` 74 ReturnStack []uint32 `json:"returnStack"` 75 ReturnData []byte `json:"returnData"` 76 Storage map[common.Hash]common.Hash `json:"-"` 77 Depth int `json:"depth"` 78 RefundCounter uint64 `json:"refund"` 79 Err error `json:"-"` 80 } 81 82 // overrides for gencodec 83 type structLogMarshaling struct { 84 Stack []*math.HexOrDecimal256 85 ReturnStack []math.HexOrDecimal64 86 Gas math.HexOrDecimal64 87 GasCost math.HexOrDecimal64 88 Memory hexutil.Bytes 89 ReturnData hexutil.Bytes 90 OpName string `json:"opName"` // adds call to OpName() in MarshalJSON 91 ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON 92 } 93 94 // OpName formats the operand name in a human-readable format. 95 func (s *StructLog) OpName() string { 96 return s.Op.String() 97 } 98 99 // ErrorString formats the log's error as a string. 100 func (s *StructLog) ErrorString() string { 101 if s.Err != nil { 102 return s.Err.Error() 103 } 104 return "" 105 } 106 107 // Tracer is used to collect execution traces from an EVM transaction 108 // execution. CaptureState is called for each step of the VM with the 109 // current VM state. 110 // Note that reference types are actual VM data structures; make copies 111 // if you need to retain them beyond the current call. 112 type Tracer interface { 113 CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) 114 CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, 115 rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) 116 CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, 117 rStack *ReturnStack, contract *Contract, depth int, err error) 118 CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) 119 } 120 121 // StructLogger is an EVM state logger and implements Tracer. 122 // 123 // StructLogger can capture state based on the given Log configuration and also keeps 124 // a track record of modified storage which is used in reporting snapshots of the 125 // contract their storage. 126 type StructLogger struct { 127 cfg LogConfig 128 129 storage map[common.Address]Storage 130 logs []StructLog 131 output []byte 132 err error 133 } 134 135 // NewStructLogger returns a new logger 136 func NewStructLogger(cfg *LogConfig) *StructLogger { 137 logger := &StructLogger{ 138 storage: make(map[common.Address]Storage), 139 } 140 if cfg != nil { 141 logger.cfg = *cfg 142 } 143 return logger 144 } 145 146 // CaptureStart implements the Tracer interface to initialize the tracing operation. 147 func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 148 } 149 150 // CaptureState logs a new structured log message and pushes it out to the environment 151 // 152 // CaptureState also tracks SLOAD/SSTORE ops to track storage change. 153 func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, 154 stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) { 155 // check if already accumulated the specified number of logs 156 if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { 157 return 158 } 159 // Copy a snapshot of the current memory state to a new buffer 160 var mem []byte 161 if !l.cfg.DisableMemory { 162 mem = make([]byte, len(memory.Data())) 163 copy(mem, memory.Data()) 164 } 165 // Copy a snapshot of the current stack state to a new buffer 166 var stck []*big.Int 167 if !l.cfg.DisableStack { 168 stck = make([]*big.Int, len(stack.Data())) 169 for i, item := range stack.Data() { 170 stck[i] = new(big.Int).Set(item.ToBig()) 171 } 172 } 173 var rstack []uint32 174 if !l.cfg.DisableStack && rStack != nil { 175 rstck := make([]uint32, len(rStack.data)) 176 copy(rstck, rStack.data) 177 } 178 // Copy a snapshot of the current storage to a new container 179 var storage Storage 180 if !l.cfg.DisableStorage { 181 // initialise new changed values storage container for this contract 182 // if not present. 183 if l.storage[contract.Address()] == nil { 184 l.storage[contract.Address()] = make(Storage) 185 } 186 // capture SLOAD opcodes and record the read entry in the local storage 187 if op == SLOAD && stack.len() >= 1 { 188 var ( 189 address = common.Hash(stack.data[stack.len()-1].Bytes32()) 190 value = env.StateDB.GetState(contract.Address(), address) 191 ) 192 l.storage[contract.Address()][address] = value 193 } 194 // capture SSTORE opcodes and record the written entry in the local storage. 195 if op == SSTORE && stack.len() >= 2 { 196 var ( 197 value = common.Hash(stack.data[stack.len()-2].Bytes32()) 198 address = common.Hash(stack.data[stack.len()-1].Bytes32()) 199 ) 200 l.storage[contract.Address()][address] = value 201 } 202 storage = l.storage[contract.Address()].Copy() 203 } 204 var rdata []byte 205 if !l.cfg.DisableReturnData { 206 rdata = make([]byte, len(rData)) 207 copy(rdata, rData) 208 } 209 // create a new snapshot of the EVM. 210 log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err} 211 l.logs = append(l.logs, log) 212 } 213 214 // CaptureFault implements the Tracer interface to trace an execution fault 215 // while running an opcode. 216 func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, 217 stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) { 218 } 219 220 // CaptureEnd is called after the call finishes to finalize the tracing. 221 func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { 222 l.output = output 223 l.err = err 224 if l.cfg.Debug { 225 fmt.Printf("0x%x\n", output) 226 if err != nil { 227 fmt.Printf(" error: %v\n", err) 228 } 229 } 230 } 231 232 // StructLogs returns the captured log entries. 233 func (l *StructLogger) StructLogs() []StructLog { return l.logs } 234 235 // Error returns the VM error captured by the trace. 236 func (l *StructLogger) Error() error { return l.err } 237 238 // Output returns the VM return value captured by the trace. 239 func (l *StructLogger) Output() []byte { return l.output } 240 241 // WriteTrace writes a formatted trace to the given writer 242 func WriteTrace(writer io.Writer, logs []StructLog) { 243 for _, log := range logs { 244 _, _ = fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) 245 if log.Err != nil { 246 _, _ = fmt.Fprintf(writer, " ERROR: %v", log.Err) 247 } 248 _, _ = fmt.Fprintln(writer) 249 250 if len(log.Stack) > 0 { 251 _, _ = fmt.Fprintln(writer, "Stack:") 252 for i := len(log.Stack) - 1; i >= 0; i-- { 253 _, _ = fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32)) 254 } 255 } 256 if len(log.ReturnStack) > 0 { 257 _, _ = fmt.Fprintln(writer, "ReturnStack:") 258 for i := len(log.Stack) - 1; i >= 0; i-- { 259 _, _ = fmt.Fprintf(writer, "%08d 0x%x (%d)\n", len(log.Stack)-i-1, log.ReturnStack[i], log.ReturnStack[i]) 260 } 261 } 262 if len(log.Memory) > 0 { 263 _, _ = fmt.Fprintln(writer, "Memory:") 264 _, _ = fmt.Fprint(writer, hex.Dump(log.Memory)) 265 } 266 if len(log.Storage) > 0 { 267 _, _ = fmt.Fprintln(writer, "Storage:") 268 for h, item := range log.Storage { 269 _, _ = fmt.Fprintf(writer, "%x: %x\n", h, item) 270 } 271 } 272 if len(log.ReturnData) > 0 { 273 _, _ = fmt.Fprintln(writer, "ReturnData:") 274 _, _ = fmt.Fprint(writer, hex.Dump(log.ReturnData)) 275 } 276 _, _ = fmt.Fprintln(writer) 277 } 278 } 279 280 // WriteLogs writes vm logs in a readable format to the given writer 281 func WriteLogs(writer io.Writer, logs []*types.Log) { 282 for _, log := range logs { 283 _, _ = fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) 284 285 for i, topic := range log.Topics { 286 _, _ = fmt.Fprintf(writer, "%08d %x\n", i, topic) 287 } 288 289 _, _ = fmt.Fprint(writer, hex.Dump(log.Data)) 290 _, _ = fmt.Fprintln(writer) 291 } 292 } 293 294 type mdLogger struct { 295 out io.Writer 296 cfg *LogConfig 297 } 298 299 // NewMarkdownLogger creates a logger which outputs information in a format adapted 300 // for human readability, and is also a valid markdown table 301 func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { 302 l := &mdLogger{writer, cfg} 303 if l.cfg == nil { 304 l.cfg = &LogConfig{} 305 } 306 return l 307 } 308 309 func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 310 if !create { 311 _, _ = fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", 312 from.String(), to.String(), 313 input, gas, value) 314 } else { 315 _, _ = fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", 316 from.String(), to.String(), 317 input, gas, value) 318 } 319 320 _, _ = fmt.Fprintf(t.out, ` 321 | Pc | Op | Cost | Stack | RStack | Refund | 322 |-------|-------------|------|-----------|-----------|---------| 323 `) 324 325 } 326 327 func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, 328 stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) { 329 _, _ = fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) 330 331 if !t.cfg.DisableStack { 332 // format stack 333 var a []string 334 for _, elem := range stack.data { 335 a = append(a, fmt.Sprintf("%v", elem.String())) 336 } 337 b := fmt.Sprintf("[%v]", strings.Join(a, ",")) 338 _, _ = fmt.Fprintf(t.out, "%10v |", b) 339 340 // format return stack 341 a = a[:0] 342 for _, elem := range rStack.data { 343 a = append(a, fmt.Sprintf("%2d", elem)) 344 } 345 b = fmt.Sprintf("[%v]", strings.Join(a, ",")) 346 _, _ = fmt.Fprintf(t.out, "%10v |", b) 347 } 348 _, _ = fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund()) 349 _, _ = fmt.Fprintln(t.out, "") 350 if err != nil { 351 _, _ = fmt.Fprintf(t.out, "Error: %v\n", err) 352 } 353 } 354 355 func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, 356 stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) { 357 _, _ = fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) 358 } 359 360 func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) { 361 _, _ = fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n", 362 output, gasUsed, err) 363 }