From b4bd70c402e75f933e694e9f9df14912df468e69 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 22 Sep 2014 14:54:27 +0200 Subject: [PATCH] Re-wrote ethereum.js --- mist/assets/debugger/debugger.qml | 8 +- mist/assets/ext/filter.js | 8 +- mist/assets/ext/html_messaging.js | 350 ++++++++++++++++- mist/assets/ext/pre.js | 38 -- mist/assets/qml/wallet.qml | 12 +- mist/assets/qml/webapp.qml | 632 ++++++++++++++++-------------- mist/debugger.go | 2 +- mist/ui_lib.go | 44 ++- 8 files changed, 729 insertions(+), 365 deletions(-) diff --git a/mist/assets/debugger/debugger.qml b/mist/assets/debugger/debugger.qml index 2309a443b..8d54b5b5d 100644 --- a/mist/assets/debugger/debugger.qml +++ b/mist/assets/debugger/debugger.qml @@ -221,7 +221,7 @@ ApplicationWindow { } height: parent.height width: 300 - TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 } + TableViewColumn{ role: "value" ; title: "Local VM stack" ; width: stackTableView.width - 2 } model: stackModel } @@ -233,7 +233,7 @@ ApplicationWindow { height: parent.height width: parent.width - stackTableView.width TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 } - TableViewColumn{ role: "value" ; title: "Memory" ; width: 750 } + TableViewColumn{ role: "value" ; title: "Memory" ; width: 650 } model: memModel } } @@ -248,8 +248,8 @@ ApplicationWindow { } height: parent.height width: parent.width - TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2} - TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2} + TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2 - 1} + TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2 - 1} model: storageModel } } diff --git a/mist/assets/ext/filter.js b/mist/assets/ext/filter.js index bc6a93144..c23706249 100644 --- a/mist/assets/ext/filter.js +++ b/mist/assets/ext/filter.js @@ -31,11 +31,11 @@ Filter.prototype.changed = function(callback) { this.callbacks.push(callback); var self = this; - message.connect(function(messages, id) { + messages.connect(function(messages, id) { if(id == self.id) { - for(var i = 0; i < self.callbacks.length; i++) { - self.callbacks[i].call(self, messages); - } + for(var i = 0; i < self.callbacks.length; i++) { + self.callbacks[i].call(self, messages); + } } }); }; diff --git a/mist/assets/ext/html_messaging.js b/mist/assets/ext/html_messaging.js index cf6d72cfa..3c67c77ea 100644 --- a/mist/assets/ext/html_messaging.js +++ b/mist/assets/ext/html_messaging.js @@ -12,15 +12,46 @@ window.eth = { _callbacks: {}, - _onCallbacks: {}, + _events: {}, prototype: Object(), - coinbase: function() { - return new Promise(function(resolve, reject) { - postData({call: "getCoinBase"}, function(coinbase) { - resolve(coinbase); - }); - }); + 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 }, block: function(numberOrHash) { @@ -107,17 +138,316 @@ }); }, - key: function() { + balanceAt: function(address) { + var promises = []; + + if(isPromise(address)) { + promises.push(address.then(function(_address) { address = _address; })); + } + + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "getBalanceAt", args: [address]}, function(balance) { + resolve(balance); + }); + }); + }); + }, + + countAt: function(address) { + var promises = []; + + if(isPromise(address)) { + promises.push(address.then(function(_address) { address = _address; })); + } + + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "getCountAt", args: [address]}, function(count) { + resolve(count); + }); + }); + }); + }, + + codeAt: function(address) { + var promises = []; + + if(isPromise(address)) { + promises.push(address.then(function(_address) { address = _address; })); + } + + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "getCodeAt", args: [address]}, function(code) { + resolve(code); + }); + }); + }); + }, + + storageAt: function(address, storageAddress) { + var promises = []; + + if(isPromise(address)) { + promises.push(address.then(function(_address) { address = _address; })); + } + + if(isPromise(storageAddress)) { + promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; })); + } + + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "getStorageAt", args: [address, storageAddress]}, function(entry) { + resolve(entry); + }); + }); + }); + }, + + stateAt: function(address, storageAddress) { + return this.storageAt(address, storageAddress); + }, + + call: function(params) { + if(params === undefined) { + params = {}; + } + + if(params.endowment !== undefined) + params.value = params.endowment; + if(params.code !== undefined) + params.data = params.code; + + + var promises = [] + if(isPromise(params.to)) { + promises.push(params.to.then(function(_to) { params.to = _to; })); + } + if(isPromise(params.from)) { + promises.push(params.from.then(function(_from) { params.from = _from; })); + } + + if(isPromise(params.data)) { + promises.push(params.data.then(function(_code) { params.data = _code; })); + } else { + if(typeof params.data === "object") { + data = ""; + for(var i = 0; i < params.data.length; i++) { + data += params.data[i] + } + } else { + data = params.data; + } + } + + // Make sure everything is string + var fields = ["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(); + } + + // Load promises then call the last "transact". + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "call", args: params}, function(data) { + if(data[1]) + reject(data[0]); + else + resolve(data[0]); + }); + }); + }) + }, + + watch: function(params) { + return new Filter(params); + }, + + secretToAddress: function(key) { + var promises = []; + if(isPromise(key)) { + promises.push(key.then(function(_key) { key = _key; })); + } + + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "getSecretToAddress", args: [key]}, function(address) { + resolve(address); + }); + }); + }); + }, + + on: function(event, cb) { + if(eth._events[event] === undefined) { + eth._events[event] = []; + } + + eth._events[event].push(cb); + + return this + }, + + off: function(event, cb) { + if(eth._events[event] !== undefined) { + var callbacks = eth._events[event]; + for(var i = 0; i < callbacks.length; i++) { + if(callbacks[i] === cb) { + delete callbacks[i]; + } + } + } + + return this + }, + + trigger: function(event, data) { + var callbacks = eth._events[event]; + if(callbacks !== undefined) { + for(var i = 0; i < callbacks.length; i++) { + // Figure out whether the returned data was an array + // array means multiple return arguments (multiple params) + if(data instanceof Array) { + callbacks[i].apply(this, data); + } else { + callbacks[i].call(this, data); + } + } + } + }, + }; + + // Eth object properties + Object.defineProperty(eth, "key", { + get: function() { return new Promise(function(resolve, reject) { postData({call: "getKey"}, function(k) { resolve(k); }); }); + }, + }); + + Object.defineProperty(eth, "gasPrice", { + get: function() { + return "1000000000000" + } + }); + + Object.defineProperty(eth, "coinbase", { + get: function() { + return new Promise(function(resolve, reject) { + postData({call: "getCoinBase"}, function(coinbase) { + resolve(coinbase); + }); + }); + }, + }); + + Object.defineProperty(eth, "listening", { + get: function() { + return new Promise(function(resolve, reject) { + postData({call: "getIsListening"}, function(listening) { + resolve(listening); + }); + }); + }, + }); + + + Object.defineProperty(eth, "mining", { + get: function() { + return new Promise(function(resolve, reject) { + postData({call: "getIsMining"}, function(mining) { + resolve(mining); + }); + }); + }, + }); + + Object.defineProperty(eth, "peerCount", { + get: function() { + return new Promise(function(resolve, reject) { + postData({call: "getPeerCount"}, function(peerCount) { + resolve(peerCount); + }); + }); + }, + }); + + var filters = []; + var Filter = function(options) { + filters.push(this); + + this.callbacks = []; + this.options = options; + + var call; + if(options === "chain") { + call = "newFilterString" + } else if(typeof options === "object") { + call = "newFilter" + } + + var self = this; // Cheaper than binding + this.promise = new Promise(function(resolve, reject) { + postData({call: call, args: [options]}, function(id) { + self.id = id; + + resolve(id); + }); + }); + }; + + Filter.prototype.changed = function(callback) { + var self = this; + this.promise.then(function(id) { + self.callbacks.push(callback); + }); + }; + + Filter.prototype.trigger = function(messages, id) { + if(id == this.id) { + for(var i = 0; i < this.callbacks.length; i++) { + this.callbacks[i].call(this, messages); + } } }; + Filter.prototype.uninstall = function() { + this.promise.then(function(id) { + postData({call: "uninstallFilter", args:[id]}); + }); + }; + + Filter.prototype.messages = function() { + var self=this; + return Q.all([this.promise]).then(function() { + var id = self.id + return new Promise(function(resolve, reject) { + postData({call: "getMessages", args: [id]}, function(messages) { + resolve(messages); + }); + }); + }); + }; + + // Register to the messages callback. "messages" will be emitted when new messages + // from the client have been created. + eth.on("messages", function(messages, id) { + for(var i = 0; i < filters.length; i++) { + filters[i].trigger(messages, id); + } + }); + + var g_seed = 1; function postData(data, cb) { - data._seed = Math.floor(Math.random() * 1000000) + data._seed = g_seed; if(cb) { eth._callbacks[data._seed] = cb; } @@ -126,6 +456,8 @@ data.args = []; } + g_seed++; + navigator.qt.postMessage(JSON.stringify(data)); } diff --git a/mist/assets/ext/pre.js b/mist/assets/ext/pre.js index 528149f6b..f298fe9a1 100644 --- a/mist/assets/ext/pre.js +++ b/mist/assets/ext/pre.js @@ -1,41 +1,3 @@ -// 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) - 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) { - // Figure out whether the returned data was an array - // array means multiple return arguments (multiple params) - if(data.data instanceof Array) { - cb.apply(this, data.data) - } else { - cb.call(this, data.data) - } - - // Remove the "trigger" callback - delete eth._callbacks[ev._seed]; - } - } - } -} - if(typeof(Promise) === "undefined") { window.Promise = Q.Promise; } diff --git a/mist/assets/qml/wallet.qml b/mist/assets/qml/wallet.qml index 5b402a214..c4379cd47 100644 --- a/mist/assets/qml/wallet.qml +++ b/mist/assets/qml/wallet.qml @@ -14,20 +14,22 @@ ApplicationWindow { property alias miningButtonText: miningButton.text property var ethx : Eth.ethx + property var web - width: 900 - height: 600 + width: 1024 + height: 750 minimumHeight: 300 title: "Mist" // This signal is used by the filter API. The filter API connects using this signal handler from // the different QML files and plugins. - signal message(var callback, int seed); + signal messages(var messages, int id); function invokeFilterCallback(data, receiverSeed) { //var messages = JSON.parse(data) // Signal handler - message(data, receiverSeed); + messages(data, receiverSeed); + root.web.messages(data, receiverSeed); } TextField { @@ -44,7 +46,7 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); - addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + root.web = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); diff --git a/mist/assets/qml/webapp.qml b/mist/assets/qml/webapp.qml index 186718ab5..8801b6b47 100644 --- a/mist/assets/qml/webapp.qml +++ b/mist/assets/qml/webapp.qml @@ -10,265 +10,279 @@ import Ethereum 1.0 import "../ext/qml_messaging.js" as Messaging //ApplicationWindow { -Rectangle { - id: window - property var title: "Browser" - property var iconSource: "../browser.png" - property var menuItem + Rectangle { + id: window + property var title: "Browser" + property var iconSource: "../browser.png" + property var menuItem - property alias url: webview.url - property alias webView: webview + property alias url: webview.url + property alias webView: webview - Component.onCompleted: { - webview.url = "http://etherian.io" - } - - Item { - objectName: "root" - id: root - anchors.fill: parent - state: "inspectorShown" - - RowLayout { - id: navBar - height: 40 - anchors { - 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 - } - } - } - - TextField { - anchors { - left: back.right - right: toggleInspector.left - leftMargin: 5 - rightMargin: 5 - } - text: "http://etherian.io" - id: uriNav - y: parent.height / 2 - this.height / 2 - - Keys.onReturnPressed: { - webview.url = this.text; - } - } - - 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 - } - } - } + Component.onCompleted: { + webview.url = "http://etherian.io" } + signal messages(var messages, int id); + onMessages: { + // Bit of a cheat to get proper JSON + var m = JSON.parse(JSON.parse(JSON.stringify(messages))) + webview.postEvent("messages", [m, id]); + } - WebView { - objectName: "webView" - id: webview - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - top: navBar.bottom - } + Item { + objectName: "root" + id: root + anchors.fill: parent + state: "inspectorShown" - property var cleanPath: false - onNavigationRequested: { - if(!this.cleanPath) { - var uri = request.url.toString(); - if(!/.*\:\/\/.*/.test(uri)) { - uri = "http://" + uri; + RowLayout { + id: navBar + height: 40 + anchors { + left: parent.left + right: parent.right + leftMargin: 7 + } + + Button { + id: back + onClicked: { + webview.goBack() } - - 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; - }); + style: ButtonStyle { + background: Image { + source: "../back.png" + width: 30 + height: 30 + } } + } - this.cleanPath = true; + TextField { + anchors { + left: back.right + right: toggleInspector.left + leftMargin: 5 + rightMargin: 5 + } + text: "http://etherian.io" + id: uriNav + y: parent.height / 2 - this.height / 2 - webview.url = uri; - } else { - // Prevent inf loop. - this.cleanPath = false; + Keys.onReturnPressed: { + webview.url = this.text; + } + } + + 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 + } + } } } - function sendMessage(data) { - //this.experimental.evaluateJavaScript("window.____returnData="+JSON.stringify(data)); - webview.experimental.postMessage(JSON.stringify(data)) - } - onTitleChanged: { - var data = Messaging.HandleMessage(title); - if(data) { - sendMessage(data) + WebView { + objectName: "webView" + id: webview + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + top: navBar.bottom } - } - experimental.preferences.javascriptEnabled: true - experimental.preferences.navigatorQtObjectEnabled: true - experimental.preferences.developerExtrasEnabled: true - experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] - experimental.onMessageReceived: { - console.log("[onMessageReceived]: ", message.data) - // TODO move to messaging.js - var data = JSON.parse(message.data) + property var cleanPath: false + onNavigationRequested: { + if(!this.cleanPath) { + var uri = request.url.toString(); + if(!/.*\:\/\/.*/.test(uri)) { + uri = "http://" + uri; + } - try { - switch(data.call) { - case "compile": - postData(data._seed, eth.compile(data.args[0])) - break + var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ - case "getCoinBase": - postData(data._seed, eth.coinBase()) + if(reg.test(uri)) { + uri.replace(reg, function(match, pre, domain, path) { + uri = pre; - break + 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)) + } - case "getIsListening": - postData(data._seed, eth.isListening()) + if(ip.length != 0) { + uri += lookup; + } else { + uri += domain; + } - break + uri += path; + }); + } - case "getIsMining": - postData(data._seed, eth.isMining()) + this.cleanPath = true; - break + webview.url = uri; + } else { + // Prevent inf loop. + this.cleanPath = false; + } + } - case "getPeerCount": - postData(data._seed, eth.peerCount()) + function sendMessage(data) { + webview.experimental.postMessage(JSON.stringify(data)) + } - break + onTitleChanged: { + var data = Messaging.HandleMessage(title); + if(data) { + sendMessage(data) + } + } - case "getTxCountAt": - require(1) - postData(data._seed, eth.txCountAt(data.args[0])) + experimental.preferences.javascriptEnabled: true + experimental.preferences.navigatorQtObjectEnabled: true + experimental.preferences.developerExtrasEnabled: true + experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] + experimental.onMessageReceived: { + console.log("[onMessageReceived]: ", message.data) + // TODO move to messaging.js + var data = JSON.parse(message.data) - break + try { + switch(data.call) { + case "compile": + postData(data._seed, eth.compile(data.args[0])) + break - case "getBlockByNumber": - var block = eth.blockByNumber(data.args[0]) - postData(data._seed, block) - - break - - case "getBlockByHash": - var block = eth.blockByHash(data.args[0]) - postData(data._seed, block) - - break - - case "transact": - require(5) - - var tx = eth.transact(data.args) - console.log("transactx", tx) - postData(data._seed, tx) - - break - - case "getStorage": - require(2); - - var stateObject = eth.stateObject(data.args[0]) - var storage = stateObject.storageAt(data.args[1]) - postData(data._seed, storage) - - break - - case "getEachStorage": - require(1); - var storage = JSON.parse(eth.eachStorage(data.args[0])) - postData(data._seed, storage) - - break - - case "getTransactionsFor": - require(1); - var txs = eth.transactionsFor(data.args[0], true) - postData(data._seed, txs) - - break - - case "getBalance": - require(1); - - postData(data._seed, eth.stateObject(data.args[0]).value()); - - break - - case "getKey": - var key = eth.key().privateKey; - - postData(data._seed, key) - break - - /* - case "watch": - require(1) - eth.watch(data.args[0], data.args[1]); + case "getCoinBase": + postData(data._seed, eth.coinBase()) break - */ - case "watch": - require(2) + + case "getIsListening": + postData(data._seed, eth.isListening()) + + break + + case "getIsMining": + postData(data._seed, eth.isMining()) + + break + + case "getPeerCount": + postData(data._seed, eth.peerCount()) + + break + + case "getCountAt": + require(1) + postData(data._seed, eth.txCountAt(data.args[0])) + + break + + case "getCodeAt": + require(1) + var code = eth.codeAt(data.args[0]) + postData(data._seed, code); + + break + + case "getBlockByNumber": + var block = eth.blockByNumber(data.args[0]) + postData(data._seed, block) + + break + + case "getBlockByHash": + var block = eth.blockByHash(data.args[0]) + postData(data._seed, block) + + break + + case "transact": + require(5) + + var tx = eth.transact(data.args) + postData(data._seed, tx) + + break + + case "getStorageAt": + require(2); + + var storage = eth.storageAt(data.args[0], data.args[1]); + postData(data._seed, storage) + + break + + case "call": + require(1); + var ret = eth.call(data.args) + postData(data._seed, ret) + + break + + case "getEachStorage": + require(1); + var storage = JSON.parse(eth.eachStorage(data.args[0])) + postData(data._seed, storage) + + break + + case "getTransactionsFor": + require(1); + var txs = eth.transactionsFor(data.args[0], true) + postData(data._seed, txs) + + break + + case "getBalanceAt": + require(1); + + postData(data._seed, eth.balanceAt(data.args[0])); + + break + + case "getKey": + var key = eth.key().privateKey; + + postData(data._seed, key) + break + + case "watch": + require(2) eth.watch(data.args[0], data.args[1]) - case "disconnect": - require(1) - postData(data._seed, null) + case "disconnect": + require(1) + postData(data._seed, null) - break; + break; - case "getSecretToAddress": - require(1) - postData(data._seed, eth.secretToAddress(data.args[0])) + case "getSecretToAddress": + require(1) - break; + var addr = eth.secretToAddress(data.args[0]) + console.log("getsecret", addr) + postData(data._seed, addr) - case "messages": + break; + + case "messages": require(1); var messages = JSON.parse(eth.getMessages(data.args[0])) @@ -276,94 +290,120 @@ Rectangle { break - case "mutan": + case "mutan": require(1) var code = eth.compileMutan(data.args[0]) postData(data._seed, "0x"+code) break; + + case "newFilterString": + require(1) + var id = eth.newFilterString(data.args[0]) + postData(data._seed, id); + break; + case "newFilter": + require(1) + var id = eth.newFilter(data.args[0]) + + postData(data._seed, id); + break; + + case "getMessages": + require(1); + + var messages = eth.messages(data.args[0]); + var m = JSON.parse(JSON.parse(JSON.stringify(messages))) + postData(data._seed, m); + + break; + + case "deleteFilter": + require(1); + eth.uninstallFilter(data.args[0]) + break; + } + } catch(e) { + console.log(data.call + ": " + e) + + postData(data._seed, null); } - } catch(e) { - console.log(data.call + ": " + e) + } - postData(data._seed, null); + + function post(seed, data) { + postData(data._seed, data) + } + + function require(args, num) { + if(args.length < num) { + throw("required argument count of "+num+" got "+args.length); + } + } + function postData(seed, data) { + webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed})) + } + function postEvent(event, data) { + 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) + } + function onObjectChangeCb(stateObject) { + postEvent("object:"+stateObject.address(), stateObject) + } + function onStorageChangeCb(storageObject) { + var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); + postEvent(ev, [storageObject.address, storageObject.value]) } } - function post(seed, data) { - console.log("data", data) - postData(data._seed, data) - } - function require(args, num) { - if(args.length < num) { - throw("required argument count of "+num+" got "+args.length); + Rectangle { + id: sizeGrip + color: "gray" + visible: false + height: 10 + anchors { + left: root.left + right: root.right + } + y: Math.round(root.height * 2 / 3) + + MouseArea { + anchors.fill: parent + drag.target: sizeGrip + drag.minimumY: 0 + drag.maximumY: root.height + drag.axis: Drag.YAxis } } - function postData(seed, data) { - webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed})) - } - function postEvent(event, data) { - 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) - } - function onObjectChangeCb(stateObject) { - postEvent("object:"+stateObject.address(), stateObject) - } - function onStorageChangeCb(storageObject) { - var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); - postEvent(ev, [storageObject.address, storageObject.value]) - } - } - - - Rectangle { - id: sizeGrip - color: "gray" - visible: false - height: 10 - anchors { - left: root.left - right: root.right - } - y: Math.round(root.height * 2 / 3) - - MouseArea { - anchors.fill: parent - drag.target: sizeGrip - drag.minimumY: 0 - drag.maximumY: root.height - drag.axis: Drag.YAxis - } - } - - WebView { - id: inspector - visible: false - anchors { - left: root.left - right: root.right - top: sizeGrip.bottom - bottom: root.bottom - } - } - - states: [ - State { - name: "inspectorShown" - PropertyChanges { - target: inspector + WebView { + id: inspector + visible: false + anchors { + left: root.left + right: root.right + top: sizeGrip.bottom + bottom: root.bottom } } - ] + + states: [ + State { + name: "inspectorShown" + PropertyChanges { + target: inspector + } + } + ] + } } -} diff --git a/mist/debugger.go b/mist/debugger.go index 8f3fc6751..9d1de8c42 100644 --- a/mist/debugger.go +++ b/mist/debugger.go @@ -276,7 +276,7 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et d.win.Root().Call("clearStorage") addr := 0 - for i := 0; i+32 <= mem.Len(); i += 16 { + for i := 0; i+16 <= mem.Len(); i += 16 { dat := mem.Data()[i : i+16] var str string diff --git a/mist/ui_lib.go b/mist/ui_lib.go index 334442f9f..a913af7db 100644 --- a/mist/ui_lib.go +++ b/mist/ui_lib.go @@ -37,7 +37,6 @@ type UiLib struct { jsEngine *javascript.JSRE filterCallbacks map[int][]int - //filters map[int]*ethpipe.JSFilter } func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { @@ -201,7 +200,7 @@ func (self *UiLib) UninstallFilter(id int) { self.eth.UninstallFilter(id) } -func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, error) { +func mapToTxParams(object map[string]interface{}) map[string]string { // Default values if object["from"] == nil { object["from"] = "" @@ -223,6 +222,8 @@ func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, var data []string if list, ok := object["data"].(*qml.List); ok { list.Convert(&data) + } else if str, ok := object["data"].(string); ok { + data = []string{str} } for _, str := range data { @@ -238,14 +239,29 @@ func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, dataStr += str } + object["data"] = dataStr + fmt.Println(object) + + conv := make(map[string]string) + for key, value := range object { + if v, ok := value.(string); ok { + conv[key] = v + } + } + + return conv +} + +func (self *UiLib) Transact(params map[string]interface{}) (*ethpipe.JSReceipt, error) { + object := mapToTxParams(params) return self.JSPipe.Transact( - object["from"].(string), - object["to"].(string), - object["value"].(string), - object["gas"].(string), - object["gasPrice"].(string), - dataStr, + object["from"], + object["to"], + object["value"], + object["gas"], + object["gasPrice"], + object["data"], ) } @@ -257,3 +273,15 @@ func (self *UiLib) Compile(code string) (string, error) { return ethutil.Bytes2Hex(bcode), err } + +func (self *UiLib) Call(params map[string]interface{}) (string, error) { + object := mapToTxParams(params) + + return self.JSPipe.Execute( + object["to"], + object["value"], + object["gas"], + object["gasPrice"], + object["data"], + ) +}