// Copyright 2017 The go-ethereum Authors // This file is part of go-ethereum. // // go-ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // go-ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . package main import ( "context" "encoding/json" "errors" "fmt" "io/ioutil" "os" "github.com/ledgerwatch/turbo-geth/core/state" "github.com/ledgerwatch/turbo-geth/core/vm" "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/ledgerwatch/turbo-geth/log" "github.com/ledgerwatch/turbo-geth/tests" "github.com/urfave/cli" ) var stateTestCommand = cli.Command{ Action: stateTestCmd, Name: "statetest", Usage: "executes the given state tests", ArgsUsage: "", } // StatetestResult contains the execution status after running a state test, any // error that might have occurred and a dump of the final state if requested. type StatetestResult struct { Name string `json:"name"` Pass bool `json:"pass"` Fork string `json:"fork"` Error *string `json:"error,omitempty"` State *state.Dump `json:"state,omitempty"` } 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 glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) log.Root().SetHandler(glogger) // Configure the EVM logger config := &vm.LogConfig{ DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name), } var ( tracer vm.Tracer debugger *vm.StructLogger ) switch { case ctx.GlobalBool(MachineFlag.Name): tracer = vm.NewJSONLogger(config, os.Stderr) case ctx.GlobalBool(DebugFlag.Name): debugger = vm.NewStructLogger(config) tracer = debugger default: debugger = vm.NewStructLogger(config) } // Load the test content from the input file src, err := ioutil.ReadFile(ctx.Args().First()) if err != nil { return err } var tests map[string]tests.StateTest if err = json.Unmarshal(src, &tests); err != nil { return err } // Iterate over all the tests, run them and aggregate the results cfg := vm.Config{ Tracer: tracer, Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), } results := make([]StatetestResult, 0, len(tests)) for key, test := range tests { for _, st := range test.Subtests() { // Run the test and aggregate the result result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true, Error: new(string)} statedb, tds, err := test.Run(context.Background(), st, cfg) // print state root for evmlab tracing if ctx.GlobalBool(MachineFlag.Name) && statedb != nil { fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", tds.Trie().Root()) } if err != nil { // Test failed, mark as so and dump any state to aid debugging result.Pass, *result.Error = false, err.Error() if ctx.GlobalBool(DumpFlag.Name) && statedb != nil { tx, err1 := tds.Database().(ethdb.HasKV).KV().Begin(context.Background(), nil, ethdb.RO) if err1 != nil { return fmt.Errorf("transition cannot open tx: %v", err1) } dump := state.NewDumper(tx, tds.GetBlockNr()).DefaultRawDump() tx.Rollback() result.State = &dump } } results = append(results, *result) // Print any structured logs collected if ctx.GlobalBool(DebugFlag.Name) { if debugger != nil { fmt.Fprintln(os.Stderr, "#### TRACE ####") vm.WriteTrace(os.Stderr, debugger.StructLogs()) } } tds.Database().Close() } } out, _ := json.MarshalIndent(results, "", " ") fmt.Println(string(out)) return nil }