Compare commits

..

68 Commits

Author SHA1 Message Date
7855a233a7 Merge branch 'release/0.6.4' 2014-08-21 20:23:09 +02:00
b849547116 bump 2014-08-21 20:23:00 +02:00
6d171ff511 Turbo mining flag 2014-08-21 20:13:38 +02:00
48a99d23cd bump 2014-08-21 19:22:16 +02:00
b3c975269e Merge branch 'release/0.6.3' into develop 2014-08-21 15:46:18 +02:00
ed73fc0be3 Merge branch 'release/0.6.3' 2014-08-21 15:45:50 +02:00
cb65848fee Version name 2014-08-21 15:45:46 +02:00
1f59c37b89 Merge branch 'release/0.6.3' 2014-08-21 15:43:45 +02:00
be9912fae2 MIT -> GPL 2014-08-21 15:42:59 +02:00
38cf52b62c Bumped version 2014-08-21 15:22:24 +02:00
7c65560f20 Changed icon source 2014-08-20 23:52:33 +02:00
245ffb1123 Fixed transact 2014-08-20 13:36:32 +02:00
fb49e5565a Fixed minor issues with filtering 2014-08-20 12:42:49 +02:00
0eb1db0d17 Added messages method 2014-08-20 10:41:20 +02:00
ecc2c609d4 Implemented QML message filtering 2014-08-20 10:00:02 +02:00
a8409b0a8b Implementing new wallet views 2014-08-18 01:35:42 +02:00
5ae3deea86 New menu 2014-08-17 21:07:09 +02:00
88655439a0 Copy to clipboard hax 2014-08-17 13:49:46 +02:00
e12abfd43b Double click 2014-08-17 13:05:43 +02:00
27735bbdfc State dumps from gui 2014-08-17 12:41:23 +02:00
2eab964a00 Switched over to ethpipe 2014-08-16 01:38:24 +02:00
8f1b461228 Corrected getEachStorageAt 2014-08-15 16:26:49 +02:00
c2c8757bd1 Added message to closure 2014-08-15 16:19:30 +02:00
203c4b99a0 LookupDomain method to uilib 2014-08-15 13:45:34 +02:00
9f4886839f Moved over to hardcoded v1 of Go QML 2014-08-15 13:27:43 +02:00
df0c43265e Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-08-15 13:16:15 +02:00
a6c4543c57 Moving over to ethpipe 2014-08-15 13:16:07 +02:00
f6a4f9c753 Merge pull request #115 from ethersphere/develop
remove mnemonic words initialisation
2014-08-15 10:49:52 +02:00
c362172567 Got rid of warnings and updated storage getters 2014-08-15 01:07:40 +02:00
aadc5be3ff Implemented new watch
* Old watch methods have been removed
* Implemented new callback handlers for onWatchCb
* added new Filter JS object
2014-08-15 00:24:14 +02:00
1fd69e9569 Implemented "messages" 2014-08-14 17:01:37 +02:00
612b631823 Json types 2014-08-14 17:01:21 +02:00
3fd0337330 DNS for a hrefs should now be fixed 2014-08-14 13:26:29 +02:00
14e4f1283c Removed old debug method 2014-08-14 10:52:13 +02:00
95ba340d07 Tweaks and fixes + added webview debugger
* Require better icons .. someone? :-)
2014-08-14 00:18:37 +02:00
d518423b9c Updated DNS Lookup 2014-08-13 10:52:30 +02:00
3569e8d7df remove mmenomic words initialisation (mnemonic wordlist now inline to support binary builds without extra asset) 2014-08-12 14:57:51 +02:00
1fa792eae7 Fixed reference 2014-08-12 13:16:44 +02:00
dc3b0e170c Name changes 2014-08-12 12:21:50 +02:00
59d9746849 Changed naming on exposed QML variables 2014-08-12 12:16:21 +02:00
2e2f23a0ae Properly hide elements on tx submit 2014-08-12 12:07:32 +02:00
0c9c79a89b UI update 2014-08-12 12:03:06 +02:00
ac14f002e6 Refactored GUI and added modular/pluginable side bar 2014-08-12 11:02:33 +02:00
c59d7a899b Added open js option for repetitive tasks in ethereal 2014-08-11 16:24:35 +02:00
ce8f24e57a Moved JSRE to it's own package for sharing between ethere(um/al) 2014-08-11 16:24:17 +02:00
51a2087081 Minor issues 2014-08-10 14:57:42 +01:00
4dc5855dfe Regular browser option added 2014-08-07 16:35:47 +02:00
a915ba17ed Support the ".eth" TLD through the DnsContract 2014-08-07 15:12:25 +02:00
bbe896875e Typo. Fixes #107 2014-08-07 00:27:58 +02:00
c7afb5fb72 output dump string, not undefined 2014-08-06 10:05:34 +02:00
852d1ee395 State dumps 2014-08-06 09:53:12 +02:00
3c319f93f2 Merge branch 'ethersphere-feature/ethutil-refactor' into develop 2014-08-01 10:30:37 +02:00
34e937c976 merge upstream 2014-07-30 18:02:43 +02:00
834803f1e8 Update 2014-07-30 15:34:36 +02:00
5501679642 Updated README to include Cpt. Obv. 2014-07-30 15:33:42 +02:00
23f83f53cc Upped version number 2014-07-30 01:05:40 +02:00
719b7784f3 Renamed to balance 2014-07-30 00:30:57 +02:00
5c9fd19105 A few start up optimisations 2014-07-30 00:17:23 +02:00
beca2234af Merge branch 'release/0.6.0' into develop 2014-07-25 10:43:23 +02:00
97004f7eb2 wip export 2014-07-25 10:41:57 +02:00
2f5c95610f use logger instead of fmt for error in ext_app 2014-07-21 19:55:47 +01:00
74abc457ad reactor event channels have large buffer to allow more tolerance 2014-07-21 19:26:01 +01:00
9f7d8ff302 Merge branch 'develop' of github.com:ethereum/go-ethereum into feature/ethutil-refactor 2014-07-21 13:31:03 +01:00
4d5a890b46 merge upstream 2014-07-21 13:30:37 +01:00
75a7a4c97c ethreact
- use ethreact.Event,
- increased buffered event channels,
- subscribe after loop reading from channel starts
2014-07-15 01:13:39 +01:00
94b12f7804 fix start mining 2014-07-15 00:05:18 +01:00
353ecbb7d7 Merge branch 'develop' of github.com:ethereum/go-ethereum into feature/ethutil-refactor 2014-07-14 18:32:54 +01:00
6b296d907b Merge branch 'develop' of github.com:ethereum/go-ethereum into feature/ethutil-refactor 2014-07-05 12:52:44 +01:00
39 changed files with 2940 additions and 1571 deletions

31
LICENSE
View File

@ -1,21 +1,16 @@
The MIT License (MIT)
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
Copyright (c) 2013 Jeffrey Wilcke
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
You should have received a copy of the GNU General Public License
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA

View File

@ -1,11 +1,13 @@
Ethereum
========
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
Master [![Build
Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-master-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-master-docker/builds/-1) Develop [![Build
Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-develop-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-develop-docker/builds/-1)
Ethereum Go Client © 2014 Jeffrey Wilcke.
Current state: Proof of Concept 0.6.0.
Current state: Proof of Concept 0.6.4.
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).

BIN
ethereal/assets/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

BIN
ethereal/assets/bug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
ethereal/assets/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

View File

@ -1,39 +1,124 @@
// Main Ethereum library
window.eth = {
prototype: Object(),
_callbacks: {},
_onCallbacks: {},
test: function() {
var t = undefined;
postData({call: "test"})
navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; }
for(;;) {
if(t !== undefined) {
return t
}
}
},
mutan: function(code, cb) {
postData({call: "mutan", args: [code]}, cb)
},
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 +151,7 @@ window.eth = {
postData({call: "getSecretToAddress", args: [sec]}, cb);
},
/*
watch: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
@ -95,6 +181,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 +233,63 @@ window.eth = {
}
}
},
}
window.eth._callbacks = {}
window.eth._onCallbacks = {}
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)
}
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];
}
}
}
}
eth.on("chain:changed", function() {
})
eth.on("messages", { /* filters */}, function(messages){
})
eth.on("pending:changed", function() {
})

View File

@ -0,0 +1,31 @@
var Filter = function(options) {
this.callbacks = {};
this.seed = Math.floor(Math.random() * 1000000);
this.options = options;
if(options == "chain") {
eth.registerFilterString(options, this.seed);
} else if(typeof options === "object") {
eth.registerFilter(options, this.seed);
}
};
Filter.prototype.changed = function(callback) {
var cbseed = Math.floor(Math.random() * 1000000);
eth.registerFilterCallback(this.seed, cbseed);
var self = this;
message.connect(function(messages, seed, callbackSeed) {
if(seed == self.seed && callbackSeed == cbseed) {
callback.call(self, messages);
}
});
};
Filter.prototype.uninstall = function() {
eth.uninstallFilter(this.seed)
}
Filter.prototype.messages = function() {
return JSON.parse(eth.messages(this.options))
}

View File

