github.com/ethereum/go-ethereum@v1.16.1/cmd/evm/staterunner.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bufio" 21 "encoding/json" 22 "fmt" 23 "os" 24 "regexp" 25 "slices" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/vm" 30 "github.com/ethereum/go-ethereum/internal/flags" 31 "github.com/ethereum/go-ethereum/tests" 32 "github.com/urfave/cli/v2" 33 ) 34 35 var ( 36 forkFlag = &cli.StringFlag{ 37 Name: "statetest.fork", 38 Usage: "Only run tests for the specified fork.", 39 Category: flags.VMCategory, 40 } 41 idxFlag = &cli.IntFlag{ 42 Name: "statetest.index", 43 Usage: "The index of the subtest to run.", 44 Category: flags.VMCategory, 45 Value: -1, // default to select all subtest indices 46 } 47 ) 48 var stateTestCommand = &cli.Command{ 49 Action: stateTestCmd, 50 Name: "statetest", 51 Usage: "Executes the given state tests. Filenames can be fed via standard input (batch mode) or as an argument (one-off execution).", 52 ArgsUsage: "<file>", 53 Flags: slices.Concat([]cli.Flag{ 54 BenchFlag, 55 DumpFlag, 56 forkFlag, 57 HumanReadableFlag, 58 idxFlag, 59 RunFlag, 60 }, traceFlags), 61 } 62 63 func stateTestCmd(ctx *cli.Context) error { 64 path := ctx.Args().First() 65 66 // If path is provided, run the tests at that path. 67 if len(path) != 0 { 68 var ( 69 collected = collectFiles(path) 70 results []testResult 71 ) 72 for _, fname := range collected { 73 r, err := runStateTest(ctx, fname) 74 if err != nil { 75 return err 76 } 77 results = append(results, r...) 78 } 79 report(ctx, results) 80 return nil 81 } 82 // Otherwise, read filenames from stdin and execute back-to-back. 83 scanner := bufio.NewScanner(os.Stdin) 84 for scanner.Scan() { 85 fname := scanner.Text() 86 if len(fname) == 0 { 87 return nil 88 } 89 results, err := runStateTest(ctx, fname) 90 if err != nil { 91 return err 92 } 93 report(ctx, results) 94 } 95 return nil 96 } 97 98 // runStateTest loads the state-test given by fname, and executes the test. 99 func runStateTest(ctx *cli.Context, fname string) ([]testResult, error) { 100 src, err := os.ReadFile(fname) 101 if err != nil { 102 return nil, err 103 } 104 var testsByName map[string]tests.StateTest 105 if err := json.Unmarshal(src, &testsByName); err != nil { 106 return nil, fmt.Errorf("unable to read test file %s: %w", fname, err) 107 } 108 109 cfg := vm.Config{Tracer: tracerFromFlags(ctx)} 110 re, err := regexp.Compile(ctx.String(RunFlag.Name)) 111 if err != nil { 112 return nil, fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err) 113 } 114 115 // Iterate over all the tests, run them and aggregate the results 116 results := make([]testResult, 0, len(testsByName)) 117 for key, test := range testsByName { 118 if !re.MatchString(key) { 119 continue 120 } 121 for i, st := range test.Subtests() { 122 if idx := ctx.Int(idxFlag.Name); idx != -1 && idx != i { 123 // If specific index requested, skip all tests that do not match. 124 continue 125 } 126 if fork := ctx.String(forkFlag.Name); fork != "" && st.Fork != fork { 127 // If specific fork requested, skip all tests that do not match. 128 continue 129 } 130 // Run the test and aggregate the result 131 result := &testResult{Name: key, Fork: st.Fork, Pass: true} 132 test.Run(st, cfg, false, rawdb.HashScheme, func(err error, state *tests.StateTestState) { 133 var root common.Hash 134 if state.StateDB != nil { 135 root = state.StateDB.IntermediateRoot(false) 136 result.Root = &root 137 fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) 138 // Dump any state to aid debugging. 139 if ctx.Bool(DumpFlag.Name) { 140 result.State = dump(state.StateDB) 141 } 142 } 143 // Collect bench stats if requested. 144 if ctx.Bool(BenchFlag.Name) { 145 _, stats, _ := timedExec(true, func() ([]byte, uint64, error) { 146 _, _, gasUsed, _ := test.RunNoVerify(st, cfg, false, rawdb.HashScheme) 147 return nil, gasUsed, nil 148 }) 149 result.Stats = &stats 150 } 151 if err != nil { 152 // Test failed, mark as so. 153 result.Pass, result.Error = false, err.Error() 154 return 155 } 156 }) 157 results = append(results, *result) 158 } 159 } 160 return results, nil 161 }