Skip to content

Commit 75f8473

Browse files
holimanfjl
andauthored
cmd/evm: consolidate evm output switches (#30849)
This PR attempts to clean up some ambiguities and quirks from recent changes to evm flag handling. This PR mainly focuses on `evm run` subcommand, to use the same flags for configuring tracing/output options as `statetest/blocktest` does. Additionally, it adds quite a lot of tests for expected outputs of the various subcommands, to avoid accidental changes. --------- Co-authored-by: Felix Lange <[email protected]>
1 parent a91dcf3 commit 75f8473

24 files changed

+2188
-122
lines changed

cmd/evm/internal/t8ntool/transition.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ func Transition(ctx *cli.Context) error {
9797
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
9898
EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name),
9999
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
100-
Debug: true,
101100
}
102101
getTracer = func(txIndex int, txHash common.Hash, _ *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
103102
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))

cmd/evm/main.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,20 @@ func tracerFromFlags(ctx *cli.Context) *tracing.Hooks {
239239
EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
240240
}
241241
switch {
242-
case ctx.Bool(TraceFlag.Name) && ctx.String(TraceFormatFlag.Name) == "struct":
243-
return logger.NewStreamingStructLogger(config, os.Stderr).Hooks()
244-
case ctx.Bool(TraceFlag.Name) && ctx.String(TraceFormatFlag.Name) == "json":
245-
return logger.NewJSONLogger(config, os.Stderr)
242+
case ctx.Bool(TraceFlag.Name):
243+
switch format := ctx.String(TraceFormatFlag.Name); format {
244+
case "struct":
245+
return logger.NewStreamingStructLogger(config, os.Stderr).Hooks()
246+
case "json":
247+
return logger.NewJSONLogger(config, os.Stderr)
248+
case "md", "markdown":
249+
return logger.NewMarkdownLogger(config, os.Stderr).Hooks()
250+
default:
251+
fmt.Fprintf(os.Stderr, "unknown trace format: %q\n", format)
252+
os.Exit(1)
253+
return nil
254+
}
255+
// Deprecated ways of configuring tracing.
246256
case ctx.Bool(MachineFlag.Name):
247257
return logger.NewJSONLogger(config, os.Stderr)
248258
case ctx.Bool(DebugFlag.Name):

cmd/evm/runner.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import (
3939
"github.com/ethereum/go-ethereum/core/types"
4040
"github.com/ethereum/go-ethereum/core/vm"
4141
"github.com/ethereum/go-ethereum/core/vm/runtime"
42-
"github.com/ethereum/go-ethereum/eth/tracers/logger"
4342
"github.com/ethereum/go-ethereum/internal/flags"
4443
"github.com/ethereum/go-ethereum/params"
4544
"github.com/ethereum/go-ethereum/triedb"
@@ -66,6 +65,7 @@ var runCommand = &cli.Command{
6665
SenderFlag,
6766
ValueFlag,
6867
StatDumpFlag,
68+
DumpFlag,
6969
}, traceFlags),
7070
}
7171

@@ -197,14 +197,6 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
197197
}
198198

