- js subcommand for vm
- console for Frontier console interactive REPL
- jspath in cli
- integrate jeth apiBindings
This commit is contained in:
zelig 2015-03-15 13:31:40 +07:00
parent 16ecb1e2ea
commit 7279a485c2
3 changed files with 93 additions and 131 deletions

View File

@ -20,18 +20,16 @@ package main
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path" "path"
"strings" "strings"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"github.com/obscuren/otto"
"github.com/peterh/liner" "github.com/peterh/liner"
) )
@ -59,7 +57,7 @@ func (r dumbterm) PasswordPrompt(p string) (string, error) {
func (r dumbterm) AppendHistory(string) {} func (r dumbterm) AppendHistory(string) {}
type jsre struct { type jsre struct {
re *javascript.JSRE re *re.JSRE
ethereum *eth.Ethereum ethereum *eth.Ethereum
xeth *xeth.XEth xeth *xeth.XEth
ps1 string ps1 string
@ -68,11 +66,11 @@ type jsre struct {
prompter prompter
} }
func newJSRE(ethereum *eth.Ethereum) *jsre { func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "} js := &jsre{ethereum: ethereum, ps1: "> "}
js.xeth = xeth.New(ethereum, js) js.xeth = xeth.New(ethereum, js)
js.re = javascript.NewJSRE(js.xeth) js.re = re.New(libPath)
js.initStdFuncs() js.apiBindings()
if !liner.TerminalSupported() { if !liner.TerminalSupported() {
js.prompter = dumbterm{bufio.NewReader(os.Stdin)} js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
@ -89,6 +87,49 @@ func newJSRE(ethereum *eth.Ethereum) *jsre {
return js 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 { func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool {
p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx)
answer, _ := self.Prompt(p) answer, _ := self.Prompt(p)
@ -111,15 +152,7 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
} }
func (self *jsre) exec(filename string) error { func (self *jsre) exec(filename string) error {
file, err := os.Open(filename) if err := self.re.Exec(filename); err != nil {
if err != nil {
return err
}
content, err := ioutil.ReadAll(file)
if err != nil {
return err
}
if _, err := self.re.Run(string(content)); err != nil {
return fmt.Errorf("Javascript Error: %v", err) return fmt.Errorf("Javascript Error: %v", err)
} }
return nil return nil
@ -193,102 +226,8 @@ func (self *jsre) setIndent() {
} }
func (self *jsre) printValue(v interface{}) { func (self *jsre) printValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint") val, err := self.re.PrettyPrint(v)
v, err := self.re.Vm.ToValue(v)
if err == nil { if err == nil {
val, err := method.Call(method, v) fmt.Printf("%v", val)
if err == nil {
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()
}

View File

@ -89,16 +89,20 @@ Use "ethereum dump 0" to dump the genesis block.
`, `,
}, },
{ {
Action: runjs, Action: console,
Name: "js", Name: "console",
Usage: `interactive JavaScript console`, Usage: `Ethereum Frontier Console: interactive JavaScript environment`,
Description: ` Description: `
In the console, you can use the eth object to interact 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.
with the running ethereum stack. The API does not match See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console
ethereum.js. `,
},
A JavaScript file can be provided as the argument. The {
runtime will execute the file and exit. 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.UnlockedAccountFlag,
utils.BootnodesFlag, utils.BootnodesFlag,
utils.DataDirFlag, utils.DataDirFlag,
utils.JSpathFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.LogFileFlag, utils.LogFileFlag,
utils.LogFormatFlag, utils.LogFormatFlag,
@ -131,6 +136,7 @@ runtime will execute the file and exit.
utils.RPCPortFlag, utils.RPCPortFlag,
utils.UnencryptedKeysFlag, utils.UnencryptedKeysFlag,
utils.VMDebugFlag, utils.VMDebugFlag,
//utils.VMTypeFlag, //utils.VMTypeFlag,
} }
@ -168,7 +174,7 @@ func run(ctx *cli.Context) {
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
} }
func runjs(ctx *cli.Context) { func console(ctx *cli.Context) {
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
ethereum, err := eth.New(cfg) ethereum, err := eth.New(cfg)
if err != nil { if err != nil {
@ -176,14 +182,26 @@ func runjs(ctx *cli.Context) {
} }
startEth(ctx, ethereum) startEth(ctx, ethereum)
repl := newJSRE(ethereum) repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name))
if len(ctx.Args()) == 0 { repl.interactive()
repl.interactive()
} else { ethereum.Stop()
for _, file := range ctx.Args() { ethereum.WaitForShutdown()
repl.exec(file) }
}
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.Stop()
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
} }

View File

@ -163,6 +163,11 @@ var (
Usage: "Port mapping mechanism (any|none|upnp|pmp|extip:<IP>)", Usage: "Port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
Value: "any", 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 { func GetNAT(ctx *cli.Context) nat.Interface {