diff --git a/README.md b/README.md index e5818ffca..104e4725c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Ethereum Ethereum Go Client © 2014 Jeffrey Wilcke. -Current state: Proof of Concept 5.0 RC8. +Current state: Proof of Concept 5.0 RC12. For the development package please see the [eth-go package](https://github.com/ethereum/eth-go). @@ -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 new file mode 100644 index 000000000..bc34233fd --- /dev/null +++ b/ethereal/assets/debugger/debugger.qml @@ -0,0 +1,264 @@ +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 { + visible: false + title: "IceCREAM" + minimumWidth: 1280 + minimumHeight: 900 + width: 1290 + height: 900 + + property alias codeText: codeEditor.text + property alias dataText: rawDataField.text + + MenuBar { + Menu { + title: "Debugger" + MenuItem { + text: "Run" + shortcut: "Ctrl+r" + onTriggered: debugCurrent() + } + + MenuItem { + text: "Next" + shortcut: "Ctrl+n" + onTriggered: dbg.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 { + color: "#00000000" + anchors.left: asmTableView.right + anchors.right: parent.right + SplitView { + orientation: Qt.Vertical + anchors.fill: parent + + Rectangle { + color: "#00000000" + height: 500 + anchors.left: parent.left + anchors.right: parent.right + + TextArea { + id: codeEditor + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: settings.left + } + + Column { + id: settings + spacing: 5 + width: 300 + height: parent.height + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + + Label { + text: "Arbitrary data" + } + TextArea { + id: rawDataField + anchors.left: parent.left + anchors.right: parent.right + height: 150 + } + + Label { + text: "Amount" + } + TextField { + id: txValue + width: 200 + placeholderText: "Amount" + validator: RegExpValidator { regExp: /\d*/ } + } + Label { + text: "Amount of gas" + } + TextField { + id: txGas + width: 200 + validator: RegExpValidator { regExp: /\d*/ } + text: "10000" + placeholderText: "Gas" + } + Label { + text: "Gas price" + } + TextField { + id: txGasPrice + width: 200 + placeholderText: "Gas price" + text: "1000000000000" + validator: RegExpValidator { regExp: /\d*/ } + } + } + } + + SplitView { + orientation: Qt.Vertical + id: inspectorPane + height: 500 + + SplitView { + orientation: Qt.Horizontal + height: 150 + + TableView { + id: stackTableView + property var stackModel: ListModel { + id: stackModel + } + height: parent.height + width: 300 + TableViewColumn{ role: "value" ; title: "Stack" ; width: 200 } + model: stackModel + } + + TableView { + id: memoryTableView + property var memModel: ListModel { + id: memModel + } + height: parent.height + width: parent.width - stackTableView.width + TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50} + TableViewColumn{ role: "value" ; title: "Memory" ; width: 750} + model: memModel + } + } + + Rectangle { + height: 100 + width: parent.width + TableView { + id: storageTableView + property var memModel: ListModel { + id: storageModel + } + height: parent.height + width: parent.width + TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2} + TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2} + model: storageModel + } + } + + Rectangle { + height: 200 + width: parent.width + TableView { + id: logTableView + property var logModel: ListModel { + id: logModel + } + height: parent.height + width: parent.width + TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width } + model: logModel + } + } + } + } + } + } + + toolBar: ToolBar { + RowLayout { + spacing: 5 + + Button { + property var enabled: true + id: debugStart + onClicked: { + debugCurrent() + } + text: "Debug" + } + + Button { + property var enabled: true + id: debugNextButton + onClicked: { + dbg.next() + } + text: "Next" + } + } + } + + function debugCurrent() { + dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text) + } + + function setAsm(asm) { + asmModel.append({asm: asm}) + } + + function clearAsm() { + asmModel.clear() + } + + function setInstruction(num) { + //asmTableView.selection.clear() + //asmTableView.selection.select(num) + } + + 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){ + debuggerLog.append({value: message}) + } + + function clearStack() { + stackModel.clear() + } + + function clearStorage() { + storageModel.clear() + } + + function setStorage(storage) { + storageModel.append({key: storage.key, value: storage.value}) + } + + function setLog(msg) { + logModel.insert(0, {message: msg}) + } + + function clearLog() { + logModel.clear() + } +} diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index d4eaf97fd..c58fe24c2 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -32,6 +32,10 @@ window.eth = { postData({call: "getStorage", args: [address, storageAddress]}, cb); }, + getStateKeyVals: function(address, cb){ + postData({call: "getStateKeyVals", args: [address]}, cb); + }, + getKey: function(cb) { postData({call: "getKey"}, cb); }, diff --git a/ethereal/assets/heart.png b/ethereal/assets/heart.png new file mode 100644 index 000000000..3c874ab7f Binary files /dev/null and b/ethereal/assets/heart.png differ diff --git a/ethereal/assets/qml/newTransaction/_new_contract.qml b/ethereal/assets/qml/newTransaction/_new_contract.qml deleted file mode 100644 index e3c7229eb..000000000 --- a/ethereal/assets/qml/newTransaction/_new_contract.qml +++ /dev/null @@ -1,196 +0,0 @@ -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 - -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 - } - } - } -} diff --git a/ethereal/assets/qml/newTransaction/_simple_send.qml b/ethereal/assets/qml/newTransaction/_simple_send.qml deleted file mode 100644 index cd1ef55b6..000000000 --- a/ethereal/assets/qml/newTransaction/_simple_send.qml +++ /dev/null @@ -1,112 +0,0 @@ -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 - -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" - } - } - } -} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 51f064adf..0555cd2f2 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -6,9 +6,12 @@ import QtQuick.Window 2.1; import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 + ApplicationWindow { id: root + property alias miningButtonText: miningButton.text + width: 900 height: 600 minimumHeight: 300 @@ -23,10 +26,13 @@ ApplicationWindow { shortcut: "Ctrl+o" onTriggered: openAppDialog.open() } + } + + Menu { MenuItem { - text: "Muted" - shortcut: "Ctrl+e" - onTriggered: ui.muted("") + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: ui.startDebugger() } } @@ -39,10 +45,12 @@ ApplicationWindow { addPeerWin.visible = true } } - MenuItem { - text: "Start" - onTriggered: ui.connect() + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } } } @@ -85,7 +93,6 @@ ApplicationWindow { //color: "#D9DDE7" color: "#252525" - ColumnLayout { y: 50 anchors.left: parent.left @@ -123,7 +130,7 @@ ApplicationWindow { } Image { - source: ui.assetPath("net.png") + source: ui.assetPath("heart.png") anchors.horizontalCenter: parent.horizontalCenter MouseArea { anchors.fill: parent @@ -155,6 +162,7 @@ ApplicationWindow { 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 } @@ -169,6 +177,7 @@ ApplicationWindow { visible: false anchors.fill: parent color: "#00000000" + /* TabView{ anchors.fill: parent anchors.rightMargin: 5 @@ -177,16 +186,14 @@ ApplicationWindow { anchors.bottomMargin: 5 id: newTransactionTab Component.onCompleted:{ - var component = Qt.createComponent("newTransaction/_simple_send.qml") - var newTransaction = component.createObject("newTransaction") - - component = Qt.createComponent("newTransaction/_new_contract.qml") - var newContract = component.createObject("newContract") - addTab("Simple send", newTransaction) addTab("Contracts", newContract) } } + */ + Component.onCompleted: { + newContract.createObject(newTxView) + } } Rectangle { @@ -199,34 +206,19 @@ ApplicationWindow { id: blockTable width: parent.width anchors.top: parent.top - anchors.bottom: logView.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.block = eth.getBlock(blockModel.get(row).hash) - popup.hashLabel.text = popup.block.hash - } - */ + 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 { @@ -236,23 +228,82 @@ ApplicationWindow { color: "#00000000" anchors.fill: parent - Label { - id: addressLabel - text: "Address" - anchors { - margins: 5 - top: parent.top - left: parent.left + Column { + spacing: 3 + anchors.fill: parent + anchors.topMargin: 5 + anchors.leftMargin: 5 + + Label { + id: addressLabel + text: "Address" + } + TextField { + text: pub.getKey().address + width: 500 + } + + Label { + text: "Client ID" + } + TextField { + text: eth.clientId() + onTextChanged: { + eth.changeClientId(text) + } } } - TextField { - anchors { - margins: 5 - left: addressLabel.right - top: parent.top + + property var addressModel: ListModel { + id: addressModel + } + TableView { + id: addressView + width: parent.width - 200 + height: 200 + anchors.bottom: logView.top + TableViewColumn{ role: "name"; title: "name" } + TableViewColumn{ role: "address"; title: "address"; width: 300} + + model: addressModel + } + + Rectangle { + anchors.top: addressView.top + anchors.left: addressView.right + anchors.leftMargin: 20 + + TextField { + placeholderText: "Name to register" + id: nameToReg + width: 150 } - text: pub.getKey().address - width: 500 + + Button { + anchors.top: nameToReg.bottom + text: "Register" + MouseArea{ + anchors.fill: parent + onClicked: { + eth.registerName(nameToReg.text) + nameToReg.text = "" + } + } + } + } + + + 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 } } @@ -285,29 +336,34 @@ ApplicationWindow { title: "Open QML Application" onAccepted: { //ui.open(openAppDialog.fileUrl.toString()) - //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html"))) - ui.openHtml(openAppDialog.fileUrl.toString()) - + //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html"))) + ui.openHtml(openAppDialog.fileUrl.toString()) } } statusBar: StatusBar { + height: 30 RowLayout { - anchors.fill: parent + Button { + id: miningButton + onClicked: { + eth.toggleMining() + } + text: "Start Mining" + } + Button { property var enabled: true - id: connectButton + id: debuggerWindow onClicked: { - if(this.enabled) { - ui.connect(this) - } + ui.startDebugger() } - text: "Connect" + text: "Debugger" } Button { id: importAppButton - anchors.left: connectButton.right + anchors.left: debuggerWindow.right anchors.leftMargin: 5 onClicked: openAppDialog.open() text: "Import App" @@ -318,20 +374,26 @@ ApplicationWindow { 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") + 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 + MouseArea { + onDoubleClicked: peerWindow.visible = true + anchors.fill: parent } + source: ui.assetPath("network.png") } } @@ -339,10 +401,134 @@ ApplicationWindow { id: popup visible: false property var block - Label { - id: hashLabel - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter + 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"} + Text { text: 'Gas used: ' + gasUsed + " / " + gasLimit; 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() } } @@ -404,115 +590,19 @@ ApplicationWindow { anchors.left: aboutIcon.right anchors.leftMargin: 10 font.pointSize: 12 - text: "

Ethereal


Development

Jeffrey Wilcke
Maran Hidskes

Binary Distribution

Jarrad Hope
" - } - - } - - Window { - 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 - 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: parent.width } - model: stackModel - } - } - } - } + text: "

Ethereal


Development

Jeffrey Wilcke
Maran Hidskes
" } } - 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 addAddress(address) { + addressModel.append({name: address.name, address: address.address}) + } + function clearAddress() { + addressModel.clear() } function loadPlugin(name) { @@ -524,27 +614,379 @@ ApplicationWindow { walletValueLabel.text = value } - function addTx(tx) { + function addTx(tx, inout) { var isContract if (tx.contract == true){ isContract = "Yes" }else{ isContract = "No" } - txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value, contract: isContract}) + + 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) { - blockModel.insert(0, {number: block.number, hash: block.hash}) + 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, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) + }else{ + blockModel.insert(0, {number: block.number, gasLimit: block.gasLimit, gasUsed: block.gasUsed, 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.append({description: str}) + logModel.insert(0, {description: str}) } } function setPeers(text) { peerLabel.text = text } + + function addPeer(peer) { + // We could just append the whole peer object but it cries if you try to alter them + peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version}) + } + + function resetPeers(){ + peerModel.clear() + } + + function timeAgo(unixTs){ + var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 + return (lapsed + " seconds ago") + } + 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; + } + // ****************************************** + // Windows + // ****************************************** + Window { + id: peerWindow + height: 200 + width: 700 + Rectangle { + anchors.fill: parent + property var peerModel: ListModel { + id: peerModel + } + TableView { + anchors.fill: parent + id: peerTable + model: peerModel + TableViewColumn{width: 100; role: "ip" ; title: "IP" } + TableViewColumn{width: 60; role: "port" ; title: "Port" } + TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" } + TableViewColumn{width: 100; role: "latency"; title: "Latency" } + TableViewColumn{width: 260; role: "version" ; title: "Version" } + } + } + } + + // ******************************************* + // 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/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index d3cffeeca..4a5a1293a 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -34,7 +34,6 @@ ApplicationWindow { top: parent.top } */ - onTitleChanged: { window.title = title } experimental.preferences.javascriptEnabled: true experimental.preferences.navigatorQtObjectEnabled: true @@ -97,6 +96,12 @@ ApplicationWindow { var storage = stateObject.getStorage(data.args[1]) postData(data._seed, storage) + break + case "getStateKeyVals": + require(1); + var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) + postData(data._seed,stateObject) + break case "getBalance": require(1); @@ -165,6 +170,30 @@ ApplicationWindow { postEvent(ev, [storageObject.address, storageObject.value]) } } + Rectangle { + id: toggleInspector + color: "#bcbcbc" + visible: true + height: 12 + width: 12 + anchors { + right: root.right + } + MouseArea { + onClicked: { + if(inspector.visible == true){ + inspector.visible = false + }else{ + inspector.visible = true + } + } + onDoubleClicked: { + console.log('refreshing') + webView.reload() + } + anchors.fill: parent + } + } Rectangle { id: sizeGrip diff --git a/ethereal/assets/samplecoin/samplecoin.html b/ethereal/assets/samplecoin/samplecoin.html index 3892141cd..dc8d0b7e9 100644 --- a/ethereal/assets/samplecoin/samplecoin.html +++ b/ethereal/assets/samplecoin/samplecoin.html @@ -9,7 +9,7 @@