Compare commits

...

111 Commits

Author SHA1 Message Date
obscuren
aa8a86f0a6 Merge branch 'release/poc5-rc12' 2014-06-09 22:24:24 +02:00
obscuren
d929c63474 bump 2014-06-09 22:23:33 +02:00
obscuren
ba3623d0cc Fixed debugger hang 2014-06-09 22:04:16 +02:00
obscuren
cc20b0e3a0 debugger output 2014-06-06 12:13:13 +02:00
obscuren
a107a5db05 Merge branch 'develop' of github.com-obscure:ethereum/go-ethereum into develop 2014-06-05 09:01:10 +02:00
obscuren
964587b14a Added more debugger output 2014-06-05 09:00:57 +02:00
Maran
7843390ecd Implement getStateKeyVal for JS bindings.
Gives JS the option to 'loop' over contract key/val storage
2014-06-04 15:54:33 +02:00
Maran
307fe4a3cd Add loading of extra build in js files to JS-Repl. Implements #67 2014-06-04 12:19:50 +02:00
Maran
3755616a29 Added namereg register option to qml wallet 2014-06-03 14:30:26 +02:00
Maran
cc1d043423 Implemented transaction catching up. Implements #73 2014-06-03 11:48:44 +02:00
Maran
9e411d785b Tweaks and latency added to peeroverview 2014-06-03 10:42:36 +02:00
Maran
a6f4eef1da Added Peer Window 2014-06-02 15:16:37 +02:00
obscuren
98811f11e5 ethereum instead of ethereal. Fixes #69 2014-05-31 11:43:08 +02:00
obscuren
d6acb74ac9 fixed logging issue that would otherwise crash the client. Fixes #68 2014-05-31 11:34:37 +02:00
obscuren
397e99fcc6 Merge branch 'develop' 2014-05-30 20:35:44 +02:00
obscuren
be27309dbb show first? 2014-05-30 20:35:37 +02:00
obscuren
4dfce5d347 Merge branch 'develop' 2014-05-30 19:36:21 +02:00
obscuren
0bdb0a9d58 Added ini file for ethereum. fixes #66 2014-05-30 19:36:05 +02:00
obscuren
5f28013f79 Merge branch 'develop' 2014-05-30 16:58:17 +02:00
obscuren
65c5a20e1c Added config file setup. Fixes #59
* Also fixes asset path problems
2014-05-30 16:56:56 +02:00
obscuren
1020d7ff67 Unified the contract interface and tx interface. Fixes #62 2014-05-30 16:14:46 +02:00
obscuren
e7c9b86a5a Improved UI
* Added mining button
2014-05-30 13:28:31 +02:00
obscuren
0938b56829 Update peer info 2014-05-30 13:04:23 +02:00
obscuren
fcbf99a30a Minor GUI updates
* IceCream => IceCREAM
* Added coin base to block info
2014-05-30 11:50:30 +02:00
obscuren
40d72ff40b . 2014-05-29 12:24:56 +02:00
obscuren
efadfbfb17 Minor UI changes
* Moved log from block view
* Prepend instead of append for logs
2014-05-29 12:24:14 +02:00
obscuren
8ee6574d12 Minimal fee for sending transactions 2014-05-29 12:14:25 +02:00
obscuren
efb3ee044b Removed regexp for namereg 2014-05-29 11:49:59 +02:00
obscuren
0b4c42d756 Disabled instruction selection 2014-05-29 02:05:06 +02:00
obscuren
f0f205004c Merge branch 'develop' 2014-05-28 23:23:52 +02:00
obscuren
f802e17626 merge 2014-05-28 23:16:15 +02:00
obscuren
8fab7ce37d Fixes and improved debugger 2014-05-28 23:14:23 +02:00
Maran
44db1a1eb2 Add 0x when feeding data to debugger 2014-05-28 18:11:27 +02:00
Maran
0aee830bde Fix merge conflict 2014-05-28 16:20:36 +02:00
Maran
06d41794f9 Merge branch 'release/poc5-rc11' into develop 2014-05-28 16:20:05 +02:00
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
obscuren
090447c664 Data and script in the debugger accept "0x" values and regular scripting 2014-05-28 12:59:10 +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
26 changed files with 2046 additions and 879 deletions

View File

@@ -5,7 +5,7 @@ Ethereum
Ethereum Go Client © 2014 Jeffrey Wilcke.
Current state: Proof of Concept 5.0 RC6.
Current state: Proof of Concept 5.0 RC12.
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
@@ -26,28 +26,27 @@ General command line options
====================
```
-c Launch the developer console (node only)
-m Start mining blocks
Shared between ethereum and ethereal
-id Set the custom identifier of the client (shows up on other clients)
-port Port on which the server will accept incomming connections
-upnp Enable UPnP
-maxpeer Desired amount of peers
-rpc Start JSON RPC
-dir Data directory used to store configs and databases
-import Import a private key
-genaddr Generates a new address and private key (destructive action)
-p Port on which the server will accept incomming connections (= 30303)
-upnp Enable UPnP (= false)
-x Desired amount of peers (= 5)
-h This help
-r Start JSON RPC
-dir Data directory used to store configs and databases (=".ethereum")
-import Import a private key (hex)
```
-h This
Developer console commands
==========================
Ethereum only
ethereum [options] [filename]
-js Start the JavaScript REPL
filename Load the given file and interpret as JavaScript
-m Start mining blocks
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
============