@ -0,0 +1,22 @@
<!doctype>
<html>
<head>
<title>Ethereum</title>
<style type="text/css">
h1 {
text-align: center;
font-family: Courier;
font-size: 50pt;
}
</style>
</head>
<body>
<h1>... Ethereum ...</h1>
<ul>
<li><a href="http://std.eth">std::Service</a></li>
</ul>
</body>
</html>

View File

@ -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;
}

View File

@ -0,0 +1,44 @@
<!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",
altered: ["aabb", {id: "eeff", "at": "aabb"}],
});
filter.changed(function(messages) {
console.log("messages", messages)
})
filter.getMessages(function(messages) {
console.log("getMessages", messages)
});
eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(entries) {
for(var i = 0; i < entries.length; i++) {
console.log(entries[i].key, " : ", entries[i].value)
}
})
eth.getBlock("f70097659f329a09642a27f11338d9269de64f1d4485786e36bfc410832148cd", function(block) {
console.log(block)
})
eth.mutan("var a = 10", function(code) {
console.log("code", code)
});
}
</script>
</body>
</html>

BIN
ethereal/assets/pick.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

View File

@ -0,0 +1,256 @@
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 {
id: root
property var title: "Network"
property var iconSource: "../net.png"
property var secondary: "Hi"
property var menuItem
objectName: "chainView"
visible: false
anchors.fill: parent
TableView {
id: blockTable
width: parent.width
anchors.top: parent.top
anchors.bottom: parent.bottom
TableViewColumn{ role: "number" ; title: "#" ; width: 100 }
TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 }
TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 }
model: blockModel
itemDelegate: Item {
Text {
anchors {
left: parent.left
right: parent.right
leftMargin: 10
verticalCenter: parent.verticalCenter
}
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
font.pixelSize: 11
MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton
propagateComposedEvents: true
anchors.fill: parent
onClicked: {
blockTable.selection.clear()
blockTable.selection.select(styleData.row)
if(mouse.button == Qt.RightButton) {
contextMenu.row = styleData.row;
contextMenu.popup()
}
}
onDoubleClicked: {
popup.visible = true
popup.setDetails(blockModel.get(styleData.row))
}
}
}
}
Menu {
id: contextMenu
property var row;
MenuItem {
text: "Details"
onTriggered: {
popup.visible = true
popup.setDetails(blockModel.get(this.row))
}
}
MenuSeparator{}
MenuItem {
text: "Copy"
onTriggered: {
copyToClipboard(blockModel.get(this.row).hash)
}
}
MenuItem {
text: "Dump State"
onTriggered: {
generalFileDialog.show(false, function(path) {
var hash = blockModel.get(this.row).hash;
gui.dumpState(hash, path);
});
}
}
}
}
function addBlock(block, initial) {
var txs = JSON.parse(block.transactions);
var amount = 0
if(initial == undefined){
initial = false
}
if(txs != null){
amount = txs.length
}
if(initial){
blockModel.append({number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
} else {
blockModel.insert(0, {number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
}
//root.secondary.text = "#" + block.number;
}
Window {
id: popup
visible: false
//flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
property var block
width: root.width
height: 300
Component{
id: blockDetailsDelegate
Rectangle {
color: "#252525"
width: popup.width
height: 150
Column {
anchors.leftMargin: 10
anchors.topMargin: 5
anchors.top: parent.top
anchors.left: parent.left
Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
Text { text: '<b>Block number:</b> ' + number; color: "#F2F2F2"}
Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
Text { text: '<b>Coinbase:</b> &lt;' + name + '&gt; ' + coinbase; color: "#F2F2F2"}
Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
Text { text: '<b>Gas used:</b> ' + gasUsed + " / " + gasLimit; color: "#F2F2F2"}
}
}
}
ListView {
model: singleBlock
delegate: blockDetailsDelegate
anchors.top: parent.top
height: 100
anchors.leftMargin: 20
id: listViewThing
Layout.maximumHeight: 40
}
TableView {
id: txView
anchors.top: listViewThing.bottom
anchors.topMargin: 50
width: parent.width
TableViewColumn{width: 90; role: "value" ; title: "Value" }
TableViewColumn{width: 200; role: "hash" ; title: "Hash" }
TableViewColumn{width: 200; role: "sender" ; title: "Sender" }
TableViewColumn{width: 200;role: "address" ; title: "Receiver" }
TableViewColumn{width: 60; role: "gas" ; title: "Gas" }
TableViewColumn{width: 60; role: "gasPrice" ; title: "Gas Price" }
TableViewColumn{width: 60; role: "isContract" ; title: "Contract" }
model: transactionModel
onClicked: {
var tx = transactionModel.get(row)
if(tx.data) {
popup.showContractData(tx)
}else{
popup.height = 440
}
}
}
function showContractData(tx) {
txDetailsDebugButton.tx = tx
if(tx.createsContract) {
contractData.text = tx.data
contractLabel.text = "<h4> Transaction created contract " + tx.address + "</h4>"
}else{
contractLabel.text = "<h4> Transaction ran contract " + tx.address + "</h4>"
contractData.text = tx.rawData
}
popup.height = 540
}
Rectangle {
id: txDetails
width: popup.width
height: 300
anchors.left: listViewThing.left
anchors.top: txView.bottom
Label {
text: "<h4>Contract data</h4>"
anchors.top: parent.top
anchors.left: parent.left
id: contractLabel
anchors.leftMargin: 10
}
Button {
property var tx
id: txDetailsDebugButton
anchors.right: parent.right
anchors.rightMargin: 10
anchors.top: parent.top
anchors.topMargin: 10
text: "Debug contract"
onClicked: {
if(tx.createsContract){
eth.startDbWithCode(tx.rawData)
}else {
eth.startDbWithContractAndData(tx.address, tx.rawData)
}
}
}
TextArea {
id: contractData
text: "Contract"
anchors.top: contractLabel.bottom
anchors.left: parent.left
anchors.bottom: popup.bottom
wrapMode: Text.Wrap
width: parent.width - 30
height: 80
anchors.leftMargin: 10
}
}
property var transactionModel: ListModel {
id: transactionModel
}
property var singleBlock: ListModel {
id: singleBlock
}
function setDetails(block){
singleBlock.set(0,block)
popup.height = 300
transactionModel.clear()
if(block.txs != undefined){
for(var i = 0; i < block.txs.count; ++i) {
transactionModel.insert(0, block.txs.get(i))
}
if(block.txs.get(0).data){
popup.showContractData(block.txs.get(0))
}
}
txView.forceActiveFocus()
}
}
}

View File

@ -0,0 +1,52 @@
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 iconSource: "../tx.png"
property var title: "Transactions"
property var menuItem
id: historyView
visible: false
anchors.fill: parent
objectName: "transactionView"
property var txModel: ListModel {
id: txModel
}
TableView {
id: txTableView
anchors.fill: parent
TableViewColumn{ role: "inout" ; title: "" ; width: 40 }
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
model: txModel
}
function addTx(tx, inout) {
var isContract
if (tx.contract == true){
isContract = "Yes"
}else{
isContract = "No"
}
var address;
if(inout == "recv") {
address = tx.sender;
} else {
address = tx.address;
}
txModel.insert(0, {inout: inout, hash: tx.hash, address: address, value: tx.value, contract: isContract})
}
}

View File

@ -0,0 +1,179 @@
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: "Information"
property var iconSource: "../heart.png"
property var menuItem
objectName: "infoView"
visible: false
anchors.fill: parent
color: "#00000000"
Column {
id: info
spacing: 3
anchors.fill: parent
anchors.topMargin: 5
anchors.leftMargin: 5
Label {
id: addressLabel
text: "Address"
}
TextField {
text: eth.key().address
width: 500
}
Label {
text: "Client ID"
}
TextField {
text: gui.getCustomIdentifier()
width: 500
placeholderText: "Anonymous"
onTextChanged: {
gui.setCustomIdentifier(text)
}
}
}
property var addressModel: ListModel {
id: addressModel
}
TableView {
id: addressView
width: parent.width
height: 200
anchors.bottom: logLayout.top
TableViewColumn{ role: "name"; title: "name" }
TableViewColumn{ role: "address"; title: "address"; width: 300}
model: addressModel
itemDelegate: Item {
Text {
anchors {
left: parent.left
right: parent.right
leftMargin: 10
verticalCenter: parent.verticalCenter
}
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
font.pixelSize: 11
MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton
propagateComposedEvents: true
anchors.fill: parent
onClicked: {
addressView.selection.clear()
addressView.selection.select(styleData.row)
if(mouse.button == Qt.RightButton) {
contextMenu.row = styleData.row;
contextMenu.popup()
}
}
}
}
}
Menu {
id: contextMenu
property var row;
MenuItem {
text: "Copy"
onTriggered: {
copyToClipboard(addressModel.get(this.row).address)
}
}
}
}
property var logModel: ListModel {
id: logModel
}
RowLayout {
id: logLayout
width: parent.width
height: 200
anchors.bottom: parent.bottom
TableView {
id: logView
headerVisible: false
anchors {
right: logLevelSlider.left
left: parent.left
bottom: parent.bottom
top: parent.top
}
TableViewColumn{ role: "description" ; title: "log" }
model: logModel
}
Slider {
id: logLevelSlider
value: gui.getLogLevelInt()
anchors {
right: parent.right
top: parent.top
bottom: parent.bottom
rightMargin: 5
leftMargin: 5
topMargin: 5
bottomMargin: 5
}
orientation: Qt.Vertical
maximumValue: 5
stepSize: 1
onValueChanged: {
gui.setLogLevel(value)
}
}
}
function addDebugMessage(message){
debuggerLog.append({value: message})
}
function addAddress(address) {
addressModel.append({name: address.name, address: address.address})
}
function clearAddress() {
addressModel.clear()
}
function addLog(str) {
// Remove first item once we've reached max log items
if(logModel.count > 250) {
logModel.remove(0)
}
if(str.len != 0) {
if(logView.flickableItem.atYEnd) {
logModel.append({description: str})
logView.positionViewAtRow(logView.rowCount - 1, ListView.Contain)
} else {
logModel.append({description: str})
}
}
}
}

