From 80f7c6c2996ad47f70a5070c400b1fd87a20c59c Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 7 Jun 2017 17:09:08 +0200 Subject: cmd/evm: add --prestate, --sender, --json flags for fuzzing (#14476) --- cmd/evm/json_logger.go | 60 ++++++++++++++++++++++++++++++++++ cmd/evm/main.go | 15 +++++++++ cmd/evm/runner.go | 87 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 cmd/evm/json_logger.go (limited to 'cmd') diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go new file mode 100644 index 000000000..a84d5daeb --- /dev/null +++ b/cmd/evm/json_logger.go @@ -0,0 +1,60 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "encoding/json" + "io" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/vm" +) + +type JSONLogger struct { + encoder *json.Encoder +} + +func NewJSONLogger(writer io.Writer) *JSONLogger { + return &JSONLogger{json.NewEncoder(writer)} +} + +// CaptureState outputs state information on the logger. +func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { + return l.encoder.Encode(vm.StructLog{ + Pc: pc, + Op: op, + Gas: gas + cost, + GasCost: cost, + Memory: memory.Data(), + Stack: stack.Data(), + Storage: nil, + Depth: depth, + Err: err, + }) +} + +// CaptureEnd is triggered at end of execution. +func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error { + type endLog struct { + Output string `json:"output"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + Time time.Duration `json:"time"` + } + 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 e85d31d03..48a1b92cb 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -90,6 +90,18 @@ var ( Name: "nogasmetering", Usage: "disable gas metering", } + GenesisFlag = cli.StringFlag{ + Name: "prestate", + Usage: "JSON file with prestate (genesis) config", + } + MachineFlag = cli.BoolFlag{ + Name: "json", + Usage: "output trace logs in machine readable format (json)", + } + SenderFlag = cli.StringFlag{ + Name: "sender", + Usage: "The transaction origin", + } ) func init() { @@ -108,6 +120,9 @@ func init() { MemProfileFlag, CPUProfileFlag, StatDumpFlag, + GenesisFlag, + MachineFlag, + SenderFlag, } app.Commands = []cli.Command{ compileCommand, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 22538d7b1..b1fb8998f 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "encoding/json" "fmt" "io/ioutil" "os" @@ -29,11 +30,13 @@ import ( "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" cli "gopkg.in/urfave/cli.v1" ) @@ -45,17 +48,59 @@ var runCommand = cli.Command{ Description: `The run command runs arbitrary EVM code.`, } +// readGenesis will read the given JSON format genesis file and return +// the initialized Genesis structure +func readGenesis(genesisPath string) *core.Genesis { + // Make sure we have a valid genesis JSON + //genesisPath := ctx.Args().First() + if len(genesisPath) == 0 { + utils.Fatalf("Must supply path to genesis JSON file") + } + file, err := os.Open(genesisPath) + if err != nil { + utils.Fatalf("Failed to read genesis file: %v", err) + } + defer file.Close() + + genesis := new(core.Genesis) + if err := json.NewDecoder(file).Decode(genesis); err != nil { + utils.Fatalf("invalid genesis file: %v", err) + } + return genesis +} + func runCmd(ctx *cli.Context) error { glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) log.Root().SetHandler(glogger) var ( - db, _ = ethdb.NewMemDatabase() - statedb, _ = state.New(common.Hash{}, db) - sender = common.StringToAddress("sender") - logger = vm.NewStructLogger(nil) + tracer vm.Tracer + debugLogger *vm.StructLogger + statedb *state.StateDB + chainConfig *params.ChainConfig + sender = common.StringToAddress("sender") ) + if ctx.GlobalBool(MachineFlag.Name) { + tracer = NewJSONLogger(os.Stdout) + } else if ctx.GlobalBool(DebugFlag.Name) { + debugLogger = vm.NewStructLogger(nil) + tracer = debugLogger + } else { + debugLogger = vm.NewStructLogger(nil) + } + if ctx.GlobalString(GenesisFlag.Name) != "" { + gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) + _, statedb = gen.ToBlock() + chainConfig = gen.Config + } else { + var db, _ = ethdb.NewMemDatabase() + statedb, _ = state.New(common.Hash{}, db) + } + if ctx.GlobalString(SenderFlag.Name) != "" { + sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) + } + statedb.CreateAccount(sender) var ( @@ -95,16 +140,16 @@ func runCmd(ctx *cli.Context) error { } code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n"))) } - + initialGas := ctx.GlobalUint64(GasFlag.Name) runtimeConfig := runtime.Config{ Origin: sender, State: statedb, - GasLimit: ctx.GlobalUint64(GasFlag.Name), + GasLimit: initialGas, GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), Value: utils.GlobalBig(ctx, ValueFlag.Name), EVMConfig: vm.Config{ - Tracer: logger, - Debug: ctx.GlobalBool(DebugFlag.Name), + Tracer: tracer, + Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name), }, } @@ -122,15 +167,19 @@ func runCmd(ctx *cli.Context) error { defer pprof.StopCPUProfile() } + if chainConfig != nil { + runtimeConfig.ChainConfig = chainConfig + } tstart := time.Now() + var leftOverGas uint64 if ctx.GlobalBool(CreateFlag.Name) { input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) - ret, _, err = runtime.Create(input, &runtimeConfig) + ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) } else { receiver := common.StringToAddress("receiver") statedb.SetCode(receiver, code) - ret, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) + ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) } execTime := time.Since(tstart) @@ -153,8 +202,10 @@ func runCmd(ctx *cli.Context) error { } if ctx.GlobalBool(DebugFlag.Name) { - fmt.Fprintln(os.Stderr, "#### TRACE ####") - vm.WriteTrace(os.Stderr, logger.StructLogs()) + if debugLogger != nil { + fmt.Fprintln(os.Stderr, "#### TRACE ####") + vm.WriteTrace(os.Stderr, debugLogger.StructLogs()) + } fmt.Fprintln(os.Stderr, "#### LOGS ####") vm.WriteLogs(os.Stderr, statedb.Logs()) } @@ -167,14 +218,18 @@ heap objects: %d allocations: %d total allocations: %d GC calls: %d +Gas used: %d -`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC) +`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas) + } + if tracer != nil { + tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime) + } else { + fmt.Printf("0x%x\n", ret) } - fmt.Printf("0x%x", ret) if err != nil { - fmt.Printf(" error: %v", err) + fmt.Printf(" error: %v\n", err) } - fmt.Println() return nil } -- cgit v1.2.3