cmd/evm: make batched state-test execution possible (#27318)

implements the ability to run several state-tests in one instance. By not providing a statetest path to the `evm statetest` command, the path(s) will instead be read from `stdin`.
This commit is contained in:
Martin Holst Swende 2023-05-23 06:23:17 -04:00 committed by GitHub
parent bfded65ed8
commit 1a18283e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 28 additions and 30 deletions

View File

@ -17,8 +17,8 @@
package main package main
import ( import (
"bufio"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os" "os"
@ -34,7 +34,7 @@ import (
var stateTestCommand = &cli.Command{ var stateTestCommand = &cli.Command{
Action: stateTestCmd, Action: stateTestCmd,
Name: "statetest", Name: "statetest",
Usage: "executes the given state tests", Usage: "Executes the given state tests. Filenames can be fed via standard input (batch mode) or as an argument (one-off execution).",
ArgsUsage: "<file>", ArgsUsage: "<file>",
} }
@ -50,9 +50,6 @@ type StatetestResult struct {
} }
func stateTestCmd(ctx *cli.Context) error { func stateTestCmd(ctx *cli.Context) error {
if len(ctx.Args().First()) == 0 {
return errors.New("path-to-test argument required")
}
// Configure the go-ethereum logger // Configure the go-ethereum logger
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
@ -65,34 +62,43 @@ func stateTestCmd(ctx *cli.Context) error {
DisableStorage: ctx.Bool(DisableStorageFlag.Name), DisableStorage: ctx.Bool(DisableStorageFlag.Name),
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
} }
var ( var cfg vm.Config
tracer vm.EVMLogger
debugger *logger.StructLogger
)
switch { switch {
case ctx.Bool(MachineFlag.Name): case ctx.Bool(MachineFlag.Name):
tracer = logger.NewJSONLogger(config, os.Stderr) cfg.Tracer = logger.NewJSONLogger(config, os.Stderr)
case ctx.Bool(DebugFlag.Name): case ctx.Bool(DebugFlag.Name):
debugger = logger.NewStructLogger(config) cfg.Tracer = logger.NewStructLogger(config)
tracer = debugger
default:
debugger = logger.NewStructLogger(config)
} }
// Load the test content from the input file // Load the test content from the input file
src, err := os.ReadFile(ctx.Args().First()) if len(ctx.Args().First()) != 0 {
return runStateTest(ctx.Args().First(), cfg, ctx.Bool(MachineFlag.Name), ctx.Bool(DumpFlag.Name))
}
// Read filenames from stdin and execute back-to-back
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
fname := scanner.Text()
if len(fname) == 0 {
return nil
}
if err := runStateTest(fname, cfg, ctx.Bool(MachineFlag.Name), ctx.Bool(DumpFlag.Name)); err != nil {
return err
}
}
return nil
}
// runStateTest loads the state-test given by fname, and executes the test.
func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
src, err := os.ReadFile(fname)
if err != nil { if err != nil {
return err return err
} }
var tests map[string]tests.StateTest var tests map[string]tests.StateTest
if err = json.Unmarshal(src, &tests); err != nil { if err := json.Unmarshal(src, &tests); err != nil {
return err return err
} }
// Iterate over all the tests, run them and aggregate the results // Iterate over all the tests, run them and aggregate the results
cfg := vm.Config{
Tracer: tracer,
}
results := make([]StatetestResult, 0, len(tests)) results := make([]StatetestResult, 0, len(tests))
for key, test := range tests { for key, test := range tests {
for _, st := range test.Subtests() { for _, st := range test.Subtests() {
@ -103,28 +109,20 @@ func stateTestCmd(ctx *cli.Context) error {
if s != nil { if s != nil {
root := s.IntermediateRoot(false) root := s.IntermediateRoot(false)
result.Root = &root result.Root = &root
if ctx.Bool(MachineFlag.Name) { if jsonOut {
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
} }
} }
if err != nil { if err != nil {
// Test failed, mark as so and dump any state to aid debugging // Test failed, mark as so and dump any state to aid debugging
result.Pass, result.Error = false, err.Error() result.Pass, result.Error = false, err.Error()
if ctx.Bool(DumpFlag.Name) && s != nil { if dump && s != nil {
dump := s.RawDump(nil) dump := s.RawDump(nil)
result.State = &dump result.State = &dump
} }
} }
results = append(results, *result) results = append(results, *result)
// Print any structured logs collected
if ctx.Bool(DebugFlag.Name) {
if debugger != nil {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
logger.WriteTrace(os.Stderr, debugger.StructLogs())
}
}
} }
} }
out, _ := json.MarshalIndent(results, "", " ") out, _ := json.MarshalIndent(results, "", " ")