This repository has been archived by the owner on Jun 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
call_tracer.go
135 lines (123 loc) · 5.02 KB
/
call_tracer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package tracer
import (
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
)
type Frame struct {
Op vm.OpCode `json:"-"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Input hexutil.Bytes `json:"input"`
Output hexutil.Bytes `json:"output"`
Gas uint64 `json:"gas"`
Cost uint64 `json:"cost"`
Value *big.Int `json:"value"`
}
type CallTracer struct {
ops map[vm.OpCode]bool
stores map[common.Address]vm.Storage
frames []Frame
output []byte
err error
}
func NewCallTracer() *CallTracer {
return &CallTracer{
ops: map[vm.OpCode]bool{
vm.CREATE: true,
vm.CREATE2: true,
vm.SELFDESTRUCT: true,
vm.CALL: true,
vm.CALLCODE: true,
vm.DELEGATECALL: true,
vm.STATICCALL: true,
},
stores: make(map[common.Address]vm.Storage),
frames: make([]Frame, 0),
}
}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (tracer *CallTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
return nil
}
// CaptureState logs a new structured log message and pushes it out to the environment
func (tracer *CallTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error {
if op == vm.SSTORE {
if tracer.stores[contract.Address()] == nil {
tracer.stores[contract.Address()] = make(vm.Storage)
}
var (
value = common.BigToHash(stack.Back(1).ToBig())
address = common.BigToHash(stack.Back(0).ToBig())
)
tracer.stores[contract.Address()][address] = value
return nil
}
if !tracer.ops[op] {
return nil
}
frame := Frame{
Op: op,
From: contract.Address(),
Gas: gas,
Cost: cost,
}
switch op {
// create(v, p, n)
// create new contract with code mem[p…(p+n)) and send v wei and return the new address
// create2(v, p, n, s)
// create new contract with code mem[p…(p+n)) at address keccak256(0xff . this . s . keccak256(mem[p…(p+n))) and
// send v wei and return the new address, where 0xff is a 8 byte value, this is the current contract’s address
// as a 20 byte value and s is a big-endian 256-bit value
case vm.CREATE, vm.CREATE2:
frame.Value = new(big.Int).Set(stack.Back(0).ToBig())
frame.Input = memory.GetCopy(int64(stack.Back(1).Uint64()), int64(stack.Back(2).Uint64()))
// selfdestruct(a)
// end execution, destroy current contract and send funds to a
case vm.SELFDESTRUCT:
frame.To = common.BigToAddress(stack.Back(0).ToBig())
frame.Value = env.StateDB.GetBalance(contract.Address())
// call (g, a, v, in, insize, out, outsize)
// call contract at address a with input mem[in…(in+insize))
// providing g gas and v wei and output area mem[out…(out+outsize))
// returning 0 on error (eg. out of gas) and 1 on success
// callcode (g, a, v, in, insize, out, outsize)
// dentical to call but only use the code from a and stay
// in the context of the current contract otherwise
case vm.CALL, vm.CALLCODE:
frame.To = common.BigToAddress(stack.Back(1).ToBig())
frame.Value = new(big.Int).Set(stack.Back(2).ToBig())
frame.Input = memory.GetCopy(int64(stack.Back(3).Uint64()), int64(stack.Back(4).Uint64()))
frame.Output = memory.GetCopy(int64(stack.Back(5).Uint64()), int64(stack.Back(6).Uint64()))
// delegatecall (g, a, in, insize, out, outsize)
// identical to callcode but also keep caller and callvalue
// staticcall (g, a, in, insize, out, outsize)
// identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications
case vm.DELEGATECALL, vm.STATICCALL:
frame.To = common.BigToAddress(stack.Back(1).ToBig())
frame.Value = big.NewInt(0)
frame.Input = memory.GetCopy(int64(stack.Back(2).Uint64()), int64(stack.Back(3).Uint64()))
frame.Output = memory.GetCopy(int64(stack.Back(4).Uint64()), int64(stack.Back(5).Uint64()))
}
tracer.frames = append(tracer.frames, frame)
return nil
}
// CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode.
func (tracer *CallTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
return nil
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (tracer *CallTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
tracer.err = err
tracer.output = output
return nil
}
// Frames returns the captured call frames.
func (tracer *CallTracer) Frames() []Frame { return tracer.frames }
// Error returns the VM error captured by the trace.
func (tracer *CallTracer) Error() error { return tracer.err }
// Output returns the VM return value captured by the trace.
func (tracer *CallTracer) Output() hexutil.Bytes { return hexutil.Bytes(tracer.output) }