199199
func runCmd(ctx *cli.Context) error {
200-
logconfig := &logger.Config{
201-
EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
202-
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
203-
DisableStorage: ctx.Bool(TraceDisableStorageFlag.Name),
204-
EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
205-
Debug: ctx.Bool(DebugFlag.Name),
206-
}
207-
208200
var (
209201
tracer *tracing.Hooks
210202
prestate *state.StateDB
@@ -215,12 +207,7 @@ func runCmd(ctx *cli.Context) error {
215207
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
216208
blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests
217209
)
218-
if ctx.Bool(MachineFlag.Name) {
219-
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
220-
} else if ctx.Bool(DebugFlag.Name) {
221-
tracer = logger.NewStreamingStructLogger(logconfig, os.Stderr).Hooks()
222-
}
223-
210+
tracer = tracerFromFlags(ctx)
224211
initialGas := ctx.Uint64(GasFlag.Name)
225212
genesisConfig := new(core.Genesis)
226213
genesisConfig.GasLimit = initialGas

cmd/evm/t8n_test.go

Lines changed: 172 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -341,98 +341,6 @@ func lineIterator(path string) func() (string, error) {
341341
}
342342
}
343343

344-
// TestT8nTracing is a test that checks the tracing-output from t8n.
345-
func TestT8nTracing(t *testing.T) {
346-
t.Parallel()
347-
tt := new(testT8n)
348-
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
349-
for i, tc := range []struct {
350-
base string
351-
input t8nInput
352-
expExitCode int
353-
extraArgs []string
354-
expectedTraces []string
355-
}{
356-
{
357-
base: "./testdata/31",
358-
input: t8nInput{
359-
"alloc.json", "txs.json", "env.json", "Cancun", "",
360-
},
361-
extraArgs: []string{"--trace"},
362-
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"},
363-
},
364-
{
365-
base: "./testdata/31",
366-
input: t8nInput{
367-
"alloc.json", "txs.json", "env.json", "Cancun", "",
368-
},
369-
extraArgs: []string{"--trace.tracer", `
370-
{
371-
result: function(){
372-
return "hello world"
373-
},
374-
fault: function(){}
375-
}`},
376-
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"},
377-
},
378-
{
379-
base: "./testdata/32",
380-
input: t8nInput{
381-
"alloc.json", "txs.json", "env.json", "Paris", "",
382-
},
383-
extraArgs: []string{"--trace", "--trace.callframes"},
384-
expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"},
385-
},
386-
} {
387-
args := []string{"t8n"}
388-
args = append(args, tc.input.get(tc.base)...)
389-
// Place the output somewhere we can find it
390-
outdir := t.TempDir()
391-
args = append(args, "--output.basedir", outdir)
392-
args = append(args, tc.extraArgs...)
393-
394-
var qArgs []string // quoted args for debugging purposes
395-
for _, arg := range args {
396-
if len(arg) == 0 {
397-
qArgs = append(qArgs, `""`)
398-
} else {
399-
qArgs = append(qArgs, arg)
400-
}
401-
}
402-
tt.Logf("args: %v\n", strings.Join(qArgs, " "))
403-
tt.Run("evm-test", args...)
404-
t.Log(string(tt.Output()))
405-
406-
// Compare the expected traces
407-
for _, traceFile := range tc.expectedTraces {
408-
haveFn := lineIterator(filepath.Join(outdir, traceFile))
409-
wantFn := lineIterator(filepath.Join(tc.base, traceFile))
410-
411-
for line := 0; ; line++ {
412-
want, wErr := wantFn()
413-
have, hErr := haveFn()
414-
if want != have {
415-
t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n",
416-
i, traceFile, line, want, have)
417-
}
418-
if wErr != nil && hErr != nil {
419-
break
420-
}
421-
if wErr != nil {
422-
t.Fatal(wErr)
423-
}
424-
if hErr != nil {
425-
t.Fatal(hErr)
426-
}
427-
t.Logf("%v\n", want)
428-
}
429-
}
430-
if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
431-
t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
432-
}
433-
}
434-
}
435-
436344
type t9nInput struct {
437345
inTxs string
438346
stFork string
@@ -672,6 +580,88 @@ func TestB11r(t *testing.T) {
672580
}
673581
}
674582

