From bdba044a8031d810555196cde1b97792fa2b8084 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 6 Mar 2015 02:46:56 +0100 Subject: ethutil: remove Config variable Various functions throughout the codebase used it to grab settings. This has to stop because I want to use them without reading the config file. These functions can now be used without reading the config first: * ethdb.NewLDBDatabase * ethrepl.NewJSRepl * vm.New --- cmd/ethereum/repl/repl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cmd/ethereum') diff --git a/cmd/ethereum/repl/repl.go b/cmd/ethereum/repl/repl.go index ec1aa6918..05ea71e79 100644 --- a/cmd/ethereum/repl/repl.go +++ b/cmd/ethereum/repl/repl.go @@ -54,7 +54,7 @@ type JSRepl struct { } func NewJSRepl(ethereum *eth.Ethereum) *JSRepl { - hist, err := os.OpenFile(path.Join(ethutil.Config.ExecPath, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + hist, err := os.OpenFile(path.Join(ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { panic(err) } -- cgit v1.2.3 From bae7e93a9c5af679682f89b0f475e98c1eee9e58 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 6 Mar 2015 03:00:41 +0100 Subject: cmd/ethereum: improve command line interface The ethereum command line interface is now structured using subcommands. These separate the different tasks it can perform. Almost all flag names are backwards compatible. The key tasks have not been ported to subcommands since they will be replaced by the new accounts infrastructure very soon. --- cmd/ethereum/cmd.go | 40 --------- cmd/ethereum/flags.go | 168 ----------------------------------- cmd/ethereum/js.go | 43 +++++++++ cmd/ethereum/main.go | 238 +++++++++++++++++++++++++++++++------------------- 4 files changed, 191 insertions(+), 298 deletions(-) delete mode 100644 cmd/ethereum/cmd.go delete mode 100644 cmd/ethereum/flags.go create mode 100644 cmd/ethereum/js.go (limited to 'cmd/ethereum') diff --git a/cmd/ethereum/cmd.go b/cmd/ethereum/cmd.go deleted file mode 100644 index 7bb7c3ef7..000000000 --- a/cmd/ethereum/cmd.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -// -// This library 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 2.1 of the License, or (at your option) any later version. -// -// This 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA - -package main - -import ( - "io/ioutil" - "os" - - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/javascript" - "github.com/ethereum/go-ethereum/xeth" -) - -func ExecJsFile(ethereum *eth.Ethereum, InputFile string) { - file, err := os.Open(InputFile) - if err != nil { - clilogger.Fatalln(err) - } - content, err := ioutil.ReadAll(file) - if err != nil { - clilogger.Fatalln(err) - } - re := javascript.NewJSRE(xeth.New(ethereum)) - re.Run(string(content)) -} diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go deleted file mode 100644 index a3004f503..000000000 --- a/cmd/ethereum/flags.go +++ /dev/null @@ -1,168 +0,0 @@ -/* - 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 . -*/ -/** - * @authors - * Jeffrey Wilcke - */ -package main - -import ( - "crypto/ecdsa" - "flag" - "fmt" - "log" - "os" - "path" - "runtime" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/vm" -) - -var ( - Identifier string - KeyRing string - DiffTool bool - DiffType string - KeyStore string - StartRpc bool - StartWebSockets bool - RpcListenAddress string - RpcPort int - OutboundPort string - ShowGenesis bool - AddPeer string - MaxPeer int - GenAddr bool - BootNodes string - NodeKey *ecdsa.PrivateKey - NAT nat.Interface - SecretFile string - ExportDir string - NonInteractive bool - Datadir string - LogFile string - ConfigFile string - DebugFile string - LogLevel int - LogFormat string - Dump bool - DumpHash string - DumpNumber int - VmType int - ImportChain string - SHH bool - Dial bool - PrintVersion bool - MinerThreads int -) - -// flags specific to cli client -var ( - StartMining bool - StartJsConsole bool - InputFile string -) - -var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini") - -func Init() { - // TODO: move common flag processing to cmd/util - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "%s [options] [filename]:\noptions precedence: default < config file < environment variables < command line\n", os.Args[0]) - flag.PrintDefaults() - } - - flag.IntVar(&VmType, "vm", 0, "Virtual Machine type: 0-1: standard, debug") - flag.StringVar(&Identifier, "id", "", "Custom client identifier") - flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use") - flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file") - - flag.StringVar(&RpcListenAddress, "rpcaddr", "127.0.0.1", "address for json-rpc server to listen on") - flag.IntVar(&RpcPort, "rpcport", 8545, "port to start json-rpc server on") - flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") - flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") - flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") - flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") - flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") - flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)") - flag.StringVar(&Datadir, "datadir", ethutil.DefaultDataDir(), "specifies the datadir to use") - flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") - flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)") - flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5 (= silent,error,warn,info,debug,debug detail)") - flag.StringVar(&LogFormat, "logformat", "std", "logformat: std,raw") - flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0") - flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false") - flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block") - flag.StringVar(&ImportChain, "chain", "", "Imports given chain") - - flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]") - flag.StringVar(&DumpHash, "hash", "", "specify arg in hex") - flag.IntVar(&DumpNumber, "number", -1, "specify arg in number") - - flag.BoolVar(&StartMining, "mine", false, "start mining") - flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console") - flag.BoolVar(&PrintVersion, "version", false, "prints version number") - flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads") - - // Network stuff - var ( - nodeKeyFile = flag.String("nodekey", "", "network private key file") - nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)") - natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:)") - ) - flag.BoolVar(&Dial, "dial", true, "dial out connections (default on)") - //flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)") - flag.StringVar(&OutboundPort, "port", "30303", "listening port") - - flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap") - flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers") - - flag.Parse() - - // When the javascript console is started log to a file instead - // of stdout - if StartJsConsole { - LogFile = path.Join(Datadir, "ethereum.log") - } - - var err error - if NAT, err = nat.Parse(*natstr); err != nil { - log.Fatalf("-nat: %v", err) - } - switch { - case *nodeKeyFile != "" && *nodeKeyHex != "": - log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive") - case *nodeKeyFile != "": - if NodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil { - log.Fatalf("-nodekey: %v", err) - } - case *nodeKeyHex != "": - if NodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { - log.Fatalf("-nodekeyhex: %v", err) - } - } - - if VmType >= int(vm.MaxVmTy) { - log.Fatal("Invalid VM type ", VmType) - } - - InputFile = flag.Arg(0) -} diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go new file mode 100644 index 000000000..f0aeb45f5 --- /dev/null +++ b/cmd/ethereum/js.go @@ -0,0 +1,43 @@ +// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. +// +// This library 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 2.1 of the License, or (at your option) any later version. +// +// This 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA + +package main + +import ( + "io/ioutil" + "os" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/javascript" + "github.com/ethereum/go-ethereum/xeth" +) + +func execJsFile(ethereum *eth.Ethereum, filename string) { + file, err := os.Open(filename) + if err != nil { + utils.Fatalf("%v", err) + } + content, err := ioutil.ReadAll(file) + if err != nil { + utils.Fatalf("%v", err) + } + re := javascript.NewJSRE(xeth.New(ethereum)) + if _, err := re.Run(string(content)); err != nil { + utils.Fatalf("Javascript Error: %v", err) + } +} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index b9e69f700..a38e012c2 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -24,15 +24,16 @@ import ( "fmt" "os" "runtime" + "strconv" "time" + "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/cmd/ethereum/repl" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/state" ) @@ -41,117 +42,174 @@ const ( Version = "0.8.6" ) -var clilogger = logger.NewLogger("CLI") +var ( + clilogger = logger.NewLogger("CLI") + app = cli.NewApp() +) -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) +func init() { + app.Version = Version + app.Usage = "the go-ethereum command-line client" + app.Action = run + app.HideVersion = true // we have a command to print the version + app.Commands = []cli.Command{ + { + Action: version, + Name: "version", + Usage: "print ethereum version numbers", + Description: ` +The output of this command is supposed to be machine-readable. +`, + }, + { + Action: dump, + Name: "dump", + Usage: `dump a specific block from storage`, + Description: ` +The arguments are interpreted as block numbers or hashes. +Use "ethereum dump 0" to dump the genesis block. +`, + }, + { + Action: runjs, + Name: "js", + Usage: `interactive JavaScript console`, + Description: ` +In the console, you can use the eth object to interact +with the running ethereum stack. The API does not match +ethereum.js. + +A JavaScript file can be provided as the argument. The +runtime will execute the file and exit. +`, + }, + { + Action: importchain, + Name: "import", + Usage: `import a blockchain file`, + }, + } + app.Author = "" + app.Email = "" + app.Flags = []cli.Flag{ + utils.BootnodesFlag, + utils.DataDirFlag, + utils.KeyRingFlag, + utils.KeyStoreFlag, + utils.ListenPortFlag, + utils.LogFileFlag, + utils.LogFormatFlag, + utils.LogLevelFlag, + utils.MaxPeersFlag, + utils.MinerThreadsFlag, + utils.MiningEnabledFlag, + utils.NATFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.RPCEnabledFlag, + utils.RPCListenAddrFlag, + utils.RPCPortFlag, + utils.VMTypeFlag, + } + + // missing: + // flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") + // flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0") + // flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false") - defer func() { - logger.Flush() - }() + // potential subcommands: + // flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") + // flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") + // flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") +} +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + defer logger.Flush() utils.HandleInterrupt() + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} - // precedence: code-internal flag default < config file < environment variables < command line - Init() // parsing command line +func run(ctx *cli.Context) { + fmt.Printf("Welcome to the FRONTIER\n") + eth := utils.GetEthereum(ClientIdentifier, Version, ctx) + startEth(ctx, eth) + // this blocks the thread + eth.WaitForShutdown() +} - if PrintVersion { - printVersion() - return +func runjs(ctx *cli.Context) { + eth := utils.GetEthereum(ClientIdentifier, Version, ctx) + startEth(ctx, eth) + if len(ctx.Args()) == 0 { + repl := ethrepl.NewJSRepl(eth) + repl.Start() + utils.RegisterInterrupt(func(os.Signal) { repl.Stop() }) + eth.WaitForShutdown() + } else if len(ctx.Args()) == 1 { + execJsFile(eth, ctx.Args()[0]) + } else { + utils.Fatalf("This command can handle at most one argument.") } +} - utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") - - ethereum, err := eth.New(ð.Config{ - Name: p2p.MakeName(ClientIdentifier, Version), - KeyStore: KeyStore, - DataDir: Datadir, - LogFile: LogFile, - LogLevel: LogLevel, - LogFormat: LogFormat, - MaxPeers: MaxPeer, - Port: OutboundPort, - NAT: NAT, - KeyRing: KeyRing, - Shh: true, - Dial: Dial, - BootNodes: BootNodes, - NodeKey: NodeKey, - MinerThreads: MinerThreads, - }) +func startEth(ctx *cli.Context, eth *eth.Ethereum) { + utils.StartEthereum(eth) + if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { + addr := ctx.GlobalString(utils.RPCListenAddrFlag.Name) + port := ctx.GlobalInt(utils.RPCPortFlag.Name) + utils.StartRpc(eth, addr, port) + } + if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { + eth.Miner().Start() + } +} +func importchain(ctx *cli.Context) { + if len(ctx.Args()) != 1 { + utils.Fatalf("This command requires an argument.") + } + chain, _ := utils.GetChain(ctx) + start := time.Now() + err := utils.ImportChain(chain, ctx.Args().First()) if err != nil { - clilogger.Fatalln(err) + utils.Fatalf("Import error: %v\n", err) } + fmt.Printf("Import done in", time.Since(start)) + return +} - utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive) - - if Dump { +func dump(ctx *cli.Context) { + chain, db := utils.GetChain(ctx) + for _, arg := range ctx.Args() { var block *types.Block - - if len(DumpHash) == 0 && DumpNumber == -1 { - block = ethereum.ChainManager().CurrentBlock() - } else if len(DumpHash) > 0 { - block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash)) + if hashish(arg) { + block = chain.GetBlock(ethutil.Hex2Bytes(arg)) } else { - block = ethereum.ChainManager().GetBlockByNumber(uint64(DumpNumber)) + num, _ := strconv.Atoi(arg) + block = chain.GetBlockByNumber(uint64(num)) } - if block == nil { - fmt.Fprintln(os.Stderr, "block not found") - - // We want to output valid JSON fmt.Println("{}") - - os.Exit(1) - } - - // Leave the Println. This needs clean output for piping - statedb := state.New(block.Root(), ethereum.Db()) - fmt.Printf("%s\n", statedb.Dump()) - - fmt.Println(block) - - return - } - - if len(ImportChain) > 0 { - start := time.Now() - err := utils.ImportChain(ethereum, ImportChain) - if err != nil { - clilogger.Infoln(err) + utils.Fatalf("block not found") + } else { + statedb := state.New(block.Root(), db) + fmt.Printf("%s\n", statedb.Dump()) + // fmt.Println(block) } - clilogger.Infoln("import done in", time.Since(start)) - return - } - - if StartRpc { - utils.StartRpc(ethereum, RpcListenAddress, RpcPort) - } - - utils.StartEthereum(ethereum) - - fmt.Printf("Welcome to the FRONTIER\n") - - if StartMining { - ethereum.Miner().Start() } +} - if StartJsConsole { - repl := ethrepl.NewJSRepl(ethereum) - repl.Start() - utils.RegisterInterrupt(func(os.Signal) { - repl.Stop() - }) - - } else if len(InputFile) > 0 { - ExecJsFile(ethereum, InputFile) - } - // this blocks the thread - ethereum.WaitForShutdown() +// hashish returns true for strings that look like hashes. +func hashish(x string) bool { + _, err := strconv.Atoi(x) + return err != nil } -func printVersion() { +func version(c *cli.Context) { fmt.Printf(`%v %v PV=%d GOOS=%s -- cgit v1.2.3 From 38f6d60e6e699db24b7a850b5999823b9e36d5bb Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 6 Mar 2015 03:38:19 +0100 Subject: cmd/ethereum: new JS repl with cross-platform line editing --- cmd/ethereum/js.go | 207 ++++++++++++++++++++++++++++ cmd/ethereum/main.go | 5 +- cmd/ethereum/repl/console_colors_windows.go | 97 ------------- cmd/ethereum/repl/repl.go | 201 --------------------------- cmd/ethereum/repl/repl_darwin.go | 144 ------------------- cmd/ethereum/repl/repl_linux.go | 1 - cmd/ethereum/repl/repl_windows.go | 92 ------------- 7 files changed, 209 insertions(+), 538 deletions(-) delete mode 100644 cmd/ethereum/repl/console_colors_windows.go delete mode 100644 cmd/ethereum/repl/repl.go delete mode 100644 cmd/ethereum/repl/repl_darwin.go delete mode 120000 cmd/ethereum/repl/repl_linux.go delete mode 100644 cmd/ethereum/repl/repl_windows.go (limited to 'cmd/ethereum') diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index f0aeb45f5..e16ee171e 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -18,13 +18,21 @@ package main import ( + "fmt" "io/ioutil" "os" + "path" + "strings" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/javascript" + "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/xeth" + "github.com/obscuren/otto" + "github.com/peterh/liner" ) func execJsFile(ethereum *eth.Ethereum, filename string) { @@ -41,3 +49,202 @@ func execJsFile(ethereum *eth.Ethereum, filename string) { utils.Fatalf("Javascript Error: %v", err) } } + +type repl struct { + re *javascript.JSRE + ethereum *eth.Ethereum + xeth *xeth.XEth + prompt string + histfile *os.File + lr *liner.State + running bool +} + +func newREPL(ethereum *eth.Ethereum) *repl { + hist, err := os.OpenFile(path.Join(ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + panic(err) + } + xeth := xeth.New(ethereum) + repl := &repl{ + re: javascript.NewJSRE(xeth), + xeth: xeth, + ethereum: ethereum, + prompt: "> ", + histfile: hist, + lr: liner.NewLiner(), + } + repl.initStdFuncs() + return repl +} + +func (self *repl) Start() { + if !self.running { + self.running = true + self.lr.ReadHistory(self.histfile) + go self.read() + } +} + +func (self *repl) Stop() { + if self.running { + self.running = false + self.histfile.Truncate(0) + self.lr.WriteHistory(self.histfile) + self.histfile.Close() + } +} + +func (self *repl) parseInput(code string) { + defer func() { + if r := recover(); r != nil { + fmt.Println("[native] error", r) + } + }() + value, err := self.re.Run(code) + if err != nil { + fmt.Println(err) + return + } + self.printValue(value) +} + +var indentCount = 0 +var str = "" + +func (self *repl) setIndent() { + open := strings.Count(str, "{") + open += strings.Count(str, "(") + closed := strings.Count(str, "}") + closed += strings.Count(str, ")") + indentCount = open - closed + if indentCount <= 0 { + self.prompt = "> " + } else { + self.prompt = strings.Join(make([]string, indentCount*2), "..") + self.prompt += " " + } +} + +func (self *repl) read() { + for { + input, err := self.lr.Prompt(self.prompt) + if err != nil { + return + } + if input == "" { + continue + } + str += input + "\n" + self.setIndent() + if indentCount <= 0 { + if input == "exit" { + self.Stop() + return + } + hist := str[:len(str)-1] + self.lr.AppendHistory(hist) + self.parseInput(str) + str = "" + } + } +} + +func (self *repl) printValue(v interface{}) { + method, _ := self.re.Vm.Get("prettyPrint") + v, err := self.re.Vm.ToValue(v) + if err == nil { + val, err := method.Call(method, v) + if err == nil { + fmt.Printf("%v", val) + } + } +} + +func (self *repl) initStdFuncs() { + t, _ := self.re.Vm.Get("eth") + eth := t.Object() + eth.Set("connect", self.connect) + eth.Set("stopMining", self.stopMining) + eth.Set("startMining", self.startMining) + eth.Set("dump", self.dump) + eth.Set("export", self.export) +} + +/* + * The following methods are natively implemented javascript functions. + */ + +func (self *repl) dump(call otto.FunctionCall) otto.Value { + var block *types.Block + + if len(call.ArgumentList) > 0 { + if call.Argument(0).IsNumber() { + num, _ := call.Argument(0).ToInteger() + block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num)) + } else if call.Argument(0).IsString() { + hash, _ := call.Argument(0).ToString() + block = self.ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(hash)) + } else { + fmt.Println("invalid argument for dump. Either hex string or number") + } + + if block == nil { + fmt.Println("block not found") + + return otto.UndefinedValue() + } + + } else { + block = self.ethereum.ChainManager().CurrentBlock() + } + + statedb := state.New(block.Root(), self.ethereum.Db()) + + v, _ := self.re.Vm.ToValue(statedb.RawDump()) + + return v +} + +func (self *repl) stopMining(call otto.FunctionCall) otto.Value { + self.xeth.Miner().Stop() + return otto.TrueValue() +} + +func (self *repl) startMining(call otto.FunctionCall) otto.Value { + self.xeth.Miner().Start() + return otto.TrueValue() +} + +func (self *repl) connect(call otto.FunctionCall) otto.Value { + nodeURL, err := call.Argument(0).ToString() + if err != nil { + return otto.FalseValue() + } + if err := self.ethereum.SuggestPeer(nodeURL); err != nil { + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (self *repl) export(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("err: require file name") + return otto.FalseValue() + } + + fn, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + data := self.ethereum.ChainManager().Export() + + if err := ethutil.WriteFile(fn, data); err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index a38e012c2..c85caf229 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -28,7 +28,6 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/ethereum/go-ethereum/cmd/ethereum/repl" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -145,9 +144,9 @@ func runjs(ctx *cli.Context) { eth := utils.GetEthereum(ClientIdentifier, Version, ctx) startEth(ctx, eth) if len(ctx.Args()) == 0 { - repl := ethrepl.NewJSRepl(eth) - repl.Start() + repl := newREPL(eth) utils.RegisterInterrupt(func(os.Signal) { repl.Stop() }) + repl.Start() eth.WaitForShutdown() } else if len(ctx.Args()) == 1 { execJsFile(eth, ctx.Args()[0]) diff --git a/cmd/ethereum/repl/console_colors_windows.go b/cmd/ethereum/repl/console_colors_windows.go deleted file mode 100644 index 8062746fb..000000000 --- a/cmd/ethereum/repl/console_colors_windows.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -// -// This library 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 2.1 of the License, or (at your option) any later version. -// -// This 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA - -/* Inspired by https://github.com/xuyu/logging/blob/master/colorful_win.go */ - -package ethrepl - -import ( - "syscall" - "unsafe" -) - -type color uint16 - -const ( - green = color(0x0002) - red = color(0x0004) - yellow = color(0x000E) -) - -const ( - mask = uint16(yellow | green | red) -) - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - procGetStdHandle = kernel32.NewProc("GetStdHandle") - procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - hStdout uintptr - initScreenInfo *consoleScreenBufferInfo -) - -func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { - ret, _, _ := procSetConsoleTextAttribute.Call(hConsoleOutput, uintptr(wAttributes)) - return ret != 0 -} - -type coord struct { - X, Y int16 -} - -type smallRect struct { - Left, Top, Right, Bottom int16 -} - -type consoleScreenBufferInfo struct { - DwSize coord - DwCursorPosition coord - WAttributes uint16 - SrWindow smallRect - DwMaximumWindowSize coord -} - -func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo { - var csbi consoleScreenBufferInfo - ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi))) - if ret == 0 { - return nil - } - return &csbi -} - -const ( - stdOutputHandle = uint32(-11 & 0xFFFFFFFF) -) - -func init() { - hStdout, _, _ = procGetStdHandle.Call(uintptr(stdOutputHandle)) - initScreenInfo = getConsoleScreenBufferInfo(hStdout) -} - -func resetColorful() { - if initScreenInfo == nil { - return - } - setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes) -} - -func changeColor(c color) { - attr := uint16(0) & ^mask | uint16(c) - setConsoleTextAttribute(hStdout, attr) -} diff --git a/cmd/ethereum/repl/repl.go b/cmd/ethereum/repl/repl.go deleted file mode 100644 index 05ea71e79..000000000 --- a/cmd/ethereum/repl/repl.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -// -// This library 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 2.1 of the License, or (at your option) any later version. -// -// This 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA - -package ethrepl - -import ( - "bufio" - "fmt" - "io" - "os" - "path" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/javascript" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/state" - "github.com/ethereum/go-ethereum/xeth" - "github.com/obscuren/otto" -) - -var repllogger = logger.NewLogger("REPL") - -type Repl interface { - Start() - Stop() -} - -type JSRepl struct { - re *javascript.JSRE - ethereum *eth.Ethereum - xeth *xeth.XEth - - prompt string - - history *os.File - - running bool -} - -func NewJSRepl(ethereum *eth.Ethereum) *JSRepl { - hist, err := os.OpenFile(path.Join(ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - panic(err) - } - - xeth := xeth.New(ethereum) - repl := &JSRepl{re: javascript.NewJSRE(xeth), xeth: xeth, ethereum: ethereum, prompt: "> ", history: hist} - repl.initStdFuncs() - - return repl -} - -func (self *JSRepl) Start() { - if !self.running { - self.running = true - repllogger.Infoln("init JS Console") - - reader := bufio.NewReader(self.history) - for { - line, err := reader.ReadString('\n') - if err != nil && err == io.EOF { - break - } else if err != nil { - fmt.Println("error reading history", err) - break - } - - addHistory(line[:len(line)-1]) - } - self.read() - } -} - -func (self *JSRepl) Stop() { - if self.running { - self.running = false - repllogger.Infoln("exit JS Console") - self.history.Close() - } -} - -func (self *JSRepl) parseInput(code string) { - defer func() { - if r := recover(); r != nil { - fmt.Println("[native] error", r) - } - }() - - value, err := self.re.Run(code) - if err != nil { - fmt.Println(err) - return - } - - self.PrintValue(value) -} - -func (self *JSRepl) initStdFuncs() { - t, _ := self.re.Vm.Get("eth") - eth := t.Object() - eth.Set("connect", self.connect) - eth.Set("stopMining", self.stopMining) - eth.Set("startMining", self.startMining) - eth.Set("dump", self.dump) - eth.Set("export", self.export) -} - -/* - * The following methods are natively implemented javascript functions - */ - -func (self *JSRepl) dump(call otto.FunctionCall) otto.Value { - var block *types.Block - - if len(call.ArgumentList) > 0 { - if call.Argument(0).IsNumber() { - num, _ := call.Argument(0).ToInteger() - block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num)) - } else if call.Argument(0).IsString() { - hash, _ := call.Argument(0).ToString() - block = self.ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(hash)) - } else { - fmt.Println("invalid argument for dump. Either hex string or number") - } - - if block == nil { - fmt.Println("block not found") - - return otto.UndefinedValue() - } - - } else { - block = self.ethereum.ChainManager().CurrentBlock() - } - - statedb := state.New(block.Root(), self.ethereum.Db()) - - v, _ := self.re.Vm.ToValue(statedb.RawDump()) - - return v -} - -func (self *JSRepl) stopMining(call otto.FunctionCall) otto.Value { - self.xeth.Miner().Stop() - - return otto.TrueValue() -} - -func (self *JSRepl) startMining(call otto.FunctionCall) otto.Value { - self.xeth.Miner().Start() - return otto.TrueValue() -} - -func (self *JSRepl) connect(call otto.FunctionCall) otto.Value { - nodeURL, err := call.Argument(0).ToString() - if err != nil { - return otto.FalseValue() - } - if err := self.ethereum.SuggestPeer(nodeURL); err != nil { - return otto.FalseValue() - } - return otto.TrueValue() -} - -func (self *JSRepl) export(call otto.FunctionCall) otto.Value { - if len(call.ArgumentList) == 0 { - fmt.Println("err: require file name") - return otto.FalseValue() - } - - fn, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - - data := self.ethereum.ChainManager().Export() - - if err := ethutil.WriteFile(fn, data); err != nil { - fmt.Println(err) - return otto.FalseValue() - } - - return otto.TrueValue() -} diff --git a/cmd/ethereum/repl/repl_darwin.go b/cmd/ethereum/repl/repl_darwin.go deleted file mode 100644 index 3710150cc..000000000 --- a/cmd/ethereum/repl/repl_darwin.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -// -// This library 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 2.1 of the License, or (at your option) any later version. -// -// This 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA - -package ethrepl - -// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include -// #cgo darwin LDFLAGS: -L/usr/local/opt/readline/lib -// #cgo LDFLAGS: -lreadline -// #include -// #include -// #include -// #include -import "C" -import ( - "fmt" - "os" - "os/signal" - "strings" - "syscall" - "unsafe" -) - -func initReadLine() { - C.rl_catch_sigwinch = 0 - C.rl_catch_signals = 0 - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGWINCH) - signal.Notify(c, os.Interrupt) - go func() { - for sig := range c { - switch sig { - case syscall.SIGWINCH: - C.rl_resize_terminal() - - case os.Interrupt: - C.rl_cleanup_after_signal() - default: - - } - } - }() -} - -func readLine(prompt *string) *string { - var p *C.char - - //readline allows an empty prompt(NULL) - if prompt != nil { - p = C.CString(*prompt) - } - - ret := C.readline(p) - - if p != nil { - C.free(unsafe.Pointer(p)) - } - - if ret == nil { - return nil - } //EOF - - s := C.GoString(ret) - C.free(unsafe.Pointer(ret)) - return &s -} - -func addHistory(s string) { - p := C.CString(s) - C.add_history(p) - C.free(unsafe.Pointer(p)) -} - -var indentCount = 0 -var str = "" - -func (self *JSRepl) setIndent() { - open := strings.Count(str, "{") - open += strings.Count(str, "(") - closed := strings.Count(str, "}") - closed += strings.Count(str, ")") - indentCount = open - closed - if indentCount <= 0 { - self.prompt = "> " - } else { - self.prompt = strings.Join(make([]string, indentCount*2), "..") - self.prompt += " " - } -} - -func (self *JSRepl) read() { - initReadLine() -L: - for { - switch result := readLine(&self.prompt); true { - case result == nil: - break L - - case *result != "": - str += *result + "\n" - - self.setIndent() - - if indentCount <= 0 { - if *result == "exit" { - self.Stop() - break L - } - - hist := str[:len(str)-1] - addHistory(hist) //allow user to recall this line - self.history.WriteString(str) - - self.parseInput(str) - - str = "" - } - } - } -} - -func (self *JSRepl) PrintValue(v interface{}) { - method, _ := self.re.Vm.Get("prettyPrint") - v, err := self.re.Vm.ToValue(v) - if err == nil { - val, err := method.Call(method, v) - if err == nil { - fmt.Printf("%v", val) - } - } -} diff --git a/cmd/ethereum/repl/repl_linux.go b/cmd/ethereum/repl/repl_linux.go deleted file mode 120000 index 276f135d7..000000000 --- a/cmd/ethereum/repl/repl_linux.go +++ /dev/null @@ -1 +0,0 @@ -repl_darwin.go \ No newline at end of file diff --git a/cmd/ethereum/repl/repl_windows.go b/cmd/ethereum/repl/repl_windows.go deleted file mode 100644 index d2c405ee9..000000000 --- a/cmd/ethereum/repl/repl_windows.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -// -// This library 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 2.1 of the License, or (at your option) any later version. -// -// This 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA - -package ethrepl - -import ( - "bufio" - "fmt" - "os" - "strings" -) - -func (self *JSRepl) read() { - reader := bufio.NewReader(os.Stdin) - for { - fmt.Printf(self.prompt) - str, _, err := reader.ReadLine() - if err != nil { - fmt.Println("Error reading input", err) - } else { - if string(str) == "exit" { - self.Stop() - break - } else { - self.parseInput(string(str)) - } - } - } -} - -func addHistory(s string) { -} - -func printColored(outputVal string) { - for outputVal != "" { - codePart := "" - if strings.HasPrefix(outputVal, "\033[32m") { - codePart = "\033[32m" - changeColor(2) - } - if strings.HasPrefix(outputVal, "\033[1m\033[30m") { - codePart = "\033[1m\033[30m" - changeColor(8) - } - if strings.HasPrefix(outputVal, "\033[31m") { - codePart = "\033[31m" - changeColor(red) - } - if strings.HasPrefix(outputVal, "\033[35m") { - codePart = "\033[35m" - changeColor(5) - } - if strings.HasPrefix(outputVal, "\033[0m") { - codePart = "\033[0m" - resetColorful() - } - textPart := outputVal[len(codePart):len(outputVal)] - index := strings.Index(textPart, "\033") - if index == -1 { - outputVal = "" - } else { - outputVal = textPart[index:len(textPart)] - textPart = textPart[0:index] - } - fmt.Printf("%v", textPart) - } -} - -func (self *JSRepl) PrintValue(v interface{}) { - method, _ := self.re.Vm.Get("prettyPrint") - v, err := self.re.Vm.ToValue(v) - if err == nil { - val, err := method.Call(method, v) - if err == nil { - printColored(fmt.Sprintf("%v", val)) - } - } -} -- cgit v1.2.3 From de86403f330e68df8fc4aee00df98374b7842d0d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 6 Mar 2015 12:18:44 +0100 Subject: cmd/ethereum: fix JS REPL exit and add support for dumb terminals It is now possible to exit the REPL using Ctrl-C, Ctrl-D or by typing "exit". --- cmd/ethereum/js.go | 84 +++++++++++++++++++++++++++++++++++----------------- cmd/ethereum/main.go | 7 ++--- 2 files changed, 60 insertions(+), 31 deletions(-) (limited to 'cmd/ethereum') diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index e16ee171e..9125ccbba 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -18,9 +18,11 @@ package main import ( + "bufio" "fmt" "io/ioutil" "os" + "os/signal" "path" "strings" @@ -55,44 +57,38 @@ type repl struct { ethereum *eth.Ethereum xeth *xeth.XEth prompt string - histfile *os.File lr *liner.State - running bool } -func newREPL(ethereum *eth.Ethereum) *repl { - hist, err := os.OpenFile(path.Join(ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - panic(err) - } +func runREPL(ethereum *eth.Ethereum) { xeth := xeth.New(ethereum) repl := &repl{ re: javascript.NewJSRE(xeth), xeth: xeth, ethereum: ethereum, prompt: "> ", - histfile: hist, - lr: liner.NewLiner(), } repl.initStdFuncs() - return repl -} - -func (self *repl) Start() { - if !self.running { - self.running = true - self.lr.ReadHistory(self.histfile) - go self.read() + if !liner.TerminalSupported() { + repl.dumbRead() + } else { + lr := liner.NewLiner() + defer lr.Close() + lr.SetCtrlCAborts(true) + repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) + repl.read(lr) + repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) } } -func (self *repl) Stop() { - if self.running { - self.running = false - self.histfile.Truncate(0) - self.lr.WriteHistory(self.histfile) - self.histfile.Close() +func (self *repl) withHistory(op func(*os.File)) { + hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + fmt.Printf("unable to open history file: %v\n", err) + return } + op(hist) + hist.Close() } func (self *repl) parseInput(code string) { @@ -126,9 +122,9 @@ func (self *repl) setIndent() { } } -func (self *repl) read() { +func (self *repl) read(lr *liner.State) { for { - input, err := self.lr.Prompt(self.prompt) + input, err := lr.Prompt(self.prompt) if err != nil { return } @@ -139,17 +135,51 @@ func (self *repl) read() { self.setIndent() if indentCount <= 0 { if input == "exit" { - self.Stop() return } hist := str[:len(str)-1] - self.lr.AppendHistory(hist) + lr.AppendHistory(hist) self.parseInput(str) str = "" } } } +func (self *repl) dumbRead() { + fmt.Println("Unsupported terminal, line editing will not work.") + + // process lines + readDone := make(chan struct{}) + go func() { + r := bufio.NewReader(os.Stdin) + loop: + for { + fmt.Print(self.prompt) + line, err := r.ReadString('\n') + switch { + case err != nil || line == "exit": + break loop + case line == "": + continue + default: + self.parseInput(line + "\n") + } + } + close(readDone) + }() + + // wait for Ctrl-C + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, os.Kill) + defer signal.Stop(sigc) + + select { + case <-readDone: + case <-sigc: + os.Stdin.Close() // terminate read + } +} + func (self *repl) printValue(v interface{}) { method, _ := self.re.Vm.Get("prettyPrint") v, err := self.re.Vm.ToValue(v) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index c85caf229..1133bd6f7 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -125,7 +125,6 @@ runtime will execute the file and exit. func main() { runtime.GOMAXPROCS(runtime.NumCPU()) defer logger.Flush() - utils.HandleInterrupt() if err := app.Run(os.Args); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) @@ -134,6 +133,7 @@ func main() { func run(ctx *cli.Context) { fmt.Printf("Welcome to the FRONTIER\n") + utils.HandleInterrupt() eth := utils.GetEthereum(ClientIdentifier, Version, ctx) startEth(ctx, eth) // this blocks the thread @@ -144,9 +144,8 @@ func runjs(ctx *cli.Context) { eth := utils.GetEthereum(ClientIdentifier, Version, ctx) startEth(ctx, eth) if len(ctx.Args()) == 0 { - repl := newREPL(eth) - utils.RegisterInterrupt(func(os.Signal) { repl.Stop() }) - repl.Start() + runREPL(eth) + eth.Stop() eth.WaitForShutdown() } else if len(ctx.Args()) == 1 { execJsFile(eth, ctx.Args()[0]) -- cgit v1.2.3