Re-wrote ethereum.js

This commit is contained in:
obscuren 2014-09-22 14:54:27 +02:00
parent 8585e59718
commit b4bd70c402
8 changed files with 729 additions and 365 deletions

View File

@ -221,7 +221,7 @@ ApplicationWindow {
} }
height: parent.height height: parent.height
width: 300 width: 300
TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 } TableViewColumn{ role: "value" ; title: "Local VM stack" ; width: stackTableView.width - 2 }
model: stackModel model: stackModel
} }
@ -233,7 +233,7 @@ ApplicationWindow {
height: parent.height height: parent.height
width: parent.width - stackTableView.width width: parent.width - stackTableView.width
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 } TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 }
TableViewColumn{ role: "value" ; title: "Memory" ; width: 750 } TableViewColumn{ role: "value" ; title: "Memory" ; width: 650 }
model: memModel model: memModel
} }
} }
@ -248,8 +248,8 @@ ApplicationWindow {
} }
height: parent.height height: parent.height
width: parent.width width: parent.width
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2} TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2 - 1}
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2} TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2 - 1}
model: storageModel model: storageModel
} }
} }

View File

@ -31,11 +31,11 @@ Filter.prototype.changed = function(callback) {
this.callbacks.push(callback); this.callbacks.push(callback);
var self = this; var self = this;
message.connect(function(messages, id) { messages.connect(function(messages, id) {
if(id == self.id) { if(id == self.id) {
for(var i = 0; i < self.callbacks.length; i++) { for(var i = 0; i < self.callbacks.length; i++) {
self.callbacks[i].call(self, messages); self.callbacks[i].call(self, messages);
} }
} }
}); });
}; };

View File

