aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--README.md41
-rw-r--r--config.go8
-rw-r--r--dev_console.go51
-rw-r--r--ethereum.go147
-rw-r--r--net.pngbin0 -> 4669 bytes
-rw-r--r--net.pxmbin0 -> 95418 bytes
-rw-r--r--network.pngbin0 -> 2900 bytes
-rw-r--r--new.pngbin0 -> 4776 bytes
-rw-r--r--test_app.qml35
-rw-r--r--transactions.qml9
-rw-r--r--tx.pngbin0 -> 4070 bytes
-rw-r--r--tx.pxmbin0 -> 93227 bytes
-rw-r--r--ui/gui.go217
-rw-r--r--ui/library.go60
-rw-r--r--ui/ui_lib.go40
-rw-r--r--wallet.qml357
17 files changed, 882 insertions, 85 deletions
diff --git a/LICENSE b/LICENSE
index 803f2ef4e..b77f7909a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2013 Geff Obscura
+Copyright (c) 2013 Jeffrey Wilcke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index d0a08f7b8..034b35f97 100644
--- a/README.md
+++ b/README.md
@@ -3,44 +3,18 @@ Ethereum
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
-Ethereum Go developer client (c) Jeffrey Wilcke
-
-Ethereum is currently in its testing phase. The current state is "Proof
-of Concept 2". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge).
-
-Ethereum Go is split up in several sub packages Please refer to each
-individual package for more information.
- 1. [eth](https://github.com/ethereum/eth-go)
- 2. [ethchain](https://github.com/ethereum/ethchain-go)
- 3. [ethwire](https://github.com/ethereum/ethwire-go)
- 4. [ethdb](https://github.com/ethereum/ethdb-go)
- 5. [ethutil](https://github.com/ethereum/ethutil-go)
-
-The [eth](https://github.com/ethereum/eth-go) is the top-level package
-of the Ethereum protocol. It functions as the Ethereum bootstrapping and
-peer communication layer. The [ethchain](https://github.com/ethereum/ethchain-go)
-contains the Ethereum blockchain, block manager, transaction and
-transaction handlers. The [ethwire](https://github.com/ethereum/ethwire-go) contains
-the Ethereum [wire protocol](http://wiki.ethereum.org/index.php/Wire_Protocol) which can be used
-to hook in to the Ethereum network. [ethutil](https://github.com/ethereum/ethutil-go) contains
-utility functions which are not Ethereum specific. The utility package
-contains the [patricia trie](http://wiki.ethereum.org/index.php/Patricia_Tree),
-[RLP Encoding](http://wiki.ethereum.org/index.php/RLP) and hex encoding
-helpers. The [ethdb](https://github.com/ethereum/ethdb-go) package
-contains the LevelDB interface and memory DB interface.
-
-This executable is the front-end (currently nothing but a dev console) for
-the Ethereum Go implementation.
-
-If you'd like to start developing your own tools please check out the
-[development](https://github.com/ethereum/eth-go) package.
+Ethereum Go Client (c) Jeffrey Wilcke
+
+The current state is "Proof of Concept 3". For build instructions see
+the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge).
+
+For the development Go Package please see [eth-go package](https://github.com/ethereum/eth-go).
Build
=======
For build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge)
-
Command line options
====================
@@ -52,6 +26,9 @@ Command line options
-upnp Enable UPnP (= false)
-x Desired amount of peers (= 5)
-h This help
+-gui Launch with GUI (= true)
+-dir Data directory used to store configs and databases (=".ethereum")
+-import Import a private key (hex)
```
Developer console commands
diff --git a/config.go b/config.go
index d13bb863b..bafc3e300 100644
--- a/config.go
+++ b/config.go
@@ -13,15 +13,23 @@ var AddPeer string
var MaxPeer int
var GenAddr bool
var UseSeed bool
+var ImportKey string
+var ExportKey bool
+var UseGui 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(&UseGui, "gui", true, "use the gui")
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", ".ethereum", "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/dev_console.go b/dev_console.go
index f2283e341..09e06aa22 100644
--- a/dev_console.go
+++ b/dev_console.go
@@ -2,6 +2,7 @@ package main
import (
"bufio"
+ "bytes"
"encoding/hex"
"errors"
"fmt"
@@ -78,6 +79,32 @@ func (i *Console) ValidateInput(action string, argumentLength int) error {
}
}
+func (i *Console) Editor() []string {
+ var buff bytes.Buffer
+ for {
+ reader := bufio.NewReader(os.Stdin)
+ str, _, err := reader.ReadLine()
+ if len(str) > 0 {
+ buff.Write(str)
+ buff.WriteString("\n")
+ }
+
+ if err != nil && err.Error() == "EOF" {
+ break
+ }
+ }
+
+ scanner := bufio.NewScanner(strings.NewReader(buff.String()))
+ scanner.Split(bufio.ScanLines)
+
+ var lines []string
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+
+ return lines
+}
+
func (i *Console) PrintRoot() {
root := ethutil.NewValue(i.trie.Root)
if len(root.Bytes()) != 0 {
@@ -136,7 +163,8 @@ func (i *Console) ParseInput(input string) bool {
case "block":
encoded, _ := hex.DecodeString(tokens[1])
block := i.ethereum.BlockManager.BlockChain().GetBlock(encoded)
- fmt.Println(block)
+ info := block.BlockInfo()
+ fmt.Printf("++++++++++ #%d ++++++++++\n%v\n", info.Number, block)
case "say":
i.ethereum.Broadcast(ethwire.MsgTalkTy, []interface{}{tokens[1]})
case "addp":
@@ -151,13 +179,13 @@ func (i *Console) ParseInput(input string) bool {
fmt.Println("recipient err:", err)
} else {
tx := ethchain.NewTransaction(recipient, ethutil.Big(tokens[2]), []string{""})
- data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
- keyRing := ethutil.NewValueFromBytes(data)
- tx.Sign(keyRing.Get(0).Bytes())
- fmt.Printf("%x\n", tx.Hash())
+
+ key := ethutil.Config.Db.GetKeys()[0]
+ tx.Sign(key.PrivateKey)
i.ethereum.TxPool.QueueTransaction(tx)
- }
+ fmt.Printf("%x\n", tx.Hash())
+ }
case "gettx":
addr, _ := hex.DecodeString(tokens[1])
data, _ := ethutil.Config.Db.Get(addr)
@@ -168,10 +196,17 @@ func (i *Console) ParseInput(input string) bool {
fmt.Println("gettx: tx not found")
}
case "contract":
- contract := ethchain.NewTransaction([]byte{}, ethutil.Big(tokens[1]), []string{"PUSH", "1234"})
- fmt.Printf("%x\n", contract.Hash())
+ fmt.Println("Contract editor (Ctrl-D = done)")
+ code := ethchain.Compile(i.Editor())
+
+ contract := ethchain.NewTransaction(ethchain.ContractAddr, ethutil.Big(tokens[1]), code)
+
+ key := ethutil.Config.Db.GetKeys()[0]
+ contract.Sign(key.PrivateKey)
i.ethereum.TxPool.QueueTransaction(contract)
+
+ fmt.Printf("%x\n", contract.Hash()[12:])
case "exit", "quit", "q":
return false
case "help":
diff --git a/ethereum.go b/ethereum.go
index 372d434af..336cd4d00 100644
--- a/ethereum.go
+++ b/ethereum.go
@@ -5,6 +5,9 @@ import (
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "github.com/ethereum/go-ethereum/ui"
+ "github.com/niemeyer/qml"
"github.com/obscuren/secp256k1-go"
"log"
"os"
@@ -33,13 +36,12 @@ func CreateKeyPair(force bool) {
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
if len(data) == 0 || force {
pub, prv := secp256k1.GenerateKeyPair()
- addr := ethutil.Sha3Bin(pub[1:])[12:]
+ pair := &ethutil.Key{PrivateKey: prv, PublicKey: pub}
+ ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
fmt.Printf(`
Generating new address and keypair.
Please keep your keys somewhere save.
-Currently Ethereum(G) does not support
-exporting keys.
++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x
@@ -47,19 +49,45 @@ prvk: %x
pubk: %x
++++++++++++++++++++++++++++++++++++++++++++
-`, addr, prv, pub)
+`, pair.Address(), prv, pub)
- keyRing := ethutil.NewValue([]interface{}{prv, addr, pub[1:]})
- ethutil.Config.Db.Put([]byte("KeyRing"), keyRing.Encode())
}
}
+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)
+}
+
func main() {
- runtime.GOMAXPROCS(runtime.NumCPU())
Init()
+ // Qt has to be initialized in the main thread or it will throw errors
+ // It has to be called BEFORE setting the maximum procs.
+ if UseGui {
+ qml.Init(nil)
+ }
+
+ runtime.GOMAXPROCS(runtime.NumCPU())
+
ethchain.InitFees()
- ethutil.ReadConfig(".ethereum")
+ ethutil.ReadConfig(DataDir)
ethutil.Config.Seed = UseSeed
// Instantiated a eth stack
@@ -68,6 +96,7 @@ func main() {
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)")
@@ -87,7 +116,31 @@ func main() {
}
os.Exit(0)
} else {
- CreateKeyPair(false)
+ 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" {
+ ImportPrivateKey(ImportKey)
+ os.Exit(0)
+ }
+ } else {
+ CreateKeyPair(false)
+ }
+ }
+
+ if ExportKey {
+ key := ethutil.Config.Db.GetKeys()[0]
+ fmt.Printf("%x\n", key.PrivateKey)
+ os.Exit(0)
}
if ShowGenesis {
@@ -111,41 +164,47 @@ func main() {
go console.Start()
}
- RegisterInterupts(ethereum)
-
- ethereum.Start()
-
- if StartMining {
- log.Printf("Miner started\n")
-
- // Fake block mining. It broadcasts a new block every 5 seconds
- go func() {
- pow := &ethchain.EasyPow{}
- data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
- keyRing := ethutil.NewValueFromBytes(data)
- addr := keyRing.Get(1).Bytes()
-
- for {
- txs := ethereum.TxPool.Flush()
- // Create a new block which we're going to mine
- block := ethereum.BlockManager.BlockChain().NewBlock(addr, txs)
- // Apply all transactions to the block
- ethereum.BlockManager.ApplyTransactions(block, block.Transactions())
-
- ethereum.BlockManager.AccumelateRewards(block, block)
-
- // Search the nonce
- block.Nonce = pow.Search(block)
- err := ethereum.BlockManager.ProcessBlock(block)
- if err != nil {
- log.Println(err)
- } else {
- log.Println("\n+++++++ MINED BLK +++++++\n", ethereum.BlockManager.BlockChain().CurrentBlock)
+ if UseGui {
+ gui := ethui.New(ethereum)
+ gui.Start()
+ //ethereum.Stop()
+ } else {
+ RegisterInterupts(ethereum)
+ ethereum.Start()
+
+ if StartMining {
+ log.Printf("Miner started\n")
+
+ // Fake block mining. It broadcasts a new block every 5 seconds
+ go func() {
+ pow := &ethchain.EasyPow{}
+ data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+ keyRing := ethutil.NewValueFromBytes(data)
+ addr := keyRing.Get(1).Bytes()
+
+ for {
+ txs := ethereum.TxPool.Flush()
+ // Create a new block which we're going to mine
+ block := ethereum.BlockManager.BlockChain().NewBlock(addr, txs)
+ // Apply all transactions to the block
+ ethereum.BlockManager.ApplyTransactions(block, block.Transactions())
+
+ ethereum.BlockManager.AccumelateRewards(block, block)
+
+ // Search the nonce
+ block.Nonce = pow.Search(block)
+ ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
+ err := ethereum.BlockManager.ProcessBlock(block)
+ if err != nil {
+ log.Println(err)
+ } else {
+ log.Println("\n+++++++ MINED BLK +++++++\n", ethereum.BlockManager.BlockChain().CurrentBlock)
+ }
}
- }
- }()
- }
+ }()
+ }
- // Wait for shutdown
- ethereum.WaitForShutdown()
+ // Wait for shutdown
+ ethereum.WaitForShutdown()
+ }
}
diff --git a/net.png b/net.png
new file mode 100644
index 000000000..65a20ea00
--- /dev/null
+++ b/net.png
Binary files differ
diff --git a/net.pxm b/net.pxm
new file mode 100644
index 000000000..20d45d08c
--- /dev/null
+++ b/net.pxm
Binary files differ
diff --git a/network.png b/network.png
new file mode 100644
index 000000000..0a9ffe2ec
--- /dev/null
+++ b/network.png
Binary files differ
diff --git a/new.png b/new.png
new file mode 100644
index 000000000..e80096748
--- /dev/null
+++ b/new.png
Binary files differ
diff --git a/test_app.qml b/test_app.qml
new file mode 100644
index 000000000..aace4e881
--- /dev/null
+++ b/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/transactions.qml b/transactions.qml
new file mode 100644
index 000000000..e9a035a85
--- /dev/null
+++ b/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/tx.png b/tx.png
new file mode 100644
index 000000000..62204c315
--- /dev/null
+++ b/tx.png
Binary files differ
diff --git a/tx.pxm b/tx.pxm
new file mode 100644
index 000000000..881420da9
--- /dev/null
+++ b/tx.pxm
Binary files differ
diff --git a/ui/gui.go b/ui/gui.go
new file mode 100644
index 000000000..556e682a9
--- /dev/null
+++ b/ui/gui.go
@@ -0,0 +1,217 @@
+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{blockManager: ethereum.BlockManager, blockChain: ethereum.BlockManager.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.BlockManager.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.Log.Infoln("[GUI] Starting GUI")
+ // Create a new QML engine
+ ui.engine = qml.NewEngine()
+ // Load the main QML interface
+ component, err := ui.engine.LoadFile("wallet.qml")
+ if err != nil {
+ panic(err)
+ }
+ ui.engine.LoadFile("transactions.qml")
+
+ ui.win = component.CreateWindow(nil)
+
+ 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})
+
+ // 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.BlockManager.BlockChain().GetChain(ui.eth.BlockManager.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.BlockManager.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.BlockManager.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/ui/library.go b/ui/library.go
new file mode 100644
index 000000000..3bbb01314
--- /dev/null
+++ b/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 {
+ blockManager *ethchain.BlockManager
+ 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"))
+ keyRing := ethutil.NewValueFromBytes(k)
+
+ amount := ethutil.Big(a)
+ code := ethchain.Compile(strings.Split(data, "\n"))
+ tx := ethchain.NewTransaction(hash, amount, code)
+ tx.Nonce = lib.blockManager.GetAddrState(keyRing.Get(1).Bytes()).Nonce
+
+ tx.Sign(keyRing.Get(0).Bytes())
+
+ 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/ui/ui_lib.go b/ui/ui_lib.go
new file mode 100644
index 000000000..c956fd032
--- /dev/null
+++ b/ui/ui_lib.go
@@ -0,0 +1,40 @@
+package ethui
+
+import (
+ "github.com/ethereum/eth-go"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/niemeyer/qml"
+)
+
+// 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)
+}
diff --git a/wallet.qml b/wallet.qml
new file mode 100644
index 000000000..8c91039fc
--- /dev/null
+++ b/wallet.qml
@@ -0,0 +1,357 @@
+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: "tx.png"
+ anchors.horizontalCenter: parent.horizontalCenter
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ setView(historyView)
+ }
+ }
+ }
+ Image {
+ source: "new.png"
+ anchors.horizontalCenter: parent.horizontalCenter
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ setView(newTxView)
+ }
+ }
+ }
+ Image {
+ source: "net.png"
+ anchors.horizontalCenter: parent.horizontalCenter
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ setView(networkView)
+ }
+ }
+ }
+ }
+
+ }
+
+ property var txModel: ListModel {
+ id: txModel
+ }
+
+ Rectangle {
+ id: historyView
+ property var title: "Transactions"
+ anchors.right: parent.right
+ anchors.left: menu.right
+ anchors.bottom: parent.bottom
+ anchors.top: parent.top
+ 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.right: parent.right
+ anchors.left: menu.right
+ anchors.bottom: parent.bottom
+ anchors.top: parent.top
+ 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.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.top: parent.top
+
+ 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
+ }
+ }
+ }
+
+ 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: "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"
+ }
+ Button {
+ anchors.left: addrField.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 5
+ text: "Add"
+ onClicked: {
+ ui.connectToPeer(addrField.text)
+ addPeerWin.visible = false
+ }
+ }
+ }
+
+ Window {
+ id: aboutWin
+ visible: false
+ title: "About"
+ minimumWidth: 300
+ maximumWidth: 300
+ maximumHeight: 200
+ minimumHeight: 200
+
+ Text {
+ font.pointSize: 18
+ text: "Eth Go"
+ }
+
+ }
+
+ 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
+ }
+}