583+
func TestEvmRun(t *testing.T) {
584+
t.Parallel()
585+
tt := cmdtest.NewTestCmd(t, nil)
586+
for i, tc := range []struct {
587+
input []string
588+
wantStdout string
589+
wantStderr string
590+
}{
591+
{ // json tracing
592+
input: []string{"run", "--trace", "--trace.format=json", "6040"},
593+
wantStdout: "./testdata/evmrun/1.out.1.txt",
594+
wantStderr: "./testdata/evmrun/1.out.2.txt",
595+
},
596+
{ // Same as above, using the deprecated --json
597+
input: []string{"run", "--json", "6040"},
598+
wantStdout: "./testdata/evmrun/1.out.1.txt",
599+
wantStderr: "./testdata/evmrun/1.out.2.txt",
600+
},
601+
{ // default tracing (struct)
602+
input: []string{"run", "--trace", "0x6040"},
603+
wantStdout: "./testdata/evmrun/2.out.1.txt",
604+
wantStderr: "./testdata/evmrun/2.out.2.txt",
605+
},
606+
{ // default tracing (struct), plus alloc-dump
607+
input: []string{"run", "--trace", "--dump", "0x6040"},
608+
wantStdout: "./testdata/evmrun/3.out.1.txt",
609+
//wantStderr: "./testdata/evmrun/3.out.2.txt",
610+
},
611+
{ // json-tracing, plus alloc-dump
612+
input: []string{"run", "--trace", "--trace.format=json", "--dump", "0x6040"},
613+
wantStdout: "./testdata/evmrun/4.out.1.txt",
614+
//wantStderr: "./testdata/evmrun/4.out.2.txt",
615+
},
616+
{ // md-tracing
617+
input: []string{"run", "--trace", "--trace.format=md", "0x6040"},
618+
wantStdout: "./testdata/evmrun/5.out.1.txt",
619+
wantStderr: "./testdata/evmrun/5.out.2.txt",
620+
},
621+
{ // statetest subcommand
622+
input: []string{"statetest", "./testdata/statetest.json"},
623+
wantStdout: "./testdata/evmrun/6.out.1.txt",
624+
wantStderr: "./testdata/evmrun/6.out.2.txt",
625+
},
626+
{ // statetest subcommand with output
627+
input: []string{"statetest", "--trace", "--trace.format=md", "./testdata/statetest.json"},
628+
wantStdout: "./testdata/evmrun/7.out.1.txt",
629+
wantStderr: "./testdata/evmrun/7.out.2.txt",
630+
},
631+
{ // statetest subcommand with output
632+
input: []string{"statetest", "--trace", "--trace.format=json", "./testdata/statetest.json"},
633+
wantStdout: "./testdata/evmrun/8.out.1.txt",
634+
wantStderr: "./testdata/evmrun/8.out.2.txt",
635+
},
636+
} {
637+
tt.Logf("args: go run ./cmd/evm %v\n", strings.Join(tc.input, " "))
638+
tt.Run("evm-test", tc.input...)
639+
640+
haveStdOut := tt.Output()
641+
tt.WaitExit()
642+
haveStdErr := tt.StderrText()
643+
644+
if have, wantFile := haveStdOut, tc.wantStdout; wantFile != "" {
645+
want, err := os.ReadFile(wantFile)
646+
if err != nil {
647+
t.Fatalf("test %d: could not read expected output: %v", i, err)
648+
}
649+
if string(haveStdOut) != string(want) {
650+
t.Fatalf("test %d, output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
651+
}
652+
}
653+
if have, wantFile := haveStdErr, tc.wantStderr; wantFile != "" {
654+
want, err := os.ReadFile(wantFile)
655+
if err != nil {
656+
t.Fatalf("test %d: could not read expected output: %v", i, err)
657+
}
658+
if have != string(want) {
659+
t.Fatalf("test %d, output wrong\nhave %q\nwant %q\n", i, have, string(want))
660+
}
661+
}
662+
}
663+
}
664+
675665
// cmpJson compares the JSON in two byte slices.
676666
func cmpJson(a, b []byte) (bool, error) {
677667
var j, j2 interface{}
@@ -683,3 +673,93 @@ func cmpJson(a, b []byte) (bool, error) {
683673
}
684674
return reflect.DeepEqual(j2, j), nil
685675
}
676+
677+
// TestEVMTracing is a test that checks the tracing-output from evm.
678+
func TestEVMTracing(t *testing.T) {
679+
t.Parallel()
680+
tt := cmdtest.NewTestCmd(t, nil)
681+
for i, tc := range []struct {
682+
base string
683+
input []string
684+
expectedTraces []string
685+
}{
686+
{
687+
base: "./testdata/31",
688+
input: []string{"t8n",
689+
"--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json",
690+
"--input.env=./testdata/31/env.json", "--state.fork=Cancun",
691+
"--trace",
692+
},
693+
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"},
694+
},
695+
{
696+
base: "./testdata/31",
697+
input: []string{"t8n",
698+
"--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json",
699+
"--input.env=./testdata/31/env.json", "--state.fork=Cancun",
700+
"--trace.tracer", `
701+
{
702+
result: function(){
703+
return "hello world"
704+
},
705+
fault: function(){}
706+
}`,
707+
},
708+
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"},
709+
},
710+
{
711+
base: "./testdata/32",
712+
input: []string{"t8n",
713+
"--input.alloc=./testdata/32/alloc.json", "--input.txs=./testdata/32/txs.json",
714+
"--input.env=./testdata/32/env.json", "--state.fork=Paris",
715+
"--trace", "--trace.callframes",
716+
},
717+
expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"},
718+
},
719+
// TODO, make it possible to run tracers on statetests, e.g:
720+
//{
721+
// base: "./testdata/31",
722+
// input: []string{"statetest", "--trace", "--trace.tracer", `{
723+
// result: function(){
724+
// return "hello world"
725+
// },
726+
// fault: function(){}
727+
//}`, "./testdata/statetest.json"},
728+
// expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"},
729+
// },
730+
} {
731+
// Place the output somewhere we can find it
732+
outdir := t.TempDir()
733+
args := append(tc.input, "--output.basedir", outdir)
734+
735+
tt.Run("evm-test", args...)
736+
tt.Logf("args: go run ./cmd/evm %v\n", args)
737+
tt.WaitExit()
738+
//t.Log(string(tt.Output()))
739+
740+
// Compare the expected traces
741+
for _, traceFile := range tc.expectedTraces {
742+
haveFn := lineIterator(filepath.Join(outdir, traceFile))
743+
wantFn := lineIterator(filepath.Join(tc.base, traceFile))
744+
745+
for line := 0; ; line++ {
746+
want, wErr := wantFn()
747+
have, hErr := haveFn()
748+
if want != have {
749+
t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n",
750+
i, traceFile, line, want, have)
751+
}
752+
if wErr != nil && hErr != nil {
753+
break
754+
}
755+
if wErr != nil {
756+
t.Fatal(wErr)
757+
}
758+
if hErr != nil {
759+
t.Fatal(hErr)
760+
}
761+
//t.Logf("%v\n", want)
762+
}
763+
}
764+
}
765+
}

cmd/evm/testdata/evmrun/1.out.1.txt

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
2+
{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"STOP"}
3+
{"output":"","gasUsed":"0x3"}

cmd/evm/testdata/evmrun/2.out.1.txt

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
PUSH1 pc=00000000 gas=10000000000 cost=3
2+
3+
STOP pc=00000002 gas=9999999997 cost=0
4+
Stack:
5+
00000000 0x40
6+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9",
3+
"accounts": {
4+
"pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": {
5+
"balance": "0",
6+
"nonce": 0,
7+
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
8+
"codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6",
9+
"code": "0x6040",
10+
"key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142"
11+
}
12+
}
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
PUSH1 pc=00000000 gas=10000000000 cost=3
2+
3+
STOP pc=00000002 gas=9999999997 cost=0
4+
Stack:
5+
00000000 0x40
6+
7+
INFO [12-03|10:37:15.827] Trie dumping started root=b44448..bf69f9
8+
WARN [12-03|10:37:15.827] Dump incomplete due to missing preimages missing=1
9+
INFO [12-03|10:37:15.827] Trie dumping complete accounts=1 elapsed="163.513µs"

0 commit comments

Comments
 (0)