diff --git a/ethereal/assets/back.png b/ethereal/assets/back.png new file mode 100644 index 0000000000..71486c7c05 Binary files /dev/null and b/ethereal/assets/back.png differ diff --git a/ethereal/assets/close.png b/ethereal/assets/close.png new file mode 100644 index 0000000000..88df442c5c Binary files /dev/null and b/ethereal/assets/close.png differ diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index de6fb02553..9970c6379f 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -2,30 +2,97 @@ window.eth = { prototype: Object(), + mutan: function(code) { + }, + + toHex: function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; + }, + + toAscii: function(hex) { + // Find termination + var str = ""; + var i = 0, l = hex.length; + for(; i < l; i+=2) { + var code = hex.charCodeAt(i) + if(code == 0) { + break; + } + + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + + return str; + }, + + fromAscii: function(str, pad) { + if(pad === undefined) { + pad = 32 + } + + var hex = this.toHex(str); + + while(hex.length < pad*2) + hex += "00"; + + return hex + }, + + // Retrieve block // // Either supply a number or a string. Type is determent for the lookup method // string - Retrieves the block by looking up the hash // number - Retrieves the block by looking up the block number - getBlock: function(numberOrHash, cb) { - var func; - if(typeof numberOrHash == "string") { - func = "getBlockByHash"; - } else { - func = "getBlockByNumber"; - } - postData({call: func, args: [numberOrHash]}, cb); - }, + getBlock: function(numberOrHash, cb) { + var func; + if(typeof numberOrHash == "string") { + func = "getBlockByHash"; + } else { + func = "getBlockByNumber"; + } + postData({call: func, args: [numberOrHash]}, cb); + }, // Create transaction // // Transact between two state objects - transact: function(sec, recipient, value, gas, gasPrice, data, cb) { - postData({call: "transact", args: [sec, recipient, value, gas, gasPrice, data]}, cb); - }, + transact: function(params, cb) { + if(params === undefined) { + params = {}; + } + + if(params.endowment !== undefined) + params.value = params.endowment; + if(params.code !== undefined) + params.data = params.code; + + // Make sure everything is string + var fields = ["to", "from", "value", "gas", "gasPrice"]; + for(var i = 0; i < fields.length; i++) { + if(params[fields[i]] === undefined) { + params[fields[i]] = ""; + } + params[fields[i]] = params[fields[i]].toString(); + } + + var data; + if(typeof params.data === "object") { + data = ""; + for(var i = 0; i < params.data.length; i++) { + data += params.data[i] + } + } else { + data = params.data; + } - create: function(sec, value, gas, gasPrice, init, body, cb) { - postData({call: "create", args: [sec, value, gas, gasPrice, init, body]}, cb); + postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb); }, getStorageAt: function(address, storageAddress, cb) { diff --git a/ethereal/assets/ext/messaging.js b/ethereal/assets/ext/messaging.js deleted file mode 100644 index e7bc630202..0000000000 --- a/ethereal/assets/ext/messaging.js +++ /dev/null @@ -1,117 +0,0 @@ -function handleMessage(message) { - console.log("[onMessageReceived]: ", message.data) - // TODO move to messaging.js - var data = JSON.parse(message.data) - - try { - switch(data.call) { - case "getCoinBase": - postData(data._seed, eth.getCoinBase()) - - break - case "getIsListening": - postData(data._seed, eth.getIsListening()) - - break - case "getIsMining": - postData(data._seed, eth.getIsMining()) - - break - case "getPeerCount": - postData(data._seed, eth.getPeerCount()) - - break - - case "getTxCountAt": - require(1) - postData(data._seed, eth.getTxCountAt(data.args[0])) - - break - case "getBlockByNumber": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) - - break - case "getBlockByHash": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) - - break - case "transact": - require(5) - - var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5]) - postData(data._seed, tx) - - break - case "create": - postData(data._seed, null) - - break - case "getStorage": - require(2); - - var stateObject = eth.getStateObject(data.args[0]) - 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 "getTransactionsFor": - require(1); - var txs = eth.getTransactionsFor(data.args[0], true) - postData(data._seed, txs) - - break - case "getBalance": - require(1); - - postData(data._seed, eth.getStateObject(data.args[0]).value()); - - break - case "getKey": - var key = eth.getKey().privateKey; - - postData(data._seed, key) - break - case "watch": - require(1) - eth.watch(data.args[0], data.args[1]); - break - case "disconnect": - require(1) - postData(data._seed, null) - break; - case "set": - console.log("'Set' has been depcrecated") - /* - for(var key in data.args) { - if(webview.hasOwnProperty(key)) { - window[key] = data.args[key]; - } - } - */ - break; - case "getSecretToAddress": - require(1) - postData(data._seed, eth.secretToAddress(data.args[0])) - break; - case "debug": - console.log(data.args[0]); - break; - } - } catch(e) { - console.log(data.call + ": " + e) - - postData(data._seed, null); - } -} - -function postData(seed, data) { - webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed})) -} diff --git a/ethereal/assets/pick.png b/ethereal/assets/pick.png new file mode 100644 index 0000000000..2f5a261c26 Binary files /dev/null and b/ethereal/assets/pick.png differ diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml index f50ae80041..a73b7367db 100644 --- a/ethereal/assets/qml/views/history.qml +++ b/ethereal/assets/qml/views/history.qml @@ -29,7 +29,7 @@ Rectangle { model: txModel } - function addTx(type, tx, inout) { + function addTx(tx, inout) { var isContract if (tx.contract == true){ isContract = "Yes" diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index 9e05e2f8e7..fcddd46e2e 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -17,6 +17,7 @@ Rectangle { color: "#00000000" Column { + id: info spacing: 3 anchors.fill: parent anchors.topMargin: 5 @@ -49,7 +50,7 @@ Rectangle { } TableView { id: addressView - width: parent.width - 200 + width: parent.width height: 200 anchors.bottom: logLayout.top TableViewColumn{ role: "name"; title: "name" } @@ -58,30 +59,6 @@ Rectangle { 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: { - gui.registerName(nameToReg.text) - nameToReg.text = "" - } - } - } - } - property var logModel: ListModel { id: logModel } diff --git a/ethereal/assets/qml/views/javascript.qml b/ethereal/assets/qml/views/javascript.qml new file mode 100644 index 0000000000..376397130f --- /dev/null +++ b/ethereal/assets/qml/views/javascript.qml @@ -0,0 +1,45 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0; +import QtQuick.Layouts 1.0; +import QtQuick.Dialogs 1.0; +import QtQuick.Window 2.1; +import QtQuick.Controls.Styles 1.1 +import Ethereum 1.0 + +Rectangle { + property var title: "JavaScript" + property var iconFile: "../tx.png" + + objectName: "javascriptView" + visible: false + anchors.fill: parent + + TextField { + id: input + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 20 + + Keys.onReturnPressed: { + var res = eth.evalJavascriptString(this.text); + this.text = ""; + + output.append(res) + } + } + + TextArea { + id: output + verticalAlignment: TextEdit.AlignBottom + text: "> JSRE Ready..." + anchors { + top: parent.top + left: parent.left + right: parent.right + bottom: input.top + } + } +} diff --git a/ethereal/assets/qml/views/pending_tx.qml b/ethereal/assets/qml/views/pending_tx.qml index 18572e3e2a..5c5c496d67 100644 --- a/ethereal/assets/qml/views/pending_tx.qml +++ b/ethereal/assets/qml/views/pending_tx.qml @@ -30,7 +30,7 @@ Rectangle { model: pendingTxModel } - function addTx(type, tx, inout) { + function addTx(tx, inout) { var isContract if (tx.contract == true){ isContract = "Yes" diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 3fc9a024c6..769edfc6a9 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -24,6 +24,7 @@ ApplicationWindow { var chainView = addPlugin("./views/chain.qml", {title: "Block chain"}) var infoView = addPlugin("./views/info.qml", {title: "Info"}) var pendingTxView = addPlugin("./views/pending_tx.qml", {title: "Pending", canClose: true}) + var pendingTxView = addPlugin("./views/javascript.qml", {title: "JavaScript", canClose: true}) // Call the ready handler gui.done() @@ -259,8 +260,8 @@ ApplicationWindow { ********************/ Rectangle { id: menu - Layout.minimumWidth: 180 - Layout.maximumWidth: 180 + Layout.minimumWidth: 80 + Layout.maximumWidth: 80 anchors.top: parent.top color: "#252525" @@ -398,9 +399,9 @@ ApplicationWindow { function importApp(path) { var ext = path.split('.').pop() if(ext == "html" || ext == "htm") { - ui.openHtml(path) + eth.openHtml(path) }else if(ext == "qml"){ - ui.openQml(path) + eth.openQml(path) } } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index a848adf45d..ec2f01741a 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -21,46 +21,62 @@ ApplicationWindow { id: root anchors.fill: parent state: "inspectorShown" - TextField { + + RowLayout { + id: navBar anchors { - top: parent.top left: parent.left right: parent.right } - id: uriNav - //text: webview.url - Keys.onReturnPressed: { - var uri = this.text; - if(!/.*\:\/\/.*/.test(uri)) { - uri = "http://" + uri; + Button { + id: back + iconSource: "../back.png" + onClicked: { + webview.goBack() + } + } + + TextField { + anchors { + top: parent.top + left: back.right + right: parent.right } + id: uriNav + + Keys.onReturnPressed: { + var uri = this.text; + if(!/.*\:\/\/.*/.test(uri)) { + uri = "http://" + uri; + } - var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ + var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ - if(reg.test(uri)) { - this.text.replace(reg, function(match, pre, domain, path) { - uri = pre; + if(reg.test(uri)) { + this.text.replace(reg, function(match, pre, domain, path) { + uri = pre; - var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); - var ip = []; - for(var i = 0, l = lookup.length; i < l; i++) { - ip.push(lookup.charCodeAt(i)) - } + var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); + var ip = []; + for(var i = 0, l = lookup.length; i < l; i++) { + ip.push(lookup.charCodeAt(i)) + } - if(ip.length != 0) { - uri += lookup; - } else { - uri += domain; - } + if(ip.length != 0) { + uri += lookup; + } else { + uri += domain; + } - uri += path; - }); - } + uri += path; + }); + } - console.log("connecting to ...", uri) + console.log("connecting to ...", uri) - webview.url = uri; + webview.url = uri; + } } } @@ -71,7 +87,7 @@ ApplicationWindow { left: parent.left right: parent.right bottom: parent.bottom - top: uriNav.bottom + top: navBar.bottom } onTitleChanged: { window.title = title } experimental.preferences.javascriptEnabled: true @@ -86,103 +102,107 @@ ApplicationWindow { try { switch(data.call) { case "getCoinBase": - postData(data._seed, eth.getCoinBase()) + postData(data._seed, eth.getCoinBase()) + + break - break case "getIsListening": - postData(data._seed, eth.getIsListening()) + postData(data._seed, eth.getIsListening()) + + break - break case "getIsMining": - postData(data._seed, eth.getIsMining()) + postData(data._seed, eth.getIsMining()) + + break - break case "getPeerCount": - postData(data._seed, eth.getPeerCount()) + postData(data._seed, eth.getPeerCount()) - break + break case "getTxCountAt": - require(1) - postData(data._seed, eth.getTxCountAt(data.args[0])) + require(1) + postData(data._seed, eth.getTxCountAt(data.args[0])) + + break - break case "getBlockByNumber": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) + + break - break case "getBlockByHash": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) + + break - break case "transact": - require(5) + require(5) - var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5]) - postData(data._seed, tx) + var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5]) + postData(data._seed, tx) - break - case "create": - postData(data._seed, null) + break - break case "getStorage": - require(2); + require(2); - var stateObject = eth.getStateObject(data.args[0]) - var storage = stateObject.getStorage(data.args[1]) - postData(data._seed, storage) + var stateObject = eth.getStateObject(data.args[0]) + var storage = stateObject.getStorage(data.args[1]) + postData(data._seed, storage) + + break - break case "getStateKeyVals": - require(1); - var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) - postData(data._seed,stateObject) + require(1); + var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) + postData(data._seed,stateObject) + + break - break case "getTransactionsFor": - require(1); - var txs = eth.getTransactionsFor(data.args[0], true) - postData(data._seed, txs) + require(1); + var txs = eth.getTransactionsFor(data.args[0], true) + postData(data._seed, txs) + + break - break case "getBalance": - require(1); + require(1); + + postData(data._seed, eth.getStateObject(data.args[0]).value()); - postData(data._seed, eth.getStateObject(data.args[0]).value()); + break - break case "getKey": - var key = eth.getKey().privateKey; + var key = eth.getKey().privateKey; + + postData(data._seed, key) + break - postData(data._seed, key) - break case "watch": - require(1) - eth.watch(data.args[0], data.args[1]); - break + require(1) + eth.watch(data.args[0], data.args[1]); + + break + case "disconnect": - require(1) - postData(data._seed, null) - break; - case "set": - console.log("'Set' has been depcrecated") - /* - for(var key in data.args) { - if(webview.hasOwnProperty(key)) { - window[key] = data.args[key]; - } - } - */ - break; + require(1) + postData(data._seed, null) + + break; + case "getSecretToAddress": - require(1) - postData(data._seed, eth.secretToAddress(data.args[0])) - break; + require(1) + postData(data._seed, eth.secretToAddress(data.args[0])) + + break; + case "debug": - console.log(data.args[0]); + console.log(data.args[0]); break; } } catch(e) { @@ -215,12 +235,13 @@ ApplicationWindow { postEvent(ev, [storageObject.address, storageObject.value]) } } + Rectangle { id: toggleInspector color: "#bcbcbc" visible: true - height: 12 - width: 12 + height: 20 + width: 20 anchors { right: root.right } @@ -233,8 +254,8 @@ ApplicationWindow { inspector.url = webview.experimental.remoteInspectorUrl } } + onDoubleClicked: { - console.log('refreshing') webView.reload() } anchors.fill: parent diff --git a/ethereal/gui.go b/ethereal/gui.go index a4e3efb19a..710a1bd1e1 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -282,7 +282,11 @@ func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) { ptx.Sender = s ptx.Address = r - gui.getObjectByName("transactionView").Call("addTx", window, ptx, inout) + if window == "post" { + gui.getObjectByName("transactionView").Call("addTx", ptx, inout) + } else { + gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout) + } } func (gui *Gui) readPreviousTransactions() { diff --git a/ethereal/main.go b/ethereal/main.go index 04a04536d2..431307c6f3 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -17,6 +17,9 @@ const ( func main() { runtime.GOMAXPROCS(runtime.NumCPU()) + // This is a bit of a cheat, but ey! + os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999") + qml.Init(nil) var interrupted = false diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index b7cabf3a8e..f900fcaeef 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "path" "github.com/ethereum/eth-go" @@ -42,6 +43,15 @@ func (self *UiLib) EvalJavascriptFile(path string) { self.jsEngine.LoadExtFile(path[7:]) } +func (self *UiLib) EvalJavascriptString(str string) string { + value, err := self.jsEngine.Run(str) + if err != nil { + return err.Error() + } + + return fmt.Sprintf("%v", value) +} + func (ui *UiLib) OpenQml(path string) { container := NewQmlApplication(path[7:], ui) app := NewExtApplication(container, ui)