View File

@ -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 iconSource: "../tx.png"
property var menuItem
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
text: "> JSRE Ready..."
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: input.top
}
}
}

View File

@ -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: "Pending Transactions"
property var iconSource: "../tx.png"
property var menuItem
objectName: "pendingTxView"
anchors.fill: parent
visible: false
id: pendingTxView
property var pendingTxModel: ListModel {
id: pendingTxModel
}
TableView {
id: pendingTxTableView
anchors.fill: parent
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
TableViewColumn{ role: "from" ; title: "sender" ; width: 230 }
TableViewColumn{ role: "to" ; title: "Reciever" ; width: 230 }
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
model: pendingTxModel
}
function addTx(tx, inout) {
var isContract
if (tx.contract == true){
isContract = "Yes"
}else{
isContract = "No"
}
pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract})
}
}

View File

@ -0,0 +1,215 @@
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 iconSource: "../new.png"
property var title: "New transaction"
property var menuItem
objectName: "newTxView"
visible: false
anchors.fill: parent
color: "#00000000"
Column {
id: mainContractColumn
anchors.fill: parent
states: [
State{
name: "ERROR"
PropertyChanges { target: txResult; visible:true}
PropertyChanges { target: codeView; visible:true}
},
State {
name: "DONE"
PropertyChanges { target: txValue; visible:false}
PropertyChanges { target: txGas; visible:false}
PropertyChanges { target: txGasPrice; visible:false}
PropertyChanges { target: codeView; visible:false}
PropertyChanges { target: txButton; visible:false}
PropertyChanges { target: txDataLabel; visible:false}
PropertyChanges { target: atLabel; visible:false}
PropertyChanges { target: txFuelRecipient; visible:false}
PropertyChanges { target: valueDenom; visible:false}
PropertyChanges { target: gasDenom; visible:false}
PropertyChanges { target: txResult; visible:true}
PropertyChanges { target: txOutput; visible:true}
PropertyChanges { target: newTxButton; visible:true}
},
State {
name: "SETUP"
PropertyChanges { target: txValue; visible:true; text: ""}
PropertyChanges { target: txGas; visible:true;}
PropertyChanges { target: txGasPrice; visible:true;}
PropertyChanges { target: codeView; visible:true; text: ""}
PropertyChanges { target: txButton; visible:true}
PropertyChanges { target: txDataLabel; visible:true}
PropertyChanges { target: valueDenom; visible:true}
PropertyChanges { target: gasDenom; visible:true}
PropertyChanges { target: txResult; visible:false}
PropertyChanges { target: txOutput; visible:false}
PropertyChanges { target: newTxButton; visible:false}
}
]
width: 400
spacing: 5
anchors.left: parent.left
anchors.top: parent.top
anchors.leftMargin: 5
anchors.topMargin: 5
ListModel {
id: denomModel
ListElement { text: "Wei" ; zeros: "" }
ListElement { text: "Ada" ; zeros: "000" }
ListElement { text: "Babbage" ; zeros: "000000" }
ListElement { text: "Shannon" ; zeros: "000000000" }
ListElement { text: "Szabo" ; zeros: "000000000000" }
ListElement { text: "Finney" ; zeros: "000000000000000" }
ListElement { text: "Ether" ; zeros: "000000000000000000" }
ListElement { text: "Einstein" ;zeros: "000000000000000000000" }
ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" }
}
TextField {
id: txFuelRecipient
placeholderText: "Address / Name or empty for contract"
//validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
width: 400
}
RowLayout {
TextField {
id: txValue
width: 222
placeholderText: "Amount"
validator: RegExpValidator { regExp: /\d*/ }
onTextChanged: {
contractFormReady()
}
}
ComboBox {
id: valueDenom
currentIndex: 6
model: denomModel
}
}
RowLayout {
TextField {
id: txGas
width: 50
validator: RegExpValidator { regExp: /\d*/ }
placeholderText: "Gas"
text: "500"
}
Label {
id: atLabel
text: "@"
}
TextField {
id: txGasPrice
width: 200
placeholderText: "Gas price"
text: "10"
validator: RegExpValidator { regExp: /\d*/ }
}
ComboBox {
id: gasDenom
currentIndex: 4
model: denomModel
}
}
Label {
id: txDataLabel
text: "Data"
}
TextArea {
id: codeView
height: 300
anchors.topMargin: 5
width: 400
onTextChanged: {
contractFormReady()
}
}
Button {
id: txButton
/* enabled: false */
states: [
State {
name: "READY"
PropertyChanges { target: txButton; /*enabled: true*/}
},
State {
name: "NOTREADY"
PropertyChanges { target: txButton; /*enabled:false*/}
}
]
text: "Send"
onClicked: {
var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros;
var res = gui.transact(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text)
if(res[1]) {
txResult.text = "Your contract <b>could not</b> be sent over the network:\n<b>"
txResult.text += res[1].error()
txResult.text += "</b>"
mainContractColumn.state = "ERROR"
} else {
txResult.text = "Your transaction has been submitted:\n"
txOutput.text = res[0].address
mainContractColumn.state = "DONE"
}
}
}
Text {
id: txResult
visible: false
}
TextField {
id: txOutput
visible: false
width: 530
}
Button {
id: newTxButton
visible: false
text: "Create a new transaction"
onClicked: {
this.visible = false
txResult.text = ""
txOutput.text = ""
mainContractColumn.state = "SETUP"
}
}
}
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"
}
}
}

View File

