Compare commits

...

127 Commits

Author SHA1 Message Date
Maran
2114218ed8 Merge branch 'release/poc5-rc11' 2014-05-28 16:19:58 +02:00
Maran
58032d60e7 Bump to RC11 2014-05-28 16:17:57 +02:00
Maran
d4f9daa631 Refactor hex encode and remove coupling of popup to main window 2014-05-28 16:14:24 +02:00
Maran
1eda1d25b0 Hooked up the Block Explorer to the Debugger so we can instantly debug made transactions 2014-05-28 15:48:17 +02:00
obscuren
aaeb268522 Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-05-28 13:01:04 +02:00
obscuren
540d39220d Merge branch 'master' into develop 2014-05-28 13:00:51 +02:00
obscuren
09728bf43c Debugger script&data now accept "0x" values 2014-05-28 13:00:45 +02:00
Maran
d4af5a5763 Merge branch 'hotfix/2' into develop 2014-05-28 11:53:31 +02:00
Maran
198ef97108 Merge branch 'hotfix/2' 2014-05-28 11:53:07 +02:00
Maran
138b7fe2d8 Consolidated external qml files to hopefully prevent crashing bug 2014-05-28 11:52:52 +02:00
obscuren
87a669aeda Merge branch 'develop' 2014-05-27 16:23:59 +02:00
obscuren
969b4a4a36 Added some shortcuts 2014-05-27 16:23:53 +02:00
obscuren
118860abb2 Merge branch 'release/poc5-rc10' into develop 2014-05-27 16:11:08 +02:00
obscuren
0dda955f90 Merge branch 'release/poc5-rc10' 2014-05-27 16:11:01 +02:00
obscuren
34b861c19c bump 2014-05-27 16:10:15 +02:00
obscuren
a0f73c2703 Minor fixes and improvements to the ui 2014-05-27 16:09:04 +02:00
obscuren
2be9823010 Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop
Conflicts:
	ethereal/assets/qml/wallet.qml
2014-05-27 13:31:57 +02:00
obscuren
47417506c3 New debugger implemented 2014-05-27 13:28:11 +02:00
obscuren
d0b31e2030 New debugger 2014-05-27 13:09:47 +02:00
Maran
6fe42f007c Uhm what 2014-05-27 12:46:02 +02:00
Maran
1ab865a994 Adding new blocks on broadcast 2014-05-27 11:49:42 +02:00
obscuren
4fd267a778 Sep debugger from main 2014-05-27 10:42:37 +02:00
Maran
07204f129e Increase size for asm 2014-05-27 10:38:51 +02:00
Maran
47a58b40cd Removed recursive function for loop 2014-05-27 10:29:39 +02:00
Maran
474c85bc9d Fix merge conflict 2014-05-27 10:29:12 +02:00
obscuren
d694e00a33 Fixed debugger 2014-05-26 21:11:38 +02:00
Maran
f7eb4e587f Remove extra log statement 2014-05-26 17:18:34 +02:00
Maran
5fc6ee6a4a Implemented simple block/tx explorer 2014-05-26 17:07:20 +02:00
Maran
5374a95c58 Merge branch 'develop' of github.com:ethereum/go-ethereum into develop 2014-05-26 10:53:58 +02:00
obscuren
26ecf4b780 Merge branch 'release/poc5-rc9' into develop 2014-05-26 00:42:03 +02:00
obscuren
0d89c1d212 Merge branch 'release/poc5-rc9' 2014-05-26 00:41:55 +02:00
obscuren
818bc84591 Bump 2014-05-26 00:39:05 +02:00
obscuren
b42c70be9c Recv send for txs 2014-05-26 00:10:38 +02:00
obscuren
d35380c19e New main script through init return value 2014-05-23 14:37:03 +02:00
Maran
36683f2e29 Merge branch 'develop' of github.com:ethereum/go-ethereum into develop 2014-05-23 10:29:17 +02:00
obscuren
5f8911f7cb Custom identifier 2014-05-22 10:38:37 +02:00
Maran
6b115659ca Hide inspector by default 2014-05-22 10:27:21 +02:00
Maran
8419ba0ec0 Fix merge conflicts 2014-05-22 10:26:39 +02:00
obscuren
01b833146f Added mining stop and start 2014-05-22 00:25:48 +02:00
obscuren
b902de20c7 Fixes #49 2014-05-21 23:46:16 +02:00
obscuren
3f5b348451 Fixes #50 2014-05-21 23:36:55 +02:00
Maran
93d79babc9 Merge branch 'develop' of github.com:ethereum/go-ethereum into develop 2014-05-21 14:07:45 +02:00
obscuren
941e0ba60a Merge branch 'release/poc5-rc8' into develop 2014-05-21 14:05:01 +02:00
obscuren
7f1a4c377c Merge branch 'release/poc5-rc8' 2014-05-21 14:04:54 +02:00
obscuren
3ddaf56afd Bumped 2014-05-21 14:04:11 +02:00
obscuren
10e2c40b59 Improved on some ui elements 2014-05-21 14:00:54 +02:00
obscuren
68f4a12a8b Fixed unconfirmed balance string 2014-05-21 13:37:46 +02:00
Maran
34008da807 Merge branch 'develop' of github.com:ethereum/go-ethereum into develop 2014-05-21 12:14:55 +02:00
obscuren
16bd88c10a Removed method name 2014-05-21 12:14:39 +02:00
Maran
0bccf1c3cd Wait with mining until up to date 2014-05-21 11:45:19 +02:00
obscuren
d16d56d39f Updated readme to reflect options 2014-05-20 22:13:39 +02:00
obscuren
93e12250c7 Switch variables as intended 2014-05-20 22:12:42 +02:00
obscuren
f4551a7e9f Changed flag parsing 2014-05-20 22:12:22 +02:00
obscuren
563c035eb5 Refactored some of the functions 2014-05-20 19:28:48 +02:00
obscuren
de1dfae717 Forked version of otto so we can support lowerCased methods 2014-05-20 17:49:12 +02:00
obscuren
4198969302 Merge branch 'release/poc5-rc7' into develop 2014-05-20 17:09:36 +02:00
obscuren
c07c454935 Merge branch 'release/poc5-rc7' 2014-05-20 17:09:26 +02:00
obscuren
34014c1c51 Bump 2014-05-20 17:08:23 +02:00
Maran
0cf617ef0c Implemented GUI watchers for rapid reload. Implements #46 2014-05-20 16:58:13 +02:00
obscuren
0ef7f63729 Removed old console in favor of the new JS REPL 2014-05-20 12:57:43 +02:00
obscuren
a05adb1128 Refactored file structure 2014-05-20 12:48:34 +02:00
Maran
dfc3cb441b Increase default peer amount to 10 2014-05-20 11:52:36 +02:00
obscuren
92eaa98e83 Added js interpret mode 2014-05-19 17:01:40 +02:00
obscuren
017bbbb582 Improved REPL output 2014-05-19 16:32:45 +02:00
obscuren
16421106d4 Added multi-line support 2014-05-19 13:04:31 +02:00
obscuren
3b7707c3fd Improved console
* Added watch
2014-05-19 12:15:03 +02:00
obscuren
30842eb8d0 Changed logging 2014-05-19 12:14:47 +02:00
obscuren
43f88b2bbb Removed nonce incrementing 2014-05-19 12:14:32 +02:00
obscuren
770808ce0d Readline repl for linux & osx 2014-05-17 15:15:46 +02:00
obscuren
2ac292dc7a Merge branch 'feature/otto' into develop 2014-05-15 22:17:23 +02:00
obscuren
6a78e080e6 Tell config which loggers to use 2014-05-15 22:17:09 +02:00
obscuren
0a03484188 Implemented JavaScript console 2014-05-15 22:15:14 +02:00
obscuren
cbce882f5e Basic javascript console 2014-05-15 20:45:19 +02:00
obscuren
3a2bddc160 Refactored to reactor. Fixes #42 2014-05-15 14:06:06 +02:00
obscuren
9ba3c6d1af Merge branch 'release/poc5-rc6' into develop 2014-05-14 21:34:37 +02:00
obscuren
942f552c62 Merge branch 'release/poc5-rc6' 2014-05-14 21:34:21 +02:00
obscuren
a73ae8727d Bumped version 2014-05-14 21:34:01 +02:00
obscuren
278ee3f16c Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-05-14 14:57:16 +02:00
obscuren
9a057021c3 Update wallet value for coinbase rewards. Implements #44 & #43 2014-05-14 14:57:05 +02:00
Maran
a1dcc5cd17 Prevent crash during import of privkeys.
@obscuren please check if this was commented out for a reason
2014-05-14 14:11:45 +02:00
Maran
c9db87277b Fix merge conflicts 2014-05-14 14:04:43 +02:00
Maran
2c7b625daa Make sure we have a coinbase address to mine with 2014-05-14 13:55:55 +02:00
obscuren
f18ec51cb3 Switched to new keyring methods 2014-05-14 13:55:08 +02:00
Maran
9fce273ce9 Refactored RPC client to utils so it can be reused 2014-05-14 13:32:49 +02:00
Maran
2012e0c67a Rewritten a check to only start mining once we are caught up with all peers 2014-05-14 13:26:15 +02:00
Maran
e8147cf7c6 Refactored mining into utils and exposed it to ethereal. Partly fixes #43 2014-05-14 12:41:30 +02:00
Maran
0d9c948b9b Generate coinbase from privatekey, not pubkey. Partily fixes #43 2014-05-14 12:24:49 +02:00
obscuren
faa3073625 Changed validators to regexp validators
IntValidator limits to 32bit int (JavaScript limitation) and therefor
the input fields are limited in length.
2014-05-13 22:25:05 +02:00
obscuren
809b4ae0f6 Merge branch 'develop' 2014-05-13 16:39:32 +02:00
obscuren
7f94b266c7 Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-05-13 16:39:23 +02:00
obscuren
1adfc272a8 Merge branch 'release/poc5-rc4' 2014-05-13 16:37:47 +02:00
obscuren
9caf53f8c6 Bumped 2014-05-13 16:37:15 +02:00
Maran
fca36cc03d Typo 2014-05-13 16:36:29 +02:00
Maran
b71094b01c Removed harcoded addresses from GetBlock JS bindings. Fixes #39 2014-05-13 16:32:35 +02:00
Maran
edc281ac5f Depcrecated set 2014-05-13 14:53:46 +02:00
Maran
ee2cef3b2e Adding new API test case html 2014-05-13 14:49:21 +02:00
Maran
54eff2d778 Change coinbase to be the address not public key 2014-05-13 14:48:45 +02:00
Maran
20ea78945e Implemented new JS/EthPub methods
- getTxCountAt
- getPeerCount
- getIsMining
- getIsListening
- getCoinbase
2014-05-13 14:43:08 +02:00
obscuren
dd60382fc3 Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop
Conflicts:
	ethereal/ethereum.go
	ethereum/ethereum.go
2014-05-13 12:46:40 +02:00
obscuren
32c6126593 Fix 2014-05-13 12:45:47 +02:00
obscuren
03371b74d7 Public ethereum interface uses EthManager 2014-05-13 12:42:01 +02:00
Maran
c99aa7bdcf Fix merge conflict 2014-05-13 12:40:45 +02:00
Maran
5208b04821 Merge branch 'develop' of github.com:ethereum/go-ethereum into develop 2014-05-13 12:08:54 +02:00
Maran
9a03df7bd8 Implemented a flag for a different RPC port; --rpcport 2014-05-13 12:00:48 +02:00
obscuren
67820506cb Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-05-13 11:59:13 +02:00
obscuren
8c9e6746ce Fixed wallet crash for new account. Fixes #38 2014-05-13 11:59:03 +02:00
Maran
b9876df5dc Added support to NewJsonRpc to return an error as well as an interface 2014-05-13 11:50:39 +02:00
Maran
a5963d1377 Enable seed again 2014-05-13 11:34:47 +02:00
Maran
c3ad210846 Merge branch 'develop' of github.com:ethereum/go-ethereum into develop 2014-05-12 17:23:16 +02:00
Maran
618f523124 Actually start the Ethereum server on starting the GUI 2014-05-12 17:23:14 +02:00
obscuren
c7132e5d22 Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-05-12 13:56:37 +02:00
obscuren
c43ea30e75 Refactored some code and fixed #37 2014-05-12 13:56:29 +02:00
Maran
cf7ab07264 Disable seed by default
Seed host seems down, only causes timeouts, not helpful
2014-05-12 13:41:52 +02:00
obscuren
5d15563ea7 PreProcess => PreParse 2014-05-12 12:22:16 +02:00
obscuren
a3cc4b0b80 Merge branch 'release/poc5-rc3' into develop 2014-05-10 16:25:08 +02:00
obscuren
721d3a9a57 Merge branch 'release/poc5-rc3' 2014-05-10 16:25:01 +02:00
obscuren
23fc50c61b Upgraded to new mutan 2014-05-10 16:22:57 +02:00
obscuren
faa3aa14ed Merge branch 'release/poc5-rc2' into develop 2014-05-10 02:03:30 +02:00
obscuren
b0023f66b0 Merge branch 'release/poc5-rc2' 2014-05-10 02:03:22 +02:00
obscuren
109395daaa Bump 2014-05-10 02:02:59 +02:00
obscuren
f73a5f067a fxed 2014-05-10 02:00:18 +02:00
obscuren
1471585af0 Moved Ext app js to its own dir 2014-05-10 01:57:10 +02:00
obscuren
0bf2d24cb0 Changed seeding 2014-05-09 14:51:02 +02:00
obscuren
f59f515def Cleanup 2014-05-08 18:24:28 +02:00
obscuren
71defc11fa Fixed closure 2014-05-08 14:20:17 +02:00
obscuren
53eb59ed81 Normalized font 2014-05-05 20:56:48 +02:00
obscuren
fb903619e8 Merge branch 'release/poc5-rc1' into develop 2014-05-05 15:56:11 +02:00
34 changed files with 3109 additions and 1573 deletions

View File

