From 94b12f7804bfb32763c9bbbce323af5d84c74910 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 15 Jul 2014 00:05:18 +0100 Subject: [PATCH 01/25] fix start mining --- utils/cmd.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/utils/cmd.go b/utils/cmd.go index 889726b049..dfd867d64f 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -124,6 +124,7 @@ func NewDatabase() ethutil.Database { } func NewClientIdentity(clientIdentifier, version, customIdentifier string) *ethwire.SimpleClientIdentity { + logger.Infoln("identity created") return ethwire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier) } @@ -209,10 +210,10 @@ var miner ethminer.Miner func StartMining(ethereum *eth.Ethereum) bool { if !ethereum.Mining { ethereum.Mining = true - addr := ethereum.KeyManager().Address() go func() { + logger.Infoln("Start mining") miner = ethminer.NewDefaultMiner(addr, ethereum) // Give it some time to connect with peers time.Sleep(3 * time.Second) @@ -220,8 +221,6 @@ func StartMining(ethereum *eth.Ethereum) bool { time.Sleep(5 * time.Second) } - logger.Infoln("Miner started") - miner := ethminer.NewDefaultMiner(addr, ethereum) miner.Start() }() RegisterInterrupt(func(os.Signal) { @@ -235,7 +234,7 @@ func StartMining(ethereum *eth.Ethereum) bool { func StopMining(ethereum *eth.Ethereum) bool { if ethereum.Mining { miner.Stop() - logger.Infoln("Miner stopped") + logger.Infoln("Stopped mining") ethereum.Mining = false return true } From 75a7a4c97c350e911f4d721e940a59c0740ae967 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 15 Jul 2014 01:13:39 +0100 Subject: [PATCH 02/25] ethreact - use ethreact.Event, - increased buffered event channels, - subscribe after loop reading from channel starts --- ethereal/ext_app.go | 9 +-- ethereal/gui.go | 123 +++++++++++++++++---------------- ethereum/javascript_runtime.go | 17 ++--- 3 files changed, 76 insertions(+), 73 deletions(-) diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 17c342a1b9..736b059e50 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/go-qml/qml" ) @@ -24,8 +25,8 @@ type AppContainer interface { type ExtApplication struct { *ethpub.PEthereum - blockChan chan ethutil.React - changeChan chan ethutil.React + blockChan chan ethreact.Event + changeChan chan ethreact.Event quitChan chan bool watcherQuitChan chan bool @@ -37,8 +38,8 @@ type ExtApplication struct { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ ethpub.NewPEthereum(lib.eth), - make(chan ethutil.React, 1), - make(chan ethutil.React, 1), + make(chan ethreact.Event, 10), + make(chan ethreact.Event, 10), make(chan bool), make(chan bool), container, diff --git a/ethereal/gui.go b/ethereal/gui.go index 9f28045f86..eb0c50cc36 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "github.com/ethereum/go-ethereum/utils" @@ -143,7 +144,7 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { gui.readPreviousTransactions() gui.setPeerInfo() - go gui.update() + gui.update() return win, nil } @@ -266,11 +267,67 @@ func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) { func (gui *Gui) update() { reactor := gui.eth.Reactor() - blockChan := make(chan ethutil.React, 1) - txChan := make(chan ethutil.React, 1) - objectChan := make(chan ethutil.React, 1) - peerChan := make(chan ethutil.React, 1) + blockChan := make(chan ethreact.Event, 1) + txChan := make(chan ethreact.Event, 1) + objectChan := make(chan ethreact.Event, 1) + peerChan := make(chan ethreact.Event, 1) + ticker := time.NewTicker(5 * time.Second) + state := gui.eth.StateManager().TransState() + + unconfirmedFunds := new(big.Int) + gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Amount))) + + go func() { + for { + select { + case b := <-blockChan: + block := b.Resource.(*ethchain.Block) + gui.processBlock(block, false) + if bytes.Compare(block.Coinbase, gui.address()) == 0 { + gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Amount, nil) + } + + case txMsg := <-txChan: + tx := txMsg.Resource.(*ethchain.Transaction) + + if txMsg.Name == "newTx:pre" { + object := state.GetAccount(gui.address()) + + if bytes.Compare(tx.Sender(), gui.address()) == 0 { + gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send") + gui.txDb.Put(tx.Hash(), tx.RlpEncode()) + + unconfirmedFunds.Sub(unconfirmedFunds, tx.Value) + } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { + gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv") + gui.txDb.Put(tx.Hash(), tx.RlpEncode()) + + unconfirmedFunds.Add(unconfirmedFunds, tx.Value) + } + + gui.setWalletValue(object.Amount, unconfirmedFunds) + } else { + object := state.GetAccount(gui.address()) + if bytes.Compare(tx.Sender(), gui.address()) == 0 { + object.SubAmount(tx.Value) + } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { + object.AddAmount(tx.Value) + } + + gui.setWalletValue(object.Amount, nil) + + state.UpdateStateObject(object) + } + case <-objectChan: + gui.loadAddressBook() + case <-peerChan: + gui.setPeerInfo() + case <-ticker.C: + gui.setPeerInfo() + } + } + }() reactor.Subscribe("newBlock", blockChan) reactor.Subscribe("newTx:pre", txChan) reactor.Subscribe("newTx:post", txChan) @@ -280,62 +337,6 @@ func (gui *Gui) update() { reactor.Subscribe("object:"+string(nameReg.Address()), objectChan) } reactor.Subscribe("peerList", peerChan) - - ticker := time.NewTicker(5 * time.Second) - - state := gui.eth.StateManager().TransState() - - unconfirmedFunds := new(big.Int) - gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Amount))) - - for { - select { - case b := <-blockChan: - block := b.Resource.(*ethchain.Block) - gui.processBlock(block, false) - if bytes.Compare(block.Coinbase, gui.address()) == 0 { - gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Amount, nil) - } - - case txMsg := <-txChan: - tx := txMsg.Resource.(*ethchain.Transaction) - - if txMsg.Event == "newTx:pre" { - object := state.GetAccount(gui.address()) - - if bytes.Compare(tx.Sender(), gui.address()) == 0 { - gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send") - gui.txDb.Put(tx.Hash(), tx.RlpEncode()) - - unconfirmedFunds.Sub(unconfirmedFunds, tx.Value) - } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { - gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv") - gui.txDb.Put(tx.Hash(), tx.RlpEncode()) - - unconfirmedFunds.Add(unconfirmedFunds, tx.Value) - } - - gui.setWalletValue(object.Amount, unconfirmedFunds) - } else { - object := state.GetAccount(gui.address()) - if bytes.Compare(tx.Sender(), gui.address()) == 0 { - object.SubAmount(tx.Value) - } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { - object.AddAmount(tx.Value) - } - - gui.setWalletValue(object.Amount, nil) - - state.UpdateStateObject(object) - } - case <-objectChan: - gui.loadAddressBook() - case <-peerChan: - gui.setPeerInfo() - case <-ticker.C: - gui.setPeerInfo() - } - } } func (gui *Gui) setPeerInfo() { diff --git a/ethereum/javascript_runtime.go b/ethereum/javascript_runtime.go index 852a504879..998ff7520e 100644 --- a/ethereum/javascript_runtime.go +++ b/ethereum/javascript_runtime.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/utils" "github.com/obscuren/otto" @@ -22,8 +23,8 @@ type JSRE struct { vm *otto.Otto lib *ethpub.PEthereum - blockChan chan ethutil.React - changeChan chan ethutil.React + blockChan chan ethreact.Event + changeChan chan ethreact.Event quitChan chan bool objectCb map[string][]otto.Value @@ -48,8 +49,8 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { ethereum, otto.New(), ethpub.NewPEthereum(ethereum), - make(chan ethutil.React, 1), - make(chan ethutil.React, 1), + make(chan ethreact.Event, 10), + make(chan ethreact.Event, 10), make(chan bool), make(map[string][]otto.Value), } @@ -64,6 +65,10 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { // We have to make sure that, whoever calls this, calls "Stop" go re.mainLoop() + // Subscribe to events + reactor := ethereum.Reactor() + reactor.Subscribe("newBlock", self.blockChan) + re.Bind("eth", &JSEthereum{re.lib, re.vm}) re.initStdFuncs() @@ -108,10 +113,6 @@ func (self *JSRE) Stop() { } func (self *JSRE) mainLoop() { - // Subscribe to events - reactor := self.ethereum.Reactor() - reactor.Subscribe("newBlock", self.blockChan) - out: for { select { From 74abc457ada9ef17c39c488a9e7625cecd4e6141 Mon Sep 17 00:00:00 2001 From: zelig Date: Mon, 21 Jul 2014 19:26:01 +0100 Subject: [PATCH 03/25] reactor event channels have large buffer to allow more tolerance --- ethereal/ext_app.go | 4 ++-- ethereal/gui.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 736b059e50..ee723fc3db 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -38,8 +38,8 @@ type ExtApplication struct { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ ethpub.NewPEthereum(lib.eth), - make(chan ethreact.Event, 10), - make(chan ethreact.Event, 10), + make(chan ethreact.Event, 100), + make(chan ethreact.Event, 100), make(chan bool), make(chan bool), container, diff --git a/ethereal/gui.go b/ethereal/gui.go index bfae97050d..9bc11e81ec 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -281,12 +281,12 @@ func (self *Gui) getObjectByName(objectName string) qml.Object { func (gui *Gui) update() { var ( - blockChan = make(chan ethreact.Event, 1) - txChan = make(chan ethreact.Event, 1) - objectChan = make(chan ethreact.Event, 1) - peerChan = make(chan ethreact.Event, 1) - chainSyncChan = make(chan ethreact.Event, 1) - miningChan = make(chan ethreact.Event, 1) + blockChan = make(chan ethreact.Event, 100) + txChan = make(chan ethreact.Event, 100) + objectChan = make(chan ethreact.Event, 100) + peerChan = make(chan ethreact.Event, 100) + chainSyncChan = make(chan ethreact.Event, 100) + miningChan = make(chan ethreact.Event, 100) ) peerUpdateTicker := time.NewTicker(5 * time.Second) From 2f5c95610feb77dd714bf9295d6127bef58f31bc Mon Sep 17 00:00:00 2001 From: zelig Date: Mon, 21 Jul 2014 19:55:47 +0100 Subject: [PATCH 04/25] use logger instead of fmt for error in ext_app --- ethereal/ext_app.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index ee723fc3db..ac745ae377 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethreact" @@ -58,8 +57,7 @@ func (app *ExtApplication) run() { err := app.container.Create() if err != nil { - fmt.Println(err) - + logger.Errorln(err) return } From 97004f7eb22ab30ba1acc5dd3ee2f17b5466d41a Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 25 Jul 2014 10:41:57 +0200 Subject: [PATCH 05/25] wip export --- ethereal/assets/qml/wallet.qml | 34 ++++++++++++++++++++++++++++++++-- ethereal/gui.go | 3 +++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index eef49824f1..aadc90e3b4 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -26,6 +26,22 @@ ApplicationWindow { shortcut: "Ctrl+o" onTriggered: openAppDialog.open() } + + MenuSeparator {} + + MenuItem { + text: "Import key" + shortcut: "Ctrl+i" + onTriggered: importDialog.open() + } + + MenuItem { + text: "Export keys" + shortcut: "Ctrl+e" + onTriggered: exportDialog.open() + } + + //MenuSeparator {} } Menu { @@ -375,9 +391,7 @@ ApplicationWindow { //ui.open(openAppDialog.fileUrl.toString()) //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html"))) var path = openAppDialog.fileUrl.toString() - console.log(path) var ext = path.split('.').pop() - console.log(ext) if(ext == "html" || ext == "htm") { ui.openHtml(path) }else if(ext == "qml"){ @@ -386,6 +400,22 @@ ApplicationWindow { } } + FileDialog { + id: exportDialog + title: "Export keys" + onAccepted: { + } + } + + FileDialog { + id: importDialog + title: "Import key" + onAccepted: { + var path = this.fileUrl.toString() + ui.importKey(path) + } + } + statusBar: StatusBar { height: 30 RowLayout { diff --git a/ethereal/gui.go b/ethereal/gui.go index df01cddda0..832200176f 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -155,6 +155,9 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { return win, nil } +func (gui *Gui) ImportKey(filePath string) { +} + func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) { context.SetVar("lib", gui) component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml")) From 5c9fd19105c572ea717a8bc04758dcf3e71af47e Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 00:17:23 +0200 Subject: [PATCH 06/25] A few start up optimisations --- ethereal/assets/qml/wallet.qml | 2 +- ethereal/gui.go | 4 ++-- ethereal/html_container.go | 13 +++++++------ ethereal/qml_container.go | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index aadc90e3b4..50ff73060c 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -690,7 +690,7 @@ ApplicationWindow { anchors.left: aboutIcon.right anchors.leftMargin: 10 font.pointSize: 12 - text: "

Ethereal


Development

Jeffrey Wilcke
Maran Hidskes
Viktor Trón
" + text: "

Ethereal - Adrastea


Development

Jeffrey Wilcke
Maran Hidskes
Viktor Trón
" } } diff --git a/ethereal/gui.go b/ethereal/gui.go index 832200176f..573f68959f 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -144,10 +144,10 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { win := gui.createWindow(component) go func() { - gui.setInitialBlockChain() + go gui.setInitialBlockChain() gui.loadAddressBook() - gui.readPreviousTransactions() gui.setPeerInfo() + gui.readPreviousTransactions() }() go gui.update() diff --git a/ethereal/html_container.go b/ethereal/html_container.go index b00d3f78e0..40a9f55842 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -2,17 +2,18 @@ package main import ( "errors" + "io/ioutil" + "net/url" + "os" + "path" + "path/filepath" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/go-qml/qml" "github.com/howeyc/fsnotify" - "io/ioutil" - "net/url" - "os" - "path" - "path/filepath" ) type HtmlApplication struct { @@ -41,7 +42,7 @@ func (app *HtmlApplication) Create() error { return errors.New("Ethereum package not yet supported") // TODO - ethutil.OpenPackage(app.path) + //ethutil.OpenPackage(app.path) } win := component.CreateWindow(nil) diff --git a/ethereal/qml_container.go b/ethereal/qml_container.go index 1b420ee219..53ff13c2f3 100644 --- a/ethereal/qml_container.go +++ b/ethereal/qml_container.go @@ -25,7 +25,7 @@ func (app *QmlApplication) Create() error { path := string(app.path) // For some reason for windows we get /c:/path/to/something, windows doesn't like the first slash but is fine with the others so we are removing it - if string(app.path[0]) == "/" && runtime.GOOS == "windows" { + if app.path[0] == '/' && runtime.GOOS == "windows" { path = app.path[1:] } From 719b7784f38a8ee6158d4d5a9230a98041f140b1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 00:30:57 +0200 Subject: [PATCH 07/25] Renamed to balance --- ethereal/debugger.go | 24 ++++++------------------ ethereal/gui.go | 17 +++++++++-------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/ethereal/debugger.go b/ethereal/debugger.go index 0963874057..1cf5e0b664 100644 --- a/ethereal/debugger.go +++ b/ethereal/debugger.go @@ -2,15 +2,16 @@ package main import ( "fmt" + "math/big" + "strconv" + "strings" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethvm" "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" - "math/big" - "strconv" - "strings" ) type DebuggerWindow struct { @@ -134,26 +135,13 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data state := self.lib.eth.StateManager().TransState() account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address()) contract := ethstate.NewStateObject([]byte{0}) - contract.Amount = value + contract.Balance = value self.SetAsm(script) - callerClosure := ethvm.NewClosure(account, contract, script, gas, gasPrice) - block := self.lib.eth.BlockChain().CurrentBlock - /* - vm := ethchain.NewVm(state, self.lib.eth.StateManager(), ethchain.RuntimeVars{ - Block: block, - Origin: account.Address(), - BlockNumber: block.Number, - PrevHash: block.PrevHash, - Coinbase: block.Coinbase, - Time: block.Time, - Diff: block.Difficulty, - Value: ethutil.Big(valueStr), - }) - */ + callerClosure := ethvm.NewClosure(account, contract, script, gas, gasPrice) env := utils.NewEnv(state, block, account.Address(), value) vm := ethvm.New(env) vm.Verbose = true diff --git a/ethereal/gui.go b/ethereal/gui.go index 573f68959f..31d4248b2a 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -3,6 +3,11 @@ package main import ( "bytes" "fmt" + "math/big" + "strconv" + "strings" + "time" + "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" @@ -13,10 +18,6 @@ import ( "github.com/ethereum/eth-go/ethwire" "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" - "math/big" - "strconv" - "strings" - "time" ) var logger = ethlog.NewLogger("GUI") @@ -313,7 +314,7 @@ func (gui *Gui) update() { state := gui.eth.StateManager().TransState() unconfirmedFunds := new(big.Int) - gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Amount))) + gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Balance))) gui.getObjectByName("syncProgressIndicator").Set("visible", !gui.eth.IsUpToDate()) lastBlockLabel := gui.getObjectByName("lastBlockLabel") @@ -324,7 +325,7 @@ func (gui *Gui) update() { block := b.Resource.(*ethchain.Block) gui.processBlock(block, false) if bytes.Compare(block.Coinbase, gui.address()) == 0 { - gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Amount, nil) + gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.address()).Balance, nil) } case txMsg := <-txChan: @@ -345,7 +346,7 @@ func (gui *Gui) update() { unconfirmedFunds.Add(unconfirmedFunds, tx.Value) } - gui.setWalletValue(object.Amount, unconfirmedFunds) + gui.setWalletValue(object.Balance, unconfirmedFunds) } else { object := state.GetAccount(gui.address()) if bytes.Compare(tx.Sender(), gui.address()) == 0 { @@ -354,7 +355,7 @@ func (gui *Gui) update() { object.AddAmount(tx.Value) } - gui.setWalletValue(object.Amount, nil) + gui.setWalletValue(object.Balance, nil) state.UpdateStateObject(object) } From 23f83f53ccd71e5aefa9faf93e3527a589d1e487 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 01:05:40 +0200 Subject: [PATCH 08/25] Upped version number --- ethereal/main.go | 7 ++++--- ethereum/main.go | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ethereal/main.go b/ethereal/main.go index 0f99be8865..04a04536d2 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -1,16 +1,17 @@ package main import ( + "os" + "runtime" + "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" - "os" - "runtime" ) const ( ClientIdentifier = "Ethereal" - Version = "0.6.0" + Version = "0.6.1" ) func main() { diff --git a/ethereum/main.go b/ethereum/main.go index 2179910748..9ece8133da 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -1,15 +1,16 @@ package main import ( + "runtime" + "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/utils" - "runtime" ) const ( ClientIdentifier = "Ethereum(G)" - Version = "0.6.0" + Version = "0.6.1" ) var logger = ethlog.NewLogger("CLI") From 5501679642321f3ad17b6dd7f25e3c090c1405df Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 15:33:42 +0200 Subject: [PATCH 09/25] Updated README to include Cpt. Obv. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 790ee541e1..e22bfc4ec3 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,12 @@ Ethereum [![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum) +Master [![Build +Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-master-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-master-docker/builds/-1) + +Develop [![Build +Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-develop-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-develop-docker/builds/-1) + Ethereum Go Client © 2014 Jeffrey Wilcke. Current state: Proof of Concept 0.6.0. From 834803f1e8ce45040045359185c8b8d75f63de2c Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 15:34:36 +0200 Subject: [PATCH 10/25] Update --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index e22bfc4ec3..186c979bc9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ Ethereum ======== -[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum) - Master [![Build -Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-master-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-master-docker/builds/-1) - -Develop [![Build +Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-master-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-master-docker/builds/-1) Develop [![Build Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ethereum-develop-docker)](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-develop-docker/builds/-1) Ethereum Go Client © 2014 Jeffrey Wilcke. From 852d1ee395feabaa0e72265106374a0df197db9a Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 6 Aug 2014 09:53:12 +0200 Subject: [PATCH 11/25] State dumps --- ethereum/flags.go | 10 ++++++- ethereum/main.go | 31 +++++++++++++++++++++- ethereum/repl/javascript_runtime.go | 41 ++++++++++++++++++++++++++--- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/ethereum/flags.go b/ethereum/flags.go index 4f59ddf060..5ed208411b 100644 --- a/ethereum/flags.go +++ b/ethereum/flags.go @@ -3,10 +3,11 @@ package main import ( "flag" "fmt" - "github.com/ethereum/eth-go/ethlog" "os" "os/user" "path" + + "github.com/ethereum/eth-go/ethlog" ) var Identifier string @@ -31,6 +32,9 @@ var LogFile string var ConfigFile string var DebugFile string var LogLevel int +var Dump bool +var DumpHash string +var DumpNumber int // flags specific to cli client var StartMining bool @@ -71,6 +75,10 @@ func Init() { flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0") flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false") + flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]") + flag.StringVar(&DumpHash, "hash", "", "specify arg in hex") + flag.IntVar(&DumpNumber, "number", -1, "specify arg in number") + flag.BoolVar(&StartMining, "mine", false, "start dagger mining") flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console") diff --git a/ethereum/main.go b/ethereum/main.go index 9ece8133da..17838997c4 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -1,8 +1,11 @@ package main import ( + "fmt" + "os" "runtime" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/utils" @@ -24,7 +27,7 @@ func main() { Init() // parsing command line // If the difftool option is selected ignore all other log output - if DiffTool { + if DiffTool || Dump { LogLevel = 0 } @@ -47,6 +50,32 @@ func main() { ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer) + if Dump { + var block *ethchain.Block + + if len(DumpHash) == 0 && DumpNumber == -1 { + block = ethereum.BlockChain().CurrentBlock + } else if len(DumpHash) > 0 { + block = ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(DumpHash)) + } else { + block = ethereum.BlockChain().GetBlockByNumber(uint64(DumpNumber)) + } + + if block == nil { + fmt.Fprintln(os.Stderr, "block not found") + + // We want to output valid JSON + fmt.Println("{}") + + os.Exit(1) + } + + // Leave the Println. This needs clean output for piping + fmt.Println(block.State().Dump()) + + os.Exit(0) + } + if ShowGenesis { utils.ShowGenesis(ethereum) } diff --git a/ethereum/repl/javascript_runtime.go b/ethereum/repl/javascript_runtime.go index f5aea2dd92..29b5f442f6 100644 --- a/ethereum/repl/javascript_runtime.go +++ b/ethereum/repl/javascript_runtime.go @@ -2,6 +2,11 @@ package ethrepl import ( "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethlog" @@ -11,10 +16,6 @@ import ( "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/utils" "github.com/obscuren/otto" - "io/ioutil" - "os" - "path" - "path/filepath" ) var jsrelogger = ethlog.NewLogger("JSRE") @@ -147,12 +148,44 @@ func (self *JSRE) initStdFuncs() { eth.Set("stopMining", self.stopMining) eth.Set("startMining", self.startMining) eth.Set("execBlock", self.execBlock) + eth.Set("dump", self.dump) } /* * The following methods are natively implemented javascript functions */ +func (self *JSRE) dump(call otto.FunctionCall) otto.Value { + var state *ethstate.State + + if len(call.ArgumentList) > 0 { + var block *ethchain.Block + if call.Argument(0).IsNumber() { + num, _ := call.Argument(0).ToInteger() + block = self.ethereum.BlockChain().GetBlockByNumber(uint64(num)) + } else if call.Argument(0).IsString() { + hash, _ := call.Argument(0).ToString() + block = self.ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(hash)) + } else { + fmt.Println("invalid argument for dump. Either hex string or number") + } + + if block == nil { + fmt.Println("block not found") + + return otto.UndefinedValue() + } + + state = block.State() + } else { + state = self.ethereum.StateManager().CurrentState() + } + + fmt.Println(state.Dump()) + + return otto.UndefinedValue() +} + func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value { v, _ := self.vm.ToValue(utils.StopMining(self.ethereum)) return v From c7afb5fb72579fb61139a6365c380a4d9370a7b9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 6 Aug 2014 10:05:34 +0200 Subject: [PATCH 12/25] output dump string, not undefined --- ethereum/repl/javascript_runtime.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethereum/repl/javascript_runtime.go b/ethereum/repl/javascript_runtime.go index 29b5f442f6..026e6f374c 100644 --- a/ethereum/repl/javascript_runtime.go +++ b/ethereum/repl/javascript_runtime.go @@ -181,9 +181,9 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value { state = self.ethereum.StateManager().CurrentState() } - fmt.Println(state.Dump()) + v, _ := self.vm.ToValue(state.Dump()) - return otto.UndefinedValue() + return v } func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value { From bbe896875e8b51143ebea759dfd9f25a4bcc2b2f Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 7 Aug 2014 00:27:58 +0200 Subject: [PATCH 13/25] Typo. Fixes #107 --- ethereal/assets/qml/wallet.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 50ff73060c..84022a230a 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -992,7 +992,7 @@ ApplicationWindow { var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros; var res = eth.create(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text) if(res[1]) { - txResult.text = "Your contract could not be send over the network:\n" + txResult.text = "Your contract could not be sent over the network:\n" txResult.text += res[1].error() txResult.text += "" mainContractColumn.state = "ERROR" From a915ba17edb0e8d2369d79036c0dc9585c0201ec Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 7 Aug 2014 15:12:25 +0200 Subject: [PATCH 14/25] Support the ".eth" TLD through the DnsContract --- ethereal/assets/qml/webapp.qml | 149 ++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 5e4c035d8d..15177e3fd0 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -21,19 +21,54 @@ ApplicationWindow { id: root anchors.fill: parent state: "inspectorShown" + TextField { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + id: uriNav + //text: webview.url + + Keys.onReturnPressed: { + var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ + + var uri = this.text; + if(reg.test(uri)) { + this.text.replace(reg, function(match, pre, domain, path) { + uri = pre; + + var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); + var ip = []; + for(var i = 0, l = lookup.length; i < l; i++) { + ip.push(lookup.charCodeAt(i)) + } + + if(ip.length != 0) { + uri += ip.join("."); + } else { + uri += domain; + } + + uri += path; + }); + } + + console.log("connecting to ...", uri) + + webview.url = uri; + } + } WebView { objectName: "webView" id: webview - anchors.fill: parent - /* - anchors { - left: parent.left - right: parent.right - bottom: sizeGrip.top - top: parent.top - } - */ + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + top: uriNav.bottom + } onTitleChanged: { window.title = title } experimental.preferences.javascriptEnabled: true experimental.preferences.navigatorQtObjectEnabled: true @@ -46,50 +81,50 @@ ApplicationWindow { try { switch(data.call) { - case "getCoinBase": - postData(data._seed, eth.getCoinBase()) + case "getCoinBase": + postData(data._seed, eth.getCoinBase()) - break - case "getIsListening": - postData(data._seed, eth.getIsListening()) + break + case "getIsListening": + postData(data._seed, eth.getIsListening()) - break - case "getIsMining": - postData(data._seed, eth.getIsMining()) + break + case "getIsMining": + postData(data._seed, eth.getIsMining()) - break - case "getPeerCount": - postData(data._seed, eth.getPeerCount()) + break + case "getPeerCount": + postData(data._seed, eth.getPeerCount()) - break + break - case "getTxCountAt": - require(1) - postData(data._seed, eth.getTxCountAt(data.args[0])) + case "getTxCountAt": + require(1) + postData(data._seed, eth.getTxCountAt(data.args[0])) - break - case "getBlockByNumber": + break + case "getBlockByNumber": var block = eth.getBlock(data.args[0]) postData(data._seed, block) break - case "getBlockByHash": + case "getBlockByHash": var block = eth.getBlock(data.args[0]) postData(data._seed, block) break - case "transact": + case "transact": require(5) var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5]) postData(data._seed, tx) break - case "create": + case "create": postData(data._seed, null) break - case "getStorage": + case "getStorage": require(2); var stateObject = eth.getStateObject(data.args[0]) @@ -97,52 +132,52 @@ ApplicationWindow { postData(data._seed, storage) break - case "getStateKeyVals": - require(1); - var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) - postData(data._seed,stateObject) + case "getStateKeyVals": + require(1); + var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) + postData(data._seed,stateObject) break - case "getTransactionsFor": - require(1); - var txs = eth.getTransactionsFor(data.args[0], true) - postData(data._seed, txs) + case "getTransactionsFor": + require(1); + var txs = eth.getTransactionsFor(data.args[0], true) + postData(data._seed, txs) - break - case "getBalance": + break + case "getBalance": require(1); postData(data._seed, eth.getStateObject(data.args[0]).value()); break - case "getKey": + case "getKey": var key = eth.getKey().privateKey; postData(data._seed, key) break - case "watch": + case "watch": require(1) eth.watch(data.args[0], data.args[1]); break - case "disconnect": + case "disconnect": require(1) postData(data._seed, null) break; - case "set": - console.log("'Set' has been depcrecated") - /* - for(var key in data.args) { - if(webview.hasOwnProperty(key)) { - window[key] = data.args[key]; - } - } - */ + case "set": + console.log("'Set' has been depcrecated") + /* + for(var key in data.args) { + if(webview.hasOwnProperty(key)) { + window[key] = data.args[key]; + } + } + */ break; - case "getSecretToAddress": + case "getSecretToAddress": require(1) postData(data._seed, eth.secretToAddress(data.args[0])) break; - case "debug": + case "debug": console.log(data.args[0]); break; } @@ -191,12 +226,12 @@ ApplicationWindow { inspector.visible = false }else{ inspector.visible = true - inspector.url = webview.experimental.remoteInspectorUrl + inspector.url = webview.experimental.remoteInspectorUrl } } onDoubleClicked: { - console.log('refreshing') - webView.reload() + console.log('refreshing') + webView.reload() } anchors.fill: parent } From 4dc5855dfe08bd427e931d03f2c7ae9105688f67 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 7 Aug 2014 16:35:47 +0200 Subject: [PATCH 15/25] Regular browser option added --- ethereal/assets/ext/home.html | 21 +++++++++++++++++++++ ethereal/assets/qml/wallet.qml | 5 +++++ ethereal/ui_lib.go | 7 ++++++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 ethereal/assets/ext/home.html diff --git a/ethereal/assets/ext/home.html b/ethereal/assets/ext/home.html new file mode 100644 index 0000000000..54af769911 --- /dev/null +++ b/ethereal/assets/ext/home.html @@ -0,0 +1,21 @@ + + + +Ethereum + + + + + +

Ethereum

+ + + + diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 84022a230a..92641fb3ef 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -27,6 +27,11 @@ ApplicationWindow { onTriggered: openAppDialog.open() } + MenuItem { + text: "Browser" + onTriggered: ui.openBrowser() + } + MenuSeparator {} MenuItem { diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 6a62fa1df2..42c5c9ad2d 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -1,10 +1,11 @@ package main import ( + "path" + "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethutil" "github.com/go-qml/qml" - "path" ) type memAddr struct { @@ -42,6 +43,10 @@ func (ui *UiLib) OpenHtml(path string) { go app.run() } +func (ui *UiLib) OpenBrowser() { + ui.OpenHtml("file://" + ui.AssetPath("ext/home.html")) +} + func (ui *UiLib) Muted(content string) { component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml")) if err != nil { From 51a2087081ec0c8a9d0d739c344929c8494e13b6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 10 Aug 2014 14:57:42 +0100 Subject: [PATCH 16/25] Minor issues --- ethereal/assets/ext/home.html | 2 +- ethereal/assets/ext/messaging.js | 117 +++++++++++++++++++++++++++++++ ethereal/assets/qml/wallet.qml | 111 ++++++++++++++++++++++++----- ethereal/gui.go | 72 +++++++++++++++---- 4 files changed, 269 insertions(+), 33 deletions(-) create mode 100644 ethereal/assets/ext/messaging.js diff --git a/ethereal/assets/ext/home.html b/ethereal/assets/ext/home.html index 54af769911..86a659d65f 100644 --- a/ethereal/assets/ext/home.html +++ b/ethereal/assets/ext/home.html @@ -14,7 +14,7 @@ h1 { -

Ethereum

+

... Ethereum ...

diff --git a/ethereal/assets/ext/messaging.js b/ethereal/assets/ext/messaging.js new file mode 100644 index 0000000000..e7bc630202 --- /dev/null +++ b/ethereal/assets/ext/messaging.js @@ -0,0 +1,117 @@ +function handleMessage(message) { + console.log("[onMessageReceived]: ", message.data) + // TODO move to messaging.js + var data = JSON.parse(message.data) + + try { + switch(data.call) { + case "getCoinBase": + postData(data._seed, eth.getCoinBase()) + + break + case "getIsListening": + postData(data._seed, eth.getIsListening()) + + break + case "getIsMining": + postData(data._seed, eth.getIsMining()) + + break + case "getPeerCount": + postData(data._seed, eth.getPeerCount()) + + break + + case "getTxCountAt": + require(1) + postData(data._seed, eth.getTxCountAt(data.args[0])) + + break + case "getBlockByNumber": + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) + + break + case "getBlockByHash": + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) + + break + case "transact": + require(5) + + var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5]) + postData(data._seed, tx) + + break + case "create": + postData(data._seed, null) + + break + case "getStorage": + require(2); + + var stateObject = eth.getStateObject(data.args[0]) + 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 "getTransactionsFor": + require(1); + var txs = eth.getTransactionsFor(data.args[0], true) + postData(data._seed, txs) + + break + case "getBalance": + require(1); + + postData(data._seed, eth.getStateObject(data.args[0]).value()); + + break + case "getKey": + var key = eth.getKey().privateKey; + + postData(data._seed, key) + break + case "watch": + require(1) + eth.watch(data.args[0], data.args[1]); + break + case "disconnect": + require(1) + postData(data._seed, null) + break; + case "set": + console.log("'Set' has been depcrecated") + /* + for(var key in data.args) { + if(webview.hasOwnProperty(key)) { + window[key] = data.args[key]; + } + } + */ + break; + case "getSecretToAddress": + require(1) + postData(data._seed, eth.secretToAddress(data.args[0])) + break; + case "debug": + console.log(data.args[0]); + break; + } + } catch(e) { + console.log(data.call + ": " + e) + + postData(data._seed, null); + } +} + +function postData(seed, data) { + webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed})) +} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 92641fb3ef..e3ef148b0b 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -56,6 +56,13 @@ ApplicationWindow { shortcut: "Ctrl+d" onTriggered: ui.startDebugger() } + + MenuItem { + text: "Import Tx" + onTriggered: { + txImportDialog.visible = true + } + } } Menu { @@ -98,6 +105,7 @@ ApplicationWindow { historyView.visible = false newTxView.visible = false infoView.visible = false + pendingTxView.visible = false view.visible = true //root.title = "Ethereal - " = view.title } @@ -161,6 +169,17 @@ ApplicationWindow { } } } + + Image { + source: "../tx.png" + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + setView(pendingTxView) + } + } + } } } @@ -365,6 +384,28 @@ ApplicationWindow { } } + Rectangle { + anchors.fill: parent + visible: false + id: pendingTxView + property var title: "Pending Transactions" + + property var pendingTxModel: ListModel { + id: pendingTxModel + } + + TableView { + id: pendingTxTableView + anchors.fill: parent + TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } + TableViewColumn{ role: "from" ; title: "sender" ; width: 230 } + TableViewColumn{ role: "to" ; title: "Reciever" ; width: 230 } + TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 } + + model: pendingTxModel + } + } + /* signal addPlugin(string name) Component { @@ -500,6 +541,36 @@ ApplicationWindow { } } + Window { + id: txImportDialog + minimumWidth: 270 + maximumWidth: 270 + maximumHeight: 50 + minimumHeight: 50 + TextField { + id: txImportField + width: 170 + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 10 + onAccepted: { + } + } + Button { + anchors.left: txImportField.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 5 + text: "Import" + onClicked: { + eth.importTx(txImportField.text) + txImportField.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } + Window { id: popup visible: false @@ -719,7 +790,7 @@ ApplicationWindow { walletValueLabel.text = value } - function addTx(tx, inout) { + function addTx(type, tx, inout) { var isContract if (tx.contract == true){ isContract = "Yes" @@ -727,13 +798,19 @@ ApplicationWindow { isContract = "No" } - var address; - if(inout == "recv") { - address = tx.sender; - } else { - address = tx.address; + + if(type == "post") { + 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}) + } else if(type == "pre") { + pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract}) } - txModel.insert(0, {inout: inout, hash: tx.hash, address: address, value: tx.value, contract: isContract}) } function addBlock(block, initial) { @@ -749,7 +826,7 @@ ApplicationWindow { if(initial){ blockModel.append({number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) - }else{ + } else { blockModel.insert(0, {number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) } } @@ -805,7 +882,7 @@ ApplicationWindow { // ****************************************** Window { id: peerWindow - //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint + //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint height: 200 width: 700 Rectangle { @@ -932,10 +1009,10 @@ ApplicationWindow { placeholderText: "Gas" text: "500" /* - onTextChanged: { - contractFormReady() - } - */ + onTextChanged: { + contractFormReady() + } + */ } Label { id: atLabel @@ -949,10 +1026,10 @@ ApplicationWindow { text: "10" validator: RegExpValidator { regExp: /\d*/ } /* - onTextChanged: { - contractFormReady() - } - */ + onTextChanged: { + contractFormReady() + } + */ } ComboBox { diff --git a/ethereal/gui.go b/ethereal/gui.go index 61f7b1099a..36e147ba97 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethminer" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" @@ -236,20 +237,54 @@ func (gui *Gui) loadAddressBook() { } } +func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) { + nameReg := ethpipe.New(gui.eth).World().Config().Get("NameReg") + addr := gui.address() + + var inout string + if bytes.Compare(tx.Sender(), addr) == 0 { + inout = "send" + } else { + inout = "recv" + } + + var ( + ptx = ethpub.NewPTx(tx) + send = nameReg.Storage(tx.Sender()) + rec = nameReg.Storage(tx.Recipient) + s, r string + ) + + if tx.CreatesContract() { + rec = nameReg.Storage(tx.CreationAddress()) + } + + if send.Len() != 0 { + s = strings.Trim(send.Str(), "\x00") + } else { + s = ethutil.Bytes2Hex(tx.Sender()) + } + if rec.Len() != 0 { + r = strings.Trim(rec.Str(), "\x00") + } else { + if tx.CreatesContract() { + r = ethutil.Bytes2Hex(tx.CreationAddress()) + } else { + r = ethutil.Bytes2Hex(tx.Recipient) + } + } + ptx.Sender = s + ptx.Address = r + + gui.win.Root().Call("addTx", window, ptx, inout) +} + func (gui *Gui) readPreviousTransactions() { it := gui.txDb.Db().NewIterator(nil, nil) - addr := gui.address() for it.Next() { tx := ethchain.NewTransactionFromBytes(it.Value()) - var inout string - if bytes.Compare(tx.Sender(), addr) == 0 { - inout = "send" - } else { - inout = "recv" - } - - gui.win.Root().Call("addTx", ethpub.NewPTx(tx), inout) + gui.insertTransaction("post", tx) } it.Release() @@ -322,24 +357,26 @@ func (gui *Gui) update() { object := state.GetAccount(gui.address()) if bytes.Compare(tx.Sender(), gui.address()) == 0 { - gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send") - gui.txDb.Put(tx.Hash(), tx.RlpEncode()) - unconfirmedFunds.Sub(unconfirmedFunds, tx.Value) } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { - gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv") - gui.txDb.Put(tx.Hash(), tx.RlpEncode()) - unconfirmedFunds.Add(unconfirmedFunds, tx.Value) } gui.setWalletValue(object.Balance, unconfirmedFunds) + + gui.insertTransaction("pre", tx) } else { object := state.GetAccount(gui.address()) if bytes.Compare(tx.Sender(), gui.address()) == 0 { object.SubAmount(tx.Value) + + gui.win.Root().Call("addTx", "post", ethpub.NewPTx(tx), "send") + gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { object.AddAmount(tx.Value) + + gui.win.Root().Call("addTx", "post", ethpub.NewPTx(tx), "recv") + gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } gui.setWalletValue(object.Balance, nil) @@ -422,6 +459,11 @@ func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PR return gui.pub.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) } +func (self *Gui) ImportTx(rlpTx string) { + tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx)) + self.eth.TxPool().QueueTransaction(tx) +} + func (gui *Gui) SetCustomIdentifier(customIdentifier string) { gui.clientIdentity.SetCustomIdentifier(customIdentifier) gui.config.Save("id", customIdentifier) From ce8f24e57a3ba31d17c34db284bd3d9efa15e7d8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 11 Aug 2014 16:24:17 +0200 Subject: [PATCH 17/25] Moved JSRE to it's own package for sharing between ethere(um/al) --- .../repl => javascript}/javascript_runtime.go | 30 +++---- {ethereum/repl => javascript}/js_lib.go | 2 +- {ethereum/repl => javascript}/types.go | 79 ++++++++++++++++++- 3 files changed, 93 insertions(+), 18 deletions(-) rename {ethereum/repl => javascript}/javascript_runtime.go (90%) rename {ethereum/repl => javascript}/js_lib.go (98%) rename {ethereum/repl => javascript}/types.go (54%) diff --git a/ethereum/repl/javascript_runtime.go b/javascript/javascript_runtime.go similarity index 90% rename from ethereum/repl/javascript_runtime.go rename to javascript/javascript_runtime.go index 026e6f374c..158fc93cf4 100644 --- a/ethereum/repl/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -1,4 +1,4 @@ -package ethrepl +package javascript import ( "fmt" @@ -22,7 +22,7 @@ var jsrelogger = ethlog.NewLogger("JSRE") type JSRE struct { ethereum *eth.Ethereum - vm *otto.Otto + Vm *otto.Otto lib *ethpub.PEthereum blockChan chan ethreact.Event @@ -35,9 +35,9 @@ type JSRE struct { func (jsre *JSRE) LoadExtFile(path string) { result, err := ioutil.ReadFile(path) if err == nil { - jsre.vm.Run(result) + jsre.Vm.Run(result) } else { - jsrelogger.Debugln("Could not load file:", path) + jsrelogger.Infoln("Could not load file:", path) } } @@ -58,7 +58,7 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { } // Init the JS lib - re.vm.Run(jsLib) + re.Vm.Run(jsLib) // Load extra javascript files re.LoadIntFile("string.js") @@ -71,7 +71,7 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { reactor := ethereum.Reactor() reactor.Subscribe("newBlock", re.blockChan) - re.Bind("eth", &JSEthereum{re.lib, re.vm}) + re.Bind("eth", &JSEthereum{re.lib, re.Vm, ethereum}) re.initStdFuncs() @@ -81,11 +81,11 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { } func (self *JSRE) Bind(name string, v interface{}) { - self.vm.Set(name, v) + self.Vm.Set(name, v) } func (self *JSRE) Run(code string) (otto.Value, error) { - return self.vm.Run(code) + return self.Vm.Run(code) } func (self *JSRE) Require(file string) error { @@ -126,12 +126,12 @@ out: case object := <-self.changeChan: if stateObject, ok := object.Resource.(*ethstate.StateObject); ok { for _, cb := range self.objectCb[ethutil.Bytes2Hex(stateObject.Address())] { - val, _ := self.vm.ToValue(ethpub.NewPStateObject(stateObject)) + val, _ := self.Vm.ToValue(ethpub.NewPStateObject(stateObject)) cb.Call(cb, val) } } else if storageObject, ok := object.Resource.(*ethstate.StorageState); ok { for _, cb := range self.objectCb[ethutil.Bytes2Hex(storageObject.StateAddress)+ethutil.Bytes2Hex(storageObject.Address)] { - val, _ := self.vm.ToValue(ethpub.NewPStorageState(storageObject)) + val, _ := self.Vm.ToValue(ethpub.NewPStorageState(storageObject)) cb.Call(cb, val) } } @@ -140,7 +140,7 @@ out: } func (self *JSRE) initStdFuncs() { - t, _ := self.vm.Get("eth") + t, _ := self.Vm.Get("eth") eth := t.Object() eth.Set("watch", self.watch) eth.Set("addPeer", self.addPeer) @@ -181,18 +181,18 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value { state = self.ethereum.StateManager().CurrentState() } - v, _ := self.vm.ToValue(state.Dump()) + v, _ := self.Vm.ToValue(state.Dump()) return v } func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value { - v, _ := self.vm.ToValue(utils.StopMining(self.ethereum)) + v, _ := self.Vm.ToValue(utils.StopMining(self.ethereum)) return v } func (self *JSRE) startMining(call otto.FunctionCall) otto.Value { - v, _ := self.vm.ToValue(utils.StartMining(self.ethereum)) + v, _ := self.Vm.ToValue(utils.StartMining(self.ethereum)) return v } @@ -245,7 +245,7 @@ func (self *JSRE) require(call otto.FunctionCall) otto.Value { return otto.UndefinedValue() } - t, _ := self.vm.Get("exports") + t, _ := self.Vm.Get("exports") return t } diff --git a/ethereum/repl/js_lib.go b/javascript/js_lib.go similarity index 98% rename from ethereum/repl/js_lib.go rename to javascript/js_lib.go index c781c43d0c..a3e9b8a5b0 100644 --- a/ethereum/repl/js_lib.go +++ b/javascript/js_lib.go @@ -1,4 +1,4 @@ -package ethrepl +package javascript const jsLib = ` function pp(object) { diff --git a/ethereum/repl/types.go b/javascript/types.go similarity index 54% rename from ethereum/repl/types.go rename to javascript/types.go index 16a18e6e5d..f9d18b26ae 100644 --- a/ethereum/repl/types.go +++ b/javascript/types.go @@ -1,8 +1,12 @@ -package ethrepl +package javascript import ( "fmt" + + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/otto" ) @@ -34,9 +38,37 @@ func (self *JSBlock) GetTransaction(hash string) otto.Value { return self.eth.toVal(self.PBlock.GetTransaction(hash)) } +type JSMessage struct { + To, From string + Input string + Output string + Path int + Origin string + Timestamp int32 + Coinbase string + Block string + Number int32 +} + +func NewJSMessage(message *ethstate.Message) JSMessage { + return JSMessage{ + To: ethutil.Bytes2Hex(message.To), + From: ethutil.Bytes2Hex(message.From), + Input: ethutil.Bytes2Hex(message.Input), + Output: ethutil.Bytes2Hex(message.Output), + Path: message.Path, + Origin: ethutil.Bytes2Hex(message.Origin), + Timestamp: int32(message.Timestamp), + Coinbase: ethutil.Bytes2Hex(message.Origin), + Block: ethutil.Bytes2Hex(message.Block), + Number: int32(message.Number.Int64()), + } +} + type JSEthereum struct { *ethpub.PEthereum - vm *otto.Otto + vm *otto.Otto + ethereum *eth.Ethereum } func (self *JSEthereum) GetBlock(hash string) otto.Value { @@ -93,3 +125,46 @@ func (self *JSEthereum) toVal(v interface{}) otto.Value { return result } + +func (self *JSEthereum) Messages(object map[string]interface{}) otto.Value { + filter := ethchain.NewFilter(self.ethereum) + + if object["earliest"] != nil { + earliest := object["earliest"] + if e, ok := earliest.(string); ok { + filter.SetEarliestBlock(ethutil.Hex2Bytes(e)) + } else { + filter.SetEarliestBlock(earliest) + } + } + if object["latest"] != nil { + latest := object["latest"] + if l, ok := latest.(string); ok { + filter.SetLatestBlock(ethutil.Hex2Bytes(l)) + } else { + filter.SetLatestBlock(latest) + } + } + if object["to"] != nil { + filter.SetTo(ethutil.Hex2Bytes(object["to"].(string))) + } + if object["from"] != nil { + filter.SetFrom(ethutil.Hex2Bytes(object["from"].(string))) + } + if object["max"] != nil { + filter.SetMax(object["max"].(int)) + } + if object["skip"] != nil { + filter.SetSkip(object["skip"].(int)) + } + + messages := filter.Find() + var msgs []JSMessage + for _, m := range messages { + msgs = append(msgs, NewJSMessage(m)) + } + + v, _ := self.vm.ToValue(msgs) + + return v +} From c59d7a899b0ca121b3f982fa12405629109f1b47 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 11 Aug 2014 16:24:35 +0200 Subject: [PATCH 18/25] Added open js option for repetitive tasks in ethereal --- ethereal/assets/qml/wallet.qml | 20 ++++++++++++++++++++ ethereal/gui.go | 15 ++++++++++++++- ethereum/cmd.go | 10 ++++++---- ethereum/repl/repl.go | 12 +++++++----- ethereum/repl/repl_darwin.go | 4 ++-- utils/vm_env.go | 4 +++- 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index e3ef148b0b..e264d3f4ce 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -63,6 +63,16 @@ ApplicationWindow { txImportDialog.visible = true } } + + MenuItem { + text: "Run JS file" + onTriggered: { + generalFileDialog.callback = function(path) { + eth.evalJavascriptFile(path) + } + generalFileDialog.open() + } + } } Menu { @@ -452,6 +462,16 @@ ApplicationWindow { onAccepted: { } } + + + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString() + callback.call(this, path) + } + } FileDialog { id: importDialog diff --git a/ethereal/gui.go b/ethereal/gui.go index 36e147ba97..d2363b5a98 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" + "github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" ) @@ -47,6 +48,8 @@ type Gui struct { config *ethutil.ConfigManager miner *ethminer.Miner + + jsEngine *javascript.JSRE } // Create GUI, but doesn't start it @@ -58,7 +61,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden pub := ethpub.NewPEthereum(ethereum) - return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config} + return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, jsEngine: javascript.NewJSRE(ethereum)} } func (gui *Gui) Start(assetPath string) { @@ -121,6 +124,9 @@ func (gui *Gui) Stop() { gui.open = false gui.win.Hide() } + + gui.jsEngine.Stop() + logger.Infoln("Stopped") } @@ -464,6 +470,13 @@ func (self *Gui) ImportTx(rlpTx string) { self.eth.TxPool().QueueTransaction(tx) } +func (self *Gui) SearchChange(blockHash, address, storageAddress string) { +} + +func (self *Gui) EvalJavascriptFile(path string) { + self.jsEngine.LoadExtFile(path[7:]) +} + func (gui *Gui) SetCustomIdentifier(customIdentifier string) { gui.clientIdentity.SetCustomIdentifier(customIdentifier) gui.config.Save("id", customIdentifier) diff --git a/ethereum/cmd.go b/ethereum/cmd.go index ff2b8409cf..5ddc916190 100644 --- a/ethereum/cmd.go +++ b/ethereum/cmd.go @@ -1,11 +1,13 @@ package main import ( - "github.com/ethereum/eth-go" - "github.com/ethereum/go-ethereum/ethereum/repl" - "github.com/ethereum/go-ethereum/utils" "io/ioutil" "os" + + "github.com/ethereum/eth-go" + "github.com/ethereum/go-ethereum/ethereum/repl" + "github.com/ethereum/go-ethereum/javascript" + "github.com/ethereum/go-ethereum/utils" ) func InitJsConsole(ethereum *eth.Ethereum) { @@ -25,7 +27,7 @@ func ExecJsFile(ethereum *eth.Ethereum, InputFile string) { if err != nil { logger.Fatalln(err) } - re := ethrepl.NewJSRE(ethereum) + re := javascript.NewJSRE(ethereum) utils.RegisterInterrupt(func(os.Signal) { re.Stop() }) diff --git a/ethereum/repl/repl.go b/ethereum/repl/repl.go index 92d4ad86a3..d08feb7b43 100644 --- a/ethereum/repl/repl.go +++ b/ethereum/repl/repl.go @@ -3,12 +3,14 @@ package ethrepl import ( "bufio" "fmt" - "github.com/ethereum/eth-go" - "github.com/ethereum/eth-go/ethlog" - "github.com/ethereum/eth-go/ethutil" "io" "os" "path" + + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/javascript" ) var logger = ethlog.NewLogger("REPL") @@ -19,7 +21,7 @@ type Repl interface { } type JSRepl struct { - re *JSRE + re *javascript.JSRE prompt string @@ -34,7 +36,7 @@ func NewJSRepl(ethereum *eth.Ethereum) *JSRepl { panic(err) } - return &JSRepl{re: NewJSRE(ethereum), prompt: "> ", history: hist} + return &JSRepl{re: javascript.NewJSRE(ethereum), prompt: "> ", history: hist} } func (self *JSRepl) Start() { diff --git a/ethereum/repl/repl_darwin.go b/ethereum/repl/repl_darwin.go index 3a91b0d442..4c07280f70 100644 --- a/ethereum/repl/repl_darwin.go +++ b/ethereum/repl/repl_darwin.go @@ -115,8 +115,8 @@ L: } func (self *JSRepl) PrintValue(v interface{}) { - method, _ := self.re.vm.Get("prettyPrint") - v, err := self.re.vm.ToValue(v) + method, _ := self.re.Vm.Get("prettyPrint") + v, err := self.re.Vm.ToValue(v) if err == nil { method.Call(method, v) } diff --git a/utils/vm_env.go b/utils/vm_env.go index 2c40dd7b85..30568c421d 100644 --- a/utils/vm_env.go +++ b/utils/vm_env.go @@ -1,9 +1,10 @@ package utils import ( + "math/big" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethstate" - "math/big" ) type VMEnv struct { @@ -29,5 +30,6 @@ func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } func (self *VMEnv) Time() int64 { return self.block.Time } func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } +func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } func (self *VMEnv) Value() *big.Int { return self.value } func (self *VMEnv) State() *ethstate.State { return self.state } From ac14f002e6d861ed646fdcc2febb35b2a3ca57aa Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 12 Aug 2014 11:02:33 +0200 Subject: [PATCH 19/25] Refactored GUI and added modular/pluginable side bar --- ethereal/assets/qml/views/chain.qml | 187 ++++ ethereal/assets/qml/views/history.qml | 50 + ethereal/assets/qml/views/info.qml | 160 +++ ethereal/assets/qml/views/pending_tx.qml | 44 + ethereal/assets/qml/views/transaction.qml | 214 ++++ ethereal/assets/qml/wallet.qml | 1168 ++++----------------- ethereal/gui.go | 42 +- 7 files changed, 901 insertions(+), 964 deletions(-) create mode 100644 ethereal/assets/qml/views/chain.qml create mode 100644 ethereal/assets/qml/views/history.qml create mode 100644 ethereal/assets/qml/views/info.qml create mode 100644 ethereal/assets/qml/views/pending_tx.qml create mode 100644 ethereal/assets/qml/views/transaction.qml diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml new file mode 100644 index 0000000000..7ff6ffcecf --- /dev/null +++ b/ethereal/assets/qml/views/chain.qml @@ -0,0 +1,187 @@ +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 + +Rectangle { + property var title: "Network" + property var iconFile: "../net.png" + + objectName: "chainView" + 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)) + } + } + + 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, name: block.name, 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, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)}) + } + } + + Window { + id: popup + visible: false + //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint + 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: <' + name + '> ' + 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() + } + } +} diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml new file mode 100644 index 0000000000..f50ae80041 --- /dev/null +++ b/ethereal/assets/qml/views/history.qml @@ -0,0 +1,50 @@ +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 + +Rectangle { + property var iconFile: "../tx.png" + property var title: "Transactions" + + property var txModel: ListModel { + id: txModel + } + + id: historyView + anchors.fill: parent + objectName: "transactionView" + + 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 + } + + function addTx(type, 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}) + } +} diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml new file mode 100644 index 0000000000..96b8e4accc --- /dev/null +++ b/ethereal/assets/qml/views/info.qml @@ -0,0 +1,160 @@ +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 + +Rectangle { + property var title: "Information" + property var iconFile: "../heart.png" + + objectName: "infoView" + visible: false + anchors.fill: parent + + color: "#00000000" + + 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.getCustomIdentifier() + width: 500 + placeholderText: "Anonymous" + onTextChanged: { + eth.setCustomIdentifier(text) + } + } + } + + property var addressModel: ListModel { + id: addressModel + } + TableView { + id: addressView + width: parent.width - 200 + height: 200 + anchors.bottom: logLayout.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 + } + + Button { + anchors.top: nameToReg.bottom + text: "Register" + MouseArea{ + anchors.fill: parent + onClicked: { + eth.registerName(nameToReg.text) + nameToReg.text = "" + } + } + } + } + + property var logModel: ListModel { + id: logModel + } + RowLayout { + id: logLayout + width: parent.width + height: 200 + anchors.bottom: parent.bottom + TableView { + id: logView + headerVisible: false + anchors { + right: logLevelSlider.left + left: parent.left + bottom: parent.bottom + top: parent.top + } + + TableViewColumn{ role: "description" ; title: "log" } + + model: logModel + } + + Slider { + id: logLevelSlider + value: eth.getLogLevelInt() + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + + rightMargin: 5 + leftMargin: 5 + topMargin: 5 + bottomMargin: 5 + } + + orientation: Qt.Vertical + maximumValue: 5 + stepSize: 1 + + onValueChanged: { + eth.setLogLevel(value) + } + } + } + + function addDebugMessage(message){ + debuggerLog.append({value: message}) + } + + function addAddress(address) { + addressModel.append({name: address.name, address: address.address}) + } + + function clearAddress() { + addressModel.clear() + } + + function addLog(str) { + // Remove first item once we've reached max log items + if(logModel.count > 250) { + logModel.remove(0) + } + + if(str.len != 0) { + if(logView.flickableItem.atYEnd) { + logModel.append({description: str}) + logView.positionViewAtRow(logView.rowCount - 1, ListView.Contain) + } else { + logModel.append({description: str}) + } + } + + } +} diff --git a/ethereal/assets/qml/views/pending_tx.qml b/ethereal/assets/qml/views/pending_tx.qml new file mode 100644 index 0000000000..18572e3e2a --- /dev/null +++ b/ethereal/assets/qml/views/pending_tx.qml @@ -0,0 +1,44 @@ +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 + +Rectangle { + property var title: "Pending Transactions" + property var iconFile: "../tx.png" + + objectName: "pendingTxView" + anchors.fill: parent + visible: false + id: pendingTxView + + property var pendingTxModel: ListModel { + id: pendingTxModel + } + + TableView { + id: pendingTxTableView + anchors.fill: parent + TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } + TableViewColumn{ role: "from" ; title: "sender" ; width: 230 } + TableViewColumn{ role: "to" ; title: "Reciever" ; width: 230 } + TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 } + + model: pendingTxModel + } + + function addTx(type, tx, inout) { + var isContract + if (tx.contract == true){ + isContract = "Yes" + }else{ + isContract = "No" + } + + + pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract}) + } +} diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml new file mode 100644 index 0000000000..e7fe529a0c --- /dev/null +++ b/ethereal/assets/qml/views/transaction.qml @@ -0,0 +1,214 @@ +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 + +Rectangle { + property var iconFile: "../new.png" + property var title: "New transaction" + + objectName: "newTxView" + visible: false + anchors.fill: parent + color: "#00000000" + + 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 + + ListModel { + id: denomModel + ListElement { text: "Wei" ; zeros: "" } + ListElement { text: "Ada" ; zeros: "000" } + ListElement { text: "Babbage" ; zeros: "000000" } + ListElement { text: "Shannon" ; zeros: "000000000" } + ListElement { text: "Szabo" ; zeros: "000000000000" } + ListElement { text: "Finney" ; zeros: "000000000000000" } + ListElement { text: "Ether" ; zeros: "000000000000000000" } + ListElement { text: "Einstein" ;zeros: "000000000000000000000" } + ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" } + } + + + TextField { + id: txFuelRecipient + placeholderText: "Address / Name or empty for contract" + //validator: RegExpValidator { regExp: /[a-f0-9]{40}/ } + width: 400 + } + + RowLayout { + TextField { + id: txValue + width: 222 + placeholderText: "Amount" + validator: RegExpValidator { regExp: /\d*/ } + onTextChanged: { + contractFormReady() + } + } + + ComboBox { + id: valueDenom + currentIndex: 6 + model: denomModel + } + } + + 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: "10" + validator: RegExpValidator { regExp: /\d*/ } + /* + onTextChanged: { + contractFormReady() + } + */ + } + + ComboBox { + id: gasDenom + currentIndex: 4 + model: denomModel + } + } + + 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: { + var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros; + var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros; + var res = eth.create(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text) + if(res[1]) { + txResult.text = "Your contract could not be sent 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" + } + } + } +} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index e264d3f4ce..cbd3fdf18b 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -6,7 +6,6 @@ import QtQuick.Window 2.1; import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 - ApplicationWindow { id: root @@ -18,6 +17,30 @@ ApplicationWindow { title: "Ethereal" + // Takes care of loading all default plugins + Component.onCompleted: { + var historyView = addPlugin("./views/history.qml") + var newTxView = addPlugin("./views/transaction.qml") + var chainView = addPlugin("./views/chain.qml") + var infoView = addPlugin("./views/info.qml") + var pendingTxView = addPlugin("./views/pending_tx.qml") + + // Call the ready handler + eth.done() + } + + function addPlugin(path, options) { + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + console.debug("Error:"+ component.errorString()); + } + return + } + + return mainSplit.addComponent(component, {objectName: objectName}) + } + MenuBar { Menu { title: "File" @@ -32,6 +55,13 @@ ApplicationWindow { onTriggered: ui.openBrowser() } + MenuItem { + text: "Add plugin" + onTriggered: { + mainSplit.addPlugin("test") + } + } + MenuSeparator {} MenuItem { @@ -110,342 +140,93 @@ ApplicationWindow { id: blockModel } - function setView(view) { - networkView.visible = false - historyView.visible = false - newTxView.visible = false - infoView.visible = false - pendingTxView.visible = false - view.visible = true - //root.title = "Ethereal - " = view.title - } - SplitView { + property var views: []; + + id: mainSplit anchors.fill: parent resizing: false + function setView(view) { + for(var i = 0; i < views.length; i++) { + views[i].visible = false + } + + view.visible = true + } + + function addComponent(component, options) { + var view = mainView.createView(component, options) + if(!view.hasOwnProperty("iconFile")) { + console.log("Could not load plugin. Property 'iconFile' not found on view."); + return; + } + + menu.createMenuItem(view.iconFile, view); + mainSplit.views.push(view); + + return view + } + Rectangle { id: menu Layout.minimumWidth: 80 Layout.maximumWidth: 80 - anchors.bottom: parent.bottom anchors.top: parent.top - //color: "#D9DDE7" color: "#252525" + Component { + id: menuItemTemplate + Image { + property var view; + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view) + } + } + } + } + + + function createMenuItem(icon, view) { + var comp = menuItemTemplate.createObject(menuColumn) + comp.view = view + comp.source = icon + } + ColumnLayout { + id: menuColumn y: 50 anchors.left: parent.left anchors.right: parent.right - height: 200 - Image { - source: "../tx.png" - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - setView(historyView) - } - } - } - Image { - source: "../new.png" - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - setView(newTxView) - } - } - } - Image { - source: "../net.png" - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - setView(networkView) - } - } - } - - Image { - source: "../heart.png" - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - setView(infoView) - } - } - } - - Image { - source: "../tx.png" - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - setView(pendingTxView) - } - } - } } } 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 + function createView(component) { + var view = component.createObject(mainView) + + return view; } - - 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 - - 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.getCustomIdentifier() - width: 500 - placeholderText: "Anonymous" - onTextChanged: { - eth.setCustomIdentifier(text) - } - } - } - - property var addressModel: ListModel { - id: addressModel - } - TableView { - id: addressView - width: parent.width - 200 - height: 200 - anchors.bottom: logLayout.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 - } - - Button { - anchors.top: nameToReg.bottom - text: "Register" - MouseArea{ - anchors.fill: parent - onClicked: { - eth.registerName(nameToReg.text) - nameToReg.text = "" - } - } - } - } - - - property var logModel: ListModel { - id: logModel - } - RowLayout { - id: logLayout - width: parent.width - height: 200 - anchors.bottom: parent.bottom - TableView { - id: logView - headerVisible: false - anchors { - right: logLevelSlider.left - left: parent.left - bottom: parent.bottom - top: parent.top - } - - TableViewColumn{ role: "description" ; title: "log" } - - model: logModel - } - - Slider { - id: logLevelSlider - value: eth.getLogLevelInt() - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - - rightMargin: 5 - leftMargin: 5 - topMargin: 5 - bottomMargin: 5 - } - - orientation: Qt.Vertical - maximumValue: 5 - stepSize: 1 - - onValueChanged: { - eth.setLogLevel(value) - } - } - } - } - - Rectangle { - anchors.fill: parent - visible: false - id: pendingTxView - property var title: "Pending Transactions" - - property var pendingTxModel: ListModel { - id: pendingTxModel - } - - TableView { - id: pendingTxTableView - anchors.fill: parent - TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } - TableViewColumn{ role: "from" ; title: "sender" ; width: 230 } - TableViewColumn{ role: "to" ; title: "Reciever" ; width: 230 } - TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 } - - model: pendingTxModel - } - } - - /* - 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"))) var path = openAppDialog.fileUrl.toString() var ext = path.split('.').pop() if(ext == "html" || ext == "htm") { @@ -462,7 +243,7 @@ ApplicationWindow { onAccepted: { } } - + FileDialog { id: generalFileDialog @@ -517,48 +298,143 @@ ApplicationWindow { } } - Label { - y: 6 - id: lastBlockLabel - objectName: "lastBlockLabel" - visible: true - text: "" + Label { + y: 6 + id: lastBlockLabel + objectName: "lastBlockLabel" + visible: true + text: "" font.pixelSize: 10 - anchors.right: peerGroup.left - anchors.rightMargin: 5 - } + anchors.right: peerGroup.left + anchors.rightMargin: 5 + } - ProgressBar { - id: syncProgressIndicator - visible: false - objectName: "syncProgressIndicator" - y: 3 - width: 140 - indeterminate: true - anchors.right: peerGroup.left - anchors.rightMargin: 5 - } + ProgressBar { + id: syncProgressIndicator + visible: false + objectName: "syncProgressIndicator" + y: 3 + width: 140 + indeterminate: true + anchors.right: peerGroup.left + anchors.rightMargin: 5 + } - RowLayout { - id: peerGroup - y: 7 - anchors.right: parent.right - MouseArea { - onDoubleClicked: peerWindow.visible = true - anchors.fill: parent - } + RowLayout { + id: peerGroup + y: 7 + anchors.right: parent.right + MouseArea { + onDoubleClicked: peerWindow.visible = true + anchors.fill: parent + } - Label { - id: peerLabel - font.pixelSize: 8 - text: "0 / 0" - } - Image { - id: peerImage - width: 10; height: 10 - source: "../network.png" - } - } + Label { + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + id: peerImage + width: 10; height: 10 + source: "../network.png" + } + } + } + + + function setWalletValue(value) { + walletValueLabel.text = value + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + mainView.addPlugin(name) + } + + 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 + //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint + 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" } + } + } + } + + 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: "../facet.png" + x: 10 + y: 10 + } + + Text { + anchors.left: aboutIcon.right + anchors.leftMargin: 10 + font.pointSize: 12 + text: "

Ethereal - Adrastea


Development

Jeffrey Wilcke
Maran Hidskes
Viktor Trón
" + } } Window { @@ -591,145 +467,8 @@ ApplicationWindow { } } - Window { - id: popup - visible: false - //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint - 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: <' + name + '> ' + 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() - } - } - Window { id: addPeerWin - //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint visible: false minimumWidth: 230 maximumWidth: 230 @@ -761,475 +500,4 @@ ApplicationWindow { 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: "../facet.png" - x: 10 - y: 10 - } - - Text { - anchors.left: aboutIcon.right - anchors.leftMargin: 10 - font.pointSize: 12 - text: "

Ethereal - Adrastea


Development

Jeffrey Wilcke
Maran Hidskes
Viktor Trón
" - } - } - - 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(type, tx, inout) { - var isContract - if (tx.contract == true){ - isContract = "Yes" - }else{ - isContract = "No" - } - - - if(type == "post") { - 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}) - } else if(type == "pre") { - pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, 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, name: block.name, 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, name: block.name, 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) { - // Remove first item once we've reached max log items - if(logModel.count > 250) { - logModel.remove(0) - } - - if(str.len != 0) { - if(logView.flickableItem.atYEnd) { - logModel.append({description: str}) - logView.positionViewAtRow(logView.rowCount - 1, ListView.Contain) - } else { - logModel.append({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 - //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint - 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 - - ListModel { - id: denomModel - ListElement { text: "Wei" ; zeros: "" } - ListElement { text: "Ada" ; zeros: "000" } - ListElement { text: "Babbage" ; zeros: "000000" } - ListElement { text: "Shannon" ; zeros: "000000000" } - ListElement { text: "Szabo" ; zeros: "000000000000" } - ListElement { text: "Finney" ; zeros: "000000000000000" } - ListElement { text: "Ether" ; zeros: "000000000000000000" } - ListElement { text: "Einstein" ;zeros: "000000000000000000000" } - ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" } - } - - - TextField { - id: txFuelRecipient - placeholderText: "Address / Name or empty for contract" - //validator: RegExpValidator { regExp: /[a-f0-9]{40}/ } - width: 400 - } - - RowLayout { - TextField { - id: txValue - width: 222 - placeholderText: "Amount" - validator: RegExpValidator { regExp: /\d*/ } - onTextChanged: { - contractFormReady() - } - } - - ComboBox { - id: valueDenom - currentIndex: 6 - model: denomModel - } - } - - 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: "10" - validator: RegExpValidator { regExp: /\d*/ } - /* - onTextChanged: { - contractFormReady() - } - */ - } - - ComboBox { - id: gasDenom - currentIndex: 4 - model: denomModel - } - } - - 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: { - var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros; - var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros; - var res = eth.create(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text) - if(res[1]) { - txResult.text = "Your contract could not be sent 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/gui.go b/ethereal/gui.go index d2363b5a98..d8ab50ac69 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -31,6 +31,7 @@ type Gui struct { // QML Engine engine *qml.Engine component *qml.Common + qmlDone bool // The ethereum interface eth *eth.Ethereum @@ -150,18 +151,16 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { return nil, err } - win := gui.createWindow(component) - - go func() { - go gui.setInitialBlockChain() - gui.loadAddressBook() - gui.setPeerInfo() - gui.readPreviousTransactions() - }() + gui.win = gui.createWindow(component) gui.update() - return win, nil + return gui.win, nil +} + +// The done handler will be called by QML when all views have been loaded +func (gui *Gui) Done() { + gui.qmlDone = true } func (gui *Gui) ImportKey(filePath string) { @@ -230,14 +229,16 @@ type address struct { } func (gui *Gui) loadAddressBook() { - gui.win.Root().Call("clearAddress") + view := gui.getObjectByName("infoView") + view.Call("clearAddress") nameReg := ethpub.EthereumConfig(gui.eth.StateManager()).NameReg() if nameReg != nil { nameReg.EachStorage(func(name string, value *ethutil.Value) { if name[0] != 0 { value.Decode() - gui.win.Root().Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())}) + + view.Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())}) } }) } @@ -282,7 +283,7 @@ func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) { ptx.Sender = s ptx.Address = r - gui.win.Root().Call("addTx", window, ptx, inout) + gui.getObjectByName("transactionView").Call("addTx", window, ptx, inout) } func (gui *Gui) readPreviousTransactions() { @@ -301,7 +302,7 @@ func (gui *Gui) processBlock(block *ethchain.Block, initial bool) { b := ethpub.NewPBlock(block) b.Name = name - gui.win.Root().Call("addBlock", b, initial) + gui.getObjectByName("chainView").Call("addBlock", b, initial) } func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) { @@ -326,6 +327,17 @@ func (self *Gui) getObjectByName(objectName string) qml.Object { // Simple go routine function that updates the list of peers in the GUI func (gui *Gui) update() { + // We have to wait for qml to be done loading all the windows. + for !gui.qmlDone { + time.Sleep(500 * time.Millisecond) + } + + go func() { + go gui.setInitialBlockChain() + gui.loadAddressBook() + gui.setPeerInfo() + gui.readPreviousTransactions() + }() var ( blockChan = make(chan ethreact.Event, 100) @@ -514,7 +526,9 @@ func (gui *Gui) Printf(format string, v ...interface{}) { func (gui *Gui) printLog(s string) { str := strings.TrimRight(s, "\n") lines := strings.Split(str, "\n") + + view := gui.getObjectByName("infoView") for _, line := range lines { - gui.win.Root().Call("addLog", line) + view.Call("addLog", line) } } From 0c9c79a89ba7579f6a8b8614cd1601b7f0ddaf60 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 12 Aug 2014 12:03:06 +0200 Subject: [PATCH 20/25] UI update --- ethereal/assets/qml/wallet.qml | 304 ++++++++++++++++----------------- 1 file changed, 151 insertions(+), 153 deletions(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index cbd3fdf18b..b3fda0a584 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -47,7 +47,10 @@ ApplicationWindow { MenuItem { text: "Import App" shortcut: "Ctrl+o" - onTriggered: openAppDialog.open() + onTriggered: { + generalFileDialog.callback = importApp; + generalFileDialog.open() + } } MenuItem { @@ -58,7 +61,10 @@ ApplicationWindow { MenuItem { text: "Add plugin" onTriggered: { - mainSplit.addPlugin("test") + generalFileDialog.callback = function(path) { + addPlugin(path, {canClose: true}) + } + generalFileDialog.open() } } @@ -67,16 +73,23 @@ ApplicationWindow { MenuItem { text: "Import key" shortcut: "Ctrl+i" - onTriggered: importDialog.open() + onTriggered: { + generalFileDialog.callback = function(path) { + ui.importKey(path) + } + generalFileDialog.open() + } } MenuItem { text: "Export keys" shortcut: "Ctrl+e" - onTriggered: exportDialog.open() + onTriggered: { + generalFileDialog.callback = function(path) { + } + generalFileDialog.open() + } } - - //MenuSeparator {} } Menu { @@ -135,166 +148,34 @@ ApplicationWindow { } - - property var blockModel: ListModel { - id: blockModel - } - - SplitView { - property var views: []; - - id: mainSplit - anchors.fill: parent - resizing: false - - function setView(view) { - for(var i = 0; i < views.length; i++) { - views[i].visible = false - } - - view.visible = true - } - - function addComponent(component, options) { - var view = mainView.createView(component, options) - if(!view.hasOwnProperty("iconFile")) { - console.log("Could not load plugin. Property 'iconFile' not found on view."); - return; - } - - menu.createMenuItem(view.iconFile, view); - mainSplit.views.push(view); - - return view - } - - Rectangle { - id: menu - Layout.minimumWidth: 80 - Layout.maximumWidth: 80 - anchors.top: parent.top - color: "#252525" - - Component { - id: menuItemTemplate - Image { - property var view; - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view) - } - } - } - } - - - function createMenuItem(icon, view) { - var comp = menuItemTemplate.createObject(menuColumn) - comp.view = view - comp.source = icon - } - - ColumnLayout { - id: menuColumn - y: 50 - anchors.left: parent.left - anchors.right: parent.right - } - } - - Rectangle { - id: mainView - color: "#00000000" - - anchors.right: parent.right - anchors.left: menu.right - anchors.bottom: parent.bottom - anchors.top: parent.top - - function createView(component) { - var view = component.createObject(mainView) - - return view; - } - } - - - } - - FileDialog { - id: openAppDialog - title: "Open QML Application" - onAccepted: { - var path = openAppDialog.fileUrl.toString() - var ext = path.split('.').pop() - if(ext == "html" || ext == "htm") { - ui.openHtml(path) - }else if(ext == "qml"){ - ui.openQml(path) - } - } - } - - FileDialog { - id: exportDialog - title: "Export keys" - onAccepted: { - } - } - - - FileDialog { - id: generalFileDialog - property var callback; - onAccepted: { - var path = this.fileUrl.toString() - callback.call(this, path) - } - } - - FileDialog { - id: importDialog - title: "Import key" - onAccepted: { - var path = this.fileUrl.toString() - ui.importKey(path) - } - } - statusBar: StatusBar { - height: 30 + height: 32 RowLayout { Button { id: miningButton + text: "Start Mining" 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" + text: "Browser" + onClicked: { + ui.openBrowser() + } } - Label { - anchors.left: importAppButton.right - anchors.leftMargin: 5 - id: walletValueLabel + RowLayout { + Label { + anchors.left: importAppButton.right + anchors.leftMargin: 5 + id: walletValueLabel + + font.pixelSize: 10 + styleColor: "#797979" + } } } @@ -343,6 +224,123 @@ ApplicationWindow { } + property var blockModel: ListModel { + id: blockModel + } + + SplitView { + property var views: []; + + id: mainSplit + anchors.fill: parent + resizing: false + + function setView(view) { + for(var i = 0; i < views.length; i++) { + views[i].visible = false + } + + view.visible = true + } + + function addComponent(component, options) { + var view = mainView.createView(component, options) + if(!view.hasOwnProperty("iconFile")) { + console.log("Could not load plugin. Property 'iconFile' not found on view."); + return; + } + + menu.createMenuItem(view.iconFile, view); + mainSplit.views.push(view); + + return view + } + + /********************* + * Main menu. + ********************/ + Rectangle { + id: menu + Layout.minimumWidth: 80 + Layout.maximumWidth: 80 + anchors.top: parent.top + color: "#252525" + + Component { + id: menuItemTemplate + Image { + property var view; + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view) + } + } + } + } + + + function createMenuItem(icon, view) { + var comp = menuItemTemplate.createObject(menuColumn) + comp.view = view + comp.source = icon + } + + ColumnLayout { + id: menuColumn + y: 50 + anchors.left: parent.left + anchors.right: parent.right + spacing: 10 + } + } + + /********************* + * Main view + ********************/ + Rectangle { + id: mainView + color: "#00000000" + + anchors.right: parent.right + anchors.left: menu.right + anchors.bottom: parent.bottom + anchors.top: parent.top + + function createView(component) { + var view = component.createObject(mainView) + + return view; + } + } + + + } + + function importApp(path) { + var ext = path.split('.').pop() + if(ext == "html" || ext == "htm") { + ui.openHtml(path) + }else if(ext == "qml"){ + ui.openQml(path) + } + } + + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString() + callback.call(this, path) + } + } + + + function setWalletValue(value) { walletValueLabel.text = value } From 2e2f23a0aef637b8b43362bb8a2dba16d5c02c25 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 12 Aug 2014 12:07:32 +0200 Subject: [PATCH 21/25] Properly hide elements on tx submit --- ethereal/assets/qml/views/transaction.qml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index e7fe529a0c..4ede9e10b2 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -28,11 +28,13 @@ Rectangle { 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} @@ -41,6 +43,8 @@ Rectangle { PropertyChanges { target: txDataLabel; visible:false} PropertyChanges { target: atLabel; visible:false} PropertyChanges { target: txFuelRecipient; visible:false} + PropertyChanges { target: valueDenom; visible:false} + PropertyChanges { target: gasDenom; visible:false} PropertyChanges { target: txResult; visible:true} PropertyChanges { target: txOutput; visible:true} @@ -48,12 +52,15 @@ Rectangle { }, State { name: "SETUP" + PropertyChanges { target: txValue; visible:true; text: ""} - PropertyChanges { target: txGas; visible:true; text: ""} - PropertyChanges { target: txGasPrice; visible:true; text: ""} + PropertyChanges { target: txGas; visible:true;} + PropertyChanges { target: txGasPrice; visible:true;} PropertyChanges { target: codeView; visible:true; text: ""} PropertyChanges { target: txButton; visible:true} PropertyChanges { target: txDataLabel; visible:true} + PropertyChanges { target: valueDenom; visible:true} + PropertyChanges { target: gasDenom; visible:true} PropertyChanges { target: txResult; visible:false} PropertyChanges { target: txOutput; visible:false} @@ -113,11 +120,6 @@ Rectangle { validator: RegExpValidator { regExp: /\d*/ } placeholderText: "Gas" text: "500" - /* - onTextChanged: { - contractFormReady() - } - */ } Label { id: atLabel @@ -130,11 +132,6 @@ Rectangle { placeholderText: "Gas price" text: "10" validator: RegExpValidator { regExp: /\d*/ } - /* - onTextChanged: { - contractFormReady() - } - */ } ComboBox { From 59d9746849e9ba794a190b04d3d9f444321b82b8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 12 Aug 2014 12:16:21 +0200 Subject: [PATCH 22/25] Changed naming on exposed QML variables --- ethereal/assets/qml/views/chain.qml | 4 ++-- ethereal/assets/qml/views/info.qml | 10 ++++----- ethereal/assets/qml/views/transaction.qml | 2 +- ethereal/assets/qml/wallet.qml | 8 ++++---- ethereal/gui.go | 25 +++++------------------ ethereal/ui_lib.go | 15 +++++++++++++- 6 files changed, 31 insertions(+), 33 deletions(-) diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index 7ff6ffcecf..2b968d56cf 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -145,9 +145,9 @@ Rectangle { text: "Debug contract" onClicked: { if(tx.createsContract){ - ui.startDbWithCode(tx.rawData) + eth.startDbWithCode(tx.rawData) }else { - ui.startDbWithContractAndData(tx.address, tx.rawData) + eth.startDbWithContractAndData(tx.address, tx.rawData) } } } diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index 96b8e4accc..9e05e2f8e7 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -35,11 +35,11 @@ Rectangle { text: "Client ID" } TextField { - text: eth.getCustomIdentifier() + text: gui.getCustomIdentifier() width: 500 placeholderText: "Anonymous" onTextChanged: { - eth.setCustomIdentifier(text) + gui.setCustomIdentifier(text) } } } @@ -75,7 +75,7 @@ Rectangle { MouseArea{ anchors.fill: parent onClicked: { - eth.registerName(nameToReg.text) + gui.registerName(nameToReg.text) nameToReg.text = "" } } @@ -107,7 +107,7 @@ Rectangle { Slider { id: logLevelSlider - value: eth.getLogLevelInt() + value: gui.getLogLevelInt() anchors { right: parent.right top: parent.top @@ -124,7 +124,7 @@ Rectangle { stepSize: 1 onValueChanged: { - eth.setLogLevel(value) + gui.setLogLevel(value) } } } diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index 4ede9e10b2..61a1b81cd9 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -174,7 +174,7 @@ Rectangle { onClicked: { var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros; var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros; - var res = eth.create(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text) + var res = gui.create(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text) if(res[1]) { txResult.text = "Your contract could not be sent over the network:\n" txResult.text += res[1].error() diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index b3fda0a584..10cbe5c1e3 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -26,7 +26,7 @@ ApplicationWindow { var pendingTxView = addPlugin("./views/pending_tx.qml") // Call the ready handler - eth.done() + gui.done() } function addPlugin(path, options) { @@ -111,7 +111,7 @@ ApplicationWindow { text: "Run JS file" onTriggered: { generalFileDialog.callback = function(path) { - eth.evalJavascriptFile(path) + lib.evalJavascriptFile(path) } generalFileDialog.open() } @@ -155,7 +155,7 @@ ApplicationWindow { id: miningButton text: "Start Mining" onClicked: { - eth.toggleMining() + gui.toggleMining() } } @@ -456,7 +456,7 @@ ApplicationWindow { anchors.leftMargin: 5 text: "Import" onClicked: { - eth.importTx(txImportField.text) + lib.importTx(txImportField.text) txImportField.visible = false } } diff --git a/ethereal/gui.go b/ethereal/gui.go index d8ab50ac69..e0a415201c 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -18,7 +18,6 @@ import ( "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/utils" "github.com/go-qml/qml" ) @@ -49,8 +48,6 @@ type Gui struct { config *ethutil.ConfigManager miner *ethminer.Miner - - jsEngine *javascript.JSRE } // Create GUI, but doesn't start it @@ -62,7 +59,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden pub := ethpub.NewPEthereum(ethereum) - return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, jsEngine: javascript.NewJSRE(ethereum)} + return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config} } func (gui *Gui) Start(assetPath string) { @@ -81,12 +78,12 @@ func (gui *Gui) Start(assetPath string) { // Create a new QML engine gui.engine = qml.NewEngine() context := gui.engine.Context() + gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath) // Expose the eth library and the ui library to QML - context.SetVar("eth", gui) + context.SetVar("gui", gui) context.SetVar("pub", gui.pub) - gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath) - context.SetVar("ui", gui.uiLib) + context.SetVar("eth", gui.uiLib) // Load the main QML interface data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) @@ -126,7 +123,7 @@ func (gui *Gui) Stop() { gui.win.Hide() } - gui.jsEngine.Stop() + gui.uiLib.jsEngine.Stop() logger.Infoln("Stopped") } @@ -477,18 +474,6 @@ func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PR return gui.pub.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) } -func (self *Gui) ImportTx(rlpTx string) { - tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx)) - self.eth.TxPool().QueueTransaction(tx) -} - -func (self *Gui) SearchChange(blockHash, address, storageAddress string) { -} - -func (self *Gui) EvalJavascriptFile(path string) { - self.jsEngine.LoadExtFile(path[7:]) -} - func (gui *Gui) SetCustomIdentifier(customIdentifier string) { gui.clientIdentity.SetCustomIdentifier(customIdentifier) gui.config.Save("id", customIdentifier) diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 42c5c9ad2d..1d9085fcb1 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -4,7 +4,9 @@ import ( "path" "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" ) @@ -23,10 +25,21 @@ type UiLib struct { win *qml.Window Db *Debugger DbWindow *DebuggerWindow + + jsEngine *javascript.JSRE } func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { - return &UiLib{engine: engine, eth: eth, assetPath: assetPath} + return &UiLib{engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth)} +} + +func (self *UiLib) ImportTx(rlpTx string) { + tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx)) + self.eth.TxPool().QueueTransaction(tx) +} + +func (self *UiLib) EvalJavascriptFile(path string) { + self.jsEngine.LoadExtFile(path[7:]) } func (ui *UiLib) OpenQml(path string) { From dc3b0e170c4c5659d5534ee6b6b6db700790dfc5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 12 Aug 2014 12:21:50 +0200 Subject: [PATCH 23/25] Name changes --- ethereal/assets/qml/wallet.qml | 25 ++++++++++++++----------- ethereal/gui.go | 2 ++ ethereal/ui_lib.go | 1 - 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 10cbe5c1e3..af9a61105c 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -318,14 +318,6 @@ ApplicationWindow { } - function importApp(path) { - var ext = path.split('.').pop() - if(ext == "html" || ext == "htm") { - ui.openHtml(path) - }else if(ext == "qml"){ - ui.openQml(path) - } - } /****************** * Dialogs @@ -340,6 +332,17 @@ ApplicationWindow { } + /****************** + * Wallet functions + *****************/ + function importApp(path) { + var ext = path.split('.').pop() + if(ext == "html" || ext == "htm") { + ui.openHtml(path) + }else if(ext == "qml"){ + ui.openQml(path) + } + } function setWalletValue(value) { walletValueLabel.text = value @@ -381,9 +384,9 @@ ApplicationWindow { return time; } - // ****************************************** - // Windows - // ****************************************** + /********************** + * Windows + *********************/ Window { id: peerWindow //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint diff --git a/ethereal/gui.go b/ethereal/gui.go index e0a415201c..78a930e026 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -106,11 +106,13 @@ func (gui *Gui) Start(assetPath string) { logger.Infoln("Starting GUI") gui.open = true win.Show() + // only add the gui logger after window is shown otherwise slider wont be shown if addlog { ethlog.AddLogSystem(gui) } win.Wait() + // need to silence gui logger after window closed otherwise logsystem hangs (but do not save loglevel) gui.logLevel = ethlog.Silence gui.open = false diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 1d9085fcb1..b7cabf3a8e 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -112,7 +112,6 @@ func (self *UiLib) StartDbWithCode(code string) { func (self *UiLib) StartDebugger() { dbWindow := NewDebuggerWindow(self) - //self.DbWindow = dbWindow dbWindow.Show() } From 1fa792eae7584f61624121e46dbad82484109c64 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 12 Aug 2014 13:16:44 +0200 Subject: [PATCH 24/25] Fixed reference --- ethereal/assets/qml/wallet.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index af9a61105c..58d39381b3 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -55,7 +55,7 @@ ApplicationWindow { MenuItem { text: "Browser" - onTriggered: ui.openBrowser() + onTriggered: eth.openBrowser() } MenuItem { @@ -163,7 +163,7 @@ ApplicationWindow { id: importAppButton text: "Browser" onClicked: { - ui.openBrowser() + eth.openBrowser() } } From d518423b9c493bf5b42e6575db9a32106812e6bc Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 13 Aug 2014 10:52:30 +0200 Subject: [PATCH 25/25] Updated DNS Lookup --- ethereal/assets/qml/views/transaction.qml | 17 +++-- ethereal/assets/qml/wallet.qml | 92 +++++++++++++++++++---- ethereal/assets/qml/webapp.qml | 8 +- ethereal/gui.go | 4 +- 4 files changed, 94 insertions(+), 27 deletions(-) diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index 61a1b81cd9..80e1670f81 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -18,13 +18,8 @@ Rectangle { 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" @@ -208,4 +203,12 @@ Rectangle { } } } + + 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" + } + } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 58d39381b3..3fc9a024c6 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -19,11 +19,11 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { - var historyView = addPlugin("./views/history.qml") - var newTxView = addPlugin("./views/transaction.qml") - var chainView = addPlugin("./views/chain.qml") - var infoView = addPlugin("./views/info.qml") - var pendingTxView = addPlugin("./views/pending_tx.qml") + var historyView = addPlugin("./views/history.qml", {title: "History"}) + var newTxView = addPlugin("./views/transaction.qml", {title: "New Transaction"}) + var chainView = addPlugin("./views/chain.qml", {title: "Block chain"}) + var infoView = addPlugin("./views/info.qml", {title: "Info"}) + var pendingTxView = addPlugin("./views/pending_tx.qml", {title: "Pending", canClose: true}) // Call the ready handler gui.done() @@ -38,7 +38,7 @@ ApplicationWindow { return } - return mainSplit.addComponent(component, {objectName: objectName}) + return mainSplit.addComponent(component, options) } MenuBar { @@ -111,7 +111,7 @@ ApplicationWindow { text: "Run JS file" onTriggered: { generalFileDialog.callback = function(path) { - lib.evalJavascriptFile(path) + eth.evalJavascriptFile(path) } generalFileDialog.open() } @@ -169,8 +169,6 @@ ApplicationWindow { RowLayout { Label { - anchors.left: importAppButton.right - anchors.leftMargin: 5 id: walletValueLabel font.pixelSize: 10 @@ -250,7 +248,7 @@ ApplicationWindow { return; } - menu.createMenuItem(view.iconFile, view); + menu.createMenuItem(view.iconFile, view, options); mainSplit.views.push(view); return view @@ -261,8 +259,8 @@ ApplicationWindow { ********************/ Rectangle { id: menu - Layout.minimumWidth: 80 - Layout.maximumWidth: 80 + Layout.minimumWidth: 180 + Layout.maximumWidth: 180 anchors.top: parent.top color: "#252525" @@ -280,11 +278,73 @@ ApplicationWindow { } } + /* + Component { + id: menuItemTemplate + Rectangle { + property var view; + property var source; + property alias title: title.text + height: 25 + + id: tab + + anchors { + left: parent.left + right: parent.right + } + + Label { + id: title + y: parent.height / 2 - this.height / 2 + x: 5 + font.pixelSize: 10 + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view) + } + } + + Image { + id: closeButton + y: parent.height / 2 - this.height / 2 + visible: false + + source: "../close.png" + anchors { + right: parent.right + rightMargin: 5 + } + + MouseArea { + anchors.fill: parent + onClicked: { + console.log("should close") + } + } + } + } + } + */ + + + function createMenuItem(icon, view, options) { + if(options === undefined) { + options = {}; + } - function createMenuItem(icon, view) { var comp = menuItemTemplate.createObject(menuColumn) comp.view = view comp.source = icon + /* + comp.title = options.title + if(options.canClose) { + //comp.closeButton.visible = options.canClose + } + */ } ColumnLayout { @@ -459,7 +519,7 @@ ApplicationWindow { anchors.leftMargin: 5 text: "Import" onClicked: { - lib.importTx(txImportField.text) + eth.importTx(txImportField.text) txImportField.visible = false } } @@ -483,7 +543,7 @@ ApplicationWindow { anchors.leftMargin: 10 placeholderText: "address:port" onAccepted: { - ui.connectToPeer(addrField.text) + eth.connectToPeer(addrField.text) addPeerWin.visible = false } } @@ -493,7 +553,7 @@ ApplicationWindow { anchors.leftMargin: 5 text: "Add" onClicked: { - ui.connectToPeer(addrField.text) + eth.connectToPeer(addrField.text) addPeerWin.visible = false } } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 15177e3fd0..a848adf45d 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -31,9 +31,13 @@ ApplicationWindow { //text: webview.url Keys.onReturnPressed: { + var uri = this.text; + if(!/.*\:\/\/.*/.test(uri)) { + uri = "http://" + uri; + } + var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ - var uri = this.text; if(reg.test(uri)) { this.text.replace(reg, function(match, pre, domain, path) { uri = pre; @@ -45,7 +49,7 @@ ApplicationWindow { } if(ip.length != 0) { - uri += ip.join("."); + uri += lookup; } else { uri += domain; } diff --git a/ethereal/gui.go b/ethereal/gui.go index 78a930e026..a4e3efb19a 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -387,12 +387,12 @@ func (gui *Gui) update() { if bytes.Compare(tx.Sender(), gui.address()) == 0 { object.SubAmount(tx.Value) - gui.win.Root().Call("addTx", "post", ethpub.NewPTx(tx), "send") + gui.getObjectByName("transactionView").Call("addTx", "post", ethpub.NewPTx(tx), "send") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { object.AddAmount(tx.Value) - gui.win.Root().Call("addTx", "post", ethpub.NewPTx(tx), "recv") + gui.getObjectByName("transactionView").Call("addTx", "post", ethpub.NewPTx(tx), "recv") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) }