View File

@@ -0,0 +1,264 @@
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: 150
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
}
}
Rectangle {
height: 100
width: parent.width
TableView {
id: storageTableView
property var memModel: ListModel {
id: storageModel
}
height: parent.height
width: parent.width
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2}
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2}
model: storageModel
}
}
Rectangle {
height: 200
width: parent.width
TableView {
id: logTableView
property var logModel: ListModel {
id: logModel
}
height: parent.height
width: parent.width
TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width }
model: logModel
}
}
}
}
}
}
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})
}
function setLog(msg) {
logModel.insert(0, {message: msg})
}
function clearLog() {
logModel.clear()
}
}

View File

@@ -32,6 +32,10 @@ window.eth = {
postData({call: "getStorage", args: [address, storageAddress]}, cb);
},
getStateKeyVals: function(address, cb){
postData({call: "getStateKeyVals", args: [address]}, cb);
},
getKey: function(cb) {
postData({call: "getKey"}, cb);
},

BIN
ethereal/assets/heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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: 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
}
}
}
}

View File

@@ -1,112 +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
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

@@ -6,9 +6,12 @@ import QtQuick.Window 2.1;
import QtQuick.Controls.Styles 1.1
import Ethereum 1.0
ApplicationWindow {
id: root
property alias miningButtonText: miningButton.text
width: 900
height: 600
minimumHeight: 300
@@ -23,10 +26,13 @@ ApplicationWindow {
shortcut: "Ctrl+o"
onTriggered: openAppDialog.open()
}
}
Menu {
MenuItem {
text: "Muted"
shortcut: "Ctrl+e"
onTriggered: ui.muted("")
text: "Debugger"
shortcut: "Ctrl+d"
onTriggered: ui.startDebugger()
}
}
@@ -39,10 +45,12 @@ ApplicationWindow {
addPeerWin.visible = true
}
}
MenuItem {
text: "Start"
onTriggered: ui.connect()
text: "Show Peers"
shortcut: "Ctrl+e"
onTriggered: {
peerWindow.visible = true
}
}
}
@@ -67,6 +75,7 @@ ApplicationWindow {
networkView.visible = false
historyView.visible = false
newTxView.visible = false
infoView.visible = false
view.visible = true
//root.title = "Ethereal - " = view.title
}
@@ -84,7 +93,6 @@ ApplicationWindow {
//color: "#D9DDE7"
color: "#252525"
ColumnLayout {
y: 50
anchors.left: parent.left
@@ -120,6 +128,17 @@ ApplicationWindow {
}
}
}
Image {
source: ui.assetPath("heart.png")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onClicked: {
setView(infoView)
}
}
}
}
}
@@ -143,6 +162,7 @@ ApplicationWindow {
TableView {
id: txTableView
anchors.fill: parent
TableViewColumn{ role: "inout" ; title: "" ; width: 40 }
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
@@ -157,6 +177,7 @@ ApplicationWindow {
visible: false
anchors.fill: parent
color: "#00000000"
/*
TabView{
anchors.fill: parent
anchors.rightMargin: 5
@@ -165,16 +186,14 @@ ApplicationWindow {
anchors.bottomMargin: 5
id: newTransactionTab
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("Contracts", newContract)
}
}
*/
Component.onCompleted: {
newContract.createObject(newTxView)
}
}
Rectangle {
@@ -187,29 +206,100 @@ ApplicationWindow {
id: blockTable
width: parent.width
anchors.top: parent.top
anchors.bottom: logView.top
anchors.bottom: parent.bottom
TableViewColumn{ role: "number" ; title: "#" ; width: 100 }
TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 }
TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 }
model: blockModel
/*
onDoubleClicked: {
popup.visible = true
popup.block = eth.getBlock(blockModel.get(row).hash)
popup.hashLabel.text = popup.block.hash
}
*/
onDoubleClicked: {
popup.visible = true
popup.setDetails(blockModel.get(row))
}
}
}
Rectangle {
id: infoView
property var title: "Information"
visible: false
color: "#00000000"
anchors.fill: parent
Column {
spacing: 3
anchors.fill: parent
anchors.topMargin: 5
anchors.leftMargin: 5
Label {
id: addressLabel
text: "Address"
}
TextField {
text: pub.getKey().address
width: 500
}
Label {
text: "Client ID"
}
TextField {
text: eth.clientId()
onTextChanged: {
eth.changeClientId(text)
}
}
}
property var addressModel: ListModel {
id: addressModel
}
TableView {
id: addressView
width: parent.width - 200
height: 200
anchors.bottom: logView.top
TableViewColumn{ role: "name"; title: "name" }
TableViewColumn{ role: "address"; title: "address"; width: 300}
model: addressModel
}
Rectangle {
anchors.top: addressView.top
anchors.left: addressView.right
anchors.leftMargin: 20
TextField {
placeholderText: "Name to register"
id: nameToReg
width: 150
}
Button {
anchors.top: nameToReg.bottom
text: "Register"
MouseArea{
anchors.fill: parent
onClicked: {
eth.registerName(nameToReg.text)
nameToReg.text = ""
}
}
}
}
property var logModel: ListModel {
id: logModel
}
TableView {
id: logView
width: parent.width
height: 150
height: 200
anchors.bottom: parent.bottom
TableViewColumn{ role: "description" ; title: "log" }
@@ -246,29 +336,34 @@ ApplicationWindow {
title: "Open QML Application"
onAccepted: {
//ui.open(openAppDialog.fileUrl.toString())
//ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html")))
ui.openHtml(openAppDialog.fileUrl.toString())
//ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html")))
ui.openHtml(openAppDialog.fileUrl.toString())
}
}
statusBar: StatusBar {
height: 30
RowLayout {
anchors.fill: parent
Button {
id: miningButton
onClicked: {
eth.toggleMining()
}
text: "Start Mining"
}
Button {
property var enabled: true
id: connectButton
id: debuggerWindow
onClicked: {
if(this.enabled) {
ui.connect(this)
}
ui.startDebugger()
}
text: "Connect"
text: "Debugger"
}
Button {
id: importAppButton
anchors.left: connectButton.right
anchors.left: debuggerWindow.right
anchors.leftMargin: 5
onClicked: openAppDialog.open()
text: "Import App"
@@ -279,20 +374,26 @@ ApplicationWindow {
anchors.leftMargin: 5
id: walletValueLabel
}
}
Label {
anchors.right: peerImage.left
anchors.rightMargin: 5
id: peerLabel
font.pixelSize: 8
text: "0 / 0"
}
Image {
id: peerImage
anchors.right: parent.right
width: 10; height: 10
source: ui.assetPath("network.png")
Label {
y: 7
anchors.right: peerImage.left
anchors.rightMargin: 5
id: peerLabel
font.pixelSize: 8
text: "0 / 0"
}
Image {
y: 7
id: peerImage
anchors.right: parent.right
width: 10; height: 10
MouseArea {
onDoubleClicked: peerWindow.visible = true
anchors.fill: parent
}
source: ui.assetPath("network.png")
}
}
@@ -300,10 +401,133 @@ ApplicationWindow {
id: popup
visible: false
property var block
Label {
id: hashLabel
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: root.width
height: 300
Component{
id: blockDetailsDelegate
Rectangle {
color: "#252525"
width: popup.width
height: 150
Column {
anchors.leftMargin: 10
anchors.topMargin: 5
anchors.top: parent.top
anchors.left: parent.left
Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
Text { text: '<b>Block number:</b> ' + number; color: "#F2F2F2"}
Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
Text { text: '<b>Coinbase:</b> ' + coinbase; color: "#F2F2F2"}
Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
}
}
}
ListView {
model: singleBlock
delegate: blockDetailsDelegate
anchors.top: parent.top
height: 100
anchors.leftMargin: 20
id: listViewThing
Layout.maximumHeight: 40
}
TableView {
id: txView
anchors.top: listViewThing.bottom
anchors.topMargin: 50
width: parent.width
TableViewColumn{width: 90; role: "value" ; title: "Value" }
TableViewColumn{width: 200; role: "hash" ; title: "Hash" }
TableViewColumn{width: 200; role: "sender" ; title: "Sender" }
TableViewColumn{width: 200;role: "address" ; title: "Receiver" }
TableViewColumn{width: 60; role: "gas" ; title: "Gas" }
TableViewColumn{width: 60; role: "gasPrice" ; title: "Gas Price" }
TableViewColumn{width: 60; role: "isContract" ; title: "Contract" }
model: transactionModel
onClicked: {
var tx = transactionModel.get(row)
if(tx.data) {
popup.showContractData(tx)
}else{
popup.height = 440
}
}
}
function showContractData(tx) {
txDetailsDebugButton.tx = tx
if(tx.createsContract) {
contractData.text = tx.data
contractLabel.text = "<h4> Transaction created contract " + tx.address + "</h4>"
}else{
contractLabel.text = "<h4> Transaction ran contract " + tx.address + "</h4>"
contractData.text = tx.rawData
}
popup.height = 540
}
Rectangle {
id: txDetails
width: popup.width
height: 300
anchors.left: listViewThing.left
anchors.top: txView.bottom
Label {
text: "<h4>Contract data</h4>"
anchors.top: parent.top
anchors.left: parent.left
id: contractLabel
anchors.leftMargin: 10
}
Button {
property var tx
id: txDetailsDebugButton
anchors.right: parent.right
anchors.rightMargin: 10
anchors.top: parent.top
anchors.topMargin: 10
text: "Debug contract"
onClicked: {
if(tx.createsContract){
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 = 300
transactionModel.clear()
if(block.txs != undefined){
for(var i = 0; i < block.txs.count; ++i) {
transactionModel.insert(0, block.txs.get(i))
}
if(block.txs.get(0).data){
popup.showContractData(block.txs.get(0))
}
}
txView.forceActiveFocus()
}
}
@@ -365,115 +589,19 @@ ApplicationWindow {
anchors.left: aboutIcon.right
anchors.leftMargin: 10
font.pointSize: 12
text: "<h2>Ethereal</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br><h3>Binary Distribution</h3>Jarrad Hope<br>"
}
}
Window {
id: debugWindow
visible: false
title: "Debugger"
minimumWidth: 600
minimumHeight: 600
width: 800
height: 600
Item {
id: keyHandler
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_Space) {
ui.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 {
anchors.left: asmTableView.right
anchors.right: parent.right
SplitView {
orientation: Qt.Vertical
anchors.fill: parent
TableView {
property var memModel: ListModel {
id: memModel
}
height: parent.height/2
width: parent.width
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50}
TableViewColumn{ role: "value" ; title: "Memory" ; width: 750}
model: memModel
}
SplitView {
orientation: Qt.Horizontal
TableView {
property var debuggerLog: ListModel {
id: debuggerLog
}
TableViewColumn{ role: "value"; title: "Debug messages" }
model: debuggerLog
}
TableView {
property var stackModel: ListModel {
id: stackModel
}
height: parent.height/2
width: parent.width
TableViewColumn{ role: "value" ; title: "Stack" ; width: parent.width }
model: stackModel
}
}
}
}
text: "<h2>Ethereal</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>"
}
}
function setAsm(asm) {
asmModel.append({asm: asm})
}
function setInstruction(num) {
asmTableView.selection.clear()
asmTableView.selection.select(num-1)
}
function clearAsm() {
asmModel.clear()
}
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){
console.log("WOOP:")
debuggerLog.append({value: message})
}
function clearStack() {
stackModel.clear()
function addAddress(address) {
addressModel.append({name: address.name, address: address.address})
}
function clearAddress() {
addressModel.clear()
}
function loadPlugin(name) {
@@ -485,27 +613,379 @@ ApplicationWindow {
walletValueLabel.text = value
}
function addTx(tx) {
function addTx(tx, inout) {
var isContract
if (tx.contract == true){
isContract = "Yes"
}else{
isContract = "No"
}
txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value, contract: isContract})
var address;
if(inout == "recv") {
address = tx.sender;
} else {
address = tx.address;
}
txModel.insert(0, {inout: inout, hash: tx.hash, address: address, value: tx.value, contract: isContract})
}
function addBlock(block) {
blockModel.insert(0, {number: block.number, hash: block.hash})
function addBlock(block, initial) {
var txs = JSON.parse(block.transactions);
var amount = 0
if(initial == undefined){
initial = false
}
if(txs != null){
amount = txs.length
}
if(initial){
blockModel.append({number: block.number, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
}else{
blockModel.insert(0, {number: block.number, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
}
}
function addLog(str) {
if(str.len != 0) {
logModel.append({description: str})
logModel.insert(0, {description: str})
}
}
function setPeers(text) {
peerLabel.text = text
}
function addPeer(peer) {
// We could just append the whole peer object but it cries if you try to alter them
peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version})
}
function resetPeers(){
peerModel.clear()
}
function timeAgo(unixTs){
var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000
return (lapsed + " seconds ago")
}
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;
}
// ******************************************
// Windows
// ******************************************
Window {
id: peerWindow
height: 200
width: 700
Rectangle {
anchors.fill: parent
property var peerModel: ListModel {
id: peerModel
}
TableView {
anchors.fill: parent
id: peerTable
model: peerModel
TableViewColumn{width: 100; role: "ip" ; title: "IP" }
TableViewColumn{width: 60; role: "port" ; title: "Port" }
TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
TableViewColumn{width: 100; role: "latency"; title: "Latency" }
TableViewColumn{width: 260; role: "version" ; title: "Version" }
}
}
}
// *******************************************
// Components
// *******************************************
// New Contract component
Component {
id: newContract
Column {
id: mainContractColumn
anchors.fill: parent
function contractFormReady(){
if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) {
txButton.state = "READY"
}else{
txButton.state = "NOTREADY"
}
}
states: [
State{
name: "ERROR"
PropertyChanges { target: txResult; visible:true}
PropertyChanges { target: codeView; visible:true}
},
State {
name: "DONE"
PropertyChanges { target: txValue; visible:false}
PropertyChanges { target: txGas; visible:false}
PropertyChanges { target: txGasPrice; visible:false}
PropertyChanges { target: codeView; visible:false}
PropertyChanges { target: txButton; visible:false}
PropertyChanges { target: txDataLabel; visible:false}
PropertyChanges { target: atLabel; visible:false}
PropertyChanges { target: txFuelRecipient; visible:false}
PropertyChanges { target: 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: txFuelRecipient
placeholderText: "Address / Name or empty for contract"
//validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
width: 400
}
TextField {
id: txValue
width: 222
placeholderText: "Amount"
validator: RegExpValidator { regExp: /\d*/ }
onTextChanged: {
contractFormReady()
}
}
RowLayout {
TextField {
id: txGas
width: 50
validator: RegExpValidator { regExp: /\d*/ }
placeholderText: "Gas"
text: "500"
/*
onTextChanged: {
contractFormReady()
}
*/
}
Label {
id: atLabel
text: "@"
}
TextField {
id: txGasPrice
width: 200
placeholderText: "Gas price"
text: "1000000"
validator: RegExpValidator { regExp: /\d*/ }
/*
onTextChanged: {
contractFormReady()
}
*/
}
}
Label {
id: txDataLabel
text: "Data"
}
TextArea {
id: codeView
height: 300
anchors.topMargin: 5
width: 400
onTextChanged: {
contractFormReady()
}
}
Button {
id: txButton
/* enabled: false */
states: [
State {
name: "READY"
PropertyChanges { target: txButton; /*enabled: true*/}
},
State {
name: "NOTREADY"
PropertyChanges { target: txButton; /*enabled:false*/}
}
]
text: "Send"
onClicked: {
//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 a new transaction"
onClicked: {
this.visible = false
txResult.text = ""
txOutput.text = ""
mainContractColumn.state = "SETUP"
}
}
}
}
// 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, "500", "1000000", "")
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

@@ -34,7 +34,6 @@ ApplicationWindow {
top: parent.top
}
*/
onTitleChanged: { window.title = title }
experimental.preferences.javascriptEnabled: true
experimental.preferences.navigatorQtObjectEnabled: true
@@ -97,6 +96,12 @@ ApplicationWindow {
var storage = stateObject.getStorage(data.args[1])
postData(data._seed, storage)
break
case "getStateKeyVals":
require(1);
var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true)
postData(data._seed,stateObject)
break
case "getBalance":
require(1);
@@ -188,7 +193,7 @@ ApplicationWindow {
WebView {
id: inspector
visible: false
visible: true
url: webview.experimental.remoteInspectorUrl
anchors {
left: root.left

View File

@@ -9,7 +9,7 @@
<script type="text/javascript">
var jefcoinAddr = "681fd48ffa236549fbcd16bdf9f98bb541a7f742"
var jefcoinAddr = "de0bd4ea1947deabf1749d7ed633f289358c9f6c"
var mAddr = ""
function createTransaction() {

View File

@@ -4,8 +4,9 @@ import (
"flag"
)
var StartConsole bool
var StartMining bool
var Identifier string
//var StartMining bool
var StartRpc bool
var RpcPort int
var UseUPnP bool
@@ -17,24 +18,22 @@ var GenAddr bool
var UseSeed bool
var ImportKey string
var ExportKey bool
var DataDir string
var AssetPath string
func Init() {
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
flag.BoolVar(&StartRpc, "r", false, "start rpc server")
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory")
flag.BoolVar(&ShowGenesis, "genesis", false, "prints genesis header and exits")
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub 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(&DataDir, "dir", ".ethereal", "ethereum data directory")
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory")
flag.Parse()
}

View File

@@ -8,9 +8,11 @@ import (
"github.com/ethereum/go-ethereum/ethereal/ui"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"github.com/rakyll/globalconf"
"log"
"os"
"os/signal"
"path"
"runtime"
)
@@ -39,7 +41,16 @@ func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
ethchain.InitFees()
ethutil.ReadConfig(DataDir)
g, err := globalconf.NewWithOptions(&globalconf.Options{
Filename: path.Join(ethutil.ApplicationFolder(".ethereal"), "conf.ini"),
})
if err != nil {
fmt.Println(err)
} else {
g.ParseAll()
}
ethutil.ReadConfig(".ethereal", ethutil.LogFile|ethutil.LogStd, g, Identifier)
// Instantiated a eth stack
ethereum, err := eth.New(eth.CapDefault, UseUPnP)
@@ -108,9 +119,11 @@ save these words so you can restore your account later: %s
os.Exit(0)
}
if StartMining {
utils.DoMining(ethereum)
}
/*
if StartMining {
utils.DoMining(ethereum)
}
*/
if StartRpc {
utils.DoRpc(ethereum, RpcPort)

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

@@ -0,0 +1,225 @@
package ethui
import (
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"math/big"
"strings"
)
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, false}
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 {
self.Logf("compile FAULT: %v", r)
}
}()
data := ethutil.StringToByteFunc(dataStr, func(s string) (ret []byte) {
slice := strings.Split(dataStr, "\n")
for _, dataItem := range slice {
d := ethutil.FormatData(dataItem)
ret = append(ret, d...)
}
return
})
var err error
script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
ret, err = ethutil.Compile(s)
fmt.Printf("%x\n", ret)
return
})
if err != nil {
self.Logln(err)
return
}
dis := ethchain.Disassemble(script)
self.win.Root().Call("clearAsm")
self.win.Root().Call("clearLog")
for _, str := range dis {
self.win.Root().Call("setAsm", str)
}
gas := ethutil.Big(gasStr)
gasPrice := ethutil.Big(gasPriceStr)
// Contract addr as test address
keyPair := ethutil.GetKeyRing().Get(0)
callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), gas, gasPrice, 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, gas, gasPrice)
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,
Value: ethutil.Big(valueStr),
})
self.Db.done = false
self.Logf("callsize %d", len(script))
go func() {
ret, g, err := callerClosure.Call(vm, data, self.Db.halting)
tot := new(big.Int).Mul(g, gasPrice)
self.Logf("gas usage %v total price = %v (%v)", g, tot, ethutil.CurrencyToString(tot))
if err != nil {
self.Logln("exited with errors:", err)
} else {
if len(ret) > 0 {
self.Logf("exited: % x", ret)
} else {
self.Logf("exited: nil")
}
}
state.Reset()
if !self.Db.interrupt {
self.Db.done = true
} else {
self.Db.interrupt = false
}
}()
}
func (self *DebuggerWindow) Logf(format string, v ...interface{}) {
self.win.Root().Call("setLog", fmt.Sprintf(format, v...))
}
func (self *DebuggerWindow) Logln(v ...interface{}) {
str := fmt.Sprintln(v...)
self.Logf("%s", str[:len(str)-1])
}
func (self *DebuggerWindow) Next() {
self.Db.Next()
}
type Debugger struct {
win *qml.Window
N chan bool
Q chan bool
done, interrupt 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.interrupt = true
d.clearBuffers()
return false
}
}
return true
}
func (d *Debugger) clearBuffers() {
out:
// drain
for {
select {
case <-d.N:
case <-d.Q:
default:
break out
}
}
}
func (d *Debugger) Next() {
if !d.done {
d.N <- true
}
}

View File

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

View File

@@ -8,10 +8,11 @@ import (
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"github.com/obscuren/mutan"
"math/big"
"strings"
"time"
)
type Gui struct {
@@ -55,7 +56,7 @@ func New(ethereum *eth.Ethereum) *Gui {
}
func (gui *Gui) Start(assetPath string) {
const version = "0.5.0 RC6"
const version = "0.5.0 RC12"
defer gui.txDb.Close()
@@ -64,16 +65,19 @@ func (gui *Gui) Start(assetPath string) {
Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
}, {
Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
}, {
Init: func(p *ethpub.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" },
}})
ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", version))
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
// Create a new QML engine
gui.engine = qml.NewEngine()
context := gui.engine.Context()
// Expose the eth library and the ui library to QML
context.SetVar("eth", gui)
context.SetVar("pub", gui.pub)
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
context.SetVar("ui", gui.uiLib)
@@ -86,19 +90,36 @@ func (gui *Gui) Start(assetPath string) {
win, err = gui.showKeyImport(context)
} else {
win, err = gui.showWallet(context)
ethutil.Config.Log.AddLogSystem(gui)
}
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'", err)
panic(err)
}
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
win.Show()
win.Wait()
gui.eth.Stop()
}
func (gui *Gui) ToggleMining() {
var txt string
if gui.eth.Mining {
utils.StopMining(gui.eth)
txt = "Start mining"
} else {
utils.StartMining(gui.eth)
txt = "Stop mining"
}
gui.win.Root().Set("miningButtonText", txt)
}
func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
if err != nil {
@@ -107,8 +128,11 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
win := gui.createWindow(component)
go gui.setInitialBlockChain()
go gui.readPreviousTransactions()
gui.setInitialBlockChain()
gui.loadAddressBook()
gui.readPreviousTransactions()
gui.setPeerInfo()
go gui.update()
return win, nil
@@ -130,20 +154,46 @@ func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
gui.win = win
gui.uiLib.win = win
db := &Debugger{gui.win, make(chan bool)}
db := &Debugger{gui.win, make(chan bool), make(chan bool), true, false}
gui.lib.Db = db
gui.uiLib.Db = db
return gui.win
}
func (gui *Gui) setInitialBlockChain() {
// Load previous 10 blocks
chain := gui.eth.BlockChain().GetChain(gui.eth.BlockChain().CurrentBlock.Hash(), 10)
for _, block := range chain {
gui.processBlock(block)
}
sBlk := gui.eth.BlockChain().LastBlockHash
blk := gui.eth.BlockChain().GetBlock(sBlk)
for ; blk != nil; blk = gui.eth.BlockChain().GetBlock(sBlk) {
sBlk = blk.PrevHash
// Loop through all transactions to see if we missed any while being offline
for _, tx := range blk.Transactions() {
if bytes.Compare(tx.Sender(), gui.addr) == 0 || bytes.Compare(tx.Recipient, gui.addr) == 0 {
if ok, _ := gui.txDb.Get(tx.Hash()); ok == nil {
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
}
}
gui.processBlock(blk, true)
}
}
type address struct {
Name, Address string
}
var namereg = ethutil.FromHex("bb5f186604d057c1c5240ca2ae0f6430138ac010")
func (gui *Gui) loadAddressBook() {
gui.win.Root().Call("clearAddress")
stateObject := gui.eth.StateManager().CurrentState().GetStateObject(namereg)
if stateObject != nil {
stateObject.State().EachStorage(func(name string, value *ethutil.Value) {
gui.win.Root().Call("addAddress", struct{ Name, Address string }{name, ethutil.Hex(value.Bytes())})
})
}
}
func (gui *Gui) readPreviousTransactions() {
@@ -151,20 +201,28 @@ func (gui *Gui) readPreviousTransactions() {
for it.Next() {
tx := ethchain.NewTransactionFromBytes(it.Value())
gui.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()
}
func (gui *Gui) processBlock(block *ethchain.Block) {
gui.win.Root().Call("addBlock", ethpub.NewPBlock(block))
func (gui *Gui) processBlock(block *ethchain.Block, initial bool) {
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 {
if unconfirmedFunds.Cmp(big.NewInt(0)) < 0 {
pos = "-"
}
val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
@@ -178,13 +236,20 @@ func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) {
// Simple go routine function that updates the list of peers in the GUI
func (gui *Gui) update() {
blockChan := make(chan ethutil.React, 1)
reactor := gui.eth.Reactor()
reactor.Subscribe("newBlock", blockChan)
blockChan := make(chan ethutil.React, 1)
txChan := make(chan ethutil.React, 1)
objectChan := make(chan ethutil.React, 1)
peerChan := make(chan ethutil.React, 1)
txChan := make(chan ethchain.TxMsg, 1)
gui.eth.TxPool().Subscribe(txChan)
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("newTx:pre", txChan)
reactor.Subscribe("newTx:post", txChan)
reactor.Subscribe("object:"+string(namereg), objectChan)
reactor.Subscribe("peerList", peerChan)
ticker := time.NewTicker(5 * time.Second)
state := gui.eth.StateManager().TransState()
@@ -195,26 +260,24 @@ func (gui *Gui) update() {
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().ProcState().GetAccount(gui.addr).Amount, nil)
gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.addr).Amount, nil)
}
case txMsg := <-txChan:
tx := txMsg.Tx
tx := txMsg.Resource.(*ethchain.Transaction)
if txMsg.Type == ethchain.TxPre {
if txMsg.Event == "newTx:pre" {
object := state.GetAccount(gui.addr)
if bytes.Compare(tx.Sender(), gui.addr) == 0 && object.Nonce <= tx.Nonce {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx))
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
object.Nonce += 1
state.SetStateObject(object)
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx))
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
@@ -231,12 +294,27 @@ func (gui *Gui) update() {
gui.setWalletValue(object.Amount, nil)
state.SetStateObject(object)
state.UpdateStateObject(object)
}
case <-objectChan:
gui.loadAddressBook()
case <-peerChan:
gui.setPeerInfo()
case <-ticker.C:
gui.setPeerInfo()
}
}
}
func (gui *Gui) setPeerInfo() {
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
gui.win.Root().Call("resetPeers")
for _, peer := range gui.pub.GetPeers() {
gui.win.Root().Call("addPeer", peer)
}
}
// Logging functions that log directly to the GUI interface
func (gui *Gui) Println(v ...interface{}) {
str := strings.TrimRight(fmt.Sprintln(v...), "\n")
@@ -253,6 +331,11 @@ func (gui *Gui) Printf(format string, v ...interface{}) {
gui.win.Root().Call("addLog", line)
}
}
func (gui *Gui) RegisterName(name string) {
keyPair := ethutil.GetKeyRing().Get(0)
name = fmt.Sprintf("\"%s\"\n1", name)
gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), "namereg", "1000", "1000000", "150", name)
}
func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
keyPair := ethutil.GetKeyRing().Get(0)
@@ -263,7 +346,13 @@ func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.
func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
keyPair := ethutil.GetKeyRing().Get(0)
mainInput, initInput := mutan.PreParse(data)
return gui.pub.Create(ethutil.Hex(keyPair.PrivateKey), value, gas, gasPrice, initInput, mainInput)
return gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), recipient, value, gas, gasPrice, data)
}
func (gui *Gui) ChangeClientId(id string) {
ethutil.Config.SetIdentifier(id)
}
func (gui *Gui) ClientId() string {
return ethutil.Config.Identifier
}