@@ -5,7 +5,7 @@ Ethereum
Ethereum Go Client © 2014 Jeffrey Wilcke. Ethereum Go Client © 2014 Jeffrey Wilcke.
Current state: Proof of Concept 5.0 RC1. Current state: Proof of Concept 5.0 RC11.
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go). For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
@@ -26,28 +26,25 @@ General command line options
==================== ====================
``` ```
-c Launch the developer console (node only) Shared between ethereum and ethereal
-m Start mining blocks -m Start mining blocks
-genaddr Generates a new address and private key (destructive action) -genaddr Generates a new address and private key (destructive action)
-p Port on which the server will accept incomming connections (= 30303) -p Port on which the server will accept incomming connections
-upnp Enable UPnP (= false) -upnp Enable UPnP
-x Desired amount of peers (= 5) -x Desired amount of peers
-h This help
-r Start JSON RPC -r Start JSON RPC
-dir Data directory used to store configs and databases (=".ethereum") -dir Data directory used to store configs and databases
-import Import a private key (hex) -import Import a private key
``` -h This
Developer console commands Ethereum only
========================== ethereum [options] [filename]
-js Start the JavaScript REPL
filename Load the given file and interpret as JavaScript
Etheral only
-asset_path absolute path to GUI assets directory
``` ```
addp <host>:<port> Connect to the given host
tx <addr> <amount> Send <amount> Wei to the specified <addr>
contract <value> <gasprice> Creates a new contract and launches the editor
```
See the "help" command for *developer* options.
Contribution Contribution
============ ============

View File

@@ -0,0 +1,240 @@
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
ApplicationWindow {
visible: false
title: "IceCream"
minimumWidth: 1280
minimumHeight: 900
width: 1290
height: 900
property alias codeText: codeEditor.text
property alias dataText: rawDataField.text
MenuBar {
Menu {
title: "Debugger"
MenuItem {
text: "Run"
shortcut: "Ctrl+r"
onTriggered: debugCurrent()
}
MenuItem {
text: "Next"
shortcut: "Ctrl+n"
onTriggered: dbg.next()
}
}
}
SplitView {
anchors.fill: parent
property var asmModel: ListModel {
id: asmModel
}
TableView {
id: asmTableView
width: 200
TableViewColumn{ role: "value" ; title: "" ; width: 100 }
model: asmModel
}
Rectangle {
color: "#00000000"
anchors.left: asmTableView.right
anchors.right: parent.right
SplitView {
orientation: Qt.Vertical
anchors.fill: parent
Rectangle {
color: "#00000000"
height: 500
anchors.left: parent.left
anchors.right: parent.right
TextArea {
id: codeEditor
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: settings.left
}
Column {
id: settings
spacing: 5
width: 300
height: parent.height
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
Label {
text: "Arbitrary data"
}
TextArea {
id: rawDataField
anchors.left: parent.left
anchors.right: parent.right
height: 150
}
Label {
text: "Amount"
}
TextField {
id: txValue
width: 200
placeholderText: "Amount"
validator: RegExpValidator { regExp: /\d*/ }
}
Label {
text: "Amount of gas"
}
TextField {
id: txGas
width: 200
validator: RegExpValidator { regExp: /\d*/ }
text: "10000"
placeholderText: "Gas"
}
Label {
text: "Gas price"
}
TextField {
id: txGasPrice
width: 200
placeholderText: "Gas price"
text: "1000000000000"
validator: RegExpValidator { regExp: /\d*/ }
}
}
}
SplitView {
orientation: Qt.Vertical
id: inspectorPane
height: 500
SplitView {
orientation: Qt.Horizontal
height: 250
TableView {
id: stackTableView
property var stackModel: ListModel {
id: stackModel
}
height: parent.height
width: 300
TableViewColumn{ role: "value" ; title: "Stack" ; width: 200 }
model: stackModel
}
TableView {
id: memoryTableView
property var memModel: ListModel {
id: memModel
}
height: parent.height
width: parent.width - stackTableView.width
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50}
TableViewColumn{ role: "value" ; title: "Memory" ; width: 750}
model: memModel
}
}
SplitView {
height: 300
TableView {
id: storageTableView
property var memModel: ListModel {
id: storageModel
}
height: parent.height
width: parent.width - stackTableView.width
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2}
TableViewColumn{ role: "value" ; title: "value" ; width: storageTableView.width / 2}
model: storageModel
}
}
}
}
}
}
toolBar: ToolBar {
RowLayout {
spacing: 5
Button {
property var enabled: true
id: debugStart
onClicked: {
debugCurrent()
}
text: "Debug"
}
Button {
property var enabled: true
id: debugNextButton
onClicked: {
dbg.next()
}
text: "Next"
}
}
}
function debugCurrent() {
dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text)
}
function setAsm(asm) {
asmModel.append({asm: asm})
}
function clearAsm() {
asmModel.clear()
}
function setInstruction(num) {
asmTableView.selection.clear()
asmTableView.selection.select(num)
}
function setMem(mem) {
memModel.append({num: mem.num, value: mem.value})
}
function clearMem(){
memModel.clear()
}
function setStack(stack) {
stackModel.append({value: stack})
}
function addDebugMessage(message){
debuggerLog.append({value: message})
}
function clearStack() {
stackModel.clear()
}
function clearStorage() {
storageModel.clear()
}
function setStorage(storage) {
storageModel.append({key: storage.key, value: storage.value})
}
}

380
ethereal/assets/ext/big.js Normal file
View File

@@ -0,0 +1,380 @@
var bigInt = (function () {
var base = 10000000, logBase = 7;
var sign = {
positive: false,
negative: true
};
var normalize = function (first, second) {
var a = first.value, b = second.value;
var length = a.length > b.length ? a.length : b.length;
for (var i = 0; i < length; i++) {
a[i] = a[i] || 0;
b[i] = b[i] || 0;
}
for (var i = length - 1; i >= 0; i--) {
if (a[i] === 0 && b[i] === 0) {
a.pop();
b.pop();
} else break;
}
if (!a.length) a = [0], b = [0];
first.value = a;
second.value = b;
};
var parse = function (text, first) {
if (typeof text === "object") return text;
text += "";
var s = sign.positive, value = [];
if (text[0] === "-") {
s = sign.negative;
text = text.slice(1);
}
var base = 10;
if (text.slice(0, 2) == "0x") {
base = 16;
text = text.slice(2);
}
else {
var texts = text.split("e");
if (texts.length > 2) throw new Error("Invalid integer");
if (texts[1]) {
var exp = texts[1];
if (exp[0] === "+") exp = exp.slice(1);
exp = parse(exp);
if (exp.lesser(0)) throw new Error("Cannot include negative exponent part for integers");
while (exp.notEquals(0)) {
texts[0] += "0";
exp = exp.prev();
}
}
text = texts[0];
}
if (text === "-0") text = "0";
text = text.toUpperCase();
var isValid = (base == 16 ? /^[0-9A-F]*$/ : /^[0-9]+$/).test(text);
if (!isValid) throw new Error("Invalid integer");
if (base == 16) {
var val = bigInt(0);
while (text.length) {
v = text.charCodeAt(0) - 48;
if (v > 9)
v -= 7;
text = text.slice(1);
val = val.times(16).plus(v);
}
return val;
}
else {
while (text.length) {
var divider = text.length > logBase ? text.length - logBase : 0;
value.push(+text.slice(divider));
text = text.slice(0, divider);
}
var val = bigInt(value, s);
if (first) normalize(first, val);
return val;
}
};
var goesInto = function (a, b) {
var a = bigInt(a, sign.positive), b = bigInt(b, sign.positive);
if (a.equals(0)) throw new Error("Cannot divide by 0");
var n = 0;
do {
var inc = 1;
var c = bigInt(a.value, sign.positive), t = c.times(10);
while (t.lesser(b)) {
c = t;
inc *= 10;
t = t.times(10);
}
while (c.lesserOrEquals(b)) {
b = b.minus(c);
n += inc;
}
} while (a.lesserOrEquals(b));
return {
remainder: b.value,
result: n
};
};
var bigInt = function (value, s) {
var self = {
value: value,
sign: s
};
var o = {
value: value,
sign: s,
negate: function (m) {
var first = m || self;
return bigInt(first.value, !first.sign);
},
abs: function (m) {
var first = m || self;
return bigInt(first.value, sign.positive);
},
add: function (n, m) {
var s, first = self, second;
if (m) (first = parse(n)) && (second = parse(m));
else second = parse(n, first);
s = first.sign;
if (first.sign !== second.sign) {
first = bigInt(first.value, sign.positive);
second = bigInt(second.value, sign.positive);
return s === sign.positive ?
o.subtract(first, second) :
o.subtract(second, first);
}
normalize(first, second);
var a = first.value, b = second.value;
var result = [],
carry = 0;
for (var i = 0; i < a.length || carry > 0; i++) {
var sum = (a[i] || 0) + (b[i] || 0) + carry;
carry = sum >= base ? 1 : 0;
sum -= carry * base;
result.push(sum);
}
return bigInt(result, s);
},
plus: function (n, m) {
return o.add(n, m);
},
subtract: function (n, m) {
var first = self, second;
if (m) (first = parse(n)) && (second = parse(m));
else second = parse(n, first);
if (first.sign !== second.sign) return o.add(first, o.negate(second));
if (first.sign === sign.negative) return o.subtract(o.negate(second), o.negate(first));
if (o.compare(first, second) === -1) return o.negate(o.subtract(second, first));
var a = first.value, b = second.value;
var result = [],
borrow = 0;
for (var i = 0; i < a.length; i++) {
var tmp = a[i] - borrow;
borrow = tmp < b[i] ? 1 : 0;
var minuend = (borrow * base) + tmp - b[i];
result.push(minuend);
}
return bigInt(result, sign.positive);
},
minus: function (n, m) {
return o.subtract(n, m);
},
multiply: function (n, m) {
var s, first = self, second;
if (m) (first = parse(n)) && (second = parse(m));
else second = parse(n, first);
s = first.sign !== second.sign;
var a = first.value, b = second.value;
var resultSum = [];
for (var i = 0; i < a.length; i++) {
resultSum[i] = [];
var j = i;
while (j--) {
resultSum[i].push(0);
}
}
var carry = 0;
for (var i = 0; i < a.length; i++) {
var x = a[i];
for (var j = 0; j < b.length || carry > 0; j++) {
var y = b[j];
var product = y ? (x * y) + carry : carry;
carry = product > base ? Math.floor(product / base) : 0;
product -= carry * base;
resultSum[i].push(product);
}
}
var max = -1;
for (var i = 0; i < resultSum.length; i++) {
var len = resultSum[i].length;
if (len > max) max = len;
}
var result = [], carry = 0;
for (var i = 0; i < max || carry > 0; i++) {
var sum = carry;
for (var j = 0; j < resultSum.length; j++) {
sum += resultSum[j][i] || 0;
}
carry = sum > base ? Math.floor(sum / base) : 0;
sum -= carry * base;
result.push(sum);
}
return bigInt(result, s);
},
times: function (n, m) {
return o.multiply(n, m);
},
divmod: function (n, m) {
var s, first = self, second;
if (m) (first = parse(n)) && (second = parse(m));
else second = parse(n, first);
s = first.sign !== second.sign;
if (bigInt(first.value, first.sign).equals(0)) return {
quotient: bigInt([0], sign.positive),
remainder: bigInt([0], sign.positive)
};
if (second.equals(0)) throw new Error("Cannot divide by zero");
var a = first.value, b = second.value;
var result = [], remainder = [];
for (var i = a.length - 1; i >= 0; i--) {
var n = [a[i]].concat(remainder);
var quotient = goesInto(b, n);
result.push(quotient.result);
remainder = quotient.remainder;
}
result.reverse();
return {
quotient: bigInt(result, s),
remainder: bigInt(remainder, first.sign)
};
},
divide: function (n, m) {
return o.divmod(n, m).quotient;
},
over: function (n, m) {
return o.divide(n, m);
},
mod: function (n, m) {
return o.divmod(n, m).remainder;
},
pow: function (n, m) {
var first = self, second;
if (m) (first = parse(n)) && (second = parse(m));
else second = parse(n, first);
var a = first, b = second;
if (b.lesser(0)) return ZERO;
if (b.equals(0)) return ONE;
var result = bigInt(a.value, a.sign);
if (b.mod(2).equals(0)) {
var c = result.pow(b.over(2));
return c.times(c);
} else {
return result.times(result.pow(b.minus(1)));
}
},
next: function (m) {
var first = m || self;
return o.add(first, 1);
},
prev: function (m) {
var first = m || self;
return o.subtract(first, 1);
},
compare: function (n, m) {
var first = self, second;
if (m) (first = parse(n)) && (second = parse(m, first));
else second = parse(n, first);
normalize(first, second);
if (first.value.length === 1 && second.value.length === 1 && first.value[0] === 0 && second.value[0] === 0) return 0;
if (second.sign !== first.sign) return first.sign === sign.positive ? 1 : -1;
var multiplier = first.sign === sign.positive ? 1 : -1;
var a = first.value, b = second.value;
for (var i = a.length - 1; i >= 0; i--) {
if (a[i] > b[i]) return 1 * multiplier;
if (b[i] > a[i]) return -1 * multiplier;
}
return 0;
},
compareAbs: function (n, m) {
var first = self, second;
if (m) (first = parse(n)) && (second = parse(m, first));
else second = parse(n, first);
first.sign = second.sign = sign.positive;
return o.compare(first, second);
},
equals: function (n, m) {
return o.compare(n, m) === 0;
},
notEquals: function (n, m) {
return !o.equals(n, m);
},
lesser: function (n, m) {
return o.compare(n, m) < 0;
},
greater: function (n, m) {
return o.compare(n, m) > 0;
},
greaterOrEquals: function (n, m) {
return o.compare(n, m) >= 0;
},
lesserOrEquals: function (n, m) {
return o.compare(n, m) <= 0;
},
isPositive: function (m) {
var first = m || self;
return first.sign === sign.positive;
},
isNegative: function (m) {
var first = m || self;
return first.sign === sign.negative;
},
isEven: function (m) {
var first = m || self;
return first.value[0] % 2 === 0;
},
isOdd: function (m) {
var first = m || self;
return first.value[0] % 2 === 1;
},
toString: function (m) {
var first = m || self;
var str = "", len = first.value.length;
while (len--) {
if (first.value[len].toString().length === 8) str += first.value[len];
else str += (base.toString() + first.value[len]).slice(-logBase);
}
while (str[0] === "0") {
str = str.slice(1);
}
if (!str.length) str = "0";
var s = (first.sign === sign.positive || str == "0") ? "" : "-";
return s + str;
},
toHex: function (m) {
var first = m || self;
var str = "";
var l = this.abs();
while (l > 0) {
var qr = l.divmod(256);
var b = qr.remainder.toJSNumber();
str = (b >> 4).toString(16) + (b & 15).toString(16) + str;
l = qr.quotient;
}
return (this.isNegative() ? "-" : "") + "0x" + str;
},
toJSNumber: function (m) {
return +o.toString(m);
},
valueOf: function (m) {
return o.toJSNumber(m);
}
};
return o;
};
var ZERO = bigInt([0], sign.positive);
var ONE = bigInt([1], sign.positive);
var MINUS_ONE = bigInt([1], sign.negative);
var fnReturn = function (a) {
if (typeof a === "undefined") return ZERO;
return parse(a);
};
fnReturn.zero = ZERO;
fnReturn.one = ONE;
fnReturn.minusOne = MINUS_ONE;
return fnReturn;
})();
if (typeof module !== "undefined") {
module.exports = bigInt;
}

