From 767b00b0b514771a663f3362dd0310fc28d40c25 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:12:57 +0100 Subject: [PATCH] t8ntool: add optional call frames to json logger (#29353) Adds a flag `--trace.callframes` to t8n which will log info when entering or exiting a call frame in addition to the execution steps. --------- Co-authored-by: Mario Vega --- cmd/evm/internal/t8ntool/flags.go | 4 ++ cmd/evm/internal/t8ntool/transition.go | 10 ++- cmd/evm/main.go | 1 + cmd/evm/t8n_test.go | 8 +++ cmd/evm/testdata/32/README.md | 1 + cmd/evm/testdata/32/alloc.json | 30 +++++++++ cmd/evm/testdata/32/env.json | 12 ++++ ...48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl | 61 +++++++++++++++++ cmd/evm/testdata/32/txs.json | 17 +++++ eth/tracers/logger/gen_callframe.go | 65 +++++++++++++++++++ eth/tracers/logger/logger_json.go | 64 +++++++++++++++++- 11 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 cmd/evm/testdata/32/README.md create mode 100644 cmd/evm/testdata/32/alloc.json create mode 100644 cmd/evm/testdata/32/env.json create mode 100644 cmd/evm/testdata/32/trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl create mode 100644 cmd/evm/testdata/32/txs.json create mode 100644 eth/tracers/logger/gen_callframe.go diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index c2eca8cc21..f2606c86d1 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -50,6 +50,10 @@ var ( Name: "trace.returndata", Usage: "Enable return data output in traces", } + TraceEnableCallFramesFlag = &cli.BoolFlag{ + Name: "trace.callframes", + Usage: "Enable call frames output in traces", + } OutputBasedir = &cli.StringFlag{ Name: "output.basedir", Usage: "Specifies where output files are placed. Will be created if it does not exist.", diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 5aa554e133..2b5eaa65aa 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" @@ -101,9 +102,14 @@ func Transition(ctx *cli.Context) error { if err != nil { return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } - logger := logger.NewJSONLogger(logConfig, traceFile) + var l *tracing.Hooks + if ctx.Bool(TraceEnableCallFramesFlag.Name) { + l = logger.NewJSONLoggerWithCallFrames(logConfig, traceFile) + } else { + l = logger.NewJSONLogger(logConfig, traceFile) + } tracer := &tracers.Tracer{ - Hooks: logger, + Hooks: l, // jsonLogger streams out result to file. GetResult: func() (json.RawMessage, error) { return nil, nil }, Stop: func(err error) {}, diff --git a/cmd/evm/main.go b/cmd/evm/main.go index c3e6a4af91..f9a2a075d0 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -152,6 +152,7 @@ var stateTransitionCommand = &cli.Command{ t8ntool.TraceEnableMemoryFlag, t8ntool.TraceDisableStackFlag, t8ntool.TraceEnableReturnDataFlag, + t8ntool.TraceEnableCallFramesFlag, t8ntool.OutputBasedir, t8ntool.OutputAllocFlag, t8ntool.OutputResultFlag, diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index 7e0bc36cbe..5a74491c3b 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -375,6 +375,14 @@ func TestT8nTracing(t *testing.T) { }`}, expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, }, + { + base: "./testdata/32", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Merge", "", + }, + extraArgs: []string{"--trace", "--trace.callframes"}, + expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"}, + }, } { args := []string{"t8n"} args = append(args, tc.input.get(tc.base)...) diff --git a/cmd/evm/testdata/32/README.md b/cmd/evm/testdata/32/README.md new file mode 100644 index 0000000000..508ac970dd --- /dev/null +++ b/cmd/evm/testdata/32/README.md @@ -0,0 +1 @@ +This test does some EVM execution, and can be used to test callframes emitted by the tracer when they are enabled. diff --git a/cmd/evm/testdata/32/alloc.json b/cmd/evm/testdata/32/alloc.json new file mode 100644 index 0000000000..0cd4493955 --- /dev/null +++ b/cmd/evm/testdata/32/alloc.json @@ -0,0 +1,30 @@ +{ + "0x8a0a19589531694250d570040a0c4b74576919b8": { + "nonce": "0x00", + "balance": "0x0de0b6b3a7640000", + "code": "0x600060006000600060007310000000000000000000000000000000000000015af1600155600060006000600060007310000000000000000000000000000000000000025af16002553d600060003e600051600355", + "storage": { + "0x01": "0x0100", + "0x02": "0x0100", + "0x03": "0x0100" + } + }, + "0x1000000000000000000000000000000000000001": { + "nonce": "0x00", + "balance": "0x29a2241af62c0000", + "code": "0x6103e8ff", + "storage": {} + }, + "0x1000000000000000000000000000000000000002": { + "nonce": "0x00", + "balance": "0x4563918244f40000", + "code": "0x600060006000600060647310000000000000000000000000000000000000015af1600f0160005260206000fd", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x6124fee993bc0000", + "code": "0x", + "storage": {} + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/32/env.json b/cmd/evm/testdata/32/env.json new file mode 100644 index 0000000000..4f0833e711 --- /dev/null +++ b/cmd/evm/testdata/32/env.json @@ -0,0 +1,12 @@ +{ + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentGasLimit": "71794957647893862", + "currentNumber": "1", + "currentTimestamp": "1000", + "currentRandom": "0", + "currentDifficulty": "0", + "blockHashes": {}, + "ommers": [], + "currentBaseFee": "7", + "parentUncleHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/cmd/evm/testdata/32/trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl b/cmd/evm/testdata/32/trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl new file mode 100644 index 0000000000..b6c5237baa --- /dev/null +++ b/cmd/evm/testdata/32/trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl @@ -0,0 +1,61 @@ +{"from":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b","to":"0x8a0a19589531694250d570040a0c4b74576919b8","gas":"0x74f18","value":"0x0","type":"CALL"} +{"pc":0,"op":96,"gas":"0x74f18","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0x74f15","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":96,"gas":"0x74f12","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":96,"gas":"0x74f0f","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":8,"op":96,"gas":"0x74f0c","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":10,"op":115,"gas":"0x74f09","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH20"} +{"pc":31,"op":90,"gas":"0x74f06","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x1000000000000000000000000000000000000001"],"depth":1,"refund":0,"opName":"GAS"} +{"pc":32,"op":241,"gas":"0x74f04","gasCost":"0x731f1","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x1000000000000000000000000000000000000001","0x74f04"],"depth":1,"refund":0,"opName":"CALL"} +{"from":"0x8a0a19589531694250d570040a0c4b74576919b8","to":"0x1000000000000000000000000000000000000001","gas":"0x727c9","value":"0x0","type":"CALL"} +{"pc":0,"op":97,"gas":"0x727c9","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":3,"op":255,"gas":"0x727c6","gasCost":"0x7f58","memSize":0,"stack":["0x3e8"],"depth":2,"refund":0,"opName":"SELFDESTRUCT"} +{"from":"0x1000000000000000000000000000000000000001","to":"0x00000000000000000000000000000000000003e8","gas":"0x0","value":"0x29a2241af62c0000","type":"SELFDESTRUCT"} +{"output":"","gasUsed":"0x0"} +{"output":"","gasUsed":"0x7f5b"} +{"pc":33,"op":96,"gas":"0x6c581","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":35,"op":85,"gas":"0x6c57e","gasCost":"0x1388","memSize":0,"stack":["0x1","0x1"],"depth":1,"refund":0,"opName":"SSTORE"} +{"pc":36,"op":96,"gas":"0x6b1f6","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":38,"op":96,"gas":"0x6b1f3","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":40,"op":96,"gas":"0x6b1f0","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":42,"op":96,"gas":"0x6b1ed","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":44,"op":96,"gas":"0x6b1ea","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":46,"op":115,"gas":"0x6b1e7","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH20"} +{"pc":67,"op":90,"gas":"0x6b1e4","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x1000000000000000000000000000000000000002"],"depth":1,"refund":0,"opName":"GAS"} +{"pc":68,"op":241,"gas":"0x6b1e2","gasCost":"0x69744","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x1000000000000000000000000000000000000002","0x6b1e2"],"depth":1,"refund":0,"opName":"CALL"} +{"from":"0x8a0a19589531694250d570040a0c4b74576919b8","to":"0x1000000000000000000000000000000000000002","gas":"0x68d1c","value":"0x0","type":"CALL"} +{"pc":0,"op":96,"gas":"0x68d1c","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0x68d19","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":96,"gas":"0x68d16","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":96,"gas":"0x68d13","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":8,"op":96,"gas":"0x68d10","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":10,"op":115,"gas":"0x68d0d","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x64"],"depth":2,"refund":0,"opName":"PUSH20"} +{"pc":31,"op":90,"gas":"0x68d0a","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x64","0x1000000000000000000000000000000000000001"],"depth":2,"refund":0,"opName":"GAS"} +{"pc":32,"op":241,"gas":"0x68d08","gasCost":"0x67363","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x64","0x1000000000000000000000000000000000000001","0x68d08"],"depth":2,"refund":0,"opName":"CALL"} +{"from":"0x1000000000000000000000000000000000000002","to":"0x1000000000000000000000000000000000000001","gas":"0x658d3","value":"0x64","type":"CALL"} +{"pc":0,"op":97,"gas":"0x658d3","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":3,"op":255,"gas":"0x658d0","gasCost":"0x1388","memSize":0,"stack":["0x3e8"],"depth":3,"refund":0,"opName":"SELFDESTRUCT"} +{"from":"0x1000000000000000000000000000000000000001","to":"0x00000000000000000000000000000000000003e8","gas":"0x0","value":"0x64","type":"SELFDESTRUCT"} +{"output":"","gasUsed":"0x0"} +{"output":"","gasUsed":"0x138b"} +{"pc":33,"op":96,"gas":"0x65eed","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":35,"op":1,"gas":"0x65eea","gasCost":"0x3","memSize":0,"stack":["0x1","0xf"],"depth":2,"refund":0,"opName":"ADD"} +{"pc":36,"op":96,"gas":"0x65ee7","gasCost":"0x3","memSize":0,"stack":["0x10"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":38,"op":82,"gas":"0x65ee4","gasCost":"0x6","memSize":0,"stack":["0x10","0x0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":39,"op":96,"gas":"0x65ede","gasCost":"0x3","memSize":32,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":41,"op":96,"gas":"0x65edb","gasCost":"0x3","memSize":32,"stack":["0x20"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":43,"op":253,"gas":"0x65ed8","gasCost":"0x0","memSize":32,"stack":["0x20","0x0"],"depth":2,"refund":0,"opName":"REVERT"} +{"pc":43,"op":253,"gas":"0x65ed8","gasCost":"0x0","memSize":32,"stack":[],"depth":2,"refund":0,"opName":"REVERT","error":"execution reverted"} +{"output":"0000000000000000000000000000000000000000000000000000000000000010","gasUsed":"0x2e44","error":"execution reverted"} +{"pc":69,"op":96,"gas":"0x67976","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":71,"op":85,"gas":"0x67973","gasCost":"0x1388","memSize":0,"stack":["0x0","0x2"],"depth":1,"refund":4800,"opName":"SSTORE"} +{"pc":72,"op":61,"gas":"0x665eb","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":4800,"opName":"RETURNDATASIZE"} +{"pc":73,"op":96,"gas":"0x665e9","gasCost":"0x3","memSize":0,"stack":["0x20"],"depth":1,"refund":4800,"opName":"PUSH1"} +{"pc":75,"op":96,"gas":"0x665e6","gasCost":"0x3","memSize":0,"stack":["0x20","0x0"],"depth":1,"refund":4800,"opName":"PUSH1"} +{"pc":77,"op":62,"gas":"0x665e3","gasCost":"0x9","memSize":0,"stack":["0x20","0x0","0x0"],"depth":1,"refund":4800,"opName":"RETURNDATACOPY"} +{"pc":78,"op":96,"gas":"0x665da","gasCost":"0x3","memSize":32,"stack":[],"depth":1,"refund":4800,"opName":"PUSH1"} +{"pc":80,"op":81,"gas":"0x665d7","gasCost":"0x3","memSize":32,"stack":["0x0"],"depth":1,"refund":4800,"opName":"MLOAD"} +{"pc":81,"op":96,"gas":"0x665d4","gasCost":"0x3","memSize":32,"stack":["0x10"],"depth":1,"refund":4800,"opName":"PUSH1"} +{"pc":83,"op":85,"gas":"0x665d1","gasCost":"0x1388","memSize":32,"stack":["0x10","0x3"],"depth":1,"refund":4800,"opName":"SSTORE"} +{"pc":84,"op":0,"gas":"0x65249","gasCost":"0x0","memSize":32,"stack":[],"depth":1,"refund":4800,"opName":"STOP"} +{"output":"","gasUsed":"0xfccf"} diff --git a/cmd/evm/testdata/32/txs.json b/cmd/evm/testdata/32/txs.json new file mode 100644 index 0000000000..0530fd60e6 --- /dev/null +++ b/cmd/evm/testdata/32/txs.json @@ -0,0 +1,17 @@ +[ + { + "type": "0x0", + "chainId": "0x0", + "nonce": "0x0", + "gasPrice": "0xa", + "gas": "0x7a120", + "to": "0x8a0a19589531694250d570040a0c4b74576919b8", + "value": "0x0", + "input": "0x", + "v": "0x1c", + "r": "0x9a207ad45b7fc2aa5f8e72a30487f2b0bc489778e6d022f19036efdf2a922a17", + "s": "0x640d4da05078b5a4aa561f1b4d58176ea828bfa0f88d27d14459c1d789e1a1eb", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + } +] \ No newline at end of file diff --git a/eth/tracers/logger/gen_callframe.go b/eth/tracers/logger/gen_callframe.go new file mode 100644 index 0000000000..b7b2cc2881 --- /dev/null +++ b/eth/tracers/logger/gen_callframe.go @@ -0,0 +1,65 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package logger + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*callFrameMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (c callFrame) MarshalJSON() ([]byte, error) { + type callFrame struct { + From common.Address `json:"from"` + To common.Address `json:"to"` + Input hexutil.Bytes `json:"input,omitempty"` + Gas math.HexOrDecimal64 `json:"gas"` + Value *hexutil.Big `json:"value"` + Type string `json:"type"` + } + var enc callFrame + enc.From = c.From + enc.To = c.To + enc.Input = c.Input + enc.Gas = math.HexOrDecimal64(c.Gas) + enc.Value = (*hexutil.Big)(c.Value) + enc.Type = c.Type() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (c *callFrame) UnmarshalJSON(input []byte) error { + type callFrame struct { + From *common.Address `json:"from"` + To *common.Address `json:"to"` + Input *hexutil.Bytes `json:"input,omitempty"` + Gas *math.HexOrDecimal64 `json:"gas"` + Value *hexutil.Big `json:"value"` + } + var dec callFrame + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.From != nil { + c.From = *dec.From + } + if dec.To != nil { + c.To = *dec.To + } + if dec.Input != nil { + c.Input = *dec.Input + } + if dec.Gas != nil { + c.Gas = uint64(*dec.Gas) + } + if dec.Value != nil { + c.Value = (*big.Int)(dec.Value) + } + return nil +} diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 6fac2d1159..d66b8c4b8a 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -19,14 +19,41 @@ package logger import ( "encoding/json" "io" + "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) +//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe.go + +// overrides for gencodec +type callFrameMarshaling struct { + Input hexutil.Bytes + Gas math.HexOrDecimal64 + Value *hexutil.Big + Type string `json:"type"` // adds call to Type() in MarshalJSON +} + +// callFrame is emitted every call frame entered. +type callFrame struct { + op vm.OpCode + From common.Address `json:"from"` + To common.Address `json:"to"` + Input []byte `json:"input,omitempty"` + Gas uint64 `json:"gas"` + Value *big.Int `json:"value"` +} + +// Type formats the call type in a human-readable format. +func (c *callFrame) Type() string { + return c.op.String() +} + type jsonLogger struct { encoder *json.Encoder cfg *Config @@ -48,6 +75,22 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks { } } +// NewJSONLoggerWithCallFrames creates a new EVM tracer that prints execution steps as JSON objects +// into the provided stream. It also includes call frames in the output. +func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) *tracing.Hooks { + l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg} + if l.cfg == nil { + l.cfg = &Config{} + } + return &tracing.Hooks{ + OnTxStart: l.OnTxStart, + OnEnter: l.OnEnter, + OnExit: l.OnExit, + OnOpcode: l.OnOpcode, + OnFault: l.OnFault, + } +} + func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { // TODO: Add rData to this interface as well l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err) @@ -79,10 +122,29 @@ func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracin l.encoder.Encode(log) } -func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { +// OnEnter is not enabled by default. +func (l *jsonLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + frame := callFrame{ + op: vm.OpCode(typ), + From: from, + To: to, + Gas: gas, + Value: value, + } + if l.cfg.EnableMemory { + frame.Input = input + } + l.encoder.Encode(frame) +} + +func (l *jsonLogger) OnEnd(depth int, output []byte, gasUsed uint64, err error, reverted bool) { if depth > 0 { return } + l.OnExit(depth, output, gasUsed, err, false) +} + +func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { type endLog struct { Output string `json:"output"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`