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