View File

@@ -36,6 +36,21 @@ window.eth = {
postData({call: "getKey"}, cb); postData({call: "getKey"}, cb);
}, },
getTxCountAt: function(address, cb) {
postData({call: "getTxCountAt", args: [address]}, cb);
},
getIsMining: function(cb){
postData({call: "getIsMining"}, cb)
},
getIsListening: function(cb){
postData({call: "getIsListening"}, cb)
},
getCoinBase: function(cb){
postData({call: "getCoinBase"}, cb);
},
getPeerCount: function(cb){
postData({call: "getPeerCount"}, cb);
},
getBalanceAt: function(address, cb) { getBalanceAt: function(address, cb) {
postData({call: "getBalance", args: [address]}, cb); postData({call: "getBalance", args: [address]}, cb);
}, },
@@ -115,122 +130,9 @@ window.eth = {
} }
} }
}, },
} }
window.eth._callbacks = {} window.eth._callbacks = {}
window.eth._onCallbacks = {} window.eth._onCallbacks = {}
function hello() {
debug("hello")
window.dataTest = true;
}
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)
if(cb) {
eth._callbacks[data._seed] = cb;
}
if(data.args === undefined) {
data.args = [];
}
navigator.qt.postMessage(JSON.stringify(data));
}
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
// Figure out whether the returned data was an array
// array means multiple return arguments (multiple params)
if(data.data instanceof Array) {
cb.apply(this, data.data)
} else {
cb.call(this, data.data)
}
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
}
window.eth._0 = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
String.prototype.pad = function(len) {
var bin = this.bin();
var l = bin.length;
if(l < 32) {
return eth._0.substr(0, 32 - bin.length) + bin;
}
return bin;
}
String.prototype.unpad = function() {
var i, l;
for(i = 0, l = this.length; i < l; i++) {
if(this[i] != "\0") {
return this.substr(i, this.length);
}
}
return this.substr(i, this.length);
}
String.prototype.bin = function() {
if(this.substr(0, 2) == "0x") {
return this.hex2bin();
} else if(/^\d+$/.test(this)) {
return this.num2bin()
}
// Otherwise we'll return the "String" object instead of an actual string
return this.substr(0, this.length)
}
String.prototype.unbin = function() {
var i, l, o = '';
for(i = 0, l = this.length; i < l; i++) {
var n = this.charCodeAt(i).toString(16);
o += n.length < 2 ? '0' + n : n;
}
return "0x" + o;
}
String.prototype.hex2bin = function() {
bytes = []
for(var i=2; i< this.length-1; i+=2) {
bytes.push(parseInt(this.substr(i, 2), 16));
}
return String.fromCharCode.apply(String, bytes);
}
String.prototype.num2bin = function() {
return ("0x"+parseInt(this).toString(16)).bin()
}

View File

@@ -0,0 +1,58 @@
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)
if(cb) {
eth._callbacks[data._seed] = cb;
}
if(data.args === undefined) {
data.args = [];
}
navigator.qt.postMessage(JSON.stringify(data));
}
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
// Figure out whether the returned data was an array
// array means multiple return arguments (multiple params)
if(data.data instanceof Array) {
cb.apply(this, data.data)
} else {
cb.call(this, data.data)
}
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
}
window.onerror = function(message, file, lineNumber, column, errorObj) {
debug(file, message, lineNumber+":"+column, errorObj);
return false;
}

View File

@@ -0,0 +1,58 @@
String.prototype.pad = function(l, r) {
if (r === undefined) {
r = l
if (!(this.substr(0, 2) == "0x" || /^\d+$/.test(this)))
l = 0
}
var ret = this.bin();
while (ret.length < l)
ret = "\0" + ret
while (ret.length < r)
ret = ret + "\0"
return ret;
}
String.prototype.unpad = function() {
var i = this.length;
while (i && this[i - 1] == "\0")
--i
return this.substr(0, i)
}
String.prototype.bin = function() {
if (this.substr(0, 2) == "0x") {
bytes = []
var i = 2;
// Check if it's odd - pad with a zero if so.
if (this.length % 2)
bytes.push(parseInt(this.substr(i++, 1), 16))
for (; i < this.length - 1; i += 2)
bytes.push(parseInt(this.substr(i, 2), 16));
return String.fromCharCode.apply(String, bytes);
} else if (/^\d+$/.test(this))
return bigInt(this.substr(0)).toHex().bin()
// Otherwise we'll return the "String" object instead of an actual string
return this.substr(0, this.length)
}
String.prototype.unbin = function() {
var i, l, o = '';
for(i = 0, l = this.length; i < l; i++) {
var n = this.charCodeAt(i).toString(16);
o += n.length < 2 ? '0' + n : n;
}
return "0x" + o;
}
String.prototype.dec = function() {
return bigInt(this.substr(0)).toString()
}
String.prototype.hex = function() {
return bigInt(this.substr(0)).toHex()
}

