Compare commits

..

No commits in common. "v0.6.5" and "poc5-rc10" have entirely different histories.

101 changed files with 3288 additions and 8518 deletions

3
.gitignore vendored
View file

@ -9,6 +9,5 @@
*un~
.DS_Store
*/**/.DS_Store
ethereum/ethereum
ethereal/ethereal
./ethereum/ethereum

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "ethereal/assets/samplecoin"]
path = ethereal/assets/samplecoin
url = git@github.com:obscuren/SampleCoin.git

31
LICENSE
View file

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

View file

@ -1,13 +1,11 @@
Ethereum
========
Master [![Build
Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-master-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-master-docker/builds/-1) Develop [![Build
Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-develop-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-develop-docker/builds/-1)
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
Ethereum Go Client © 2014 Jeffrey Wilcke.
Current state: Proof of Concept 0.6.5.
Current state: Proof of Concept 5.0 RC10.
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
@ -29,22 +27,20 @@ General command line options
```
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
-m Start mining blocks
-genaddr Generates a new address and private key (destructive action)
-p Port on which the server will accept incomming connections
-upnp Enable UPnP
-maxpeer Desired amount of peers
-rpc Start JSON RPC
-x Desired amount of peers
-r 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)
-h This
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

22
ethereal/Makefile Normal file
View file

@ -0,0 +1,22 @@
UNAME = $(shell uname)
FILES=qml *.png
GOPATH=$(PWD)
# Default is building
all:
go get -d
cp *.go $(GOPATH)/src/github.com/ethereum/go-ethereum
cp -r ui $(GOPATH)/src/github.com/ethereum/go-ethereum
go build
install:
# Linux build
ifeq ($(UNAME),Linux)
cp -r assets/* /usr/share/ethereal
cp go-ethereum /usr/local/bin/ethereal
endif
# OS X build
ifeq ($(UNAME),Darwin)
# Execute py script
endif

View file

@ -0,0 +1,215 @@
import QtQuick 2.0
import QtQuick.Controls 1.0;
import QtQuick.Layouts 1.0;
import QtQuick.Dialogs 1.0;
import QtQuick.Window 2.1;
import QtQuick.Controls.Styles 1.1
import Ethereum 1.0
ApplicationWindow {
visible: false
title: "IceCream"
minimumWidth: 1280
minimumHeight: 900
width: 1290
height: 900
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: {
dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text)
}
text: "Debug"
}
Button {
property var enabled: true
id: debugNextButton
onClicked: {
dbg.next()
}
text: "Next"
}
}
}
function setAsm(asm) {
asmModel.append({asm: asm})
}
function clearAsm() {
asmModel.clear()
}
function setInstruction(num) {
asmTableView.selection.clear()
asmTableView.selection.select(num-1)
}
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})
}
}

View file

@ -0,0 +1,138 @@
// Main Ethereum library
window.eth = {
prototype: Object(),
// Retrieve block
//
// Either supply a number or a string. Type is determent for the lookup method
// string - Retrieves the block by looking up the hash
// number - Retrieves the block by looking up the block number
getBlock: function(numberOrHash, cb) {
var func;
if(typeof numberOrHash == "string") {
func = "getBlockByHash";
} else {
func = "getBlockByNumber";
}
postData({call: func, args: [numberOrHash]}, cb);
},
// Create transaction
//
// Transact between two state objects
transact: function(sec, recipient, value, gas, gasPrice, data, cb) {
postData({call: "transact", args: [sec, recipient, value, gas, gasPrice, data]}, cb);
},
create: function(sec, value, gas, gasPrice, init, body, cb) {
postData({call: "create", args: [sec, value, gas, gasPrice, init, body]}, cb);
},
getStorageAt: function(address, storageAddress, cb) {
postData({call: "getStorage", args: [address, storageAddress]}, cb);
},
getKey: function(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) {
postData({call: "getBalance", args: [address]}, cb);
},
getSecretToAddress: function(sec, cb) {
postData({call: "getSecretToAddress", args: [sec]}, cb);
},
watch: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
cb = storageAddrOrCb;
storageAddrOrCb = "";
ev = "object:"+address;
} else {
ev = "storage:"+address+":"+storageAddrOrCb;
}
eth.on(ev, cb)
postData({call: "watch", args: [address, storageAddrOrCb]});
},
disconnect: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
cb = storageAddrOrCb;
storageAddrOrCb = "";
ev = "object:"+address;
} else {
ev = "storage:"+address+":"+storageAddrOrCb;
}
eth.off(ev, cb)
postData({call: "disconnect", args: [address, storageAddrOrCb]});
},
set: function(props) {
postData({call: "set", args: props});
},
on: function(event, cb) {
if(eth._onCallbacks[event] === undefined) {
eth._onCallbacks[event] = [];
}
eth._onCallbacks[event].push(cb);
return this
},
off: function(event, cb) {
if(eth._onCallbacks[event] !== undefined) {
var callbacks = eth._onCallbacks[event];
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] === cb) {
delete callbacks[i];
}
}
}
return this
},
trigger: function(event, data) {
var callbacks = eth._onCallbacks[event];
if(callbacks !== undefined) {
for(var i = 0; i < callbacks.length; i++) {
// Figure out whether the returned data was an array
// array means multiple return arguments (multiple params)
if(data instanceof Array) {
callbacks[i].apply(this, data);
} else {
callbacks[i].call(this, data);
}
}
}
},
}
window.eth._callbacks = {}
window.eth._onCallbacks = {}

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

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before After
Before After

View file

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

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

@ -0,0 +1,35 @@
import QtQuick 2.0
import QtQuick.Controls 1.0;
import QtQuick.Layouts 1.0;
import Ethereum 1.0
ApplicationWindow {
minimumWidth: 500
maximumWidth: 500
maximumHeight: 100
minimumHeight: 100
title: "Ethereum Dice"
TextField {
id: textField
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
placeholderText: "Amount"
}
Label {
id: txHash
anchors.bottom: textField.top
anchors.bottomMargin: 5
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
anchors.top: textField.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 5
text: "Place bet"
onClicked: {
txHash.text = eth.createTx("e6716f9544a56c530d868e4bfbacb172315bdead", textField.text)
}
}
}

View file

@ -0,0 +1,683 @@
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 {
id: root
width: 900
height: 600
minimumHeight: 300
title: "Ethereal"
MenuBar {
Menu {
title: "File"
MenuItem {
text: "Import App"
shortcut: "Ctrl+o"
onTriggered: openAppDialog.open()
}
MenuItem {
text: "Muted"
shortcut: "Ctrl+e"
onTriggered: ui.muted("")
}
}
Menu {
title: "Network"
MenuItem {
text: "Add Peer"
shortcut: "Ctrl+p"
onTriggered: {
addPeerWin.visible = true
}
}
MenuItem {
text: "Start"
onTriggered: ui.connect()
}
}
Menu {
title: "Help"
MenuItem {
text: "About"
onTriggered: {
aboutWin.visible = true
}
}
}
}
property var blockModel: ListModel {
id: blockModel
}
function setView(view) {
networkView.visible = false
historyView.visible = false
newTxView.visible = false
infoView.visible = false
view.visible = true
//root.title = "Ethereal - " = view.title
}
SplitView {
anchors.fill: parent
resizing: false
Rectangle {
id: menu
Layout.minimumWidth: 80
Layout.maximumWidth: 80
anchors.bottom: parent.bottom
anchors.top: parent.top
//color: "#D9DDE7"
color: "#252525"
ColumnLayout {
y: 50
anchors.left: parent.left
anchors.right: parent.right
height: 200
Image {
source: ui.assetPath("tx.png")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onClicked: {
setView(historyView)
}
}
}
Image {
source: ui.assetPath("new.png")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onClicked: {
setView(newTxView)
}
}
}
Image {
source: ui.assetPath("net.png")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onClicked: {
setView(networkView)
}
}
}
Image {
source: ui.assetPath("heart.png")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onClicked: {
setView(infoView)
}
}
}
}
}
Rectangle {
id: mainView
color: "#00000000"
anchors.right: parent.right
anchors.left: menu.right
anchors.bottom: parent.bottom
anchors.top: parent.top
property var txModel: ListModel {
id: txModel
}
Rectangle {
id: historyView
anchors.fill: parent
property var title: "Transactions"
TableView {
id: txTableView
anchors.fill: parent
TableViewColumn{ role: "inout" ; title: "" ; width: 40 }
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
model: txModel
}
}
Rectangle {
id: newTxView
property var title: "New transaction"
visible: false
anchors.fill: parent
color: "#00000000"
TabView{
anchors.fill: parent
anchors.rightMargin: 5
anchors.leftMargin: 5
anchors.topMargin: 5
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)
}
}
}
Rectangle {
id: networkView
property var title: "Network"
visible: false
anchors.fill: parent
TableView {
id: blockTable
width: parent.width
anchors.top: parent.top
anchors.bottom: logView.top
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.setDetails(blockModel.get(row))
}
}
property var logModel: ListModel {
id: logModel
}
TableView {
id: logView
width: parent.width
height: 150
anchors.bottom: parent.bottom
TableViewColumn{ role: "description" ; title: "log" }
model: logModel
}
}
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)
Component {
id: pluginWindow
Rectangle {
anchors.fill: parent
Label {
id: pluginTitle
anchors.centerIn: parent
text: "Hello world"
}
Component.onCompleted: setView(this)
}
}
onAddPlugin: {
var pluginWin = pluginWindow.createObject(mainView)
console.log(pluginWin)
pluginWin.pluginTitle.text = "Test"
}
*/
}
}
FileDialog {
id: openAppDialog
title: "Open QML Application"
onAccepted: {
//ui.open(openAppDialog.fileUrl.toString())
//ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html")))
ui.openHtml(openAppDialog.fileUrl.toString())
}
}
statusBar: StatusBar {
RowLayout {
anchors.fill: parent
Button {
property var enabled: true
id: debuggerWindow
onClicked: {
ui.startDebugger()
}
text: "Debugger"
}
Button {
id: importAppButton
anchors.left: debuggerWindow.right
anchors.leftMargin: 5
onClicked: openAppDialog.open()
text: "Import App"
}
Label {
anchors.left: importAppButton.right
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")
}
}
}
Window {
id: popup
visible: false
property var block
width: 800
height: 280
x: root.x
y: root.y + root.height
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.data)
}else{
popup.height = 230
}
}
}
function showContractData(data) {
contractData.text = data
popup.height = 400
}
Rectangle {
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
}
TextArea {
id: contractData
text: "Contract"
anchors.top: contractLabel.bottom
anchors.left: parent.left
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).data)
}
}
txView.forceActiveFocus()
}
}
Window {
id: addPeerWin
visible: false
minimumWidth: 230
maximumWidth: 230
maximumHeight: 50
minimumHeight: 50
TextField {
id: addrField
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
placeholderText: "address:port"
onAccepted: {
ui.connectToPeer(addrField.text)
addPeerWin.visible = false
}
}
Button {
anchors.left: addrField.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 5
text: "Add"
onClicked: {
ui.connectToPeer(addrField.text)
addPeerWin.visible = false
}
}
Component.onCompleted: {
addrField.focus = true
}
}
Window {
id: aboutWin
visible: false
title: "About"
minimumWidth: 350
maximumWidth: 350
maximumHeight: 200
minimumHeight: 200
Image {
id: aboutIcon
height: 150
width: 150
fillMode: Image.PreserveAspectFit
smooth: true
source: ui.assetPath("facet.png")
x: 10
y: 10
}
Text {
anchors.left: aboutIcon.right
anchors.leftMargin: 10
font.pointSize: 12
text: "<h2>Ethereal</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>"
}
}
ApplicationWindow {
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
id: debugSplitView
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: debugSplitView.width }
model: stackModel
}
}
}
}
}
statusBar: StatusBar {
RowLayout {
anchors.fill: parent
Button {
property var enabled: true
id: debugNextButton
onClicked: {
ui.next()
}
text: "Next"
}
}
}
}
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 loadPlugin(name) {
console.log("Loading plugin" + name)
mainView.addPlugin(name)
}
function setWalletValue(value) {
walletValueLabel.text = value
}
function addTx(tx, inout) {
var isContract
if (tx.contract == true){
isContract = "Yes"
}else{
isContract = "No"
}
txModel.insert(0, {inout: inout, hash: tx.hash, address: tx.address, value: tx.value, contract: isContract})
}
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, 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) {
if(str.len != 0) {
logModel.append({description: str})
}
}
function setPeers(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;
}
}

View file

@ -0,0 +1,211 @@
import QtQuick 2.0
import QtWebKit 3.0
import QtWebKit.experimental 1.0
import QtQuick.Controls 1.0;
import QtQuick.Layouts 1.0;
import QtQuick.Window 2.1;
import Ethereum 1.0
ApplicationWindow {
id: window
title: "Ethereum"
width: 900
height: 600
minimumHeight: 300
property alias url: webview.url
property alias webView: webview
Item {
objectName: "root"
id: root
anchors.fill: parent
state: "inspectorShown"
WebView {
objectName: "webView"
id: webview
anchors.fill: parent
/*
anchors {
left: parent.left
right: parent.right
bottom: sizeGrip.top
top: parent.top
}
*/
onTitleChanged: { window.title = title }
experimental.preferences.javascriptEnabled: true
experimental.preferences.navigatorQtObjectEnabled: true
experimental.preferences.developerExtrasEnabled: true
experimental.userScripts: [ui.assetPath("ext/pre.js"), ui.assetPath("ext/big.js"), ui.assetPath("ext/string.js"), ui.assetPath("ext/ethereum.js")]
experimental.onMessageReceived: {
console.log("[onMessageReceived]: ", message.data)
// TODO move to messaging.js
var data = JSON.parse(message.data)
try {
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":
var block = eth.getBlock(data.args[0])
postData(data._seed, block)
break
case "getBlockByHash":
var block = eth.getBlock(data.args[0])
postData(data._seed, block)
break
case "transact":
require(5)
var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5])
postData(data._seed, tx)
break
case "create":
postData(data._seed, null)
break
case "getStorage":
require(2);
var stateObject = eth.getStateObject(data.args[0])
var storage = stateObject.getStorage(data.args[1])
postData(data._seed, storage)
break
case "getBalance":
require(1);
postData(data._seed, eth.getStateObject(data.args[0]).value());
break
case "getKey":
var key = eth.getKey().privateKey;
postData(data._seed, key)
break
case "watch":
require(1)
eth.watch(data.args[0], data.args[1]);
break
case "disconnect":
require(1)
postData(data._seed, null)
break;
case "set":
console.log("'Set' has been depcrecated")
/*
for(var key in data.args) {
if(webview.hasOwnProperty(key)) {
window[key] = data.args[key];
}
}
*/
break;
case "getSecretToAddress":
require(1)
postData(data._seed, eth.secretToAddress(data.args[0]))
break;
case "debug":
console.log(data.args[0]);
break;
}
} catch(e) {
console.log(data.call + ": " + e)
postData(data._seed, null);
}
}
function require(args, num) {
if(args.length < num) {
throw("required argument count of "+num+" got "+args.length);
}
}
function postData(seed, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
}
function postEvent(event, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
}
function onNewBlockCb(block) {
postEvent("block:new", block)
}
function onObjectChangeCb(stateObject) {
postEvent("object:"+stateObject.address(), stateObject)
}
function onStorageChangeCb(storageObject) {
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
postEvent(ev, [storageObject.address, storageObject.value])
}
}
Rectangle {
id: sizeGrip
color: "gray"
visible: false
height: 10
anchors {
left: root.left
right: root.right
}
y: Math.round(root.height * 2 / 3)
MouseArea {
anchors.fill: parent
drag.target: sizeGrip
drag.minimumY: 0
drag.maximumY: root.height
drag.axis: Drag.YAxis
}
}
WebView {
id: inspector
visible: false
url: webview.experimental.remoteInspectorUrl
anchors {
left: root.left
right: root.right
top: sizeGrip.bottom
bottom: root.bottom
}
}
states: [
State {
name: "inspectorShown"
PropertyChanges {
target: inspector
url: webview.experimental.remoteInspectorUrl
}
}
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Before After
Before After

View file

@ -0,0 +1,34 @@
/* Space out content a bit */
body {
padding-top: 20px;
padding-bottom: 20px;
}
/* Everything but the jumbotron gets side spacing for mobile first
* views */
.header,
.marketing,
.footer {
padding-right: 15px;
padding-left: 15px;
}
/* Custom page header */
.header {
border-bottom: 1px solid #e5e5e5;
}
/* Make the masthead heading the same height as the navigation */
.header h3 {
padding-bottom: 19px;
margin-top: 0;
margin-bottom: 0;
line-height: 40px;
}
.jumbotron {
text-align: center;
border-bottom: 1px solid #e5e5e5;
margin: 0 auto;
width: 300px;
}

View file

@ -0,0 +1,68 @@
<html>
<head>
<title>jeffcoin</title>
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="bootstrap-theme.min.css">
<link rel="stylesheet" href="samplecoin.css">
<meta name="viewport" content="minimum-scale=1; maximum-scale=1; initial-scale=1;">
<script type="text/javascript">
var jefcoinAddr = "518546ffa883dcc838a64bc2dabada0fd64af459"
var mAddr = ""
function createTransaction() {
var addr = ("0x" + document.querySelector("#addr").value).pad(32);
var amount = document.querySelector("#amount").value.pad(32);
var data = (addr + amount).unbin();
eth.transact(mAddr, jefcoinAddr, 0, "10000000", "250", data, function(receipt) {
debug("received tx hash:", reciept.address)
})
}
function init() {
eth.getKey(function(sec) {
mAddr = sec;
eth.getSecretToAddress(sec, function(addr) {
eth.getStorageAt(jefcoinAddr, addr, function(storage) {
document.querySelector("#current-amount").innerHTML = storage;
});
eth.watch(jefcoinAddr, addr, function(addr, value) {
document.querySelector("#current-amount").innerHTML = value
});
});
});
}
</script>
</head>
<body onload="init();">
<div class="container">
<div class="header">
<h3 class="text-muted">JeffCoin</h3>
</div>
<div class="jumbotron ">
<img src="icon.png">
<div>Amount: <strong id="current-amount"></strong></div>
<div id="transactions">
<div class="form-group">
<input id="addr" class="form-control" type="text" placeholder="Receiver address"></input><br>
<input id="amount" class="form-control" type="text" placeholder="Amount"></input><br>
</div>
<button class="btn btn-default" onclick="createTransaction();">Send Tx</button>
</div>
</div>
</div>
<div id="debug" style="border: 1px solid black; min-height: 30px;"></div>
</body>
</html>

View file

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

42
ethereal/config.go Normal file
View file

@ -0,0 +1,42 @@
package main
import (
"flag"
)
var Identifier string
var StartConsole bool
var StartMining bool
var StartRpc bool
var RpcPort int
var UseUPnP bool
var OutboundPort string
var ShowGenesis bool
var AddPeer string
var MaxPeer int
var GenAddr bool
var UseSeed bool
var ImportKey string
var ExportKey bool
var DataDir string
var AssetPath string
func Init() {
flag.StringVar(&Identifier, "i", "", "Custom client identifier")
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.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
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", 10, "maximum desired peers")
flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory")
flag.Parse()
}

132
ethereal/ethereum.go Normal file
View file

@ -0,0 +1,132 @@
package main
import (
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/ethereal/ui"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
"log"
"os"
"os/signal"
"runtime"
)
const Debug = true
// Register interrupt handlers so we can stop the ethereum
func RegisterInterupts(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)
go func() {
for sig := range c {
fmt.Printf("Shutting down (%v) ... \n", sig)
s.Stop()
}
}()
}
func main() {
Init()
qml.Init(nil)
runtime.GOMAXPROCS(runtime.NumCPU())
ethchain.InitFees()
ethutil.ReadConfig(DataDir, ethutil.LogFile|ethutil.LogStd, Identifier)
// Instantiated a eth stack
ethereum, err := eth.New(eth.CapDefault, UseUPnP)
if err != nil {
log.Println("eth start err:", err)
return
}
ethereum.Port = OutboundPort
if GenAddr {
fmt.Println("This action overwrites your old private key. Are you sure? (y/n)")
var r string
fmt.Scanln(&r)
for ; ; fmt.Scanln(&r) {
if r == "n" || r == "y" {
break
} else {
fmt.Printf("Yes or no?", r)
}
}
if r == "y" {
utils.CreateKeyPair(true)
}
os.Exit(0)
} else {
if len(ImportKey) > 0 {
fmt.Println("This action overwrites your old private key. Are you sure? (y/n)")
var r string
fmt.Scanln(&r)
for ; ; fmt.Scanln(&r) {
if r == "n" || r == "y" {
break
} else {
fmt.Printf("Yes or no?", r)
}
}
if r == "y" {
utils.ImportPrivateKey(ImportKey)
os.Exit(0)
}
}
}
if ExportKey {
keyPair := ethutil.GetKeyRing().Get(0)
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)
}
if ShowGenesis {
fmt.Println(ethereum.BlockChain().Genesis())
os.Exit(0)
}
if StartMining {
utils.DoMining(ethereum)
}
if StartRpc {
utils.DoRpc(ethereum, RpcPort)
}
log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver)
// Set the max peers
ethereum.MaxPeers = MaxPeer
gui := ethui.New(ethereum)
ethereum.Start(UseSeed)
gui.Start(AssetPath)
// Wait for shutdown
ethereum.WaitForShutdown()
}

177
ethereal/ui/debugger.go Normal file
View file

@ -0,0 +1,177 @@
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}
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 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)
}
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
if !self.Db.done {
self.Db.Q <- true
}
dataSlice := strings.Split(dataStr, "\n")
var data []byte
for _, dataItem := range dataSlice {
d := formatData(dataItem)
data = append(data, d...)
}
state := self.lib.eth.BlockChain().CurrentBlock.State()
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
script, err := ethutil.Compile(scriptStr)
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)
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

@ -1,14 +1,11 @@
package main
package ethui
import (
"encoding/json"
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/go-ethereum/javascript"
"gopkg.in/qml.v1"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
)
type AppContainer interface {
@ -19,37 +16,34 @@ type AppContainer interface {
Engine() *qml.Engine
NewBlock(*ethchain.Block)
ObjectChanged(*ethchain.StateObject)
StorageChanged(*ethchain.StorageState)
NewWatcher(chan bool)
Messages(ethstate.Messages, string)
Post(string, int)
}
type ExtApplication struct {
*ethpipe.JSPipe
eth ethchain.EthManager
*ethpub.PEthereum
blockChan chan ethreact.Event
messageChan chan ethreact.Event
blockChan chan ethutil.React
changeChan chan ethutil.React
quitChan chan bool
watcherQuitChan chan bool
filters map[string]*ethchain.Filter
container AppContainer
lib *UiLib
container AppContainer
lib *UiLib
registeredEvents []string
}
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
app := &ExtApplication{
ethpipe.NewJSPipe(lib.eth),
lib.eth,
make(chan ethreact.Event, 100),
make(chan ethreact.Event, 100),
ethpub.NewPEthereum(lib.eth),
make(chan ethutil.React, 1),
make(chan ethutil.React, 1),
make(chan bool),
make(chan bool),
make(map[string]*ethchain.Filter),
container,
lib,
nil,
}
return app
@ -63,7 +57,8 @@ func (app *ExtApplication) run() {
err := app.container.Create()
if err != nil {
logger.Errorln(err)
fmt.Println(err)
return
}
@ -73,7 +68,6 @@ func (app *ExtApplication) run() {
// Subscribe to events
reactor := app.lib.eth.Reactor()
reactor.Subscribe("newBlock", app.blockChan)
reactor.Subscribe("messages", app.messageChan)
app.container.NewWatcher(app.watcherQuitChan)
@ -88,6 +82,9 @@ func (app *ExtApplication) stop() {
// Clean up
reactor := app.lib.eth.Reactor()
reactor.Unsubscribe("newBlock", app.blockChan)
for _, event := range app.registeredEvents {
reactor.Unsubscribe(event, app.changeChan)
}
// Kill the main loop
app.quitChan <- true
@ -95,6 +92,7 @@ func (app *ExtApplication) stop() {
close(app.blockChan)
close(app.quitChan)
close(app.changeChan)
app.container.Destroy()
}
@ -109,37 +107,26 @@ out:
if block, ok := block.Resource.(*ethchain.Block); ok {
app.container.NewBlock(block)
}
case msg := <-app.messageChan:
if messages, ok := msg.Resource.(ethstate.Messages); ok {
for id, filter := range app.filters {
msgs := filter.FilterMessages(messages)
if len(msgs) > 0 {
app.container.Messages(msgs, id)
}
}
case object := <-app.changeChan:
if stateObject, ok := object.Resource.(*ethchain.StateObject); ok {
app.container.ObjectChanged(stateObject)
} else if storageObject, ok := object.Resource.(*ethchain.StorageState); ok {
app.container.StorageChanged(storageObject)
}
}
}
}
func (self *ExtApplication) Watch(filterOptions map[string]interface{}, identifier string) {
self.filters[identifier] = ethchain.NewFilterFromMap(filterOptions, self.eth)
}
func (self *ExtApplication) GetMessages(object map[string]interface{}) string {
filter := ethchain.NewFilterFromMap(object, self.eth)
messages := filter.Find()
var msgs []javascript.JSMessage
for _, m := range messages {
msgs = append(msgs, javascript.NewJSMessage(m))
func (app *ExtApplication) Watch(addr, storageAddr string) {
var event string
if len(storageAddr) == 0 {
event = "object:" + string(ethutil.FromHex(addr))
app.lib.eth.Reactor().Subscribe(event, app.changeChan)
} else {
event = "storage:" + string(ethutil.FromHex(addr)) + ":" + string(ethutil.FromHex(storageAddr))
app.lib.eth.Reactor().Subscribe(event, app.changeChan)
}
b, err := json.Marshal(msgs)
if err != nil {
return "{\"error\":" + err.Error() + "}"
}
return string(b)
app.registeredEvents = append(app.registeredEvents, event)
}

273
ethereal/ui/gui.go Normal file
View file

@ -0,0 +1,273 @@
package ethui
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"math/big"
"strings"
)
type Gui struct {
// The main application window
win *qml.Window
// QML Engine
engine *qml.Engine
component *qml.Common
// The ethereum interface
eth *eth.Ethereum
// The public Ethereum library
lib *EthLib
uiLib *UiLib
txDb *ethdb.LDBDatabase
addr []byte
pub *ethpub.PEthereum
}
// Create GUI, but doesn't start it
func New(ethereum *eth.Ethereum) *Gui {
lib := &EthLib{stateManager: ethereum.StateManager(), blockChain: ethereum.BlockChain(), txPool: ethereum.TxPool()}
db, err := ethdb.NewLDBDatabase("tx_database")
if err != nil {
panic(err)
}
// 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
var addr []byte
if ethutil.GetKeyRing().Len() != 0 {
addr = ethutil.GetKeyRing().Get(0).Address()
}
pub := ethpub.NewPEthereum(ethereum)
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr, pub: pub}
}
func (gui *Gui) Start(assetPath string) {
const version = "0.5.0 RC10"
defer gui.txDb.Close()
// Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
}, {
Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
}})
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)
// Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
var win *qml.Window
var err error
if len(data) == 0 {
win, err = gui.showKeyImport(context)
} else {
win, err = gui.showWallet(context)
}
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'")
panic(err)
}
win.Show()
win.Wait()
gui.eth.Stop()
}
func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
if err != nil {
return nil, err
}
win := gui.createWindow(component)
go gui.setInitialBlockChain()
go gui.readPreviousTransactions()
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
}
return gui.createWindow(component), 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() {
tx := ethchain.NewTransactionFromBytes(it.Value())
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, 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 {
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
func (gui *Gui) update() {
reactor := gui.eth.Reactor()
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()
unconfirmedFunds := new(big.Int)
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.addr).Amount)))
for {
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:
tx := txMsg.Resource.(*ethchain.Transaction)
if txMsg.Event == "newTx:pre" {
object := state.GetAccount(gui.addr)
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
}
gui.setWalletValue(object.Amount, unconfirmedFunds)
} else {
object := state.GetAccount(gui.addr)
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
object.SubAmount(tx.Value)
} else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
object.AddAmount(tx.Value)
}
gui.setWalletValue(object.Amount, nil)
state.UpdateStateObject(object)
}
}
}
}
// Logging functions that log directly to the GUI interface
func (gui *Gui) Println(v ...interface{}) {
str := strings.TrimRight(fmt.Sprintln(v...), "\n")
lines := strings.Split(str, "\n")
for _, line := range lines {
gui.win.Root().Call("addLog", line)
}
}
func (gui *Gui) Printf(format string, v ...interface{}) {
str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
lines := strings.Split(str, "\n")
for _, line := range lines {
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

@ -1,22 +1,18 @@
package main
package ethui
import (
"encoding/json"
"errors"
"fmt"
"github.com/ethereum/eth-go/ethchain"
"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"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"github.com/howeyc/fsnotify"
"gopkg.in/qml.v1"
)
type HtmlApplication struct {
@ -45,7 +41,7 @@ func (app *HtmlApplication) Create() error {
return errors.New("Ethereum package not yet supported")
// TODO
//ethutil.OpenPackage(app.path)
ethutil.OpenPackage(app.path)
}
win := component.CreateWindow(nil)
@ -63,7 +59,7 @@ func (app *HtmlApplication) RootFolder() string {
if err != nil {
return ""
}
return path.Dir(ethutil.WindonizePath(folder.RequestURI()))
return path.Dir(folder.RequestURI())
}
func (app *HtmlApplication) RecursiveFolders() []os.FileInfo {
files, _ := ioutil.ReadDir(app.RootFolder())
@ -81,13 +77,11 @@ func (app *HtmlApplication) NewWatcher(quitChan chan bool) {
app.watcher, err = fsnotify.NewWatcher()
if err != nil {
logger.Infoln("Could not create new auto-reload watcher:", err)
return
}
err = app.watcher.Watch(app.RootFolder())
if err != nil {
logger.Infoln("Could not start auto-reload watcher:", err)
return
log.Fatal(err)
}
for _, folder := range app.RecursiveFolders() {
fullPath := app.RootFolder() + "/" + folder.Name()
@ -102,11 +96,11 @@ func (app *HtmlApplication) NewWatcher(quitChan chan bool) {
app.watcher.Close()
break out
case <-app.watcher.Event:
//logger.Debugln("Got event:", ev)
//ethutil.Config.Log.Debugln("Got event:", ev)
app.webView.Call("reload")
case err := <-app.watcher.Error:
// TODO: Do something here
logger.Infoln("Watcher error:", err)
ethutil.Config.Log.Infoln("Watcher error:", err)
}
}
}()
@ -122,26 +116,18 @@ func (app *HtmlApplication) Window() *qml.Window {
}
func (app *HtmlApplication) NewBlock(block *ethchain.Block) {
b := &ethpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
b := &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
app.webView.Call("onNewBlockCb", b)
}
func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) {
var msgs []javascript.JSMessage
for _, m := range messages {
msgs = append(msgs, javascript.NewJSMessage(m))
}
func (app *HtmlApplication) ObjectChanged(stateObject *ethchain.StateObject) {
app.webView.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
}
b, _ := json.Marshal(msgs)
self.webView.Call("onWatchedCb", string(b), id)
func (app *HtmlApplication) StorageChanged(storageObject *ethchain.StorageState) {
app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject))
}
func (app *HtmlApplication) Destroy() {
app.engine.Destroy()
}
func (app *HtmlApplication) Post(data string, seed int) {
fmt.Println("about to call 'post'")
app.webView.Call("post", seed, data)
}

46
ethereal/ui/library.go Normal file
View file

@ -0,0 +1,46 @@
package ethui
import (
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/secp256k1-go"
"strings"
)
type EthLib struct {
stateManager *ethchain.StateManager
blockChain *ethchain.BlockChain
txPool *ethchain.TxPool
Db *Debugger
}
func (lib *EthLib) ImportAndSetPrivKey(privKey string) bool {
fmt.Println(privKey)
mnemonic := strings.Split(privKey, " ")
if len(mnemonic) == 24 {
fmt.Println("Got mnemonic key, importing.")
key := ethutil.MnemonicDecode(mnemonic)
utils.ImportPrivateKey(key)
} else if len(mnemonic) == 1 {
fmt.Println("Got hex key, importing.")
utils.ImportPrivateKey(privKey)
} else {
fmt.Println("Did not recognise format, exiting.")
return false
}
return true
}
func (lib *EthLib) CreateAndSetPrivKey() (string, string, string, string) {
_, prv := secp256k1.GenerateKeyPair()
keyPair, err := ethutil.GetKeyRing().NewKeyPair(prv)
if err != nil {
panic(err)
}
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
mnemonicString := strings.Join(mne, " ")
return mnemonicString, fmt.Sprintf("%x", keyPair.Address()), ethutil.Hex(keyPair.PrivateKey), ethutil.Hex(keyPair.PublicKey)
}

172
ethereal/ui/ui_lib.go Normal file
View file

@ -0,0 +1,172 @@
package ethui
import (
"bitbucket.org/kardianos/osext"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/go-qml/qml"
"os"
"path"
"path/filepath"
"runtime"
)
type memAddr struct {
Num string
Value string
}
// UI Library that has some basic functionality exposed
type UiLib struct {
engine *qml.Engine
eth *eth.Ethereum
connected bool
assetPath string
// The main application window
win *qml.Window
Db *Debugger
}
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
if assetPath == "" {
assetPath = DefaultAssetPath()
}
return &UiLib{engine: engine, eth: eth, assetPath: assetPath}
}
// Opens a QML file (external application)
func (ui *UiLib) Open(path string) {
component, err := ui.engine.LoadFile(path[7:])
if err != nil {
ethutil.Config.Log.Debugln(err)
}
win := component.CreateWindow(nil)
go func() {
win.Show()
win.Wait()
}()
}
func (ui *UiLib) OpenHtml(path string) {
container := NewHtmlApplication(path, ui)
app := NewExtApplication(container, ui)
go app.run()
}
func (ui *UiLib) Muted(content string) {
component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
if err != nil {
ethutil.Config.Log.Debugln(err)
return
}
win := component.CreateWindow(nil)
go func() {
path := "file://" + ui.AssetPath("muted/index.html")
win.Set("url", path)
win.Show()
win.Wait()
}()
}
func (ui *UiLib) Connect(button qml.Object) {
if !ui.connected {
ui.eth.Start(true)
ui.connected = true
button.Set("enabled", false)
}
}
func (ui *UiLib) ConnectToPeer(addr string) {
ui.eth.ConnectToPeer(addr)
}
func (ui *UiLib) AssetPath(p string) string {
return path.Join(ui.assetPath, p)
}
func (self *UiLib) StartDebugger() {
dbWindow := NewDebuggerWindow(self)
dbWindow.Show()
}
func DefaultAssetPath() string {
var base string
// If the current working directory is the go-ethereum dir
// assume a debug build and use the source directory as
// asset directory.
pwd, _ := os.Getwd()
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") {
base = path.Join(pwd, "assets")
} else {
switch runtime.GOOS {
case "darwin":
// Get Binary Directory
exedir, _ := osext.ExecutableFolder()
base = filepath.Join(exedir, "../Resources")
case "linux":
base = "/usr/share/ethereal"
case "window":
fallthrough
default:
base = "."
}
}
return base
}
func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string) {
state := ui.eth.BlockChain().CurrentBlock.State()
script, err := ethutil.Compile(data)
if err != nil {
ethutil.Config.Log.Debugln(err)
return
}
dis := ethchain.Disassemble(script)
ui.win.Root().Call("clearAsm")
for _, str := range dis {
ui.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)
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
vm := ethchain.NewVm(state, ui.eth.StateManager(), ethchain.RuntimeVars{
Origin: account.Address(),
BlockNumber: block.BlockInfo().Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
})
ui.Db.done = false
go func() {
callerClosure.Call(vm, contract.Init(), ui.Db.halting)
state.Reset()
ui.Db.done = true
}()
}
func (ui *UiLib) Next() {
ui.Db.Next()
}

View file

@ -1,35 +0,0 @@
package main
import (
"io/ioutil"
"os"
"github.com/ethereum/eth-go"
"github.com/ethereum/go-ethereum/ethereum/repl"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/utils"
)
func InitJsConsole(ethereum *eth.Ethereum) {
repl := ethrepl.NewJSRepl(ethereum)
go repl.Start()
utils.RegisterInterrupt(func(os.Signal) {
repl.Stop()
})
}
func ExecJsFile(ethereum *eth.Ethereum, InputFile string) {
file, err := os.Open(InputFile)
if err != nil {
logger.Fatalln(err)
}
content, err := ioutil.ReadAll(file)
if err != nil {
logger.Fatalln(err)
}
re := javascript.NewJSRE(ethereum)
utils.RegisterInterrupt(func(os.Signal) {
re.Stop()
})
re.Run(string(content))
}

54
ethereum/config.go Normal file
View file

@ -0,0 +1,54 @@
package main
import (
"flag"
"fmt"
"os"
)
var Identifier string
var StartMining bool
var StartRpc bool
var RpcPort int
var UseUPnP bool
var OutboundPort string
var ShowGenesis bool
var AddPeer string
var MaxPeer int
var GenAddr bool
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.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(&ShowGenesis, "g", false, "prints genesis header and exits")
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(&UseUPnP, "upnp", false, "enable UPnP support")
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", 10, "maximum desired peers")
flag.BoolVar(&StartJsConsole, "js", false, "exp")
flag.Parse()
InputFile = flag.Arg(0)
}

186
ethereum/ethereum.go Normal file
View file

@ -0,0 +1,186 @@
package main
import (
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"io/ioutil"
"log"
"os"
"os/signal"
"runtime"
"strings"
)
const Debug = true
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 {
cb(sig)
}
}()
}
func confirm(message string) bool {
fmt.Println(message, "Are you sure? (y/n)")
var r string
fmt.Scanln(&r)
for ; ; fmt.Scanln(&r) {
if r == "n" || r == "y" {
break
} else {
fmt.Printf("Yes or no?", r)
}
}
return r == "y"
}
func main() {
Init()
runtime.GOMAXPROCS(runtime.NumCPU())
// set logger
var logSys *log.Logger
flags := log.LstdFlags
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
if LogFile != "" {
logfile, err := os.OpenFile(LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("error opening log file '%s': %v", LogFile, err))
}
defer logfile.Close()
log.SetOutput(logfile)
logSys = log.New(logfile, "", flags)
logger.AddLogSystem(logSys)
} else {
logSys = log.New(os.Stdout, "", flags)
}
ethchain.InitFees()
// Instantiated a eth stack
ethereum, err := eth.New(eth.CapDefault, UseUPnP)
if err != nil {
log.Println("eth start err:", err)
return
}
ethereum.Port = OutboundPort
// bookkeeping tasks
switch {
case GenAddr:
if NonInteractive || confirm("This action overwrites your old private key.") {
utils.CreateKeyPair(true)
}
os.Exit(0)
case len(ImportKey) > 0:
if NonInteractive || confirm("This action overwrites your old private key.") {
mnemonic := strings.Split(ImportKey, " ")
if len(mnemonic) == 24 {
logSys.Println("Got mnemonic key, importing.")
key := ethutil.MnemonicDecode(mnemonic)
utils.ImportPrivateKey(key)
} else if len(mnemonic) == 1 {
logSys.Println("Got hex key, importing.")
utils.ImportPrivateKey(ImportKey)
} else {
logSys.Println("Did not recognise format, exiting.")
}
}
os.Exit(0)
case ExportKey:
keyPair := ethutil.GetKeyRing().Get(0)
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)
case ShowGenesis:
logSys.Println(ethereum.BlockChain().Genesis())
os.Exit(0)
default:
// Creates a keypair if non exists
utils.CreateKeyPair(false)
}
// client
logger.Infoln(fmt.Sprintf("Starting Ethereum v%s", ethutil.Config.Ver))
// Set the max peers
ethereum.MaxPeers = MaxPeer
// Set Mining status
ethereum.Mining = StartMining
if StartMining {
utils.DoMining(ethereum)
}
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)
}
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)
}
RegisterInterrupt(func(sig os.Signal) {
fmt.Printf("Shutting down (%v) ... \n", sig)
ethereum.Stop()
})
ethereum.Start(UseSeed)
// Wait for shutdown
ethereum.WaitForShutdown()
}

View file

@ -1,89 +0,0 @@
package main
import (
"flag"
"fmt"
"os"
"os/user"
"path"
"github.com/ethereum/eth-go/ethlog"
)
var Identifier string
var KeyRing string
var DiffTool bool
var DiffType string
var KeyStore string
var StartRpc bool
var RpcPort int
var UseUPnP bool
var OutboundPort string
var ShowGenesis bool
var AddPeer string
var MaxPeer int
var GenAddr bool
var UseSeed bool
var SecretFile string
var ExportDir string
var NonInteractive bool
var Datadir string
var LogFile string
var ConfigFile string
var DebugFile string
var LogLevel int
var Dump bool
var DumpHash string
var DumpNumber int
// flags specific to cli client
var StartMining bool
var StartJsConsole bool
var InputFile string
func defaultDataDir() string {
usr, _ := user.Current()
return path.Join(usr.HomeDir, ".ethereum")
}
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
func Init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "%s [options] [filename]:\noptions precedence: default < config file < environment variables < command line\n", os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
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(&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.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
flag.IntVar(&LogLevel, "loglevel", int(ethlog.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
flag.IntVar(&DumpNumber, "number", -1, "specify arg in number")
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")
flag.Parse()
InputFile = flag.Arg(0)
}

View file

@ -1,91 +1,60 @@
package javascript
package main
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
"github.com/obscuren/otto"
"io/ioutil"
"os"
"path/filepath"
)
var jsrelogger = ethlog.NewLogger("JSRE")
type JSRE struct {
ethereum *eth.Ethereum
Vm *otto.Otto
pipe *ethpipe.JSPipe
vm *otto.Otto
lib *ethpub.PEthereum
blockChan chan ethreact.Event
changeChan chan ethreact.Event
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 {
jsrelogger.Infoln("Could not load file:", path)
}
}
func (jsre *JSRE) LoadIntFile(file string) {
assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist", "assets", "ext")
jsre.LoadExtFile(path.Join(assetPath, file))
}
func NewJSRE(ethereum *eth.Ethereum) *JSRE {
re := &JSRE{
ethereum,
otto.New(),
ethpipe.NewJSPipe(ethereum),
make(chan ethreact.Event, 10),
make(chan ethreact.Event, 10),
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")
re.vm.Run(jsLib)
// We have to make sure that, whoever calls this, calls "Stop"
go re.mainLoop()
// Subscribe to events
reactor := ethereum.Reactor()
reactor.Subscribe("newBlock", re.blockChan)
re.Bind("eth", &JSEthereum{re.pipe, re.Vm, ethereum})
re.Bind("eth", &JSEthereum{re.lib, re.vm})
re.initStdFuncs()
jsrelogger.Infoln("started")
return re
}
func (self *JSRE) Bind(name string, v interface{}) {
self.Vm.Set(name, v)
self.vm.Set(name, v)
}
func (self *JSRE) Run(code string) (otto.Value, error) {
return self.Vm.Run(code)
return self.vm.Run(code)
}
func (self *JSRE) Require(file string) error {
@ -111,10 +80,13 @@ func (self *JSRE) Stop() {
close(self.blockChan)
close(self.quitChan)
close(self.changeChan)
jsrelogger.Infoln("stopped")
}
func (self *JSRE) mainLoop() {
// Subscribe to events
reactor := self.ethereum.Reactor()
reactor.Subscribe("newBlock", self.blockChan)
out:
for {
select {
@ -123,64 +95,43 @@ 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")
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)
eth.Set("execBlock", self.execBlock)
eth.Set("dump", self.dump)
}
/*
* The following methods are natively implemented javascript functions
*/
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
var state *ethstate.State
if len(call.ArgumentList) > 0 {
var block *ethchain.Block
if call.Argument(0).IsNumber() {
num, _ := call.Argument(0).ToInteger()
block = self.ethereum.BlockChain().GetBlockByNumber(uint64(num))
} else if call.Argument(0).IsString() {
hash, _ := call.Argument(0).ToString()
block = self.ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(hash))
} else {
fmt.Println("invalid argument for dump. Either hex string or number")
}
if block == nil {
fmt.Println("block not found")
return otto.UndefinedValue()
}
state = block.State()
} else {
state = self.ethereum.StateManager().CurrentState()
}
v, _ := self.Vm.ToValue(state.Dump())
return v
}
func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value {
v, _ := self.Vm.ToValue(utils.StopMining(self.ethereum))
v, _ := self.vm.ToValue(utils.StopMining(self.ethereum))
return v
}
func (self *JSRE) startMining(call otto.FunctionCall) otto.Value {
v, _ := self.Vm.ToValue(utils.StartMining(self.ethereum))
v, _ := self.vm.ToValue(utils.StartMining(self.ethereum))
return v
}
@ -201,12 +152,12 @@ func (self *JSRE) watch(call otto.FunctionCall) otto.Value {
if storageCallback {
self.objectCb[addr+storageAddr] = append(self.objectCb[addr+storageAddr], cb)
event := "storage:" + string(ethutil.Hex2Bytes(addr)) + ":" + string(ethutil.Hex2Bytes(storageAddr))
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.Hex2Bytes(addr))
event := "object:" + string(ethutil.FromHex(addr))
self.ethereum.Reactor().Subscribe(event, self.changeChan)
}
@ -233,22 +184,7 @@ func (self *JSRE) require(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue()
}
t, _ := self.Vm.Get("exports")
t, _ := self.vm.Get("exports")
return t
}
func (self *JSRE) execBlock(call otto.FunctionCall) otto.Value {
hash, err := call.Argument(0).ToString()
if err != nil {
return otto.UndefinedValue()
}
err = utils.BlockDo(self.ethereum, ethutil.Hex2Bytes(hash))
if err != nil {
fmt.Println(err)
return otto.FalseValue()
}
return otto.TrueValue()
}

View file

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

View file

@ -1,111 +0,0 @@
package main
import (
"fmt"
"os"
"runtime"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
)
const (
ClientIdentifier = "Ethereum(G)"
Version = "0.6.5"
)
var logger = ethlog.NewLogger("CLI")
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
utils.HandleInterrupt()
// precedence: code-internal flag default < config file < environment variables < command line
Init() // parsing command line
// If the difftool option is selected ignore all other log output
if DiffTool || Dump {
LogLevel = 0
}
utils.InitConfig(ConfigFile, Datadir, "ETH")
ethutil.Config.Diff = DiffTool
ethutil.Config.DiffType = DiffType
utils.InitDataDir(Datadir)
utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
db := utils.NewDatabase()
err := utils.DBSanityCheck(db)
if err != nil {
logger.Errorln(err)
os.Exit(1)
}
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
if Dump {
var block *ethchain.Block
if len(DumpHash) == 0 && DumpNumber == -1 {
block = ethereum.BlockChain().CurrentBlock
} else if len(DumpHash) > 0 {
block = ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(DumpHash))
} else {
block = ethereum.BlockChain().GetBlockByNumber(uint64(DumpNumber))
}
if block == nil {
fmt.Fprintln(os.Stderr, "block not found")
// We want to output valid JSON
fmt.Println("{}")
os.Exit(1)
}
fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash())
// Leave the Println. This needs clean output for piping
fmt.Printf("%s\n", block.State().Dump())
os.Exit(0)
}
if ShowGenesis {
utils.ShowGenesis(ethereum)
}
if StartMining {
utils.StartMining(ethereum)
}
// better reworked as cases
if StartJsConsole {
InitJsConsole(ethereum)
} else if len(InputFile) > 0 {
ExecJsFile(ethereum, InputFile)
}
if StartRpc {
utils.StartRpc(ethereum, RpcPort)
}
utils.StartEthereum(ethereum, UseSeed)
// this blocks the thread
ethereum.WaitForShutdown()
ethlog.Flush()
}

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
}

View file

@ -1,85 +0,0 @@
package ethrepl
import (
"bufio"
"fmt"
"io"
"os"
"path"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/javascript"
)
var logger = ethlog.NewLogger("REPL")
type Repl interface {
Start()
Stop()
}
type JSRepl struct {
re *javascript.JSRE
prompt string
history *os.File
running bool
}
func NewJSRepl(ethereum *eth.Ethereum) *JSRepl {
hist, err := os.OpenFile(path.Join(ethutil.Config.ExecPath, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
panic(err)
}
return &JSRepl{re: javascript.NewJSRE(ethereum), prompt: "> ", history: hist}
}
func (self *JSRepl) Start() {
if !self.running {
self.running = true
logger.Infoln("init JS Console")
reader := bufio.NewReader(self.history)
for {
line, err := reader.ReadString('\n')
if err != nil && err == io.EOF {
break
} else if err != nil {
fmt.Println("error reading history", err)
break
}
addHistory(line[:len(line)-1])
}
self.read()
}
}
func (self *JSRepl) Stop() {
if self.running {
self.running = false
self.re.Stop()
logger.Infoln("exit JS Console")
self.history.Close()
}
}
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)
}

View file

@ -1,4 +1,4 @@
package ethrepl
package main
// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include
// #cgo darwin LDFLAGS: -L/usr/local/opt/readline/lib
@ -102,9 +102,7 @@ L:
break L
}
hist := str[:len(str)-1]
addHistory(hist) //allow user to recall this line
self.history.WriteString(str)
addHistory(str[:len(str)-1]) //allow user to recall this line
self.parseInput(str)
@ -115,8 +113,8 @@ L:
}
func (self *JSRepl) PrintValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v)
method, _ := self.re.vm.Get("prettyPrint")
v, err := self.re.vm.ToValue(v)
if err == nil {
method.Call(method, v)
}

View file

@ -1,4 +1,4 @@
package ethrepl
package main
import (
"bufio"

View file

@ -1,57 +0,0 @@
#!/bin/sh
if [ "$1" == "" ]; then
echo "Usage $0 executable branch ethereum develop"
echo "executable ethereum or ethereal"
echo "branch develop or master"
exit
fi
exe=$1
branch=$2
# Test if go is installed
command -v go >/dev/null 2>&1 || { echo >&2 "Unable to find 'go'. This script requires go."; exit 1; }
# Test if $GOPATH is set
if [ "$GOPATH" == "" ]; then
echo "\$GOPATH not set"
exit
fi
echo "go get -u -d github.com/ethereum/go-ethereum/$exe"
go get -v -u -d github.com/ethereum/go-ethereum/$exe
if [ $? != 0 ]; then
echo "go get failed"
exit
fi
echo "serpent-go"
cd $GOPATH/src/github.com/obscuren/serpent-go
echo "init submodule"
git submodule init
git submodule update
echo "eth-go"
cd $GOPATH/src/github.com/ethereum/eth-go
git checkout $branch
echo "go-ethereum"
cd $GOPATH/src/github.com/ethereum/go-ethereum/$exe
git checkout $branch
if [ "$exe" == "ethereal" ]; then
echo "Building ethereal GUI. Assuming Qt is installed. If this step"
echo "fails; please refer to: https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)"
else
echo "Building ethereum CLI."
fi
go install
if [ $? == 0 ]; then
echo "go install failed"
exit
fi
echo "done. Please run $exe :-)"

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,004 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 B

View file

@ -1,435 +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
ApplicationWindow {
id: win
visible: false
title: "IceCREAM"
minimumWidth: 1280
minimumHeight: 700
width: 1290
height: 750
property alias codeText: codeEditor.text
property alias dataText: rawDataField.text
onClosing: {
dbg.Stop()
}
menuBar: MenuBar {
Menu {
title: "Edit"
MenuItem {
text: "Focus code"
shortcut: "Ctrl+1"
onTriggered: {
codeEditor.focus = true
}
}
MenuItem {
text: "Focus data"
shortcut: "Ctrl+2"
onTriggered: {
rawDataField.focus = true
}
}
MenuItem {
text: "Command"
shortcut: "Ctrl+l"
onTriggered: {
dbgCommand.focus = true
}
}
}
Menu {
title: "Debugger"
MenuItem {
text: "Run"
shortcut: "Ctrl+r"
onTriggered: debugCurrent()
}
MenuItem {
text: "Stop"
onTriggered: dbp.stop()
}
MenuSeparator {}
MenuItem {
text: "Next"
shortcut: "Ctrl+n"
onTriggered: dbg.next()
}
MenuItem {
text: "Continue"
shortcut: "Ctrl+g"
onTriggered: dbg.continue()
}
}
}
SplitView {
anchors.fill: parent
property var asmModel: ListModel {
id: asmModel
}
TableView {
id: asmTableView
width: 200
headerVisible: false
TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 }
model: asmModel
/*
alternatingRowColors: false
itemDelegate: Item {
Rectangle {
anchors.fill: parent
color: "#DDD"
Text {
anchors {
left: parent.left
right: parent.right
leftMargin: 10
verticalCenter: parent.verticalCenter
}
color: "#333"
elide: styleData.elideMode
text: styleData.value
font.pixelSize: 11
MouseArea {
acceptedButtons: Qt.LeftButton
anchors.fill: parent
onClicked: {
mouse.accepted = true
}
}
}
}
}
*/
}
Rectangle {
color: "#00000000"
anchors.left: asmTableView.right
anchors.right: parent.right
SplitView {
orientation: Qt.Vertical
anchors.fill: parent
Rectangle {
color: "#00000000"
height: 330
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
focus: true
/*
Timer {
id: compileTimer
interval: 500 ; running: true ; repeat: true
onTriggered: {
dbg.autoComp(codeEditor.text)
}
}
*/
}
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: "Local VM stack" ; width: stackTableView.width - 2 }
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: 650 }
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 - 1}
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2 - 1}
model: storageModel
}
}
Rectangle {
height: 200
width: parent.width * 0.66
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 - 2 }
model: logModel
}
}
}
}
}
}
function exec() {
dbg.execCommand(dbgCommand.text);
dbgCommand.text = "";
}
statusBar: StatusBar {
height: 30
TextField {
id: dbgCommand
y: 1
x: asmTableView.width
width: 500
placeholderText: "Debugger (type 'help')"
Keys.onReturnPressed: {
exec()
}
}
RowLayout {
anchors.left: dbgCommand.right
anchors.leftMargin: 10
spacing: 5
y: parent.height / 2 - this.height / 2
Text {
objectName: "stackFrame"
font.pixelSize: 10
text: "<b>stack ptr</b>: 0"
}
Text {
objectName: "stackSize"
font.pixelSize: 10
text: "<b>stack size</b>: 0"
}
Text {
objectName: "memSize"
font.pixelSize: 10
text: "<b>mem size</b>: 0"
}
}
}
toolBar: ToolBar {
height: 30
RowLayout {
spacing: 10
Button {
property var enabled: true
id: debugStart
onClicked: {
debugCurrent()
}
text: "Debug"
}
Button {
property var enabled: true
id: debugNextButton
onClicked: {
dbg.next()
}
text: "Next"
}
Button {
id: debugContinueButton
onClicked: {
dbg.continue()
}
text: "Continue"
}
}
ComboBox {
id: snippets
anchors.right: parent.right
model: ListModel {
ListElement { text: "Snippets" ; value: "" }
ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" }
}
onCurrentIndexChanged: {
if(currentIndex != 0) {
var code = snippets.model.get(currentIndex).value;
codeEditor.insert(codeEditor.cursorPosition, code)
}
}
}
}
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)
asmTableView.positionViewAtRow(num, ListView.Center)
}
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) {
// Remove first item once we've reached max log items
if(logModel.count > 250) {
logModel.remove(0)
}
if(msg.len != 0) {
if(logTableView.flickableItem.atYEnd) {
logModel.append({message: msg})
logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain)
} else {
logModel.append({message: msg})
}
}
}
function clearLog() {
logModel.clear()
}
}

View file

@ -1,295 +0,0 @@
// Main Ethereum library
window.eth = {
prototype: Object(),
_callbacks: {},
_onCallbacks: {},
test: function() {
var t = undefined;
postData({call: "test"})
navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; }
for(;;) {
if(t !== undefined) {
return t
}
}
},
mutan: function(code, cb) {
postData({call: "mutan", args: [code]}, cb)
},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i)
if(code == 0) {
break;
}
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str;
},
fromAscii: function(str, pad) {
if(pad === undefined) {
pad = 32
}
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return hex
},
// Retrieve block
//
// Either supply a number or a string. Type is determent for the lookup method
// string - Retrieves the block by looking up the hash
// number - Retrieves the block by looking up the block number
getBlock: function(numberOrHash, cb) {
var func;
if(typeof numberOrHash == "string") {
func = "getBlockByHash";
} else {
func = "getBlockByNumber";
}
postData({call: func, args: [numberOrHash]}, cb);
},
// Create transaction
//
// Transact between two state objects
transact: function(params, cb) {
if(params === undefined) {
params = {};
}
if(params.endowment !== undefined)
params.value = params.endowment;
if(params.code !== undefined)
params.data = params.code;
// Make sure everything is string
var fields = ["to", "from", "value", "gas", "gasPrice"];
for(var i = 0; i < fields.length; i++) {
if(params[fields[i]] === undefined) {
params[fields[i]] = "";
}
params[fields[i]] = params[fields[i]].toString();
}
var data;
if(typeof params.data === "object") {
data = "";
for(var i = 0; i < params.data.length; i++) {
data += params.data[i]
}
} else {
data = params.data;
}
postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb);
},
getMessages: function(filter, cb) {
postData({call: "messages", args: [filter]}, cb);
},
getStorageAt: function(address, storageAddress, cb) {
postData({call: "getStorage", args: [address, storageAddress]}, cb);
},
getEachStorageAt: function(address, cb){
postData({call: "getEachStorage", args: [address]}, cb);
},
getKey: function(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) {
postData({call: "getBalance", args: [address]}, cb);
},
getTransactionsFor: function(address, cb) {
postData({call: "getTransactionsFor", args: [address]}, cb);
},
getSecretToAddress: function(sec, cb) {
postData({call: "getSecretToAddress", args: [sec]}, cb);
},
/*
watch: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
cb = storageAddrOrCb;
storageAddrOrCb = "";
ev = "object:"+address;
} else {
ev = "storage:"+address+":"+storageAddrOrCb;
}
eth.on(ev, cb)
postData({call: "watch", args: [address, storageAddrOrCb]});
},
disconnect: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
cb = storageAddrOrCb;
storageAddrOrCb = "";
ev = "object:"+address;
} else {
ev = "storage:"+address+":"+storageAddrOrCb;
}
eth.off(ev, cb)
postData({call: "disconnect", args: [address, storageAddrOrCb]});
},
*/
watch: function(options) {
var filter = new Filter(options);
filter.number = newWatchNum().toString()
postData({call: "watch", args: [options, filter.number]})
return filter;
},
set: function(props) {
postData({call: "set", args: props});
},
on: function(event, cb) {
if(eth._onCallbacks[event] === undefined) {
eth._onCallbacks[event] = [];
}
eth._onCallbacks[event].push(cb);
return this
},
off: function(event, cb) {
if(eth._onCallbacks[event] !== undefined) {
var callbacks = eth._onCallbacks[event];
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] === cb) {
delete callbacks[i];
}
}
}
return this
},
trigger: function(event, data) {
var callbacks = eth._onCallbacks[event];
if(callbacks !== undefined) {
for(var i = 0; i < callbacks.length; i++) {
// Figure out whether the returned data was an array
// array means multiple return arguments (multiple params)
if(data instanceof Array) {
callbacks[i].apply(this, data);
} else {
callbacks[i].call(this, data);
}
}
}
},
}
var Filter = function(options) {
this.options = options;
};
Filter.prototype.changed = function(callback) {
// Register the watched:<number>. Qml will call the appropriate event if anything
// interesting happens in the land of Go.
eth.on("watched:"+this.number, callback)
}
Filter.prototype.getMessages = function(cb) {
return eth.getMessages(this.options, cb)
}
var watchNum = 0;
function newWatchNum() {
return watchNum++;
}
function postData(data, cb) {
data._seed = Math.floor(Math.random() * 1000000)
if(cb) {
eth._callbacks[data._seed] = cb;
}
if(data.args === undefined) {
data.args = [];
}
navigator.qt.postMessage(JSON.stringify(data));
}
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
cb.call(this, data.data)
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
}
eth.on("chain:changed", function() {
})
eth.on("messages", { /* filters */}, function(messages){
})
eth.on("pending:changed", function() {
})

View file

@ -1,49 +0,0 @@
var ethx = {
prototype: Object,
watch: function(options) {
return new Filter(options);
},
note: function() {
var args = Array.prototype.slice.call(arguments, 0);
var o = []
for(var i = 0; i < args.length; i++) {
o.push(args[i].toString())
}
eth.notef(o);
},
};
var Filter = function(options) {
this.callbacks = [];
this.options = options;
if(options === "chain") {
this.id = eth.newFilterString(options);
} else if(typeof options === "object") {
this.id = eth.newFilter(options);
}
};
Filter.prototype.changed = function(callback) {
this.callbacks.push(callback);
var self = this;
messages.connect(function(messages, id) {
if(id == self.id) {
for(var i = 0; i < self.callbacks.length; i++) {
self.callbacks[i].call(self, messages);
}
}
});
};
Filter.prototype.uninstall = function() {
eth.uninstallFilter(this.id)
}
Filter.prototype.messages = function() {
return eth.messages(this.id)
}

View file

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

View file

@ -1,481 +0,0 @@
// The magic return variable. The magic return variable will be set during the execution of the QML call.
(function(window) {
function message(type, data) {
document.title = JSON.stringify({type: type, data: data});
return window.____returnData;
}
function isPromise(o) {
return typeof o === "object" && o.then
}
window.eth = {
_callbacks: {},
_events: {},
prototype: Object(),
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i)
if(code == 0) {
break;
}
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str;
},
fromAscii: function(str, pad) {
if(pad === undefined) {
pad = 32
}
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return hex
},
block: function(numberOrHash) {
return new Promise(function(resolve, reject) {
var func;
if(typeof numberOrHash == "string") {
func = "getBlockByHash";
} else {
func = "getBlockByNumber";
}
postData({call: func, args: [numberOrHash]}, function(block) {
if(block)
resolve(block);
else
reject("not found");
});
});
},
transact: function(params) {
if(params === undefined) {
params = {};
}
if(params.endowment !== undefined)
params.value = params.endowment;
if(params.code !== undefined)
params.data = params.code;
var promises = []
if(isPromise(params.to)) {
promises.push(params.to.then(function(_to) { params.to = _to; }));
}
if(isPromise(params.from)) {
promises.push(params.from.then(function(_from) { params.from = _from; }));
}
if(isPromise(params.data)) {
promises.push(params.data.then(function(_code) { params.data = _code; }));
} else {
if(typeof params.data === "object") {
data = "";
for(var i = 0; i < params.data.length; i++) {
data += params.data[i]
}
} else {
data = params.data;
}
}
// Make sure everything is string
var fields = ["value", "gas", "gasPrice"];
for(var i = 0; i < fields.length; i++) {
if(params[fields[i]] === undefined) {
params[fields[i]] = "";
}
params[fields[i]] = params[fields[i]].toString();
}
// Load promises then call the last "transact".
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "transact", args: params}, function(data) {
if(data[1])
reject(data[0]);
else
resolve(data[0]);
});
});
})
},
compile: function(code) {
return new Promise(function(resolve, reject) {
postData({call: "compile", args: [code]}, function(data) {
if(data[1])
reject(data[0]);
else
resolve(data[0]);
});
});
},
balanceAt: function(address) {
var promises = [];
if(isPromise(address)) {
promises.push(address.then(function(_address) { address = _address; }));
}
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "getBalanceAt", args: [address]}, function(balance) {
resolve(balance);
});
});
});
},
countAt: function(address) {
var promises = [];
if(isPromise(address)) {
promises.push(address.then(function(_address) { address = _address; }));
}
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "getCountAt", args: [address]}, function(count) {
resolve(count);
});
});
});
},
codeAt: function(address) {
var promises = [];
if(isPromise(address)) {
promises.push(address.then(function(_address) { address = _address; }));
}
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "getCodeAt", args: [address]}, function(code) {
resolve(code);
});
});
});
},
storageAt: function(address, storageAddress) {
var promises = [];
if(isPromise(address)) {
promises.push(address.then(function(_address) { address = _address; }));
}
if(isPromise(storageAddress)) {
promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; }));
}
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "getStorageAt", args: [address, storageAddress]}, function(entry) {
resolve(entry);
});
});
});
},
stateAt: function(address, storageAddress) {
return this.storageAt(address, storageAddress);
},
call: function(params) {
if(params === undefined) {
params = {};
}
if(params.endowment !== undefined)
params.value = params.endowment;
if(params.code !== undefined)
params.data = params.code;
var promises = []
if(isPromise(params.to)) {
promises.push(params.to.then(function(_to) { params.to = _to; }));
}
if(isPromise(params.from)) {
promises.push(params.from.then(function(_from) { params.from = _from; }));
}
if(isPromise(params.data)) {
promises.push(params.data.then(function(_code) { params.data = _code; }));
} else {
if(typeof params.data === "object") {
data = "";
for(var i = 0; i < params.data.length; i++) {
data += params.data[i]
}
} else {
data = params.data;
}
}
// Make sure everything is string
var fields = ["value", "gas", "gasPrice"];
for(var i = 0; i < fields.length; i++) {
if(params[fields[i]] === undefined) {
params[fields[i]] = "";
}
params[fields[i]] = params[fields[i]].toString();
}
// Load promises then call the last "transact".
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "call", args: params}, function(data) {
if(data[1])
reject(data[0]);
else
resolve(data[0]);
});
});
})
},
watch: function(params) {
return new Filter(params);
},
secretToAddress: function(key) {
var promises = [];
if(isPromise(key)) {
promises.push(key.then(function(_key) { key = _key; }));
}
return Q.all(promises).then(function() {
return new Promise(function(resolve, reject) {
postData({call: "getSecretToAddress", args: [key]}, function(address) {
resolve(address);
});
});
});
},
on: function(event, cb) {
if(eth._events[event] === undefined) {
eth._events[event] = [];
}
eth._events[event].push(cb);
return this
},
off: function(event, cb) {
if(eth._events[event] !== undefined) {
var callbacks = eth._events[event];
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] === cb) {
delete callbacks[i];
}
}
}
return this
},
trigger: function(event, data) {
var callbacks = eth._events[event];
if(callbacks !== undefined) {
for(var i = 0; i < callbacks.length; i++) {
// Figure out whether the returned data was an array
// array means multiple return arguments (multiple params)
if(data instanceof Array) {
callbacks[i].apply(this, data);
} else {
callbacks[i].call(this, data);
}
}
}
},
};
// Eth object properties
Object.defineProperty(eth, "key", {
get: function() {
return new Promise(function(resolve, reject) {
postData({call: "getKey"}, function(k) {
resolve(k);
});
});
},
});
Object.defineProperty(eth, "gasPrice", {
get: function() {
return "1000000000000"
}
});
Object.defineProperty(eth, "coinbase", {
get: function() {
return new Promise(function(resolve, reject) {
postData({call: "getCoinBase"}, function(coinbase) {
resolve(coinbase);
});
});
},
});
Object.defineProperty(eth, "listening", {
get: function() {
return new Promise(function(resolve, reject) {
postData({call: "getIsListening"}, function(listening) {
resolve(listening);
});
});
},
});
Object.defineProperty(eth, "mining", {
get: function() {
return new Promise(function(resolve, reject) {
postData({call: "getIsMining"}, function(mining) {
resolve(mining);
});
});
},
});
Object.defineProperty(eth, "peerCount", {
get: function() {
return new Promise(function(resolve, reject) {
postData({call: "getPeerCount"}, function(peerCount) {
resolve(peerCount);
});
});
},
});
var filters = [];
var Filter = function(options) {
filters.push(this);
this.callbacks = [];
this.options = options;
var call;
if(options === "chain") {
call = "newFilterString"
} else if(typeof options === "object") {
call = "newFilter"
}
var self = this; // Cheaper than binding
this.promise = new Promise(function(resolve, reject) {
postData({call: call, args: [options]}, function(id) {
self.id = id;
resolve(id);
});
});
};
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
Filter.prototype.trigger = function(messages, id) {
if(id == this.id) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
}
};
Filter.prototype.uninstall = function() {
this.promise.then(function(id) {
postData({call: "uninstallFilter", args:[id]});
});
};
Filter.prototype.messages = function() {
var self=this;
return Q.all([this.promise]).then(function() {
var id = self.id
return new Promise(function(resolve, reject) {
postData({call: "getMessages", args: [id]}, function(messages) {
resolve(messages);
});
});
});
};
// Register to the messages callback. "messages" will be emitted when new messages
// from the client have been created.
eth.on("messages", function(messages, id) {
for(var i = 0; i < filters.length; i++) {
filters[i].trigger(messages, id);
}
});
var g_seed = 1;
function postData(data, cb) {
data._seed = g_seed;
if(cb) {
eth._callbacks[data._seed] = cb;
}
if(data.args === undefined) {
data.args = [];
}
g_seed++;
navigator.qt.postMessage(JSON.stringify(data));
}
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
cb.call(this, data.data)
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
}
})(this);

View file

@ -1,13 +0,0 @@
// this function is included locally, but you can also include separately via a header definition
function request(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = (function(req) {
return function() {
if(req.readyState === 4) {
callback(req);
}
}
})(xhr);
xhr.open('GET', url, true);
xhr.send('');
}

View file

@ -1,3 +0,0 @@
if(typeof(Promise) === "undefined") {
window.Promise = Q.Promise;
}

File diff suppressed because it is too large Load diff

View file

@ -1,13 +0,0 @@
function HandleMessage(data) {
var message;
try { message = JSON.parse(data) } catch(e) {};
if(message) {
switch(message.type) {
case "coinbase":
return eth.coinBase();
case "block":
return eth.blockByNumber(0);
}
}
}

View file

@ -1,44 +0,0 @@
<!doctype>
<html>
<head>
<title>Tests</title>
</head>
<body>
<button onclick="test();">Test me</button>
<script type="text/javascript">
function test() {
var filter = eth.watch({
latest: -1,
from: "e6716f9544a56c530d868e4bfbacb172315bdead",
altered: ["aabb", {id: "eeff", "at": "aabb"}],
});
filter.changed(function(messages) {
console.log("messages", messages)
})
filter.getMessages(function(messages) {
console.log("getMessages", messages)
});
eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(entries) {
for(var i = 0; i < entries.length; i++) {
console.log(entries[i].key, " : ", entries[i].value)
}
})
eth.getBlock("f70097659f329a09642a27f11338d9269de64f1d4485786e36bfc410832148cd", function(block) {
console.log(block)
})
eth.mutan("var a = 10", function(code) {
console.log("code", code)
});
}
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

View file

@ -1,22 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.0;
import QtQuick.Layouts 1.0;
import Ethereum 1.0
ApplicationWindow {
minimumWidth: 500
maximumWidth: 500
maximumHeight: 400
minimumHeight: 400
function onNewBlockCb(block) {
console.log("Please overwrite onNewBlock(block):", block)
}
function onObjectChangeCb(stateObject) {
console.log("Please overwrite onObjectChangeCb(object)", stateObject)
}
function onStorageChangeCb(storageObject) {
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
console.log("Please overwrite onStorageChangeCb(object)", ev)
}
}

View file

@ -1,70 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.0;
import QtQuick.Layouts 1.0;
import Ethereum 1.0
QmlApp {
minimumWidth: 350
maximumWidth: 350
maximumHeight: 80
minimumHeight: 80
title: "Generic Coin"
property string contractAddr: "f299f6c74515620e4c4cd8fe3d205b5c4f2e25c8"
property string addr: "2ef47100e0787b915105fd5e3f4ff6752079d5cb"
Component.onCompleted: {
eth.watch(contractAddr, addr)
eth.watch(addr, contractAddr)
setAmount()
}
function onStorageChangeCb(storageObject) {
setAmount()
}
function setAmount(){
var state = eth.getStateObject(contractAddr)
var storage = state.getStorage(addr)
amountLabel.text = storage
}
Column {
spacing: 5
Row {
spacing: 20
Label {
id: genLabel
text: "Generic coin balance:"
}
Label {
id: amountLabel
}
}
Row {
spacing: 20
TextField {
id: address
placeholderText: "Address"
}
TextField {
id: amount
placeholderText: "Amount"
}
}
Button {
text: "Send coins"
onClicked: {
var privKey = eth.getKey().privateKey
if(privKey){
var result = eth.transact(privKey, contractAddr, 0,"100000","250", "0x" + address.text + "\n" + amount.text)
resultTx.text = result.hash
}
}
}
Label {
id: resultTx
}
}
}

View file

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

View file

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

View file

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

View file

@ -1,45 +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
Rectangle {
property var title: "JavaScript"
property var iconSource: "../tx.png"
property var menuItem
objectName: "javascriptView"
visible: false
anchors.fill: parent
TextField {
id: input
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: 20
Keys.onReturnPressed: {
var res = eth.evalJavascriptString(this.text);
this.text = "";
output.append(res)
}
}
TextArea {
id: output
text: "> JSRE Ready..."
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: input.top
}
}
}

View file

@ -1,184 +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
Rectangle {
id: root
property var title: "JeffCoin"
property var iconSource: "./views/jeffcoin/jeff.png"
property var menuItem
property var filter
property var address: "fc0a9436890478bb9b1c6ed7455c2535366f4a99"
function insertTx(message, blockNumber) {
if(!message) return;
var from = message.from
var to = message.input.substr(24, 40)
var value = eth.fromNumber(message.input.substr(64, 64))
var me = eth.key().address;
if((to == me|| from == me) && message.input.length == 128) {
txModel.insert(0, {confirmations: blockNumber - message.number, from: from, to: to, value: value})
}
}
function setBalance() {
var jeffCoinAmount = eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
menuItem.secondaryTitle = jeffCoinAmount
balance.text = "<b>Balance</b>: " + jeffCoinAmount;
}
function onReady() {
setBalance()
filter = new ethx.watch({latest: -1, to: address})
filter.changed(function(messages) {
setBalance()
var blockNumber = eth.block(-1).number;
for(var i = 0; i < messages.length; i++) {
insertTx(messages.get(i), blockNumber);
}
});
var blockNumber = eth.block(-1).number;
var messages = filter.messages()
for(var i = messages.length-1; i >= 0; i--) {
var message = messages.get(i)
insertTx(message, blockNumber)
}
var chainChanged = ethx.watch("chain")
chainChanged.changed(function() {
for(var i = 0; i < txModel.count; i++) {
var entry = txModel.get(i);
entry.confirmations++;
}
});
}
function onDestroy() {
filter.uninstall()
}
ColumnLayout {
spacing: 10
y: 40
anchors.fill: parent
Text {
id: balance
text: "<b>Balance</b>: " + eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
font.pixelSize: 24
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: 20
}
}
Rectangle {
id: newTxPane
color: "#ececec"
border.color: "#cccccc"
border.width: 1
anchors {
top: balance.bottom
topMargin: 10
left: parent.left
leftMargin: 5
right: parent.right
rightMargin: 5
}
height: 100
RowLayout {
id: amountFields
spacing: 10
anchors {
top: parent.top
topMargin: 20
left: parent.left
leftMargin: 20
}
Text {
text: "JΞF "
}
// There's something off with the row layout where textfields won't listen to the width setting
Rectangle {
width: 50
height: 20
TextField {
id: txValue
width: parent.width
placeholderText: "0.00"
}
}
}
RowLayout {
id: toFields
spacing: 10
anchors {
top: amountFields.bottom
topMargin: 5
left: parent.left
leftMargin: 20
}
Text {
text: "To"
}
Rectangle {
width: 200
height: 20
TextField {
id: txTo
width: parent.width
placeholderText: "Address or name"
}
}
Button {
text: "Send"
onClicked: {
eth.transact({from: eth.key().privateKey, to:address, gas: "9000", gasPrice: "100000000000", data: ["0x"+txTo.text, txValue.text]})
}
}
}
}
Rectangle {
anchors {
left: parent.left
right: parent.right
top: newTxPane.bottom
topMargin: 10
bottom: parent.bottom
}
TableView {
id: txTableView
anchors.fill : parent
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
TableViewColumn{ role: "confirmations" ; title: "Confirmations" ; width: 100 }
model: ListModel {
id: txModel
Component.onCompleted: {
}
}
}
}
}
}

View file

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

View file

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

View file

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

View file

@ -1,894 +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
import "../ext/filter.js" as Eth
import "../ext/http.js" as Http
ApplicationWindow {
id: root
property alias miningButtonText: miningButton.text
property var ethx : Eth.ethx
property var web
width: 1200
height: 820
minimumHeight: 300
title: "Mist"
// This signal is used by the filter API. The filter API connects using this signal handler from
// the different QML files and plugins.
signal messages(var messages, int id);
function invokeFilterCallback(data, receiverSeed) {
//var messages = JSON.parse(data)
// Signal handler
messages(data, receiverSeed);
root.web.messages(data, receiverSeed);
}
TextField {
id: copyElementHax
visible: false
}
function copyToClipboard(text) {
copyElementHax.text = text
copyElementHax.selectAll()
copyElementHax.copy()
}
// Takes care of loading all default plugins
Component.onCompleted: {
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
root.web = browser.view;
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/javascript.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/jeffcoin/jeffcoin.qml", {noAdd: true, close: false, section: "apps"})
mainSplit.setView(wallet.view, wallet.menuItem);
// Call the ready handler
gui.done();
}
function addViews(view, path, options) {
var views = mainSplit.addComponent(view, options)
views.menuItem.path = path
mainSplit.views.push(views);
if(!options.noAdd) {
gui.addPlugin(path)
}
return views
}
function addPlugin(path, options) {
try {
if(typeof(path) === "string" && /^https?/.test(path)) {
console.log('load http')
Http.request(path, function(o) {
if(o.status === 200) {
var view = Qt.createQmlObject(o.responseText, mainView, path)
addViews(view, path, options)
}
})
return
}
var component = Qt.createComponent(path);
if(component.status != Component.Ready) {
if(component.status == Component.Error) {
ethx.note("error: ", component.errorString());
}
return
}
var view = mainView.createView(component, options)
var views = addViews(view, path, options)
return views
} catch(e) {
ethx.note(e)
}
}
menuBar: MenuBar {
Menu {
title: "File"
MenuItem {
text: "Import App"
shortcut: "Ctrl+o"
onTriggered: {
generalFileDialog.show(true, importApp)
}
}
/*
MenuItem {
text: "Browser"
onTriggered: eth.openBrowser()
}
*/
MenuItem {
text: "Add plugin"
onTriggered: {
generalFileDialog.show(true, function(path) {
addPlugin(path, {close: true, section: "apps"})
})
}
}
MenuSeparator {}
MenuItem {
text: "Import key"
shortcut: "Ctrl+i"
onTriggered: {
generalFileDialog.show(true, function(path) {
gui.importKey(path)
})
}
}
MenuItem {
text: "Export keys"
shortcut: "Ctrl+e"
onTriggered: {
generalFileDialog.show(false, function(path) {
})
}
}
}
Menu {
title: "Developer"
MenuItem {
iconSource: "../icecream.png"
text: "Debugger"
shortcut: "Ctrl+d"
onTriggered: eth.startDebugger()
}
MenuItem {
text: "Import Tx"
onTriggered: {
txImportDialog.visible = true
}
}
MenuItem {
text: "Run JS file"
onTriggered: {
generalFileDialog.show(true, function(path) {
eth.evalJavascriptFile(path)
})
}
}
MenuItem {
text: "Dump state"
onTriggered: {
generalFileDialog.show(false, function(path) {
// Empty hash for latest
gui.dumpState("", path)
})
}
}
MenuSeparator {}
MenuItem {
id: miningSpeed
text: "Mining: Turbo"
onTriggered: {
gui.toggleTurboMining()
if(text == "Mining: Turbo") {
text = "Mining: Normal";
} else {
text = "Mining: Turbo";
}
}
}
}
Menu {
title: "Network"
MenuItem {
text: "Add Peer"
shortcut: "Ctrl+p"
onTriggered: {
addPeerWin.visible = true
}
}
MenuItem {
text: "Show Peers"
shortcut: "Ctrl+e"
onTriggered: {
peerWindow.visible = true
}
}
}
Menu {
title: "Help"
MenuItem {
text: "About"
onTriggered: {
aboutWin.visible = true
}
}
}
Menu {
title: "GLOBAL SHORTCUTS"
visible: false
MenuItem {
visible: false
shortcut: "Ctrl+l"
onTriggered: {
url.focus = true
}
}
}
}
statusBar: StatusBar {
height: 32
RowLayout {
Button {
id: miningButton
text: "Start Mining"
onClicked: {
gui.toggleMining()
}
}
RowLayout {
Label {
id: walletValueLabel
font.pixelSize: 10
styleColor: "#797979"
}
}
}
Label {
y: 6
objectName: "miningLabel"
visible: true
font.pixelSize: 10
anchors.right: lastBlockLabel.left
anchors.rightMargin: 5
}
Label {
y: 6
id: lastBlockLabel
objectName: "lastBlockLabel"
visible: true
text: ""
font.pixelSize: 10
anchors.right: peerGroup.left
anchors.rightMargin: 5
}
ProgressBar {
id: syncProgressIndicator
visible: false
objectName: "syncProgressIndicator"
y: 3
width: 140
indeterminate: true
anchors.right: peerGroup.left
anchors.rightMargin: 5
}
RowLayout {
id: peerGroup
y: 7
anchors.right: parent.right
MouseArea {
onDoubleClicked: peerWindow.visible = true
anchors.fill: parent
}
Label {
id: peerLabel
font.pixelSize: 8
text: "0 / 0"
}
Image {
id: peerImage
width: 10; height: 10
source: "../network.png"
}
}
}
property var blockModel: ListModel {
id: blockModel
}
SplitView {
property var views: [];
id: mainSplit
anchors.fill: parent
resizing: false
function setView(view, menu) {
for(var i = 0; i < views.length; i++) {
views[i].view.visible = false
views[i].menuItem.setSelection(false)
}
view.visible = true
//menu.border.color = "#CCCCCC"
//menu.color = "#FFFFFFFF"
menu.setSelection(true)
}
function addComponent(view, options) {
view.visible = false
view.anchors.fill = mainView
if( !view.hasOwnProperty("iconSource") ) {
console.log("Could not load plugin. Property 'iconSourc' not found on view.");
return;
}
var menuItem = menu.createMenuItem(view.iconSource, view, options);
if( view.hasOwnProperty("menuItem") ) {
view.menuItem = menuItem;
}
if( view.hasOwnProperty("onReady") ) {
view.onReady.call(view)
}
if( options.active ) {
setView(view, menuItem)
}
return {view: view, menuItem: menuItem}
}
/*********************
* Main menu.
********************/
Rectangle {
id: menu
Layout.minimumWidth: 210
Layout.maximumWidth: 210
anchors.top: parent.top
color: "#ececec"
Component {
id: menuItemTemplate
Rectangle {
id: menuItem
property var view;
property var path;
property var closable;
property alias title: label.text
property alias icon: icon.source
property alias secondaryTitle: secondary.text
function setSelection(on) {
sel.visible = on
}
width: 206
height: 28
color: "#00000000"
anchors {
left: parent.left
leftMargin: 4
}
Rectangle {
id: sel
visible: false
anchors.fill: parent
color: "#00000000"
Rectangle {
id: r
anchors.fill: parent
border.color: "#CCCCCC"
border.width: 1
radius: 5
color: "#FFFFFFFF"
}
Rectangle {
anchors {
top: r.top
bottom: r.bottom
right: r.right
}
width: 10
color: "#FFFFFFFF"
Rectangle {
anchors {
left: parent.left
right: parent.right
top: parent.top
}
height: 1
color: "#CCCCCC"
}
Rectangle {
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: 1
color: "#CCCCCC"
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
mainSplit.setView(view, menuItem)
}
}
Image {
id: icon
height: 20
width: 20
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
leftMargin: 3
}
MouseArea {
anchors.fill: parent
onClicked: {
menuItem.closeApp()
}
}
}
Text {
id: label
anchors {
left: icon.right
verticalCenter: parent.verticalCenter
leftMargin: 3
}
color: "#0D0A01"
font.pixelSize: 12
}
Text {
id: secondary
anchors {
right: parent.right
rightMargin: 8
verticalCenter: parent.verticalCenter
}
color: "#AEADBE"
font.pixelSize: 12
}
function closeApp() {
if(!this.closable) { return; }
if(this.view.hasOwnProperty("onDestroy")) {
this.view.onDestroy.call(this.view)
}
this.view.destroy()
this.destroy()
gui.removePlugin(this.path)
}
}
}
function createMenuItem(icon, view, options) {
if(options === undefined) {
options = {};
}
var section;
switch(options.section) {
case "ethereum":
section = menuDefault;
break;
case "legacy":
section = menuLegacy;
break;
default:
section = menuApps;
break;
}
var comp = menuItemTemplate.createObject(section)
comp.view = view
comp.title = view.title
comp.icon = view.iconSource
comp.closable = options.close;
return comp
}
ColumnLayout {
id: menuColumn
y: 10
width: parent.width
anchors.left: parent.left
anchors.right: parent.right
spacing: 3
Text {
text: "ETHEREUM"
font.bold: true
anchors {
left: parent.left
leftMargin: 5
}
color: "#888888"
}
ColumnLayout {
id: menuDefault
spacing: 3
anchors {
left: parent.left
right: parent.right
}
}
Text {
text: "APPS"
font.bold: true
anchors {
left: parent.left
leftMargin: 5
}
color: "#888888"
}
ColumnLayout {
id: menuApps
spacing: 3
anchors {
left: parent.left
right: parent.right
}
}
Text {
text: "DEBUG"
font.bold: true
anchors {
left: parent.left
leftMargin: 5
}
color: "#888888"
}
ColumnLayout {
id: menuLegacy
spacing: 3
anchors {
left: parent.left
right: parent.right
}
}
}
}
/*********************
* Main view
********************/
Rectangle {
anchors.right: parent.right
anchors.left: menu.right
anchors.bottom: parent.bottom
anchors.top: parent.top
color: "#00000000"
Rectangle {
id: urlPane
height: 40
color: "#00000000"
anchors {
left: parent.left
right: parent.right
leftMargin: 5
rightMargin: 5
top: parent.top
topMargin: 5
}
TextField {
id: url
objectName: "url"
placeholderText: "DApp URL"
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: 5
rightMargin: 5
leftMargin: 5
}
Keys.onReturnPressed: {
addPlugin(this.text, {close: true, section: "apps"})
}
}
}
// Border
Rectangle {
id: divider
anchors {
left: parent.left
right: parent.right
top: urlPane.bottom
}
z: -1
height: 1
color: "#CCCCCC"
}
Rectangle {
id: mainView
color: "#00000000"
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.top: divider.bottom
function createView(component) {
var view = component.createObject(mainView)
return view;
}
}
}
}
/******************
* Dialogs
*****************/
FileDialog {
id: generalFileDialog
property var callback;
onAccepted: {
var path = this.fileUrl.toString();
callback.call(this, path);
}
function show(selectExisting, callback) {
generalFileDialog.callback = callback;
generalFileDialog.selectExisting = selectExisting;
this.open();
}
}
/******************
* Wallet functions
*****************/
function importApp(path) {
var ext = path.split('.').pop()
if(ext == "html" || ext == "htm") {
eth.openHtml(path)
}else if(ext == "qml"){
addPlugin(path, {close: true, section: "apps"})
}
}
function setWalletValue(value) {
walletValueLabel.text = value
}
function loadPlugin(name) {
console.log("Loading plugin" + name)
var view = mainView.addPlugin(name)
}
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
//flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
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" }
}
}
}
Window {
id: aboutWin
visible: false
title: "About"
minimumWidth: 350
maximumWidth: 350
maximumHeight: 200
minimumHeight: 200
Image {
id: aboutIcon
height: 150
width: 150
fillMode: Image.PreserveAspectFit
smooth: true
source: "../facet.png"
x: 10
y: 10
}
Text {
anchors.left: aboutIcon.right
anchors.leftMargin: 10
anchors.top: parent.top
anchors.topMargin: 30
font.pointSize: 12
text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes"
}
}
Window {
id: txImportDialog
minimumWidth: 270
maximumWidth: 270
maximumHeight: 50
minimumHeight: 50
TextField {
id: txImportField
width: 170
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
onAccepted: {
}
}
Button {
anchors.left: txImportField.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 5
text: "Import"
onClicked: {
eth.importTx(txImportField.text)
txImportField.visible = false
}
}
Component.onCompleted: {
addrField.focus = true
}
}
Window {
id: addPeerWin
visible: false
minimumWidth: 300
maximumWidth: 300
maximumHeight: 50
minimumHeight: 50
title: "Connect to peer"
ComboBox {
id: addrField
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: addPeerButton.left
anchors.leftMargin: 10
anchors.rightMargin: 10
onAccepted: {
eth.connectToPeer(addrField.currentText)
addPeerWin.visible = false
}
editable: true
model: ListModel { id: pastPeers }
Component.onCompleted: {
var ips = eth.pastPeers()
for(var i = 0; i < ips.length; i++) {
pastPeers.append({text: ips.get(i)})
}
pastPeers.insert(0, {text: "poc-6.ethdev.com:30303"})
}
}
Button {
id: addPeerButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 10
text: "Add"
onClicked: {
eth.connectToPeer(addrField.currentText)
addPeerWin.visible = false
}
}
Component.onCompleted: {
addrField.focus = true
}
}
}

View file

@ -1,409 +0,0 @@
import QtQuick 2.0
import QtWebKit 3.0
import QtWebKit.experimental 1.0
import QtQuick.Controls 1.0;
import QtQuick.Controls.Styles 1.0
import QtQuick.Layouts 1.0;
import QtQuick.Window 2.1;
import Ethereum 1.0
import "../ext/qml_messaging.js" as Messaging
//ApplicationWindow {
Rectangle {
id: window
property var title: "Browser"
property var iconSource: "../browser.png"
property var menuItem
property alias url: webview.url
property alias webView: webview
Component.onCompleted: {
webview.url = "http://etherian.io"
}
signal messages(var messages, int id);
onMessages: {
// Bit of a cheat to get proper JSON
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
webview.postEvent("messages", [m, id]);
}
Item {
objectName: "root"
id: root
anchors.fill: parent
state: "inspectorShown"
RowLayout {
id: navBar
height: 40
anchors {
left: parent.left
right: parent.right
leftMargin: 7
}
Button {
id: back
onClicked: {
webview.goBack()
}
style: ButtonStyle {
background: Image {
source: "../back.png"
width: 30
height: 30
}
}
}
TextField {
anchors {
left: back.right
right: toggleInspector.left
leftMargin: 5
rightMargin: 5
}
text: "http://etherian.io"
id: uriNav
y: parent.height / 2 - this.height / 2
Keys.onReturnPressed: {
webview.url = this.text;
}
}
Button {
id: toggleInspector
anchors {
right: parent.right
}
iconSource: "../bug.png"
onClicked: {
if(inspector.visible == true){
inspector.visible = false
}else{
inspector.visible = true
inspector.url = webview.experimental.remoteInspectorUrl
}
}
}
}
WebView {
objectName: "webView"
id: webview
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
top: navBar.bottom
}
property var cleanPath: false
onNavigationRequested: {
if(!this.cleanPath) {
var uri = request.url.toString();
if(!/.*\:\/\/.*/.test(uri)) {
uri = "http://" + uri;
}
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
if(reg.test(uri)) {
uri.replace(reg, function(match, pre, domain, path) {
uri = pre;
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
var ip = [];
for(var i = 0, l = lookup.length; i < l; i++) {
ip.push(lookup.charCodeAt(i))
}
if(ip.length != 0) {
uri += lookup;
} else {
uri += domain;
}
uri += path;
});
}
this.cleanPath = true;
webview.url = uri;
} else {
// Prevent inf loop.
this.cleanPath = false;
}
}
function sendMessage(data) {
webview.experimental.postMessage(JSON.stringify(data))
}
onTitleChanged: {
var data = Messaging.HandleMessage(title);
if(data) {
sendMessage(data)
}
}
experimental.preferences.javascriptEnabled: true
experimental.preferences.navigatorQtObjectEnabled: true
experimental.preferences.developerExtrasEnabled: true
experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"]
experimental.onMessageReceived: {
console.log("[onMessageReceived]: ", message.data)
// TODO move to messaging.js
var data = JSON.parse(message.data)
try {
switch(data.call) {
case "compile":
postData(data._seed, eth.compile(data.args[0]))
break
case "getCoinBase":
postData(data._seed, eth.coinBase())
break
case "getIsListening":
postData(data._seed, eth.isListening())
break
case "getIsMining":
postData(data._seed, eth.isMining())
break
case "getPeerCount":
postData(data._seed, eth.peerCount())
break
case "getCountAt":
require(1)
postData(data._seed, eth.txCountAt(data.args[0]))
break
case "getCodeAt":
require(1)
var code = eth.codeAt(data.args[0])
postData(data._seed, code);
break
case "getBlockByNumber":
var block = eth.blockByNumber(data.args[0])
postData(data._seed, block)
break
case "getBlockByHash":
var block = eth.blockByHash(data.args[0])
postData(data._seed, block)
break
case "transact":
require(5)
var tx = eth.transact(data.args)
postData(data._seed, tx)
break
case "getStorageAt":
require(2);
var storage = eth.storageAt(data.args[0], data.args[1]);
postData(data._seed, storage)
break
case "call":
require(1);
var ret = eth.call(data.args)
postData(data._seed, ret)
break
case "getEachStorage":
require(1);
var storage = JSON.parse(eth.eachStorage(data.args[0]))
postData(data._seed, storage)
break
case "getTransactionsFor":
require(1);
var txs = eth.transactionsFor(data.args[0], true)
postData(data._seed, txs)
break
case "getBalanceAt":
require(1);
postData(data._seed, eth.balanceAt(data.args[0]));
break
case "getKey":
var key = eth.key().privateKey;
postData(data._seed, key)
break
case "watch":
require(2)
eth.watch(data.args[0], data.args[1])
case "disconnect":
require(1)
postData(data._seed, null)
break;
case "getSecretToAddress":
require(1)
var addr = eth.secretToAddress(data.args[0])
console.log("getsecret", addr)
postData(data._seed, addr)
break;
case "messages":
require(1);
var messages = JSON.parse(eth.getMessages(data.args[0]))
postData(data._seed, messages)
break
case "mutan":
require(1)
var code = eth.compileMutan(data.args[0])
postData(data._seed, "0x"+code)
break;
case "newFilterString":
require(1)
var id = eth.newFilterString(data.args[0])
postData(data._seed, id);
break;
case "newFilter":
require(1)
var id = eth.newFilter(data.args[0])
postData(data._seed, id);
break;
case "getMessages":
require(1);
var messages = eth.messages(data.args[0]);
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
postData(data._seed, m);
break;
case "deleteFilter":
require(1);
eth.uninstallFilter(data.args[0])
break;
}
} catch(e) {
console.log(data.call + ": " + e)
postData(data._seed, null);
}
}
function post(seed, data) {
postData(data._seed, data)
}
function require(args, num) {
if(args.length < num) {
throw("required argument count of "+num+" got "+args.length);
}
}
function postData(seed, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
}
function postEvent(event, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
}
function onWatchedCb(data, id) {
var messages = JSON.parse(data)
postEvent("watched:"+id, messages)
}
function onNewBlockCb(block) {
postEvent("block:new", block)
}
function onObjectChangeCb(stateObject) {
postEvent("object:"+stateObject.address(), stateObject)
}
function onStorageChangeCb(storageObject) {
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
postEvent(ev, [storageObject.address, storageObject.value])
}
}
Rectangle {
id: sizeGrip
color: "gray"
visible: false
height: 10
anchors {
left: root.left
right: root.right
}
y: Math.round(root.height * 2 / 3)
MouseArea {
anchors.fill: parent
drag.target: sizeGrip
drag.minimumY: 0
drag.maximumY: root.height
drag.axis: Drag.YAxis
}
}
WebView {
id: inspector
visible: false
anchors {
left: root.left
right: root.right
top: sizeGrip.bottom
bottom: root.bottom
}
}
states: [
State {
name: "inspectorShown"
PropertyChanges {
target: inspector
}
}
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,148 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strconv"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
)
type plugin struct {
Name string `json:"name"`
Path string `json:"path"`
}
func (gui *Gui) Println(v ...interface{}) {
gui.printLog(fmt.Sprintln(v...))
}
func (gui *Gui) Printf(format string, v ...interface{}) {
gui.printLog(fmt.Sprintf(format, v...))
}
// Print function that logs directly to the GUI
func (gui *Gui) printLog(s string) {
/*
str := strings.TrimRight(s, "\n")
lines := strings.Split(str, "\n")
view := gui.getObjectByName("infoView")
for _, line := range lines {
view.Call("addLog", line)
}
*/
}
func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) {
var data string
if len(recipient) == 0 {
code, err := ethutil.Compile(d, false)
if err != nil {
return nil, err
}
data = ethutil.Bytes2Hex(code)
} else {
data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
}
return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
}
func (gui *Gui) SetCustomIdentifier(customIdentifier string) {
gui.clientIdentity.SetCustomIdentifier(customIdentifier)
gui.config.Save("id", customIdentifier)
}
func (gui *Gui) GetCustomIdentifier() string {
return gui.clientIdentity.GetCustomIdentifier()
}
func (gui *Gui) ToggleTurboMining() {
gui.miner.ToggleTurbo()
}
// functions that allow Gui to implement interface ethlog.LogSystem
func (gui *Gui) SetLogLevel(level ethlog.LogLevel) {
gui.logLevel = level
gui.stdLog.SetLogLevel(level)
gui.config.Save("loglevel", level)
}
func (gui *Gui) GetLogLevel() ethlog.LogLevel {
return gui.logLevel
}
func (self *Gui) AddPlugin(pluginPath string) {
self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath}
json, _ := json.MarshalIndent(self.plugins, "", " ")
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
}
func (self *Gui) RemovePlugin(pluginPath string) {
delete(self.plugins, pluginPath)
json, _ := json.MarshalIndent(self.plugins, "", " ")
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
}
// this extra function needed to give int typecast value to gui widget
// that sets initial loglevel to default
func (gui *Gui) GetLogLevelInt() int {
return int(gui.logLevel)
}
func (self *Gui) DumpState(hash, path string) {
var stateDump []byte
if len(hash) == 0 {
stateDump = self.eth.StateManager().CurrentState().Dump()
} else {
var block *ethchain.Block
if hash[0] == '#' {
i, _ := strconv.Atoi(hash[1:])
block = self.eth.BlockChain().GetBlockByNumber(uint64(i))
} else {
block = self.eth.BlockChain().GetBlock(ethutil.Hex2Bytes(hash))
}
if block == nil {
logger.Infof("block err: not found %s\n", hash)
return
}
stateDump = block.State().Dump()
}
file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)
if err != nil {
logger.Infoln("dump err: ", err)
return
}
defer file.Close()
logger.Infof("dumped state (%s) to %s\n", hash, path)
file.Write(stateDump)
}
func (gui *Gui) ToggleMining() {
var txt string
if gui.eth.Mining {
utils.StopMining(gui.eth)
txt = "Start mining"
gui.getObjectByName("miningLabel").Set("visible", false)
} else {
utils.StartMining(gui.eth)
gui.miner = utils.GetMiner()
txt = "Stop mining"
gui.getObjectByName("miningLabel").Set("visible", true)
}
gui.win.Root().Set("miningButtonText", txt)
}

View file

@ -1,342 +0,0 @@
package main
import (
"fmt"
"math/big"
"strconv"
"strings"
"unicode"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethvm"
"github.com/ethereum/go-ethereum/utils"
"gopkg.in/qml.v1"
)
type DebuggerWindow struct {
win *qml.Window
engine *qml.Engine
lib *UiLib
vm *ethvm.Vm
Db *Debugger
state *ethstate.State
}
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)
w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &ethvm.Vm{}}
w.Db = NewDebugger(w)
return w
}
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 []byte) {
self.win.Root().Call("clearAsm")
dis := ethchain.Disassemble(data)
for _, str := range dis {
self.win.Root().Call("setAsm", str)
}
}
func (self *DebuggerWindow) Compile(code string) {
var err error
script := ethutil.StringToByteFunc(code, func(s string) (ret []byte) {
ret, err = ethutil.Compile(s, true)
return
})
if err == nil {
self.SetAsm(script)
}
}
// Used by QML
func (self *DebuggerWindow) AutoComp(code string) {
if self.Db.done {
self.Compile(code)
}
}
func (self *DebuggerWindow) ClearLog() {
self.win.Root().Call("clearLog")
}
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
self.Stop()
defer func() {
if r := recover(); r != nil {
self.Logf("compile FAULT: %v", r)
}
}()
data := utils.FormatTransactionData(dataStr)
var err error
script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
ret, err = ethutil.Compile(s, false)
return
})
if err != nil {
self.Logln(err)
return
}
var (
gas = ethutil.Big(gasStr)
gasPrice = ethutil.Big(gasPriceStr)
value = ethutil.Big(valueStr)
// Contract addr as test address
keyPair = self.lib.eth.KeyManager().KeyPair()
)
state := self.lib.eth.StateManager().TransState()
account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
contract := ethstate.NewStateObject([]byte{0})
contract.Balance = value
self.SetAsm(script)
block := self.lib.eth.BlockChain().CurrentBlock
callerClosure := ethvm.NewClosure(&ethstate.Message{}, account, contract, script, gas, gasPrice)
env := utils.NewEnv(state, block, account.Address(), value)
vm := ethvm.New(env)
vm.Verbose = true
vm.Dbg = self.Db
self.vm = vm
self.Db.done = false
self.Logf("callsize %d", len(script))
go func() {
ret, g, err := callerClosure.Call(vm, data)
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()
}
func (self *DebuggerWindow) Continue() {
self.vm.Stepping = false
self.Next()
}
func (self *DebuggerWindow) Stop() {
if !self.Db.done {
self.Db.Q <- true
}
}
func (self *DebuggerWindow) ExecCommand(command string) {
if len(command) > 0 {
cmd := strings.Split(command, " ")
switch cmd[0] {
case "help":
self.Logln("Debugger commands:")
self.Logln("break, bp Set breakpoint on instruction")
self.Logln("clear [log, break, bp] Clears previous set sub-command(s)")
case "break", "bp":
if len(cmd) > 1 {
lineNo, err := strconv.Atoi(cmd[1])
if err != nil {
self.Logln(err)
break
}
self.Db.breakPoints = append(self.Db.breakPoints, int64(lineNo))
self.Logf("break point set on instruction %d", lineNo)
} else {
self.Logf("'%s' requires line number", cmd[0])
}
case "clear":
if len(cmd) > 1 {
switch cmd[1] {
case "break", "bp":
self.Db.breakPoints = nil
self.Logln("Breakpoints cleared")
case "log":
self.ClearLog()
default:
self.Logf("clear '%s' is not valid", cmd[1])
}
} else {
self.Logln("'clear' requires sub command")
}
default:
self.Logf("Unknown command %s", cmd[0])
}
}
}
type Debugger struct {
N chan bool
Q chan bool
done, interrupt bool
breakPoints []int64
main *DebuggerWindow
win *qml.Window
}
func NewDebugger(main *DebuggerWindow) *Debugger {
db := &Debugger{make(chan bool), make(chan bool), true, false, nil, main, main.win}
return db
}
type storeVal struct {
Key, Value string
}
func (self *Debugger) BreakHook(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
self.main.Logln("break on instr:", pc)
return self.halting(pc, op, mem, stack, stateObject)
}
func (self *Debugger) StepHook(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
return self.halting(pc, op, mem, stack, stateObject)
}
func (self *Debugger) SetCode(byteCode []byte) {
self.main.SetAsm(byteCode)
}
func (self *Debugger) BreakPoints() []int64 {
return self.breakPoints
}
func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.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+16 <= mem.Len(); i += 16 {
dat := mem.Data()[i : i+16]
var str string
for _, d := range dat {
if unicode.IsGraphic(rune(d)) {
str += string(d)
} else {
str += "?"
}
}
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)})
addr += 16
}
for _, val := range stack.Data() {
d.win.Root().Call("setStack", val.String())
}
stateObject.EachStorage(func(key string, node *ethutil.Value) {
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
})
stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32))
psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64())
d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`<b>stack ptr</b>: %v`, stackFrameAt))
d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`<b>stack size</b>: %d`, psize))
d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`<b>mem size</b>: %v`, mem.Len()))
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

@ -1,36 +0,0 @@
package main
import (
"fmt"
"os"
"gopkg.in/qml.v1"
)
func ErrorWindow(err error) {
engine := qml.NewEngine()
component, e := engine.LoadString("local", qmlErr)
if e != nil {
fmt.Println("err:", err)
os.Exit(1)
}
win := component.CreateWindow(nil)
win.Root().ObjectByName("label").Set("text", err.Error())
win.Show()
win.Wait()
}
const qmlErr = `
import QtQuick 2.0; import QtQuick.Controls 1.0;
ApplicationWindow {
width: 600; height: 150;
flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
title: "Error"
Text {
x: parent.width / 2 - this.width / 2;
y: parent.height / 2 - this.height / 2;
objectName: "label";
}
}
`

View file

@ -1,100 +0,0 @@
package main
import (
"flag"
"fmt"
"os"
"os/user"
"path"
"path/filepath"
"runtime"
"bitbucket.org/kardianos/osext"
"github.com/ethereum/eth-go/ethlog"
)
var Identifier string
var KeyRing string
var KeyStore string
var StartRpc bool
var RpcPort int
var UseUPnP bool
var OutboundPort string
var ShowGenesis bool
var AddPeer string
var MaxPeer int
var GenAddr bool
var UseSeed bool
var SecretFile string
var ExportDir string
var NonInteractive bool
var Datadir string
var LogFile string
var ConfigFile string
var DebugFile string
var LogLevel int
// flags specific to gui client
var AssetPath string
//TODO: If we re-use the one defined in cmd.go the binary osx image crashes. If somebody finds out why we can dry this up.
func defaultAssetPath() string {
var assetPath string
// If the current working directory is the go-ethereum dir
// assume a debug build and use the source directory as
// asset directory.
pwd, _ := os.Getwd()
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist") {
assetPath = path.Join(pwd, "assets")
} else {
switch runtime.GOOS {
case "darwin":
// Get Binary Directory
exedir, _ := osext.ExecutableFolder()
assetPath = filepath.Join(exedir, "../Resources")
case "linux":
assetPath = "/usr/share/mist"
case "windows":
assetPath = "./assets"
default:
assetPath = "."
}
}
return assetPath
}
func defaultDataDir() string {
usr, _ := user.Current()
return path.Join(usr.HomeDir, ".mist")
}
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
func Init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "%s [options] [filename]:\noptions precedence: default < config file < environment variables < command line\n", os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.BoolVar(&UseUPnP, "upnp", true, "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(&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.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
flag.IntVar(&LogLevel, "loglevel", int(ethlog.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
flag.StringVar(&AssetPath, "asset_path", defaultAssetPath(), "absolute path to GUI assets directory")
flag.Parse()
}

View file

@ -1,530 +0,0 @@
package main
import "C"
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"path"
"runtime"
"strconv"
"strings"
"time"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethminer"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"gopkg.in/qml.v1"
)
/*
func LoadExtension(path string) (uintptr, error) {
lib, err := ffi.NewLibrary(path)
if err != nil {
return 0, err
}
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
if err != nil {
return 0, err
}
ptr := so()
err = lib.Close()
if err != nil {
return 0, err
}
return ptr.Interface().(uintptr), nil
}
*/
var logger = ethlog.NewLogger("GUI")
type Gui struct {
// The main application window
win *qml.Window
// QML Engine
engine *qml.Engine
component *qml.Common
qmlDone bool
// The ethereum interface
eth *eth.Ethereum
// The public Ethereum library
uiLib *UiLib
txDb *ethdb.LDBDatabase
logLevel ethlog.LogLevel
open bool
pipe *ethpipe.JSPipe
Session string
clientIdentity *ethwire.SimpleClientIdentity
config *ethutil.ConfigManager
plugins map[string]plugin
miner *ethminer.Miner
stdLog ethlog.LogSystem
}
// Create GUI, but doesn't start it
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *ethwire.SimpleClientIdentity, session string, logLevel int) *Gui {
db, err := ethdb.NewLDBDatabase("tx_database")
if err != nil {
panic(err)
}
pipe := ethpipe.NewJSPipe(ethereum)
gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)}
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
json.Unmarshal([]byte(data), &gui.plugins)
return gui
}
func (gui *Gui) Start(assetPath string) {
defer gui.txDb.Close()
// Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
Init: func(p *ethpipe.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
}, {
Init: func(p *ethpipe.JSTransaction, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
}, {
Init: func(p *ethpipe.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" },
}})
// Create a new QML engine
gui.engine = qml.NewEngine()
context := gui.engine.Context()
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
// Expose the eth library and the ui library to QML
context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib)
/*
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
fmt.Printf("Fetched vec with addr: %#x\n", vec)
if errr != nil {
fmt.Println(errr)
} else {
context.SetVar("vec", (unsafe.Pointer)(vec))
}
*/
// Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
var win *qml.Window
var err error
var addlog = false
if len(data) == 0 {
win, err = gui.showKeyImport(context)
} else {
win, err = gui.showWallet(context)
addlog = true
}
if err != nil {
logger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err)
panic(err)
}
logger.Infoln("Starting GUI")
gui.open = true
win.Show()
// only add the gui logger after window is shown otherwise slider wont be shown
if addlog {
ethlog.AddLogSystem(gui)
}
win.Wait()
// need to silence gui logger after window closed otherwise logsystem hangs (but do not save loglevel)
gui.logLevel = ethlog.Silence
gui.open = false
}
func (gui *Gui) Stop() {
if gui.open {
gui.logLevel = ethlog.Silence
gui.open = false
gui.win.Hide()
}
gui.uiLib.jsEngine.Stop()
logger.Infoln("Stopped")
}
func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
if err != nil {
return nil, err
}
gui.win = gui.createWindow(component)
gui.update()
return gui.win, nil
}
// The done handler will be called by QML when all views have been loaded
func (gui *Gui) Done() {
gui.qmlDone = true
}
func (gui *Gui) ImportKey(filePath string) {
}
func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
context.SetVar("lib", gui)
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml"))
if err != nil {
return nil, err
}
return gui.createWindow(component), nil
}
func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
win := comp.CreateWindow(nil)
gui.win = win
gui.uiLib.win = win
return gui.win
}
func (gui *Gui) ImportAndSetPrivKey(secret string) bool {
err := gui.eth.KeyManager().InitFromString(gui.Session, 0, secret)
if err != nil {
logger.Errorln("unable to import: ", err)
return false
}
logger.Errorln("successfully imported: ", err)
return true
}
func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
err := gui.eth.KeyManager().Init(gui.Session, 0, true)
if err != nil {
logger.Errorln("unable to create key: ", err)
return "", "", "", ""
}
return gui.eth.KeyManager().KeyPair().AsStrings()
}
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
addr := gui.address()
// Loop through all transactions to see if we missed any while being offline
for _, tx := range blk.Transactions() {
if bytes.Compare(tx.Sender(), addr) == 0 || bytes.Compare(tx.Recipient, 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
}
func (gui *Gui) loadAddressBook() {
view := gui.getObjectByName("infoView")
view.Call("clearAddress")
nameReg := gui.pipe.World().Config().Get("NameReg")
if nameReg != nil {
nameReg.EachStorage(func(name string, value *ethutil.Value) {
if name[0] != 0 {
value.Decode()
view.Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())})
}
})
}
}
func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) {
nameReg := ethpipe.New(gui.eth).World().Config().Get("NameReg")
addr := gui.address()
var inout string
if bytes.Compare(tx.Sender(), addr) == 0 {
inout = "send"
} else {
inout = "recv"
}
var (
ptx = ethpipe.NewJSTx(tx)
send = nameReg.Storage(tx.Sender())
rec = nameReg.Storage(tx.Recipient)
s, r string
)
if tx.CreatesContract() {
rec = nameReg.Storage(tx.CreationAddress())
}
if send.Len() != 0 {
s = strings.Trim(send.Str(), "\x00")
} else {
s = ethutil.Bytes2Hex(tx.Sender())
}
if rec.Len() != 0 {
r = strings.Trim(rec.Str(), "\x00")
} else {
if tx.CreatesContract() {
r = ethutil.Bytes2Hex(tx.CreationAddress())
} else {
r = ethutil.Bytes2Hex(tx.Recipient)
}
}
ptx.Sender = s
ptx.Address = r
if window == "post" {
//gui.getObjectByName("transactionView").Call("addTx", ptx, inout)
} else {
gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout)
}
}
func (gui *Gui) readPreviousTransactions() {
it := gui.txDb.Db().NewIterator(nil, nil)
for it.Next() {
tx := ethchain.NewTransactionFromBytes(it.Value())
gui.insertTransaction("post", tx)
}
it.Release()
}
func (gui *Gui) processBlock(block *ethchain.Block, initial bool) {
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
b := ethpipe.NewJSBlock(block)
b.Name = name
gui.getObjectByName("chainView").Call("addBlock", b, 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)
}
func (self *Gui) getObjectByName(objectName string) qml.Object {
return self.win.Root().ObjectByName(objectName)
}
// Simple go routine function that updates the list of peers in the GUI
func (gui *Gui) update() {
// We have to wait for qml to be done loading all the windows.
for !gui.qmlDone {
time.Sleep(500 * time.Millisecond)
}
go func() {
go gui.setInitialBlockChain()
gui.loadAddressBook()
gui.setPeerInfo()
gui.readPreviousTransactions()
}()
for _, plugin := range gui.plugins {
logger.Infoln("Loading plugin ", plugin.Name)
gui.win.Root().Call("addPlugin", plugin.Path, "")
}
var (
blockChan = make(chan ethreact.Event, 100)
txChan = make(chan ethreact.Event, 100)
objectChan = make(chan ethreact.Event, 100)
peerChan = make(chan ethreact.Event, 100)
chainSyncChan = make(chan ethreact.Event, 100)
miningChan = make(chan ethreact.Event, 100)
)
peerUpdateTicker := time.NewTicker(5 * time.Second)
generalUpdateTicker := time.NewTicker(1 * time.Second)
statsUpdateTicker := time.NewTicker(5 * time.Second)
state := gui.eth.StateManager().TransState()
unconfirmedFunds := new(big.Int)
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Balance)))
gui.getObjectByName("syncProgressIndicator").Set("visible", !gui.eth.IsUpToDate())
lastBlockLabel := gui.getObjectByName("lastBlockLabel")
miningLabel := gui.getObjectByName("miningLabel")
go func() {
for {
select {
case b := <-blockChan:
block := b.Resource.(*ethchain.Block)
gui.processBlock(block, false)
if bytes.Compare(block.Coinbase, gui.address()) == 0 {
gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Balance, nil)
}
case txMsg := <-txChan:
tx := txMsg.Resource.(*ethchain.Transaction)
if txMsg.Name == "newTx:pre" {
object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
}
gui.setWalletValue(object.Balance, unconfirmedFunds)
gui.insertTransaction("pre", tx)
} else {
object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
object.SubAmount(tx.Value)
//gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
object.AddAmount(tx.Value)
//gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "recv")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
gui.setWalletValue(object.Balance, nil)
state.UpdateStateObject(object)
}
case msg := <-chainSyncChan:
sync := msg.Resource.(bool)
gui.win.Root().ObjectByName("syncProgressIndicator").Set("visible", sync)
case <-objectChan:
gui.loadAddressBook()
case <-peerChan:
gui.setPeerInfo()
case <-peerUpdateTicker.C:
gui.setPeerInfo()
case msg := <-miningChan:
if msg.Name == "miner:start" {
gui.miner = msg.Resource.(*ethminer.Miner)
} else {
gui.miner = nil
}
case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.BlockChain().CurrentBlock.Number.String()
lastBlockLabel.Set("text", statusText)
if gui.miner != nil {
pow := gui.miner.GetPow()
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
}
case <-statsUpdateTicker.C:
gui.setStatsPane()
}
}
}()
reactor := gui.eth.Reactor()
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("newTx:pre", txChan)
reactor.Subscribe("newTx:post", txChan)
reactor.Subscribe("chainSync", chainSyncChan)
reactor.Subscribe("miner:start", miningChan)
reactor.Subscribe("miner:stop", miningChan)
nameReg := gui.pipe.World().Config().Get("NameReg")
reactor.Subscribe("object:"+string(nameReg.Address()), objectChan)
reactor.Subscribe("peerList", peerChan)
}
func (gui *Gui) setStatsPane() {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
statsPane := gui.getObjectByName("statsPane")
statsPane.Set("text", fmt.Sprintf(`###### Mist 0.6.5 (%s) #######
eth %d (p2p = %d)
CPU: # %d
Goroutines: # %d
CGoCalls: # %d
Alloc: %d
Heap Alloc: %d
CGNext: %x
NumGC: %d
`, runtime.Version(),
eth.ProtocolVersion, eth.P2PVersion,
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
memStats.Alloc, memStats.HeapAlloc,
memStats.NextGC, memStats.NumGC,
))
}
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.pipe.Peers() {
gui.win.Root().Call("addPeer", peer)
}
}
func (gui *Gui) privateKey() string {
return ethutil.Bytes2Hex(gui.eth.KeyManager().PrivateKey())
}
func (gui *Gui) address() []byte {
return gui.eth.KeyManager().Address()
}

View file

@ -1,91 +0,0 @@
package main
import (
"os"
"runtime"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/go-ethereum/utils"
"gopkg.in/qml.v1"
)
const (
ClientIdentifier = "Mist"
Version = "0.6.5"
)
var ethereum *eth.Ethereum
func run() error {
// precedence: code-internal flag default < config file < environment variables < command line
Init() // parsing command line
config := utils.InitConfig(ConfigFile, Datadir, "ETH")
utils.InitDataDir(Datadir)
stdLog := utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
db := utils.NewDatabase()
err := utils.DBSanityCheck(db)
if err != nil {
ErrorWindow(err)
os.Exit(1)
}
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
if ShowGenesis {
utils.ShowGenesis(ethereum)
}
if StartRpc {
utils.StartRpc(ethereum, RpcPort)
}
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
gui.stdLog = stdLog
utils.RegisterInterrupt(func(os.Signal) {
gui.Stop()
})
utils.StartEthereum(ethereum, UseSeed)
// gui blocks the main thread
gui.Start(AssetPath)
return nil
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
// This is a bit of a cheat, but ey!
os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")
qml.Run(run)
var interrupted = false
utils.RegisterInterrupt(func(os.Signal) {
interrupted = true
})
utils.HandleInterrupt()
// we need to run the interrupt callbacks in case gui is closed
// this skips if we got here by actual interrupt stopping the GUI
if !interrupted {
utils.RunInterruptCallbacks(os.Interrupt)
}
// this blocks the thread
ethereum.WaitForShutdown()
ethlog.Flush()
}

View file

@ -1,68 +0,0 @@
package main
import (
"fmt"
"runtime"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"gopkg.in/qml.v1"
)
type QmlApplication struct {
win *qml.Window
engine *qml.Engine
lib *UiLib
path string
}
func NewQmlApplication(path string, lib *UiLib) *QmlApplication {
engine := qml.NewEngine()
return &QmlApplication{engine: engine, path: path, lib: lib}
}
func (app *QmlApplication) Create() error {
path := string(app.path)
// For some reason for windows we get /c:/path/to/something, windows doesn't like the first slash but is fine with the others so we are removing it
if app.path[0] == '/' && runtime.GOOS == "windows" {
path = app.path[1:]
}
component, err := app.engine.LoadFile(path)
if err != nil {
logger.Warnln(err)
}
app.win = component.CreateWindow(nil)
return nil
}
func (app *QmlApplication) Destroy() {
app.engine.Destroy()
}
func (app *QmlApplication) NewWatcher(quitChan chan bool) {
}
// Events
func (app *QmlApplication) NewBlock(block *ethchain.Block) {
pblock := &ethpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
app.win.Call("onNewBlockCb", pblock)
}
func (self *QmlApplication) Messages(msgs ethstate.Messages, id string) {
fmt.Println("IMPLEMENT QML APPLICATION MESSAGES METHOD")
}
// Getters
func (app *QmlApplication) Engine() *qml.Engine {
return app.engine
}
func (app *QmlApplication) Window() *qml.Window {
return app.win
}
func (app *QmlApplication) Post(data string, s int) {}

View file

@ -1,287 +0,0 @@
package main
import (
"bytes"
"fmt"
"path"
"strconv"
"strings"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"gopkg.in/qml.v1"
)
type memAddr struct {
Num string
Value string
}
// UI Library that has some basic functionality exposed
type UiLib struct {
*ethpipe.JSPipe
engine *qml.Engine
eth *eth.Ethereum
connected bool
assetPath string
// The main application window
win *qml.Window
Db *Debugger
DbWindow *DebuggerWindow
jsEngine *javascript.JSRE
filterCallbacks map[int][]int
}
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*ethpipe.JSFilter)}
}
func (self *UiLib) Notef(args []interface{}) {
logger.Infoln(args...)
}
func (self *UiLib) LookupDomain(domain string) string {
world := self.World()
if len(domain) > 32 {
domain = string(ethcrypto.Sha3Bin([]byte(domain)))
}
data := world.Config().Get("DnsReg").StorageString(domain).Bytes()
// Left padded = A record, Right padded = CNAME
if len(data) > 0 && data[0] == 0 {
data = bytes.TrimLeft(data, "\x00")
var ipSlice []string
for _, d := range data {
ipSlice = append(ipSlice, strconv.Itoa(int(d)))
}
return strings.Join(ipSlice, ".")
} else {
data = bytes.TrimRight(data, "\x00")
return string(data)
}
}
func (self *UiLib) PastPeers() *ethutil.List {
return ethutil.NewList(eth.PastPeers())
}
func (self *UiLib) ImportTx(rlpTx string) {
tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
self.eth.TxPool().QueueTransaction(tx)
}
func (self *UiLib) EvalJavascriptFile(path string) {
self.jsEngine.LoadExtFile(path[7:])
}
func (self *UiLib) EvalJavascriptString(str string) string {
value, err := self.jsEngine.Run(str)
if err != nil {
return err.Error()
}
return fmt.Sprintf("%v", value)
}
func (ui *UiLib) OpenQml(path string) {
container := NewQmlApplication(path[7:], ui)
app := NewExtApplication(container, ui)
go app.run()
}
func (ui *UiLib) OpenHtml(path string) {
container := NewHtmlApplication(path, ui)
app := NewExtApplication(container, ui)
go app.run()
}
func (ui *UiLib) OpenBrowser() {
ui.OpenHtml("file://" + ui.AssetPath("ext/home.html"))
}
func (ui *UiLib) Muted(content string) {
component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
if err != nil {
logger.Debugln(err)
return
}
win := component.CreateWindow(nil)
go func() {
path := "file://" + ui.AssetPath("muted/index.html")
win.Set("url", path)
win.Show()
win.Wait()
}()
}
func (ui *UiLib) Connect(button qml.Object) {
if !ui.connected {
ui.eth.Start(true)
ui.connected = true
button.Set("enabled", false)
}
}
func (ui *UiLib) ConnectToPeer(addr string) {
ui.eth.ConnectToPeer(addr)
}
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.Hex2Bytes(contractHash))
if len(object.Code) > 0 {
dbWindow.SetCode("0x" + ethutil.Bytes2Hex(object.Code))
}
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)
dbWindow.Show()
}
func (self *UiLib) NewFilter(object map[string]interface{}) int {
filter, id := self.eth.InstallFilter(object)
filter.MessageCallback = func(messages ethstate.Messages) {
self.win.Root().Call("invokeFilterCallback", ethpipe.ToJSMessages(messages), id)
}
return id
}
func (self *UiLib) NewFilterString(typ string) int {
filter, id := self.eth.InstallFilter(nil)
filter.BlockCallback = func(block *ethchain.Block) {
self.win.Root().Call("invokeFilterCallback", "{}", id)
}
return id
}
func (self *UiLib) Messages(id int) *ethutil.List {
filter := self.eth.GetFilter(id)
if filter != nil {
messages := filter.Find()
return ethpipe.ToJSMessages(messages)
}
return ethutil.EmptyList()
}
func (self *UiLib) UninstallFilter(id int) {
self.eth.UninstallFilter(id)
}
func mapToTxParams(object map[string]interface{}) map[string]string {
// Default values
if object["from"] == nil {
object["from"] = ""
}
if object["to"] == nil {
object["to"] = ""
}
if object["value"] == nil {
object["value"] = ""
}
if object["gas"] == nil {
object["gas"] = ""
}
if object["gasPrice"] == nil {
object["gasPrice"] = ""
}
var dataStr string
var data []string
if list, ok := object["data"].(*qml.List); ok {
list.Convert(&data)
} else if str, ok := object["data"].(string); ok {
data = []string{str}
}
for _, str := range data {
if ethutil.IsHex(str) {
str = str[2:]
if len(str) != 64 {
str = ethutil.LeftPadString(str, 64)
}
} else {
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
}
dataStr += str
}
object["data"] = dataStr
fmt.Println(object)
conv := make(map[string]string)
for key, value := range object {
if v, ok := value.(string); ok {
conv[key] = v
}
}
return conv
}
func (self *UiLib) Transact(params map[string]interface{}) (*ethpipe.JSReceipt, error) {
object := mapToTxParams(params)
return self.JSPipe.Transact(
object["from"],
object["to"],
object["value"],
object["gas"],
object["gasPrice"],
object["data"],
)
}
func (self *UiLib) Compile(code string) (string, error) {
bcode, err := ethutil.Compile(code, false)
if err != nil {
return err.Error(), err
}
return ethutil.Bytes2Hex(bcode), err
}
func (self *UiLib) Call(params map[string]interface{}) (string, error) {
object := mapToTxParams(params)
return self.JSPipe.Execute(
object["to"],
object["value"],
object["gas"],
object["gasPrice"],
object["data"],
)
}

View file

@ -1,302 +1,62 @@
package utils
import (
"fmt"
"io"
"log"
"os"
"os/signal"
"path"
"path/filepath"
"regexp"
"runtime"
"time"
"bitbucket.org/kardianos/osext"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethminer"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"time"
)
var logger = ethlog.NewLogger("CLI")
var interruptCallbacks = []func(os.Signal){}
// Register interrupt handlers callbacks
func RegisterInterrupt(cb func(os.Signal)) {
interruptCallbacks = append(interruptCallbacks, cb)
}
// go routine that call interrupt handlers in order of registering
func HandleInterrupt() {
c := make(chan os.Signal, 1)
go func() {
signal.Notify(c, os.Interrupt)
for sig := range c {
logger.Errorf("Shutting down (%v) ... \n", sig)
RunInterruptCallbacks(sig)
}
}()
}
func RunInterruptCallbacks(sig os.Signal) {
for _, cb := range interruptCallbacks {
cb(sig)
}
}
func AbsolutePath(Datadir string, filename string) string {
if path.IsAbs(filename) {
return filename
}
return path.Join(Datadir, filename)
}
func openLogFile(Datadir string, filename string) *os.File {
path := AbsolutePath(Datadir, filename)
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("error opening log file '%s': %v", filename, err))
}
return file
}
func confirm(message string) bool {
fmt.Println(message, "Are you sure? (y/n)")
var r string
fmt.Scanln(&r)
for ; ; fmt.Scanln(&r) {
if r == "n" || r == "y" {
break
} else {
fmt.Printf("Yes or no?", r)
}
}
return r == "y"
}
func DBSanityCheck(db ethutil.Database) error {
d, _ := db.Get([]byte("ProtocolVersion"))
protov := ethutil.NewValue(d).Uint()
if protov != eth.ProtocolVersion && protov != 0 {
return fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, eth.ProtocolVersion, ethutil.Config.ExecPath+"/database")
}
return nil
}
func InitDataDir(Datadir string) {
_, err := os.Stat(Datadir)
if err != nil {
if os.IsNotExist(err) {
fmt.Printf("Data directory '%s' doesn't exist, creating it\n", Datadir)
os.Mkdir(Datadir, 0777)
}
}
}
func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) ethlog.LogSystem {
var writer io.Writer
if LogFile == "" {
writer = os.Stdout
} else {
writer = openLogFile(Datadir, LogFile)
}
sys := ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.LogLevel(LogLevel))
ethlog.AddLogSystem(sys)
if DebugFile != "" {
writer = openLogFile(Datadir, DebugFile)
ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.DebugLevel))
}
return sys
}
func InitConfig(ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager {
InitDataDir(Datadir)
return ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix)
}
func exit(err error) {
status := 0
if err != nil {
logger.Errorln("Fatal: ", err)
status = 1
}
ethlog.Flush()
os.Exit(status)
}
func NewDatabase() ethutil.Database {
db, err := ethdb.NewLDBDatabase("database")
if err != nil {
exit(err)
}
return db
}
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *ethwire.SimpleClientIdentity {
logger.Infoln("identity created")
return ethwire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
}
func NewEthereum(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager *ethcrypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum {
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
if err != nil {
logger.Fatalln("eth start err:", err)
}
ethereum.Port = OutboundPort
ethereum.MaxPeers = MaxPeer
return ethereum
}
func StartEthereum(ethereum *eth.Ethereum, UseSeed bool) {
logger.Infof("Starting %s", ethereum.ClientIdentity())
ethereum.Start(UseSeed)
RegisterInterrupt(func(sig os.Signal) {
ethereum.Stop()
ethlog.Flush()
})
}
func ShowGenesis(ethereum *eth.Ethereum) {
logger.Infoln(ethereum.BlockChain().Genesis())
exit(nil)
}
func NewKeyManager(KeyStore string, Datadir string, db ethutil.Database) *ethcrypto.KeyManager {
var keyManager *ethcrypto.KeyManager
switch {
case KeyStore == "db":
keyManager = ethcrypto.NewDBKeyManager(db)
case KeyStore == "file":
keyManager = ethcrypto.NewFileKeyManager(Datadir)
default:
exit(fmt.Errorf("unknown keystore type: %s", KeyStore))
}
return keyManager
}
func DefaultAssetPath() string {
var assetPath string
// If the current working directory is the go-ethereum dir
// assume a debug build and use the source directory as
// asset directory.
pwd, _ := os.Getwd()
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist") {
assetPath = path.Join(pwd, "assets")
} else {
switch runtime.GOOS {
case "darwin":
// Get Binary Directory
exedir, _ := osext.ExecutableFolder()
assetPath = filepath.Join(exedir, "../Resources")
case "linux":
assetPath = "/usr/share/mist"
case "windows":
assetPath = "./assets"
default:
assetPath = "."
}
}
return assetPath
}
func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) {
func DoRpc(ethereum *eth.Ethereum, RpcPort int) {
var err error
switch {
case GenAddr:
if NonInteractive || confirm("This action overwrites your old private key.") {
err = keyManager.Init(KeyRing, 0, true)
}
exit(err)
case len(SecretFile) > 0:
SecretFile = ethutil.ExpandHomePath(SecretFile)
if NonInteractive || confirm("This action overwrites your old private key.") {
err = keyManager.InitFromSecretsFile(KeyRing, 0, SecretFile)
}
exit(err)
case len(ExportDir) > 0:
err = keyManager.Init(KeyRing, 0, false)
if err == nil {
err = keyManager.Export(ExportDir)
}
exit(err)
default:
// Creates a keypair if none exists
err = keyManager.Init(KeyRing, 0, false)
if err != nil {
exit(err)
}
}
}
func StartRpc(ethereum *eth.Ethereum, RpcPort int) {
var err error
ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpipe.NewJSPipe(ethereum), RpcPort)
ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort)
if err != nil {
logger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err)
ethutil.Config.Log.Infoln("Could not start RPC interface:", err)
} else {
go ethereum.RpcServer.Start()
}
}
var miner *ethminer.Miner
var miner ethminer.Miner
func GetMiner() *ethminer.Miner {
return miner
}
func DoMining(ethereum *eth.Ethereum) {
// Set Mining status
ethereum.Mining = true
func StartMining(ethereum *eth.Ethereum) bool {
if !ethereum.Mining {
ethereum.Mining = true
addr := ethereum.KeyManager().Address()
go func() {
logger.Infoln("Start mining")
if miner == nil {
miner = ethminer.NewDefaultMiner(addr, ethereum)
}
// Give it some time to connect with peers
time.Sleep(3 * time.Second)
for !ethereum.IsUpToDate() {
time.Sleep(5 * time.Second)
}
miner.Start()
}()
RegisterInterrupt(func(os.Signal) {
StopMining(ethereum)
})
return true
}
return false
}
func FormatTransactionData(data string) []byte {
d := ethutil.StringToByteFunc(data, func(s string) (ret []byte) {
slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000)
for _, dataItem := range slice {
d := ethutil.FormatData(dataItem)
ret = append(ret, d...)
}
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()
return d
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 != nil {
if ethereum.Mining {
miner.Stop()
logger.Infoln("Stopped mining")
ethutil.Config.Log.Infoln("Miner stopped")
ethereum.Mining = false
return true
@ -305,20 +65,12 @@ func StopMining(ethereum *eth.Ethereum) bool {
return false
}
// Replay block
func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
block := ethereum.BlockChain().GetBlock(hash)
if block == nil {
return fmt.Errorf("unknown block %x", hash)
func StartMining(ethereum *eth.Ethereum) bool {
if !ethereum.Mining {
DoMining(ethereum)
return true
}
parent := ethereum.BlockChain().GetBlock(block.PrevHash)
_, err := ethereum.StateManager().ApplyDiff(parent.State(), parent, block)
if err != nil {
return err
}
return nil
return false
}

41
utils/compile.go Normal file
View file

@ -0,0 +1,41 @@
package utils
import (
"fmt"
"github.com/obscuren/mutan"
"strings"
)
// General compile function
func Compile(script string) ([]byte, error) {
byteCode, errors := mutan.Compile(strings.NewReader(script), false)
if len(errors) > 0 {
var errs string
for _, er := range errors {
if er != nil {
errs += er.Error()
}
}
return nil, fmt.Errorf("%v", errs)
}
return byteCode, nil
}
func CompileScript(script string) ([]byte, []byte, error) {
// Preprocess
mainInput, initInput := mutan.PreParse(script)
// Compile main script
mainScript, err := Compile(mainInput)
if err != nil {
return nil, nil, err
}
// Compile init script
initScript, err := Compile(initInput)
if err != nil {
return nil, nil, err
}
return mainScript, initScript, nil
}

108
utils/keys.go Normal file
View file

@ -0,0 +1,108 @@
package utils
import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
"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) {
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
if len(data) == 0 || force {
pub, prv := secp256k1.GenerateKeyPair()
pair := &ethutil.Key{PrivateKey: prv, PublicKey: pub}
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
mne := ethutil.MnemonicEncode(ethutil.Hex(prv))
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
`, pair.Address(), prv, pub, mne)
}
}
*/
/*
func ImportPrivateKey(prvKey string) {
key := ethutil.FromHex(prvKey)
msg := []byte("tmp")
// Couldn't think of a better way to get the pub key
sig, _ := secp256k1.Sign(msg, key)
pub, _ := secp256k1.RecoverPubkey(msg, sig)
pair := &ethutil.Key{PrivateKey: key, PublicKey: pub}
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
fmt.Printf(`
Importing private key
++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x
prvk: %x
pubk: %x
++++++++++++++++++++++++++++++++++++++++++++
`, pair.Address(), key, pub)
}
*/

Some files were not shown because too many files have changed in this diff Show more