From e2bf5d1270b1dc33f308ab134e7e7d3f4f64b7d4 Mon Sep 17 00:00:00 2001
From: Maran <maran.hidskes@gmail.com>
Date: Thu, 10 Apr 2014 14:53:12 -0400
Subject: Implemented key importing/generation for the GUI

---
 ethereal/assets/qml/first_run.qml | 155 ++++++++++++++++++++++++++++++++++++++
 ethereal/assets/qml/wallet.qml    |  29 +++++++
 ethereal/ethereum.go              |   2 -
 ethereal/ui/gui.go                |  34 ++++++---
 ethereal/ui/library.go            |  28 +++++++
 5 files changed, 237 insertions(+), 11 deletions(-)
 create mode 100644 ethereal/assets/qml/first_run.qml

(limited to 'ethereal')

diff --git a/ethereal/assets/qml/first_run.qml b/ethereal/assets/qml/first_run.qml
new file mode 100644
index 000000000..0bd3b4ce1
--- /dev/null
+++ b/ethereal/assets/qml/first_run.qml
@@ -0,0 +1,155 @@
+import QtQuick 2.0
+import Ethereum 1.0
+
+// Which ones do we actually need?
+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 QtQuick.Dialogs 1.1
+
+ApplicationWindow {
+  id: wizardRoot
+  width: 500
+  height: 400
+  title: "Ethereal first run setup"
+
+  Column {
+    spacing: 5
+    anchors.leftMargin: 10
+    anchors.left: parent.left
+
+    Text {
+      visible: true
+      text: "<h2>Ethereal setup</h2>"
+    }
+
+    Column {
+      id: restoreColumn
+      spacing: 5
+      Text {
+        visible: true
+        font.pointSize: 14
+        text: "Restore your Ethereum account"
+        id: restoreLabel
+      }
+
+      TextField {
+        id: txPrivKey
+        width: 480
+        placeholderText: "Private key or mnemonic words"
+        focus: true
+        onTextChanged: {
+          if(this.text.length == 64){
+            detailLabel.text = "Private (hex) key detected."
+            actionButton.enabled = true
+          }
+          else if(this.text.split(" ").length == 24){
+            detailLabel.text = "Mnemonic key detected."
+            actionButton.enabled = true
+          }else{
+            detailLabel.text = ""
+            actionButton.enabled = false
+          }
+        }
+      }
+      Row {
+        spacing: 10
+        Button {
+          id: actionButton
+          text: "Restore"
+          enabled: false
+          onClicked: {
+           var success = eth.importAndSetPrivKey(txPrivKey.text)
+           if(success){
+             importedDetails.visible = true
+             restoreColumn.visible = false
+             newKey.visible = false
+             wizardRoot.height = 120
+           }
+          }
+        }
+        Text {
+          id: detailLabel
+          font.pointSize: 12
+          anchors.topMargin: 10
+        }
+      }
+    }
+    Column {
+      id: importedDetails
+      visible: false
+      Text {
+        text: "<b>Your account has been imported. Please close the application and restart it again to let the changes take effect.</b>"
+        wrapMode: Text.WordWrap
+        width: 460
+      }
+    }
+    Column {
+      spacing: 5
+      id: newDetailsColumn
+      visible: false
+      Text {
+        font.pointSize: 14
+        text: "Your account details"
+      }
+      Label {
+        text: "Address"
+      }
+      TextField {
+        id: addressInput
+        readOnly:true
+        width: 480
+      }
+      Label {
+        text: "Private key"
+      }
+      TextField {
+        id: privkeyInput
+        readOnly:true
+        width: 480
+      }
+      Label {
+        text: "Mnemonic words"
+      }
+      TextField {
+        id: mnemonicInput
+        readOnly:true
+        width: 480
+      }
+      Label {
+        text: "<b>A new account has been created. Please take the time to write down the <i>24 words</i>. You can use those to restore your account at a later date.</b>"
+        wrapMode: Text.WordWrap
+        width: 480
+      }
+      Label {
+        text: "Please restart the application once you have completed the steps above."
+        wrapMode: Text.WordWrap
+        width: 480
+      }
+    }
+
+  }
+  Button {
+    anchors.right: parent.right
+    anchors.bottom: parent.bottom
+    anchors.rightMargin: 10
+    anchors.bottomMargin: 10
+    id: newKey
+    text: "I don't have an account yet"
+    onClicked: {
+      var res = eth.createAndSetPrivKey()
+      mnemonicInput.text = res[0]
+      addressInput.text = res[1]
+      privkeyInput.text = res[2]
+
+      // Hide restore
+      restoreColumn.visible = false
+
+      // Show new details
+      newDetailsColumn.visible = true
+      newKey.visible = false
+    }
+  }
+}
diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
index b22e82f9a..58ba3e3dc 100644
--- a/ethereal/assets/qml/wallet.qml
+++ b/ethereal/assets/qml/wallet.qml
@@ -25,6 +25,35 @@ ApplicationWindow {
 			}
 		}
 