BIN
ethereal/assets/heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -61,7 +61,7 @@ ApplicationWindow {
text: "Restore" text: "Restore"
enabled: false enabled: false
onClicked: { onClicked: {
var success = eth.importAndSetPrivKey(txPrivKey.text) var success = lib.importAndSetPrivKey(txPrivKey.text)
if(success){ if(success){
importedDetails.visible = true importedDetails.visible = true
restoreColumn.visible = false restoreColumn.visible = false
@@ -139,7 +139,7 @@ ApplicationWindow {
id: newKey id: newKey
text: "I don't have an account yet" text: "I don't have an account yet"
onClicked: { onClicked: {
var res = eth.createAndSetPrivKey() var res = lib.createAndSetPrivKey()
mnemonicInput.text = res[0] mnemonicInput.text = res[0]
addressInput.text = res[1] addressInput.text = res[1]
privkeyInput.text = res[2] privkeyInput.text = res[2]

View File

@@ -1,196 +0,0 @@
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
Component {
id: newContract
Column {
id: mainContractColumn
function contractFormReady(){
if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) {
txButton.state = "READY"
}else{
txButton.state = "NOTREADY"
}
}
states: [
State{
name: "ERROR"
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: 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; text: ""}
PropertyChanges { target: txGasPrice; visible:true; text: ""}
PropertyChanges { target: codeView; visible:true; text: ""}
PropertyChanges { target: txButton; visible:true}
PropertyChanges { target: txDataLabel; 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
TextField {
id: txValue
width: 200
placeholderText: "Amount"
validator: IntValidator { }
onTextChanged: {
contractFormReady()
}
}
TextField {
id: txGas
width: 200
validator: IntValidator { }
placeholderText: "Gas"
onTextChanged: {
contractFormReady()
}
}
TextField {
id: txGasPrice
width: 200
placeholderText: "Gas price"
validator: IntValidator { }
onTextChanged: {
contractFormReady()
}
}
Row {
id: rowContract
ExclusiveGroup { id: contractTypeGroup }
RadioButton {
id: createContractRadio
text: "Create contract"
checked: true
exclusiveGroup: contractTypeGroup
onClicked: {
txFuelRecipient.visible = false
txDataLabel.text = "Contract code"
}
}
RadioButton {
id: runContractRadio
text: "Run contract"
exclusiveGroup: contractTypeGroup
onClicked: {
txFuelRecipient.visible = true
txDataLabel.text = "Contract arguments"
}
}
}
Label {
id: txDataLabel
text: "Contract code"
}
TextArea {
id: codeView
height: 300
anchors.topMargin: 5
Layout.fillWidth: true
width: parent.width /2
onTextChanged: {
contractFormReady()
}
}
TextField {
id: txFuelRecipient
placeholderText: "Contract address"
validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
visible: false
width: 530
}
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: {
//this.enabled = false
var res = eth.createTx(txFuelRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text)
if(res[1]) {
txResult.text = "Your contract <b>could not</b> be send 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]
mainContractColumn.state = "DONE"
}
}
}
Text {
id: txResult
visible: false
}
TextField {
id: txOutput
visible: false
width: 530
}
Button {
id: newTxButton
visible: false
text: "Create an other contract"
onClicked: {
this.visible = false
txResult.text = ""
txOutput.text = ""
mainContractColumn.state = "SETUP"
}
}
Button {
id: debugButton
text: "Debug"
onClicked: {
var res = ui.debugTx("", txValue.text, txGas.text, txGasPrice.text, codeView.text)
debugWindow.visible = true
}
}
}
}

View File

@@ -1,111 +0,0 @@
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
Component {
id: newTransaction
Column {
id: simpleSendColumn
states: [
State{
name: "ERROR"
},
State {
name: "DONE"
PropertyChanges { target: txSimpleValue; visible:false}
PropertyChanges { target: txSimpleRecipient; visible:false}
PropertyChanges { target:newSimpleTxButton; visible:false}
PropertyChanges { target: txSimpleResult; visible:true}
PropertyChanges { target: txSimpleOutput; visible:true}
PropertyChanges { target:newSimpleTxButton; visible:true}
},
State {
name: "SETUP"
PropertyChanges { target: txSimpleValue; visible:true; text: ""}
PropertyChanges { target: txSimpleRecipient; visible:true; text: ""}
PropertyChanges { target: txSimpleButton; visible:true}
PropertyChanges { target:newSimpleTxButton; visible:false}
}
]
spacing: 5
anchors.leftMargin: 5
anchors.topMargin: 5
anchors.top: parent.top
anchors.left: parent.left
function checkFormState(){
if(txSimpleRecipient.text.length == 40 && txSimpleValue.text.length > 0) {
txSimpleButton.state = "READY"
}else{
txSimpleButton.state = "NOTREADY"
}
}
TextField {
id: txSimpleRecipient
placeholderText: "Recipient address"
Layout.fillWidth: true
validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
width: 530
onTextChanged: { checkFormState() }
}
TextField {
id: txSimpleValue
placeholderText: "Amount"
anchors.rightMargin: 5
validator: IntValidator { }
onTextChanged: { checkFormState() }
}
Button {
id: txSimpleButton
/*enabled: false*/
states: [
State {
name: "READY"
PropertyChanges { target: txSimpleButton; /*enabled: true*/}
},
State {
name: "NOTREADY"
PropertyChanges { target: txSimpleButton; /*enabled: false*/}
}
]
text: "Send"
onClicked: {
//this.enabled = false
var res = eth.createTx(txSimpleRecipient.text, txSimpleValue.text,"","","")
if(res[1]) {
txSimpleResult.text = "There has been an error broadcasting your transaction:" + res[1].error()
} else {
txSimpleResult.text = "Your transaction has been broadcasted over the network.\nYour transaction id is:"
txSimpleOutput.text = res[0]
this.visible = false
simpleSendColumn.state = "DONE"
}
}
}
Text {
id: txSimpleResult
visible: false
}
TextField {
id: txSimpleOutput
visible: false
width: 530
}
Button {
id: newSimpleTxButton
visible: false
text: "Create an other transaction"
onClicked: {
this.visible = false
simpleSendColumn.state = "SETUP"
}
}
}
}

View File

@@ -6,6 +6,7 @@ import QtQuick.Window 2.1;
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import Ethereum 1.0 import Ethereum 1.0
ApplicationWindow { ApplicationWindow {
id: root id: root
@@ -23,11 +24,21 @@ ApplicationWindow {
shortcut: "Ctrl+o" shortcut: "Ctrl+o"
onTriggered: openAppDialog.open() onTriggered: openAppDialog.open()
} }
}
Menu {
title: "Tools"
MenuItem { MenuItem {
text: "Muted" text: "Muted"
shortcut: "Ctrl+e" shortcut: "Ctrl+e"
onTriggered: ui.muted("") onTriggered: ui.muted("")
} }
MenuItem {
text: "Debugger"
shortcut: "Ctrl+d"
onTriggered: ui.startDebugger()
}
} }
Menu { Menu {
@@ -67,6 +78,7 @@ ApplicationWindow {
networkView.visible = false networkView.visible = false
historyView.visible = false historyView.visible = false
newTxView.visible = false newTxView.visible = false
infoView.visible = false
view.visible = true view.visible = true
//root.title = "Ethereal - " = view.title //root.title = "Ethereal - " = view.title
} }
@@ -84,7 +96,6 @@ ApplicationWindow {
//color: "#D9DDE7" //color: "#D9DDE7"
color: "#252525" color: "#252525"
ColumnLayout { ColumnLayout {
y: 50 y: 50
anchors.left: parent.left anchors.left: parent.left
@@ -120,6 +131,17 @@ ApplicationWindow {
} }
} }
} }
Image {
source: ui.assetPath("heart.png")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onClicked: {
setView(infoView)
}
}
}
} }
} }
@@ -143,6 +165,7 @@ ApplicationWindow {
TableView { TableView {
id: txTableView id: txTableView
anchors.fill: parent anchors.fill: parent
TableViewColumn{ role: "inout" ; title: "" ; width: 40 }
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 } TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 } TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
@@ -165,12 +188,6 @@ ApplicationWindow {
anchors.bottomMargin: 5 anchors.bottomMargin: 5
id: newTransactionTab id: newTransactionTab
Component.onCompleted:{ Component.onCompleted:{
var component = Qt.createComponent("newTransaction/_simple_send.qml")
var newTransaction = component.createObject("newTransaction")
component = Qt.createComponent("newTransaction/_new_contract.qml")
var newContract = component.createObject("newContract")
addTab("Simple send", newTransaction) addTab("Simple send", newTransaction)
addTab("Contracts", newContract) addTab("Contracts", newContract)
} }
@@ -190,16 +207,14 @@ ApplicationWindow {
anchors.bottom: logView.top anchors.bottom: logView.top
TableViewColumn{ role: "number" ; title: "#" ; width: 100 } TableViewColumn{ role: "number" ; title: "#" ; width: 100 }
TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 } TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 }
TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 }
model: blockModel model: blockModel
/*
onDoubleClicked: { onDoubleClicked: {
popup.visible = true popup.visible = true
popup.block = eth.getBlock(blockModel.get(row).hash) popup.setDetails(blockModel.get(row))
popup.hashLabel.text = popup.block.hash
} }
*/
} }
property var logModel: ListModel { property var logModel: ListModel {
@@ -217,6 +232,33 @@ ApplicationWindow {
} }
} }
Rectangle {
id: infoView
property var title: "Information"
visible: false
color: "#00000000"
anchors.fill: parent
Label {
id: addressLabel
text: "Address"
anchors {
margins: 5
top: parent.top
left: parent.left
}
}
TextField {
anchors {
margins: 5
left: addressLabel.right
top: parent.top
}
text: pub.getKey().address
width: 500
}
}
/* /*
signal addPlugin(string name) signal addPlugin(string name)
Component { Component {
@@ -248,27 +290,25 @@ ApplicationWindow {
//ui.open(openAppDialog.fileUrl.toString()) //ui.open(openAppDialog.fileUrl.toString())
//ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html"))) //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html")))
ui.openHtml(openAppDialog.fileUrl.toString()) ui.openHtml(openAppDialog.fileUrl.toString())
} }
} }
statusBar: StatusBar { statusBar: StatusBar {
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
Button { Button {
property var enabled: true property var enabled: true
id: connectButton id: debuggerWindow
onClicked: { onClicked: {
if(this.enabled) { ui.startDebugger()
ui.connect(this)
} }
} text: "Debugger"
text: "Connect"
} }
Button { Button {
id: importAppButton id: importAppButton
anchors.left: connectButton.right anchors.left: debuggerWindow.right
anchors.leftMargin: 5 anchors.leftMargin: 5
onClicked: openAppDialog.open() onClicked: openAppDialog.open()
text: "Import App" text: "Import App"
@@ -300,10 +340,132 @@ ApplicationWindow {
id: popup id: popup
visible: false visible: false
property var block property var block
width: root.width
height: 240
Component{
id: blockDetailsDelegate
Rectangle {
color: "#252525"
width: popup.width
height: 200
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>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
}
}
}
ListView {
model: singleBlock
delegate: blockDetailsDelegate
anchors.top: parent.top
height: 70
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 = 230
}
}
}
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 = 400
}
Rectangle {
id: txDetails
width: popup.width
height: 300
anchors.left: listViewThing.left
anchors.top: txView.bottom
Label { Label {
id: hashLabel text: "<h4>Contract data</h4>"
anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top
anchors.verticalCenter: parent.verticalCenter 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){
ui.startDbWithCode(tx.rawData)
}else {
ui.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 = 230
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()
} }
} }
@@ -365,12 +527,12 @@ ApplicationWindow {
anchors.left: aboutIcon.right anchors.left: aboutIcon.right
anchors.leftMargin: 10 anchors.leftMargin: 10
font.pointSize: 12 font.pointSize: 12
text: "<h2>Ethereal</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br><h3>Binary Distribution</h3>Jarrad Hope<br>" text: "<h2>Ethereal</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>"
} }
} }
Window { ApplicationWindow {
id: debugWindow id: debugWindow
visible: false visible: false
title: "Debugger" title: "Debugger"
@@ -390,7 +552,6 @@ ApplicationWindow {
} }
} }
SplitView { SplitView {
anchors.fill: parent anchors.fill: parent
property var asmModel: ListModel { property var asmModel: ListModel {
id: asmModel id: asmModel
@@ -422,6 +583,7 @@ ApplicationWindow {
SplitView { SplitView {
orientation: Qt.Horizontal orientation: Qt.Horizontal
id: debugSplitView
TableView { TableView {
property var debuggerLog: ListModel { property var debuggerLog: ListModel {
id: debuggerLog id: debuggerLog
@@ -435,13 +597,26 @@ ApplicationWindow {
} }
height: parent.height/2 height: parent.height/2
width: parent.width width: parent.width
TableViewColumn{ role: "value" ; title: "Stack" ; width: parent.width } TableViewColumn{ role: "value" ; title: "Stack" ; width: debugSplitView.width }
model: stackModel model: stackModel
} }
} }
} }
} }
} }
statusBar: StatusBar {
RowLayout {
anchors.fill: parent
Button {
property var enabled: true
id: debugNextButton
onClicked: {
ui.next()
}
text: "Next"
}
}
}
} }
function setAsm(asm) { function setAsm(asm) {
@@ -485,18 +660,32 @@ ApplicationWindow {
walletValueLabel.text = value walletValueLabel.text = value
} }
function addTx(tx) { function addTx(tx, inout) {
var isContract var isContract
if (tx.contract == true){ if (tx.contract == true){
isContract = "Yes" isContract = "Yes"
}else{ }else{
isContract = "No" isContract = "No"
} }
txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value, contract: isContract}) txModel.insert(0, {inout: inout, hash: tx.hash, address: tx.address, value: tx.value, contract: isContract})
} }
function addBlock(block) { function addBlock(block, initial) {
blockModel.insert(0, {number: block.number, hash: block.hash}) 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, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
}else{
blockModel.insert(0, {number: block.number, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
}
} }
function addLog(str) { function addLog(str) {
@@ -508,4 +697,316 @@ ApplicationWindow {
function setPeers(text) { function setPeers(text) {
peerLabel.text = text peerLabel.text = text
} }
function convertToPretty(unixTs){
var a = new Date(unixTs*1000);
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var year = a.getFullYear();
var month = months[a.getMonth()];
var date = a.getDate();
var hour = a.getHours();
var min = a.getMinutes();
var sec = a.getSeconds();
var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ;
return time;
}
// *******************************************
// Components
// *******************************************
// New Contract component
Component {
id: newContract
Column {
id: mainContractColumn
function contractFormReady(){
if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) {
txButton.state = "READY"
}else{
txButton.state = "NOTREADY"
}
}
states: [
State{
name: "ERROR"
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: 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; text: ""}
PropertyChanges { target: txGasPrice; visible:true; text: ""}
PropertyChanges { target: codeView; visible:true; text: ""}
PropertyChanges { target: txButton; visible:true}
PropertyChanges { target: txDataLabel; 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
TextField {
id: txValue
width: 200
placeholderText: "Amount"
validator: RegExpValidator { regExp: /\d*/ }
onTextChanged: {
contractFormReady()
}
}
TextField {
id: txGas
width: 200
validator: RegExpValidator { regExp: /\d*/ }
placeholderText: "Gas"
onTextChanged: {
contractFormReady()
}
}
TextField {
id: txGasPrice
width: 200
placeholderText: "Gas price"
validator: RegExpValidator { regExp: /\d*/ }
onTextChanged: {
contractFormReady()
}
}
Row {
id: rowContract
ExclusiveGroup { id: contractTypeGroup }
RadioButton {
id: createContractRadio
text: "Create contract"
checked: true
exclusiveGroup: contractTypeGroup
onClicked: {
txFuelRecipient.visible = false
txDataLabel.text = "Contract code"
}
}
RadioButton {
id: runContractRadio
text: "Run contract"
exclusiveGroup: contractTypeGroup
onClicked: {
txFuelRecipient.visible = true
txDataLabel.text = "Contract arguments"
}
}
}
Label {
id: txDataLabel
text: "Contract code"
}
TextArea {
id: codeView
height: 300
anchors.topMargin: 5
Layout.fillWidth: true
width: parent.width /2
onTextChanged: {
contractFormReady()
}
}
TextField {
id: txFuelRecipient
placeholderText: "Contract address"
validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
visible: false
width: 530
}
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: {
//this.enabled = false
var res = eth.create(txFuelRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text)
if(res[1]) {
txResult.text = "Your contract <b>could not</b> be send 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 an other contract"
onClicked: {
this.visible = false
txResult.text = ""
txOutput.text = ""
mainContractColumn.state = "SETUP"
}
}
Button {
id: debugButton
text: "Debug"
onClicked: {
var res = ui.debugTx("", txValue.text, txGas.text, txGasPrice.text, codeView.text)
debugWindow.visible = true
}
}
}
}
// New Transaction component
Component {
id: newTransaction
Column {
id: simpleSendColumn
states: [
State{
name: "ERROR"
},
State {
name: "DONE"
PropertyChanges { target: txSimpleValue; visible:false}
PropertyChanges { target: txSimpleRecipient; visible:false}
PropertyChanges { target:newSimpleTxButton; visible:false}
PropertyChanges { target: txSimpleResult; visible:true}
PropertyChanges { target: txSimpleOutput; visible:true}
PropertyChanges { target:newSimpleTxButton; visible:true}
},
State {
name: "SETUP"
PropertyChanges { target: txSimpleValue; visible:true; text: ""}
PropertyChanges { target: txSimpleRecipient; visible:true; text: ""}
PropertyChanges { target: txSimpleButton; visible:true}
PropertyChanges { target:newSimpleTxButton; visible:false}
}
]
spacing: 5
anchors.leftMargin: 5
anchors.topMargin: 5
anchors.top: parent.top
anchors.left: parent.left
function checkFormState(){
if(txSimpleRecipient.text.length == 40 && txSimpleValue.text.length > 0) {
txSimpleButton.state = "READY"
}else{
txSimpleButton.state = "NOTREADY"
}
}
TextField {
id: txSimpleRecipient
placeholderText: "Recipient address"
Layout.fillWidth: true
validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
width: 530
onTextChanged: { checkFormState() }
}
TextField {
id: txSimpleValue
width: 200
placeholderText: "Amount"
anchors.rightMargin: 5
validator: RegExpValidator { regExp: /\d*/ }
onTextChanged: { checkFormState() }
}
Button {
id: txSimpleButton
/*enabled: false*/
states: [
State {
name: "READY"
PropertyChanges { target: txSimpleButton; /*enabled: true*/}
},
State {
name: "NOTREADY"
PropertyChanges { target: txSimpleButton; /*enabled: false*/}
}
]
text: "Send"
onClicked: {
//this.enabled = false
var res = eth.transact(txSimpleRecipient.text, txSimpleValue.text,"","","")
if(res[1]) {
txSimpleResult.text = "There has been an error broadcasting your transaction:" + res[1].error()
} else {
txSimpleResult.text = "Your transaction has been broadcasted over the network.\nYour transaction id is:"
txSimpleOutput.text = res[0].hash
this.visible = false
simpleSendColumn.state = "DONE"
}
}
}
Text {
id: txSimpleResult
visible: false
}
TextField {
id: txSimpleOutput
visible: false
width: 530
}
Button {
id: newSimpleTxButton
visible: false
text: "Create an other transaction"
onClicked: {
this.visible = false
simpleSendColumn.state = "SETUP"
}
}
}
}
} }

View File

