cmd/ethereum: unlock accounts on JS REPL

pull/447/head
Felix Lange 10 years ago
parent 395da0e7c1
commit 9f0e3bd286
  1. 210
      cmd/ethereum/js.go
  2. 5
      cmd/ethereum/main.go

@ -22,11 +22,9 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/signal"
"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" "github.com/ethereum/go-ethereum/ethutil"
@ -37,94 +35,96 @@ import (
"github.com/peterh/liner" "github.com/peterh/liner"
) )
func execJsFile(ethereum *eth.Ethereum, filename string) { type prompter interface {
file, err := os.Open(filename) AppendHistory(string)
if err != nil { Prompt(p string) (string, error)
utils.Fatalf("%v", err) PasswordPrompt(p string) (string, error)
} }
content, err := ioutil.ReadAll(file)
if err != nil { type dumbPrompter struct{ r *bufio.Reader }
utils.Fatalf("%v", err)
} func (r dumbPrompter) Prompt(p string) (string, error) {
re := javascript.NewJSRE(xeth.New(ethereum, nil)) fmt.Print(p)
if _, err := re.Run(string(content)); err != nil { return r.r.ReadString('\n')
utils.Fatalf("Javascript Error: %v", err)
}
} }
type repl struct { func (r dumbPrompter) PasswordPrompt(p string) (string, error) {
fmt.Println("!! Unsupported terminal, password will echo.")
fmt.Print(p)
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
fmt.Println()
return input, err
}
func (r dumbPrompter) AppendHistory(string) {}
type jsre struct {
re *javascript.JSRE re *javascript.JSRE
ethereum *eth.Ethereum ethereum *eth.Ethereum
xeth *xeth.XEth xeth *xeth.XEth
prompt string ps1 string
lr *liner.State prompter
} }
func runREPL(ethereum *eth.Ethereum) { func newJSRE(ethereum *eth.Ethereum) *jsre {
xeth := xeth.New(ethereum, nil) js := &jsre{ethereum: ethereum, ps1: "> "}
repl := &repl{ js.xeth = xeth.New(ethereum, js)
re: javascript.NewJSRE(xeth), js.re = javascript.NewJSRE(js.xeth)
xeth: xeth, js.initStdFuncs()
ethereum: ethereum,
prompt: "> ",
}
repl.initStdFuncs()
if !liner.TerminalSupported() { if !liner.TerminalSupported() {
repl.dumbRead() js.prompter = dumbPrompter{bufio.NewReader(os.Stdin)}
} else { } else {
lr := liner.NewLiner() lr := liner.NewLiner()
defer lr.Close()
lr.SetCtrlCAborts(true) lr.SetCtrlCAborts(true)
repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) defer lr.Close()
repl.read(lr) js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) defer js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
js.prompter = lr
} }
return js
} }
func (self *repl) withHistory(op func(*os.File)) { func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool {
hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx)
if err != nil { answer, _ := self.prompter.Prompt(p)
fmt.Printf("unable to open history file: %v\n", err) return strings.HasPrefix(strings.Trim(answer, " "), "y")
return
}
op(hist)
hist.Close()
} }
func (self *repl) parseInput(code string) { func (self *jsre) UnlockAccount(addr []byte) bool {
defer func() { fmt.Printf("Please unlock account %x.\n", addr)
if r := recover(); r != nil { pass, err := self.prompter.PasswordPrompt("Passphrase: ")
fmt.Println("[native] error", r)
}
}()
value, err := self.re.Run(code)
if err != nil { if err != nil {
fmt.Println(err) return false
return }
// TODO: allow retry
if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil {
fmt.Println("Unlocking failed: ", err)
return false
} else {
fmt.Println("Account is now unlocked for this session.")
return true
} }
self.printValue(value)
} }
var indentCount = 0 func (self *jsre) exec(filename string) error {
var str = "" file, err := os.Open(filename)
if err != nil {
func (self *repl) setIndent() { return err
open := strings.Count(str, "{") }
open += strings.Count(str, "(") content, err := ioutil.ReadAll(file)
closed := strings.Count(str, "}") if err != nil {
closed += strings.Count(str, ")") return err
indentCount = open - closed
if indentCount <= 0 {
self.prompt = "> "
} else {
self.prompt = strings.Join(make([]string, indentCount*2), "..")
self.prompt += " "
} }
if _, err := self.re.Run(string(content)); err != nil {
return fmt.Errorf("Javascript Error: %v", err)
}
return nil
} }
func (self *repl) read(lr *liner.State) { func (self *jsre) interactive() {
for { for {
input, err := lr.Prompt(self.prompt) input, err := self.prompter.Prompt(self.ps1)
if err != nil { if err != nil {
return return
} }
@ -138,49 +138,55 @@ func (self *repl) read(lr *liner.State) {
return return
} }
hist := str[:len(str)-1] hist := str[:len(str)-1]
lr.AppendHistory(hist) self.prompter.AppendHistory(hist)
self.parseInput(str) self.parseInput(str)
str = "" str = ""
} }
} }
} }
func (self *repl) dumbRead() { func (self *jsre) withHistory(op func(*os.File)) {
fmt.Println("Unsupported terminal, line editing will not work.") hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
// process lines fmt.Printf("unable to open history file: %v\n", err)
readDone := make(chan struct{}) return
go func() { }
r := bufio.NewReader(os.Stdin) op(hist)
loop: hist.Close()
for { }
fmt.Print(self.prompt)
line, err := r.ReadString('\n') func (self *jsre) parseInput(code string) {
switch { defer func() {
case err != nil || line == "exit": if r := recover(); r != nil {
break loop fmt.Println("[native] error", r)
case line == "":
continue
default:
self.parseInput(line + "\n")
}
} }
close(readDone)
}() }()
value, err := self.re.Run(code)
if err != nil {
fmt.Println(err)
return
}
self.printValue(value)
}
// wait for Ctrl-C var indentCount = 0
sigc := make(chan os.Signal, 1) var str = ""
signal.Notify(sigc, os.Interrupt, os.Kill)
defer signal.Stop(sigc)
select { func (self *jsre) setIndent() {
case <-readDone: open := strings.Count(str, "{")
case <-sigc: open += strings.Count(str, "(")
os.Stdin.Close() // terminate read closed := strings.Count(str, "}")
closed += strings.Count(str, ")")
indentCount = open - closed
if indentCount <= 0 {
self.ps1 = "> "
} else {
self.ps1 = strings.Join(make([]string, indentCount*2), "..")
self.ps1 += " "
} }
} }
func (self *repl) printValue(v interface{}) { func (self *jsre) printValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint") method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v) v, err := self.re.Vm.ToValue(v)
if err == nil { if err == nil {
@ -191,7 +197,7 @@ func (self *repl) printValue(v interface{}) {
} }
} }
func (self *repl) initStdFuncs() { func (self *jsre) initStdFuncs() {
t, _ := self.re.Vm.Get("eth") t, _ := self.re.Vm.Get("eth")
eth := t.Object() eth := t.Object()
eth.Set("connect", self.connect) eth.Set("connect", self.connect)
@ -205,7 +211,7 @@ func (self *repl) initStdFuncs() {
* The following methods are natively implemented javascript functions. * The following methods are natively implemented javascript functions.
*/ */
func (self *repl) dump(call otto.FunctionCall) otto.Value { func (self *jsre) dump(call otto.FunctionCall) otto.Value {
var block *types.Block var block *types.Block
if len(call.ArgumentList) > 0 { if len(call.ArgumentList) > 0 {
@ -236,17 +242,17 @@ func (self *repl) dump(call otto.FunctionCall) otto.Value {
return v return v
} }
func (self *repl) stopMining(call otto.FunctionCall) otto.Value { func (self *jsre) stopMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Stop() self.xeth.Miner().Stop()
return otto.TrueValue() return otto.TrueValue()
} }
func (self *repl) startMining(call otto.FunctionCall) otto.Value { func (self *jsre) startMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Start() self.xeth.Miner().Start()
return otto.TrueValue() return otto.TrueValue()
} }
func (self *repl) connect(call otto.FunctionCall) otto.Value { func (self *jsre) connect(call otto.FunctionCall) otto.Value {
nodeURL, err := call.Argument(0).ToString() nodeURL, err := call.Argument(0).ToString()
if err != nil { if err != nil {
return otto.FalseValue() return otto.FalseValue()
@ -257,7 +263,7 @@ func (self *repl) connect(call otto.FunctionCall) otto.Value {
return otto.TrueValue() return otto.TrueValue()
} }
func (self *repl) export(call otto.FunctionCall) otto.Value { func (self *jsre) export(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 { if len(call.ArgumentList) == 0 {
fmt.Println("err: require file name") fmt.Println("err: require file name")
return otto.FalseValue() return otto.FalseValue()

@ -157,11 +157,12 @@ func run(ctx *cli.Context) {
func runjs(ctx *cli.Context) { func runjs(ctx *cli.Context) {
eth := utils.GetEthereum(ClientIdentifier, Version, ctx) eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
startEth(ctx, eth) startEth(ctx, eth)
repl := newJSRE(eth)
if len(ctx.Args()) == 0 { if len(ctx.Args()) == 0 {
runREPL(eth) repl.interactive()
} else { } else {
for _, file := range ctx.Args() { for _, file := range ctx.Args() {
execJsFile(eth, file) repl.exec(file)
} }
} }
eth.Stop() eth.Stop()

Loading…
Cancel
Save