View File

@@ -6,6 +6,12 @@ import (
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"github.com/howeyc/fsnotify"
"io/ioutil"
"log"
"net/url"
"os"
"path"
"path/filepath"
)
@@ -15,6 +21,7 @@ type HtmlApplication struct {
engine *qml.Engine
lib *UiLib
path string
watcher *fsnotify.Watcher
}
func NewHtmlApplication(path string, lib *UiLib) *HtmlApplication {
@@ -47,6 +54,59 @@ func (app *HtmlApplication) Create() error {
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 {
return app.engine
}

View File

@@ -2,13 +2,10 @@ package ethui
import (
"bitbucket.org/kardianos/osext"
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"github.com/obscuren/mutan"
"os"
"path"
"path/filepath"
@@ -27,8 +24,9 @@ type UiLib struct {
connected bool
assetPath string
// The main application window
win *qml.Window
Db *Debugger
win *qml.Window
Db *Debugger
DbWindow *DebuggerWindow
}
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
@@ -91,6 +89,29 @@ func (ui *UiLib) ConnectToPeer(addr string) {
func (ui *UiLib) AssetPath(p string) string {
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("0x" + 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 {
var base string
@@ -121,27 +142,28 @@ func DefaultAssetPath() string {
func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string) {
state := ui.eth.BlockChain().CurrentBlock.State()
mainInput, _ := mutan.PreParse(data)
callerScript, err := utils.Compile(mainInput)
script, err := ethutil.Compile(data)
if err != nil {
ethutil.Config.Log.Debugln(err)
return
}
dis := ethchain.Disassemble(callerScript)
dis := ethchain.Disassemble(script)
ui.win.Root().Call("clearAsm")
for _, str := range dis {
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
keyPair := ethutil.GetKeyRing().Get(0)
callerTx :=
ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), script)
callerTx.Sign(keyPair.PrivateKey)
account := ui.eth.StateManager().TransState().GetStateObject(keyPair.Address())
c := ethchain.MakeContract(callerTx, state)
callerClosure := ethchain.NewClosure(account, c, c.Script(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr))
contract := ethchain.MakeContract(callerTx, state)
callerClosure := ethchain.NewClosure(account, contract, contract.Init(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr))
block := ui.eth.BlockChain().CurrentBlock
vm := ethchain.NewVm(state, ui.eth.StateManager(), ethchain.RuntimeVars{
@@ -151,50 +173,18 @@ func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string)
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
TxData: nil,
})
ui.Db.done = false
go func() {
callerClosure.Call(vm, nil, ui.Db.halting)
callerClosure.Call(vm, contract.Init(), ui.Db.halting)
state.Reset()
ui.Db.done = true
}()
}
func (ui *UiLib) 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,9 +2,11 @@ package main
import (
"flag"
"fmt"
"os"
)
var StartConsole bool
var Identifier string
var StartMining bool
var StartRpc bool
var RpcPort int
@@ -18,26 +20,33 @@ var UseSeed bool
var ImportKey string
var ExportKey bool
var LogFile string
var DataDir string
var NonInteractive bool
var StartJsConsole bool
var InputFile string
func Init() {
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
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.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "%s [options] [filename]:\n", os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
flag.BoolVar(&StartJsConsole, "js", false, "exp")
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
flag.BoolVar(&ExportKey, "export", false, "export private key")
flag.StringVar(&OutboundPort, "p", "30303", "listening port")
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory")
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
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)
keyPair := ethutil.GetKeyRing().Get(0)
tx.Sign(keyPair.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.PreParse(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)
keyPair := ethutil.GetKeyRing().Get(0)
contract.Sign(keyPair.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

@@ -6,25 +6,27 @@ import (
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/rakyll/globalconf"
"io/ioutil"
"log"
"os"
"os/signal"
"path"
"runtime"
"strings"
)
const Debug = true
// Register interrupt handlers so we can stop the ethereum
func RegisterInterrupts(s *eth.Ethereum) {
// Buffered chan of one is enough
c := make(chan os.Signal, 1)
// Notify about interrupts for now
signal.Notify(c, os.Interrupt)
func RegisterInterrupt(cb func(os.Signal)) {
go func() {
// Buffered chan of one is enough
c := make(chan os.Signal, 1)
// Notify about interrupts for now
signal.Notify(c, os.Interrupt)
for sig := range c {
fmt.Printf("Shutting down (%v) ... \n", sig)
s.Stop()
cb(sig)
}
}()
}
@@ -52,7 +54,23 @@ func main() {
var logSys *log.Logger
flags := log.LstdFlags
ethutil.ReadConfig(DataDir)
var lt ethutil.LoggerType
if StartJsConsole || len(InputFile) > 0 {
lt = ethutil.LogFile
} else {
lt = ethutil.LogFile | ethutil.LogStd
}
g, err := globalconf.NewWithOptions(&globalconf.Options{
Filename: path.Join(ethutil.ApplicationFolder(".ethereum"), "conf.ini"),
})
if err != nil {
fmt.Println(err)
} else {
g.ParseAll()
}
ethutil.ReadConfig(".ethereum", lt, g, Identifier)
logger := ethutil.Config.Log
if LogFile != "" {
@@ -136,21 +154,40 @@ save these words so you can restore your account later: %s
utils.DoMining(ethereum)
}
if StartConsole {
err := os.Mkdir(ethutil.Config.ExecPath, os.ModePerm)
// Error is OK if the error is ErrExist
if err != nil && !os.IsExist(err) {
log.Panic("Unable to create EXECPATH:", err)
if StartJsConsole {
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)
}
console := NewConsole(ethereum)
go console.Start()
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 {
utils.DoRpc(ethereum, RpcPort)
}
RegisterInterrupts(ethereum)
RegisterInterrupt(func(sig os.Signal) {
fmt.Printf("Shutting down (%v) ... \n", sig)
ethereum.Stop()
})
ethereum.Start(UseSeed)

View File

@@ -0,0 +1,209 @@
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"
"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 (jsre *JSRE) LoadExtFile(path string) {
result, err := ioutil.ReadFile(path)
if err == nil {
jsre.vm.Run(result)
} else {
ethutil.Config.Log.Debugln("Could not load file:", path)
}
}
func (jsre *JSRE) LoadIntFile(file string) {
assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal", "assets", "ext")
jsre.LoadExtFile(path.Join(assetPath, file))
}
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)
// Load extra javascript files
re.LoadIntFile("string.js")
re.LoadIntFile("big.js")
// 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;
`

118
ethereum/repl.go Normal file
View File

@@ -0,0 +1,118 @@
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) GetPeers() otto.Value {
return self.toVal(self.PEthereum.GetPeers())
}
func (self *JSEthereum) GetKey() otto.Value {
return self.toVal(self.PEthereum.GetKey())
}
func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(self.PEthereum.GetStateObject(addr))
}
func (self *JSEthereum) GetStateKeyVals(addr string) otto.Value {
return self.toVal(self.PEthereum.GetStateObject(addr).StateKeyVal(false))
}
func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
r, err := self.PEthereum.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return self.toVal(r)
}
func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, scriptStr string) otto.Value {
r, err := self.PEthereum.Create(key, valueStr, gasStr, gasPriceStr, scriptStr)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return self.toVal(r)
}
func (self *JSEthereum) toVal(v interface{}) otto.Value {
result, err := self.vm.ToValue(v)
if err != nil {
fmt.Println("Value unknown:", err)
return otto.UndefinedValue()
}
return result
}

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)
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"log"
"time"
)
@@ -14,33 +13,62 @@ func DoRpc(ethereum *eth.Ethereum, RpcPort int) {
var err error
ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort)
if err != nil {
log.Println("Could not start RPC interface:", err)
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 {
log.Println("No address found, can't start mining")
ethutil.Config.Log.Infoln("No address found, can't start mining")
return
}
keyPair := ethutil.GetKeyRing().Get(0)
addr := keyPair.Address()
go func() {
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)
}
log.Println("Miner started")
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
}