diff --git a/README.md b/README.md index 085030ce2..c32b51037 100644 --- a/README.md +++ b/README.md @@ -27,20 +27,22 @@ General command line options ``` Shared between ethereum and ethereal --m Start mining blocks --genaddr Generates a new address and private key (destructive action) --p Port on which the server will accept incomming connections +-id Set the custom identifier of the client (shows up on other clients) +-port Port on which the server will accept incomming connections -upnp Enable UPnP --x Desired amount of peers --r Start JSON RPC +-maxpeer Desired amount of peers +-rpc Start JSON RPC + -dir Data directory used to store configs and databases -import Import a private key +-genaddr Generates a new address and private key (destructive action) -h This Ethereum only ethereum [options] [filename] -js Start the JavaScript REPL filename Load the given file and interpret as JavaScript +-m Start mining blocks Etheral only -asset_path absolute path to GUI assets directory diff --git a/ethereal/assets/debugger/debugger.qml b/ethereal/assets/debugger/debugger.qml index bff653fb8..ca67e857d 100644 --- a/ethereal/assets/debugger/debugger.qml +++ b/ethereal/assets/debugger/debugger.qml @@ -8,7 +8,7 @@ import Ethereum 1.0 ApplicationWindow { visible: false - title: "IceCream" + title: "IceCREAM" minimumWidth: 1280 minimumHeight: 900 width: 1290 @@ -224,8 +224,8 @@ ApplicationWindow { } function setInstruction(num) { - asmTableView.selection.clear() - asmTableView.selection.select(num) + //asmTableView.selection.clear() + //asmTableView.selection.select(num) } function setMem(mem) { @@ -255,6 +255,10 @@ ApplicationWindow { } function setLog(msg) { - logModel.append({message: msg}) + logModel.insert(0, {message: msg}) + } + + function clearLog() { + logModel.clear() } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 847f219a9..458ce90e4 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -8,1004 +8,905 @@ 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: "Tools" - MenuItem { - text: "Muted" - shortcut: "Ctrl+e" - onTriggered: ui.muted("") - } - - MenuItem { - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: ui.startDebugger() - } - } - - 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 - infoView.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) - } - } - } - - Image { - source: ui.assetPath("heart.png") - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - setView(infoView) - } - } - } - } - } - - 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: "inout" ; title: "" ; width: 40 } - TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } - TableViewColumn{ role: "address" ; title: "Address" ; width: 430 } - TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 } - - model: txModel - } - } - - Rectangle { - id: newTxView - property var title: "New transaction" - visible: false - anchors.fill: parent - color: "#00000000" - TabView{ - anchors.fill: parent - anchors.rightMargin: 5 - anchors.leftMargin: 5 - anchors.topMargin: 5 - anchors.bottomMargin: 5 - id: newTransactionTab - Component.onCompleted:{ - addTab("Simple send", newTransaction) - addTab("Contracts", newContract) - } - } - } - - 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 } - TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 } - - model: blockModel - - onDoubleClicked: { - popup.visible = true - popup.setDetails(blockModel.get(row)) - } - } - - 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 - } - } - - Rectangle { - id: infoView - property var title: "Information" - visible: false - color: "#00000000" - anchors.fill: parent - - Label { - id: addressLabel - text: "Address" - anchors { - margins: 5 - top: parent.top - left: parent.left - } - } - TextField { - anchors { - margins: 5 - left: addressLabel.right - top: parent.top - } - text: pub.getKey().address - width: 500 - } - } - - /* - 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()) - //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html"))) - ui.openHtml(openAppDialog.fileUrl.toString()) - } - } - - statusBar: StatusBar { - RowLayout { - anchors.fill: parent - - Button { - property var enabled: true - id: debuggerWindow - onClicked: { - ui.startDebugger() - } - text: "Debugger" - } - - Button { - id: importAppButton - anchors.left: debuggerWindow.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 - width: root.width - height: 240 - Component{ - id: blockDetailsDelegate - Rectangle { - color: "#252525" - width: popup.width - height: 200 - Column { - anchors.leftMargin: 10 - anchors.topMargin: 5 - anchors.top: parent.top - anchors.left: parent.left - Text { text: '

Block details

'; color: "#F2F2F2"} - Text { text: 'Block number: ' + number; color: "#F2F2F2"} - Text { text: 'Hash: ' + hash; color: "#F2F2F2"} - Text { text: 'Block found at: ' + prettyTime; color: "#F2F2F2"} - } - } - } - ListView { - model: singleBlock - delegate: blockDetailsDelegate - anchors.top: parent.top - height: 70 - anchors.leftMargin: 20 - id: listViewThing - Layout.maximumHeight: 40 - } - TableView { - id: txView - anchors.top: listViewThing.bottom - anchors.topMargin: 50 - width: parent.width - - TableViewColumn{width: 90; role: "value" ; title: "Value" } - TableViewColumn{width: 200; role: "hash" ; title: "Hash" } - TableViewColumn{width: 200; role: "sender" ; title: "Sender" } - TableViewColumn{width: 200;role: "address" ; title: "Receiver" } - TableViewColumn{width: 60; role: "gas" ; title: "Gas" } - TableViewColumn{width: 60; role: "gasPrice" ; title: "Gas Price" } - TableViewColumn{width: 60; role: "isContract" ; title: "Contract" } - - model: transactionModel - onClicked: { - var tx = transactionModel.get(row) - if(tx.data) { - popup.showContractData(tx) - }else{ - popup.height = 230 - } - } - } - - function showContractData(tx) { - txDetailsDebugButton.tx = tx - if(tx.createsContract) { - contractData.text = tx.data - contractLabel.text = "

Transaction created contract " + tx.address + "

" - }else{ - contractLabel.text = "

Transaction ran contract " + tx.address + "

" - contractData.text = tx.rawData - } - popup.height = 400 - } - - Rectangle { - id: txDetails - width: popup.width - height: 300 - anchors.left: listViewThing.left - anchors.top: txView.bottom - Label { - text: "

Contract data

" - anchors.top: parent.top - anchors.left: parent.left - id: contractLabel - anchors.leftMargin: 10 - } - Button { - property var tx - id: txDetailsDebugButton - anchors.right: parent.right - anchors.rightMargin: 10 - anchors.top: parent.top - anchors.topMargin: 10 - text: "Debug contract" - onClicked: { - if(tx.createsContract){ - ui.startDbWithCode(tx.rawData) - }else { - ui.startDbWithContractAndData(tx.address, tx.rawData) - } - } - } - TextArea { - id: contractData - text: "Contract" - anchors.top: contractLabel.bottom - anchors.left: parent.left - anchors.bottom: popup.bottom - wrapMode: Text.Wrap - width: parent.width - 30 - height: 80 - anchors.leftMargin: 10 - } - } - property var transactionModel: ListModel { - id: transactionModel - } - property var singleBlock: ListModel { - id: singleBlock - } - function setDetails(block){ - singleBlock.set(0,block) - popup.height = 230 - transactionModel.clear() - if(block.txs != undefined){ - for(var i = 0; i < block.txs.count; ++i) { - transactionModel.insert(0, block.txs.get(i)) - } - if(block.txs.get(0).data){ - popup.showContractData(block.txs.get(0)) - } - } - txView.forceActiveFocus() - } - } - - 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: "

Ethereal


Development

Jeffrey Wilcke
Maran Hidskes
" - } - - } - - ApplicationWindow { - id: debugWindow - visible: false - title: "Debugger" - minimumWidth: 600 - minimumHeight: 600 - width: 800 - height: 600 - - - Item { - id: keyHandler - focus: true - Keys.onPressed: { - if (event.key == Qt.Key_Space) { - ui.next() - } - } - } - SplitView { - anchors.fill: parent - property var asmModel: ListModel { - id: asmModel - } - TableView { - id: asmTableView - width: 200 - TableViewColumn{ role: "value" ; title: "" ; width: 100 } - model: asmModel - } - - Rectangle { - anchors.left: asmTableView.right - anchors.right: parent.right - SplitView { - orientation: Qt.Vertical - anchors.fill: parent - - TableView { - property var memModel: ListModel { - id: memModel - } - height: parent.height/2 - width: parent.width - TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50} - TableViewColumn{ role: "value" ; title: "Memory" ; width: 750} - model: memModel - } - - SplitView { - orientation: Qt.Horizontal - id: debugSplitView - TableView { - property var debuggerLog: ListModel { - id: debuggerLog - } - TableViewColumn{ role: "value"; title: "Debug messages" } - model: debuggerLog - } - TableView { - property var stackModel: ListModel { - id: stackModel - } - height: parent.height/2 - width: parent.width - TableViewColumn{ role: "value" ; title: "Stack" ; width: debugSplitView.width } - model: stackModel - } - } - } - } - } - statusBar: StatusBar { - RowLayout { - anchors.fill: parent - Button { - property var enabled: true - id: debugNextButton - onClicked: { - ui.next() - } - text: "Next" - } - } - } - } - - function setAsm(asm) { - asmModel.append({asm: asm}) - } - - function setInstruction(num) { - asmTableView.selection.clear() - asmTableView.selection.select(num-1) - } - - function clearAsm() { - asmModel.clear() - } - - function setMem(mem) { - memModel.append({num: mem.num, value: mem.value}) - } - function clearMem(){ - memModel.clear() - } - - function setStack(stack) { - stackModel.append({value: stack}) - } - function addDebugMessage(message){ - console.log("WOOP:") - debuggerLog.append({value: message}) - } - - function clearStack() { - stackModel.clear() - } - - function loadPlugin(name) { - console.log("Loading plugin" + name) - mainView.addPlugin(name) - } - - function setWalletValue(value) { - walletValueLabel.text = value - } - - function addTx(tx, inout) { - var isContract - if (tx.contract == true){ - isContract = "Yes" - }else{ - isContract = "No" - } - txModel.insert(0, {inout: inout, hash: tx.hash, address: tx.address, value: tx.value, contract: isContract}) - } - - function addBlock(block, initial) { - var txs = JSON.parse(block.transactions); - var amount = 0 - if(initial == undefined){ - initial = false - } - - if(txs != null){ - amount = txs.length - } - - if(initial){ - blockModel.append({number: block.number, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) - }else{ - blockModel.insert(0, {number: block.number, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) - } - } - - function addLog(str) { - if(str.len != 0) { - logModel.append({description: str}) - } - } - - function setPeers(text) { - peerLabel.text = text - } - function convertToPretty(unixTs){ - var a = new Date(unixTs*1000); - var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; - var year = a.getFullYear(); - var month = months[a.getMonth()]; - var date = a.getDate(); - var hour = a.getHours(); - var min = a.getMinutes(); - var sec = a.getSeconds(); - var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; - return time; - } - // ******************************************* - // Components - // ******************************************* - - // New Contract component - 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 - - TextField { - id: txValue - width: 200 - placeholderText: "Amount" - validator: RegExpValidator { regExp: /\d*/ } - onTextChanged: { - contractFormReady() - } - } - TextField { - id: txGas - width: 200 - validator: RegExpValidator { regExp: /\d*/ } - placeholderText: "Gas" - onTextChanged: { - contractFormReady() - } - } - TextField { - id: txGasPrice - width: 200 - placeholderText: "Gas price" - validator: RegExpValidator { regExp: /\d*/ } - onTextChanged: { - contractFormReady() - } - } - - Row { - id: rowContract - ExclusiveGroup { id: contractTypeGroup } - RadioButton { - id: createContractRadio - text: "Create contract" - checked: true - exclusiveGroup: contractTypeGroup - onClicked: { - txFuelRecipient.visible = false - txDataLabel.text = "Contract code" - } - } - RadioButton { - id: runContractRadio - text: "Run contract" - exclusiveGroup: contractTypeGroup - onClicked: { - txFuelRecipient.visible = true - txDataLabel.text = "Contract arguments" - } - } - } - - - Label { - id: txDataLabel - text: "Contract code" - } - - TextArea { - id: codeView - height: 300 - anchors.topMargin: 5 - Layout.fillWidth: true - width: parent.width /2 - onTextChanged: { - contractFormReady() - } - } - - TextField { - id: txFuelRecipient - placeholderText: "Contract address" - validator: RegExpValidator { regExp: /[a-f0-9]{40}/ } - visible: false - width: 530 - } - - Button { - id: txButton - /* enabled: false */ - states: [ - State { - name: "READY" - PropertyChanges { target: txButton; /*enabled: true*/} - }, - State { - name: "NOTREADY" - PropertyChanges { target: txButton; /*enabled:false*/} - } - ] - text: "Send" - onClicked: { - //this.enabled = false - var res = eth.create(txFuelRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text) - if(res[1]) { - txResult.text = "Your contract could not be send over the network:\n" - txResult.text += res[1].error() - txResult.text += "" - mainContractColumn.state = "ERROR" - } else { - txResult.text = "Your transaction has been submitted:\n" - txOutput.text = res[0].address - 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" - } - } - - Button { - id: debugButton - text: "Debug" - onClicked: { - var res = ui.debugTx("", txValue.text, txGas.text, txGasPrice.text, codeView.text) - debugWindow.visible = true - } - } - } - } - - // New Transaction component - Component { - id: newTransaction - Column { - id: simpleSendColumn - states: [ - State{ - name: "ERROR" - }, - State { - name: "DONE" - PropertyChanges { target: txSimpleValue; visible:false} - PropertyChanges { target: txSimpleRecipient; visible:false} - PropertyChanges { target:newSimpleTxButton; visible:false} - - PropertyChanges { target: txSimpleResult; visible:true} - PropertyChanges { target: txSimpleOutput; visible:true} - PropertyChanges { target:newSimpleTxButton; visible:true} - }, - State { - name: "SETUP" - PropertyChanges { target: txSimpleValue; visible:true; text: ""} - PropertyChanges { target: txSimpleRecipient; visible:true; text: ""} - PropertyChanges { target: txSimpleButton; visible:true} - PropertyChanges { target:newSimpleTxButton; visible:false} - } - ] - spacing: 5 - anchors.leftMargin: 5 - anchors.topMargin: 5 - anchors.top: parent.top - anchors.left: parent.left - - function checkFormState(){ - if(txSimpleRecipient.text.length == 40 && txSimpleValue.text.length > 0) { - txSimpleButton.state = "READY" - }else{ - txSimpleButton.state = "NOTREADY" - } - } - - TextField { - id: txSimpleRecipient - placeholderText: "Recipient address" - Layout.fillWidth: true - validator: RegExpValidator { regExp: /[a-f0-9]{40}/ } - width: 530 - onTextChanged: { checkFormState() } - } - TextField { - id: txSimpleValue - width: 200 - placeholderText: "Amount" - anchors.rightMargin: 5 - validator: RegExpValidator { regExp: /\d*/ } - onTextChanged: { checkFormState() } - } - Button { - id: txSimpleButton - /*enabled: false*/ - states: [ - State { - name: "READY" - PropertyChanges { target: txSimpleButton; /*enabled: true*/} - }, - State { - name: "NOTREADY" - PropertyChanges { target: txSimpleButton; /*enabled: false*/} - } - ] - text: "Send" - onClicked: { - //this.enabled = false - var res = eth.transact(txSimpleRecipient.text, txSimpleValue.text,"","","") - if(res[1]) { - txSimpleResult.text = "There has been an error broadcasting your transaction:" + res[1].error() - } else { - txSimpleResult.text = "Your transaction has been broadcasted over the network.\nYour transaction id is:" - txSimpleOutput.text = res[0].hash - this.visible = false - simpleSendColumn.state = "DONE" - } - } - } - 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 - simpleSendColumn.state = "SETUP" - } - } - } - } + id: root + + property alias miningButtonText: miningButton.text + + width: 900 + height: 600 + minimumHeight: 300 + + title: "Ethereal" + + MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: openAppDialog.open() + } + } + + Menu { + MenuItem { + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: ui.startDebugger() + } + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + } + + 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 + infoView.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) + } + } + } + + Image { + source: ui.assetPath("heart.png") + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + setView(infoView) + } + } + } + } + } + + 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: "inout" ; title: "" ; width: 40 } + TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } + TableViewColumn{ role: "address" ; title: "Address" ; width: 430 } + TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 } + + model: txModel + } + } + + Rectangle { + id: newTxView + property var title: "New transaction" + visible: false + anchors.fill: parent + color: "#00000000" + /* + TabView{ + anchors.fill: parent + anchors.rightMargin: 5 + anchors.leftMargin: 5 + anchors.topMargin: 5 + anchors.bottomMargin: 5 + id: newTransactionTab + Component.onCompleted:{ + addTab("Simple send", newTransaction) + addTab("Contracts", newContract) + } + } + */ + Component.onCompleted: { + newContract.createObject(newTxView) + } + } + + Rectangle { + id: networkView + property var title: "Network" + visible: false + anchors.fill: parent + + TableView { + id: blockTable + width: parent.width + anchors.top: parent.top + anchors.bottom: parent.bottom + TableViewColumn{ role: "number" ; title: "#" ; width: 100 } + TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 } + TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 } + + model: blockModel + + onDoubleClicked: { + popup.visible = true + popup.setDetails(blockModel.get(row)) + } + } + + } + + Rectangle { + id: infoView + property var title: "Information" + visible: false + color: "#00000000" + anchors.fill: parent + + Label { + id: addressLabel + text: "Address" + anchors { + margins: 5 + top: parent.top + left: parent.left + } + } + TextField { + anchors { + margins: 5 + left: addressLabel.right + top: parent.top + } + text: pub.getKey().address + width: 500 + } + + + property var addressModel: ListModel { + id: addressModel + } + TableView { + id: addressView + width: parent.width + height: 200 + anchors.bottom: logView.top + TableViewColumn{ role: "name"; title: "name" } + TableViewColumn{ role: "address"; title: "address"; width: 300} + + model: addressModel + } + + + property var logModel: ListModel { + id: logModel + } + TableView { + id: logView + width: parent.width + height: 200 + 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()) + //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html"))) + ui.openHtml(openAppDialog.fileUrl.toString()) + } + } + + statusBar: StatusBar { + height: 30 + RowLayout { + Button { + id: miningButton + onClicked: { + eth.toggleMining() + } + text: "Start Mining" + } + + Button { + property var enabled: true + id: debuggerWindow + onClicked: { + ui.startDebugger() + } + text: "Debugger" + } + + Button { + id: importAppButton + anchors.left: debuggerWindow.right + anchors.leftMargin: 5 + onClicked: openAppDialog.open() + text: "Import App" + } + + Label { + anchors.left: importAppButton.right + anchors.leftMargin: 5 + id: walletValueLabel + } + } + + Label { + y: 7 + anchors.right: peerImage.left + anchors.rightMargin: 5 + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + y: 7 + id: peerImage + anchors.right: parent.right + width: 10; height: 10 + source: ui.assetPath("network.png") + } + } + + Window { + id: popup + visible: false + property var block + width: root.width + height: 300 + Component{ + id: blockDetailsDelegate + Rectangle { + color: "#252525" + width: popup.width + height: 150 + Column { + anchors.leftMargin: 10 + anchors.topMargin: 5 + anchors.top: parent.top + anchors.left: parent.left + Text { text: '

Block details

'; color: "#F2F2F2"} + Text { text: 'Block number: ' + number; color: "#F2F2F2"} + Text { text: 'Hash: ' + hash; color: "#F2F2F2"} + Text { text: 'Coinbase: ' + coinbase; color: "#F2F2F2"} + Text { text: 'Block found at: ' + prettyTime; color: "#F2F2F2"} + } + } + } + ListView { + model: singleBlock + delegate: blockDetailsDelegate + anchors.top: parent.top + height: 100 + anchors.leftMargin: 20 + id: listViewThing + Layout.maximumHeight: 40 + } + TableView { + id: txView + anchors.top: listViewThing.bottom + anchors.topMargin: 50 + width: parent.width + + TableViewColumn{width: 90; role: "value" ; title: "Value" } + TableViewColumn{width: 200; role: "hash" ; title: "Hash" } + TableViewColumn{width: 200; role: "sender" ; title: "Sender" } + TableViewColumn{width: 200;role: "address" ; title: "Receiver" } + TableViewColumn{width: 60; role: "gas" ; title: "Gas" } + TableViewColumn{width: 60; role: "gasPrice" ; title: "Gas Price" } + TableViewColumn{width: 60; role: "isContract" ; title: "Contract" } + + model: transactionModel + onClicked: { + var tx = transactionModel.get(row) + if(tx.data) { + popup.showContractData(tx) + }else{ + popup.height = 440 + } + } + } + + function showContractData(tx) { + txDetailsDebugButton.tx = tx + if(tx.createsContract) { + contractData.text = tx.data + contractLabel.text = "

Transaction created contract " + tx.address + "

" + }else{ + contractLabel.text = "

Transaction ran contract " + tx.address + "

" + contractData.text = tx.rawData + } + popup.height = 540 + } + + Rectangle { + id: txDetails + width: popup.width + height: 300 + anchors.left: listViewThing.left + anchors.top: txView.bottom + Label { + text: "

Contract data

" + anchors.top: parent.top + anchors.left: parent.left + id: contractLabel + anchors.leftMargin: 10 + } + Button { + property var tx + id: txDetailsDebugButton + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.top: parent.top + anchors.topMargin: 10 + text: "Debug contract" + onClicked: { + if(tx.createsContract){ + ui.startDbWithCode(tx.rawData) + }else { + ui.startDbWithContractAndData(tx.address, tx.rawData) + } + } + } + TextArea { + id: contractData + text: "Contract" + anchors.top: contractLabel.bottom + anchors.left: parent.left + anchors.bottom: popup.bottom + wrapMode: Text.Wrap + width: parent.width - 30 + height: 80 + anchors.leftMargin: 10 + } + } + property var transactionModel: ListModel { + id: transactionModel + } + property var singleBlock: ListModel { + id: singleBlock + } + function setDetails(block){ + singleBlock.set(0,block) + popup.height = 300 + transactionModel.clear() + if(block.txs != undefined){ + for(var i = 0; i < block.txs.count; ++i) { + transactionModel.insert(0, block.txs.get(i)) + } + if(block.txs.get(0).data){ + popup.showContractData(block.txs.get(0)) + } + } + txView.forceActiveFocus() + } + } + + 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: "

Ethereal


Development

Jeffrey Wilcke
Maran Hidskes
" + } + } + + function addDebugMessage(message){ + debuggerLog.append({value: message}) + } + + function addAddress(address) { + addressModel.append({name: address.name, address: address.address}) + } + function clearAddress() { + addressModel.clear() + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + mainView.addPlugin(name) + } + + function setWalletValue(value) { + walletValueLabel.text = value + } + + function addTx(tx, inout) { + var isContract + if (tx.contract == true){ + isContract = "Yes" + }else{ + isContract = "No" + } + + var address; + if(inout == "recv") { + address = tx.sender; + } else { + address = tx.address; + } + txModel.insert(0, {inout: inout, hash: tx.hash, address: address, value: tx.value, contract: isContract}) + } + + function addBlock(block, initial) { + var txs = JSON.parse(block.transactions); + var amount = 0 + if(initial == undefined){ + initial = false + } + + if(txs != null){ + amount = txs.length + } + + if(initial){ + blockModel.append({number: block.number, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) + }else{ + blockModel.insert(0, {number: block.number, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) + } + } + + function addLog(str) { + if(str.len != 0) { + logModel.insert(0, {description: str}) + } + } + + function setPeers(text) { + peerLabel.text = text + } + function convertToPretty(unixTs){ + var a = new Date(unixTs*1000); + var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + var year = a.getFullYear(); + var month = months[a.getMonth()]; + var date = a.getDate(); + var hour = a.getHours(); + var min = a.getMinutes(); + var sec = a.getSeconds(); + var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; + return time; + } + // ******************************************* + // Components + // ******************************************* + + // New Contract component + Component { + id: newContract + Column { + id: mainContractColumn + anchors.fill: parent + 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: atLabel; visible:false} + PropertyChanges { target: txFuelRecipient; 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 + + TextField { + id: txFuelRecipient + placeholderText: "Address / Name or empty for contract" + //validator: RegExpValidator { regExp: /[a-f0-9]{40}/ } + width: 400 + } + + TextField { + id: txValue + width: 222 + placeholderText: "Amount" + validator: RegExpValidator { regExp: /\d*/ } + onTextChanged: { + contractFormReady() + } + } + + RowLayout { + TextField { + id: txGas + width: 50 + validator: RegExpValidator { regExp: /\d*/ } + placeholderText: "Gas" + text: "500" + /* + onTextChanged: { + contractFormReady() + } + */ + } + Label { + id: atLabel + text: "@" + } + + TextField { + id: txGasPrice + width: 200 + placeholderText: "Gas price" + text: "1000000" + validator: RegExpValidator { regExp: /\d*/ } + /* + onTextChanged: { + contractFormReady() + } + */ + } + } + + Label { + id: txDataLabel + text: "Data" + } + + TextArea { + id: codeView + height: 300 + anchors.topMargin: 5 + width: 400 + onTextChanged: { + contractFormReady() + } + } + + + Button { + id: txButton + /* enabled: false */ + states: [ + State { + name: "READY" + PropertyChanges { target: txButton; /*enabled: true*/} + }, + State { + name: "NOTREADY" + PropertyChanges { target: txButton; /*enabled:false*/} + } + ] + text: "Send" + onClicked: { + //this.enabled = false + var res = eth.create(txFuelRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text) + if(res[1]) { + txResult.text = "Your contract could not be send over the network:\n" + txResult.text += res[1].error() + txResult.text += "" + mainContractColumn.state = "ERROR" + } else { + txResult.text = "Your transaction has been submitted:\n" + txOutput.text = res[0].address + mainContractColumn.state = "DONE" + } + } + } + Text { + id: txResult + visible: false + } + TextField { + id: txOutput + visible: false + width: 530 + } + Button { + id: newTxButton + visible: false + text: "Create a new transaction" + onClicked: { + this.visible = false + txResult.text = "" + txOutput.text = "" + mainContractColumn.state = "SETUP" + } + } + } + } + + // New Transaction component + Component { + id: newTransaction + Column { + id: simpleSendColumn + states: [ + State{ + name: "ERROR" + }, + State { + name: "DONE" + PropertyChanges { target: txSimpleValue; visible:false} + PropertyChanges { target: txSimpleRecipient; visible:false} + PropertyChanges { target:newSimpleTxButton; visible:false} + + PropertyChanges { target: txSimpleResult; visible:true} + PropertyChanges { target: txSimpleOutput; visible:true} + PropertyChanges { target:newSimpleTxButton; visible:true} + }, + State { + name: "SETUP" + PropertyChanges { target: txSimpleValue; visible:true; text: ""} + PropertyChanges { target: txSimpleRecipient; visible:true; text: ""} + PropertyChanges { target: txSimpleButton; visible:true} + PropertyChanges { target:newSimpleTxButton; visible:false} + } + ] + spacing: 5 + anchors.leftMargin: 5 + anchors.topMargin: 5 + anchors.top: parent.top + anchors.left: parent.left + + function checkFormState(){ + if(txSimpleRecipient.text.length == 40 && txSimpleValue.text.length > 0) { + txSimpleButton.state = "READY" + }else{ + txSimpleButton.state = "NOTREADY" + } + } + + TextField { + id: txSimpleRecipient + placeholderText: "Recipient address" + Layout.fillWidth: true + //validator: RegExpValidator { regExp: /[a-f0-9]{40}/ } + width: 530 + onTextChanged: { checkFormState() } + } + TextField { + id: txSimpleValue + width: 200 + placeholderText: "Amount" + anchors.rightMargin: 5 + validator: RegExpValidator { regExp: /\d*/ } + onTextChanged: { checkFormState() } + } + Button { + id: txSimpleButton + /*enabled: false*/ + states: [ + State { + name: "READY" + PropertyChanges { target: txSimpleButton; /*enabled: true*/} + }, + State { + name: "NOTREADY" + PropertyChanges { target: txSimpleButton; /*enabled: false*/} + } + ] + text: "Send" + onClicked: { + //this.enabled = false + var res = eth.transact(txSimpleRecipient.text, txSimpleValue.text, "500", "1000000", "") + if(res[1]) { + txSimpleResult.text = "There has been an error broadcasting your transaction:" + res[1].error() + } else { + txSimpleResult.text = "Your transaction has been broadcasted over the network.\nYour transaction id is:" + txSimpleOutput.text = res[0].hash + this.visible = false + simpleSendColumn.state = "DONE" + } + } + } + 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 + simpleSendColumn.state = "SETUP" + } + } + } + } } diff --git a/ethereal/config.go b/ethereal/config.go index 3864126db..6a42663e7 100644 --- a/ethereal/config.go +++ b/ethereal/config.go @@ -5,8 +5,8 @@ import ( ) var Identifier string -var StartConsole bool -var StartMining bool + +//var StartMining bool var StartRpc bool var RpcPort int var UseUPnP bool @@ -18,25 +18,22 @@ var GenAddr bool var UseSeed bool var ImportKey string var ExportKey bool -var DataDir string var AssetPath string func Init() { - flag.StringVar(&Identifier, "i", "", "Custom client identifier") - flag.BoolVar(&StartConsole, "c", false, "debug and testing console") - flag.BoolVar(&StartMining, "m", false, "start dagger mining") - flag.BoolVar(&StartRpc, "r", false, "start rpc server") - flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") + flag.StringVar(&Identifier, "id", "", "Custom client identifier") + flag.StringVar(&OutboundPort, "port", "30303", "listening port") flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") + flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers") + flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") + flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") + flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory") + + flag.BoolVar(&ShowGenesis, "genesis", false, "prints genesis header and exits") 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.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") - 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", 10, "maximum desired peers") - flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory") flag.Parse() } diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go index 30187d956..a7e43cd9a 100644 --- a/ethereal/ethereum.go +++ b/ethereal/ethereum.go @@ -8,9 +8,11 @@ import ( "github.com/ethereum/go-ethereum/ethereal/ui" "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" + "github.com/rakyll/globalconf" "log" "os" "os/signal" + "path" "runtime" ) @@ -39,7 +41,16 @@ func main() { runtime.GOMAXPROCS(runtime.NumCPU()) ethchain.InitFees() - ethutil.ReadConfig(DataDir, ethutil.LogFile|ethutil.LogStd, Identifier) + + g, err := globalconf.NewWithOptions(&globalconf.Options{ + Filename: path.Join(ethutil.ApplicationFolder(".ethereal"), "conf.ini"), + }) + if err != nil { + fmt.Println(err) + } else { + g.ParseAll() + } + ethutil.ReadConfig(".ethereal", ethutil.LogFile|ethutil.LogStd, Identifier) // Instantiated a eth stack ethereum, err := eth.New(eth.CapDefault, UseUPnP) @@ -108,9 +119,11 @@ save these words so you can restore your account later: %s os.Exit(0) } - if StartMining { - utils.DoMining(ethereum) - } + /* + if StartMining { + utils.DoMining(ethereum) + } + */ if StartRpc { utils.DoRpc(ethereum, RpcPort) diff --git a/ethereal/ui/debugger.go b/ethereal/ui/debugger.go index 9bca7e4fe..a6b8e16d0 100644 --- a/ethereal/ui/debugger.go +++ b/ethereal/ui/debugger.go @@ -71,6 +71,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data var err error script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { ret, err = ethutil.Compile(s) + fmt.Printf("%x\n", ret) return }) @@ -82,6 +83,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data dis := ethchain.Disassemble(script) self.win.Root().Call("clearAsm") + self.win.Root().Call("clearLog") for _, str := range dis { self.win.Root().Call("setAsm", str) diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index 1698f5de0..d6430d1fe 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" "math/big" "strings" @@ -66,7 +67,6 @@ func (gui *Gui) Start(assetPath string) { }}) ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", version)) - ethutil.Config.Log.Infoln("[GUI] Starting GUI") // Create a new QML engine gui.engine = qml.NewEngine() context := gui.engine.Context() @@ -93,12 +93,28 @@ func (gui *Gui) Start(assetPath string) { panic(err) } + ethutil.Config.Log.AddLogSystem(gui) + ethutil.Config.Log.Infoln("[GUI] Starting GUI") + win.Show() win.Wait() gui.eth.Stop() } +func (gui *Gui) ToggleMining() { + var txt string + if gui.eth.Mining { + utils.StopMining(gui.eth) + txt = "Start mining" + } else { + utils.StartMining(gui.eth) + txt = "Stop mining" + } + + gui.win.Root().Set("miningButtonText", txt) +} + func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml")) if err != nil { @@ -107,8 +123,11 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { win := gui.createWindow(component) - go gui.setInitialBlockChain() - go gui.readPreviousTransactions() + gui.setInitialBlockChain() + gui.loadAddressBook() + gui.readPreviousTransactions() + gui.setPeerInfo() + go gui.update() return win, nil @@ -145,6 +164,19 @@ func (gui *Gui) setInitialBlockChain() { } } +type address struct { + Name, Address string +} + +var namereg = ethutil.FromHex("bb5f186604d057c1c5240ca2ae0f6430138ac010") + +func (gui *Gui) loadAddressBook() { + gui.win.Root().Call("clearAddress") + gui.eth.StateManager().CurrentState().GetStateObject(namereg).State().EachStorage(func(name string, value *ethutil.Value) { + gui.win.Root().Call("addAddress", struct{ Name, Address string }{name, ethutil.Hex(value.Bytes())}) + }) +} + func (gui *Gui) readPreviousTransactions() { it := gui.txDb.Db().NewIterator(nil, nil) for it.Next() { @@ -189,10 +221,14 @@ func (gui *Gui) update() { blockChan := make(chan ethutil.React, 1) txChan := make(chan ethutil.React, 1) + objectChan := make(chan ethutil.React, 1) + peerChan := make(chan ethutil.React, 1) reactor.Subscribe("newBlock", blockChan) reactor.Subscribe("newTx:pre", txChan) reactor.Subscribe("newTx:post", txChan) + reactor.Subscribe("object:"+string(namereg), objectChan) + reactor.Subscribe("peerList", peerChan) state := gui.eth.StateManager().TransState() @@ -239,10 +275,18 @@ func (gui *Gui) update() { state.UpdateStateObject(object) } + case <-objectChan: + gui.loadAddressBook() + case <-peerChan: + gui.setPeerInfo() } } } +func (gui *Gui) setPeerInfo() { + gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers)) +} + // Logging functions that log directly to the GUI interface func (gui *Gui) Println(v ...interface{}) { str := strings.TrimRight(fmt.Sprintln(v...), "\n") diff --git a/ethereum/config.go b/ethereum/config.go index 5da910f2b..39dc11727 100644 --- a/ethereum/config.go +++ b/ethereum/config.go @@ -20,7 +20,6 @@ var UseSeed bool var ImportKey string var ExportKey bool var LogFile string -var DataDir string var NonInteractive bool var StartJsConsole bool var InputFile string @@ -31,22 +30,21 @@ func Init() { flag.PrintDefaults() } - flag.StringVar(&Identifier, "i", "", "custom client identifier") - flag.BoolVar(&StartMining, "m", false, "start dagger mining") - flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") - flag.BoolVar(&StartRpc, "r", false, "start rpc server") - flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") - flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") + flag.StringVar(&Identifier, "id", "", "Custom client identifier") + flag.StringVar(&OutboundPort, "port", "30303", "listening port") flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") + flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers") + flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") + flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") + flag.BoolVar(&StartJsConsole, "js", false, "exp") + + flag.BoolVar(&StartMining, "mine", false, "start dagger mining") + flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") 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(&LogFile, "logfile", "", "log file (defaults to standard output)") - flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") - flag.IntVar(&MaxPeer, "x", 10, "maximum desired peers") - flag.BoolVar(&StartJsConsole, "js", false, "exp") flag.Parse() diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 34bacb7b9..7bb668235 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -59,7 +59,7 @@ func main() { lt = ethutil.LogFile | ethutil.LogStd } - ethutil.ReadConfig(DataDir, lt, Identifier) + ethutil.ReadConfig(".ethereum", lt, Identifier) logger := ethutil.Config.Log diff --git a/utils/cmd.go b/utils/cmd.go index f8b7b5fe2..e1fc0fc00 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -33,8 +33,6 @@ func DoMining(ethereum *eth.Ethereum) { addr := keyPair.Address() go func() { - ethutil.Config.Log.Infoln("Miner started") - miner = ethminer.NewDefaultMiner(addr, ethereum) // Give it some time to connect with peers