@ -0,0 +1,165 @@
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 {
id: root
property var title: "Wallet"
property var iconSource: "../wallet.png"
property var menuItem
objectName: "walletView"
anchors.fill: parent
function onReady() {
menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.key().address))
}
ListModel {
id: denomModel
ListElement { text: "Wei" ; zeros: "" }
ListElement { text: "Ada" ; zeros: "000" }
ListElement { text: "Babbage" ; zeros: "000000" }
ListElement { text: "Shannon" ; zeros: "000000000" }
ListElement { text: "Szabo" ; zeros: "000000000000" }
ListElement { text: "Finney" ; zeros: "000000000000000" }
ListElement { text: "Ether" ; zeros: "000000000000000000" }
ListElement { text: "Einstein" ;zeros: "000000000000000000000" }
ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" }
}
ColumnLayout {
spacing: 10
y: 40
anchors.fill: parent
Text {
id: balance
text: "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.key().address))
font.pixelSize: 24
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: 20
}
}
Rectangle {
id: newTxPane
color: "#ececec"
border.color: "#cccccc"
border.width: 1
anchors {
top: balance.bottom
topMargin: 10
left: parent.left
leftMargin: 5
right: parent.right
rightMargin: 5
}
height: 100
RowLayout {
id: amountFields
spacing: 10
anchors {
top: parent.top
topMargin: 20
left: parent.left
leftMargin: 20
}
Text {
text: "Ξ "
}
// There's something off with the row layout where textfields won't listen to the width setting
Rectangle {
width: 50
height: 20
TextField {
id: txValue
width: parent.width
placeholderText: "0.00"
}
}
ComboBox {
id: valueDenom
currentIndex: 6
model: denomModel
}
}
RowLayout {
id: toFields
spacing: 10
anchors {
top: amountFields.bottom
topMargin: 5
left: parent.left
leftMargin: 20
}
Text {
text: "To"
}
Rectangle {
width: 200
height: 20
TextField {
id: txTo
width: parent.width
placeholderText: "Address or name"
}
}
Button {
text: "Send"
onClicked: {
var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
var gasPrice = "10000000000000"
var res = eth.transact({from: eth.key().privateKey, to: txTo.text, value: value, gas: "500", gasPrice: gasPrice})
console.log(res)
}
}
}
}
Rectangle {
anchors {
left: parent.left
right: parent.right
top: newTxPane.bottom
topMargin: 10
bottom: parent.bottom
}
TableView {
id: txTableView
anchors.fill : parent
TableViewColumn{ role: "num" ; title: "#" ; width: 30 }
TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
model: ListModel {
id: txModel
Component.onCompleted: {
var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"}))
for(var i = 0; i < messages.length; i++) {
var message = messages[i];
this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)})
}
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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
@ -22,19 +23,113 @@ ApplicationWindow {
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
}
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
}
}
}
}
WebView {
objectName: "webView"
id: webview
anchors.fill: parent
/*
anchors {
left: parent.left
right: parent.right
bottom: sizeGrip.top
top: parent.top
}
*/
anchors {
left: parent.left
right: parent.right
bottom: parent.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 = ui.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
@ -46,105 +141,126 @@ ApplicationWindow {
try {
switch(data.call) {
case "getCoinBase":
postData(data._seed, eth.getCoinBase())
case "getCoinBase":
postData(data._seed, eth.coinBase())
break
case "getIsListening":
postData(data._seed, eth.getIsListening())
break
break
case "getIsMining":
postData(data._seed, eth.getIsMining())
case "getIsListening":
postData(data._seed, eth.isListening())
break
case "getPeerCount":
postData(data._seed, eth.getPeerCount())
break
break
case "getIsMining":
postData(data._seed, eth.isMining())
case "getTxCountAt":
require(1)
postData(data._seed, eth.getTxCountAt(data.args[0]))
break
break
case "getBlockByNumber":
var block = eth.getBlock(data.args[0])
case "getPeerCount":
postData(data._seed, eth.peerCount())
break
case "getTxCountAt":
require(1)
postData(data._seed, eth.txCountAt(data.args[0]))
break
case "getBlockByNumber":
var block = eth.blockByNumber(data.args[0])
postData(data._seed, block)
break
case "getBlockByHash":
var block = eth.getBlock(data.args[0])
case "getBlockByHash":
var block = eth.blockByHash(data.args[0])
postData(data._seed, block)
break
case "transact":
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":
case "getStorage":
require(2);
var stateObject = eth.getStateObject(data.args[0])
var storage = stateObject.getStorage(data.args[1])
var stateObject = eth.stateObject(data.args[0])
var storage = stateObject.storageAt(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)
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.getTransactionsFor(data.args[0], true)
postData(data._seed, txs)
break
case "getBalance":
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.getStateObject(data.args[0]).value());
postData(data._seed, eth.stateObject(data.args[0]).value());
break
case "getKey":
var key = eth.getKey().privateKey;
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
case "disconnect":
/*
case "watch":
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":
case "getSecretToAddress":
require(1)
postData(data._seed, eth.secretToAddress(data.args[0]))
break;
case "debug":
console.log(data.args[0]);
break;
case "messages":
require(1);
var messages = JSON.parse(eth.getMessages(data.args[0]))
postData(data._seed, messages)
break
case "mutan":
require(1)
var code = eth.compileMutan(data.args[0])
postData(data._seed, "0x"+code)
break;
}
} catch(e) {
console.log(data.call + ": " + e)
@ -153,6 +269,11 @@ ApplicationWindow {
}
}
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);
@ -165,6 +286,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)
}
@ -176,31 +302,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

BIN
ethereal/assets/wallet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -2,15 +2,16 @@ package main
import (
"fmt"
"math/big"
"strconv"
"strings"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethvm"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"math/big"
"strconv"
"strings"
"gopkg.in/qml.v1"
)
type DebuggerWindow struct {
@ -102,14 +103,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
}
}()
data := ethutil.StringToByteFunc(dataStr, func(s string) (ret []byte) {
slice := strings.Split(dataStr, "\n")
for _, dataItem := range slice {
d := ethutil.FormatData(dataItem)
ret = append(ret, d...)
}
return
})
data := utils.FormatTransactionData(dataStr)
var err error
script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
@ -134,26 +128,13 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
state := self.lib.eth.StateManager().TransState()
account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
contract := ethstate.NewStateObject([]byte{0})
contract.Amount = value
contract.Balance = value
self.SetAsm(script)
callerClosure := ethvm.NewClosure(account, contract, script, gas, gasPrice)
block := self.lib.eth.BlockChain().CurrentBlock
/*
vm := ethchain.NewVm(state, self.lib.eth.StateManager(), ethchain.RuntimeVars{
Block: block,
Origin: account.Address(),
BlockNumber: block.Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
Value: ethutil.Big(valueStr),
})
*/
callerClosure := ethvm.NewClosure(&ethstate.Message{}, account, contract, script, gas, gasPrice)
env := utils.NewEnv(state, block, account.Address(), value)
vm := ethvm.New(env)
vm.Verbose = true

View File

@ -1,12 +1,14 @@
package main
import (
"fmt"
"encoding/json"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"github.com/ethereum/go-ethereum/javascript"
"gopkg.in/qml.v1"
)
type AppContainer interface {
@ -17,34 +19,37 @@ type AppContainer interface {
Engine() *qml.Engine
NewBlock(*ethchain.Block)
ObjectChanged(*ethstate.StateObject)
StorageChanged(*ethstate.StorageState)
NewWatcher(chan bool)
Messages(ethstate.Messages, string)
Post(string, int)
}
type ExtApplication struct {
*ethpub.PEthereum
*ethpipe.JSPipe
eth ethchain.EthManager
blockChan chan ethutil.React
changeChan chan ethutil.React
blockChan chan ethreact.Event
messageChan chan ethreact.Event
quitChan chan bool
watcherQuitChan chan bool
container AppContainer
lib *UiLib
registeredEvents []string
filters map[string]*ethchain.Filter
container AppContainer
lib *UiLib
}
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
app := &ExtApplication{
ethpub.NewPEthereum(lib.eth),
make(chan ethutil.React, 1),
make(chan ethutil.React, 1),
ethpipe.NewJSPipe(lib.eth),
lib.eth,
make(chan ethreact.Event, 100),
make(chan ethreact.Event, 100),
make(chan bool),
make(chan bool),
make(map[string]*ethchain.Filter),
container,
lib,
nil,
}
return app
@ -58,8 +63,7 @@ func (app *ExtApplication) run() {
err := app.container.Create()
if err != nil {
fmt.Println(err)
logger.Errorln(err)
return
}
@ -69,6 +73,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)
@ -83,9 +88,6 @@ func (app *ExtApplication) stop() {
// Clean up
reactor := app.lib.eth.Reactor()
reactor.Unsubscribe("newBlock", app.blockChan)
for _, event := range app.registeredEvents {
reactor.Unsubscribe(event, app.changeChan)
}
// Kill the main loop
app.quitChan <- true
@ -93,7 +95,6 @@ func (app *ExtApplication) stop() {
close(app.blockChan)
close(app.quitChan)
close(app.changeChan)
app.container.Destroy()
}
@ -108,26 +109,37 @@ out:
if block, ok := block.Resource.(*ethchain.Block); ok {
app.container.NewBlock(block)
}
case object := <-app.changeChan:
if stateObject, ok := object.Resource.(*ethstate.StateObject); ok {
app.container.ObjectChanged(stateObject)
} 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))
}
app.registeredEvents = append(app.registeredEvents, event)
b, err := json.Marshal(msgs)
if err != nil {
return "{\"error\":" + err.Error() + "}"
}
return string(b)
}

View File

@ -2,31 +2,41 @@ package main
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"os"
"strconv"
"strings"
"time"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethminer"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"math/big"
"strconv"
"strings"
"time"
"gopkg.in/qml.v1"
)
var logger = ethlog.NewLogger("GUI")
type plugin struct {
Name string `json:"name"`
Path string `json:"path"`
}
type Gui struct {
// The main application window
win *qml.Window
// QML Engine
engine *qml.Engine
component *qml.Common
qmlDone bool
// The ethereum interface
eth *eth.Ethereum
@ -35,14 +45,17 @@ type Gui struct {
txDb *ethdb.LDBDatabase
pub *ethpub.PEthereum
logLevel ethlog.LogLevel
open bool
pipe *ethpipe.JSPipe
Session string
clientIdentity *ethwire.SimpleClientIdentity
config *ethutil.ConfigManager
plugins map[string]plugin
miner *ethminer.Miner
}
@ -53,9 +66,17 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
panic(err)
}
pub := ethpub.NewPEthereum(ethereum)
pipe := ethpipe.NewJSPipe(ethereum)
gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)}
data, err := ethutil.ReadAllFile(ethutil.Config.ExecPath + "/plugins.json")
if err != nil {
fmt.Println(err)
}
fmt.Println("plugins:", string(data))
return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config}
json.Unmarshal([]byte(data), &gui.plugins)
return gui
}
func (gui *Gui) Start(assetPath string) {
@ -64,22 +85,20 @@ func (gui *Gui) Start(assetPath string) {
// Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
Init: func(p *ethpipe.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
}, {
Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
Init: func(p *ethpipe.JSTransaction, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
}, {
Init: func(p *ethpub.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" },
Init: func(p *ethpipe.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" },
}})
// Create a new QML engine
gui.engine = qml.NewEngine()
context := gui.engine.Context()
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
// Expose the eth library and the ui library to QML
context.SetVar("eth", gui)
context.SetVar("pub", gui.pub)
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
context.SetVar("ui", gui.uiLib)
context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib)
// Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
@ -102,11 +121,13 @@ func (gui *Gui) Start(assetPath string) {
logger.Infoln("Starting GUI")
gui.open = true
win.Show()
// only add the gui logger after window is shown otherwise slider wont be shown
if addlog {
ethlog.AddLogSystem(gui)
}
win.Wait()
// need to silence gui logger after window closed otherwise logsystem hangs (but do not save loglevel)
gui.logLevel = ethlog.Silence
gui.open = false
@ -118,6 +139,9 @@ func (gui *Gui) Stop() {
gui.open = false
gui.win.Hide()
}
gui.uiLib.jsEngine.Stop()
logger.Infoln("Stopped")
}
@ -126,10 +150,14 @@ func (gui *Gui) ToggleMining() {
if gui.eth.Mining {
utils.StopMining(gui.eth)
txt = "Start mining"
gui.getObjectByName("miningLabel").Set("visible", false)
} else {
utils.StartMining(gui.eth)
gui.miner = utils.GetMiner()
txt = "Stop mining"
gui.getObjectByName("miningLabel").Set("visible", true)
}
gui.win.Root().Set("miningButtonText", txt)
@ -141,18 +169,54 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
return nil, err
}
win := gui.createWindow(component)
gui.win = gui.createWindow(component)
go func() {
gui.setInitialBlockChain()
gui.loadAddressBook()
gui.readPreviousTransactions()
gui.setPeerInfo()
}()
gui.update()
go gui.update()
return gui.win, nil
}
return win, nil
func (self *Gui) DumpState(hash, path string) {
var stateDump []byte
if len(hash) == 0 {
stateDump = self.eth.StateManager().CurrentState().Dump()
} else {
var block *ethchain.Block
if hash[0] == '#' {
i, _ := strconv.Atoi(hash[1:])
block = self.eth.BlockChain().GetBlockByNumber(uint64(i))
} else {
block = self.eth.BlockChain().GetBlock(ethutil.Hex2Bytes(hash))
}
if block == nil {
logger.Infof("block err: not found %s\n", hash)
return
}
stateDump = block.State().Dump()
}
file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)
if err != nil {
logger.Infoln("dump err: ", err)
return
}
defer file.Close()
logger.Infof("dumped state (%s) to %s\n", hash, path)
file.Write(stateDump)
}
// The done handler will be called by QML when all views have been loaded
func (gui *Gui) Done() {
gui.qmlDone = true
}
func (gui *Gui) ImportKey(filePath string) {
}
func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
@ -218,44 +282,84 @@ type address struct {
}
func (gui *Gui) loadAddressBook() {
gui.win.Root().Call("clearAddress")
view := gui.getObjectByName("infoView")
view.Call("clearAddress")
nameReg := ethpub.EthereumConfig(gui.eth.StateManager()).NameReg()
nameReg := gui.pipe.World().Config().Get("NameReg")
if nameReg != nil {
nameReg.EachStorage(func(name string, value *ethutil.Value) {
if name[0] != 0 {
value.Decode()
gui.win.Root().Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())})
view.Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())})
}
})
}
}
func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) {
nameReg := ethpipe.New(gui.eth).World().Config().Get("NameReg")
addr := gui.address()
var inout string
if bytes.Compare(tx.Sender(), addr) == 0 {
inout = "send"
} else {
inout = "recv"
}
var (
ptx = ethpipe.NewJSTx(tx)
send = nameReg.Storage(tx.Sender())
rec = nameReg.Storage(tx.Recipient)
s, r string
)
if tx.CreatesContract() {
rec = nameReg.Storage(tx.CreationAddress())
}
if send.Len() != 0 {
s = strings.Trim(send.Str(), "\x00")
} else {
s = ethutil.Bytes2Hex(tx.Sender())
}
if rec.Len() != 0 {
r = strings.Trim(rec.Str(), "\x00")
} else {
if tx.CreatesContract() {
r = ethutil.Bytes2Hex(tx.CreationAddress())
} else {
r = ethutil.Bytes2Hex(tx.Recipient)
}
}
ptx.Sender = s
ptx.Address = r
if window == "post" {
gui.getObjectByName("transactionView").Call("addTx", ptx, inout)
} else {
gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout)
}
}
func (gui *Gui) readPreviousTransactions() {
it := gui.txDb.Db().NewIterator(nil, nil)
addr := gui.address()
for it.Next() {
tx := ethchain.NewTransactionFromBytes(it.Value())
var inout string
if bytes.Compare(tx.Sender(), addr) == 0 {
inout = "send"
} else {
inout = "recv"
}
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), inout)
gui.insertTransaction("post", tx)
}
it.Release()
}
func (gui *Gui) processBlock(block *ethchain.Block, initial bool) {
name := ethpub.FindNameInNameReg(gui.eth.StateManager(), block.Coinbase)
b := ethpub.NewPBlock(block)
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
b := ethpipe.NewJSBlock(block)
b.Name = name
gui.win.Root().Call("addBlock", b, initial)
gui.getObjectByName("chainView").Call("addBlock", b, initial)
}
func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) {
@ -280,17 +384,115 @@ func (self *Gui) getObjectByName(objectName string) qml.Object {
// Simple go routine function that updates the list of peers in the GUI
func (gui *Gui) update() {
reactor := gui.eth.Reactor()
// We have to wait for qml to be done loading all the windows.
for !gui.qmlDone {
time.Sleep(500 * time.Millisecond)
}
go func() {
go gui.setInitialBlockChain()
gui.loadAddressBook()
gui.setPeerInfo()
gui.readPreviousTransactions()
}()
for _, plugin := range gui.plugins {
gui.win.Root().Call("addPlugin", plugin.Path, "")
}
var (
blockChan = make(chan ethutil.React, 1)
txChan = make(chan ethutil.React, 1)
objectChan = make(chan ethutil.React, 1)
peerChan = make(chan ethutil.React, 1)
chainSyncChan = make(chan ethutil.React, 1)
miningChan = make(chan ethutil.React, 1)
blockChan = make(chan ethreact.Event, 100)
txChan = make(chan ethreact.Event, 100)
objectChan = make(chan ethreact.Event, 100)
peerChan = make(chan ethreact.Event, 100)
chainSyncChan = make(chan ethreact.Event, 100)
miningChan = make(chan ethreact.Event, 100)
)
peerUpdateTicker := time.NewTicker(5 * time.Second)
generalUpdateTicker := time.NewTicker(1 * time.Second)
state := gui.eth.StateManager().TransState()
unconfirmedFunds := new(big.Int)
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Balance)))
gui.getObjectByName("syncProgressIndicator").Set("visible", !gui.eth.IsUpToDate())
lastBlockLabel := gui.getObjectByName("lastBlockLabel")
miningLabel := gui.getObjectByName("miningLabel")
go func() {
for {
select {
case b := <-blockChan:
block := b.Resource.(*ethchain.Block)
gui.processBlock(block, false)
if bytes.Compare(block.Coinbase, gui.address()) == 0 {
gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Balance, nil)
}
case txMsg := <-txChan:
tx := txMsg.Resource.(*ethchain.Transaction)
if txMsg.Name == "newTx:pre" {
object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
}
gui.setWalletValue(object.Balance, unconfirmedFunds)
gui.insertTransaction("pre", tx)
} else {
object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
object.SubAmount(tx.Value)
gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
object.AddAmount(tx.Value)
gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "recv")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
gui.setWalletValue(object.Balance, nil)
state.UpdateStateObject(object)
}
case msg := <-chainSyncChan:
sync := msg.Resource.(bool)
gui.win.Root().ObjectByName("syncProgressIndicator").Set("visible", sync)
case <-objectChan:
gui.loadAddressBook()
case <-peerChan:
gui.setPeerInfo()
case <-peerUpdateTicker.C:
gui.setPeerInfo()
case msg := <-miningChan:
if msg.Name == "miner:start" {
gui.miner = msg.Resource.(*ethminer.Miner)
} else {
gui.miner = nil
}
case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.BlockChain().CurrentBlock.Number.String()
lastBlockLabel.Set("text", statusText)
if gui.miner != nil {
pow := gui.miner.GetPow()
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
}
}
}
}()
reactor := gui.eth.Reactor()
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("newTx:pre", txChan)
reactor.Subscribe("newTx:post", txChan)
@ -298,98 +500,22 @@ func (gui *Gui) update() {
reactor.Subscribe("miner:start", miningChan)
reactor.Subscribe("miner:stop", miningChan)
nameReg := ethpub.EthereumConfig(gui.eth.StateManager()).NameReg()
if nameReg != nil {
reactor.Subscribe("object:"+string(nameReg.Address()), objectChan)
}
nameReg := gui.pipe.World().Config().Get("NameReg")
reactor.Subscribe("object:"+string(nameReg.Address()), objectChan)
reactor.Subscribe("peerList", peerChan)
}
peerUpdateTicker := time.NewTicker(5 * time.Second)
generalUpdateTicker := time.NewTicker(1 * time.Second)
state := gui.eth.StateManager().TransState()
unconfirmedFunds := new(big.Int)
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Amount)))
gui.getObjectByName("syncProgressIndicator").Set("visible", !gui.eth.IsUpToDate())
lastBlockLabel := gui.getObjectByName("lastBlockLabel")
for {
select {
case b := <-blockChan:
block := b.Resource.(*ethchain.Block)
gui.processBlock(block, false)
if bytes.Compare(block.Coinbase, gui.address()) == 0 {
gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Amount, nil)
}
case txMsg := <-txChan:
tx := txMsg.Resource.(*ethchain.Transaction)
if txMsg.Event == "newTx:pre" {
object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
}
gui.setWalletValue(object.Amount, unconfirmedFunds)
} else {
object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
object.SubAmount(tx.Value)
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
object.AddAmount(tx.Value)
}
gui.setWalletValue(object.Amount, nil)
state.UpdateStateObject(object)
}
case msg := <-chainSyncChan:
sync := msg.Resource.(bool)
gui.win.Root().ObjectByName("syncProgressIndicator").Set("visible", sync)
case <-objectChan:
gui.loadAddressBook()
case <-peerChan:
gui.setPeerInfo()
case <-peerUpdateTicker.C:
gui.setPeerInfo()
case msg := <-miningChan:
if msg.Event == "miner:start" {
gui.miner = msg.Resource.(*ethminer.Miner)
} else {
gui.miner = nil
}
case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.BlockChain().CurrentBlock.Number.String()
if gui.miner != nil {
pow := gui.miner.GetPow()
if pow.GetHashrate() != 0 {
statusText = "Mining @ " + strconv.FormatInt(pow.GetHashrate(), 10) + "Khash - " + statusText
}
}
lastBlockLabel.Set("text", statusText)
}
}
func (gui *Gui) CopyToClipboard(data string) {
//clipboard.WriteAll("test")
fmt.Println("COPY currently BUGGED. Here are the contents:\n", data)
}
func (gui *Gui) setPeerInfo() {
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
gui.win.Root().Call("resetPeers")
for _, peer := range gui.pub.GetPeers() {
for _, peer := range gui.pipe.Peers() {
gui.win.Root().Call("addPeer", peer)
}
}
@ -402,18 +528,19 @@ func (gui *Gui) address() []byte {
return gui.eth.KeyManager().Address()
}
func (gui *Gui) RegisterName(name string) {
name = fmt.Sprintf("\"register\"\n\"%s\"", name)
func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) {
var data string
if len(recipient) == 0 {
code, err := ethutil.Compile(d, false)
if err != nil {
return nil, err
}
data = ethutil.Bytes2Hex(code)
} else {
data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
}
gui.pub.Transact(gui.privateKey(), "NameReg", "", "10000", "10000000000000", name)
}
func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
return gui.pub.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
}
func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
return gui.pub.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
}
func (gui *Gui) SetCustomIdentifier(customIdentifier string) {
@ -425,6 +552,10 @@ func (gui *Gui) GetCustomIdentifier() string {
return gui.clientIdentity.GetCustomIdentifier()
}
func (gui *Gui) ToggleTurboMining() {
gui.miner.ToggleTurbo()
}
// functions that allow Gui to implement interface ethlog.LogSystem
func (gui *Gui) SetLogLevel(level ethlog.LogLevel) {
gui.logLevel = level
@ -435,6 +566,20 @@ func (gui *Gui) GetLogLevel() ethlog.LogLevel {
return gui.logLevel
}
func (self *Gui) AddPlugin(pluginPath string) {
self.plugins[pluginPath] = plugin{Name: "SomeName", Path: pluginPath}
json, _ := json.MarshalIndent(self.plugins, "", " ")
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
}
func (self *Gui) RemovePlugin(pluginPath string) {
delete(self.plugins, pluginPath)
json, _ := json.MarshalIndent(self.plugins, "", " ")
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
}
// this extra function needed to give int typecast value to gui widget
// that sets initial loglevel to default
func (gui *Gui) GetLogLevelInt() int {
@ -451,9 +596,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")
for _, line := range lines {
gui.win.Root().Call("addLog", line)
}
/*
str := strings.TrimRight(s, "\n")
lines := strings.Split(str, "\n")
view := gui.getObjectByName("infoView")
for _, line := range lines {
view.Call("addLog", line)
}
*/
}

View File

@ -1,18 +1,22 @@
package main
import (
"encoding/json"
"errors"
"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"
"github.com/howeyc/fsnotify"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"github.com/howeyc/fsnotify"
"gopkg.in/qml.v1"
)
type HtmlApplication struct {
@ -41,7 +45,7 @@ func (app *HtmlApplication) Create() error {
return errors.New("Ethereum package not yet supported")
// TODO
ethutil.OpenPackage(app.path)
//ethutil.OpenPackage(app.path)
}
win := component.CreateWindow(nil)
@ -118,18 +122,26 @@ func (app *HtmlApplication) Window() *qml.Window {
}
func (app *HtmlApplication) NewBlock(block *ethchain.Block) {
b := &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
b := &ethpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
app.webView.Call("onNewBlockCb", b)
}
func (app *HtmlApplication) ObjectChanged(stateObject *ethstate.StateObject) {
app.webView.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
}
func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) {
var msgs []javascript.JSMessage
for _, m := range messages {
msgs = append(msgs, javascript.NewJSMessage(m))
}
func (app *HtmlApplication) StorageChanged(storageObject *ethstate.StorageState) {
app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject))
b, _ := json.Marshal(msgs)
self.webView.Call("onWatchedCb", string(b), id)
}
func (app *HtmlApplication) Destroy() {
app.engine.Destroy()
}
func (app *HtmlApplication) Post(data string, seed int) {
fmt.Println("about to call 'post'")
app.webView.Call("post", seed, data)
}

View File

@ -1,30 +1,23 @@
package main
import (
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"os"
"runtime"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/go-ethereum/utils"
"gopkg.in/qml.v1"
)
const (
ClientIdentifier = "Ethereal"
Version = "0.6.0"
Version = "0.6.4"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
qml.Init(nil)
var interrupted = false
utils.RegisterInterrupt(func(os.Signal) {
interrupted = true
})
utils.HandleInterrupt()
var ethereum *eth.Ethereum
func run() error {
// precedence: code-internal flag default < config file < environment variables < command line
Init() // parsing command line
@ -43,7 +36,7 @@ func main() {
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
if ShowGenesis {
utils.ShowGenesis(ethereum)
@ -61,6 +54,26 @@ func main() {
utils.StartEthereum(ethereum, UseSeed)
// gui blocks the main thread
gui.Start(AssetPath)
return nil
}
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)
qml.Run(run)
var interrupted = false
utils.RegisterInterrupt(func(os.Signal) {
interrupted = true
})
utils.HandleInterrupt()
// we need to run the interrupt callbacks in case gui is closed
// this skips if we got here by actual interrupt stopping the GUI
if !interrupted {

View File

@ -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/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"runtime"
"gopkg.in/qml.v1"
)
type QmlApplication struct {
@ -25,7 +27,7 @@ func (app *QmlApplication) Create() error {
path := string(app.path)
// For some reason for windows we get /c:/path/to/something, windows doesn't like the first slash but is fine with the others so we are removing it
if string(app.path[0]) == "/" && runtime.GOOS == "windows" {
if app.path[0] == '/' && runtime.GOOS == "windows" {
path = app.path[1:]
}
@ -47,16 +49,12 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
// Events
func (app *QmlApplication) NewBlock(block *ethchain.Block) {
pblock := &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
pblock := &ethpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
app.win.Call("onNewBlockCb", pblock)
}
func (app *QmlApplication) ObjectChanged(stateObject *ethstate.StateObject) {
app.win.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
}
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
@ -66,3 +64,5 @@ func (app *QmlApplication) Engine() *qml.Engine {
func (app *QmlApplication) Window() *qml.Window {
return app.win
}
func (app *QmlApplication) Post(data string, s int) {}

View File

@ -1,10 +1,20 @@
package main
import (
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"bytes"
"fmt"
"path"
"strconv"
"strings"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"gopkg.in/qml.v1"
)
type memAddr struct {
@ -14,6 +24,7 @@ type memAddr struct {
// UI Library that has some basic functionality exposed
type UiLib struct {
*ethpipe.JSPipe
engine *qml.Engine
eth *eth.Ethereum
connected bool
@ -22,10 +33,57 @@ type UiLib struct {
win *qml.Window
Db *Debugger
DbWindow *DebuggerWindow
jsEngine *javascript.JSRE
filterCallbacks map[int][]int
filters map[int]*GuiFilter
}
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
return &UiLib{engine: engine, eth: eth, assetPath: assetPath}
return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int), filters: make(map[int]*GuiFilter)}
}
func (self *UiLib) LookupDomain(domain string) string {
world := self.World()
if len(domain) > 32 {
domain = string(ethcrypto.Sha3Bin([]byte(domain)))
}
data := world.Config().Get("DnsReg").StorageString(domain).Bytes()
// Left padded = A record, Right padded = CNAME
if data[0] == 0 {
data = bytes.TrimLeft(data, "\x00")
var ipSlice []string
for _, d := range data {
ipSlice = append(ipSlice, strconv.Itoa(int(d)))
}
return strings.Join(ipSlice, ".")
} else {
data = bytes.TrimRight(data, "\x00")
return string(data)
}
}
func (self *UiLib) ImportTx(rlpTx string) {
tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
self.eth.TxPool().QueueTransaction(tx)
}
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) {
@ -42,6 +100,10 @@ func (ui *UiLib) OpenHtml(path string) {
go app.run()
}
func (ui *UiLib) OpenBrowser() {
ui.OpenHtml("file://" + ui.AssetPath("ext/home.html"))
}
func (ui *UiLib) Muted(content string) {
component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
if err != nil {
@ -94,7 +156,96 @@ func (self *UiLib) StartDbWithCode(code string) {
func (self *UiLib) StartDebugger() {
dbWindow := NewDebuggerWindow(self)
//self.DbWindow = dbWindow
dbWindow.Show()
}
func (self *UiLib) RegisterFilter(object map[string]interface{}, seed int) {
filter := &GuiFilter{ethpipe.NewJSFilterFromMap(object, self.eth), seed}
self.filters[seed] = filter
filter.MessageCallback = func(messages ethstate.Messages) {
for _, callbackSeed := range self.filterCallbacks[seed] {
self.win.Root().Call("invokeFilterCallback", filter.MessagesToJson(messages), seed, callbackSeed)
}
}
}
func (self *UiLib) RegisterFilterString(typ string, seed int) {
filter := &GuiFilter{ethpipe.NewJSFilterFromMap(nil, self.eth), seed}
self.filters[seed] = filter
if typ == "chain" {
filter.BlockCallback = func(block *ethchain.Block) {
for _, callbackSeed := range self.filterCallbacks[seed] {
self.win.Root().Call("invokeFilterCallback", "{}", seed, callbackSeed)
}
}
}
}
func (self *UiLib) RegisterFilterCallback(seed, cbSeed int) {
self.filterCallbacks[seed] = append(self.filterCallbacks[seed], cbSeed)
}
func (self *UiLib) UninstallFilter(seed int) {
filter := self.filters[seed]
if filter != nil {
filter.Uninstall()
delete(self.filters, seed)
}
}
type GuiFilter struct {
*ethpipe.JSFilter
seed int
}
func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, error) {
// Default values
if object["from"] == nil {
object["from"] = ""
}
if object["to"] == nil {
object["to"] = ""
}
if object["value"] == nil {
object["value"] = ""
}
if object["gas"] == nil {
object["gas"] = ""
}
if object["gasPrice"] == nil {
object["gasPrice"] = ""
}
var dataStr string
var data []string
if list, ok := object["data"].(*qml.List); ok {
list.Convert(&data)
}
for _, str := range data {
if ethutil.IsHex(str) {
str = str[2:]
if len(str) != 64 {
str = ethutil.LeftPadString(str, 64)
}
} else {
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
}
dataStr += str
}
return self.JSPipe.Transact(
object["from"].(string),
object["to"].(string),
object["value"].(string),
object["gas"].(string),
object["gasPrice"].(string),
dataStr,
)
}

View File

@ -1,11 +1,13 @@
package main
import (
"github.com/ethereum/eth-go"
"github.com/ethereum/go-ethereum/ethereum/repl"
"github.com/ethereum/go-ethereum/utils"
"io/ioutil"
"os"
"github.com/ethereum/eth-go"
"github.com/ethereum/go-ethereum/ethereum/repl"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/utils"
)
func InitJsConsole(ethereum *eth.Ethereum) {
@ -25,7 +27,7 @@ func ExecJsFile(ethereum *eth.Ethereum, InputFile string) {
if err != nil {
logger.Fatalln(err)
}
re := ethrepl.NewJSRE(ethereum)
re := javascript.NewJSRE(ethereum)
utils.RegisterInterrupt(func(os.Signal) {
re.Stop()
})

View File

@ -3,10 +3,11 @@ package main
import (
"flag"
"fmt"
"github.com/ethereum/eth-go/ethlog"
"os"
"os/user"
"path"
"github.com/ethereum/eth-go/ethlog"
)
var Identifier string
@ -31,6 +32,9 @@ var LogFile string
var ConfigFile string
var DebugFile string
var LogLevel int
var Dump bool
var DumpHash string
var DumpNumber int
// flags specific to cli client
var StartMining bool
@ -71,6 +75,10 @@ func Init() {
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
flag.IntVar(&DumpNumber, "number", -1, "specify arg in number")
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")

View File

@ -1,15 +1,19 @@
package main
import (
"fmt"
"os"
"runtime"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"runtime"
)
const (
ClientIdentifier = "Ethereum(G)"
Version = "0.6.0"
Version = "0.6.4"
)
var logger = ethlog.NewLogger("CLI")
@ -23,7 +27,7 @@ func main() {
Init() // parsing command line
// If the difftool option is selected ignore all other log output
if DiffTool {
if DiffTool || Dump {
LogLevel = 0
}
@ -46,6 +50,32 @@ func main() {
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
if Dump {
var block *ethchain.Block
if len(DumpHash) == 0 && DumpNumber == -1 {
block = ethereum.BlockChain().CurrentBlock
} else if len(DumpHash) > 0 {
block = ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(DumpHash))
} else {
block = ethereum.BlockChain().GetBlockByNumber(uint64(DumpNumber))
}
if block == nil {
fmt.Fprintln(os.Stderr, "block not found")
// We want to output valid JSON
fmt.Println("{}")
os.Exit(1)
}
// Leave the Println. This needs clean output for piping
fmt.Printf("%s\n", block.State().Dump())
os.Exit(0)
}
if ShowGenesis {
utils.ShowGenesis(ethereum)
}

View File

@ -3,12 +3,14 @@ package ethrepl
import (
"bufio"
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"io"
"os"
"path"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/javascript"
)
var logger = ethlog.NewLogger("REPL")
@ -19,7 +21,7 @@ type Repl interface {
}
type JSRepl struct {
re *JSRE
re *javascript.JSRE
prompt string
@ -34,7 +36,7 @@ func NewJSRepl(ethereum *eth.Ethereum) *JSRepl {
panic(err)
}
return &JSRepl{re: NewJSRE(ethereum), prompt: "> ", history: hist}
return &JSRepl{re: javascript.NewJSRE(ethereum), prompt: "> ", history: hist}
}
func (self *JSRepl) Start() {

View File

@ -115,8 +115,8 @@ L:
}
func (self *JSRepl) PrintValue(v interface{}) {
method, _ := self.re.vm.Get("prettyPrint")
v, err := self.re.vm.ToValue(v)
method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v)
if err == nil {
method.Call(method, v)
}

View File

@ -1,95 +0,0 @@
package ethrepl
import (
"fmt"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/otto"
)
type JSStateObject struct {
*ethpub.PStateObject
eth *JSEthereum
}
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
cb := call.Argument(0)
self.PStateObject.EachStorage(func(key string, value *ethutil.Value) {
value.Decode()
cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes())))
})
return otto.UndefinedValue()
}
// The JSEthereum object attempts to wrap the PEthereum object and returns
// meaningful javascript objects
type JSBlock struct {
*ethpub.PBlock
eth *JSEthereum
}
func (self *JSBlock) GetTransaction(hash string) otto.Value {
return self.eth.toVal(self.PBlock.GetTransaction(hash))
}
type JSEthereum struct {
*ethpub.PEthereum
vm *otto.Otto
}
func (self *JSEthereum) GetBlock(hash string) otto.Value {
return self.toVal(&JSBlock{self.PEthereum.GetBlock(hash), self})
}
func (self *JSEthereum) GetPeers() otto.Value {
return self.toVal(self.PEthereum.GetPeers())
}
func (self *JSEthereum) GetKey() otto.Value {
return self.toVal(self.PEthereum.GetKey())
}
func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(&JSStateObject{self.PEthereum.GetStateObject(addr), self})
}
func (self *JSEthereum) GetStateKeyVals(addr string) otto.Value {
return self.toVal(self.PEthereum.GetStateObject(addr).StateKeyVal(false))
}
func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
r, err := self.PEthereum.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return self.toVal(r)
}
func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, scriptStr string) otto.Value {
r, err := self.PEthereum.Create(key, valueStr, gasStr, gasPriceStr, scriptStr)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return self.toVal(r)
}
func (self *JSEthereum) toVal(v interface{}) otto.Value {
result, err := self.vm.ToValue(v)
if err != nil {
fmt.Println("Value unknown:", err)
return otto.UndefinedValue()
}
return result
}

View File

@ -1,30 +1,32 @@
package ethrepl
package javascript
import (
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/otto"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/otto"
)
var jsrelogger = ethlog.NewLogger("JSRE")
type JSRE struct {
ethereum *eth.Ethereum
vm *otto.Otto
lib *ethpub.PEthereum
Vm *otto.Otto
pipe *ethpipe.JSPipe
blockChan chan ethutil.React
changeChan chan ethutil.React
blockChan chan ethreact.Event
changeChan chan ethreact.Event
quitChan chan bool
objectCb map[string][]otto.Value
@ -33,9 +35,9 @@ type JSRE struct {
func (jsre *JSRE) LoadExtFile(path string) {
result, err := ioutil.ReadFile(path)
if err == nil {
jsre.vm.Run(result)
jsre.Vm.Run(result)
} else {
jsrelogger.Debugln("Could not load file:", path)
jsrelogger.Infoln("Could not load file:", path)
}
}
@ -48,15 +50,15 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE {
re := &JSRE{
ethereum,
otto.New(),
ethpub.NewPEthereum(ethereum),
make(chan ethutil.React, 1),
make(chan ethutil.React, 1),
ethpipe.NewJSPipe(ethereum),
make(chan ethreact.Event, 10),
make(chan ethreact.Event, 10),
make(chan bool),
make(map[string][]otto.Value),
}
// Init the JS lib
re.vm.Run(jsLib)
re.Vm.Run(jsLib)
// Load extra javascript files
re.LoadIntFile("string.js")
@ -65,7 +67,11 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE {
// We have to make sure that, whoever calls this, calls "Stop"
go re.mainLoop()
re.Bind("eth", &JSEthereum{re.lib, re.vm})
// Subscribe to events
reactor := ethereum.Reactor()
reactor.Subscribe("newBlock", re.blockChan)
re.Bind("eth", &JSEthereum{re.pipe, re.Vm, ethereum})
re.initStdFuncs()
@ -75,11 +81,11 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE {
}
func (self *JSRE) Bind(name string, v interface{}) {
self.vm.Set(name, v)
self.Vm.Set(name, v)
}
func (self *JSRE) Run(code string) (otto.Value, error) {
return self.vm.Run(code)
return self.Vm.Run(code)
}
func (self *JSRE) Require(file string) error {
@ -109,10 +115,6 @@ func (self *JSRE) Stop() {
}
func (self *JSRE) mainLoop() {
// Subscribe to events
reactor := self.ethereum.Reactor()
reactor.Subscribe("newBlock", self.blockChan)
out:
for {
select {
@ -121,24 +123,12 @@ out:
case block := <-self.blockChan:
if _, ok := block.Resource.(*ethchain.Block); ok {
}
case object := <-self.changeChan:
if stateObject, ok := object.Resource.(*ethstate.StateObject); ok {
for _, cb := range self.objectCb[ethutil.Bytes2Hex(stateObject.Address())] {
val, _ := self.vm.ToValue(ethpub.NewPStateObject(stateObject))
cb.Call(cb, val)
}
} else if storageObject, ok := object.Resource.(*ethstate.StorageState); ok {
for _, cb := range self.objectCb[ethutil.Bytes2Hex(storageObject.StateAddress)+ethutil.Bytes2Hex(storageObject.Address)] {
val, _ := self.vm.ToValue(ethpub.NewPStorageState(storageObject))
cb.Call(cb, val)
}
}
}
}
}
func (self *JSRE) initStdFuncs() {
t, _ := self.vm.Get("eth")
t, _ := self.Vm.Get("eth")
eth := t.Object()
eth.Set("watch", self.watch)
eth.Set("addPeer", self.addPeer)
@ -146,19 +136,51 @@ func (self *JSRE) initStdFuncs() {
eth.Set("stopMining", self.stopMining)
eth.Set("startMining", self.startMining)
eth.Set("execBlock", self.execBlock)
eth.Set("dump", self.dump)
}
/*
* The following methods are natively implemented javascript functions
*/
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
var state *ethstate.State
if len(call.ArgumentList) > 0 {
var block *ethchain.Block
if call.Argument(0).IsNumber() {
num, _ := call.Argument(0).ToInteger()
block = self.ethereum.BlockChain().GetBlockByNumber(uint64(num))
} else if call.Argument(0).IsString() {
hash, _ := call.Argument(0).ToString()
block = self.ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(hash))
} else {
fmt.Println("invalid argument for dump. Either hex string or number")
}
if block == nil {
fmt.Println("block not found")
return otto.UndefinedValue()
}
state = block.State()
} else {
state = self.ethereum.StateManager().CurrentState()
}
v, _ := self.Vm.ToValue(state.Dump())
return v
}
func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value {
v, _ := self.vm.ToValue(utils.StopMining(self.ethereum))
v, _ := self.Vm.ToValue(utils.StopMining(self.ethereum))
return v
}
func (self *JSRE) startMining(call otto.FunctionCall) otto.Value {
v, _ := self.vm.ToValue(utils.StartMining(self.ethereum))
v, _ := self.Vm.ToValue(utils.StartMining(self.ethereum))
return v
}
@ -211,7 +233,7 @@ func (self *JSRE) require(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue()
}
t, _ := self.vm.Get("exports")
t, _ := self.Vm.Get("exports")
return t
}

View File

@ -1,4 +1,4 @@
package ethrepl
package javascript
const jsLib = `
function pp(object) {

138
javascript/types.go Normal file
View File

@ -0,0 +1,138 @@
package javascript
import (
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/otto"
)
type JSStateObject struct {
*ethpipe.JSObject
eth *JSEthereum
}
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
cb := call.Argument(0)
self.JSObject.EachStorage(func(key string, value *ethutil.Value) {
value.Decode()
cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes())))
})
return otto.UndefinedValue()
}
// The JSEthereum object attempts to wrap the PEthereum object and returns
// meaningful javascript objects
type JSBlock struct {
*ethpipe.JSBlock
eth *JSEthereum
}
func (self *JSBlock) GetTransaction(hash string) otto.Value {
return self.eth.toVal(self.JSBlock.GetTransaction(hash))
}
type JSMessage struct {
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 {
return JSMessage{
To: ethutil.Bytes2Hex(message.To),
From: ethutil.Bytes2Hex(message.From),
Input: ethutil.Bytes2Hex(message.Input),
Output: ethutil.Bytes2Hex(message.Output),
Path: message.Path,
Origin: ethutil.Bytes2Hex(message.Origin),
Timestamp: int32(message.Timestamp),
Coinbase: ethutil.Bytes2Hex(message.Origin),
Block: ethutil.Bytes2Hex(message.Block),
Number: int32(message.Number.Int64()),
}
}
type JSEthereum struct {
*ethpipe.JSPipe
vm *otto.Otto
ethereum *eth.Ethereum
}
func (self *JSEthereum) GetBlock(hash string) otto.Value {
return self.toVal(&JSBlock{self.JSPipe.BlockByHash(hash), self})
}
func (self *JSEthereum) GetPeers() otto.Value {
return self.toVal(self.JSPipe.Peers())
}
func (self *JSEthereum) GetKey() otto.Value {
return self.toVal(self.JSPipe.Key())
}
func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(&JSStateObject{ethpipe.NewJSObject(self.JSPipe.World().SafeGet(ethutil.Hex2Bytes(addr))), self})
}
func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
r, err := self.JSPipe.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return self.toVal(r)
}
func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, scriptStr string) otto.Value {
r, err := self.JSPipe.Transact(key, "", valueStr, gasStr, gasPriceStr, scriptStr)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return self.toVal(r)
}
func (self *JSEthereum) toVal(v interface{}) otto.Value {
result, err := self.vm.ToValue(v)
if err != nil {
fmt.Println("Value unknown:", err)
return otto.UndefinedValue()
}
return result
}
func (self *JSEthereum) Messages(object map[string]interface{}) otto.Value {
filter := ethchain.NewFilterFromMap(object, self.ethereum)
messages := filter.Find()
var msgs []JSMessage
for _, m := range messages {
msgs = append(msgs, NewJSMessage(m))
}
v, _ := self.vm.ToValue(msgs)
return v
}

View File

@ -1,25 +1,27 @@
package utils
import (
"bitbucket.org/kardianos/osext"
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethminer"
"github.com/ethereum/eth-go/ethpub"
"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"
"regexp"
"runtime"
"time"
"bitbucket.org/kardianos/osext"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethminer"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
)
var logger = ethlog.NewLogger("CLI")
@ -127,6 +129,7 @@ func NewDatabase() ethutil.Database {
}
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *ethwire.SimpleClientIdentity {
logger.Infoln("identity created")
return ethwire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
}
@ -193,7 +196,6 @@ func DefaultAssetPath() string {
}
func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) {
ethcrypto.InitWords(DefaultAssetPath()) // Init mnemonic word list
var err error
switch {
@ -226,7 +228,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(ethpipe.NewJSPipe(ethereum), RpcPort)
if err != nil {
logger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err)
} else {
@ -243,21 +245,18 @@ func GetMiner() *ethminer.Miner {
func StartMining(ethereum *eth.Ethereum) bool {
if !ethereum.Mining {
ethereum.Mining = true
addr := ethereum.KeyManager().Address()
go func() {
logger.Infoln("Start mining")
if miner == nil {
miner = ethminer.NewDefaultMiner(addr, ethereum)
}
// Give it some time to connect with peers
time.Sleep(3 * time.Second)
for !ethereum.IsUpToDate() {
time.Sleep(5 * time.Second)
}
logger.Infoln("Miner started")
miner.Start()
}()
RegisterInterrupt(func(os.Signal) {
@ -268,12 +267,23 @@ func StartMining(ethereum *eth.Ethereum) bool {
return false
}
func FormatTransactionData(data string) []byte {
d := ethutil.StringToByteFunc(data, func(s string) (ret []byte) {
slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000)
for _, dataItem := range slice {
d := ethutil.FormatData(dataItem)
ret = append(ret, d...)
}
return
})
return d
}
func StopMining(ethereum *eth.Ethereum) bool {
if ethereum.Mining && miner != nil {
miner.Stop()
logger.Infoln("Miner stopped")
logger.Infoln("Stopped mining")
ethereum.Mining = false
return true

View File

@ -1,9 +1,10 @@
package utils
import (
"math/big"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethstate"
"math/big"
)
type VMEnv struct {
@ -29,5 +30,6 @@ func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
func (self *VMEnv) Time() int64 { return self.block.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) State() *ethstate.State { return self.state }