diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index d4eaf97fdf..c58fe24c24 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -32,6 +32,10 @@ window.eth = { postData({call: "getStorage", args: [address, storageAddress]}, cb); }, + getStateKeyVals: function(address, cb){ + postData({call: "getStateKeyVals", args: [address]}, cb); + }, + getKey: function(cb) { postData({call: "getKey"}, cb); }, diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index bbb147d897..fece8e7d6e 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -45,6 +45,13 @@ ApplicationWindow { addPeerWin.visible = true } } + MenuItem { + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } + } } Menu { @@ -247,13 +254,12 @@ ApplicationWindow { } } - property var addressModel: ListModel { id: addressModel } TableView { id: addressView - width: parent.width + width: parent.width - 200 height: 200 anchors.bottom: logView.top TableViewColumn{ role: "name"; title: "name" } @@ -262,6 +268,30 @@ ApplicationWindow { model: addressModel } + Rectangle { + anchors.top: addressView.top + anchors.left: addressView.right + anchors.leftMargin: 20 + + TextField { + placeholderText: "Name to register" + id: nameToReg + width: 150 + } + + Button { + anchors.top: nameToReg.bottom + text: "Register" + MouseArea{ + anchors.fill: parent + onClicked: { + eth.registerName(nameToReg.text) + nameToReg.text = "" + } + } + } + } + property var logModel: ListModel { id: logModel @@ -359,6 +389,10 @@ ApplicationWindow { id: peerImage anchors.right: parent.right width: 10; height: 10 + MouseArea { + onDoubleClicked: peerWindow.visible = true + anchors.fill: parent + } source: ui.assetPath("network.png") } } @@ -623,6 +657,20 @@ ApplicationWindow { function setPeers(text) { peerLabel.text = text } + + function addPeer(peer) { + // We could just append the whole peer object but it cries if you try to alter them + peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version}) + } + + function resetPeers(){ + peerModel.clear() + } + + function timeAgo(unixTs){ + var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 + return (lapsed + " seconds ago") + } function convertToPretty(unixTs){ var a = new Date(unixTs*1000); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; @@ -635,6 +683,31 @@ ApplicationWindow { var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; return time; } + // ****************************************** + // Windows + // ****************************************** + Window { + id: peerWindow + height: 200 + width: 700 + Rectangle { + anchors.fill: parent + property var peerModel: ListModel { + id: peerModel + } + TableView { + anchors.fill: parent + id: peerTable + model: peerModel + TableViewColumn{width: 100; role: "ip" ; title: "IP" } + TableViewColumn{width: 60; role: "port" ; title: "Port" } + TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" } + TableViewColumn{width: 100; role: "latency"; title: "Latency" } + TableViewColumn{width: 260; role: "version" ; title: "Version" } + } + } + } + // ******************************************* // Components // ******************************************* @@ -810,7 +883,6 @@ ApplicationWindow { } } } - // New Transaction component Component { id: newTransaction diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index d3cffeecab..ec236a18c2 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -34,7 +34,6 @@ ApplicationWindow { top: parent.top } */ - onTitleChanged: { window.title = title } experimental.preferences.javascriptEnabled: true experimental.preferences.navigatorQtObjectEnabled: true @@ -97,6 +96,12 @@ ApplicationWindow { var storage = stateObject.getStorage(data.args[1]) postData(data._seed, storage) + break + case "getStateKeyVals": + require(1); + var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) + postData(data._seed,stateObject) + break case "getBalance": require(1); @@ -188,7 +193,7 @@ ApplicationWindow { WebView { id: inspector - visible: false + visible: true url: webview.experimental.remoteInspectorUrl anchors { left: root.left diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index 44215efdb9..4dda5017fd 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -12,6 +12,7 @@ import ( "github.com/go-qml/qml" "math/big" "strings" + "time" ) type Gui struct { @@ -64,6 +65,8 @@ func (gui *Gui) Start(assetPath string) { Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" }, }, { Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" }, + }, { + Init: func(p *ethpub.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" }, }}) ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", version)) @@ -91,7 +94,7 @@ func (gui *Gui) Start(assetPath string) { ethutil.Config.Log.AddLogSystem(gui) } if err != nil { - ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'") + ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'", err) panic(err) } @@ -162,6 +165,17 @@ func (gui *Gui) setInitialBlockChain() { blk := gui.eth.BlockChain().GetBlock(sBlk) for ; blk != nil; blk = gui.eth.BlockChain().GetBlock(sBlk) { sBlk = blk.PrevHash + + // Loop through all transactions to see if we missed any while being offline + for _, tx := range blk.Transactions() { + if bytes.Compare(tx.Sender(), gui.addr) == 0 || bytes.Compare(tx.Recipient, gui.addr) == 0 { + if ok, _ := gui.txDb.Get(tx.Hash()); ok == nil { + gui.txDb.Put(tx.Hash(), tx.RlpEncode()) + } + + } + } + gui.processBlock(blk, true) } } @@ -235,6 +249,8 @@ func (gui *Gui) update() { reactor.Subscribe("object:"+string(namereg), objectChan) reactor.Subscribe("peerList", peerChan) + ticker := time.NewTicker(5 * time.Second) + state := gui.eth.StateManager().TransState() unconfirmedFunds := new(big.Int) @@ -284,12 +300,19 @@ func (gui *Gui) update() { gui.loadAddressBook() case <-peerChan: gui.setPeerInfo() + case <-ticker.C: + gui.setPeerInfo() } } } func (gui *Gui) setPeerInfo() { gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers)) + + gui.win.Root().Call("resetPeers") + for _, peer := range gui.pub.GetPeers() { + gui.win.Root().Call("addPeer", peer) + } } // Logging functions that log directly to the GUI interface @@ -308,6 +331,11 @@ func (gui *Gui) Printf(format string, v ...interface{}) { gui.win.Root().Call("addLog", line) } } +func (gui *Gui) RegisterName(name string) { + keyPair := ethutil.GetKeyRing().Get(0) + name = fmt.Sprintf("\"%s\"\n1", name) + gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), "namereg", "1000", "1000000", "150", name) +} func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) { keyPair := ethutil.GetKeyRing().Get(0) diff --git a/ethereum/javascript_runtime.go b/ethereum/javascript_runtime.go index 93297f604a..b05d392320 100644 --- a/ethereum/javascript_runtime.go +++ b/ethereum/javascript_runtime.go @@ -10,6 +10,7 @@ import ( "github.com/obscuren/otto" "io/ioutil" "os" + "path" "path/filepath" ) @@ -25,6 +26,20 @@ type JSRE struct { objectCb map[string][]otto.Value } +func (jsre *JSRE) LoadExtFile(path string) { + result, err := ioutil.ReadFile(path) + if err == nil { + jsre.vm.Run(result) + } else { + ethutil.Config.Log.Debugln("Could not load file:", path) + } +} + +func (jsre *JSRE) LoadIntFile(file string) { + assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal", "assets", "ext") + jsre.LoadExtFile(path.Join(assetPath, file)) +} + func NewJSRE(ethereum *eth.Ethereum) *JSRE { re := &JSRE{ ethereum, @@ -39,6 +54,10 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { // Init the JS lib re.vm.Run(jsLib) + // Load extra javascript files + re.LoadIntFile("string.js") + re.LoadIntFile("big.js") + // We have to make sure that, whoever calls this, calls "Stop" go re.mainLoop() diff --git a/ethereum/repl.go b/ethereum/repl.go index 10f51675eb..0208459ad2 100644 --- a/ethereum/repl.go +++ b/ethereum/repl.go @@ -66,6 +66,10 @@ func (self *JSEthereum) GetBlock(hash string) otto.Value { return self.toVal(&JSBlock{self.PEthereum.GetBlock(hash), self}) } +func (self *JSEthereum) GetPeers() otto.Value { + return self.toVal(self.PEthereum.GetPeers()) +} + func (self *JSEthereum) GetKey() otto.Value { return self.toVal(self.PEthereum.GetKey()) } @@ -74,6 +78,10 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value { return self.toVal(self.PEthereum.GetStateObject(addr)) } +func (self *JSEthereum) GetStateKeyVals(addr string) otto.Value { + return self.toVal(self.PEthereum.GetStateObject(addr).StateKeyVal(false)) +} + 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 { @@ -101,7 +109,7 @@ func (self *JSEthereum) toVal(v interface{}) otto.Value { result, err := self.vm.ToValue(v) if err != nil { - fmt.Println(err) + fmt.Println("Value unknown:", err) return otto.UndefinedValue() }