@@ -39,7 +39,7 @@ ApplicationWindow {
experimental.preferences.javascriptEnabled: true experimental.preferences.javascriptEnabled: true
experimental.preferences.navigatorQtObjectEnabled: true experimental.preferences.navigatorQtObjectEnabled: true
experimental.preferences.developerExtrasEnabled: true experimental.preferences.developerExtrasEnabled: true
experimental.userScripts: [ui.assetPath("ethereum.js")] experimental.userScripts: [ui.assetPath("ext/pre.js"), ui.assetPath("ext/big.js"), ui.assetPath("ext/string.js"), ui.assetPath("ext/ethereum.js")]
experimental.onMessageReceived: { experimental.onMessageReceived: {
console.log("[onMessageReceived]: ", message.data) console.log("[onMessageReceived]: ", message.data)
// TODO move to messaging.js // TODO move to messaging.js
@@ -47,13 +47,37 @@ ApplicationWindow {
try { try {
switch(data.call) { switch(data.call) {
case "getCoinBase":
postData(data._seed, eth.getCoinBase())
break
case "getIsListening":
postData(data._seed, eth.getIsListening())
break
case "getIsMining":
postData(data._seed, eth.getIsMining())
break
case "getPeerCount":
postData(data._seed, eth.getPeerCount())
break
case "getTxCountAt":
require(1)
postData(data._seed, eth.getTxCountAt(data.args[0]))
break
case "getBlockByNumber": case "getBlockByNumber":
var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") var block = eth.getBlock(data.args[0])
postData(data._seed, block) postData(data._seed, block)
break break
case "getBlockByHash": case "getBlockByHash":
var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") var block = eth.getBlock(data.args[0])
postData(data._seed, block) postData(data._seed, block)
break break
case "transact": case "transact":
require(5) require(5)
@@ -94,11 +118,14 @@ ApplicationWindow {
postData(data._seed, null) postData(data._seed, null)
break; break;
case "set": case "set":
console.log("'Set' has been depcrecated")
/*
for(var key in data.args) { for(var key in data.args) {
if(webview.hasOwnProperty(key)) { if(webview.hasOwnProperty(key)) {
window[key] = data.args[key]; window[key] = data.args[key];
} }
} }
*/
break; break;
case "getSecretToAddress": case "getSecretToAddress":
require(1) require(1)

View File

@@ -5,31 +5,27 @@
<link rel="stylesheet" href="bootstrap.min.css"> <link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="bootstrap-theme.min.css"> <link rel="stylesheet" href="bootstrap-theme.min.css">
<link rel="stylesheet" href="samplecoin.css"> <link rel="stylesheet" href="samplecoin.css">
<script src="promise.min.js"></script>
<meta name="viewport" content="minimum-scale=1; maximum-scale=1; initial-scale=1;"> <meta name="viewport" content="minimum-scale=1; maximum-scale=1; initial-scale=1;">
<script type="text/javascript"> <script type="text/javascript">
var jefcoinAddr = "3dff537f51350239abc95c76a5864aa605259e7d" var jefcoinAddr = "518546ffa883dcc838a64bc2dabada0fd64af459"
var mAddr = "" var mAddr = ""
function createTransaction() { function createTransaction() {
var addr = document.querySelector("#addr").value; var addr = ("0x" + document.querySelector("#addr").value).pad(32);
var amount = document.querySelector("#amount").value; var amount = document.querySelector("#amount").value.pad(32);
var data = (("0x"+addr).pad(32) + amount.pad(32)).unbin() var data = (addr + amount).unbin();
eth.transact(mAddr, jefcoinAddr, 0, "10000000", "250", data, function(receipt) { eth.transact(mAddr, jefcoinAddr, 0, "10000000", "250", data, function(receipt) {
debug("received tx hash:", reciept.address) debug("received tx hash:", reciept.address)
}) })
} }
function init() { function init() {
eth.set({width: 500});
eth.getKey(function(sec) { eth.getKey(function(sec) {
mAddr = sec;
eth.getSecretToAddress(sec, function(addr) { eth.getSecretToAddress(sec, function(addr) {
mAddr = addr;
eth.getStorageAt(jefcoinAddr, addr, function(storage) { eth.getStorageAt(jefcoinAddr, addr, function(storage) {
document.querySelector("#current-amount").innerHTML = storage; document.querySelector("#current-amount").innerHTML = storage;
}); });

View File

@@ -0,0 +1,43 @@
<html>
<head>
<title>Utils</title>
</head>
<body onload="init();">
<label>Nonce for 2ef47100e0787b915105fd5e3f4ff6752079d5cb</label>
<p id="nonce"></p>
<label>Connected peers</label>
<p id="peers"></p>
<label>Is mining</label>
<p id="isMining"></p>
<label>Is listening</label>
<p id="isListen"></p>
<label>Coinbase</label>
<p id="coinbase"></p>
<script type="text/javascript">
function init() {
eth.getTxCountAt("2ef47100e0787b915105fd5e3f4ff6752079d5cb", function(nonce){
document.querySelector("#nonce").innerHTML = nonce;
})
eth.getPeerCount(function(peerLength){
document.querySelector("#peers").innerHTML = peerLength;
})
eth.getIsMining(function(mining){
document.querySelector("#isMining").innerHTML = mining;
})
eth.getIsListening(function(listen){
document.querySelector("#isListen").innerHTML = listen;
})
eth.getCoinBase(function(address){
document.querySelector("#coinbase").innerHTML = address;
})
}
</script>
</body>
</html>

View File

@@ -4,9 +4,11 @@ import (
"flag" "flag"
) )
var Identifier string
var StartConsole bool var StartConsole bool
var StartMining bool var StartMining bool
var StartRpc bool var StartRpc bool
var RpcPort int
var UseUPnP bool var UseUPnP bool
var OutboundPort string var OutboundPort string
var ShowGenesis bool var ShowGenesis bool
@@ -20,6 +22,7 @@ var DataDir string
var AssetPath string var AssetPath string
func Init() { func Init() {
flag.StringVar(&Identifier, "i", "", "Custom client identifier")
flag.BoolVar(&StartConsole, "c", false, "debug and testing console") flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
flag.BoolVar(&StartMining, "m", false, "start dagger mining") flag.BoolVar(&StartMining, "m", false, "start dagger mining")
flag.BoolVar(&StartRpc, "r", false, "start rpc server") flag.BoolVar(&StartRpc, "r", false, "start rpc server")
@@ -28,10 +31,11 @@ func Init() {
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
flag.BoolVar(&ExportKey, "export", false, "export private key") flag.BoolVar(&ExportKey, "export", false, "export private key")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.StringVar(&OutboundPort, "p", "30303", "listening port") flag.StringVar(&OutboundPort, "p", "30303", "listening port")
flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory") flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory")
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") flag.IntVar(&MaxPeer, "x", 10, "maximum desired peers")
flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory") flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory")
flag.Parse() flag.Parse()

View File

@@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"github.com/ethereum/eth-go" "github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/ethereal/ui" "github.com/ethereum/go-ethereum/ethereal/ui"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
@@ -41,8 +39,7 @@ func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
ethchain.InitFees() ethchain.InitFees()
ethutil.ReadConfig(DataDir) ethutil.ReadConfig(DataDir, ethutil.LogFile|ethutil.LogStd, Identifier)
ethutil.Config.Seed = UseSeed
// Instantiated a eth stack // Instantiated a eth stack
ethereum, err := eth.New(eth.CapDefault, UseUPnP) ethereum, err := eth.New(eth.CapDefault, UseUPnP)
@@ -90,8 +87,19 @@ func main() {
} }
if ExportKey { if ExportKey {
key := ethutil.Config.Db.GetKeys()[0] keyPair := ethutil.GetKeyRing().Get(0)
fmt.Printf("%x\n", key.PrivateKey) fmt.Printf(`
Generating new address and keypair.
Please keep your keys somewhere save.
++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x
prvk: %x
pubk: %x
++++++++++++++++++++++++++++++++++++++++++++
save these words so you can restore your account later: %s
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey)
os.Exit(0) os.Exit(0)
} }
@@ -100,9 +108,12 @@ func main() {
os.Exit(0) os.Exit(0)
} }
if StartMining {
utils.DoMining(ethereum)
}
if StartRpc { if StartRpc {
ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool())) utils.DoRpc(ethereum, RpcPort)
go ethereum.RpcServer.Start()
} }
log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver) log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver)
@@ -111,5 +122,11 @@ func main() {
ethereum.MaxPeers = MaxPeer ethereum.MaxPeers = MaxPeer
gui := ethui.New(ethereum) gui := ethui.New(ethereum)
ethereum.Start(UseSeed)
gui.Start(AssetPath) gui.Start(AssetPath)
// Wait for shutdown
ethereum.WaitForShutdown()
} }

199
ethereal/ui/debugger.go Normal file
View File

@@ -0,0 +1,199 @@
package ethui
import (
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"math/big"
"strings"
)
func formatData(data string) []byte {
if len(data) == 0 {
return nil
}
// Simple stupid
d := new(big.Int)
if data[0:1] == "\"" && data[len(data)-1:] == "\"" {
d.SetBytes([]byte(data[1 : len(data)-1]))
} else if data[:2] == "0x" {
d.SetBytes(ethutil.FromHex(data[2:]))
} else {
d.SetString(data, 0)
}
return ethutil.BigToBytes(d, 256)
}
type DebuggerWindow struct {
win *qml.Window
engine *qml.Engine
lib *UiLib
Db *Debugger
}
func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
engine := qml.NewEngine()
component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml"))
if err != nil {
fmt.Println(err)
return nil
}
win := component.CreateWindow(nil)
db := &Debugger{win, make(chan bool), make(chan bool), true}
return &DebuggerWindow{engine: engine, win: win, lib: lib, Db: db}
}
func (self *DebuggerWindow) Show() {
context := self.engine.Context()
context.SetVar("dbg", self)
go func() {
self.win.Show()
self.win.Wait()
}()
}
func (self *DebuggerWindow) SetCode(code string) {
self.win.Set("codeText", code)
}
func (self *DebuggerWindow) SetData(data string) {
self.win.Set("dataText", data)
}
func (self *DebuggerWindow) SetAsm(data string) {
dis := ethchain.Disassemble(ethutil.FromHex(data))
for _, str := range dis {
self.win.Root().Call("setAsm", str)
}
}
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
if !self.Db.done {
self.Db.Q <- true
}
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
self.Db.done = true
}
}()
data := ethutil.StringToByteFunc(dataStr, func(s string) (ret []byte) {
slice := strings.Split(dataStr, "\n")
for _, dataItem := range slice {
d := formatData(dataItem)
ret = append(ret, d...)
}
return
})
var err error
script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
ret, err = ethutil.Compile(s)
return
})
if err != nil {
ethutil.Config.Log.Debugln(err)
return
}
dis := ethchain.Disassemble(script)
self.win.Root().Call("clearAsm")
for _, str := range dis {
self.win.Root().Call("setAsm", str)
}
// Contract addr as test address
keyPair := ethutil.GetKeyRing().Get(0)
callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), script)
callerTx.Sign(keyPair.PrivateKey)
state := self.lib.eth.BlockChain().CurrentBlock.State()
account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
contract := ethchain.MakeContract(callerTx, state)
callerClosure := ethchain.NewClosure(account, contract, script, state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr))
block := self.lib.eth.BlockChain().CurrentBlock
vm := ethchain.NewVm(state, self.lib.eth.StateManager(), ethchain.RuntimeVars{
Origin: account.Address(),
BlockNumber: block.BlockInfo().Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
})
self.Db.done = false
go func() {
callerClosure.Call(vm, data, self.Db.halting)
state.Reset()
self.Db.done = true
}()
}
func (self *DebuggerWindow) Next() {
self.Db.Next()
}
type Debugger struct {
win *qml.Window
N chan bool
Q chan bool
done bool
}
type storeVal struct {
Key, Value string
}
func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack, stateObject *ethchain.StateObject) bool {
d.win.Root().Call("setInstruction", pc)
d.win.Root().Call("clearMem")
d.win.Root().Call("clearStack")
d.win.Root().Call("clearStorage")
addr := 0
for i := 0; i+32 <= mem.Len(); i += 32 {
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
addr++
}
for _, val := range stack.Data() {
d.win.Root().Call("setStack", val.String())
}
stateObject.State().EachStorage(func(key string, node *ethutil.Value) {
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
})
out:
for {
select {
case <-d.N:
break out
case <-d.Q:
d.done = true
return false
}
}
return true
}
func (d *Debugger) Next() {
if !d.done {
d.N <- true
}
}

View File

@@ -18,6 +18,7 @@ type AppContainer interface {
NewBlock(*ethchain.Block) NewBlock(*ethchain.Block)
ObjectChanged(*ethchain.StateObject) ObjectChanged(*ethchain.StateObject)
StorageChanged(*ethchain.StorageState) StorageChanged(*ethchain.StorageState)
NewWatcher(chan bool)
} }
type ExtApplication struct { type ExtApplication struct {
@@ -26,6 +27,7 @@ type ExtApplication struct {
blockChan chan ethutil.React blockChan chan ethutil.React
changeChan chan ethutil.React changeChan chan ethutil.React
quitChan chan bool quitChan chan bool
watcherQuitChan chan bool
container AppContainer container AppContainer
lib *UiLib lib *UiLib
@@ -34,10 +36,11 @@ type ExtApplication struct {
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
app := &ExtApplication{ app := &ExtApplication{
ethpub.NewPEthereum(lib.eth.StateManager(), lib.eth.BlockChain(), lib.eth.TxPool()), ethpub.NewPEthereum(lib.eth),
make(chan ethutil.React, 1), make(chan ethutil.React, 1),
make(chan ethutil.React, 1), make(chan ethutil.React, 1),
make(chan bool), make(chan bool),
make(chan bool),
container, container,
lib, lib,
nil, nil,
@@ -66,6 +69,8 @@ func (app *ExtApplication) run() {
reactor := app.lib.eth.Reactor() reactor := app.lib.eth.Reactor()
reactor.Subscribe("newBlock", app.blockChan) reactor.Subscribe("newBlock", app.blockChan)
app.container.NewWatcher(app.watcherQuitChan)
win := app.container.Window() win := app.container.Window()
win.Show() win.Show()
win.Wait() win.Wait()
@@ -83,6 +88,7 @@ func (app *ExtApplication) stop() {
// Kill the main loop // Kill the main loop
app.quitChan <- true app.quitChan <- true
app.watcherQuitChan <- true
close(app.blockChan) close(app.blockChan)
close(app.quitChan) close(app.quitChan)

View File

@@ -24,10 +24,13 @@ type Gui struct {
// The public Ethereum library // The public Ethereum library
lib *EthLib lib *EthLib
uiLib *UiLib
txDb *ethdb.LDBDatabase txDb *ethdb.LDBDatabase
addr []byte addr []byte
pub *ethpub.PEthereum
} }
// Create GUI, but doesn't start it // Create GUI, but doesn't start it
@@ -38,22 +41,22 @@ func New(ethereum *eth.Ethereum) *Gui {
panic(err) panic(err)
} }
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
// On first run we won't have any keys yet, so this would crash. // On first run we won't have any keys yet, so this would crash.
// Therefor we check if we are ready to actually start this process // Therefor we check if we are ready to actually start this process
var addr []byte var addr []byte
if len(data) > 0 { if ethutil.GetKeyRing().Len() != 0 {
key := ethutil.Config.Db.GetKeys()[0] addr = ethutil.GetKeyRing().Get(0).Address()
addr = key.Address()
ethereum.StateManager().WatchAddr(addr)
} }
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr} pub := ethpub.NewPEthereum(ethereum)
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr, pub: pub}
} }
func (ui *Gui) Start(assetPath string) { func (gui *Gui) Start(assetPath string) {
defer ui.txDb.Close() const version = "0.5.0 RC11"
defer gui.txDb.Close()
// Register ethereum functions // Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{ qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
@@ -62,27 +65,27 @@ func (ui *Gui) Start(assetPath string) {
Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" }, Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
}}) }})
ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.2")) ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", version))
ethutil.Config.Log.Infoln("[GUI] Starting GUI") ethutil.Config.Log.Infoln("[GUI] Starting GUI")
// Create a new QML engine // Create a new QML engine
ui.engine = qml.NewEngine() gui.engine = qml.NewEngine()
context := ui.engine.Context() context := gui.engine.Context()
// Expose the eth library and the ui library to QML // Expose the eth library and the ui library to QML
context.SetVar("eth", ui.lib) context.SetVar("eth", gui)
uiLib := NewUiLib(ui.engine, ui.eth, assetPath) context.SetVar("pub", gui.pub)
context.SetVar("ui", uiLib) gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
context.SetVar("ui", gui.uiLib)
// Load the main QML interface // Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
var err error
var component qml.Object
firstRun := len(data) == 0
if firstRun { var win *qml.Window
component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml")) var err error
if len(data) == 0 {
win, err = gui.showKeyImport(context)
} else { } else {
component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml")) win, err = gui.showWallet(context)
} }
if err != nil { if err != nil {
ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'") ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
@@ -90,130 +93,181 @@ func (ui *Gui) Start(assetPath string) {
panic(err) panic(err)
} }
ui.win = component.CreateWindow(nil) win.Show()
uiLib.win = ui.win win.Wait()
db := &Debugger{ui.win, make(chan bool)}
ui.lib.Db = db
uiLib.Db = db
// Register the ui as a block processor gui.eth.Stop()
//ui.eth.BlockManager.SecondaryBlockProcessor = ui
//ui.eth.TxPool.SecondaryProcessor = ui
// Add the ui as a log system so we can log directly to the UGI
ethutil.Config.Log.AddLogSystem(ui)
// Loads previous blocks
if firstRun == false {
go ui.setInitialBlockChain()
go ui.readPreviousTransactions()
go ui.update()
} }
ui.win.Show() func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
ui.win.Wait() component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
if err != nil {
ui.eth.Stop() return nil, err
} }
func (ui *Gui) setInitialBlockChain() { win := gui.createWindow(component)
// Load previous 10 blocks
chain := ui.eth.BlockChain().GetChain(ui.eth.BlockChain().CurrentBlock.Hash(), 10) go gui.setInitialBlockChain()
for _, block := range chain { go gui.readPreviousTransactions()
ui.ProcessBlock(block) go gui.update()
return win, nil
} }
func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
context.SetVar("lib", gui.lib)
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml"))
if err != nil {
return nil, err
} }
func (ui *Gui) readPreviousTransactions() { return gui.createWindow(component), nil
it := ui.txDb.Db().NewIterator(nil, nil) }
func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
win := comp.CreateWindow(nil)
gui.win = win
gui.uiLib.win = win
db := &Debugger{gui.win, make(chan bool), make(chan bool), true}
gui.lib.Db = db
gui.uiLib.Db = db
return gui.win
}
func (gui *Gui) setInitialBlockChain() {
sBlk := gui.eth.BlockChain().LastBlockHash
blk := gui.eth.BlockChain().GetBlock(sBlk)
for ; blk != nil; blk = gui.eth.BlockChain().GetBlock(sBlk) {
sBlk = blk.PrevHash
gui.processBlock(blk, true)
}
}
func (gui *Gui) readPreviousTransactions() {
it := gui.txDb.Db().NewIterator(nil, nil)
for it.Next() { for it.Next() {
tx := ethchain.NewTransactionFromBytes(it.Value()) tx := ethchain.NewTransactionFromBytes(it.Value())
ui.win.Root().Call("addTx", ethpub.NewPTx(tx)) var inout string
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
inout = "send"
} else {
inout = "recv"
}
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), inout)
} }
it.Release() it.Release()
} }
func (ui *Gui) ProcessBlock(block *ethchain.Block) { func (gui *Gui) processBlock(block *ethchain.Block, initial bool) {
ui.win.Root().Call("addBlock", ethpub.NewPBlock(block)) gui.win.Root().Call("addBlock", ethpub.NewPBlock(block), initial)
}
func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) {
var str string
if unconfirmedFunds != nil {
pos := "+"
if unconfirmedFunds.Cmp(big.NewInt(0)) < 0 {
pos = "-"
}
val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
str = fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(amount), pos, val)
} else {
str = fmt.Sprintf("%v", ethutil.CurrencyToString(amount))
}
gui.win.Root().Call("setWalletValue", str)
} }
// Simple go routine function that updates the list of peers in the GUI // Simple go routine function that updates the list of peers in the GUI
func (ui *Gui) update() { func (gui *Gui) update() {
txChan := make(chan ethchain.TxMsg, 1) reactor := gui.eth.Reactor()
ui.eth.TxPool().Subscribe(txChan)
blockChan := make(chan ethutil.React, 1)
txChan := make(chan ethutil.React, 1)
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("newTx:pre", txChan)
reactor.Subscribe("newTx:post", txChan)
state := gui.eth.StateManager().TransState()
account := ui.eth.StateManager().GetAddrState(ui.addr).Object
unconfirmedFunds := new(big.Int) unconfirmedFunds := new(big.Int)
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount))) gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.addr).Amount)))
addrState := ui.eth.StateManager().GetAddrState(ui.addr)
for { for {
select { select {
case b := <-blockChan:
block := b.Resource.(*ethchain.Block)
gui.processBlock(block, false)
if bytes.Compare(block.Coinbase, gui.addr) == 0 {
gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.addr).Amount, nil)
}
case txMsg := <-txChan: case txMsg := <-txChan:
tx := txMsg.Tx tx := txMsg.Resource.(*ethchain.Transaction)
if txMsg.Type == ethchain.TxPre { if txMsg.Event == "newTx:pre" {
if bytes.Compare(tx.Sender(), ui.addr) == 0 && addrState.Nonce <= tx.Nonce { object := state.GetAccount(gui.addr)
ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
ui.txDb.Put(tx.Hash(), tx.RlpEncode()) if bytes.Compare(tx.Sender(), gui.addr) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
addrState.Nonce += 1
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value) unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 { } else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
ui.win.Root().Call("addTx", ethpub.NewPTx(tx)) gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv")
ui.txDb.Put(tx.Hash(), tx.RlpEncode()) gui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Add(unconfirmedFunds, tx.Value) unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
} }
pos := "+" gui.setWalletValue(object.Amount, unconfirmedFunds)
if unconfirmedFunds.Cmp(big.NewInt(0)) >= 0 {
pos = "-"
}
val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
str := fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(account.Amount), pos, val)
ui.win.Root().Call("setWalletValue", str)
} else { } else {
amount := account.Amount object := state.GetAccount(gui.addr)
if bytes.Compare(tx.Sender(), ui.addr) == 0 { if bytes.Compare(tx.Sender(), gui.addr) == 0 {
amount.Sub(account.Amount, tx.Value) object.SubAmount(tx.Value)
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 { } else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
amount.Add(account.Amount, tx.Value) object.AddAmount(tx.Value)
} }
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(amount))) gui.setWalletValue(object.Amount, nil)
state.UpdateStateObject(object)
} }
} }
/*
accountAmount := ui.eth.BlockManager.GetAddrState(ui.addr).Account.Amount
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", accountAmount))
ui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", ui.eth.Peers().Len(), ui.eth.MaxPeers))
time.Sleep(1 * time.Second)
*/
} }
} }
// Logging functions that log directly to the GUI interface // Logging functions that log directly to the GUI interface
func (ui *Gui) Println(v ...interface{}) { func (gui *Gui) Println(v ...interface{}) {
str := strings.TrimRight(fmt.Sprintln(v...), "\n") str := strings.TrimRight(fmt.Sprintln(v...), "\n")
lines := strings.Split(str, "\n") lines := strings.Split(str, "\n")
for _, line := range lines { for _, line := range lines {
ui.win.Root().Call("addLog", line) gui.win.Root().Call("addLog", line)
} }
} }
func (ui *Gui) Printf(format string, v ...interface{}) { func (gui *Gui) Printf(format string, v ...interface{}) {
str := strings.TrimRight(fmt.Sprintf(format, v...), "\n") str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
lines := strings.Split(str, "\n") lines := strings.Split(str, "\n")
for _, line := range lines { for _, line := range lines {
ui.win.Root().Call("addLog", line) gui.win.Root().Call("addLog", line)
} }
} }
func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
keyPair := ethutil.GetKeyRing().Get(0)
return gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), recipient, value, gas, gasPrice, data)
}
func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
keyPair := ethutil.GetKeyRing().Get(0)
return gui.pub.Create(ethutil.Hex(keyPair.PrivateKey), value, gas, gasPrice, data)
}

View File

@@ -6,6 +6,12 @@ import (
"github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml" "github.com/go-qml/qml"
"github.com/howeyc/fsnotify"
"io/ioutil"
"log"
"net/url"
"os"
"path"
"path/filepath" "path/filepath"
) )
@@ -15,6 +21,7 @@ type HtmlApplication struct {
engine *qml.Engine engine *qml.Engine
lib *UiLib lib *UiLib
path string path string
watcher *fsnotify.Watcher
} }
func NewHtmlApplication(path string, lib *UiLib) *HtmlApplication { func NewHtmlApplication(path string, lib *UiLib) *HtmlApplication {
@@ -47,6 +54,59 @@ func (app *HtmlApplication) Create() error {
return nil return nil
} }
func (app *HtmlApplication) RootFolder() string {
folder, err := url.Parse(app.path)
if err != nil {
return ""
}
return path.Dir(folder.RequestURI())
}
func (app *HtmlApplication) RecursiveFolders() []os.FileInfo {
files, _ := ioutil.ReadDir(app.RootFolder())
var folders []os.FileInfo
for _, file := range files {
if file.IsDir() {
folders = append(folders, file)
}
}
return folders
}
func (app *HtmlApplication) NewWatcher(quitChan chan bool) {
var err error
app.watcher, err = fsnotify.NewWatcher()
if err != nil {
return
}
err = app.watcher.Watch(app.RootFolder())
if err != nil {
log.Fatal(err)
}
for _, folder := range app.RecursiveFolders() {
fullPath := app.RootFolder() + "/" + folder.Name()
app.watcher.Watch(fullPath)
}
go func() {
out:
for {
select {
case <-quitChan:
app.watcher.Close()
break out
case <-app.watcher.Event:
//ethutil.Config.Log.Debugln("Got event:", ev)
app.webView.Call("reload")
case err := <-app.watcher.Error:
// TODO: Do something here
ethutil.Config.Log.Infoln("Watcher error:", err)
}
}
}()
}
func (app *HtmlApplication) Engine() *qml.Engine { func (app *HtmlApplication) Engine() *qml.Engine {
return app.engine return app.engine
} }

View File

@@ -1,10 +1,8 @@
package ethui package ethui
import ( import (
"encoding/hex"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
@@ -36,93 +34,13 @@ func (lib *EthLib) ImportAndSetPrivKey(privKey string) bool {
} }
func (lib *EthLib) CreateAndSetPrivKey() (string, string, string, string) { func (lib *EthLib) CreateAndSetPrivKey() (string, string, string, string) {
pub, prv := secp256k1.GenerateKeyPair() _, prv := secp256k1.GenerateKeyPair()
pair := &ethutil.Key{PrivateKey: prv, PublicKey: pub} keyPair, err := ethutil.GetKeyRing().NewKeyPair(prv)
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode()) if err != nil {
mne := ethutil.MnemonicEncode(ethutil.Hex(prv)) panic(err)
}
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
mnemonicString := strings.Join(mne, " ") mnemonicString := strings.Join(mne, " ")
return mnemonicString, fmt.Sprintf("%x", pair.Address()), fmt.Sprintf("%x", prv), fmt.Sprintf("%x", pub) return mnemonicString, fmt.Sprintf("%x", keyPair.Address()), ethutil.Hex(keyPair.PrivateKey), ethutil.Hex(keyPair.PublicKey)
}
func (lib *EthLib) GetKey() string {
return ethutil.Hex(ethutil.Config.Db.GetKeys()[0].Address())
}
func (lib *EthLib) GetStateObject(address string) *ethpub.PStateObject {
stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
if stateObject != nil {
return ethpub.NewPStateObject(stateObject)
}
// See GetStorage for explanation on "nil"
return ethpub.NewPStateObject(nil)
}
func (lib *EthLib) Watch(addr, storageAddr string) {
// lib.stateManager.Watch(ethutil.FromHex(addr), ethutil.FromHex(storageAddr))
}
func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
return lib.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr)
}
func (lib *EthLib) Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
var hash []byte
var contractCreation bool
if len(recipient) == 0 {
contractCreation = true
} else {
var err error
hash, err = hex.DecodeString(recipient)
if err != nil {
return "", err
}
}
keyPair := ethutil.Config.Db.GetKeys()[0]
value := ethutil.Big(valueStr)
gas := ethutil.Big(gasStr)
gasPrice := ethutil.Big(gasPriceStr)
var tx *ethchain.Transaction
// Compile and assemble the given data
if contractCreation {
// Compile script
mainScript, initScript, err := utils.CompileScript(dataStr)
if err != nil {
return "", err
}
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript)
} else {
lines := strings.Split(dataStr, "\n")
var data []byte
for _, line := range lines {
data = append(data, ethutil.BigToBytes(ethutil.Big(line), 256)...)
}
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
}
acc := lib.stateManager.GetAddrState(keyPair.Address())
tx.Nonce = acc.Nonce
tx.Sign(keyPair.PrivateKey)
lib.txPool.QueueTransaction(tx)
if contractCreation {
ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:])
} else {
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
}
return ethutil.Hex(tx.Hash()), nil
}
func (lib *EthLib) GetBlock(hexHash string) *ethpub.PBlock {
hash, err := hex.DecodeString(hexHash)
if err != nil {
return nil
}
block := lib.blockChain.GetBlock(hash)
return &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
} }

View File

@@ -2,13 +2,10 @@ package ethui
import ( import (
"bitbucket.org/kardianos/osext" "bitbucket.org/kardianos/osext"
"fmt"
"github.com/ethereum/eth-go" "github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml" "github.com/go-qml/qml"
"github.com/obscuren/mutan"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@@ -29,6 +26,7 @@ type UiLib struct {
// The main application window // The main application window
win *qml.Window win *qml.Window
Db *Debugger Db *Debugger
DbWindow *DebuggerWindow
} }
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
@@ -59,14 +57,6 @@ func (ui *UiLib) OpenHtml(path string) {
go app.run() go app.run()
} }
func (ui *UiLib) Watch(addr, storageAddr string) {
if len(storageAddr) == 0 {
ui.eth.Reactor().Subscribe("storage:"+string(ethutil.FromHex(addr))+":"+string(ethutil.FromHex(storageAddr)), nil)
} else {
ui.eth.Reactor().Subscribe("object:"+string(ethutil.FromHex(addr)), nil)
}
}
func (ui *UiLib) Muted(content string) { func (ui *UiLib) Muted(content string) {
component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml")) component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
if err != nil { if err != nil {
@@ -78,8 +68,6 @@ func (ui *UiLib) Muted(content string) {
go func() { go func() {
path := "file://" + ui.AssetPath("muted/index.html") path := "file://" + ui.AssetPath("muted/index.html")
win.Set("url", path) win.Set("url", path)
//debuggerPath := "file://" + ui.AssetPath("muted/debugger.html")
//win.Set("debugUrl", debuggerPath)
win.Show() win.Show()
win.Wait() win.Wait()
@@ -88,7 +76,7 @@ func (ui *UiLib) Muted(content string) {
func (ui *UiLib) Connect(button qml.Object) { func (ui *UiLib) Connect(button qml.Object) {
if !ui.connected { if !ui.connected {
ui.eth.Start() ui.eth.Start(true)
ui.connected = true ui.connected = true
button.Set("enabled", false) button.Set("enabled", false)
} }
@@ -101,6 +89,29 @@ func (ui *UiLib) ConnectToPeer(addr string) {
func (ui *UiLib) AssetPath(p string) string { func (ui *UiLib) AssetPath(p string) string {
return path.Join(ui.assetPath, p) return path.Join(ui.assetPath, p)
} }
func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
dbWindow := NewDebuggerWindow(self)
object := self.eth.StateManager().CurrentState().GetStateObject(ethutil.FromHex(contractHash))
if len(object.Script()) > 0 {
dbWindow.SetCode("0x" + ethutil.Hex(object.Script()))
}
dbWindow.SetData(data)
dbWindow.Show()
}
func (self *UiLib) StartDbWithCode(code string) {
dbWindow := NewDebuggerWindow(self)
dbWindow.SetCode("0x" + code)
dbWindow.Show()
}
func (self *UiLib) StartDebugger() {
dbWindow := NewDebuggerWindow(self)
//self.DbWindow = dbWindow
dbWindow.Show()
}
func DefaultAssetPath() string { func DefaultAssetPath() string {
var base string var base string
@@ -131,27 +142,28 @@ func DefaultAssetPath() string {
func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string) { func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string) {
state := ui.eth.BlockChain().CurrentBlock.State() state := ui.eth.BlockChain().CurrentBlock.State()
mainInput, _ := mutan.PreProcess(data) script, err := ethutil.Compile(data)
callerScript, err := utils.Compile(mainInput)
if err != nil { if err != nil {
ethutil.Config.Log.Debugln(err) ethutil.Config.Log.Debugln(err)
return return
} }
dis := ethchain.Disassemble(callerScript) dis := ethchain.Disassemble(script)
ui.win.Root().Call("clearAsm") ui.win.Root().Call("clearAsm")
for _, str := range dis { for _, str := range dis {
ui.win.Root().Call("setAsm", str) ui.win.Root().Call("setAsm", str)
} }
callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), callerScript, nil)
// Contract addr as test address // Contract addr as test address
keyPair := ethutil.Config.Db.GetKeys()[0] keyPair := ethutil.GetKeyRing().Get(0)
account := ui.eth.StateManager().GetAddrState(keyPair.Address()).Object callerTx :=
c := ethchain.MakeContract(callerTx, state) ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), script)
callerClosure := ethchain.NewClosure(account, c, c.Script(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr), ethutil.Big(valueStr)) callerTx.Sign(keyPair.PrivateKey)
account := ui.eth.StateManager().TransState().GetStateObject(keyPair.Address())
contract := ethchain.MakeContract(callerTx, state)
callerClosure := ethchain.NewClosure(account, contract, contract.Init(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr))
block := ui.eth.BlockChain().CurrentBlock block := ui.eth.BlockChain().CurrentBlock
vm := ethchain.NewVm(state, ui.eth.StateManager(), ethchain.RuntimeVars{ vm := ethchain.NewVm(state, ui.eth.StateManager(), ethchain.RuntimeVars{
@@ -161,50 +173,18 @@ func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string)
Coinbase: block.Coinbase, Coinbase: block.Coinbase,
Time: block.Time, Time: block.Time,
Diff: block.Difficulty, Diff: block.Difficulty,
TxData: nil,
}) })
ui.Db.done = false
go func() { go func() {
callerClosure.Call(vm, nil, ui.Db.halting) callerClosure.Call(vm, contract.Init(), ui.Db.halting)
state.Reset() state.Reset()
ui.Db.done = true
}() }()
} }
func (ui *UiLib) Next() { func (ui *UiLib) Next() {
ui.Db.Next() ui.Db.Next()
} }
type Debugger struct {
win *qml.Window
N chan bool
}
func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack) {
d.win.Root().Call("setInstruction", pc)
d.win.Root().Call("clearMem")
d.win.Root().Call("clearStack")
addr := 0
for i := 0; i+32 <= mem.Len(); i += 32 {
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
addr++
}
for _, val := range stack.Data() {
d.win.Root().Call("setStack", val.String())
}
out:
for {
select {
case <-d.N:
break out
default:
}
}
}
func (d *Debugger) Next() {
d.N <- true
}

View File

@@ -2,11 +2,14 @@ package main
import ( import (
"flag" "flag"
"fmt"
"os"
) )
var StartConsole bool var Identifier string
var StartMining bool var StartMining bool
var StartRpc bool var StartRpc bool
var RpcPort int
var UseUPnP bool var UseUPnP bool
var OutboundPort string var OutboundPort string
var ShowGenesis bool var ShowGenesis bool
@@ -19,13 +22,20 @@ var ExportKey bool
var LogFile string var LogFile string
var DataDir string var DataDir string
var NonInteractive bool var NonInteractive bool
var StartJsConsole bool
var InputFile string
func Init() { func Init() {
flag.BoolVar(&StartConsole, "c", false, "debug and testing console") flag.Usage = func() {
fmt.Fprintf(os.Stderr, "%s [options] [filename]:\n", os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&Identifier, "i", "", "custom client identifier")
flag.BoolVar(&StartMining, "m", false, "start dagger mining") flag.BoolVar(&StartMining, "m", false, "start dagger mining")
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
//flag.BoolVar(&UseGui, "gui", true, "use the gui")
flag.BoolVar(&StartRpc, "r", false, "start rpc server") flag.BoolVar(&StartRpc, "r", false, "start rpc server")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")
@@ -35,7 +45,10 @@ func Init() {
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)") flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory") flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory")
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") flag.IntVar(&MaxPeer, "x", 10, "maximum desired peers")
flag.BoolVar(&StartJsConsole, "js", false, "exp")
flag.Parse() flag.Parse()
InputFile = flag.Arg(0)
} }

View File

@@ -1,259 +0,0 @@
package main
import (
"bufio"
"bytes"
"encoding/hex"
"errors"
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/mutan"
"os"
"strings"
)
type Console struct {
db *ethdb.MemDatabase
trie *ethutil.Trie
ethereum *eth.Ethereum
}
func NewConsole(s *eth.Ethereum) *Console {
db, _ := ethdb.NewMemDatabase()
trie := ethutil.NewTrie(db, "")
return &Console{db: db, trie: trie, ethereum: s}
}
func (i *Console) ValidateInput(action string, argumentLength int) error {
err := false
var expArgCount int
switch {
case action == "update" && argumentLength != 2:
err = true
expArgCount = 2
case action == "get" && argumentLength != 1:
err = true
expArgCount = 1
case action == "dag" && argumentLength != 2:
err = true
expArgCount = 2
case action == "decode" && argumentLength != 1:
err = true
expArgCount = 1
case action == "encode" && argumentLength != 1:
err = true
expArgCount = 1
case action == "gettx" && argumentLength != 1:
err = true
expArgCount = 1
case action == "tx" && argumentLength != 4:
err = true
expArgCount = 4
case action == "getaddr" && argumentLength != 1:
err = true
expArgCount = 1
case action == "contract" && argumentLength != 2:
err = true
expArgCount = 2
case action == "say" && argumentLength != 1:
err = true
expArgCount = 1
case action == "addp" && argumentLength != 1:
err = true
expArgCount = 1
case action == "block" && argumentLength != 1:
err = true
expArgCount = 1
}
if err {
return errors.New(fmt.Sprintf("'%s' requires %d args, got %d", action, expArgCount, argumentLength))
} else {
return nil
}
}
func (i *Console) Editor() string {
var buff bytes.Buffer
for {
reader := bufio.NewReader(os.Stdin)
str, _, err := reader.ReadLine()
if len(str) > 0 {
buff.Write(str)
buff.WriteString("\n")
}
if err != nil && err.Error() == "EOF" {
break
}
}
return buff.String()
}
func (i *Console) PrintRoot() {
root := ethutil.NewValue(i.trie.Root)
if len(root.Bytes()) != 0 {
fmt.Println(hex.EncodeToString(root.Bytes()))
} else {
fmt.Println(i.trie.Root)
}
}
func (i *Console) ParseInput(input string) bool {
scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(bufio.ScanWords)
count := 0
var tokens []string
for scanner.Scan() {
count++
tokens = append(tokens, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
if len(tokens) == 0 {
return true
}
err := i.ValidateInput(tokens[0], count-1)
if err != nil {
fmt.Println(err)
} else {
switch tokens[0] {
case "update":
i.trie.Update(tokens[1], tokens[2])
i.PrintRoot()
case "get":
fmt.Println(i.trie.Get(tokens[1]))
case "root":
i.PrintRoot()
case "rawroot":
fmt.Println(i.trie.Root)
case "print":
i.db.Print()
case "dag":
fmt.Println(ethchain.DaggerVerify(ethutil.Big(tokens[1]), // hash
ethutil.BigPow(2, 36), // diff
ethutil.Big(tokens[2]))) // nonce
case "decode":
value := ethutil.NewValueFromBytes([]byte(tokens[1]))
fmt.Println(value)
case "getaddr":
encoded, _ := hex.DecodeString(tokens[1])
addr := i.ethereum.BlockChain().CurrentBlock.State().GetAccount(encoded)
fmt.Println("addr:", addr)
case "block":
encoded, _ := hex.DecodeString(tokens[1])
block := i.ethereum.BlockChain().GetBlock(encoded)
info := block.BlockInfo()
fmt.Printf("++++++++++ #%d ++++++++++\n%v\n", info.Number, block)
case "say":
i.ethereum.Broadcast(ethwire.MsgTalkTy, []interface{}{tokens[1]})
case "addp":
i.ethereum.ConnectToPeer(tokens[1])
case "pcount":
fmt.Println("peers:", i.ethereum.Peers().Len())
case "encode":
fmt.Printf("%q\n", ethutil.Encode(tokens[1]))
case "tx":
recipient, err := hex.DecodeString(tokens[1])
if err != nil {
fmt.Println("recipient err:", err)
} else {
tx := ethchain.NewTransactionMessage(recipient, ethutil.Big(tokens[2]), ethutil.Big(tokens[3]), ethutil.Big(tokens[4]), nil)
key := ethutil.Config.Db.GetKeys()[0]
tx.Sign(key.PrivateKey)
i.ethereum.TxPool().QueueTransaction(tx)
fmt.Printf("%x\n", tx.Hash())
}
case "gettx":
addr, _ := hex.DecodeString(tokens[1])
data, _ := ethutil.Config.Db.Get(addr)
if len(data) != 0 {
decoder := ethutil.NewValueFromBytes(data)
fmt.Println(decoder)
} else {
fmt.Println("gettx: tx not found")
}
case "contract":
fmt.Println("Contract editor (Ctrl-D = done)")
mainInput, initInput := mutan.PreProcess(i.Editor())
mainScript, err := utils.Compile(mainInput)
if err != nil {
fmt.Println(err)
break
}
initScript, err := utils.Compile(initInput)
if err != nil {
fmt.Println(err)
break
}
contract := ethchain.NewContractCreationTx(ethutil.Big(tokens[0]), ethutil.Big(tokens[1]), ethutil.Big(tokens[1]), mainScript, initScript)
key := ethutil.Config.Db.GetKeys()[0]
contract.Sign(key.PrivateKey)
i.ethereum.TxPool().QueueTransaction(contract)
fmt.Printf("%x\n", contract.Hash()[12:])
case "exit", "quit", "q":
return false
case "help":
fmt.Printf("COMMANDS:\n" +
"\033[1m= DB =\033[0m\n" +
"update KEY VALUE - Updates/Creates a new value for the given key\n" +
"get KEY - Retrieves the given key\n" +
"root - Prints the hex encoded merkle root\n" +
"rawroot - Prints the raw merkle root\n" +
"block HASH - Prints the block\n" +
"getaddr ADDR - Prints the account associated with the address\n" +
"\033[1m= Dagger =\033[0m\n" +
"dag HASH NONCE - Verifies a nonce with the given hash with dagger\n" +
"\033[1m= Encoding =\033[0m\n" +
"decode STR\n" +
"encode STR\n" +
"\033[1m= Other =\033[0m\n" +
"addp HOST:PORT\n" +
"tx TO AMOUNT\n" +
"contract AMOUNT\n")
default:
fmt.Println("Unknown command:", tokens[0])
}
}
return true
}
func (i *Console) Start() {
fmt.Printf("Eth Console. Type (help) for help\n")
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("eth >>> ")
str, _, err := reader.ReadLine()
if err != nil {
fmt.Println("Error reading input", err)
} else {
if !i.ParseInput(string(str)) {
return
}
}
}
}

View File

@@ -4,11 +4,9 @@ import (
"fmt" "fmt"
"github.com/ethereum/eth-go" "github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"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/ethutil"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
"io/ioutil"
"log" "log"
"os" "os"
"os/signal" "os/signal"
@@ -18,16 +16,15 @@ import (
const Debug = true const Debug = true
// Register interrupt handlers so we can stop the ethereum func RegisterInterrupt(cb func(os.Signal)) {
func RegisterInterrupts(s *eth.Ethereum) { go func() {
// Buffered chan of one is enough // Buffered chan of one is enough
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
// Notify about interrupts for now // Notify about interrupts for now
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt)
go func() {
for sig := range c { for sig := range c {
fmt.Printf("Shutting down (%v) ... \n", sig) cb(sig)
s.Stop()
} }
}() }()
} }
@@ -55,7 +52,15 @@ func main() {
var logSys *log.Logger var logSys *log.Logger
flags := log.LstdFlags flags := log.LstdFlags
ethutil.ReadConfig(DataDir) var lt ethutil.LoggerType
if StartJsConsole || len(InputFile) > 0 {
lt = ethutil.LogFile
} else {
lt = ethutil.LogFile | ethutil.LogStd
}
ethutil.ReadConfig(DataDir, lt, Identifier)
logger := ethutil.Config.Log logger := ethutil.Config.Log
if LogFile != "" { if LogFile != "" {
@@ -67,13 +72,11 @@ func main() {
log.SetOutput(logfile) log.SetOutput(logfile)
logSys = log.New(logfile, "", flags) logSys = log.New(logfile, "", flags)
logger.AddLogSystem(logSys) logger.AddLogSystem(logSys)
} } else {
/*else {
logSys = log.New(os.Stdout, "", flags) logSys = log.New(os.Stdout, "", flags)
}*/ }
ethchain.InitFees() ethchain.InitFees()
ethutil.Config.Seed = UseSeed
// Instantiated a eth stack // Instantiated a eth stack
ethereum, err := eth.New(eth.CapDefault, UseUPnP) ethereum, err := eth.New(eth.CapDefault, UseUPnP)
@@ -106,8 +109,19 @@ func main() {
} }
os.Exit(0) os.Exit(0)
case ExportKey: case ExportKey:
key := ethutil.Config.Db.GetKeys()[0] keyPair := ethutil.GetKeyRing().Get(0)
logSys.Println(fmt.Sprintf("prvk: %x\n", key.PrivateKey)) fmt.Printf(`
Generating new address and keypair.
Please keep your keys somewhere save.
++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x
prvk: %x
pubk: %x
++++++++++++++++++++++++++++++++++++++++++++
save these words so you can restore your account later: %s
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey)
os.Exit(0) os.Exit(0)
case ShowGenesis: case ShowGenesis:
logSys.Println(ethereum.BlockChain().Genesis()) logSys.Println(ethereum.BlockChain().Genesis())
@@ -123,46 +137,49 @@ func main() {
// Set the max peers // Set the max peers
ethereum.MaxPeers = MaxPeer ethereum.MaxPeers = MaxPeer
if StartConsole { // Set Mining status
err := os.Mkdir(ethutil.Config.ExecPath, os.ModePerm) ethereum.Mining = StartMining
// Error is OK if the error is ErrExist
if err != nil && !os.IsExist(err) { if StartMining {
log.Panic("Unable to create EXECPATH:", err) utils.DoMining(ethereum)
} }
console := NewConsole(ethereum) if StartJsConsole {
go console.Start() repl := NewJSRepl(ethereum)
go repl.Start()
RegisterInterrupt(func(os.Signal) {
repl.Stop()
})
} else if len(InputFile) > 0 {
file, err := os.Open(InputFile)
if err != nil {
ethutil.Config.Log.Fatal(err)
} }
content, err := ioutil.ReadAll(file)
if err != nil {
ethutil.Config.Log.Fatal(err)
}
re := NewJSRE(ethereum)
RegisterInterrupt(func(os.Signal) {
re.Stop()
})
re.Run(string(content))
}
if StartRpc { if StartRpc {
ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool())) utils.DoRpc(ethereum, RpcPort)
go ethereum.RpcServer.Start()
} }
RegisterInterrupts(ethereum) RegisterInterrupt(func(sig os.Signal) {
ethereum.Start() fmt.Printf("Shutting down (%v) ... \n", sig)
ethereum.Stop()
})
if StartMining { ethereum.Start(UseSeed)
logger.Infoln("Miner started")
// Fake block mining. It broadcasts a new block every 5 seconds
go func() {
if StartMining {
logger.Infoln("Miner started")
go func() {
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
keyRing := ethutil.NewValueFromBytes(data)
addr := keyRing.Get(1).Bytes()
miner := ethminer.NewDefaultMiner(addr, ethereum)
miner.Start()
}()
}
}()
}
// Wait for shutdown // Wait for shutdown
ethereum.WaitForShutdown() ethereum.WaitForShutdown()

View File

@@ -0,0 +1,190 @@
package main
import (
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/otto"
"io/ioutil"
"os"
"path/filepath"
)
type JSRE struct {
ethereum *eth.Ethereum
vm *otto.Otto
lib *ethpub.PEthereum
blockChan chan ethutil.React
changeChan chan ethutil.React
quitChan chan bool
objectCb map[string][]otto.Value
}
func NewJSRE(ethereum *eth.Ethereum) *JSRE {
re := &JSRE{
ethereum,
otto.New(),
ethpub.NewPEthereum(ethereum),
make(chan ethutil.React, 1),
make(chan ethutil.React, 1),
make(chan bool),
make(map[string][]otto.Value),
}
// Init the JS lib
re.vm.Run(jsLib)
// We have to make sure that, whoever calls this, calls "Stop"
go re.mainLoop()
re.Bind("eth", &JSEthereum{re.lib, re.vm})
re.initStdFuncs()
return re
}
func (self *JSRE) Bind(name string, v interface{}) {
self.vm.Set(name, v)
}
func (self *JSRE) Run(code string) (otto.Value, error) {
return self.vm.Run(code)
}
func (self *JSRE) Require(file string) error {
if len(filepath.Ext(file)) == 0 {
file += ".js"
}
fh, err := os.Open(file)
if err != nil {
return err
}
content, _ := ioutil.ReadAll(fh)
self.Run("exports = {};(function() {" + string(content) + "})();")
return nil
}
func (self *JSRE) Stop() {
// Kill the main loop
self.quitChan <- true
close(self.blockChan)
close(self.quitChan)
close(self.changeChan)
}
func (self *JSRE) mainLoop() {
// Subscribe to events
reactor := self.ethereum.Reactor()
reactor.Subscribe("newBlock", self.blockChan)
out:
for {
select {
case <-self.quitChan:
break out
case block := <-self.blockChan:
if _, ok := block.Resource.(*ethchain.Block); ok {
}
case object := <-self.changeChan:
if stateObject, ok := object.Resource.(*ethchain.StateObject); ok {
for _, cb := range self.objectCb[ethutil.Hex(stateObject.Address())] {
val, _ := self.vm.ToValue(ethpub.NewPStateObject(stateObject))
cb.Call(cb, val)
}
} else if storageObject, ok := object.Resource.(*ethchain.StorageState); ok {
for _, cb := range self.objectCb[ethutil.Hex(storageObject.StateAddress)+ethutil.Hex(storageObject.Address)] {
val, _ := self.vm.ToValue(ethpub.NewPStorageState(storageObject))
cb.Call(cb, val)
}
}
}
}
}
func (self *JSRE) initStdFuncs() {
t, _ := self.vm.Get("eth")
eth := t.Object()
eth.Set("watch", self.watch)
eth.Set("addPeer", self.addPeer)
eth.Set("require", self.require)
eth.Set("stopMining", self.stopMining)
eth.Set("startMining", self.startMining)
}
/*
* The following methods are natively implemented javascript functions
*/
func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value {
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))
return v
}
// eth.watch
func (self *JSRE) watch(call otto.FunctionCall) otto.Value {
addr, _ := call.Argument(0).ToString()
var storageAddr string
var cb otto.Value
var storageCallback bool
if len(call.ArgumentList) > 2 {
storageCallback = true
storageAddr, _ = call.Argument(1).ToString()
cb = call.Argument(2)
} else {
cb = call.Argument(1)
}
if storageCallback {
self.objectCb[addr+storageAddr] = append(self.objectCb[addr+storageAddr], cb)
event := "storage:" + string(ethutil.FromHex(addr)) + ":" + string(ethutil.FromHex(storageAddr))
self.ethereum.Reactor().Subscribe(event, self.changeChan)
} else {
self.objectCb[addr] = append(self.objectCb[addr], cb)
event := "object:" + string(ethutil.FromHex(addr))
self.ethereum.Reactor().Subscribe(event, self.changeChan)
}
return otto.UndefinedValue()
}
func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
host, err := call.Argument(0).ToString()
if err != nil {
return otto.FalseValue()
}
self.ethereum.ConnectToPeer(host)
return otto.TrueValue()
}
func (self *JSRE) require(call otto.FunctionCall) otto.Value {
file, err := call.Argument(0).ToString()
if err != nil {
return otto.UndefinedValue()
}
if err := self.Require(file); err != nil {
fmt.Println("err:", err)
return otto.UndefinedValue()
}
t, _ := self.vm.Get("exports")
return t
}

53
ethereum/js_lib.go Normal file
View File

@@ -0,0 +1,53 @@
package main
const jsLib = `
function pp(object) {
var str = "";
if(object instanceof Array) {
str += "[ ";
for(var i = 0, l = object.length; i < l; i++) {
str += pp(object[i]);
if(i < l-1) {
str += ", ";
}
}
str += " ]";
} else if(typeof(object) === "object") {
str += "{ ";
var last = Object.keys(object).sort().pop()
for(var k in object) {
str += k + ": " + pp(object[k]);
if(k !== last) {
str += ", ";
}
}
str += " }";
} else if(typeof(object) === "string") {
str += "\033[32m'" + object + "'";
} else if(typeof(object) === "undefined") {
str += "\033[1m\033[30m" + object;
} else if(typeof(object) === "number") {
str += "\033[31m" + object;
} else if(typeof(object) === "function") {
str += "\033[35m[Function]";
} else {
str += object;
}
str += "\033[0m";
return str;
}
function prettyPrint(/* */) {
var args = arguments;
for(var i = 0, l = args.length; i < l; i++) {
console.log(pp(args[i]))
}
}
var print = prettyPrint;
`

110
ethereum/repl.go Normal file
View File

@@ -0,0 +1,110 @@
package main
import (
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethpub"
"github.com/obscuren/otto"
)
type Repl interface {
Start()
Stop()
}
type JSRepl struct {
re *JSRE
prompt string
}
func NewJSRepl(ethereum *eth.Ethereum) *JSRepl {
return &JSRepl{re: NewJSRE(ethereum), prompt: "> "}
}
func (self *JSRepl) Start() {
self.read()
}
func (self *JSRepl) Stop() {
self.re.Stop()
}
func (self *JSRepl) parseInput(code string) {
defer func() {
if r := recover(); r != nil {
fmt.Println("[native] error", r)
}
}()
value, err := self.re.Run(code)
if err != nil {
fmt.Println(err)
return
}
self.PrintValue(value)
}
// 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) GetKey() otto.Value {
return self.toVal(self.PEthereum.GetKey())
}
func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(self.PEthereum.GetStateObject(addr))
}
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(err)
return otto.UndefinedValue()
}
return result
}

121
ethereum/repl_darwin.go Normal file
View File

@@ -0,0 +1,121 @@
package main
// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include
// #cgo darwin LDFLAGS: -L/usr/local/opt/readline/lib
// #cgo LDFLAGS: -lreadline
// #include <stdio.h>
// #include <stdlib.h>
// #include <readline/readline.h>
// #include <readline/history.h>
import "C"
import (
"os"
"os/signal"
"strings"
"syscall"
"unsafe"
)
func initReadLine() {
C.rl_catch_sigwinch = 0
C.rl_catch_signals = 0
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGWINCH)
signal.Notify(c, os.Interrupt)
go func() {
for sig := range c {
switch sig {
case syscall.SIGWINCH:
C.rl_resize_terminal()
case os.Interrupt:
C.rl_cleanup_after_signal()
default:
}
}
}()
}
func readLine(prompt *string) *string {
var p *C.char
//readline allows an empty prompt(NULL)
if prompt != nil {
p = C.CString(*prompt)
}
ret := C.readline(p)
if p != nil {
C.free(unsafe.Pointer(p))
}
if ret == nil {
return nil
} //EOF
s := C.GoString(ret)
C.free(unsafe.Pointer(ret))
return &s
}
func addHistory(s string) {
p := C.CString(s)
C.add_history(p)
C.free(unsafe.Pointer(p))
}
var indentCount = 0
var str = ""
func (self *JSRepl) setIndent() {
open := strings.Count(str, "{")
open += strings.Count(str, "(")
closed := strings.Count(str, "}")
closed += strings.Count(str, ")")
indentCount = open - closed
if indentCount <= 0 {
self.prompt = "> "
} else {
self.prompt = strings.Join(make([]string, indentCount*2), "..")
self.prompt += " "
}
}
func (self *JSRepl) read() {
initReadLine()
L:
for {
switch result := readLine(&self.prompt); true {
case result == nil:
break L
case *result != "":
str += *result + "\n"
self.setIndent()
if indentCount <= 0 {
if *result == "exit" {
self.Stop()
break L
}
addHistory(str[:len(str)-1]) //allow user to recall this line
self.parseInput(str)
str = ""
}
}
}
}
func (self *JSRepl) PrintValue(v interface{}) {
method, _ := self.re.vm.Get("prettyPrint")
v, err := self.re.vm.ToValue(v)
if err == nil {
method.Call(method, v)
}
}

1
ethereum/repl_linux.go Symbolic link
View File

@@ -0,0 +1 @@
repl_darwin.go

24
ethereum/repl_windows.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"bufio"
"fmt"
"os"
)
func (self *JSRepl) read() {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf(self.prompt)
str, _, err := reader.ReadLine()
if err != nil {
fmt.Println("Error reading input", err)
} else {
self.parseInput(string(str))
}
}
}
func (self *JSRepl) PrintValue(value otto.Value) {
fmt.Println(value)
}

76
utils/cmd.go Normal file
View File

@@ -0,0 +1,76 @@
package utils
import (
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethminer"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"time"
)
func DoRpc(ethereum *eth.Ethereum, RpcPort int) {
var err error
ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort)
if err != nil {
ethutil.Config.Log.Infoln("Could not start RPC interface:", err)
} else {
go ethereum.RpcServer.Start()
}
}
var miner ethminer.Miner
func DoMining(ethereum *eth.Ethereum) {
// Set Mining status
ethereum.Mining = true
if ethutil.GetKeyRing().Len() == 0 {
ethutil.Config.Log.Infoln("No address found, can't start mining")
return
}
keyPair := ethutil.GetKeyRing().Get(0)
addr := keyPair.Address()
go func() {
ethutil.Config.Log.Infoln("Miner started")
miner = ethminer.NewDefaultMiner(addr, ethereum)
// Give it some time to connect with peers
time.Sleep(3 * time.Second)
for ethereum.IsUpToDate() == false {
time.Sleep(5 * time.Second)
}
ethutil.Config.Log.Infoln("Miner started")
miner := ethminer.NewDefaultMiner(addr, ethereum)
miner.Start()
}()
}
func StopMining(ethereum *eth.Ethereum) bool {
if ethereum.Mining {
miner.Stop()
ethutil.Config.Log.Infoln("Miner stopped")
ethereum.Mining = false
return true
}
return false
}
func StartMining(ethereum *eth.Ethereum) bool {
if !ethereum.Mining {
DoMining(ethereum)
return true
}
return false
}

View File

@@ -24,7 +24,7 @@ func Compile(script string) ([]byte, error) {
func CompileScript(script string) ([]byte, []byte, error) { func CompileScript(script string) ([]byte, []byte, error) {
// Preprocess // Preprocess
mainInput, initInput := mutan.PreProcess(script) mainInput, initInput := mutan.PreParse(script)
// Compile main script // Compile main script
mainScript, err := Compile(mainInput) mainScript, err := Compile(mainInput)
if err != nil { if err != nil {

View File

@@ -6,6 +6,60 @@ import (
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
) )
func CreateKeyPair(force bool) {
if force {
ethutil.GetKeyRing().Reset()
fmt.Println("resetting")
}
if ethutil.GetKeyRing().Get(0) == nil {
_, prv := secp256k1.GenerateKeyPair()
keyPair, err := ethutil.GetKeyRing().NewKeyPair(prv)
if err != nil {
panic(err)
}
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
fmt.Printf(`
Generating new address and keypair.
Please keep your keys somewhere save.
++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x
prvk: %x
pubk: %x
++++++++++++++++++++++++++++++++++++++++++++
save these words so you can restore your account later: %s
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey, mne)
}
}
func ImportPrivateKey(sec string) {
ethutil.GetKeyRing().Reset()
keyPair, err := ethutil.GetKeyRing().NewKeyPair(ethutil.FromHex(sec))
if err != nil {
panic(err)
}
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
fmt.Printf(`
Generating new address and keypair.
Please keep your keys somewhere save.
++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x
prvk: %x
pubk: %x
++++++++++++++++++++++++++++++++++++++++++++
save these words so you can restore your account later: %s
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey, mne)
}
/*
func CreateKeyPair(force bool) { func CreateKeyPair(force bool) {
data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
if len(data) == 0 || force { if len(data) == 0 || force {
@@ -28,7 +82,9 @@ save these words so you can restore your account later: %s
} }
} }
*/
/*
func ImportPrivateKey(prvKey string) { func ImportPrivateKey(prvKey string) {
key := ethutil.FromHex(prvKey) key := ethutil.FromHex(prvKey)
msg := []byte("tmp") msg := []byte("tmp")
@@ -49,3 +105,4 @@ pubk: %x
`, pair.Address(), key, pub) `, pair.Address(), key, pub)
} }
*/