@ -12,15 +12,46 @@
window.eth = { window.eth = {
_callbacks: {}, _callbacks: {},
_onCallbacks: {}, _events: {},
prototype: Object(), prototype: Object(),
coinbase: function() { toHex: function(str) {
return new Promise(function(resolve, reject) { var hex = "";
postData({call: "getCoinBase"}, function(coinbase) { for(var i = 0; i < str.length; i++) {
resolve(coinbase); 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) { 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) { return new Promise(function(resolve, reject) {
postData({call: "getKey"}, function(k) { postData({call: "getKey"}, function(k) {
resolve(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) { function postData(data, cb) {
data._seed = Math.floor(Math.random() * 1000000) data._seed = g_seed;
if(cb) { if(cb) {
eth._callbacks[data._seed] = cb; eth._callbacks[data._seed] = cb;
} }
@ -126,6 +456,8 @@
data.args = []; data.args = [];
} }
g_seed++;
navigator.qt.postMessage(JSON.stringify(data)); navigator.qt.postMessage(JSON.stringify(data));
} }

View File

@ -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") { if(typeof(Promise) === "undefined") {
window.Promise = Q.Promise; window.Promise = Q.Promise;
} }

View File

@ -14,20 +14,22 @@ ApplicationWindow {
property alias miningButtonText: miningButton.text property alias miningButtonText: miningButton.text
property var ethx : Eth.ethx property var ethx : Eth.ethx
property var web
width: 900 width: 1024
height: 600 height: 750
minimumHeight: 300 minimumHeight: 300
title: "Mist" title: "Mist"
// This signal is used by the filter API. The filter API connects using this signal handler from // This signal is used by the filter API. The filter API connects using this signal handler from
// the different QML files and plugins. // the different QML files and plugins.
signal message(var callback, int seed); signal messages(var messages, int id);
function invokeFilterCallback(data, receiverSeed) { function invokeFilterCallback(data, receiverSeed) {
//var messages = JSON.parse(data) //var messages = JSON.parse(data)
// Signal handler // Signal handler
message(data, receiverSeed); messages(data, receiverSeed);
root.web.messages(data, receiverSeed);
} }
TextField { TextField {
@ -44,7 +46,7 @@ ApplicationWindow {
// Takes care of loading all default plugins // Takes care of loading all default plugins
Component.onCompleted: { Component.onCompleted: {
addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); 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/transaction.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});

View File

@ -10,265 +10,279 @@ import Ethereum 1.0
import "../ext/qml_messaging.js" as Messaging import "../ext/qml_messaging.js" as Messaging
//ApplicationWindow { //ApplicationWindow {
Rectangle { Rectangle {
id: window id: window
property var title: "Browser" property var title: "Browser"
property var iconSource: "../browser.png" property var iconSource: "../browser.png"
property var menuItem property var menuItem
property alias url: webview.url property alias url: webview.url
property alias webView: webview property alias webView: webview
Component.onCompleted: { Component.onCompleted: {
webview.url = "http://etherian.io" 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
}
}
}
} }
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 { Item {
objectName: "webView" objectName: "root"
id: webview id: root
anchors { anchors.fill: parent
left: parent.left state: "inspectorShown"
right: parent.right
bottom: parent.bottom
top: navBar.bottom
}
property var cleanPath: false RowLayout {
onNavigationRequested: { id: navBar
if(!this.cleanPath) { height: 40
var uri = request.url.toString(); anchors {
if(!/.*\:\/\/.*/.test(uri)) { left: parent.left
uri = "http://" + uri; right: parent.right
leftMargin: 7
}
Button {
id: back
onClicked: {
webview.goBack()
} }
style: ButtonStyle {
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ background: Image {
source: "../back.png"
if(reg.test(uri)) { width: 30
uri.replace(reg, function(match, pre, domain, path) { height: 30
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; 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; Keys.onReturnPressed: {
} else { webview.url = this.text;
// Prevent inf loop. }
this.cleanPath = false; }
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: { WebView {
var data = Messaging.HandleMessage(title); objectName: "webView"
if(data) { id: webview
sendMessage(data) anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
top: navBar.bottom
} }
}
experimental.preferences.javascriptEnabled: true property var cleanPath: false
experimental.preferences.navigatorQtObjectEnabled: true onNavigationRequested: {
experimental.preferences.developerExtrasEnabled: true if(!this.cleanPath) {
experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] var uri = request.url.toString();
experimental.onMessageReceived: { if(!/.*\:\/\/.*/.test(uri)) {
console.log("[onMessageReceived]: ", message.data) uri = "http://" + uri;
// TODO move to messaging.js }
var data = JSON.parse(message.data)
try { var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
switch(data.call) {
case "compile":
postData(data._seed, eth.compile(data.args[0]))
break
case "getCoinBase": if(reg.test(uri)) {
postData(data._seed, eth.coinBase()) 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": if(ip.length != 0) {
postData(data._seed, eth.isListening()) uri += lookup;
} else {
uri += domain;
}
break uri += path;
});
}
case "getIsMining": this.cleanPath = true;
postData(data._seed, eth.isMining())
break webview.url = uri;
} else {
// Prevent inf loop.
this.cleanPath = false;
}
}
case "getPeerCount": function sendMessage(data) {
postData(data._seed, eth.peerCount()) webview.experimental.postMessage(JSON.stringify(data))
}
break onTitleChanged: {
var data = Messaging.HandleMessage(title);
if(data) {
sendMessage(data)
}
}
case "getTxCountAt": experimental.preferences.javascriptEnabled: true
require(1) experimental.preferences.navigatorQtObjectEnabled: true
postData(data._seed, eth.txCountAt(data.args[0])) 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": case "getCoinBase":
var block = eth.blockByNumber(data.args[0]) postData(data._seed, eth.coinBase())
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]);
break break
*/
case "watch": case "getIsListening":
require(2) 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]) eth.watch(data.args[0], data.args[1])
case "disconnect": case "disconnect":
require(1) require(1)
postData(data._seed, null) postData(data._seed, null)
break; break;
case "getSecretToAddress": case "getSecretToAddress":
require(1) require(1)
postData(data._seed, eth.secretToAddress(data.args[0]))
break; var addr = eth.secretToAddress(data.args[0])
console.log("getsecret", addr)
postData(data._seed, addr)
case "messages": break;
case "messages":
require(1); require(1);
var messages = JSON.parse(eth.getMessages(data.args[0])) var messages = JSON.parse(eth.getMessages(data.args[0]))
@ -276,94 +290,120 @@ Rectangle {
break break
case "mutan": case "mutan":
require(1) require(1)
var code = eth.compileMutan(data.args[0]) var code = eth.compileMutan(data.args[0])
postData(data._seed, "0x"+code) postData(data._seed, "0x"+code)
break; 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) { Rectangle {
if(args.length < num) { id: sizeGrip
throw("required argument count of "+num+" got "+args.length); 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) { WebView {
var messages = JSON.parse(data) id: inspector
postEvent("watched:"+id, messages) visible: false
} anchors {
left: root.left
function onNewBlockCb(block) { right: root.right
postEvent("block:new", block) top: sizeGrip.bottom
} bottom: root.bottom
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
} }
} }
]
states: [
State {
name: "inspectorShown"
PropertyChanges {
target: inspector
}
}
]
}
} }
}

View File

@ -276,7 +276,7 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et
d.win.Root().Call("clearStorage") d.win.Root().Call("clearStorage")
addr := 0 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] dat := mem.Data()[i : i+16]
var str string var str string

View File

@ -37,7 +37,6 @@ type UiLib struct {
jsEngine *javascript.JSRE jsEngine *javascript.JSRE
filterCallbacks map[int][]int filterCallbacks map[int][]int
//filters map[int]*ethpipe.JSFilter
} }
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { 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) 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 // Default values
if object["from"] == nil { if object["from"] == nil {
object["from"] = "" object["from"] = ""
@ -223,6 +222,8 @@ func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt,
var data []string var data []string
if list, ok := object["data"].(*qml.List); ok { if list, ok := object["data"].(*qml.List); ok {
list.Convert(&data) list.Convert(&data)
} else if str, ok := object["data"].(string); ok {
data = []string{str}
} }
for _, str := range data { for _, str := range data {
@ -238,14 +239,29 @@ func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt,
dataStr += str 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( return self.JSPipe.Transact(
object["from"].(string), object["from"],
object["to"].(string), object["to"],
object["value"].(string), object["value"],
object["gas"].(string), object["gas"],
object["gasPrice"].(string), object["gasPrice"],
dataStr, object["data"],
) )
} }
@ -257,3 +273,15 @@ func (self *UiLib) Compile(code string) (string, error) {
return ethutil.Bytes2Hex(bcode), err 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"],
)
}