From aa33a4b2fb9dc07468498decceb6fdb56d38f54d Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 22 Feb 2014 23:19:38 +0100 Subject: [PATCH] Added some ui elements to make it easier to connect to nodes --- config.go | 2 ++ ethereum.go | 3 +- test_app.qml | 4 +-- ui/gui.go | 85 ++++++++++++++++++++++++++++++++++++--------------- ui/library.go | 10 +++--- wallet.qml | 70 +++++++++++++++++++++++++++++++++++++++--- 6 files changed, 138 insertions(+), 36 deletions(-) diff --git a/config.go b/config.go index 79d40d7069..bafc3e300f 100644 --- a/config.go +++ b/config.go @@ -16,6 +16,7 @@ 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") @@ -27,6 +28,7 @@ func Init() { 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") diff --git a/ethereum.go b/ethereum.go index 5eda09a445..384b227483 100644 --- a/ethereum.go +++ b/ethereum.go @@ -89,11 +89,12 @@ func main() { runtime.GOMAXPROCS(runtime.NumCPU()) ethchain.InitFees() - ethutil.ReadConfig(".ethereum") + ethutil.ReadConfig(DataDir) ethutil.Config.Seed = UseSeed // Instantiated a eth stack ethereum, err := eth.New(eth.CapDefault, UseUPnP) + ethereum.Port = OutboundPort if err != nil { log.Println("eth start err:", err) return diff --git a/test_app.qml b/test_app.qml index c7593bf12e..aace4e881f 100644 --- a/test_app.qml +++ b/test_app.qml @@ -1,7 +1,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0; import QtQuick.Layouts 1.0; -import GoExtensions 1.0 +import Ethereum 1.0 ApplicationWindow { minimumWidth: 500 @@ -29,7 +29,7 @@ ApplicationWindow { anchors.topMargin: 5 text: "Place bet" onClicked: { - txHash.text = eth.createTx("e6716f9544a56c530d868e4bfbacb172315bdead", parseInt(textField.text)) + txHash.text = eth.createTx("e6716f9544a56c530d868e4bfbacb172315bdead", textField.text) } } } diff --git a/ui/gui.go b/ui/gui.go index fad7e95911..51fd6d9a84 100644 --- a/ui/gui.go +++ b/ui/gui.go @@ -12,27 +12,13 @@ import ( "time" ) -type Gui struct { - win *qml.Window - engine *qml.Engine - component *qml.Common - eth *eth.Ethereum - - // The Ethereum library - lib *EthLib -} - -func New(ethereum *eth.Ethereum) *Gui { - lib := &EthLib{blockManager: ethereum.BlockManager, blockChain: ethereum.BlockManager.BlockChain(), txPool: ethereum.TxPool} - - return &Gui{eth: ethereum, lib: lib} -} - +// Block interface exposed to QML type Block struct { Number int Hash string } +// Creates a new QML Block from a chain block func NewBlockFromBlock(block *ethchain.Block) *Block { info := block.BlockInfo() hash := hex.EncodeToString(block.Hash()) @@ -40,13 +26,42 @@ func NewBlockFromBlock(block *ethchain.Block) *Block { 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 +} + +// 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} + + data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) + keyRing := ethutil.NewValueFromBytes(data) + addr := keyRing.Get(1).Bytes() + + ethereum.BlockManager.WatchAddr(addr) + + return &Gui{eth: ethereum, lib: lib} +} + func (ui *Gui) Start() { - qml.RegisterTypes("GoExtensions", 1, 0, []qml.TypeSpec{{ + // Register ethereum functions + qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{ Init: func(p *Block, obj qml.Object) { p.Number = 0; p.Hash = "" }, }}) 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) @@ -55,13 +70,18 @@ func (ui *Gui) Start() { 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 + // 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.updatePeers() @@ -70,6 +90,7 @@ func (ui *Gui) Start() { } 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) @@ -81,17 +102,24 @@ func (ui *Gui) ProcessBlock(block *ethchain.Block) { ui.win.Root().Call("addBlock", NewBlockFromBlock(block)) } +// Logging functions that log directly to the GUI interface func (ui *Gui) Println(v ...interface{}) { - str := fmt.Sprintln(v...) - // remove last \n - ui.win.Root().Call("addLog", str[:len(str)-1]) + 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") - ui.win.Root().Call("addLog", str) + lines := strings.Split(str, "\n") + for _, line := range lines { + ui.win.Root().Call("addLog", line) + } } +// Simple go routine function that updates the list of peers in the GUI func (ui *Gui) updatePeers() { for { ui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", ui.eth.Peers().Len(), ui.eth.MaxPeers)) @@ -99,11 +127,14 @@ func (ui *Gui) updatePeers() { } } +// UI Library that has some basic functionality exposed type UiLib struct { - engine *qml.Engine - eth *eth.Ethereum + 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 { @@ -118,7 +149,13 @@ func (ui *UiLib) Open(path string) { } func (ui *UiLib) Connect() { - ui.eth.Start() + if !ui.connected { + ui.eth.Start() + } +} + +func (ui *UiLib) ConnectToPeer(addr string) { + ui.eth.ConnectToPeer(addr) } type Tester struct { diff --git a/ui/library.go b/ui/library.go index 36952e198e..0dfb10ac75 100644 --- a/ui/library.go +++ b/ui/library.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" - "math/big" ) type EthLib struct { @@ -14,15 +13,18 @@ type EthLib struct { txPool *ethchain.TxPool } -func (lib *EthLib) CreateTx(receiver string, amount uint64) string { +func (lib *EthLib) CreateTx(receiver, a string) string { hash, err := hex.DecodeString(receiver) if err != nil { return err.Error() } - - tx := ethchain.NewTransaction(hash, big.NewInt(int64(amount)), []string{""}) data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) keyRing := ethutil.NewValueFromBytes(data) + + amount := ethutil.Big(a) + tx := ethchain.NewTransaction(hash, amount, []string{""}) + tx.Nonce = lib.blockManager.GetAddrState(keyRing.Get(1).Bytes()).Nonce + tx.Sign(keyRing.Get(0).Bytes()) lib.txPool.QueueTransaction(tx) diff --git a/wallet.qml b/wallet.qml index e6cb32b182..b186148012 100644 --- a/wallet.qml +++ b/wallet.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.0; import QtQuick.Layouts 1.0; import QtQuick.Dialogs 1.0; import QtQuick.Window 2.1; -import GoExtensions 1.0 +import Ethereum 1.0 ApplicationWindow { id: root @@ -22,15 +22,19 @@ ApplicationWindow { width: parent.width Button { text: "Send" - onClicked: console.log("SEND") + onClicked: { + console.log(eth.createTx(txReceiver.text, txAmount.text)) + } } TextField { + id: txAmount width: 200 placeholderText: "Amount" } TextField { + id: txReceiver width: 300 placeholderText: "Receiver Address (or empty for contract)" Layout.fillWidth: true @@ -47,15 +51,43 @@ ApplicationWindow { anchors.left: parent.left TextArea { - id: codeView - width: parent.width /2 + id: codeView + width: parent.width /2 } TextArea { - readOnly: true + readOnly: true } } + 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() + } + } + } + + property var blockModel: ListModel { id: blockModel } @@ -142,6 +174,34 @@ ApplicationWindow { } } + 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) + addrPeerWin.visible = false + } + } + } + + function addBlock(block) { blockModel.insert(0, {number: block.number, hash: block.hash}) }