Merge branch 'release/0.3.0'

poc8 0.3.0
obscuren 11 years ago
commit 0adfa489de
  1. 2
      LICENSE
  2. 41
      README.md
  3. 8
      config.go
  4. 51
      dev_console.go
  5. 147
      ethereum.go
  6. BIN
      net.png
  7. BIN
      net.pxm
  8. BIN
      network.png
  9. BIN
      new.png
  10. 35
      test_app.qml
  11. 9
      transactions.qml
  12. BIN
      tx.png
  13. BIN
      tx.pxm
  14. 217
      ui/gui.go
  15. 60
      ui/library.go
  16. 40
      ui/ui_lib.go
  17. 357
      wallet.qml

@ -1,6 +1,6 @@
The MIT License (MIT) 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

@ -3,44 +3,18 @@ Ethereum
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-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 Go Client (c) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof The current state is "Proof of Concept 3". For build instructions see
of Concept 2". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge). the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge).
Ethereum Go is split up in several sub packages Please refer to each For the development Go Package please see [eth-go package](https://github.com/ethereum/eth-go).
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.
Build Build
======= =======
For build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge) For build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge)
Command line options Command line options
==================== ====================
@ -52,6 +26,9 @@ Command line options
-upnp Enable UPnP (= false) -upnp Enable UPnP (= false)
-x Desired amount of peers (= 5) -x Desired amount of peers (= 5)
-h This help -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 Developer console commands

