aboutsummaryrefslogtreecommitdiffstats
path: root/ethereal
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-03-22 08:02:24 +0800
committerobscuren <geffobscura@gmail.com>2014-03-22 08:02:24 +0800
commit1f2547b8a7cfe100f64428d20f4bcf95eb9ecc5c (patch)
treed8be2c7b97a86f2b9949c4b4dc14ab2c2a34dc2e /ethereal
parent22b4e9b6173437b28045d69e8fd0b468e526e559 (diff)
downloaddexon-1f2547b8a7cfe100f64428d20f4bcf95eb9ecc5c.tar.gz
dexon-1f2547b8a7cfe100f64428d20f4bcf95eb9ecc5c.tar.zst
dexon-1f2547b8a7cfe100f64428d20f4bcf95eb9ecc5c.zip
Major re-organisation.
The Ethereum node and Gui are now separated.
Diffstat (limited to 'ethereal')
-rw-r--r--ethereal/Makefile21
-rw-r--r--ethereal/assets/facet.pngbin0 -> 27302 bytes
-rw-r--r--ethereal/assets/net.pngbin0 -> 4669 bytes
-rw-r--r--ethereal/assets/network.pngbin0 -> 2900 bytes
-rw-r--r--ethereal/assets/new.pngbin0 -> 4776 bytes
-rw-r--r--ethereal/assets/qml/test_app.qml35
-rw-r--r--ethereal/assets/qml/transactions.qml9
-rw-r--r--ethereal/assets/qml/wallet.qml408
-rw-r--r--ethereal/assets/tx.pngbin0 -> 4070 bytes
-rw-r--r--ethereal/config.go34
-rw-r--r--ethereal/ethereum.go110
-rw-r--r--ethereal/ui/gui.go218
-rw-r--r--ethereal/ui/library.go60
-rw-r--r--ethereal/ui/ui_lib.go76
14 files changed, 971 insertions, 0 deletions
diff --git a/ethereal/Makefile b/ethereal/Makefile
new file mode 100644
index 000000000..02d127963
--- /dev/null
+++ b/ethereal/Makefile
@@ -0,0 +1,21 @@
+UNAME = $(shell uname)
+
+# Default is building
+all:
+ go install
+
+install:
+# Linux build
+ifeq ($(UNAME),Linux)
+ mkdir -p /usr/local/ethereal
+ files=(net.png network.png new.png tx.png)
+ for file in "${files[@]}"; do
+ cp $file /usr/share/ethereal
+ done
+ cp -r qml /usr/share/ethereal/qml
+ cp $GOPATH/bin/go-ethereum /usr/local/bin/ethereal
+endif
+# OS X build
+ifeq ($(UNAME),Darwin)
+ # Execute py script
+endif
diff --git a/ethereal/assets/facet.png b/ethereal/assets/facet.png
new file mode 100644
index 000000000..49a266e96
--- /dev/null
+++ b/ethereal/assets/facet.png
Binary files differ
diff --git a/ethereal/assets/net.png b/ethereal/assets/net.png
new file mode 100644
index 000000000..65a20ea00
--- /dev/null
+++ b/ethereal/assets/net.png
Binary files differ
diff --git a/ethereal/assets/network.png b/ethereal/assets/network.png
new file mode 100644
index 000000000..0a9ffe2ec
--- /dev/null
+++ b/ethereal/assets/network.png
Binary files differ
diff --git a/ethereal/assets/new.png b/ethereal/assets/new.png
new file mode 100644
index 000000000..e80096748
--- /dev/null
+++ b/ethereal/assets/new.png
Binary files differ
diff --git a/ethereal/assets/qml/test_app.qml b/ethereal/assets/qml/test_app.qml
new file mode 100644
index 000000000..aace4e881
--- /dev/null
+++ b/ethereal/assets/qml/test_app.qml
@@ -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)
+ }
+ }
+}
diff --git a/ethereal/assets/qml/transactions.qml b/ethereal/assets/qml/transactions.qml
new file mode 100644
index 000000000..e9a035a85
--- /dev/null
+++ b/ethereal/assets/qml/transactions.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+
+Rectangle {
+ id: transactionView
+ visible: false
+ Text { text: "TX VIEW" }
+}
diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
new file mode 100644
index 000000000..7fc7f5447
--- /dev/null
+++ b/ethereal/assets/qml/wallet.qml
@@ -0,0 +1,408 @@
+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()
+ }
+ }
+
+ 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
+ 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)
+ }
+ }
+ }
+ }
+ }
+
+ 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: "value" ; title: "Value" ; width: 100 }
+ TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
+
+ model: txModel
+ }
+ }
+
+ Rectangle {
+ id: newTxView
+ property var title: "New transaction"
+ visible: false
+ anchors.fill: parent
+ color: "#00000000"
+
+ ColumnLayout {
+ width: 400
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.leftMargin: 5
+ anchors.topMargin: 5
+ TextField {
+ id: txAmount
+ width: 200
+ placeholderText: "Amount"
+ }
+
+ TextField {
+ id: txReceiver
+ placeholderText: "Receiver Address (or empty for contract)"
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: "Transaction data"
+ }
+ TextArea {
+ id: codeView
+ anchors.topMargin: 5
+ Layout.fillWidth: true
+ width: parent.width /2
+ }
+
+ Button {
+ text: "Send"
+ onClicked: {
+ console.log(eth.createTx(txReceiver.text, txAmount.text, codeView.text))
+ }
+ }
+ }
+ }
+
+
+ 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 }
+
+ model: blockModel
+
+ /*
+ onDoubleClicked: {
+ popup.visible = true
+ popup.block = eth.getBlock(blockModel.get(row).hash)
+ popup.hashLabel.text = popup.block.hash
+ }
+ */
+ }
+
+ 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
+ }
+ }
+
+ /*
+ 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())
+ }
+ }
+
+ statusBar: StatusBar {
+ RowLayout {
+ anchors.fill: parent
+ Button {
+ property var enabled: true
+ id: connectButton
+ onClicked: {
+ if(this.enabled) {
+ ui.connect(this)
+ }
+ }
+ text: "Connect"
+ }
+
+ Button {
+ id: importAppButton
+ anchors.left: connectButton.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
+ Label {
+ id: hashLabel
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+
+ 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>Ethereum(Go)</h2><br><h3>Development</h3>Jeffrey Wilcke<br><h3>Binary Distribution</h3>Jarrad Hope<br>"
+ }
+
+ }
+
+ function loadPlugin(name) {
+ console.log("Loading plugin" + name)
+ mainView.addPlugin(name)
+ }
+
+ function setWalletValue(value) {
+ walletValueLabel.text = value
+ }
+
+ function addTx(tx) {
+ txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value})
+ }
+
+ function addBlock(block) {
+ blockModel.insert(0, {number: block.number, hash: block.hash})
+ }
+
+ function addLog(str) {
+ if(str.len != 0) {
+ logModel.append({description: str})
+ }
+ }
+
+ function setPeers(text) {
+ peerLabel.text = text
+ }
+}
diff --git a/ethereal/assets/tx.png b/ethereal/assets/tx.png
new file mode 100644
index 000000000..62204c315
--- /dev/null
+++ b/ethereal/assets/tx.png
Binary files differ
diff --git a/ethereal/config.go b/ethereal/config.go
new file mode 100644
index 000000000..a534bb182
--- /dev/null
+++ b/ethereal/config.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "flag"
+)
+
+var StartConsole bool
+var StartMining bool
+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
+
+func Init() {
+ flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
+ flag.BoolVar(&StartMining, "m", false, "start dagger mining")
+ flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
+ flag.BoolVar(&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(&DataDir, "dir", ".ethereal", "ethereum data directory")
+ flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
+ flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
+
+ flag.Parse()
+}
diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go
new file mode 100644
index 000000000..618d2b00f
--- /dev/null
+++ b/ethereal/ethereum.go
@@ -0,0 +1,110 @@
+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/niemeyer/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.Config.Seed = UseSeed
+
+ // 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)
+ }
+ } else {
+ utils.CreateKeyPair(false)
+ }
+ }
+
+ if ExportKey {
+ key := ethutil.Config.Db.GetKeys()[0]
+ fmt.Printf("%x\n", key.PrivateKey)
+ os.Exit(0)
+ }
+
+ if ShowGenesis {
+ fmt.Println(ethereum.BlockChain().Genesis())
+ os.Exit(0)
+ }
+
+ log.Printf("Starting Ethereum v%s\n", ethutil.Config.Ver)
+
+ // Set the max peers
+ ethereum.MaxPeers = MaxPeer
+
+ gui := ethui.New(ethereum)
+ gui.Start()
+}
diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go
new file mode 100644
index 000000000..c8f4bedab
--- /dev/null
+++ b/ethereal/ui/gui.go
@@ -0,0 +1,218 @@
+package ethui
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "github.com/ethereum/eth-go"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/niemeyer/qml"
+ "math/big"
+ "strings"
+)
+
+// Block interface exposed to QML
+type Block struct {
+ Number int
+ Hash string
+}
+
+type Tx struct {
+ Value, Hash, Address string
+}
+
+func NewTxFromTransaction(tx *ethchain.Transaction) *Tx {
+ hash := hex.EncodeToString(tx.Hash())
+ sender := hex.EncodeToString(tx.Recipient)
+
+ return &Tx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender}
+}
+
+// Creates a new QML Block from a chain block
+func NewBlockFromBlock(block *ethchain.Block) *Block {
+ info := block.BlockInfo()
+ hash := hex.EncodeToString(block.Hash())
+
+ return &Block{Number: int(info.Number), Hash: hash}
+}
+
+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
+
+ txDb *ethdb.LDBDatabase
+
+ addr []byte
+}
+
+// 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)
+ }
+
+ key := ethutil.Config.Db.GetKeys()[0]
+ addr := key.Address()
+
+ ethereum.StateManager().WatchAddr(addr)
+
+ return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
+}
+
+func (ui *Gui) Start() {
+ defer ui.txDb.Close()
+
+ // Register ethereum functions
+ qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
+ Init: func(p *Block, obj qml.Object) { p.Number = 0; p.Hash = "" },
+ }, {
+ Init: func(p *Tx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
+ }})
+
+ ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.1"))
+ ethutil.Config.Log.Infoln("[GUI] Starting GUI")
+ // Create a new QML engine
+ ui.engine = qml.NewEngine()
+ context := ui.engine.Context()
+
+ // Expose the eth library and the ui library to QML
+ context.SetVar("eth", ui.lib)
+ context.SetVar("ui", &UiLib{engine: ui.engine, eth: ui.eth})
+
+ // Load the main QML interface
+ component, err := ui.engine.LoadFile(AssetPath("qml/wallet.qml"))
+ if err != nil {
+ panic(err)
+ }
+ ui.engine.LoadFile(AssetPath("qml/transactions.qml"))
+
+ ui.win = component.CreateWindow(nil)
+
+ // Register the ui as a block processor
+ //ui.eth.BlockManager.SecondaryBlockProcessor = ui
+ //ui.eth.TxPool.SecondaryProcessor = ui
+
+ // Add the ui as a log system so we can log directly to the UGI
+ ethutil.Config.Log.AddLogSystem(ui)
+
+ // Loads previous blocks
+ go ui.setInitialBlockChain()
+ go ui.readPreviousTransactions()
+ go ui.update()
+
+ ui.win.Show()
+ ui.win.Wait()
+
+ ui.eth.Stop()
+}
+
+func (ui *Gui) setInitialBlockChain() {
+ // Load previous 10 blocks
+ chain := ui.eth.BlockChain().GetChain(ui.eth.BlockChain().CurrentBlock.Hash(), 10)
+ for _, block := range chain {
+ ui.ProcessBlock(block)
+ }
+
+}
+
+func (ui *Gui) readPreviousTransactions() {
+ it := ui.txDb.Db().NewIterator(nil, nil)
+ for it.Next() {
+ tx := ethchain.NewTransactionFromBytes(it.Value())
+
+ ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
+ }
+ it.Release()
+}
+
+func (ui *Gui) ProcessBlock(block *ethchain.Block) {
+ ui.win.Root().Call("addBlock", NewBlockFromBlock(block))
+}
+
+// Simple go routine function that updates the list of peers in the GUI
+func (ui *Gui) update() {
+ txChan := make(chan ethchain.TxMsg, 1)
+ ui.eth.TxPool().Subscribe(txChan)
+
+ account := ui.eth.StateManager().GetAddrState(ui.addr).Account
+ unconfirmedFunds := new(big.Int)
+ ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
+ for {
+ select {
+ case txMsg := <-txChan:
+ tx := txMsg.Tx
+
+ if txMsg.Type == ethchain.TxPre {
+ if bytes.Compare(tx.Sender(), ui.addr) == 0 {
+ ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
+ ui.txDb.Put(tx.Hash(), tx.RlpEncode())
+
+ ui.eth.StateManager().GetAddrState(ui.addr).Nonce += 1
+ unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
+ } else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
+ ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
+ ui.txDb.Put(tx.Hash(), tx.RlpEncode())
+
+ unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
+ }
+
+ 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(account.Amount), pos, val)
+
+ ui.win.Root().Call("setWalletValue", str)
+ } else {
+ amount := account.Amount
+ if bytes.Compare(tx.Sender(), ui.addr) == 0 {
+ amount.Sub(account.Amount, tx.Value)
+ } else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
+ amount.Add(account.Amount, tx.Value)
+ }
+
+ ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(amount)))
+ }
+ }
+
+ /*
+ accountAmount := ui.eth.BlockManager.GetAddrState(ui.addr).Account.Amount
+ ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", accountAmount))
+
+ ui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", ui.eth.Peers().Len(), ui.eth.MaxPeers))
+
+ time.Sleep(1 * time.Second)
+ */
+
+ }
+}
+
+// Logging functions that log directly to the GUI interface
+func (ui *Gui) Println(v ...interface{}) {
+ str := strings.TrimRight(fmt.Sprintln(v...), "\n")
+ lines := strings.Split(str, "\n")
+ for _, line := range lines {
+ ui.win.Root().Call("addLog", line)
+ }
+}
+
+func (ui *Gui) Printf(format string, v ...interface{}) {
+ str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
+ lines := strings.Split(str, "\n")
+ for _, line := range lines {
+ ui.win.Root().Call("addLog", line)
+ }
+}
diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go
new file mode 100644
index 000000000..05fffd579
--- /dev/null
+++ b/ethereal/ui/library.go
@@ -0,0 +1,60 @@
+package ethui
+
+import (
+ "encoding/hex"
+ "fmt"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+ "strings"
+)
+
+type EthLib struct {
+ stateManager *ethchain.StateManager
+ blockChain *ethchain.BlockChain
+ txPool *ethchain.TxPool
+}
+
+func (lib *EthLib) CreateTx(receiver, a, data string) string {
+ var hash []byte
+ if len(receiver) == 0 {
+ hash = ethchain.ContractAddr
+ } else {
+ var err error
+ hash, err = hex.DecodeString(receiver)
+ if err != nil {
+ return err.Error()
+ }
+ }
+
+ k, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+ keyPair := ethutil.NewKeyFromBytes(k)
+
+ amount := ethutil.Big(a)
+ code := ethchain.Compile(strings.Split(data, "\n"))
+ tx := ethchain.NewTransaction(hash, amount, code)
+ tx.Nonce = lib.stateManager.GetAddrState(keyPair.Address()).Nonce
+
+ tx.Sign(keyPair.PrivateKey)
+
+ lib.txPool.QueueTransaction(tx)
+
+ if len(receiver) == 0 {
+ ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:])
+ } else {
+ ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
+ }
+
+ return ethutil.Hex(tx.Hash())
+}
+
+func (lib *EthLib) GetBlock(hexHash string) *Block {
+ hash, err := hex.DecodeString(hexHash)
+ if err != nil {
+ return nil
+ }
+
+ block := lib.blockChain.GetBlock(hash)
+ fmt.Println(block)
+
+ return &Block{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+}
diff --git a/ethereal/ui/ui_lib.go b/ethereal/ui/ui_lib.go
new file mode 100644
index 000000000..3997191fa
--- /dev/null
+++ b/ethereal/ui/ui_lib.go
@@ -0,0 +1,76 @@
+package ethui
+
+import (
+ "bitbucket.org/kardianos/osext"
+ "github.com/ethereum/eth-go"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/niemeyer/qml"
+ "os"
+ "path"
+ "path/filepath"
+ "runtime"
+)
+
+// UI Library that has some basic functionality exposed
+type UiLib struct {
+ engine *qml.Engine
+ eth *eth.Ethereum
+ connected bool
+}
+
+// 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) Connect(button qml.Object) {
+ if !ui.connected {
+ ui.eth.Start()
+ 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 AssetPath(p)
+}
+
+func AssetPath(p string) 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 path.Join(base, p)
+}