diff options
Diffstat (limited to 'cmd')
-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 | ||||
-rw-r--r-- | cmd/geth/main.go | 18 | ||||
-rw-r--r-- | cmd/puppeth/module_ethstats.go | 25 | ||||
-rw-r--r-- | cmd/puppeth/module_node.go | 8 | ||||
-rw-r--r-- | cmd/puppeth/wizard.go | 24 | ||||
-rw-r--r-- | cmd/puppeth/wizard_ethstats.go | 18 | ||||
-rw-r--r-- | cmd/utils/cmd.go | 2 | ||||
-rw-r--r-- | cmd/utils/flags.go | 9 |
11 files changed, 248 insertions, 58 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 +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 66958e34d..8166c9ce8 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -239,24 +239,30 @@ func startNode(ctx *cli.Context, stack *node.Node) { } stateReader := ethclient.NewClient(rpcClient) - // Open and self derive any wallets already attached + // Open any wallets already attached for _, wallet := range stack.AccountManager().Wallets() { if err := wallet.Open(""); err != nil { log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) - } else { - wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader) } } // Listen for wallet event till termination for event := range events { - if event.Arrive { + switch event.Kind { + case accounts.WalletArrived: if err := event.Wallet.Open(""); err != nil { log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) + } + case accounts.WalletOpened: + status, _ := event.Wallet.Status() + log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) + + if event.Wallet.URL().Scheme == "ledger" { + event.Wallet.SelfDerive(accounts.DefaultLedgerBaseDerivationPath, stateReader) } else { - log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", event.Wallet.Status()) event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader) } - } else { + + case accounts.WalletDropped: log.Info("Old wallet dropped", "url", event.Wallet.URL()) event.Wallet.Close() } diff --git a/cmd/puppeth/module_ethstats.go b/cmd/puppeth/module_ethstats.go index da34acb3b..5d3fa5fc0 100644 --- a/cmd/puppeth/module_ethstats.go +++ b/cmd/puppeth/module_ethstats.go @@ -42,7 +42,7 @@ RUN \ WORKDIR /eth-netstats EXPOSE 3000 -RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: []};' > lib/utils/config.js +RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: [{{.Banned}}]};' > lib/utils/config.js CMD ["npm", "start"] ` @@ -59,7 +59,8 @@ services: - "{{.Port}}:3000"{{end}} environment: - WS_SECRET={{.Secret}}{{if .VHost}} - - VIRTUAL_HOST={{.VHost}}{{end}} + - VIRTUAL_HOST={{.VHost}}{{end}}{{if .Banned}} + - BANNED={{.Banned}}{{end}} logging: driver: "json-file" options: @@ -71,18 +72,24 @@ services: // deployEthstats deploys a new ethstats container to a remote machine via SSH, // docker and docker-compose. If an instance with the specified network name // already exists there, it will be overwritten! -func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string) ([]byte, error) { +func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string, banned []string) ([]byte, error) { // Generate the content to upload to the server workdir := fmt.Sprintf("%d", rand.Int63()) files := make(map[string][]byte) + trustedLabels := make([]string, len(trusted)) for i, address := range trusted { - trusted[i] = fmt.Sprintf("\"%s\"", address) + trustedLabels[i] = fmt.Sprintf("\"%s\"", address) + } + bannedLabels := make([]string, len(banned)) + for i, address := range banned { + bannedLabels[i] = fmt.Sprintf("\"%s\"", address) } dockerfile := new(bytes.Buffer) template.Must(template.New("").Parse(ethstatsDockerfile)).Execute(dockerfile, map[string]interface{}{ - "Trusted": strings.Join(trusted, ", "), + "Trusted": strings.Join(trustedLabels, ", "), + "Banned": strings.Join(bannedLabels, ", "), }) files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() @@ -92,6 +99,7 @@ func deployEthstats(client *sshClient, network string, port int, secret string, "Port": port, "Secret": secret, "VHost": vhost, + "Banned": strings.Join(banned, ","), }) files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() @@ -112,11 +120,12 @@ type ethstatsInfos struct { port int secret string config string + banned []string } // String implements the stringer interface. func (info *ethstatsInfos) String() string { - return fmt.Sprintf("host=%s, port=%d, secret=%s", info.host, info.port, info.secret) + return fmt.Sprintf("host=%s, port=%d, secret=%s, banned=%v", info.host, info.port, info.secret, info.banned) } // checkEthstats does a health-check against an ethstats server to verify whether @@ -150,6 +159,9 @@ func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) { if port != 80 && port != 443 { config += fmt.Sprintf(":%d", port) } + // Retrieve the IP blacklist + banned := strings.Split(infos.envvars["BANNED"], ",") + // Run a sanity check to see if the port is reachable if err = checkPort(host, port); err != nil { log.Warn("Ethstats service seems unreachable", "server", host, "port", port, "err", err) @@ -160,5 +172,6 @@ func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) { port: port, secret: secret, config: config, + banned: banned, }, nil } diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go index 9fe97c892..8f912f9eb 100644 --- a/cmd/puppeth/module_node.go +++ b/cmd/puppeth/module_node.go @@ -30,7 +30,7 @@ import ( // nodeDockerfile is the Dockerfile required to run an Ethereum node. var nodeDockerfile = ` -FROM ethereum/client-go:alpine-develop +FROM ethereum/client-go:latest ADD genesis.json /genesis.json {{if .Unlock}} @@ -38,9 +38,9 @@ ADD genesis.json /genesis.json ADD signer.pass /signer.pass {{end}} RUN \ - echo '/geth init /genesis.json' > geth.sh && \{{if .Unlock}} + echo 'geth init /genesis.json' > geth.sh && \{{if .Unlock}} echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}} - echo $'/geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .BootV4}}--bootnodesv4 {{.BootV4}}{{end}} {{if .BootV5}}--bootnodesv5 {{.BootV5}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh + echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .BootV4}}--bootnodesv4 {{.BootV4}}{{end}} {{if .BootV5}}--bootnodesv5 {{.BootV5}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh ENTRYPOINT ["/bin/sh", "geth.sh"] ` @@ -197,7 +197,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error) // Container available, retrieve its node ID and its genesis json var out []byte - if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 /geth --exec admin.nodeInfo.id attach", network, kind)); err != nil { + if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 geth --exec admin.nodeInfo.id attach", network, kind)); err != nil { return nil, ErrServiceUnreachable } id := bytes.Trim(bytes.TrimSpace(out), "\"") diff --git a/cmd/puppeth/wizard.go b/cmd/puppeth/wizard.go index f19bcbbcd..518741279 100644 --- a/cmd/puppeth/wizard.go +++ b/cmd/puppeth/wizard.go @@ -22,6 +22,7 @@ import ( "fmt" "io/ioutil" "math/big" + "net" "os" "path/filepath" "sort" @@ -277,3 +278,26 @@ func (w *wizard) readJSON() string { return string(blob) } } + +// readIPAddress reads a single line from stdin, trimming if from spaces and +// converts it to a network IP address. +func (w *wizard) readIPAddress() net.IP { + for { + // Read the IP address from the user + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return nil + } + // Make sure it looks ok and return it if so + ip := net.ParseIP(text) + if ip == nil { + log.Error("Invalid IP address, please retry") + continue + } + return ip + } +} diff --git a/cmd/puppeth/wizard_ethstats.go b/cmd/puppeth/wizard_ethstats.go index c117a6027..504d8fd9c 100644 --- a/cmd/puppeth/wizard_ethstats.go +++ b/cmd/puppeth/wizard_ethstats.go @@ -60,6 +60,22 @@ func (w *wizard) deployEthstats() { fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret) infos.secret = w.readDefaultString(infos.secret) } + // Gather any blacklists to ban from reporting + fmt.Println() + fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned) + if w.readDefaultString("y") != "y" { + infos.banned = nil + + fmt.Println() + fmt.Println("Which IP addresses should be blacklisted?") + for { + if ip := w.readIPAddress(); ip != nil { + infos.banned = append(infos.banned, ip.String()) + continue + } + break + } + } // Try to deploy the ethstats server on the host trusted := make([]string, 0, len(w.servers)) for _, client := range w.servers { @@ -67,7 +83,7 @@ func (w *wizard) deployEthstats() { trusted = append(trusted, client.address) } } - if out, err := deployEthstats(client, w.network, infos.port, infos.secret, infos.host, trusted); err != nil { + if out, err := deployEthstats(client, w.network, infos.port, infos.secret, infos.host, trusted, infos.banned); err != nil { log.Error("Failed to deploy ethstats container", "err", err) if len(out) > 0 { fmt.Printf("%s\n", out) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 17c258c6c..23b10c2d7 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -164,7 +164,7 @@ func ImportChain(chain *core.BlockChain, fn string) error { func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { for _, b := range bs { - if !chain.HasBlock(b.Hash()) { + if !chain.HasBlock(b.Hash(), b.NumberU64()) { return false } } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 04728b5c6..5ab6047e8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -41,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethstats" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -121,7 +120,7 @@ var ( } NoUSBFlag = cli.BoolFlag{ Name: "nousb", - Usage: "Disables monitoring for and managine USB hardware wallets", + Usage: "Disables monitoring for and managing USB hardware wallets", } NetworkIdFlag = cli.Uint64Flag{ Name: "networkid", @@ -950,10 +949,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) } - // Ethereum needs to know maxPeers to calculate the light server peer ratio. - // TODO(fjl): ensure Ethereum can get MaxPeers from node. - cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) - if ctx.GlobalIsSet(CacheFlag.Name) { cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) } @@ -1103,7 +1098,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai Fatalf("%v", err) } vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} - chain, err = core.NewBlockChain(chainDb, config, engine, new(event.TypeMux), vmcfg) + chain, err = core.NewBlockChain(chainDb, config, engine, vmcfg) if err != nil { Fatalf("Can't create BlockChain: %v", err) } |