Skip to content

Commit 07087f8

Browse files
committed
cmd/evm: better design on outputs
1 parent 9f54050 commit 07087f8

11 files changed

+167
-113
lines changed

Diff for: cmd/evm/internal/t8ntool/file_tracer.go

+78-93
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,18 @@
1717
package t8ntool
1818

1919
import (
20-
"github.com/ethereum/go-ethereum/common"
21-
"github.com/ethereum/go-ethereum/core/tracing"
22-
"github.com/ethereum/go-ethereum/core/types"
23-
"github.com/ethereum/go-ethereum/eth/tracers"
24-
"github.com/ethereum/go-ethereum/eth/tracers/logger"
25-
"github.com/ethereum/go-ethereum/log"
26-
"github.com/ethereum/go-ethereum/params"
27-
2820
"encoding/json"
2921
"fmt"
3022
"io"
3123
"math/big"
3224
"os"
3325
"path/filepath"
26+
27+
"github.com/ethereum/go-ethereum/common"
28+
"github.com/ethereum/go-ethereum/core/tracing"
29+
"github.com/ethereum/go-ethereum/core/types"
30+
"github.com/ethereum/go-ethereum/eth/tracers"
31+
"github.com/ethereum/go-ethereum/log"
3432
)
3533

3634
// fileWritingTracer wraps either a tracer or a logger. On tx start,
@@ -41,129 +39,116 @@ type fileWritingTracer struct {
4139
inner *tracing.Hooks
4240
destination io.WriteCloser
4341
baseDir string
44-
45-
// for json-tracing
46-
logConfig *logger.Config
42+
// suffix is the suffix to use when creating files
43+
suffix string
4744

4845
// for custom tracing
49-
tracerName string
50-
tracerConf json.RawMessage
51-
chainConfig *params.ChainConfig
52-
getResult func() (json.RawMessage, error)
46+
getResult func() (json.RawMessage, error)
5347
}
5448

