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 }