mirror of https://github.com/status-im/op-geth.git
cmd/ethereum: unlock accounts on JS REPL
This commit is contained in:
parent
395da0e7c1
commit
9f0e3bd286
|
@ -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,51 +35,117 @@ 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 {
|
|
||||||
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 repl struct {
|
type dumbPrompter struct{ r *bufio.Reader }
|
||||||
|
|
||||||
|
func (r dumbPrompter) Prompt(p string) (string, error) {
|
||||||
|
fmt.Print(p)
|
||||||
|
return r.r.ReadString('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
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 *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 *jsre) UnlockAccount(addr []byte) bool {
|
||||||
|
fmt.Printf("Please unlock account %x.\n", addr)
|
||||||
|
pass, err := self.prompter.PasswordPrompt("Passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *repl) withHistory(op func(*os.File)) {
|
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 *jsre) interactive() {
|
||||||
|
for {
|
||||||
|
input, err := self.prompter.Prompt(self.ps1)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if input == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
str += input + "\n"
|
||||||
|
self.setIndent()
|
||||||
|
if indentCount <= 0 {
|
||||||
|
if input == "exit" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hist := str[:len(str)-1]
|
||||||
|
self.prompter.AppendHistory(hist)
|
||||||
|
self.parseInput(str)
|
||||||
|
str = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unable to open history file: %v\n", err)
|
fmt.Printf("unable to open history file: %v\n", err)
|
||||||
|
@ -91,7 +155,7 @@ func (self *repl) withHistory(op func(*os.File)) {
|
||||||
hist.Close()
|
hist.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *repl) parseInput(code string) {
|
func (self *jsre) parseInput(code string) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
fmt.Println("[native] error", r)
|
fmt.Println("[native] error", r)
|
||||||
|
@ -108,79 +172,21 @@ func (self *repl) parseInput(code string) {
|
||||||
var indentCount = 0
|
var indentCount = 0
|
||||||
var str = ""
|
var str = ""
|
||||||
|
|
||||||
func (self *repl) setIndent() {
|
func (self *jsre) setIndent() {
|
||||||
open := strings.Count(str, "{")
|
open := strings.Count(str, "{")
|
||||||
open += strings.Count(str, "(")
|
open += strings.Count(str, "(")
|
||||||
closed := strings.Count(str, "}")
|
closed := strings.Count(str, "}")
|
||||||
closed += strings.Count(str, ")")
|
closed += strings.Count(str, ")")
|
||||||
indentCount = open - closed
|
indentCount = open - closed
|
||||||
if indentCount <= 0 {
|
if indentCount <= 0 {
|
||||||
self.prompt = "> "
|
self.ps1 = "> "
|
||||||
} else {
|
} else {
|
||||||
self.prompt = strings.Join(make([]string, indentCount*2), "..")
|
self.ps1 = strings.Join(make([]string, indentCount*2), "..")
|
||||||
self.prompt += " "
|
self.ps1 += " "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *repl) read(lr *liner.State) {
|
func (self *jsre) printValue(v interface{}) {
|
||||||
for {
|
|
||||||
input, err := lr.Prompt(self.prompt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if input == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
str += input + "\n"
|
|
||||||
self.setIndent()
|
|
||||||
if indentCount <= 0 {
|
|
||||||
if input == "exit" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hist := str[:len(str)-1]
|
|
||||||
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")
|
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…
Reference in New Issue