@ -13,15 +13,23 @@ var AddPeer string
var MaxPeer int var MaxPeer int
var GenAddr bool var GenAddr bool
var UseSeed bool var UseSeed bool
var ImportKey string
var ExportKey bool
var UseGui bool
var DataDir string
func Init() { func Init() {
flag.BoolVar(&StartConsole, "c", false, "debug and testing console") flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
flag.BoolVar(&StartMining, "m", false, "start dagger mining") flag.BoolVar(&StartMining, "m", false, "start dagger mining")
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
flag.BoolVar(&UseGui, "gui", true, "use the gui")
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
flag.BoolVar(&ExportKey, "export", false, "export private key")
flag.StringVar(&OutboundPort, "p", "30303", "listening port") 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.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
flag.Parse() flag.Parse()

@ -2,6 +2,7 @@ package main
import ( import (
"bufio" "bufio"
"bytes"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "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() { func (i *Console) PrintRoot() {
root := ethutil.NewValue(i.trie.Root) root := ethutil.NewValue(i.trie.Root)
if len(root.Bytes()) != 0 { if len(root.Bytes()) != 0 {
@ -136,7 +163,8 @@ func (i *Console) ParseInput(input string) bool {
case "block": case "block":
encoded, _ := hex.DecodeString(tokens[1]) encoded, _ := hex.DecodeString(tokens[1])
block := i.ethereum.BlockManager.BlockChain().GetBlock(encoded) block := i.ethereum.BlockManager.BlockChain().GetBlock(encoded)
fmt.Println(block) info := block.BlockInfo()
fmt.Printf("++++++++++ #%d ++++++++++\n%v\n", info.Number, block)
case "say": case "say":
i.ethereum.Broadcast(ethwire.MsgTalkTy, []interface{}{tokens[1]}) i.ethereum.Broadcast(ethwire.MsgTalkTy, []interface{}{tokens[1]})
case "addp": case "addp":
@ -151,13 +179,13 @@ func (i *Console) ParseInput(input string) bool {
fmt.Println("recipient err:", err) fmt.Println("recipient err:", err)
} else { } else {
tx := ethchain.NewTransaction(recipient, ethutil.Big(tokens[2]), []string{""}) tx := ethchain.NewTransaction(recipient, ethutil.Big(tokens[2]), []string{""})
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
keyRing := ethutil.NewValueFromBytes(data) key := ethutil.Config.Db.GetKeys()[0]
tx.Sign(keyRing.Get(0).Bytes()) tx.Sign(key.PrivateKey)
fmt.Printf("%x\n", tx.Hash())
i.ethereum.TxPool.QueueTransaction(tx) i.ethereum.TxPool.QueueTransaction(tx)
}
fmt.Printf("%x\n", tx.Hash())
}
case "gettx": case "gettx":
addr, _ := hex.DecodeString(tokens[1]) addr, _ := hex.DecodeString(tokens[1])
data, _ := ethutil.Config.Db.Get(addr) data, _ := ethutil.Config.Db.Get(addr)
@ -168,10 +196,17 @@ func (i *Console) ParseInput(input string) bool {
fmt.Println("gettx: tx not found") fmt.Println("gettx: tx not found")
} }
case "contract": case "contract":
contract := ethchain.NewTransaction([]byte{}, ethutil.Big(tokens[1]), []string{"PUSH", "1234"}) fmt.Println("Contract editor (Ctrl-D = done)")
fmt.Printf("%x\n", contract.Hash()) 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) i.ethereum.TxPool.QueueTransaction(contract)
fmt.Printf("%x\n", contract.Hash()[12:])
case "exit", "quit", "q": case "exit", "quit", "q":
return false return false
case "help": case "help":

@ -5,6 +5,9 @@ import (
"github.com/ethereum/eth-go" "github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"github.com/ethereum/go-ethereum/ui"
"github.com/niemeyer/qml"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
"log" "log"
"os" "os"
@ -33,13 +36,12 @@ func CreateKeyPair(force bool) {
data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
if len(data) == 0 || force { if len(data) == 0 || force {
pub, prv := secp256k1.GenerateKeyPair() 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(` fmt.Printf(`
Generating new address and keypair. Generating new address and keypair.
Please keep your keys somewhere save. Please keep your keys somewhere save.
Currently Ethereum(G) does not support
exporting keys.
++++++++++++++++ KeyRing +++++++++++++++++++ ++++++++++++++++ KeyRing +++++++++++++++++++
addr: %x addr: %x
@ -47,19 +49,45 @@ prvk: %x
pubk: %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() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
Init() 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() ethchain.InitFees()
ethutil.ReadConfig(".ethereum") ethutil.ReadConfig(DataDir)
ethutil.Config.Seed = UseSeed ethutil.Config.Seed = UseSeed
// Instantiated a eth stack // Instantiated a eth stack
@ -68,6 +96,7 @@ func main() {
log.Println("eth start err:", err) log.Println("eth start err:", err)
return return
} }
ethereum.Port = OutboundPort
if GenAddr { if GenAddr {
fmt.Println("This action overwrites your old private key. Are you sure? (y/n)") fmt.Println("This action overwrites your old private key. Are you sure? (y/n)")
@ -87,7 +116,31 @@ func main() {
} }
os.Exit(0) os.Exit(0)
} else { } 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 { if ShowGenesis {
@ -111,41 +164,47 @@ func main() {
go console.Start() go console.Start()
} }
RegisterInterupts(ethereum) if UseGui {
gui := ethui.New(ethereum)
ethereum.Start() gui.Start()
//ethereum.Stop()
if StartMining { } else {
log.Printf("Miner started\n") RegisterInterupts(ethereum)
ethereum.Start()
// Fake block mining. It broadcasts a new block every 5 seconds
go func() { if StartMining {
pow := &ethchain.EasyPow{} log.Printf("Miner started\n")
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
keyRing := ethutil.NewValueFromBytes(data) // Fake block mining. It broadcasts a new block every 5 seconds
addr := keyRing.Get(1).Bytes() go func() {
pow := &ethchain.EasyPow{}
for { data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
txs := ethereum.TxPool.Flush() keyRing := ethutil.NewValueFromBytes(data)
// Create a new block which we're going to mine addr := keyRing.Get(1).Bytes()
block := ethereum.BlockManager.BlockChain().NewBlock(addr, txs)
// Apply all transactions to the block for {
ethereum.BlockManager.ApplyTransactions(block, block.Transactions()) txs := ethereum.TxPool.Flush()
// Create a new block which we're going to mine
ethereum.BlockManager.AccumelateRewards(block, block) block := ethereum.BlockManager.BlockChain().NewBlock(addr, txs)
// Apply all transactions to the block
// Search the nonce ethereum.BlockManager.ApplyTransactions(block, block.Transactions())
block.Nonce = pow.Search(block)
err := ethereum.BlockManager.ProcessBlock(block) ethereum.BlockManager.AccumelateRewards(block, block)
if err != nil {
log.Println(err) // Search the nonce
} else { block.Nonce = pow.Search(block)
log.Println("\n+++++++ MINED BLK +++++++\n", ethereum.BlockManager.BlockChain().CurrentBlock) 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 // Wait for shutdown
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

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

@ -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" }
}

BIN
tx.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
tx.pxm

Binary file not shown.

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

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

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

@ -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
}
}
Loading…
Cancel
Save