|
|
|
@ -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) |
|
|
|
|
type prompter interface { |
|
|
|
|
AppendHistory(string) |
|
|
|
|
Prompt(p string) (string, error) |
|
|
|
|
PasswordPrompt(p string) (string, error) |
|
|
|
|
} |
|
|
|
|
re := javascript.NewJSRE(xeth.New(ethereum, nil)) |
|
|
|
|
if _, err := re.Run(string(content)); err != nil { |
|
|
|
|
utils.Fatalf("Javascript Error: %v", err) |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type repl struct { |
|
|
|
|
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) |
|
|
|
|
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 { |
|
|
|
|
fmt.Printf("unable to open history file: %v\n", 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 |
|
|
|
|
} |
|
|
|
|
op(hist) |
|
|
|
|
hist.Close() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (self *repl) parseInput(code string) { |
|
|
|
|
defer func() { |
|
|
|
|
if r := recover(); r != nil { |
|
|
|
|
fmt.Println("[native] error", r) |
|
|
|
|
func (self *jsre) exec(filename string) error { |
|
|
|
|
file, err := os.Open(filename) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
value, err := self.re.Run(code) |
|
|
|
|
content, err := ioutil.ReadAll(file) |
|
|
|
|
if err != nil { |
|
|
|
|
fmt.Println(err) |
|
|
|
|
return |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
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 += " " |
|
|
|
|
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() |
|
|
|
|