From 3a2bddc160ece4dcb6d2d5bcc85091d244e774c0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 15 May 2014 14:06:06 +0200 Subject: [PATCH 01/16] Refactored to reactor. Fixes #42 --- ethereal/ui/gui.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index aa03649989..c4cc1373f6 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -178,13 +178,14 @@ func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) { // Simple go routine function that updates the list of peers in the GUI func (gui *Gui) update() { - blockChan := make(chan ethutil.React, 1) reactor := gui.eth.Reactor() - reactor.Subscribe("newBlock", blockChan) + blockChan := make(chan ethutil.React, 1) + txChan := make(chan ethutil.React, 1) - txChan := make(chan ethchain.TxMsg, 1) - gui.eth.TxPool().Subscribe(txChan) + reactor.Subscribe("newBlock", blockChan) + reactor.Subscribe("newTx:pre", txChan) + reactor.Subscribe("newTx:post", txChan) state := gui.eth.StateManager().TransState() @@ -200,9 +201,9 @@ func (gui *Gui) update() { } case txMsg := <-txChan: - tx := txMsg.Tx + tx := txMsg.Resource.(*ethchain.Transaction) - if txMsg.Type == ethchain.TxPre { + if txMsg.Event == "newTx:pre" { object := state.GetAccount(gui.addr) if bytes.Compare(tx.Sender(), gui.addr) == 0 && object.Nonce <= tx.Nonce { From cbce882f5e35300016055cde81eeccb3ae052671 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 15 May 2014 20:45:19 +0200 Subject: [PATCH 02/16] Basic javascript console --- ethereum/config.go | 2 + ethereum/ethereum.go | 7 ++++ ethereum/javascript_console.go | 76 ++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 ethereum/javascript_console.go diff --git a/ethereum/config.go b/ethereum/config.go index 7ca1a9801f..4d7ea63107 100644 --- a/ethereum/config.go +++ b/ethereum/config.go @@ -20,6 +20,7 @@ var ExportKey bool var LogFile string var DataDir string var NonInteractive bool +var StartExp bool func Init() { flag.BoolVar(&StartConsole, "c", false, "debug and testing console") @@ -38,6 +39,7 @@ func Init() { flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") + flag.BoolVar(&StartExp, "ex", false, "exp") flag.Parse() } diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 2abf6da42a..8b42c2a2c8 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -146,6 +146,13 @@ save these words so you can restore your account later: %s console := NewConsole(ethereum) go console.Start() } + + if StartExp { + c := NewJSConsole(ethereum) + + go c.Start() + } + if StartRpc { utils.DoRpc(ethereum, RpcPort) } diff --git a/ethereum/javascript_console.go b/ethereum/javascript_console.go new file mode 100644 index 0000000000..a6449af8f5 --- /dev/null +++ b/ethereum/javascript_console.go @@ -0,0 +1,76 @@ +package main + +import ( + "bufio" + "fmt" + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethpub" + "github.com/robertkrimen/otto" + "os" +) + +type JSConsole struct { + vm *otto.Otto + lib *ethpub.PEthereum +} + +func NewJSConsole(ethereum *eth.Ethereum) *JSConsole { + return &JSConsole{vm: otto.New(), lib: ethpub.NewPEthereum(ethereum)} +} + +func (self *JSConsole) Start() { + self.initBindings() + + fmt.Println("Eth JS Console") + reader := bufio.NewReader(os.Stdin) + for { + fmt.Printf("eth >>> ") + str, _, err := reader.ReadLine() + if err != nil { + fmt.Println("Error reading input", err) + } else { + if string(str) == "quit" { + return + } + + self.ParseInput(string(str)) + } + } +} + +func (self *JSConsole) ParseInput(code string) { + value, err := self.vm.Run(code) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(value) +} + +type OtherStruct struct { + Test string +} + +type JSWrapper struct { + pub *ethpub.PEthereum + vm *otto.Otto +} + +func (self *JSWrapper) GetKey() otto.Value { + result, err := self.vm.ToValue(self.pub.GetKey()) + if err != nil { + fmt.Println(err) + + return otto.UndefinedValue() + } + + return result + +} + +func (self *JSConsole) initBindings() { + t := &JSWrapper{self.lib, self.vm} + + self.vm.Set("eth", t) +} From 0a03484188dc23707b343bb512ec341afc744a2e Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 15 May 2014 22:15:14 +0200 Subject: [PATCH 03/16] Implemented JavaScript console --- ethereum/ethereum.go | 11 ++++--- ethereum/javascript_console.go | 60 ++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 8b42c2a2c8..128e111393 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -52,7 +52,12 @@ func main() { var logSys *log.Logger flags := log.LstdFlags - ethutil.ReadConfig(DataDir) + if StartJsConsole { + ethutil.ReadConfig(DataDir, ethutil.LogFile) + } else { + ethutil.ReadConfig(DataDir, ethutil.LogFile|ethutil.LogStd) + } + logger := ethutil.Config.Log if LogFile != "" { @@ -145,9 +150,7 @@ save these words so you can restore your account later: %s console := NewConsole(ethereum) go console.Start() - } - - if StartExp { + } else if StartJsConsole { c := NewJSConsole(ethereum) go c.Start() diff --git a/ethereum/javascript_console.go b/ethereum/javascript_console.go index a6449af8f5..9adf51bccd 100644 --- a/ethereum/javascript_console.go +++ b/ethereum/javascript_console.go @@ -21,7 +21,7 @@ func NewJSConsole(ethereum *eth.Ethereum) *JSConsole { func (self *JSConsole) Start() { self.initBindings() - fmt.Println("Eth JS Console") + fmt.Println("Eth JavaScript console") reader := bufio.NewReader(os.Stdin) for { fmt.Printf("eth >>> ") @@ -29,16 +29,18 @@ func (self *JSConsole) Start() { if err != nil { fmt.Println("Error reading input", err) } else { - if string(str) == "quit" { - return - } - self.ParseInput(string(str)) } } } func (self *JSConsole) ParseInput(code string) { + defer func() { + if r := recover(); r != nil { + fmt.Println("[native] error", r) + } + }() + value, err := self.vm.Run(code) if err != nil { fmt.Println(err) @@ -48,29 +50,59 @@ func (self *JSConsole) ParseInput(code string) { fmt.Println(value) } -type OtherStruct struct { - Test string +func (self *JSConsole) initBindings() { + t := &JSWrapper{self.lib, self.vm} + + self.vm.Set("eth", t) } +// The JS wrapper attempts to wrap the PEthereum object and returns +// proper javascript objects type JSWrapper struct { - pub *ethpub.PEthereum - vm *otto.Otto + *ethpub.PEthereum + vm *otto.Otto } func (self *JSWrapper) GetKey() otto.Value { - result, err := self.vm.ToValue(self.pub.GetKey()) + return self.toVal(self.PEthereum.GetKey()) +} + +func (self *JSWrapper) GetStateObject(addr string) otto.Value { + return self.toVal(self.PEthereum.GetStateObject(addr)) +} + +func (self *JSWrapper) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { + r, err := self.PEthereum.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) if err != nil { fmt.Println(err) return otto.UndefinedValue() } - return result + return self.toVal(r) +} +func (self *JSWrapper) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) otto.Value { + r, err := self.PEthereum.Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr) + + if err != nil { + fmt.Println(err) + + return otto.UndefinedValue() + } + + return self.toVal(r) } -func (self *JSConsole) initBindings() { - t := &JSWrapper{self.lib, self.vm} +// Wrapper function +func (self *JSWrapper) toVal(v interface{}) otto.Value { + result, err := self.vm.ToValue(v) - self.vm.Set("eth", t) + if err != nil { + fmt.Println(err) + + return otto.UndefinedValue() + } + + return result } From 6a78e080e645753ffe3e3bef0b09e71a2469c564 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 15 May 2014 22:17:09 +0200 Subject: [PATCH 04/16] Tell config which loggers to use --- ethereal/ethereum.go | 2 +- ethereum/config.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go index 98fab18e3b..206971b413 100644 --- a/ethereal/ethereum.go +++ b/ethereal/ethereum.go @@ -39,7 +39,7 @@ func main() { runtime.GOMAXPROCS(runtime.NumCPU()) ethchain.InitFees() - ethutil.ReadConfig(DataDir) + ethutil.ReadConfig(DataDir, ethutil.LogFile|ethutil.LogStd) // Instantiated a eth stack ethereum, err := eth.New(eth.CapDefault, UseUPnP) diff --git a/ethereum/config.go b/ethereum/config.go index 4d7ea63107..5ddc8e6353 100644 --- a/ethereum/config.go +++ b/ethereum/config.go @@ -20,7 +20,7 @@ var ExportKey bool var LogFile string var DataDir string var NonInteractive bool -var StartExp bool +var StartJsConsole bool func Init() { flag.BoolVar(&StartConsole, "c", false, "debug and testing console") @@ -39,7 +39,7 @@ func Init() { flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") - flag.BoolVar(&StartExp, "ex", false, "exp") + flag.BoolVar(&StartJsConsole, "js", false, "exp") flag.Parse() } From 770808ce0d44cadfedbe01694c836be2eaf0e82c Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 17 May 2014 15:15:46 +0200 Subject: [PATCH 05/16] Readline repl for linux & osx --- ethereal/ui/gui.go | 2 +- ethereum/ethereum.go | 4 +- ethereum/javascript_console.go | 71 ++++++++++++++++++---------------- ethereum/repl_darwin.go | 55 ++++++++++++++++++++++++++ ethereum/repl_linux.go | 1 + ethereum/repl_windows.go | 20 ++++++++++ utils/cmd.go | 8 ++-- 7 files changed, 122 insertions(+), 39 deletions(-) create mode 100644 ethereum/repl_darwin.go create mode 120000 ethereum/repl_linux.go create mode 100644 ethereum/repl_windows.go diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index c4cc1373f6..24be9e0c5e 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -197,7 +197,7 @@ func (gui *Gui) update() { case b := <-blockChan: block := b.Resource.(*ethchain.Block) if bytes.Compare(block.Coinbase, gui.addr) == 0 { - gui.setWalletValue(gui.eth.StateManager().ProcState().GetAccount(gui.addr).Amount, nil) + gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.addr).Amount, nil) } case txMsg := <-txChan: diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 128e111393..04933ef8e1 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -151,9 +151,9 @@ save these words so you can restore your account later: %s console := NewConsole(ethereum) go console.Start() } else if StartJsConsole { - c := NewJSConsole(ethereum) + repl := NewJSRepl(ethereum) - go c.Start() + go repl.Start() } if StartRpc { diff --git a/ethereum/javascript_console.go b/ethereum/javascript_console.go index 9adf51bccd..9a10ca2368 100644 --- a/ethereum/javascript_console.go +++ b/ethereum/javascript_console.go @@ -1,47 +1,58 @@ package main import ( - "bufio" "fmt" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethpub" "github.com/robertkrimen/otto" - "os" ) -type JSConsole struct { +type Repl interface { + Start() +} + +type JSRE struct { vm *otto.Otto lib *ethpub.PEthereum } -func NewJSConsole(ethereum *eth.Ethereum) *JSConsole { - return &JSConsole{vm: otto.New(), lib: ethpub.NewPEthereum(ethereum)} +func NewJSRE(ethereum *eth.Ethereum) *JSRE { + re := &JSRE{vm: otto.New(), lib: ethpub.NewPEthereum(ethereum)} + + re.Bind("eth", &JSEthereum{re.lib, re.vm}) + + return re +} + +func (self *JSRE) Bind(name string, v interface{}) { + self.vm.Set(name, v) +} + +func (self *JSRE) Run(code string) (otto.Value, error) { + return self.vm.Run(code) } -func (self *JSConsole) Start() { - self.initBindings() +type JSRepl struct { + re *JSRE +} + +func NewJSRepl(ethereum *eth.Ethereum) *JSRepl { + return &JSRepl{re: NewJSRE(ethereum)} +} +func (self *JSRepl) Start() { fmt.Println("Eth JavaScript console") - reader := bufio.NewReader(os.Stdin) - for { - fmt.Printf("eth >>> ") - str, _, err := reader.ReadLine() - if err != nil { - fmt.Println("Error reading input", err) - } else { - self.ParseInput(string(str)) - } - } + self.read() } -func (self *JSConsole) ParseInput(code string) { +func (self *JSRepl) parseInput(code string) { defer func() { if r := recover(); r != nil { fmt.Println("[native] error", r) } }() - value, err := self.vm.Run(code) + value, err := self.re.Run(code) if err != nil { fmt.Println(err) return @@ -50,28 +61,22 @@ func (self *JSConsole) ParseInput(code string) { fmt.Println(value) } -func (self *JSConsole) initBindings() { - t := &JSWrapper{self.lib, self.vm} - - self.vm.Set("eth", t) -} - -// The JS wrapper attempts to wrap the PEthereum object and returns -// proper javascript objects -type JSWrapper struct { +// The JSEthereum object attempts to wrap the PEthereum object and returns +// meaningful javascript objects +type JSEthereum struct { *ethpub.PEthereum vm *otto.Otto } -func (self *JSWrapper) GetKey() otto.Value { +func (self *JSEthereum) GetKey() otto.Value { return self.toVal(self.PEthereum.GetKey()) } -func (self *JSWrapper) GetStateObject(addr string) otto.Value { +func (self *JSEthereum) GetStateObject(addr string) otto.Value { return self.toVal(self.PEthereum.GetStateObject(addr)) } -func (self *JSWrapper) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { +func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { r, err := self.PEthereum.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) if err != nil { fmt.Println(err) @@ -82,7 +87,7 @@ func (self *JSWrapper) Transact(key, recipient, valueStr, gasStr, gasPriceStr, d return self.toVal(r) } -func (self *JSWrapper) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) otto.Value { +func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) otto.Value { r, err := self.PEthereum.Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr) if err != nil { @@ -95,7 +100,7 @@ func (self *JSWrapper) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyS } // Wrapper function -func (self *JSWrapper) toVal(v interface{}) otto.Value { +func (self *JSEthereum) toVal(v interface{}) otto.Value { result, err := self.vm.ToValue(v) if err != nil { diff --git a/ethereum/repl_darwin.go b/ethereum/repl_darwin.go new file mode 100644 index 0000000000..b6de190e4d --- /dev/null +++ b/ethereum/repl_darwin.go @@ -0,0 +1,55 @@ +package main + +// #cgo LDFLAGS: -lreadline +// #include +// #include +// #include +// #include +import "C" +import "unsafe" + +func readLine(prompt *string) *string { + var p *C.char + + //readline allows an empty prompt(NULL) + if prompt != nil { + p = C.CString(*prompt) + } + + ret := C.readline(p) + + if p != nil { + C.free(unsafe.Pointer(p)) + } + + if ret == nil { + return nil + } //EOF + + s := C.GoString(ret) + C.free(unsafe.Pointer(ret)) + return &s +} + +func addHistory(s string) { + p := C.CString(s) + C.add_history(p) + C.free(unsafe.Pointer(p)) +} + +func (self *JSRepl) read() { + prompt := "eth >>> " + +L: + for { + switch result := readLine(&prompt); true { + case result == nil: + break L //exit loop + + case *result != "": //ignore blank lines + addHistory(*result) //allow user to recall this line + + self.parseInput(*result) + } + } +} diff --git a/ethereum/repl_linux.go b/ethereum/repl_linux.go new file mode 120000 index 0000000000..276f135d76 --- /dev/null +++ b/ethereum/repl_linux.go @@ -0,0 +1 @@ +repl_darwin.go \ No newline at end of file diff --git a/ethereum/repl_windows.go b/ethereum/repl_windows.go new file mode 100644 index 0000000000..c65bb1cb43 --- /dev/null +++ b/ethereum/repl_windows.go @@ -0,0 +1,20 @@ +package main + +import ( + "bufio" + "fmt" + "os" +) + +func (self *JSRepl) read() { + reader := bufio.NewReader(os.Stdin) + for { + fmt.Printf("eth >>> ") + str, _, err := reader.ReadLine() + if err != nil { + fmt.Println("Error reading input", err) + } else { + self.parseInput(string(str)) + } + } +} diff --git a/utils/cmd.go b/utils/cmd.go index 8395ac8fc0..62e8f31dcf 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -35,9 +35,11 @@ func DoMining(ethereum *eth.Ethereum) { // Give it some time to connect with peers time.Sleep(3 * time.Second) - for ethereum.IsUpToDate() == false { - time.Sleep(5 * time.Second) - } + /* + for ethereum.IsUpToDate() == false { + time.Sleep(5 * time.Second) + } + */ log.Println("Miner started") miner := ethminer.NewDefaultMiner(addr, ethereum) From 43f88b2bbb6fc993f8bfee531056a7e11bef59bd Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 May 2014 12:14:32 +0200 Subject: [PATCH 06/16] Removed nonce incrementing --- ethereal/ui/gui.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index 24be9e0c5e..e465d5273f 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -210,8 +210,10 @@ func (gui *Gui) update() { gui.win.Root().Call("addTx", ethpub.NewPTx(tx)) gui.txDb.Put(tx.Hash(), tx.RlpEncode()) - object.Nonce += 1 - state.SetStateObject(object) + /* + object.Nonce += 1 + state.SetStateObject(object) + */ unconfirmedFunds.Sub(unconfirmedFunds, tx.Value) } else if bytes.Compare(tx.Recipient, gui.addr) == 0 { From 30842eb8d013856039c128f3a3303c1fc6f833c2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 May 2014 12:14:47 +0200 Subject: [PATCH 07/16] Changed logging --- utils/cmd.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/utils/cmd.go b/utils/cmd.go index 62e8f31dcf..f163575da7 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethutil" - "log" "time" ) @@ -14,7 +13,7 @@ func DoRpc(ethereum *eth.Ethereum, RpcPort int) { var err error ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort) if err != nil { - log.Println("Could not start RPC interface:", err) + ethutil.Config.Log.Infoln("Could not start RPC interface:", err) } else { go ethereum.RpcServer.Start() } @@ -25,7 +24,7 @@ func DoMining(ethereum *eth.Ethereum) { ethereum.Mining = true if ethutil.GetKeyRing().Len() == 0 { - log.Println("No address found, can't start mining") + ethutil.Config.Log.Infoln("No address found, can't start mining") return } keyPair := ethutil.GetKeyRing().Get(0) @@ -40,7 +39,7 @@ func DoMining(ethereum *eth.Ethereum) { time.Sleep(5 * time.Second) } */ - log.Println("Miner started") + ethutil.Config.Log.Infoln("Miner started") miner := ethminer.NewDefaultMiner(addr, ethereum) miner.Start() From 3b7707c3fd2f99ee1019b8214cba1784af519f53 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 May 2014 12:15:03 +0200 Subject: [PATCH 08/16] Improved console * Added watch --- ethereum/javascript_console.go | 74 ++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/ethereum/javascript_console.go b/ethereum/javascript_console.go index 9a10ca2368..884b9a629c 100644 --- a/ethereum/javascript_console.go +++ b/ethereum/javascript_console.go @@ -3,7 +3,9 @@ package main import ( "fmt" "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethutil" "github.com/robertkrimen/otto" ) @@ -12,18 +14,83 @@ type Repl interface { } type JSRE struct { - vm *otto.Otto - lib *ethpub.PEthereum + ethereum *eth.Ethereum + vm *otto.Otto + lib *ethpub.PEthereum + + blockChan chan ethutil.React + changeChan chan ethutil.React + quitChan chan bool + + objectCb map[string][]otto.Value } func NewJSRE(ethereum *eth.Ethereum) *JSRE { - re := &JSRE{vm: otto.New(), lib: ethpub.NewPEthereum(ethereum)} + re := &JSRE{ + ethereum, + otto.New(), + ethpub.NewPEthereum(ethereum), + make(chan ethutil.React, 1), + make(chan ethutil.React, 1), + make(chan bool), + make(map[string][]otto.Value), + } + + // We have to make sure that, whoever calls this, calls "Stop" + go re.mainLoop() re.Bind("eth", &JSEthereum{re.lib, re.vm}) + t, _ := re.vm.Get("eth") + t.Object().Set("watch", func(call otto.FunctionCall) otto.Value { + addr, _ := call.Argument(0).ToString() + cb := call.Argument(1) + + re.objectCb[addr] = append(re.objectCb[addr], cb) + + event := "object:" + string(ethutil.FromHex(addr)) + ethereum.Reactor().Subscribe(event, re.changeChan) + + return otto.UndefinedValue() + }) return re } +func (self *JSRE) Stop() { + // Kill the main loop + self.quitChan <- true + + close(self.blockChan) + close(self.quitChan) + close(self.changeChan) +} + +func (self *JSRE) mainLoop() { + // Subscribe to events + reactor := self.ethereum.Reactor() + reactor.Subscribe("newBlock", self.blockChan) + +out: + for { + select { + case <-self.quitChan: + break out + case block := <-self.blockChan: + if _, ok := block.Resource.(*ethchain.Block); ok { + } + case object := <-self.changeChan: + if stateObject, ok := object.Resource.(*ethchain.StateObject); ok { + for _, cb := range self.objectCb[ethutil.Hex(stateObject.Address())] { + val, _ := self.vm.ToValue(ethpub.NewPStateObject(stateObject)) + cb.Call(cb, val) + } + } else if storageObject, ok := object.Resource.(*ethchain.StorageState); ok { + fmt.Println(storageObject) + } + } + } +} + func (self *JSRE) Bind(name string, v interface{}) { self.vm.Set(name, v) } @@ -99,7 +166,6 @@ func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, initStr, body return self.toVal(r) } -// Wrapper function func (self *JSEthereum) toVal(v interface{}) otto.Value { result, err := self.vm.ToValue(v) From 16421106d47efb65331ed9f0499f12038158cbf1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 May 2014 13:04:31 +0200 Subject: [PATCH 09/16] Added multi-line support --- ethereum/javascript_console.go | 5 +++-- ethereum/repl_darwin.go | 37 ++++++++++++++++++++++++++++------ ethereum/repl_windows.go | 2 +- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/ethereum/javascript_console.go b/ethereum/javascript_console.go index 884b9a629c..1e1ae0e482 100644 --- a/ethereum/javascript_console.go +++ b/ethereum/javascript_console.go @@ -101,14 +101,15 @@ func (self *JSRE) Run(code string) (otto.Value, error) { type JSRepl struct { re *JSRE + + prompt string } func NewJSRepl(ethereum *eth.Ethereum) *JSRepl { - return &JSRepl{re: NewJSRE(ethereum)} + return &JSRepl{re: NewJSRE(ethereum), prompt: "> "} } func (self *JSRepl) Start() { - fmt.Println("Eth JavaScript console") self.read() } diff --git a/ethereum/repl_darwin.go b/ethereum/repl_darwin.go index b6de190e4d..483d4cedff 100644 --- a/ethereum/repl_darwin.go +++ b/ethereum/repl_darwin.go @@ -6,7 +6,11 @@ package main // #include // #include import "C" -import "unsafe" + +import ( + "strings" + "unsafe" +) func readLine(prompt *string) *string { var p *C.char @@ -37,19 +41,40 @@ func addHistory(s string) { C.free(unsafe.Pointer(p)) } -func (self *JSRepl) read() { - prompt := "eth >>> " +var indentCount = 0 +var str = "" + +func (self *JSRepl) 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 *JSRepl) read() { L: for { - switch result := readLine(&prompt); true { + switch result := readLine(&self.prompt); true { case result == nil: break L //exit loop case *result != "": //ignore blank lines - addHistory(*result) //allow user to recall this line + str += *result + "\n" + + self.setIndent() + + if indentCount <= 0 { + addHistory(str) //allow user to recall this line - self.parseInput(*result) + self.parseInput(str) + } } } } diff --git a/ethereum/repl_windows.go b/ethereum/repl_windows.go index c65bb1cb43..c42fd6e6a3 100644 --- a/ethereum/repl_windows.go +++ b/ethereum/repl_windows.go @@ -9,7 +9,7 @@ import ( func (self *JSRepl) read() { reader := bufio.NewReader(os.Stdin) for { - fmt.Printf("eth >>> ") + fmt.Printf(self.prompt) str, _, err := reader.ReadLine() if err != nil { fmt.Println("Error reading input", err) From 017bbbb582b09a3264b4ff996f35275d381f284f Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 May 2014 16:32:45 +0200 Subject: [PATCH 10/16] Improved REPL output --- ethereum/ethereum.go | 24 +++++++++++------- ethereum/javascript_console.go | 10 +++++++- ethereum/js_lib.go | 46 ++++++++++++++++++++++++++++++++++ ethereum/repl_darwin.go | 17 +++++++++++-- ethereum/repl_windows.go | 4 +++ 5 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 ethereum/js_lib.go diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 04933ef8e1..1cbb610021 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -15,16 +15,15 @@ import ( const Debug = true -// Register interrupt handlers so we can stop the ethereum -func RegisterInterrupts(s *eth.Ethereum) { - // Buffered chan of one is enough - c := make(chan os.Signal, 1) - // Notify about interrupts for now - signal.Notify(c, os.Interrupt) +func RegisterInterrupt(cb func(os.Signal)) { go func() { + // Buffered chan of one is enough + c := make(chan os.Signal, 1) + // Notify about interrupts for now + signal.Notify(c, os.Interrupt) + for sig := range c { - fmt.Printf("Shutting down (%v) ... \n", sig) - s.Stop() + cb(sig) } }() } @@ -154,13 +153,20 @@ save these words so you can restore your account later: %s repl := NewJSRepl(ethereum) go repl.Start() + + RegisterInterrupt(func(os.Signal) { + repl.Stop() + }) } if StartRpc { utils.DoRpc(ethereum, RpcPort) } - RegisterInterrupts(ethereum) + RegisterInterrupt(func(sig os.Signal) { + fmt.Printf("Shutting down (%v) ... \n", sig) + ethereum.Stop() + }) ethereum.Start(UseSeed) diff --git a/ethereum/javascript_console.go b/ethereum/javascript_console.go index 1e1ae0e482..07d69dafc9 100644 --- a/ethereum/javascript_console.go +++ b/ethereum/javascript_console.go @@ -11,6 +11,7 @@ import ( type Repl interface { Start() + Stop() } type JSRE struct { @@ -36,6 +37,9 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { make(map[string][]otto.Value), } + // Init the JS lib + re.vm.Run(jsLib) + // We have to make sure that, whoever calls this, calls "Stop" go re.mainLoop() @@ -113,6 +117,10 @@ func (self *JSRepl) Start() { self.read() } +func (self *JSRepl) Stop() { + self.re.Stop() +} + func (self *JSRepl) parseInput(code string) { defer func() { if r := recover(); r != nil { @@ -126,7 +134,7 @@ func (self *JSRepl) parseInput(code string) { return } - fmt.Println(value) + self.PrintValue(value) } // The JSEthereum object attempts to wrap the PEthereum object and returns diff --git a/ethereum/js_lib.go b/ethereum/js_lib.go new file mode 100644 index 0000000000..8b59d75ca5 --- /dev/null +++ b/ethereum/js_lib.go @@ -0,0 +1,46 @@ +package main + +const jsLib = ` +function pp(object) { + var str = ""; + + if(object instanceof Array) { + str += "[ "; + for(var i = 0, l = object.length; i < l; i++) { + str += pp(object[i]); + + if(i < l-1) { + str += ", "; + } + } + str += " ]"; + } else if(typeof(object) === "object") { + str += "{ "; + var last = Object.keys(object).sort().pop() + for(var k in object) { + str += k + ": " + pp(object[k]); + + if(k !== last) { + str += ", "; + } + } + str += " }"; + } else if(typeof(object) === "string") { + str += "\033[32m'" + object + "'"; + } else if(typeof(object) === "undefined") { + str += "\033[1m\033[30m" + object; + } else if(typeof(object) === "number") { + str += "\033[31m" + object; + } else { + str += object; + } + + str += "\033[0m"; + + return str; +} + +function prettyPrint(object) { + console.log(pp(object)) +} +` diff --git a/ethereum/repl_darwin.go b/ethereum/repl_darwin.go index 483d4cedff..87da3df1d8 100644 --- a/ethereum/repl_darwin.go +++ b/ethereum/repl_darwin.go @@ -8,6 +8,7 @@ package main import "C" import ( + "github.com/robertkrimen/otto" "strings" "unsafe" ) @@ -63,18 +64,30 @@ L: for { switch result := readLine(&self.prompt); true { case result == nil: - break L //exit loop + break L - case *result != "": //ignore blank lines + case *result != "": str += *result + "\n" self.setIndent() if indentCount <= 0 { + if *result == "exit" { + self.Stop() + break L + } + addHistory(str) //allow user to recall this line self.parseInput(str) + + str = "" } } } } + +func (self *JSRepl) PrintValue(value otto.Value) { + method, _ := self.re.vm.Get("prettyPrint") + method.Call(method, value) +} diff --git a/ethereum/repl_windows.go b/ethereum/repl_windows.go index c42fd6e6a3..9d4787772f 100644 --- a/ethereum/repl_windows.go +++ b/ethereum/repl_windows.go @@ -18,3 +18,7 @@ func (self *JSRepl) read() { } } } + +func (self *JSRepl) PrintValue(value otto.Value) { + fmt.Println(value) +} From 92eaa98e8381bef5224ffe864aa1cd4288af4d12 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 May 2014 17:01:40 +0200 Subject: [PATCH 11/16] Added js interpret mode --- ethereum/config.go | 2 ++ ethereum/ethereum.go | 19 ++++++++++++++++++- ethereum/js_lib.go | 11 +++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/ethereum/config.go b/ethereum/config.go index 5ddc8e6353..090c022b03 100644 --- a/ethereum/config.go +++ b/ethereum/config.go @@ -21,6 +21,7 @@ var LogFile string var DataDir string var NonInteractive bool var StartJsConsole bool +var InputFile string func Init() { flag.BoolVar(&StartConsole, "c", false, "debug and testing console") @@ -40,6 +41,7 @@ func Init() { flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") flag.BoolVar(&StartJsConsole, "js", false, "exp") + flag.StringVar(&InputFile, "e", "", "Run javascript file") flag.Parse() } diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 1cbb610021..f680b5416b 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/utils" + "io/ioutil" "log" "os" "os/signal" @@ -51,7 +52,7 @@ func main() { var logSys *log.Logger flags := log.LstdFlags - if StartJsConsole { + if StartJsConsole || len(InputFile) > 0 { ethutil.ReadConfig(DataDir, ethutil.LogFile) } else { ethutil.ReadConfig(DataDir, ethutil.LogFile|ethutil.LogStd) @@ -157,6 +158,22 @@ save these words so you can restore your account later: %s RegisterInterrupt(func(os.Signal) { repl.Stop() }) + } else if len(InputFile) > 0 { + file, err := os.Open(InputFile) + if err != nil { + ethutil.Config.Log.Fatal(err) + } + + content, err := ioutil.ReadAll(file) + if err != nil { + ethutil.Config.Log.Fatal(err) + } + + re := NewJSRE(ethereum) + RegisterInterrupt(func(os.Signal) { + re.Stop() + }) + re.Run(string(content)) } if StartRpc { diff --git a/ethereum/js_lib.go b/ethereum/js_lib.go index 8b59d75ca5..189dcc3a0b 100644 --- a/ethereum/js_lib.go +++ b/ethereum/js_lib.go @@ -31,6 +31,8 @@ function pp(object) { str += "\033[1m\033[30m" + object; } else if(typeof(object) === "number") { str += "\033[31m" + object; + } else if(typeof(object) === "function") { + str += "\033[35m[Function]"; } else { str += object; } @@ -40,7 +42,12 @@ function pp(object) { return str; } -function prettyPrint(object) { - console.log(pp(object)) +function prettyPrint(/* */) { + var args = arguments; + for(var i = 0, l = args.length; i < l; i++) { + console.log(pp(args[i])) + } } + +var print = prettyPrint; ` From dfc3cb441bed85728914f5575a86c9fcb1f61211 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 20 May 2014 11:52:36 +0200 Subject: [PATCH 12/16] Increase default peer amount to 10 --- ethereal/config.go | 2 +- ethereum/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethereal/config.go b/ethereal/config.go index e4bdb0a000..817befc2c8 100644 --- a/ethereal/config.go +++ b/ethereal/config.go @@ -33,7 +33,7 @@ func Init() { flag.StringVar(&OutboundPort, "p", "30303", "listening port") flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") - flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") + flag.IntVar(&MaxPeer, "x", 10, "maximum desired peers") flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory") flag.Parse() diff --git a/ethereum/config.go b/ethereum/config.go index 090c022b03..117aa6f2ca 100644 --- a/ethereum/config.go +++ b/ethereum/config.go @@ -39,7 +39,7 @@ func Init() { flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)") flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") - flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") + flag.IntVar(&MaxPeer, "x", 10, "maximum desired peers") flag.BoolVar(&StartJsConsole, "js", false, "exp") flag.StringVar(&InputFile, "e", "", "Run javascript file") From a05adb11288a1ea9dc6e38a952ab89fa5eb7f794 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 20 May 2014 12:48:34 +0200 Subject: [PATCH 13/16] Refactored file structure --- ethereal/assets/samplecoin/samplecoin.html | 2 +- ...cript_console.go => javascript_runtime.go} | 126 ++++-------------- ethereum/repl.go | 97 ++++++++++++++ 3 files changed, 124 insertions(+), 101 deletions(-) rename ethereum/{javascript_console.go => javascript_runtime.go} (51%) create mode 100644 ethereum/repl.go diff --git a/ethereal/assets/samplecoin/samplecoin.html b/ethereal/assets/samplecoin/samplecoin.html index 3f8eacc00a..d47c6323c5 100644 --- a/ethereal/assets/samplecoin/samplecoin.html +++ b/ethereal/assets/samplecoin/samplecoin.html @@ -9,7 +9,7 @@