From 7279a485c24de3f0aaf839e1884151322a97dbf7 Mon Sep 17 00:00:00 2001 From: zelig Date: Sun, 15 Mar 2015 13:31:40 +0700 Subject: [PATCH] CLI: - js subcommand for vm - console for Frontier console interactive REPL - jspath in cli - integrate jeth apiBindings --- cmd/ethereum/js.go | 167 ++++++++++++++----------------------------- cmd/ethereum/main.go | 52 +++++++++----- cmd/utils/flags.go | 5 ++ 3 files changed, 93 insertions(+), 131 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 3b98b588e..361aaf7ea 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -20,18 +20,16 @@ package main import ( "bufio" "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" + re "github.com/ethereum/go-ethereum/jsre" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/xeth" - "github.com/obscuren/otto" "github.com/peterh/liner" ) @@ -59,7 +57,7 @@ func (r dumbterm) PasswordPrompt(p string) (string, error) { func (r dumbterm) AppendHistory(string) {} type jsre struct { - re *javascript.JSRE + re *re.JSRE ethereum *eth.Ethereum xeth *xeth.XEth ps1 string @@ -68,11 +66,11 @@ type jsre struct { prompter } -func newJSRE(ethereum *eth.Ethereum) *jsre { +func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} js.xeth = xeth.New(ethereum, js) - js.re = javascript.NewJSRE(js.xeth) - js.initStdFuncs() + js.re = re.New(libPath) + js.apiBindings() if !liner.TerminalSupported() { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} @@ -89,6 +87,49 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { return js } +func (js *jsre) apiBindings() { + + ethApi := rpc.NewEthereumApi(js.xeth, js.ethereum.DataDir) + js.re.Bind("jeth", rpc.NewJeth(ethApi, js.re.ToVal)) + + _, err := js.re.Eval(re.BigNumber_JS) + + if err != nil { + utils.Fatalf("Error loading bignumber.js: %v", err) + } + + // we need to declare a dummy setTimeout. Otto does not support it + _, err = js.re.Eval("setTimeout = function(cb, delay) {};") + if err != nil { + utils.Fatalf("Error defining setTimeout: %v", err) + } + + _, err = js.re.Eval(re.Ethereum_JS) + if err != nil { + utils.Fatalf("Error loading ethereum.js: %v", err) + } + + _, err = js.re.Eval("var web3 = require('web3');") + if err != nil { + utils.Fatalf("Error requiring web3: %v", err) + } + + _, err = js.re.Eval("web3.setProvider(jeth)") + if err != nil { + utils.Fatalf("Error setting web3 provider: %v", err) + } + _, err = js.re.Eval(` + var eth = web3.eth; + var shh = web3.shh; + var db = web3.db; + var net = web3.net; + `) + if err != nil { + utils.Fatalf("Error setting namespaces: %v", err) + } + +} + func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool { p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) answer, _ := self.Prompt(p) @@ -111,15 +152,7 @@ func (self *jsre) UnlockAccount(addr []byte) bool { } func (self *jsre) exec(filename string) error { - file, err := os.Open(filename) - if err != nil { - return err - } - content, err := ioutil.ReadAll(file) - if err != nil { - return err - } - if _, err := self.re.Run(string(content)); err != nil { + if err := self.re.Exec(filename); err != nil { return fmt.Errorf("Javascript Error: %v", err) } return nil @@ -193,102 +226,8 @@ func (self *jsre) setIndent() { } func (self *jsre) printValue(v interface{}) { - method, _ := self.re.Vm.Get("prettyPrint") - v, err := self.re.Vm.ToValue(v) + val, err := self.re.PrettyPrint(v) if err == nil { - val, err := method.Call(method, v) - if err == nil { - fmt.Printf("%v", val) - } + fmt.Printf("%v", val) } } - -func (self *jsre) 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 *jsre) 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.StateDb()) - - v, _ := self.re.Vm.ToValue(statedb.RawDump()) - - return v -} - -func (self *jsre) stopMining(call otto.FunctionCall) otto.Value { - self.ethereum.StopMining() - return otto.TrueValue() -} - -func (self *jsre) startMining(call otto.FunctionCall) otto.Value { - if err := self.ethereum.StartMining(); err != nil { - return otto.FalseValue() - } - return otto.TrueValue() -} - -func (self *jsre) 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 *jsre) 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 53746627a..0dae5b79b 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -89,16 +89,20 @@ Use "ethereum dump 0" to dump the genesis block. `, }, { - Action: runjs, - Name: "js", - Usage: `interactive JavaScript console`, + Action: console, + Name: "console", + Usage: `Ethereum Frontier Console: interactive JavaScript environment`, 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. +Frontier Console is an interactive shell for the Ethereum Frontier JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API. +See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console +`, + }, + { + Action: execJSFiles, + Name: "js", + Usage: `executes the given JavaScript files in the Ethereum Frontier JavaScript VM`, + Description: ` +The Ethereum Frontier JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console `, }, { @@ -116,6 +120,7 @@ runtime will execute the file and exit. utils.UnlockedAccountFlag, utils.BootnodesFlag, utils.DataDirFlag, + utils.JSpathFlag, utils.ListenPortFlag, utils.LogFileFlag, utils.LogFormatFlag, @@ -131,6 +136,7 @@ runtime will execute the file and exit. utils.RPCPortFlag, utils.UnencryptedKeysFlag, utils.VMDebugFlag, + //utils.VMTypeFlag, } @@ -168,7 +174,7 @@ func run(ctx *cli.Context) { ethereum.WaitForShutdown() } -func runjs(ctx *cli.Context) { +func console(ctx *cli.Context) { cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) ethereum, err := eth.New(cfg) if err != nil { @@ -176,14 +182,26 @@ func runjs(ctx *cli.Context) { } startEth(ctx, ethereum) - repl := newJSRE(ethereum) - if len(ctx.Args()) == 0 { - repl.interactive() - } else { - for _, file := range ctx.Args() { - repl.exec(file) - } + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) + repl.interactive() + + ethereum.Stop() + ethereum.WaitForShutdown() +} + +func execJSFiles(ctx *cli.Context) { + cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) + ethereum, err := eth.New(cfg) + if err != nil { + utils.Fatalf("%v", err) } + + startEth(ctx, ethereum) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) + for _, file := range ctx.Args() { + repl.exec(file) + } + ethereum.Stop() ethereum.WaitForShutdown() } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 6bcd7e811..563654441 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -163,6 +163,11 @@ var ( Usage: "Port mapping mechanism (any|none|upnp|pmp|extip:)", Value: "any", } + JSpathFlag = cli.StringFlag{ + Name: "jspath", + Usage: "JS library path to be used with console and js subcommands", + Value: ".", + } ) func GetNAT(ctx *cli.Context) nat.Interface {