55-
// jsonToFile creates hooks which uses an underlying jsonlogger, and writes the
56-
// jsonl-delimited output to a file, one per tx.
57-
func jsonToFile(baseDir string, logConfig *logger.Config, callFrames bool) *tracing.Hooks {
58-
t := &fileWritingTracer{
59-
baseDir: baseDir,
60-
logConfig: logConfig,
49+
func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
50+
if l.destination != nil {
51+
return l.destination.Write(p)
6152
}
62-
hooks := t.hooks()
63-
if !callFrames {
64-
hooks.OnEnter = nil
65-
}
66-
return hooks
53+
log.Warn("Tracer wrote to non-existing output")
54+
// It is tempting to return an error here, however, the json encoder
55+
// will no retry writing to an io.Writer once it has returned an error once.
56+
// Therefore, we must squash the error.
57+
return n, nil
6758
}
6859

69-
// tracerToFile creates hooks which uses an underlying tracer, and writes the
70-
// json-result to file, one per tx.
71-
func tracerToFile(baseDir, tracerName string, traceConfig json.RawMessage, chainConfig *params.ChainConfig) *tracing.Hooks {
60+
// newFileWriter creates a set of hooks which wraps inner hooks (typically a logger),
61+
// and writes the output to a file, one file per transaction.
62+
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracing.Hooks {
7263
t := &fileWritingTracer{
73-
baseDir: baseDir,
74-
tracerName: tracerName,
75-
chainConfig: chainConfig,
64+
baseDir: baseDir,
65+
suffix: "jsonl",
7666
}
67+
t.inner = innerFn(t) // instantiate the inner tracer
7768
return t.hooks()
7869
}
7970

80-
func (l *fileWritingTracer) hooks() *tracing.Hooks {
81-
hooks := &tracing.Hooks{
82-
OnTxStart: l.OnTxStartJSONL,
83-
OnTxEnd: l.OnTxEnd,
84-
// intentional no-op: we instantiate the l.inner on tx start, which has
85-
// not yet happened at this point
86-
//OnSystemCallStart: func() {},
87-
OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
88-
if l.inner != nil && l.inner.OnEnter != nil {
89-
l.inner.OnEnter(depth, typ, from, to, input, gas, value)
90-
}
91-
},
92-
OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
93-
if l.inner != nil && l.inner.OnExit != nil {
94-
l.inner.OnExit(depth, output, gasUsed, err, reverted)
95-
}
96-
},
97-
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
98-
if l.inner.OnOpcode != nil {
99-
l.inner.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
100-
}
101-
},
102-
OnFault: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
103-
if l.inner != nil && l.inner.OnFault != nil {
104-
l.inner.OnFault(pc, op, gas, cost, scope, depth, err)
105-
}
106-
},
107-
}
108-
if len(l.tracerName) > 0 { // a custom tracer
109-
hooks.OnTxStart = l.OnTxStartJSON
110-
}
111-
return hooks
112-
}
113-
114-
// OnTxStartJSONL is the OnTxStart-handler for jsonl logger.
115-
func (l *fileWritingTracer) OnTxStartJSONL(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
116-
// Open a new file,
117-
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.jsonl", l.txIndex, tx.Hash().String()))
118-
traceFile, err := os.Create(fname)
119-
if err != nil {
120-
log.Warn("Failed creating trace-file", "err", err)
121-
}
122-
log.Debug("Created tracing-file", "path", fname)
123-
l.destination = traceFile
124-
l.inner = logger.NewJSONLoggerWithCallFrames(l.logConfig, traceFile)
125-
if l.inner.OnTxStart != nil {
126-
l.inner.OnTxStart(env, tx, from)
71+
// newResultWriter creates a set of hooks wraps and invokes an underlying tracer,
72+
// and writes the result (getResult-output) to file, one per transaction.
73+
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracing.Hooks {
74+
t := &fileWritingTracer{
75+
baseDir: baseDir,
76+
getResult: tracer.GetResult,
77+
inner: tracer.Hooks,
78+
suffix: "json",
12779
}
80+
return t.hooks()
12881
}
12982

130-
// OnTxStartJSONL is the OnTxStart-handler for custom tracer.
131-
func (l *fileWritingTracer) OnTxStartJSON(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
83+
// OnTxStart creates a new output-file specific for this transaction, and invokes
84+
// the inner OnTxStart handler.
85+
func (l *fileWritingTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
13286
// Open a new file,
133-
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.json", l.txIndex, tx.Hash().String()))
87+
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.%v", l.txIndex, tx.Hash().String(), l.suffix))
13488
traceFile, err := os.Create(fname)
13589
if err != nil {
13690
log.Warn("Failed creating trace-file", "err", err)
13791
}
138-
fmt.Printf("Created tracing-file %v\n", fname)
13992
log.Info("Created tracing-file", "path", fname)
14093
l.destination = traceFile
141-
inner, err := tracers.DefaultDirectory.New(l.tracerName, nil, l.tracerConf, l.chainConfig)
142-
if err != nil {
143-
log.Warn("Failed instantiating tracer", "err", err)
144-
return
145-
}
146-
l.getResult = inner.GetResult
147-
l.inner = inner.Hooks
94+
14895
if l.inner.OnTxStart != nil {
14996
l.inner.OnTxStart(env, tx, from)
15097
}
15198
}
15299

100+
// OnTxEnd writes result (if getResult exist), closes any currently open output-file,
101+
// and invokes the inner OnTxEnd handler.
153102
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
154103
if l.inner.OnTxEnd != nil {
155104
l.inner.OnTxEnd(receipt, err)
156105
}
157-
if l.getResult != nil {
106+
if l.getResult != nil && l.destination != nil {
158107
if result, err := l.getResult(); result != nil {
159108
json.NewEncoder(l.destination).Encode(result)
160109
} else {
161110
log.Warn("Error obtaining tracer result", "err", err)
162111
}
163-
}
164-
if l.destination != nil { // Close old file
165112
l.destination.Close()
166113
l.destination = nil
167114
}
168115
l.txIndex++
169116
}
117+
118+
func (l *fileWritingTracer) hooks() *tracing.Hooks {
119+
hooks := &tracing.Hooks{
120+
OnTxStart: l.OnTxStart,
121+
OnTxEnd: l.OnTxEnd,
122+
OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
123+
if l.inner != nil && l.inner.OnEnter != nil {
124+
l.inner.OnEnter(depth, typ, from, to, input, gas, value)
125+
}
126+
},
127+
OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
128+
if l.inner != nil && l.inner.OnExit != nil {
129+
l.inner.OnExit(depth, output, gasUsed, err, reverted)
130+
}
131+
},
132+
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
133+
if l.inner.OnOpcode != nil {
134+
l.inner.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
135+
}
136+
},
137+
OnFault: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
138+
if l.inner != nil && l.inner.OnFault != nil {
139+
l.inner.OnFault(pc, op, gas, cost, scope, depth, err)
140+
}
141+
},
142+
OnSystemCallStart: func() {
143+
if l.inner != nil && l.inner.OnSystemCallStart != nil {
144+
l.inner.OnSystemCallStart()
145+
}
146+
},
147+
OnSystemCallEnd: func() {
148+
if l.inner != nil && l.inner.OnSystemCallEnd != nil {
149+
l.inner.OnSystemCallEnd()
150+
}
151+
},
152+
}
153+
return hooks
154+
}

