diff options
Diffstat (limited to 'cmd/evm')
-rw-r--r-- | cmd/evm/json_logger.go | 8 | ||||
-rw-r--r-- | cmd/evm/main.go | 8 | ||||
-rw-r--r-- | cmd/evm/runner.go | 67 | ||||
-rw-r--r-- | cmd/evm/staterunner.go | 119 |
4 files changed, 169 insertions, 33 deletions
diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go index d61981062..2cfeaa795 100644 --- a/cmd/evm/json_logger.go +++ b/cmd/evm/json_logger.go @@ -57,11 +57,15 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos } // CaptureEnd is triggered at end of execution. -func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error { +func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { type endLog struct { Output string `json:"output"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` Time time.Duration `json:"time"` + Err string `json:"error,omitempty"` } - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t}) + if err != nil { + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) + } + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) } diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 1892ae3d3..6c39cf8b8 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -53,7 +53,7 @@ var ( } CodeFileFlag = cli.StringFlag{ Name: "codefile", - Usage: "file containing EVM code", + Usage: "File containing EVM code. If '-' is specified, code is read from stdin ", } GasFlag = cli.Uint64Flag{ Name: "gas", @@ -102,6 +102,10 @@ var ( Name: "sender", Usage: "The transaction origin", } + ReceiverFlag = cli.StringFlag{ + Name: "receiver", + Usage: "The transaction receiver (execution context)", + } DisableMemoryFlag = cli.BoolFlag{ Name: "nomemory", Usage: "disable memory output", @@ -131,6 +135,7 @@ func init() { GenesisFlag, MachineFlag, SenderFlag, + ReceiverFlag, DisableMemoryFlag, DisableStackFlag, } @@ -138,6 +143,7 @@ func init() { compileCommand, disasmCommand, runCommand, + stateTestCommand, } } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 3f95a0c93..96de0c76a 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -84,6 +84,7 @@ func runCmd(ctx *cli.Context) error { statedb *state.StateDB chainConfig *params.ChainConfig sender = common.StringToAddress("sender") + receiver = common.StringToAddress("receiver") ) if ctx.GlobalBool(MachineFlag.Name) { tracer = NewJSONLogger(logconfig, os.Stdout) @@ -104,46 +105,52 @@ func runCmd(ctx *cli.Context) error { if ctx.GlobalString(SenderFlag.Name) != "" { sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) } - statedb.CreateAccount(sender) + if ctx.GlobalString(ReceiverFlag.Name) != "" { + receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name)) + } + var ( code []byte ret []byte err error ) - if fn := ctx.Args().First(); len(fn) > 0 { + // The '--code' or '--codefile' flag overrides code in state + if ctx.GlobalString(CodeFileFlag.Name) != "" { + var hexcode []byte + var err error + // If - is specified, it means that code comes from stdin + if ctx.GlobalString(CodeFileFlag.Name) == "-" { + //Try reading from stdin + if hexcode, err = ioutil.ReadAll(os.Stdin); err != nil { + fmt.Printf("Could not load code from stdin: %v\n", err) + os.Exit(1) + } + } else { + // Codefile with hex assembly + if hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)); err != nil { + fmt.Printf("Could not load code from file: %v\n", err) + os.Exit(1) + } + } + code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n"))) + + } else if ctx.GlobalString(CodeFlag.Name) != "" { + code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) + } else if fn := ctx.Args().First(); len(fn) > 0 { + // EASM-file to compile src, err := ioutil.ReadFile(fn) if err != nil { return err } - bin, err := compiler.Compile(fn, src, false) if err != nil { return err } code = common.Hex2Bytes(bin) - } else if ctx.GlobalString(CodeFlag.Name) != "" { - code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) - } else { - var hexcode []byte - if ctx.GlobalString(CodeFileFlag.Name) != "" { - var err error - hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)) - if err != nil { - fmt.Printf("Could not load code from file: %v\n", err) - os.Exit(1) - } - } else { - var err error - hexcode, err = ioutil.ReadAll(os.Stdin) - if err != nil { - fmt.Printf("Could not load code from stdin: %v\n", err) - os.Exit(1) - } - } - code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n"))) } + initialGas := ctx.GlobalUint64(GasFlag.Name) runtimeConfig := runtime.Config{ Origin: sender, @@ -180,9 +187,9 @@ func runCmd(ctx *cli.Context) error { input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) } else { - receiver := common.StringToAddress("receiver") - statedb.SetCode(receiver, code) - + if len(code) > 0 { + statedb.SetCode(receiver, code) + } ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) } execTime := time.Since(tstart) @@ -227,13 +234,13 @@ Gas used: %d `, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas) } if tracer != nil { - tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime) + tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err) } else { fmt.Printf("0x%x\n", ret) + if err != nil { + fmt.Printf(" error: %v\n", err) + } } - if err != nil { - fmt.Printf(" error: %v\n", err) - } return nil } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go new file mode 100644 index 000000000..3a4cc51c0 --- /dev/null +++ b/cmd/evm/staterunner.go @@ -0,0 +1,119 @@ +// 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 <http://www.gnu.org/licenses/>. + +package main + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/tests" + + cli "gopkg.in/urfave/cli.v1" +) + +var stateTestCommand = cli.Command{ + Action: stateTestCmd, + Name: "statetest", + Usage: "executes the given state tests", + ArgsUsage: "<file>", +} + +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), + } + var ( + tracer vm.Tracer + debugger *vm.StructLogger + ) + switch { + case ctx.GlobalBool(MachineFlag.Name): + tracer = 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} + if state, err := test.Run(st, cfg); 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) && state != nil { + dump := state.RawDump() + 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()) + } + } + } + } + out, _ := json.MarshalIndent(results, "", " ") + fmt.Println(string(out)) + return nil +} |