From 9f0e3bd286472f85ab2457fc19cd48cdf12df110 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:00:57 +0100 Subject: cmd/ethereum: unlock accounts on JS REPL --- cmd/ethereum/js.go | 210 +++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 102 deletions(-) (limited to 'cmd/ethereum/js.go') diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index e3165d3f5..96e292733 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -22,11 +22,9 @@ import ( "fmt" "io/ioutil" "os" - "os/signal" "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" @@ -37,94 +35,96 @@ import ( "github.com/peterh/liner" ) -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, nil)) - if _, err := re.Run(string(content)); err != nil { - utils.Fatalf("Javascript Error: %v", err) - } +type prompter interface { + AppendHistory(string) + Prompt(p string) (string, error) + PasswordPrompt(p string) (string, error) +} + +type dumbPrompter struct{ r *bufio.Reader } + +func (r dumbPrompter) Prompt(p string) (string, error) { + fmt.Print(p) + return r.r.ReadString('\n') } -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 ethereum *eth.Ethereum xeth *xeth.XEth - prompt string - lr *liner.State + ps1 string + prompter } -func runREPL(ethereum *eth.Ethereum) { - xeth := xeth.New(ethereum, nil) - repl := &repl{ - re: javascript.NewJSRE(xeth), - xeth: xeth, - ethereum: ethereum, - prompt: "> ", - } - repl.initStdFuncs() +func newJSRE(ethereum *eth.Ethereum) *jsre { + js := &jsre{ethereum: ethereum, ps1: "> "} + js.xeth = xeth.New(ethereum, js) + js.re = javascript.NewJSRE(js.xeth) + js.initStdFuncs() + if !liner.TerminalSupported() { - repl.dumbRead() + js.prompter = dumbPrompter{bufio.NewReader(os.Stdin)} } 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) }) + defer lr.Close() + js.withHistory(func(hist *os.File) { lr.ReadHistory(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)) { - 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 *jsre) ConfirmTransaction(tx *types.Transaction) bool { + p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) + answer, _ := self.prompter.Prompt(p) + return strings.HasPrefix(strings.Trim(answer, " "), "y") } -func (self *repl) parseInput(code string) { - defer func() { - if r := recover(); r != nil { - fmt.Println("[native] error", r) - } - }() - value, err := self.re.Run(code) +func (self *jsre) UnlockAccount(addr []byte) bool { + fmt.Printf("Please unlock account %x.\n", addr) + pass, err := self.prompter.PasswordPrompt("Passphrase: ") if err != nil { - fmt.Println(err) - return + return false + } + // 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 -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 *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 { + return fmt.Errorf("Javascript Error: %v", err) + } + return nil } -func (self *repl) read(lr *liner.State) { +func (self *jsre) interactive() { for { - input, err := lr.Prompt(self.prompt) + input, err := self.prompter.Prompt(self.ps1) if err != nil { return } @@ -138,49 +138,55 @@ func (self *repl) read(lr *liner.State) { return } hist := str[:len(str)-1] - lr.AppendHistory(hist) + self.prompter.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") - } +func (self *jsre) 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 *jsre) parseInput(code string) { + defer func() { + if r := recover(); r != nil { + fmt.Println("[native] error", r) } - close(readDone) }() + value, err := self.re.Run(code) + if err != nil { + fmt.Println(err) + return + } + self.printValue(value) +} - // wait for Ctrl-C - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, os.Kill) - defer signal.Stop(sigc) +var indentCount = 0 +var str = "" - select { - case <-readDone: - case <-sigc: - os.Stdin.Close() // terminate read +func (self *jsre) setIndent() { + open := strings.Count(str, "{") + open += strings.Count(str, "(") + 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") v, err := self.re.Vm.ToValue(v) 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") eth := t.Object() eth.Set("connect", self.connect) @@ -205,7 +211,7 @@ func (self *repl) initStdFuncs() { * 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 if len(call.ArgumentList) > 0 { @@ -236,17 +242,17 @@ func (self *repl) dump(call otto.FunctionCall) otto.Value { return v } -func (self *repl) stopMining(call otto.FunctionCall) otto.Value { +func (self *jsre) stopMining(call otto.FunctionCall) otto.Value { self.xeth.Miner().Stop() 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() 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() if err != nil { return otto.FalseValue() @@ -257,7 +263,7 @@ func (self *repl) connect(call otto.FunctionCall) otto.Value { 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 { fmt.Println("err: require file name") return otto.FalseValue() -- cgit v1.2.3 From ae38871a54b1ccb51346ce5d99beb863b7febe66 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:33:59 +0100 Subject: cmd/ethereum: remove "prompter" in identifiers --- cmd/ethereum/js.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'cmd/ethereum/js.go') diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 96e292733..de73e83a2 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -41,14 +41,14 @@ type prompter interface { PasswordPrompt(p string) (string, error) } -type dumbPrompter struct{ r *bufio.Reader } +type dumbterm struct{ r *bufio.Reader } -func (r dumbPrompter) Prompt(p string) (string, error) { +func (r dumbterm) Prompt(p string) (string, error) { fmt.Print(p) return r.r.ReadString('\n') } -func (r dumbPrompter) PasswordPrompt(p string) (string, error) { +func (r dumbterm) PasswordPrompt(p string) (string, error) { fmt.Println("!! Unsupported terminal, password will echo.") fmt.Print(p) input, err := bufio.NewReader(os.Stdin).ReadString('\n') @@ -56,13 +56,14 @@ func (r dumbPrompter) PasswordPrompt(p string) (string, error) { return input, err } -func (r dumbPrompter) AppendHistory(string) {} +func (r dumbterm) AppendHistory(string) {} type jsre struct { re *javascript.JSRE ethereum *eth.Ethereum xeth *xeth.XEth ps1 string + prompter } @@ -73,7 +74,7 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { js.initStdFuncs() if !liner.TerminalSupported() { - js.prompter = dumbPrompter{bufio.NewReader(os.Stdin)} + js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() lr.SetCtrlCAborts(true) @@ -87,13 +88,13 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool { p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) - answer, _ := self.prompter.Prompt(p) + answer, _ := self.Prompt(p) return strings.HasPrefix(strings.Trim(answer, " "), "y") } func (self *jsre) UnlockAccount(addr []byte) bool { fmt.Printf("Please unlock account %x.\n", addr) - pass, err := self.prompter.PasswordPrompt("Passphrase: ") + pass, err := self.PasswordPrompt("Passphrase: ") if err != nil { return false } @@ -124,7 +125,7 @@ func (self *jsre) exec(filename string) error { func (self *jsre) interactive() { for { - input, err := self.prompter.Prompt(self.ps1) + input, err := self.Prompt(self.ps1) if err != nil { return } @@ -138,7 +139,7 @@ func (self *jsre) interactive() { return } hist := str[:len(str)-1] - self.prompter.AppendHistory(hist) + self.AppendHistory(hist) self.parseInput(str) str = "" } -- cgit v1.2.3 From 0395c174ca5221dd936c19143ced454dac56d54a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 14:31:54 +0100 Subject: cmd/ethereum: fix line editing setup and history --- cmd/ethereum/js.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'cmd/ethereum/js.go') diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index de73e83a2..5432fb9b1 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -63,6 +63,7 @@ type jsre struct { ethereum *eth.Ethereum xeth *xeth.XEth ps1 string + atexit func() prompter } @@ -77,11 +78,13 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() - lr.SetCtrlCAborts(true) - defer lr.Close() js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) - defer js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.SetCtrlCAborts(true) js.prompter = lr + js.atexit = func() { + js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.Close() + } } return js } @@ -100,7 +103,6 @@ func (self *jsre) UnlockAccount(addr []byte) bool { } // 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.") @@ -127,7 +129,7 @@ func (self *jsre) interactive() { for { input, err := self.Prompt(self.ps1) if err != nil { - return + break } if input == "" { continue @@ -136,7 +138,7 @@ func (self *jsre) interactive() { self.setIndent() if indentCount <= 0 { if input == "exit" { - return + break } hist := str[:len(str)-1] self.AppendHistory(hist) @@ -144,6 +146,9 @@ func (self *jsre) interactive() { str = "" } } + if self.atexit != nil { + self.atexit() + } } func (self *jsre) withHistory(op func(*os.File)) { -- cgit v1.2.3