logger.go 8.65 KB
Newer Older
1
// Copyright 2015 The go-ethereum Authors
2
// This file is part of the go-ethereum library.
3
//
4
// The go-ethereum library is free software: you can redistribute it and/or modify
5 6 7 8
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
9
// The go-ethereum library is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 13 14
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
15
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16

17
package vm
18 19

import (
20
	"encoding/hex"
21
	"fmt"
22
	"io"
23
	"math/big"
24
	"time"
25 26

	"github.com/ethereum/go-ethereum/common"
27
	"github.com/ethereum/go-ethereum/common/hexutil"
28
	"github.com/ethereum/go-ethereum/common/math"
29
	"github.com/ethereum/go-ethereum/core/types"
30 31
)

32
// Storage represents a contract's storage.
33 34
type Storage map[common.Hash]common.Hash

35
// Copy duplicates the current storage.
36
func (s Storage) Copy() Storage {
37
	cpy := make(Storage)
38
	for key, value := range s {
39 40 41 42 43 44 45 46
		cpy[key] = value
	}

	return cpy
}

// LogConfig are the configuration options for structured logger the EVM
type LogConfig struct {
47 48 49
	DisableMemory  bool // disable memory capture
	DisableStack   bool // disable stack capture
	DisableStorage bool // disable storage capture
50
	Debug          bool // print output during capture end
51
	Limit          int  // maximum length of output, but zero means unlimited
52 53
}

54 55
//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go

56
// StructLog is emitted to the EVM each cycle and lists information about the current internal state
57 58
// prior to the execution of the statement.
type StructLog struct {
59 60 61 62 63 64 65 66 67 68 69
	Pc            uint64                      `json:"pc"`
	Op            OpCode                      `json:"op"`
	Gas           uint64                      `json:"gas"`
	GasCost       uint64                      `json:"gasCost"`
	Memory        []byte                      `json:"memory"`
	MemorySize    int                         `json:"memSize"`
	Stack         []*big.Int                  `json:"stack"`
	Storage       map[common.Hash]common.Hash `json:"-"`
	Depth         int                         `json:"depth"`
	RefundCounter uint64                      `json:"refund"`
	Err           error                       `json:"-"`
70 71 72 73
}

// overrides for gencodec
type structLogMarshaling struct {
74 75 76 77 78 79
	Stack       []*math.HexOrDecimal256
	Gas         math.HexOrDecimal64
	GasCost     math.HexOrDecimal64
	Memory      hexutil.Bytes
	OpName      string `json:"opName"` // adds call to OpName() in MarshalJSON
	ErrorString string `json:"error"`  // adds call to ErrorString() in MarshalJSON
80 81
}

82
// OpName formats the operand name in a human-readable format.
83 84 85 86
func (s *StructLog) OpName() string {
	return s.Op.String()
}

87
// ErrorString formats the log's error as a string.
88 89 90 91 92 93 94
func (s *StructLog) ErrorString() string {
	if s.Err != nil {
		return s.Err.Error()
	}
	return ""
}

95 96 97 98 99 100
// Tracer is used to collect execution traces from an EVM transaction
// execution. CaptureState is called for each step of the VM with the
// current VM state.
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
101
	CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
102
	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
103
	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
104
	CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
105 106 107
}

// StructLogger is an EVM state logger and implements Tracer.
108
//
109
// StructLogger can capture state based on the given Log configuration and also keeps
110 111
// a track record of modified storage which is used in reporting snapshots of the
// contract their storage.
112
type StructLogger struct {
113 114
	cfg LogConfig

115
	logs          []StructLog
116
	changedValues map[common.Address]Storage
117 118
	output        []byte
	err           error
119 120
}

121
// NewStructLogger returns a new logger
122 123
func NewStructLogger(cfg *LogConfig) *StructLogger {
	logger := &StructLogger{
124 125
		changedValues: make(map[common.Address]Storage),
	}
126 127 128 129
	if cfg != nil {
		logger.cfg = *cfg
	}
	return logger
130 131
}

132
// CaptureStart implements the Tracer interface to initialize the tracing operation.
133 134 135 136
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
	return nil
}