+		Menu {
+			title: "Test"
+			MenuItem {
+				text: "Test test"
+				shortcut: "Ctrl+t"
+        onTriggered: {
+          var win
+          function finishedLoading(){
+            console.log("Trigged")
+            win = wizard.createObject(root)
+          }
+          console.log("Loading wizard")
+
+          var wizard = Qt.createComponent("first_run.qml")
+          if(wizard.status== Component.Ready){
+            console.log("Component is ready")
+            finishedLoading()
+          }else if( wizard.status == Component.Error){
+            console.log("Error loading component:", wizard.errorString())
+          }
+          else{
+            wizard.statusChanged.connect(finishedLoading)
+            console.log("Component is NOT ready")
+            win = wizard.createObject(root)
+          }
+        }
+			}
+		}
+
 		Menu {
 			title: "Network"
 			MenuItem {
diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go
index 99f3b0b52..9cc52039d 100644
--- a/ethereal/ethereum.go
+++ b/ethereal/ethereum.go
@@ -84,8 +84,6 @@ func main() {
 				utils.ImportPrivateKey(ImportKey)
 				os.Exit(0)
 			}
-		} else {
-			utils.CreateKeyPair(false)
 		}
 	}
 
diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go
index 89736ac29..6184baee6 100644
--- a/ethereal/ui/gui.go
+++ b/ethereal/ui/gui.go
@@ -53,7 +53,6 @@ type Gui struct {
 	txDb *ethdb.LDBDatabase
 
 	addr []byte
-
 }
 
 // Create GUI, but doesn't start it
@@ -64,10 +63,16 @@ func New(ethereum *eth.Ethereum) *Gui {
 		panic(err)
 	}
 
-	key := ethutil.Config.Db.GetKeys()[0]
-	addr := key.Address()
+	data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+	// 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 len(data) > 0 {
+		key := ethutil.Config.Db.GetKeys()[0]
+		addr = key.Address()
 
-	ethereum.StateManager().WatchAddr(addr)
+		ethereum.StateManager().WatchAddr(addr)
+	}
 
 	return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
 }
@@ -94,9 +99,18 @@ func (ui *Gui) Start(assetPath string) {
 	context.SetVar("ui", uiLib)
 
 	// Load the main QML interface
-	component, err := ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
+	data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+	var err error
+	var component qml.Object
+	firstRun := len(data) == 0
+
+	if firstRun {
+		component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml"))
+	} else {
+		component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
+	}
 	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'")
+		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)
 	}
 	ui.engine.LoadFile(uiLib.AssetPath("qml/transactions.qml"))