Diff for: cmd/evm/internal/t8ntool/transition.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"fmt"
23+
"io"
2324
"math/big"
2425
"os"
2526
"path/filepath"
@@ -28,8 +29,10 @@ import (
2829
"github.com/ethereum/go-ethereum/common/hexutil"
2930
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
3031
"github.com/ethereum/go-ethereum/core/state"
32+
"github.com/ethereum/go-ethereum/core/tracing"
3133
"github.com/ethereum/go-ethereum/core/types"
3234
"github.com/ethereum/go-ethereum/core/vm"
35+
"github.com/ethereum/go-ethereum/eth/tracers"
3336
"github.com/ethereum/go-ethereum/eth/tracers/logger"
3437
"github.com/ethereum/go-ethereum/log"
3538
"github.com/ethereum/go-ethereum/params"
@@ -150,14 +153,27 @@ func Transition(ctx *cli.Context) error {
150153
// Configure tracer
151154
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
152155
config := json.RawMessage(ctx.String(TraceTracerConfigFlag.Name))
153-
vmConfig.Tracer = tracerToFile(baseDir, ctx.String(TraceTracerFlag.Name), config, chainConfig)
156+
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
157+
nil, config, chainConfig)
158+
if err != nil {
159+
return NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %v", err))
160+
}
161+
vmConfig.Tracer = newResultWriter(baseDir, tracer)
154162
} else if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
155163
logConfig := &logger.Config{
156164
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
157165
EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name),
158166
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
159167
}
160-
vmConfig.Tracer = jsonToFile(baseDir, logConfig, ctx.Bool(TraceEnableCallFramesFlag.Name))
168+
if ctx.Bool(TraceEnableCallFramesFlag.Name) {
169+
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
170+
return logger.NewJSONLoggerWithCallFrames(logConfig, out)
171+
})
172+
} else {
173+
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
174+
return logger.NewJSONLogger(logConfig, out)
175+
})
176+
}
161177
}
162178
// Run the test and aggregate the result
163179
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name))

Diff for: cmd/evm/t8n_test.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -358,22 +358,27 @@ func TestT8nTracing(t *testing.T) {
358358
input: t8nInput{
359359
"alloc.json", "txs.json", "env.json", "Cancun", "",
360360
},
361-
extraArgs: []string{"--trace"},
362-
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"},
361+
extraArgs: []string{"--trace"},
362+
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl",
363+
"trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.jsonl",
364+
"trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.jsonl"},
363365
},
364366
{
365367
base: "./testdata/31",
366368
input: t8nInput{
367369
"alloc.json", "txs.json", "env.json", "Cancun", "",
368370
},
369371
extraArgs: []string{"--trace.tracer", `
370-
{
371-
result: function(){
372-
return "hello world"
372+
{ count: 0,
373+
result: function(){
374+
this.count = this.count + 1;
375+
return "hello world " + this.count
373376
},
374377
fault: function(){}
375378
}`},
376-
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"},
379+
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json",
380+
"trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.json",
381+
"trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.json"},
377382
},
378383
{
379384
base: "./testdata/32",

Diff for: cmd/evm/testdata/31/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
11
This test does some EVM execution, and can be used to test the tracers and trace-outputs.
2+
This test should yield three output-traces, in separate files
3+
4+
For example:
5+
```
6+
[user@work evm]$ go run . t8n --input.alloc ./testdata/31/alloc.json --input.txs ./testdata/31/txs.json --input.env ./testdata/31/env.json --state.fork Cancun --output.basedir /tmp --trace
7+
INFO [12-06|09:53:32.123] Created tracing-file path=/tmp/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl
8+
INFO [12-06|09:53:32.124] Created tracing-file path=/tmp/trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.jsonl
9+
INFO [12-06|09:53:32.125] Created tracing-file path=/tmp/trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.jsonl
10+
```
11+
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"hello world"
1+
"hello world 1"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"hello world 2"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
2+
{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
3+
{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
4+
{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
5+
{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"}
6+
{"output":"","gasUsed":"0xc"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"hello world 3"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
2+
{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
3+
{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
4+
{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
5+
{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"}
6+
{"output":"","gasUsed":"0xc"}

Diff for: cmd/evm/testdata/31/txs.json

+24
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,29 @@
1010
"r" : "0x0",
1111
"s" : "0x0",
1212
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
13+
},
14+
{
15+
"gas": "0x186a0",
16+
"gasPrice": "0x600",
17+
"input": "0x",
18+
"nonce": "0x1",
19+
"to": "0x1111111111111111111111111111111111111111",
20+
"value": "0x1",
21+
"v" : "0x0",
22+
"r" : "0x0",
23+
"s" : "0x0",
24+
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
25+
},
26+
{
27+
"gas": "0x186a0",
28+
"gasPrice": "0x600",
29+
"input": "0x",
30+
"nonce": "0x2",
31+
"to": "0x1111111111111111111111111111111111111111",
32+
"value": "0x1",
33+
"v" : "0x0",
34+
"r" : "0x0",
35+
"s" : "0x0",
36+
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
1337
}
1438
]

0 commit comments

Comments
 (0)