137
// CaptureState logs a new structured log message and pushes it out to the environment
138
//
139
// CaptureState also tracks SSTORE ops to track dirty values.
140
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
141 142
	// check if already accumulated the specified number of logs
	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
143
		return ErrTraceLimitReached
144 145
	}

146 147 148 149 150 151 152
	// initialise new changed values storage container for this contract
	// if not present.
	if l.changedValues[contract.Address()] == nil {
		l.changedValues[contract.Address()] = make(Storage)
	}

	// capture SSTORE opcodes and determine the changed value and store
153 154
	// it in the local storage container.
	if op == SSTORE && stack.len() >= 2 {
155 156 157 158 159 160
		var (
			value   = common.BigToHash(stack.data[stack.len()-2])
			address = common.BigToHash(stack.data[stack.len()-1])
		)
		l.changedValues[contract.Address()][address] = value
	}
161
	// Copy a snapshot of the current memory state to a new buffer
162 163 164 165 166
	var mem []byte
	if !l.cfg.DisableMemory {
		mem = make([]byte, len(memory.Data()))
		copy(mem, memory.Data())
	}
167
	// Copy a snapshot of the current stack state to a new buffer
168 169 170 171 172 173 174
	var stck []*big.Int
	if !l.cfg.DisableStack {
		stck = make([]*big.Int, len(stack.Data()))
		for i, item := range stack.Data() {
			stck[i] = new(big.Int).Set(item)
		}
	}
175
	// Copy a snapshot of the current storage to a new container
176 177
	var storage Storage
	if !l.cfg.DisableStorage {
178
		storage = l.changedValues[contract.Address()].Copy()
179
	}
180
	// create a new snapshot of the EVM.
181
	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
182 183

	l.logs = append(l.logs, log)
184
	return nil
185 186
}

187 188
// CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode.
189 190 191 192
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
	return nil
}

193
// CaptureEnd is called after the call finishes to finalize the tracing.
194
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
195 196
	l.output = output
	l.err = err
197 198 199 200 201 202
	if l.cfg.Debug {
		fmt.Printf("0x%x\n", output)
		if err != nil {
			fmt.Printf(" error: %v\n", err)
		}
	}
203 204 205
	return nil
}

206 207 208 209 210 211 212 213
// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.logs }

// Error returns the VM error captured by the trace.
func (l *StructLogger) Error() error { return l.err }

// Output returns the VM return value captured by the trace.
func (l *StructLogger) Output() []byte { return l.output }
214

215 216
// WriteTrace writes a formatted trace to the given writer
func WriteTrace(writer io.Writer, logs []StructLog) {
217
	for _, log := range logs {
218
		fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
219
		if log.Err != nil {
220
			fmt.Fprintf(writer, " ERROR: %v", log.Err)
221
		}
222
		fmt.Fprintln(writer)
obscuren's avatar
obscuren committed
223

224 225 226 227 228
		if len(log.Stack) > 0 {
			fmt.Fprintln(writer, "Stack:")
			for i := len(log.Stack) - 1; i >= 0; i-- {
				fmt.Fprintf(writer, "%08d  %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
			}
229
		}
230 231 232 233 234 235 236 237 238
		if len(log.Memory) > 0 {
			fmt.Fprintln(writer, "Memory:")
			fmt.Fprint(writer, hex.Dump(log.Memory))
		}
		if len(log.Storage) > 0 {
			fmt.Fprintln(writer, "Storage:")
			for h, item := range log.Storage {
				fmt.Fprintf(writer, "%x: %x\n", h, item)
			}
239 240 241 242 243 244 245 246 247 248 249 250
		}
		fmt.Fprintln(writer)
	}
}

// WriteLogs writes vm logs in a readable format to the given writer
func WriteLogs(writer io.Writer, logs []*types.Log) {
	for _, log := range logs {
		fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)

		for i, topic := range log.Topics {
			fmt.Fprintf(writer, "%08d  %x\n", i, topic)
251
		}
252 253 254

		fmt.Fprint(writer, hex.Dump(log.Data))
		fmt.Fprintln(writer)
255 256
	}
}