@@ -111,9 +125,11 @@ func (ui *Gui) Start(assetPath string) {
 	ethutil.Config.Log.AddLogSystem(ui)
 
 	// Loads previous blocks
-	go ui.setInitialBlockChain()
-	go ui.readPreviousTransactions()
-	go ui.update()
+	if firstRun == false {
+		go ui.setInitialBlockChain()
+		go ui.readPreviousTransactions()
+		go ui.update()
+	}
 
 	ui.win.Show()
 	ui.win.Wait()
diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go
index 9a7469426..b8ecf3b14 100644
--- a/ethereal/ui/library.go
+++ b/ethereal/ui/library.go
@@ -5,7 +5,9 @@ import (
 	"fmt"
 	"github.com/ethereum/eth-go/ethchain"
 	"github.com/ethereum/eth-go/ethutil"
+	"github.com/ethereum/go-ethereum/utils"
 	"github.com/obscuren/mutan"
+	"github.com/obscuren/secp256k1-go"
 	"strings"
 )
 
@@ -15,6 +17,32 @@ type EthLib struct {
 	txPool       *ethchain.TxPool
 }
 
+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) {
+	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))
+	mnemonicString := strings.Join(mne, " ")
+	return mnemonicString, fmt.Sprintf("%x", pair.Address()), fmt.Sprintf("%x", prv), fmt.Sprintf("%x", pub)
+}
+
 func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, data string) (string, error) {
 	var hash []byte
 	var contractCreation bool
-- 
cgit v1.2.3


From cf1ae41bc0bedeb5208dc00696c538c13f2183c6 Mon Sep 17 00:00:00 2001
From: Maran <maran.hidskes@gmail.com>
Date: Fri, 11 Apr 2014 13:26:14 -0400
Subject: Improved (hopefully) the send transaction tab

---
 ethereal/assets/qml/wallet.qml | 269 +++++++++++++++++++++++++++++++++--------
 ethereal/ui/library.go         |  14 ++-
 2 files changed, 226 insertions(+), 57 deletions(-)

(limited to 'ethereal')

diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
index 58ba3e3dc..742a1283a 100644
--- a/ethereal/assets/qml/wallet.qml
+++ b/ethereal/assets/qml/wallet.qml
@@ -179,70 +179,233 @@ ApplicationWindow {
 				visible: false
 				anchors.fill: parent
 				color: "#00000000"
-
-				ColumnLayout {
-					width: 400
-					anchors.left: parent.left
-					anchors.top: parent.top
+				TabView{
+					anchors.fill: parent
+					anchors.rightMargin: 5
 					anchors.leftMargin: 5
 					anchors.topMargin: 5
-					TextField {
-						id: txRecipient
-						placeholderText: "Recipient address (or empty for contract)"
-						Layout.fillWidth: true
-					}
-
-					TextField {
-						id: txValue
-						width: 200
-						placeholderText: "Amount"
+					anchors.bottomMargin: 5
+					id: newTransactionTab
+					Component.onCompleted:{
+						addTab("Send ether", newTransaction)
+						addTab("Create contract", newContract)
 					}
-					TextField {
-						id: txGas
-						width: 200
-						placeholderText: "Gas"
-						anchors.left: txValue
+				}
+				Component {
+					id: newTransaction
+					Column {
+						spacing: 5
 						anchors.leftMargin: 5
+						anchors.topMargin: 5
+						anchors.top: parent.top
+						anchors.left: parent.left
+						TextField {
+							id: txSimpleRecipient
+							placeholderText: "Recipient address"
+							Layout.fillWidth: true
+              validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
+							width: 530
+						}
+						TextField {
+							id: txSimpleValue
+							placeholderText: "Amount"
+							anchors.rightMargin: 5
+							validator: IntValidator { }
+						}
+						Button {
+							id: txSimpleButton
+							text: "Send"
+							onClicked: {
+								//this.enabled = false
+								var res = eth.createTx(txSimpleRecipient.text, txSimpleValue.text,"","","")
+								if(res[1]) {
+									txSimpleResult.text = "There has been an error broadcasting your transaction:" + res[1].error()
+									txSimpleResult.visible = true
+								} else {
+									txSimpleResult.text = "Your transaction has been broadcasted over the network.\nYour transaction id is:"
+									txSimpleOutput.text = res[0]
+									txSimpleOutput.visible = true
+									txSimpleResult.visible = true
+                  txSimpleValue.visible = false
+                  txSimpleRecipient.visible = false
+									txSimpleValue.text = ""
+									txSimpleRecipient.text = ""
+                  txSimpleRecipient.focus = true
+                  newSimpleTxButton.visible = true
+                  this.visible = false
+								}
+							}
+						}
+						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
+                txSimpleResult.text = ""
+                txSimpleOutput.text = ""
+                txSimpleResult.visible = false
+                txSimpleOutput.visible = false
+                txSimpleValue.visible = true
+                txSimpleRecipient.visible = true
+                txSimpleButton.visible = true
+							}
+						}
 					}
-					TextField {
-						id: txGasPrice
-						width: 200
-						placeholderText: "Gas price"
-						anchors.left: txGas
+				}
+				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
 
-					Label {
-						text: "Transaction data"
-					}
+						TextField {
+							id: txValue
+							width: 200
+							placeholderText: "Amount"
+							validator: IntValidator { }
+              onTextChanged: {
+                contractFormReady()
+              }
+						}
+						TextField {
+							id: txGas
+							width: 200
+							validator: IntValidator { }
+							placeholderText: "Gas"
+              onTextChanged: {
+                contractFormReady()
+              }
+						}
+						TextField {
+							id: txGasPrice
+							width: 200
+							placeholderText: "Gas price"
+							validator: IntValidator { }
+              onTextChanged: {
+                contractFormReady()
+              }
+						}
 
-					TextArea {
-						id: codeView
-						anchors.topMargin: 5
-						Layout.fillWidth: true
-						width: parent.width /2 
-					}
+						Label {
+              id: txDataLabel
+							text: "Transaction data"
+						}
 
-					Button {
-						id: txButton
-						text: "Send"
-						onClicked: {
-							//this.enabled = false
-							var res = eth.createTx(txRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text)
-							if(res[1]) {
-								txOutput.text = "Output:\n" + res[1].error()
-							} else {
-								txOutput.text = "Output:\n" + res[0]
+						TextArea {
+							id: codeView
+							anchors.topMargin: 5
+							Layout.fillWidth: true
+							width: parent.width /2
+              onTextChanged: {
+                contractFormReady()
+              }
+						}
+
+						Button {
+							id: txButton
+              states: [
+                State {
+                  name: "READY"
+                  PropertyChanges { target: txButton; enabled: true}
+                },
+                State {
+                  name: "NOTREADY"
+                  PropertyChanges { target: txButton; enabled:false}
+                }
+              ]
+							text: "Send"
+              enabled: false
+							onClicked: {
+								//this.enabled = false
+								var res = eth.createTx("", 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 contract has been submitted:\n"
+                  txOutput.text = res[0]
+                  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"
 							}
-							txOutput.visible = true
 						}
-					}
-					TextArea {
-						id: txOutput
-						visible: false
-						Layout.fillWidth: true
-						height: 40
-						anchors.bottom: parent.bottom
 					}
 				}
 			}
diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go
index b8ecf3b14..d4800bf1d 100644
--- a/ethereal/ui/library.go
+++ b/ethereal/ui/library.go
@@ -63,15 +63,21 @@ func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, data strin
 	var tx *ethchain.Transaction
 	// Compile and assemble the given data
 	if contractCreation {
-		asm, err := mutan.Compile(strings.NewReader(data), false)
-		if err != nil {
-			return "", err
+		asm, errors := mutan.Compile(strings.NewReader(data), false)
+		if len(errors) > 0 {
+			var errs string
+			for _, er := range errors {
+				if er != nil {
+					errs += er.Error()
+				}
+			}
+			return "", fmt.Errorf(errs)
 		}
 
 		code := ethutil.Assemble(asm...)
 		tx = ethchain.NewContractCreationTx(value, gasPrice, code)
 	} else {
-		tx = ethchain.NewTransactionMessage(hash, value, gasPrice, gas, []string{})
+		tx = ethchain.NewTransactionMessage(hash, value, gasPrice, gas, nil)
 	}
 	acc := lib.stateManager.GetAddrState(keyPair.Address())
 	tx.Nonce = acc.Nonce
-- 
cgit v1.2.3