diff options
25 files changed, 571 insertions, 327 deletions
diff --git a/ethereal/assets/back.png b/ethereal/assets/back.png Binary files differnew file mode 100644 index 000000000..38fc84d6e --- /dev/null +++ b/ethereal/assets/back.png diff --git a/ethereal/assets/bug.png b/ethereal/assets/bug.png Binary files differnew file mode 100644 index 000000000..f5e85dc99 --- /dev/null +++ b/ethereal/assets/bug.png diff --git a/ethereal/assets/close.png b/ethereal/assets/close.png Binary files differnew file mode 100644 index 000000000..88df442c5 --- /dev/null +++ b/ethereal/assets/close.png diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index de6fb0255..03b25179b 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -1,39 +1,112 @@ // Main Ethereum library window.eth = { prototype: Object(), + _callbacks: {}, + _onCallbacks: {}, + + 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; + } + + postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb); }, - create: function(sec, value, gas, gasPrice, init, body, cb) { - postData({call: "create", args: [sec, value, gas, gasPrice, init, body]}, cb); + getMessages: function(filter, cb) { + postData({call: "messages", args: [filter]}, cb); }, getStorageAt: function(address, storageAddress, cb) { postData({call: "getStorage", args: [address, storageAddress]}, cb); }, - getStateKeyVals: function(address, cb){ - postData({call: "getStateKeyVals", args: [address]}, cb); + getEachStorageAt: function(address, cb){ + postData({call: "getEachStorage", args: [address]}, cb); }, getKey: function(cb) { @@ -66,6 +139,7 @@ window.eth = { postData({call: "getSecretToAddress", args: [sec]}, cb); }, + /* watch: function(address, storageAddrOrCb, cb) { var ev; if(cb === undefined) { @@ -95,6 +169,16 @@ window.eth = { postData({call: "disconnect", args: [address, storageAddrOrCb]}); }, + */ + + watch: function(options) { + var filter = new Filter(options); + filter.number = newWatchNum().toString() + + postData({call: "watch", args: [options, filter.number]}) + + return filter; + }, set: function(props) { postData({call: "set", args: props}); @@ -137,9 +221,53 @@ window.eth = { } } }, +} +var Filter = function(options) { + this.options = options; +}; +Filter.prototype.changed = function(callback) { + // Register the watched:<number>. Qml will call the appropriate event if anything + // interesting happens in the land of Go. + eth.on("watched:"+this.number, callback) } -window.eth._callbacks = {} -window.eth._onCallbacks = {} +Filter.prototype.getMessages = function(cb) { + return eth.getMessages(this.options, cb) +} + +var watchNum = 0; +function newWatchNum() { + return watchNum++; +} + +function postData(data, cb) { + data._seed = Math.floor(Math.random() * 1000000) + if(cb) { + eth._callbacks[data._seed] = cb; + } + + if(data.args === undefined) { + data.args = []; + } + + navigator.qt.postMessage(JSON.stringify(data)); +} + +navigator.qt.onmessage = function(ev) { + var data = JSON.parse(ev.data) + + if(data._event !== undefined) { + eth.trigger(data._event, data.data); + } else { + if(data._seed) { + var cb = eth._callbacks[data._seed]; + if(cb) { + cb.call(this, data.data) + // Remove the "trigger" callback + delete eth._callbacks[ev._seed]; + } + } + } +} diff --git a/ethereal/assets/ext/home.html b/ethereal/assets/ext/home.html index 86a659d65..a524e8403 100644 --- a/ethereal/assets/ext/home.html +++ b/ethereal/assets/ext/home.html @@ -8,14 +8,15 @@ h1 { text-align: center; font-family: Courier; font-size: 50pt; - margin-top: 25% } </style> </head> <body> <h1>... Ethereum ...</h1> -<!-- ĐΞV --!> +<ul> + <li><a href="http://std.eth">std::Service</a></li> +</ul> </body> </html> diff --git a/ethereal/assets/ext/messaging.js b/ethereal/assets/ext/messaging.js deleted file mode 100644 index e7bc63020..000000000 --- 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/ext/pre.js b/ethereal/assets/ext/pre.js index ca520f152..3e8a534e9 100644 --- a/ethereal/assets/ext/pre.js +++ b/ethereal/assets/ext/pre.js @@ -1,18 +1,3 @@ -function debug(/**/) { - var args = arguments; - var msg = "" - for(var i = 0; i < args.length; i++){ - if(typeof args[i] === "object") { - msg += " " + JSON.stringify(args[i]) - } else { - msg += " " + args[i] - } - } - - postData({call:"debug", args:[msg]}) - document.getElementById("debug").innerHTML += "<br>" + msg -} - // Helper function for generating pseudo callbacks and sending data to the QML part of the application function postData(data, cb) { data._seed = Math.floor(Math.random() * 1000000) @@ -50,9 +35,3 @@ navigator.qt.onmessage = function(ev) { } } } - -window.onerror = function(message, file, lineNumber, column, errorObj) { - debug(file, message, lineNumber+":"+column, errorObj); - - return false; -} diff --git a/ethereal/assets/ext/test.html b/ethereal/assets/ext/test.html new file mode 100644 index 000000000..0d6b710fa --- /dev/null +++ b/ethereal/assets/ext/test.html @@ -0,0 +1,33 @@ +<!doctype> +<html> +<head> +<title>Tests</title> +</head> + +<body> +<button onclick="test();">Test me</button> + +<script type="text/javascript"> +function test() { + var filter = eth.watch({ + latest: -1, + from: "e6716f9544a56c530d868e4bfbacb172315bdead", + }); + + filter.changed(function(messages) { + console.log("messages", messages) + }) + + filter.getMessages(function(messages) { + console.log("getMessages", messages) + }); + + eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(a, b) { + console.log(a,b) + }) +} +</script> + +</body> + +</html> diff --git a/ethereal/assets/pick.png b/ethereal/assets/pick.png Binary files differnew file mode 100644 index 000000000..2f5a261c2 --- /dev/null +++ b/ethereal/assets/pick.png diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml index f50ae8004..a73b7367d 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 9e05e2f8e..fcddd46e2 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 000000000..376397130 --- /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 18572e3e2..5c5c496d6 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/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index 61a1b81cd..80e1670f8 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -18,13 +18,8 @@ Rectangle { Column { id: mainContractColumn anchors.fill: parent - function contractFormReady(){ - if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) { - txButton.state = "READY" - }else{ - txButton.state = "NOTREADY" - } - } + + states: [ State{ name: "ERROR" @@ -208,4 +203,12 @@ Rectangle { } } } + + function contractFormReady(){ + if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) { + txButton.state = "READY" + }else{ + txButton.state = "NOTREADY" + } + } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 58d39381b..b35500209 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -19,11 +19,12 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { - var historyView = addPlugin("./views/history.qml") - var newTxView = addPlugin("./views/transaction.qml") - var chainView = addPlugin("./views/chain.qml") - var infoView = addPlugin("./views/info.qml") - var pendingTxView = addPlugin("./views/pending_tx.qml") + var historyView = addPlugin("./views/history.qml", {title: "History"}) + var newTxView = addPlugin("./views/transaction.qml", {title: "New Transaction"}) + 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() @@ -38,7 +39,7 @@ ApplicationWindow { return } - return mainSplit.addComponent(component, {objectName: objectName}) + return mainSplit.addComponent(component, options) } MenuBar { @@ -97,7 +98,7 @@ ApplicationWindow { MenuItem { text: "Debugger" shortcut: "Ctrl+d" - onTriggered: ui.startDebugger() + onTriggered: eth.startDebugger() } MenuItem { @@ -111,7 +112,7 @@ ApplicationWindow { text: "Run JS file" onTriggered: { generalFileDialog.callback = function(path) { - lib.evalJavascriptFile(path) + eth.evalJavascriptFile(path) } generalFileDialog.open() } @@ -169,8 +170,6 @@ ApplicationWindow { RowLayout { Label { - anchors.left: importAppButton.right - anchors.leftMargin: 5 id: walletValueLabel font.pixelSize: 10 @@ -250,7 +249,7 @@ ApplicationWindow { return; } - menu.createMenuItem(view.iconFile, view); + menu.createMenuItem(view.iconFile, view, options); mainSplit.views.push(view); return view @@ -280,11 +279,82 @@ ApplicationWindow { } } + /* + Component { + id: menuItemTemplate + + RowLayout { + property var view; + property alias source: icon.source; + property alias title: title.text + height: 25 + + id: tab + + anchors { + left: parent.left + right: parent.right + } + + Image { + id: icon + //anchors.horizontalCenter: parent.horizontalCenter + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view) + } + } + + Rectangle { + color: "#bbbbbb" + Label { + id: title + y: parent.height / 2 - this.height / 2 + //x: 5 + font.pixelSize: 10 + } + + + Image { + id: closeButton + y: parent.height / 2 - this.height / 2 + visible: false + + source: "../close.png" + anchors { + right: parent.right + rightMargin: 5 + } + + MouseArea { + anchors.fill: parent + onClicked: { + console.log("should close") + } + } + } + } + } + } + */ + + function createMenuItem(icon, view, options) { + if(options === undefined) { + options = {}; + } - function createMenuItem(icon, view) { var comp = menuItemTemplate.createObject(menuColumn) comp.view = view comp.source = icon + //comp.title = options.title + /* + if(options.canClose) { + //comp.closeButton.visible = options.canClose + } + */ } ColumnLayout { @@ -338,9 +408,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) } } @@ -459,7 +529,7 @@ ApplicationWindow { anchors.leftMargin: 5 text: "Import" onClicked: { - lib.importTx(txImportField.text) + eth.importTx(txImportField.text) txImportField.visible = false } } @@ -483,7 +553,7 @@ ApplicationWindow { anchors.leftMargin: 10 placeholderText: "address:port" onAccepted: { - ui.connectToPeer(addrField.text) + eth.connectToPeer(addrField.text) addPeerWin.visible = false } } @@ -493,7 +563,7 @@ ApplicationWindow { anchors.leftMargin: 5 text: "Add" onClicked: { - ui.connectToPeer(addrField.text) + eth.connectToPeer(addrField.text) addPeerWin.visible = false } } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 15177e3fd..5faf50e91 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0 import QtQuick.Controls 1.0; +import QtQuick.Controls.Styles 1.0 import QtQuick.Layouts 1.0; import QtQuick.Window 2.1; import Ethereum 1.0 @@ -9,8 +10,8 @@ import Ethereum 1.0 ApplicationWindow { id: window title: "Ethereum" - width: 900 - height: 600 + width: 1000 + height: 800 minimumHeight: 300 property alias url: webview.url @@ -21,42 +22,59 @@ ApplicationWindow { id: root anchors.fill: parent state: "inspectorShown" - TextField { + + RowLayout { + id: navBar + height: 40 anchors { - top: parent.top left: parent.left right: parent.right + leftMargin: 7 + } + + Button { + id: back + onClicked: { + webview.goBack() + } + style: ButtonStyle { + background: Image { + source: "../back.png" + width: 30 + height: 30 + } + } } - id: uriNav - //text: webview.url - - Keys.onReturnPressed: { - var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ - - var uri = this.text; - 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)) - } - - if(ip.length != 0) { - uri += ip.join("."); - } else { - uri += domain; - } - - uri += path; - }); + + TextField { + anchors { + left: back.right + right: toggleInspector.left + leftMargin: 5 + rightMargin: 5 } + id: uriNav + y: parent.height / 2 - this.height / 2 - console.log("connecting to ...", uri) + Keys.onReturnPressed: { + webview.url = this.text; + } + } - webview.url = uri; + Button { + id: toggleInspector + anchors { + right: parent.right + } + iconSource: "../bug.png" + onClicked: { + if(inspector.visible == true){ + inspector.visible = false + }else{ + inspector.visible = true + inspector.url = webview.experimental.remoteInspectorUrl + } + } } } @@ -67,9 +85,48 @@ ApplicationWindow { left: parent.left right: parent.right bottom: parent.bottom - top: uriNav.bottom + top: navBar.bottom } onTitleChanged: { window.title = title } + + property var cleanPath: false + onNavigationRequested: { + if(!this.cleanPath) { + var uri = request.url.toString(); + if(!/.*\:\/\/.*/.test(uri)) { + uri = "http://" + uri; + } + + var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ + + if(reg.test(uri)) { + uri.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)) + } + + if(ip.length != 0) { + uri += lookup; + } else { + uri += domain; + } + + uri += path; + }); + } + + this.cleanPath = true; + + webview.url = uri; + } else { + // Prevent inf loop. + this.cleanPath = false; + } + } experimental.preferences.javascriptEnabled: true experimental.preferences.navigatorQtObjectEnabled: true experimental.preferences.developerExtrasEnabled: true @@ -85,14 +142,17 @@ ApplicationWindow { 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()) @@ -103,16 +163,19 @@ ApplicationWindow { 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) @@ -120,10 +183,7 @@ ApplicationWindow { postData(data._seed, tx) break - case "create": - postData(data._seed, null) - break case "getStorage": require(2); @@ -132,53 +192,67 @@ ApplicationWindow { postData(data._seed, storage) break - case "getStateKeyVals": + + case "getEachStorage": 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 + require(1) + eth.watch(data.args[0], data.args[1]); + + break + */ + case "watch": + require(2) + eth.watch(data.args[0], data.args[1]) + 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 "messages": + require(1); + + var messages = JSON.parse(eth.getMessages(data.args[0])) + postData(data._seed, messages) + + break + case "debug": - console.log(data.args[0]); + console.log(data.args[0]); break; } } catch(e) { @@ -200,6 +274,11 @@ ApplicationWindow { webview.experimental.postMessage(JSON.stringify({data: data, _event: event})) } + function onWatchedCb(data, id) { + var messages = JSON.parse(data) + postEvent("watched:"+id, messages) + } + function onNewBlockCb(block) { postEvent("block:new", block) } @@ -211,31 +290,7 @@ ApplicationWindow { postEvent(ev, [storageObject.address, storageObject.value]) } } - Rectangle { - id: toggleInspector - color: "#bcbcbc" - visible: true - height: 12 - width: 12 - anchors { - right: root.right - } - MouseArea { - onClicked: { - if(inspector.visible == true){ - inspector.visible = false - }else{ - inspector.visible = true - inspector.url = webview.experimental.remoteInspectorUrl - } - } - onDoubleClicked: { - console.log('refreshing') - webView.reload() - } - anchors.fill: parent - } - } + Rectangle { id: sizeGrip diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 697630504..37e9676ff 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -1,11 +1,13 @@ package main import ( + "encoding/json" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethstate" - "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" ) @@ -20,16 +22,21 @@ type AppContainer interface { ObjectChanged(*ethstate.StateObject) StorageChanged(*ethstate.StorageState) NewWatcher(chan bool) + Messages(ethstate.Messages, string) } type ExtApplication struct { *ethpub.PEthereum + eth ethchain.EthManager blockChan chan ethreact.Event changeChan chan ethreact.Event + messageChan chan ethreact.Event quitChan chan bool watcherQuitChan chan bool + filters map[string]*ethchain.Filter + container AppContainer lib *UiLib registeredEvents []string @@ -37,11 +44,14 @@ type ExtApplication struct { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ - ethpub.NewPEthereum(lib.eth), + ethpub.New(lib.eth), + lib.eth, + make(chan ethreact.Event, 100), make(chan ethreact.Event, 100), make(chan ethreact.Event, 100), make(chan bool), make(chan bool), + make(map[string]*ethchain.Filter), container, lib, nil, @@ -68,6 +78,7 @@ func (app *ExtApplication) run() { // Subscribe to events reactor := app.lib.eth.Reactor() reactor.Subscribe("newBlock", app.blockChan) + reactor.Subscribe("messages", app.messageChan) app.container.NewWatcher(app.watcherQuitChan) @@ -113,20 +124,38 @@ out: } else if storageObject, ok := object.Resource.(*ethstate.StorageState); ok { app.container.StorageChanged(storageObject) } + + case msg := <-app.messageChan: + if messages, ok := msg.Resource.(ethstate.Messages); ok { + for id, filter := range app.filters { + msgs := filter.FilterMessages(messages) + if len(msgs) > 0 { + app.container.Messages(msgs, id) + } + } + } } } } -func (app *ExtApplication) Watch(addr, storageAddr string) { - var event string - if len(storageAddr) == 0 { - event = "object:" + string(ethutil.Hex2Bytes(addr)) - app.lib.eth.Reactor().Subscribe(event, app.changeChan) - } else { - event = "storage:" + string(ethutil.Hex2Bytes(addr)) + ":" + string(ethutil.Hex2Bytes(storageAddr)) - app.lib.eth.Reactor().Subscribe(event, app.changeChan) +func (self *ExtApplication) Watch(filterOptions map[string]interface{}, identifier string) { + self.filters[identifier] = ethchain.NewFilterFromMap(filterOptions, self.eth) +} + +func (self *ExtApplication) GetMessages(object map[string]interface{}) string { + filter := ethchain.NewFilterFromMap(object, self.eth) + + messages := filter.Find() + var msgs []javascript.JSMessage + for _, m := range messages { + msgs = append(msgs, javascript.NewJSMessage(m)) + } + + b, err := json.Marshal(msgs) + if err != nil { + return "{\"error\":" + err.Error() + "}" } - app.registeredEvents = append(app.registeredEvents, event) + return string(b) } diff --git a/ethereal/gui.go b/ethereal/gui.go index 78a930e02..c0584936d 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -57,7 +57,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden panic(err) } - pub := ethpub.NewPEthereum(ethereum) + pub := ethpub.New(ethereum) return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config} } @@ -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() { @@ -387,12 +391,12 @@ func (gui *Gui) update() { if bytes.Compare(tx.Sender(), gui.address()) == 0 { object.SubAmount(tx.Value) - gui.win.Root().Call("addTx", "post", ethpub.NewPTx(tx), "send") + gui.getObjectByName("transactionView").Call("addTx", ethpub.NewPTx(tx), "send") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { object.AddAmount(tx.Value) - gui.win.Root().Call("addTx", "post", ethpub.NewPTx(tx), "recv") + gui.getObjectByName("transactionView").Call("addTx", ethpub.NewPTx(tx), "recv") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } @@ -511,11 +515,13 @@ func (gui *Gui) Printf(format string, v ...interface{}) { // Print function that logs directly to the GUI func (gui *Gui) printLog(s string) { - str := strings.TrimRight(s, "\n") - lines := strings.Split(str, "\n") + /* + str := strings.TrimRight(s, "\n") + lines := strings.Split(str, "\n") - view := gui.getObjectByName("infoView") - for _, line := range lines { - view.Call("addLog", line) - } + view := gui.getObjectByName("infoView") + for _, line := range lines { + view.Call("addLog", line) + } + */ } diff --git a/ethereal/html_container.go b/ethereal/html_container.go index 40a9f5584..7deee487d 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "errors" "io/ioutil" "net/url" @@ -12,6 +13,7 @@ import ( "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" "github.com/howeyc/fsnotify" ) @@ -131,6 +133,17 @@ func (app *HtmlApplication) StorageChanged(storageObject *ethstate.StorageState) app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject)) } +func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) { + var msgs []javascript.JSMessage + for _, m := range messages { + msgs = append(msgs, javascript.NewJSMessage(m)) + } + + b, _ := json.Marshal(msgs) + + self.webView.Call("onWatchedCb", string(b), id) +} + func (app *HtmlApplication) Destroy() { app.engine.Destroy() } diff --git a/ethereal/main.go b/ethereal/main.go index 04a04536d..431307c6f 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/qml_container.go b/ethereal/qml_container.go index 53ff13c2f..45a6c0327 100644 --- a/ethereal/qml_container.go +++ b/ethereal/qml_container.go @@ -1,12 +1,14 @@ package main import ( + "fmt" + "runtime" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/go-qml/qml" - "runtime" ) type QmlApplication struct { @@ -59,6 +61,10 @@ func (app *QmlApplication) StorageChanged(storageObject *ethstate.StorageState) app.win.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject)) } +func (self *QmlApplication) Messages(msgs ethstate.Messages, id string) { + fmt.Println("IMPLEMENT QML APPLICATION MESSAGES METHOD") +} + // Getters func (app *QmlApplication) Engine() *qml.Engine { return app.engine diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index b7cabf3a8..f900fcaee 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) diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index 158fc93cf..d384c5048 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -50,7 +50,7 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { re := &JSRE{ ethereum, otto.New(), - ethpub.NewPEthereum(ethereum), + ethpub.New(ethereum), make(chan ethreact.Event, 10), make(chan ethreact.Event, 10), make(chan bool), diff --git a/javascript/types.go b/javascript/types.go index f9d18b26a..fb1e54ae7 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -39,15 +39,16 @@ func (self *JSBlock) GetTransaction(hash string) otto.Value { } type JSMessage struct { - To, From string - Input string - Output string - Path int - Origin string - Timestamp int32 - Coinbase string - Block string - Number int32 + To string `json:"to"` + From string `json:"from"` + Input string `json:"input"` + Output string `json:"output"` + Path int `json:"path"` + Origin string `json:"origin"` + Timestamp int32 `json:"timestamp"` + Coinbase string `json:"coinbase"` + Block string `json:"block"` + Number int32 `json:"number"` } func NewJSMessage(message *ethstate.Message) JSMessage { @@ -137,6 +138,7 @@ func (self *JSEthereum) Messages(object map[string]interface{}) otto.Value { filter.SetEarliestBlock(earliest) } } + if object["latest"] != nil { latest := object["latest"] if l, ok := latest.(string); ok { @@ -146,10 +148,10 @@ func (self *JSEthereum) Messages(object map[string]interface{}) otto.Value { } } if object["to"] != nil { - filter.SetTo(ethutil.Hex2Bytes(object["to"].(string))) + filter.AddTo(ethutil.Hex2Bytes(object["to"].(string))) } if object["from"] != nil { - filter.SetFrom(ethutil.Hex2Bytes(object["from"].(string))) + filter.AddFrom(ethutil.Hex2Bytes(object["from"].(string))) } if object["max"] != nil { filter.SetMax(object["max"].(int)) diff --git a/utils/cmd.go b/utils/cmd.go index 4b9d0644c..7b508e0bc 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -1,8 +1,17 @@ package utils import ( - "bitbucket.org/kardianos/osext" "fmt" + "io" + "log" + "os" + "os/signal" + "path" + "path/filepath" + "runtime" + "time" + + "bitbucket.org/kardianos/osext" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethdb" @@ -12,14 +21,6 @@ import ( "github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "io" - "log" - "os" - "os/signal" - "path" - "path/filepath" - "runtime" - "time" ) var logger = ethlog.NewLogger("CLI") @@ -226,7 +227,7 @@ func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, Se func StartRpc(ethereum *eth.Ethereum, RpcPort int) { var err error - ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort) + ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.New(ethereum), RpcPort) if err != nil { logger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err) } else { |