From 94b12f7804bfb32763c9bbbce323af5d84c74910 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 15 Jul 2014 00:05:18 +0100 Subject: [PATCH 01/51] 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 889726b04..dfd867d64 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/51] 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 17c342a1b..736b059e5 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 9f28045f8..eb0c50cc3 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 852a50487..998ff7520 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/51] 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 736b059e5..ee723fc3d 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 bfae97050..9bc11e81e 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/51] 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 ee723fc3d..ac745ae37 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/51] 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 eef49824f..aadc90e3b 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 df01cddda..832200176 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/51] 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 aadc90e3b..50ff73060 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 832200176..573f68959 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 b00d3f78e..40a9f5584 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 1b420ee21..53ff13c2f 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/51] 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 096387405..1cf5e0b66 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 573f68959..31d4248b2 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/51] 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 0f99be886..04a04536d 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 217991074..9ece8133d 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/51] Updated README to include Cpt. Obv. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 790ee541e..e22bfc4ec 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/51] Update --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index e22bfc4ec..186c979bc 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/51] 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 4f59ddf06..5ed208411 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 9ece8133d..17838997c 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 f5aea2dd9..29b5f442f 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/51] 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 29b5f442f..026e6f374 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/51] 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 50ff73060..84022a230 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/51] 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 5e4c035d8..15177e3fd 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/51] 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 000000000..54af76991 --- /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 84022a230..92641fb3e 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 6a62fa1df..42c5c9ad2 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/51] 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 54af76991..86a659d65 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 000000000..e7bc63020 --- /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 92641fb3e..e3ef148b0 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 61f7b1099..36e147ba9 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/51] 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 026e6f374..158fc93cf 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 c781c43d0..a3e9b8a5b 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 16a18e6e5..f9d18b26a 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/51] 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 e3ef148b0..e264d3f4c 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 36e147ba9..d2363b5a9 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 ff2b8409c..5ddc91619 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 92d4ad86a..d08feb7b4 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 3a91b0d44..4c07280f7 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 2c40dd7b8..30568c421 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/51] 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 000000000..7ff6ffcec --- /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 000000000..f50ae8004 --- /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 000000000..96b8e4acc --- /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 000000000..18572e3e2 --- /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 000000000..e7fe529a0 --- /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 e264d3f4c..cbd3fdf18 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 d2363b5a9..d8ab50ac6 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/51] 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 cbd3fdf18..b3fda0a58 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/51] 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 e7fe529a0..4ede9e10b 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/51] 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 7ff6ffcec..2b968d56c 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 96b8e4acc..9e05e2f8e 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 4ede9e10b..61a1b81cd 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 b3fda0a58..10cbe5c1e 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 d8ab50ac6..e0a415201 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 42c5c9ad2..1d9085fcb 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/51] 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 10cbe5c1e..af9a61105 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 e0a415201..78a930e02 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 1d9085fcb..b7cabf3a8 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/51] 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 af9a61105..58d39381b 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 3569e8d7dfa2e7cf2e47a04225208bd928054cdd Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 12 Aug 2014 14:57:51 +0200 Subject: [PATCH 25/51] remove mmenomic words initialisation (mnemonic wordlist now inline to support binary builds without extra asset) --- utils/cmd.go | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/cmd.go b/utils/cmd.go index 0eeb1049e..4b9d0644c 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -194,7 +194,6 @@ func DefaultAssetPath() string { } func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) { - ethcrypto.InitWords(DefaultAssetPath()) // Init mnemonic word list var err error switch { From d518423b9c493bf5b42e6575db9a32106812e6bc Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 13 Aug 2014 10:52:30 +0200 Subject: [PATCH 26/51] 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 61a1b81cd..80e1670f8 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 58d39381b..3fc9a024c 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 15177e3fd..a848adf45 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 78a930e02..a4e3efb19 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()) } From 95ba340d07a02da40000d4bcf2b1bb24bd7856ef Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 14 Aug 2014 00:18:37 +0200 Subject: [PATCH 27/51] Tweaks and fixes + added webview debugger * Require better icons .. someone? :-) --- ethereal/assets/back.png | Bin 0 -> 931 bytes ethereal/assets/close.png | Bin 0 -> 905 bytes ethereal/assets/ext/ethereum.js | 95 ++++++++-- ethereal/assets/ext/messaging.js | 117 ------------ ethereal/assets/pick.png | Bin 0 -> 932 bytes ethereal/assets/qml/views/history.qml | 2 +- ethereal/assets/qml/views/info.qml | 27 +-- ethereal/assets/qml/views/javascript.qml | 45 +++++ ethereal/assets/qml/views/pending_tx.qml | 2 +- ethereal/assets/qml/wallet.qml | 9 +- ethereal/assets/qml/webapp.qml | 215 +++++++++++++---------- ethereal/gui.go | 6 +- ethereal/main.go | 3 + ethereal/ui_lib.go | 10 ++ 14 files changed, 271 insertions(+), 260 deletions(-) create mode 100644 ethereal/assets/back.png create mode 100644 ethereal/assets/close.png delete mode 100644 ethereal/assets/ext/messaging.js create mode 100644 ethereal/assets/pick.png create mode 100644 ethereal/assets/qml/views/javascript.qml diff --git a/ethereal/assets/back.png b/ethereal/assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..71486c7c05795d7ee5a3ef0ee77af0c0085b40d9 GIT binary patch literal 931 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pn}NEkcg59UmvUF{9L`nl>DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtH5O0c3d|4@L;p!@;Rg)2@K@No-U3d9=va- zS^FI^5NLa>67XSy1BZ&7Y*XK?Nw!=NDqXyA;S8;_Js@e`(6!$+KS1xwEM0YN`B=gqQaN0t0Sa z9_u})*Y{X=X^6h>W$_-~h35*SQ{sg~yd~Ox2a516)cnHJJlW{zwb>gNvh%2{2n#Tq z5fZx5jX#8KYR`GW+$QCq!<7?_UPz}`^PWENS+|^NFH3^hD=ikYg5XU&U*;O`ZT>7JoSv|J!Ii7NB~t%FR0UICG`vapG9!Io z!dB&PZ-u|TH`ZZ%dHO4}>Fa`6)h%i+J^454CaI`zabQ}*^rzu>6Bm&PCMUl>iOiipE=GeYfPQBX=;$R|I)sItgf5wd<&v{u4#PN3)vTNDdzi! zsPy>!vqwLEnRn&7+CJwxH#aY>JNdO|imgUt@H1_mrG4K&F+Qwtl#H>PxxFAJ zE&X#>%Er~E1$KN-=blz9WOPtJt~B>3Q~uY8DP1>bzGu{voWE^mAz$BqJKF=>4L2T` zyz||Y!u+;f=Gx~j>|$KeoO|Ez-@UZWvUmTlX4?PzM@_&x$rqbIImgr0&t;ucLK6U~ CaA2hX literal 0 HcmV?d00001 diff --git a/ethereal/assets/close.png b/ethereal/assets/close.png new file mode 100644 index 0000000000000000000000000000000000000000..88df442c5c3e31aafb95e16c363f08a89de0f29a GIT binary patch literal 905 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5l;AAz zh%9Dc;1&j9Muu5)B!GrRW`;zRMELqxCFkerC8p#jrRr7W764TzCHb_`sNdc^+B->Ug!Z$#{Ilm}X!Bo#g&p^qJOF==wrYI%ND#*nR zsvXF)RmvzSDX`MlFE20GD>v55FG|-pw6wI;H!#vSGSUUA&@HaaD@m--%_~-h7y>iL zCAB!YD6^m>Ge1uOWNuW^rA6E#WYIKx_J=?u2N z+Gk8%19Q?N!w!lslXh{j-}YK|oA2ckuH7tu+g35=JvRTg$$sc(0pt|;;-PO{+JgfH|EO*WgDT=;%Af^Ati{|DM@uy3C zzrWkl!h7RDuAhTh@1hrWeUHO$a82H)@Sjn2*>2W1P8VNB9hi5aEKs~-t41QLWW--) zmu1Tjre8ie=k03u3HP#gNAGyX8N+GeVJT!bE1OC8D{5=%I>U7>KUIb WGK;!D`Q1fO8uxVdb6Mw<&;$UMO-b7T literal 0 HcmV?d00001 diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index de6fb0255..9970c6379 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -2,30 +2,97 @@ window.eth = { prototype: Object(), + mutan: function(code) { + }, + + toHex: function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; + }, + + toAscii: function(hex) { + // Find termination + var str = ""; + var i = 0, l = hex.length; + for(; i < l; i+=2) { + var code = hex.charCodeAt(i) + if(code == 0) { + break; + } + + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + + return str; + }, + + fromAscii: function(str, pad) { + if(pad === undefined) { + pad = 32 + } + + var hex = this.toHex(str); + + while(hex.length < pad*2) + hex += "00"; + + return hex + }, + + // Retrieve block // // Either supply a number or a string. Type is determent for the lookup method // string - Retrieves the block by looking up the hash // number - Retrieves the block by looking up the block number - getBlock: function(numberOrHash, cb) { - var func; - if(typeof numberOrHash == "string") { - func = "getBlockByHash"; - } else { - func = "getBlockByNumber"; - } - postData({call: func, args: [numberOrHash]}, cb); - }, + getBlock: function(numberOrHash, cb) { + var func; + if(typeof numberOrHash == "string") { + func = "getBlockByHash"; + } else { + func = "getBlockByNumber"; + } + postData({call: func, args: [numberOrHash]}, cb); + }, // Create transaction // // Transact between two state objects - transact: function(sec, recipient, value, gas, gasPrice, data, cb) { - postData({call: "transact", args: [sec, recipient, value, gas, gasPrice, data]}, cb); - }, + transact: function(params, cb) { + if(params === undefined) { + params = {}; + } - create: function(sec, value, gas, gasPrice, init, body, cb) { - postData({call: "create", args: [sec, value, gas, gasPrice, init, body]}, cb); + if(params.endowment !== undefined) + params.value = params.endowment; + if(params.code !== undefined) + params.data = params.code; + + // Make sure everything is string + var fields = ["to", "from", "value", "gas", "gasPrice"]; + for(var i = 0; i < fields.length; i++) { + if(params[fields[i]] === undefined) { + params[fields[i]] = ""; + } + params[fields[i]] = params[fields[i]].toString(); + } + + var data; + if(typeof params.data === "object") { + data = ""; + for(var i = 0; i < params.data.length; i++) { + data += params.data[i] + } + } else { + data = params.data; + } + + postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb); }, getStorageAt: function(address, storageAddress, cb) { diff --git a/ethereal/assets/ext/messaging.js b/ethereal/assets/ext/messaging.js deleted file mode 100644 index e7bc63020..000000000 --- a/ethereal/assets/ext/messaging.js +++ /dev/null @@ -1,117 +0,0 @@ -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/pick.png b/ethereal/assets/pick.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5a261c263a476ffaa7b5b4db19766077b87acb GIT binary patch literal 932 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9GG z!XV7ZFl&wkP(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1aGPZ!6K2+p@t z_Ifcp3LGzvmkMM~N?a^>BwWB-I(zk$uHGM!H(qdaWTh%1%w^#lL zhh0O(&d=tvVk3DIAYwdEEuSyVLnJi%UP})Z0xvd(j zg56ADpS*oIEQHK0e>nBWlcNPBm>|1{`XWay0$BktQHz%nn{BK_|kKweF zpYrnwd=C~s@k*4)Yx>j{a*#tjFtcT&?#(O5Tg?;ro4Def_wM@nN#mo|&bA{D6!m;N zJRi?AS*YgnF!;o~gOQ>80w!=T*OUF3`ib{s^Gx$ZsfZ0H-iG@g&7E~3g{w@kRAte% zP5izEDMf+Xzsji|y7hl;fSudheFbI0rQga7H)a<^Y`&>fqE#K>>tNG<-=J}=-y~_v zG%s5<#xj?^N59vFINd2Mx_n~2;B2;{GwL78)jqJzuJEqBm$&>6DC>B-`njxgN@xNA DTr*?p literal 0 HcmV?d00001 diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml index f50ae8004..a73b7367d 100644 --- a/ethereal/assets/qml/views/history.qml +++ b/ethereal/assets/qml/views/history.qml @@ -29,7 +29,7 @@ Rectangle { model: txModel } - function addTx(type, tx, inout) { + function addTx(tx, inout) { var isContract if (tx.contract == true){ isContract = "Yes" diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index 9e05e2f8e..fcddd46e2 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -17,6 +17,7 @@ Rectangle { color: "#00000000" Column { + id: info spacing: 3 anchors.fill: parent anchors.topMargin: 5 @@ -49,7 +50,7 @@ Rectangle { } TableView { id: addressView - width: parent.width - 200 + width: parent.width height: 200 anchors.bottom: logLayout.top TableViewColumn{ role: "name"; title: "name" } @@ -58,30 +59,6 @@ Rectangle { 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: { - gui.registerName(nameToReg.text) - nameToReg.text = "" - } - } - } - } - property var logModel: ListModel { id: logModel } diff --git a/ethereal/assets/qml/views/javascript.qml b/ethereal/assets/qml/views/javascript.qml new file mode 100644 index 000000000..376397130 --- /dev/null +++ b/ethereal/assets/qml/views/javascript.qml @@ -0,0 +1,45 @@ +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: "JavaScript" + property var iconFile: "../tx.png" + + objectName: "javascriptView" + visible: false + anchors.fill: parent + + TextField { + id: input + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 20 + + Keys.onReturnPressed: { + var res = eth.evalJavascriptString(this.text); + this.text = ""; + + output.append(res) + } + } + + TextArea { + id: output + verticalAlignment: TextEdit.AlignBottom + text: "> JSRE Ready..." + anchors { + top: parent.top + left: parent.left + right: parent.right + bottom: input.top + } + } +} diff --git a/ethereal/assets/qml/views/pending_tx.qml b/ethereal/assets/qml/views/pending_tx.qml index 18572e3e2..5c5c496d6 100644 --- a/ethereal/assets/qml/views/pending_tx.qml +++ b/ethereal/assets/qml/views/pending_tx.qml @@ -30,7 +30,7 @@ Rectangle { model: pendingTxModel } - function addTx(type, tx, inout) { + function addTx(tx, inout) { var isContract if (tx.contract == true){ isContract = "Yes" diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 3fc9a024c..769edfc6a 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -24,6 +24,7 @@ ApplicationWindow { 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}) + var pendingTxView = addPlugin("./views/javascript.qml", {title: "JavaScript", canClose: true}) // Call the ready handler gui.done() @@ -259,8 +260,8 @@ ApplicationWindow { ********************/ Rectangle { id: menu - Layout.minimumWidth: 180 - Layout.maximumWidth: 180 + Layout.minimumWidth: 80 + Layout.maximumWidth: 80 anchors.top: parent.top color: "#252525" @@ -398,9 +399,9 @@ ApplicationWindow { function importApp(path) { var ext = path.split('.').pop() if(ext == "html" || ext == "htm") { - ui.openHtml(path) + eth.openHtml(path) }else if(ext == "qml"){ - ui.openQml(path) + eth.openQml(path) } } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index a848adf45..ec2f01741 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -21,46 +21,62 @@ ApplicationWindow { id: root anchors.fill: parent state: "inspectorShown" - TextField { + + RowLayout { + id: navBar anchors { - top: parent.top left: parent.left right: parent.right } - id: uriNav - //text: webview.url - Keys.onReturnPressed: { - var uri = this.text; - if(!/.*\:\/\/.*/.test(uri)) { - uri = "http://" + uri; + Button { + id: back + iconSource: "../back.png" + onClicked: { + webview.goBack() } + } - var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ - - 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 += lookup; - } else { - uri += domain; - } - - uri += path; - }); + TextField { + anchors { + top: parent.top + left: back.right + right: parent.right } + id: uriNav - console.log("connecting to ...", uri) + Keys.onReturnPressed: { + var uri = this.text; + if(!/.*\:\/\/.*/.test(uri)) { + uri = "http://" + uri; + } - webview.url = uri; + var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ + + 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 += lookup; + } else { + uri += domain; + } + + uri += path; + }); + } + + console.log("connecting to ...", uri) + + webview.url = uri; + } } } @@ -71,7 +87,7 @@ ApplicationWindow { left: parent.left right: parent.right bottom: parent.bottom - top: uriNav.bottom + top: navBar.bottom } onTitleChanged: { window.title = title } experimental.preferences.javascriptEnabled: true @@ -86,103 +102,107 @@ ApplicationWindow { try { switch(data.call) { case "getCoinBase": - postData(data._seed, eth.getCoinBase()) + postData(data._seed, eth.getCoinBase()) + + break - break case "getIsListening": - postData(data._seed, eth.getIsListening()) + postData(data._seed, eth.getIsListening()) + + break - break case "getIsMining": - postData(data._seed, eth.getIsMining()) + postData(data._seed, eth.getIsMining()) + + break - break case "getPeerCount": - postData(data._seed, eth.getPeerCount()) + postData(data._seed, eth.getPeerCount()) - break + break case "getTxCountAt": - require(1) - postData(data._seed, eth.getTxCountAt(data.args[0])) + require(1) + postData(data._seed, eth.getTxCountAt(data.args[0])) + + break - break case "getBlockByNumber": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) + + break - break case "getBlockByHash": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) + + break - break case "transact": - require(5) + 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) + 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 - break case "getStorage": - require(2); + require(2); - var stateObject = eth.getStateObject(data.args[0]) - var storage = stateObject.getStorage(data.args[1]) - postData(data._seed, storage) + var stateObject = eth.getStateObject(data.args[0]) + var storage = stateObject.getStorage(data.args[1]) + postData(data._seed, storage) + + break - break case "getStateKeyVals": - require(1); - var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) - postData(data._seed,stateObject) + require(1); + var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) + postData(data._seed,stateObject) + + break - break case "getTransactionsFor": - require(1); - var txs = eth.getTransactionsFor(data.args[0], true) - postData(data._seed, txs) + require(1); + var txs = eth.getTransactionsFor(data.args[0], true) + postData(data._seed, txs) + + break - break case "getBalance": - require(1); + require(1); - postData(data._seed, eth.getStateObject(data.args[0]).value()); + postData(data._seed, eth.getStateObject(data.args[0]).value()); + + break - break case "getKey": - var key = eth.getKey().privateKey; + var key = eth.getKey().privateKey; + + postData(data._seed, key) + break - postData(data._seed, key) - break case "watch": - require(1) - eth.watch(data.args[0], data.args[1]); - break + 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; + require(1) + postData(data._seed, null) + + break; + case "getSecretToAddress": - require(1) - postData(data._seed, eth.secretToAddress(data.args[0])) - break; + require(1) + postData(data._seed, eth.secretToAddress(data.args[0])) + + break; + case "debug": - console.log(data.args[0]); + console.log(data.args[0]); break; } } catch(e) { @@ -215,12 +235,13 @@ ApplicationWindow { postEvent(ev, [storageObject.address, storageObject.value]) } } + Rectangle { id: toggleInspector color: "#bcbcbc" visible: true - height: 12 - width: 12 + height: 20 + width: 20 anchors { right: root.right } @@ -233,8 +254,8 @@ ApplicationWindow { inspector.url = webview.experimental.remoteInspectorUrl } } + onDoubleClicked: { - console.log('refreshing') webView.reload() } anchors.fill: parent diff --git a/ethereal/gui.go b/ethereal/gui.go index a4e3efb19..710a1bd1e 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -282,7 +282,11 @@ func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) { ptx.Sender = s ptx.Address = r - gui.getObjectByName("transactionView").Call("addTx", window, ptx, inout) + if window == "post" { + gui.getObjectByName("transactionView").Call("addTx", ptx, inout) + } else { + gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout) + } } func (gui *Gui) readPreviousTransactions() { diff --git a/ethereal/main.go b/ethereal/main.go index 04a04536d..431307c6f 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -17,6 +17,9 @@ const ( func main() { runtime.GOMAXPROCS(runtime.NumCPU()) + // This is a bit of a cheat, but ey! + os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999") + qml.Init(nil) var interrupted = false diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index b7cabf3a8..f900fcaee 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "path" "github.com/ethereum/eth-go" @@ -42,6 +43,15 @@ func (self *UiLib) EvalJavascriptFile(path string) { self.jsEngine.LoadExtFile(path[7:]) } +func (self *UiLib) EvalJavascriptString(str string) string { + value, err := self.jsEngine.Run(str) + if err != nil { + return err.Error() + } + + return fmt.Sprintf("%v", value) +} + func (ui *UiLib) OpenQml(path string) { container := NewQmlApplication(path[7:], ui) app := NewExtApplication(container, ui) From 14e4f1283cdde91b38bd8cbf2b2fed69388aa75d Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 14 Aug 2014 10:52:13 +0200 Subject: [PATCH 28/51] Removed old debug method --- ethereal/assets/ext/pre.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/ethereal/assets/ext/pre.js b/ethereal/assets/ext/pre.js index ca520f152..3e8a534e9 100644 --- a/ethereal/assets/ext/pre.js +++ b/ethereal/assets/ext/pre.js @@ -1,18 +1,3 @@ -function debug(/**/) { - var args = arguments; - var msg = "" - for(var i = 0; i < args.length; i++){ - if(typeof args[i] === "object") { - msg += " " + JSON.stringify(args[i]) - } else { - msg += " " + args[i] - } - } - - postData({call:"debug", args:[msg]}) - document.getElementById("debug").innerHTML += "
" + msg -} - // Helper function for generating pseudo callbacks and sending data to the QML part of the application function postData(data, cb) { data._seed = Math.floor(Math.random() * 1000000) @@ -50,9 +35,3 @@ navigator.qt.onmessage = function(ev) { } } } - -window.onerror = function(message, file, lineNumber, column, errorObj) { - debug(file, message, lineNumber+":"+column, errorObj); - - return false; -} From 3fd0337330ab4ceb25abfc7ec04b576e8f3f0914 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 14 Aug 2014 13:26:29 +0200 Subject: [PATCH 29/51] DNS for a hrefs should now be fixed --- ethereal/assets/back.png | Bin 931 -> 1004 bytes ethereal/assets/bug.png | Bin 0 -> 1671 bytes ethereal/assets/ext/home.html | 5 +- ethereal/assets/qml/webapp.qml | 202 +++++++++++++++++---------------- ethereal/gui.go | 4 +- 5 files changed, 112 insertions(+), 99 deletions(-) create mode 100644 ethereal/assets/bug.png diff --git a/ethereal/assets/back.png b/ethereal/assets/back.png index 71486c7c05795d7ee5a3ef0ee77af0c0085b40d9..38fc84d6ea9203583110c91f2eaf7610061dbbb9 100644 GIT binary patch delta 637 zcmV-@0)qXc2kZxGiBL{Q4GJ0x0000DNk~Le0000P0000P2nGNE0L1BqV*mgE1ZP1_ zK>z@;j|==^1poj50drDELIAGL9O(c603c&XQcVB=dL{q>fP?@5`Tzg`fam}Kbua(` z>RI+y?e7jT@qQ9Q9OeF1;)NklYkZW>Ay%h zrx2}ItWOGda{Rx&>=U@nc}Y3H3qW&>txRO7B@5+pxrsf+O^QJv zJxr41lp8k7Sk)$Io+y7_C5oa4@JY^wi6C+wX&E>Rz9f~33N|vya~bWD5abwwOE6Oj4#QNUcqgJ$ zilXjtCJiQ=*fS8CGr`_%x7&NPbIke`IurI9B6qtQ+^B0*SJ!{tt~EIoluVyhjX57x z3>g@oUe+Zu|vD92mM4hwjy2c7VpcZbyaqm40|EIX8A<(kQgG7p+EBN za%uDvxv%g(4nOi0_KDQnlKv;OY8`XJE)LZy8{-2MOjY?(h#+s1PxLGQc)x*aOf~ zKm{eDK_K81EFCnoE1n_zV`eNX4<*4)svT)|5_>#fW-^X92u9LT1yoH=2tU%4{gayO zoZO;<)pZ}ZVCmmbQBd1C#*NM{j^iz*R4+}_7mN*|&TxMlWM30hOBjaxQ4|fKDfA43 zlQPBDo3Wt>3?!&lP&SxURk217R0frd&k=2cBT~b8kuvBTXL}9GB9mHy9!gnZKlu6v zz$J!`!BUwC-b8Z^Y2FnY$$qB-y#!zu>M8^_a8t4j^qn)kgYy~_XJDtG)YVON8vaxz z5oPFr>|lTNm}kCVwIlZJ6ZY>jECK1u^#e8Qa9t&|CPa*H*nW~EC$vBUtOERi`+4Xm zbR0@Ti^N9{IR3l>yoneCQ)bVA%IA~q^9au)D3zMBl~OBDrH)W)irI$_pju4UDEBT@ zzEIL!_wZV0UvJID@${b5*BQ8Mrz=d(iL-;He#v|FjFdenfm7xy45g0u@&Vy^K^a^= znYVCUW@qz?WU;F?a6S&@oy#F{0YE3mBAvwoZ}nJ|irJa(0WKMzwwZAbj=w%Vz_&56 zz>~S|AtCd{FKg=&}F+002ovPDHLkV1l-2 B_F(`3 diff --git a/ethereal/assets/bug.png b/ethereal/assets/bug.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e85dc99c85e6c283ff978a9a8f626d11fd36e1 GIT binary patch literal 1671 zcmY*adt4G$6vpM60#X|GAf1ShY6Xbki@?gTAT`sdOlXLRio8rH5g%zu2{rT4!&j+k zXt7F7GiyFFTbZx=WrUfTMutl(S6h?~2i4kkf4_6@Ip==o`_8%lT=}Mr>#cw=AOHZc zqL4|U#&eO$EM^<`j&T{wc$kSo*ZTp^_u7pc1Iu{wb`byonrAXIKvBs;BVap+8YT`4 z3dFO7d=xWE7|BLS`0++G06>u7jV_-pX2K~=5HXjC2n*T- zUn@*t!#z+qlpDeq2#3Q72~pAbP|~_D<;IAJh!u?0 zw7Wafh(L;x1Y)KHDG)h*Ci#bl#1^pO;*{L6({WHdLP5zIpup9aPFu=W|i0nCwxvkUbhMb(7E}ndzIx(Hrgz79UD9Exw`R((ylt(15(EMTgCP)QZD|FwqKC1%G(E~ovYMpZ*0#~Hfq5i(F(oup8E%EuXL7XmPURt#ik9y_YSEvp#Nc3_^KWA_GCd;FL8ZX;}o=e$M&m zg8Mcp4DSM(ZCNMJbJ{jtDkpW?`na1{Q`+p84vNa?sJ?GF?e112XuBuEf!1|>>-ZXL zjr74iUNLiGe4O&fyLZC@r!HVe!q-(*RS{uFa6b5I0d9FkrmB`VGR9e`^TG^ZJ!XlR z4`oeQpX|TDQH(nOo6pC_P)o@bO_8xf*q8##2U2y!Yy#$T&wHQCt0eQ7H+pTTi z+Y+2@b8~a@GRcA2hpR0uW2S}~;o(=vC;CXUpc8<>Y{pd|aJ^!tYjkw9R4ew|4Vc+= z-T?XCL+dpXuiuhO?pC(r!Pl5s+3EhdE94h%xoCj~Mn@njDTFMYN_3p#I0xQ?Yt1&~ z=$CYLb*&0vSyVP$D7u5wO-!Vebck^30Hyp0tNx8Fm(_8@QD1)gH2dAhk12Pd8UtQ} zv_Eo*U2K=flG2sS-gHJRxcxnXkiG{py=_3J7}eT7b7BXSmAe%6COT^}R-=w%r+cCj z2Tsl1ZB7dh61*-iA(hs(2o*>3<+s`FOBj-jwd85e-uPxGf>X2wJPtV7F(3L}5ISH1TnX1Oy%&k|k z(tc;^g&OG@;hi1Kpyhda7O`m=oufEpSz~$6Du1hnN{=R&T@A$%2VgMRUK)*dpdVyS znPfvV7W}jhYxk%T`odubs-eYY2?d3GAl;=MjgW?jZh1F-fHsp$^Ihe*`hoS0!^>Ju zzR2oh1`SjK49V?3^DL@5iy_xrU;_iw^I B;2{72 literal 0 HcmV?d00001 diff --git a/ethereal/assets/ext/home.html b/ethereal/assets/ext/home.html index 86a659d65..a524e8403 100644 --- a/ethereal/assets/ext/home.html +++ b/ethereal/assets/ext/home.html @@ -8,14 +8,15 @@ h1 { text-align: center; font-family: Courier; font-size: 50pt; - margin-top: 25% }

... Ethereum ...

- + diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index ec2f01741..a0ec52752 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0 import QtQuick.Controls 1.0; +import QtQuick.Controls.Styles 1.0 import QtQuick.Layouts 1.0; import QtQuick.Window 2.1; import Ethereum 1.0 @@ -9,8 +10,8 @@ import Ethereum 1.0 ApplicationWindow { id: window title: "Ethereum" - width: 900 - height: 600 + width: 1000 + height: 800 minimumHeight: 300 property alias url: webview.url @@ -24,29 +25,74 @@ ApplicationWindow { RowLayout { id: navBar + height: 40 anchors { left: parent.left right: parent.right + leftMargin: 7 } Button { id: back - iconSource: "../back.png" onClicked: { webview.goBack() } + style: ButtonStyle { + background: Image { + source: "../back.png" + width: 30 + height: 30 + } + } } TextField { anchors { - top: parent.top left: back.right - right: parent.right + right: toggleInspector.left + leftMargin: 5 + rightMargin: 5 } id: uriNav + y: parent.height / 2 - this.height / 2 Keys.onReturnPressed: { - var uri = this.text; + webview.url = this.text; + } + } + + Button { + id: toggleInspector + anchors { + right: parent.right + } + iconSource: "../bug.png" + onClicked: { + if(inspector.visible == true){ + inspector.visible = false + }else{ + inspector.visible = true + inspector.url = webview.experimental.remoteInspectorUrl + } + } + } + } + + WebView { + objectName: "webView" + id: webview + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + top: navBar.bottom + } + onTitleChanged: { window.title = title } + + property var cleanPath: false + onNavigationRequested: { + if(!this.cleanPath) { + var uri = request.url.toString(); if(!/.*\:\/\/.*/.test(uri)) { uri = "http://" + uri; } @@ -54,7 +100,7 @@ ApplicationWindow { var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ if(reg.test(uri)) { - this.text.replace(reg, function(match, pre, domain, path) { + uri.replace(reg, function(match, pre, domain, path) { uri = pre; var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); @@ -73,23 +119,14 @@ ApplicationWindow { }); } - console.log("connecting to ...", uri) + this.cleanPath = true; webview.url = uri; + } else { + // Prevent inf loop. + this.cleanPath = false; } } - } - - WebView { - objectName: "webView" - id: webview - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - top: navBar.bottom - } - onTitleChanged: { window.title = title } experimental.preferences.javascriptEnabled: true experimental.preferences.navigatorQtObjectEnabled: true experimental.preferences.developerExtrasEnabled: true @@ -102,107 +139,107 @@ ApplicationWindow { try { switch(data.call) { case "getCoinBase": - postData(data._seed, eth.getCoinBase()) + postData(data._seed, eth.getCoinBase()) - break + break case "getIsListening": - postData(data._seed, eth.getIsListening()) + postData(data._seed, eth.getIsListening()) - break + break case "getIsMining": - postData(data._seed, eth.getIsMining()) + postData(data._seed, eth.getIsMining()) - break + break case "getPeerCount": - postData(data._seed, eth.getPeerCount()) + postData(data._seed, eth.getPeerCount()) - break + break case "getTxCountAt": - require(1) - postData(data._seed, eth.getTxCountAt(data.args[0])) + require(1) + postData(data._seed, eth.getTxCountAt(data.args[0])) - break + break case "getBlockByNumber": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) - break + break case "getBlockByHash": - var block = eth.getBlock(data.args[0]) - postData(data._seed, block) + var block = eth.getBlock(data.args[0]) + postData(data._seed, block) - break + break case "transact": - require(5) + 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) + 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 + break case "getStorage": - require(2); + require(2); - var stateObject = eth.getStateObject(data.args[0]) - var storage = stateObject.getStorage(data.args[1]) - postData(data._seed, storage) + var stateObject = eth.getStateObject(data.args[0]) + var storage = stateObject.getStorage(data.args[1]) + postData(data._seed, storage) - break + break case "getStateKeyVals": - require(1); - var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) - postData(data._seed,stateObject) + require(1); + var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) + postData(data._seed,stateObject) - break + break case "getTransactionsFor": - require(1); - var txs = eth.getTransactionsFor(data.args[0], true) - postData(data._seed, txs) + require(1); + var txs = eth.getTransactionsFor(data.args[0], true) + postData(data._seed, txs) - break + break case "getBalance": - require(1); + require(1); - postData(data._seed, eth.getStateObject(data.args[0]).value()); + postData(data._seed, eth.getStateObject(data.args[0]).value()); - break + break case "getKey": - var key = eth.getKey().privateKey; + var key = eth.getKey().privateKey; - postData(data._seed, key) - break + postData(data._seed, key) + break case "watch": - require(1) - eth.watch(data.args[0], data.args[1]); + require(1) + eth.watch(data.args[0], data.args[1]); - break + break case "disconnect": - require(1) - postData(data._seed, null) + require(1) + postData(data._seed, null) - break; + break; case "getSecretToAddress": - require(1) - postData(data._seed, eth.secretToAddress(data.args[0])) + require(1) + postData(data._seed, eth.secretToAddress(data.args[0])) - break; + break; case "debug": - console.log(data.args[0]); + console.log(data.args[0]); break; } } catch(e) { @@ -236,31 +273,6 @@ ApplicationWindow { } } - Rectangle { - id: toggleInspector - color: "#bcbcbc" - visible: true - height: 20 - width: 20 - anchors { - right: root.right - } - MouseArea { - onClicked: { - if(inspector.visible == true){ - inspector.visible = false - }else{ - inspector.visible = true - inspector.url = webview.experimental.remoteInspectorUrl - } - } - - onDoubleClicked: { - webView.reload() - } - anchors.fill: parent - } - } Rectangle { id: sizeGrip diff --git a/ethereal/gui.go b/ethereal/gui.go index 710a1bd1e..d191f67d0 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -391,12 +391,12 @@ func (gui *Gui) update() { if bytes.Compare(tx.Sender(), gui.address()) == 0 { object.SubAmount(tx.Value) - gui.getObjectByName("transactionView").Call("addTx", "post", ethpub.NewPTx(tx), "send") + gui.getObjectByName("transactionView").Call("addTx", 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.getObjectByName("transactionView").Call("addTx", "post", ethpub.NewPTx(tx), "recv") + gui.getObjectByName("transactionView").Call("addTx", ethpub.NewPTx(tx), "recv") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } From 612b631823c0cb80f0e559c533b32b6890349761 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 14 Aug 2014 17:01:21 +0200 Subject: [PATCH 30/51] Json types --- javascript/types.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/javascript/types.go b/javascript/types.go index f9d18b26a..fb1e54ae7 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -39,15 +39,16 @@ func (self *JSBlock) GetTransaction(hash string) otto.Value { } type JSMessage struct { - To, From string - Input string - Output string - Path int - Origin string - Timestamp int32 - Coinbase string - Block string - Number int32 + To string `json:"to"` + From string `json:"from"` + Input string `json:"input"` + Output string `json:"output"` + Path int `json:"path"` + Origin string `json:"origin"` + Timestamp int32 `json:"timestamp"` + Coinbase string `json:"coinbase"` + Block string `json:"block"` + Number int32 `json:"number"` } func NewJSMessage(message *ethstate.Message) JSMessage { @@ -137,6 +138,7 @@ func (self *JSEthereum) Messages(object map[string]interface{}) otto.Value { filter.SetEarliestBlock(earliest) } } + if object["latest"] != nil { latest := object["latest"] if l, ok := latest.(string); ok { @@ -146,10 +148,10 @@ func (self *JSEthereum) Messages(object map[string]interface{}) otto.Value { } } if object["to"] != nil { - filter.SetTo(ethutil.Hex2Bytes(object["to"].(string))) + filter.AddTo(ethutil.Hex2Bytes(object["to"].(string))) } if object["from"] != nil { - filter.SetFrom(ethutil.Hex2Bytes(object["from"].(string))) + filter.AddFrom(ethutil.Hex2Bytes(object["from"].(string))) } if object["max"] != nil { filter.SetMax(object["max"].(int)) From 1fd69e956949671806b23b7ec1feec4f6c416a81 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 14 Aug 2014 17:01:37 +0200 Subject: [PATCH 31/51] Implemented "messages" --- ethereal/assets/ext/ethereum.js | 33 ++++++++++++++++++++ ethereal/assets/qml/wallet.qml | 55 +++++++++++++++++++-------------- ethereal/assets/qml/webapp.qml | 8 +++++ ethereal/ext_app.go | 52 +++++++++++++++++++++++++++++++ ethereal/gui.go | 14 +++++---- 5 files changed, 133 insertions(+), 29 deletions(-) diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index 9970c6379..5b8fb54f3 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -95,6 +95,10 @@ window.eth = { postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb); }, + getMessages: function(filter, cb) { + postData({call: "messages", args: [filter]}, cb); + }, + getStorageAt: function(address, storageAddress, cb) { postData({call: "getStorage", args: [address, storageAddress]}, cb); }, @@ -209,4 +213,33 @@ window.eth = { } window.eth._callbacks = {} window.eth._onCallbacks = {} +function postData(data, cb) { + data._seed = Math.floor(Math.random() * 1000000) + if(cb) { + eth._callbacks[data._seed] = cb; + } + if(data.args === undefined) { + data.args = []; + } + + navigator.qt.postMessage(JSON.stringify(data)); +} + +navigator.qt.onmessage = function(ev) { + var data = JSON.parse(ev.data) + + if(data._event !== undefined) { + eth.trigger(data._event, data.data); + } else { + if(data._seed) { + var cb = eth._callbacks[data._seed]; + if(cb) { + cb.call(this, data.data) + + // Remove the "trigger" callback + delete eth._callbacks[ev._seed]; + } + } + } +} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 769edfc6a..b35500209 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -98,7 +98,7 @@ ApplicationWindow { MenuItem { text: "Debugger" shortcut: "Ctrl+d" - onTriggered: ui.startDebugger() + onTriggered: eth.startDebugger() } MenuItem { @@ -282,9 +282,10 @@ ApplicationWindow { /* Component { id: menuItemTemplate - Rectangle { + + RowLayout { property var view; - property var source; + property alias source: icon.source; property alias title: title.text height: 25 @@ -295,11 +296,9 @@ ApplicationWindow { right: parent.right } - Label { - id: title - y: parent.height / 2 - this.height / 2 - x: 5 - font.pixelSize: 10 + Image { + id: icon + //anchors.horizontalCenter: parent.horizontalCenter } MouseArea { @@ -309,21 +308,32 @@ ApplicationWindow { } } - Image { - id: closeButton - y: parent.height / 2 - this.height / 2 - visible: false - - source: "../close.png" - anchors { - right: parent.right - rightMargin: 5 + Rectangle { + color: "#bbbbbb" + Label { + id: title + y: parent.height / 2 - this.height / 2 + //x: 5 + font.pixelSize: 10 } - MouseArea { - anchors.fill: parent - onClicked: { - console.log("should close") + + 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") + } } } } @@ -331,7 +341,6 @@ ApplicationWindow { } */ - function createMenuItem(icon, view, options) { if(options === undefined) { options = {}; @@ -340,8 +349,8 @@ ApplicationWindow { var comp = menuItemTemplate.createObject(menuColumn) comp.view = view comp.source = icon + //comp.title = options.title /* - comp.title = options.title if(options.canClose) { //comp.closeButton.visible = options.canClose } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index a0ec52752..48b410787 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -238,6 +238,14 @@ ApplicationWindow { break; + case "messages": + require(1); + + var messages = JSON.parse(eth.getMessages(data.args[0])) + postData(data._seed, messages) + + break + case "debug": console.log(data.args[0]); break; diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 697630504..4533b2438 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -1,11 +1,14 @@ package main import ( + "encoding/json" + "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" ) @@ -24,6 +27,7 @@ type AppContainer interface { type ExtApplication struct { *ethpub.PEthereum + eth ethchain.EthManager blockChan chan ethreact.Event changeChan chan ethreact.Event @@ -38,6 +42,7 @@ type ExtApplication struct { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ ethpub.NewPEthereum(lib.eth), + lib.eth, make(chan ethreact.Event, 100), make(chan ethreact.Event, 100), make(chan bool), @@ -130,3 +135,50 @@ func (app *ExtApplication) Watch(addr, storageAddr string) { app.registeredEvents = append(app.registeredEvents, event) } + +func (self *ExtApplication) GetMessages(object map[string]interface{}) string { + filter := ethchain.NewFilter(self.eth) + + 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.AddTo(ethutil.Hex2Bytes(object["to"].(string))) + } + if object["from"] != nil { + filter.AddFrom(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 []javascript.JSMessage + for _, m := range messages { + msgs = append(msgs, javascript.NewJSMessage(m)) + } + + b, err := json.Marshal(msgs) + if err != nil { + return "{\"error\":" + err.Error() + "}" + } + + return string(b) +} diff --git a/ethereal/gui.go b/ethereal/gui.go index d191f67d0..276c1a348 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -515,11 +515,13 @@ func (gui *Gui) Printf(format string, v ...interface{}) { // Print function that logs directly to the GUI func (gui *Gui) printLog(s string) { - str := strings.TrimRight(s, "\n") - lines := strings.Split(str, "\n") + /* + str := strings.TrimRight(s, "\n") + lines := strings.Split(str, "\n") - view := gui.getObjectByName("infoView") - for _, line := range lines { - view.Call("addLog", line) - } + view := gui.getObjectByName("infoView") + for _, line := range lines { + view.Call("addLog", line) + } + */ } From aadc5be3ff9e3818e41d83910b9730e5f1af042e Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 00:24:14 +0200 Subject: [PATCH 32/51] Implemented new watch * Old watch methods have been removed * Implemented new callback handlers for onWatchCb * added new Filter JS object --- ethereal/assets/ext/ethereum.js | 32 +++++++++++++++-- ethereal/assets/ext/test.html | 30 ++++++++++++++++ ethereal/assets/qml/webapp.qml | 18 +++++++--- ethereal/ext_app.go | 63 +++++++++++---------------------- ethereal/html_container.go | 13 +++++++ ethereal/qml_container.go | 8 ++++- 6 files changed, 114 insertions(+), 50 deletions(-) create mode 100644 ethereal/assets/ext/test.html diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index 5b8fb54f3..5891fb9f9 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -137,6 +137,7 @@ window.eth = { postData({call: "getSecretToAddress", args: [sec]}, cb); }, + /* watch: function(address, storageAddrOrCb, cb) { var ev; if(cb === undefined) { @@ -166,6 +167,16 @@ window.eth = { postData({call: "disconnect", args: [address, storageAddrOrCb]}); }, + */ + + watch: function(options) { + var filter = new Filter(options); + filter.number = newWatchNum().toString() + + postData({call: "watch", args: [options, filter.number]}) + + return filter; + }, set: function(props) { postData({call: "set", args: props}); @@ -208,11 +219,28 @@ window.eth = { } } }, - - } + window.eth._callbacks = {} window.eth._onCallbacks = {} + +var Filter = function(options) { + this.options = options; +}; + +Filter.prototype.changed = function(callback) { + eth.on("watched:"+this.number, callback) +} + +Filter.prototype.getMessages = function(cb) { + return eth.getMessages(this.options, cb) +} + +var watchNum = 0; +function newWatchNum() { + return watchNum++; +} + function postData(data, cb) { data._seed = Math.floor(Math.random() * 1000000) if(cb) { diff --git a/ethereal/assets/ext/test.html b/ethereal/assets/ext/test.html new file mode 100644 index 000000000..b605e8dbd --- /dev/null +++ b/ethereal/assets/ext/test.html @@ -0,0 +1,30 @@ + + + +Tests + + + + + + + + + + diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 48b410787..b0f50c8f9 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -220,11 +220,16 @@ ApplicationWindow { postData(data._seed, key) break + /* case "watch": - require(1) - eth.watch(data.args[0], data.args[1]); + require(1) + eth.watch(data.args[0], data.args[1]); - break + break + */ + case "watch": + require(2) + eth.watch(data.args[0], data.args[1]) case "disconnect": require(1) @@ -247,7 +252,7 @@ ApplicationWindow { break case "debug": - console.log(data.args[0]); + console.log(data.args[0]); break; } } catch(e) { @@ -269,6 +274,11 @@ ApplicationWindow { webview.experimental.postMessage(JSON.stringify({data: data, _event: event})) } + function onWatchedCb(data, id) { + var messages = JSON.parse(data) + postEvent("watched:"+id, messages) + } + function onNewBlockCb(block) { postEvent("block:new", block) } diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 4533b2438..627f9e9ca 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethstate" - "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" ) @@ -23,6 +22,7 @@ type AppContainer interface { ObjectChanged(*ethstate.StateObject) StorageChanged(*ethstate.StorageState) NewWatcher(chan bool) + Messages(ethstate.Messages, string) } type ExtApplication struct { @@ -31,9 +31,12 @@ type ExtApplication struct { blockChan chan ethreact.Event changeChan chan ethreact.Event + messageChan chan ethreact.Event quitChan chan bool watcherQuitChan chan bool + filters map[string]*ethchain.Filter + container AppContainer lib *UiLib registeredEvents []string @@ -45,8 +48,10 @@ func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { lib.eth, make(chan ethreact.Event, 100), make(chan ethreact.Event, 100), + make(chan ethreact.Event, 100), make(chan bool), make(chan bool), + make(map[string]*ethchain.Filter), container, lib, nil, @@ -73,6 +78,7 @@ func (app *ExtApplication) run() { // Subscribe to events reactor := app.lib.eth.Reactor() reactor.Subscribe("newBlock", app.blockChan) + reactor.Subscribe("messages", app.messageChan) app.container.NewWatcher(app.watcherQuitChan) @@ -118,56 +124,27 @@ out: } else if storageObject, ok := object.Resource.(*ethstate.StorageState); ok { app.container.StorageChanged(storageObject) } + + case msg := <-app.messageChan: + if messages, ok := msg.Resource.(ethstate.Messages); ok { + for id, filter := range app.filters { + msgs := filter.FilterMessages(messages) + if len(msgs) > 0 { + app.container.Messages(msgs, id) + } + } + } } } } -func (app *ExtApplication) Watch(addr, storageAddr string) { - var event string - if len(storageAddr) == 0 { - event = "object:" + string(ethutil.Hex2Bytes(addr)) - app.lib.eth.Reactor().Subscribe(event, app.changeChan) - } else { - event = "storage:" + string(ethutil.Hex2Bytes(addr)) + ":" + string(ethutil.Hex2Bytes(storageAddr)) - app.lib.eth.Reactor().Subscribe(event, app.changeChan) - } - - app.registeredEvents = append(app.registeredEvents, event) +func (self *ExtApplication) Watch(filterOptions map[string]interface{}, identifier string) { + self.filters[identifier] = ethchain.NewFilterFromMap(filterOptions, self.eth) } func (self *ExtApplication) GetMessages(object map[string]interface{}) string { - filter := ethchain.NewFilter(self.eth) - - 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.AddTo(ethutil.Hex2Bytes(object["to"].(string))) - } - if object["from"] != nil { - filter.AddFrom(ethutil.Hex2Bytes(object["from"].(string))) - } - if object["max"] != nil { - filter.SetMax(object["max"].(int)) - } - if object["skip"] != nil { - filter.SetSkip(object["skip"].(int)) - } + filter := ethchain.NewFilterFromMap(object, self.eth) messages := filter.Find() var msgs []javascript.JSMessage diff --git a/ethereal/html_container.go b/ethereal/html_container.go index 40a9f5584..7deee487d 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "errors" "io/ioutil" "net/url" @@ -12,6 +13,7 @@ import ( "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" "github.com/howeyc/fsnotify" ) @@ -131,6 +133,17 @@ func (app *HtmlApplication) StorageChanged(storageObject *ethstate.StorageState) app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject)) } +func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) { + var msgs []javascript.JSMessage + for _, m := range messages { + msgs = append(msgs, javascript.NewJSMessage(m)) + } + + b, _ := json.Marshal(msgs) + + self.webView.Call("onWatchedCb", string(b), id) +} + func (app *HtmlApplication) Destroy() { app.engine.Destroy() } diff --git a/ethereal/qml_container.go b/ethereal/qml_container.go index 53ff13c2f..45a6c0327 100644 --- a/ethereal/qml_container.go +++ b/ethereal/qml_container.go @@ -1,12 +1,14 @@ package main import ( + "fmt" + "runtime" + "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" - "runtime" ) type QmlApplication struct { @@ -59,6 +61,10 @@ func (app *QmlApplication) StorageChanged(storageObject *ethstate.StorageState) app.win.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject)) } +func (self *QmlApplication) Messages(msgs ethstate.Messages, id string) { + fmt.Println("IMPLEMENT QML APPLICATION MESSAGES METHOD") +} + // Getters func (app *QmlApplication) Engine() *qml.Engine { return app.engine From c362172567e7bd499ea0d0bcf84c54a9b7788614 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 01:07:40 +0200 Subject: [PATCH 33/51] Got rid of warnings and updated storage getters --- ethereal/assets/ext/ethereum.js | 12 ++++++------ ethereal/assets/ext/test.html | 3 +++ ethereal/assets/qml/webapp.qml | 2 +- ethereal/ext_app.go | 2 +- ethereal/gui.go | 2 +- javascript/javascript_runtime.go | 2 +- utils/cmd.go | 21 +++++++++++---------- 7 files changed, 24 insertions(+), 20 deletions(-) diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index 5891fb9f9..03b25179b 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -1,6 +1,8 @@ // Main Ethereum library window.eth = { prototype: Object(), + _callbacks: {}, + _onCallbacks: {}, mutan: function(code) { }, @@ -103,8 +105,8 @@ window.eth = { postData({call: "getStorage", args: [address, storageAddress]}, cb); }, - getStateKeyVals: function(address, cb){ - postData({call: "getStateKeyVals", args: [address]}, cb); + getEachStorageAt: function(address, cb){ + postData({call: "getEachStorage", args: [address]}, cb); }, getKey: function(cb) { @@ -221,17 +223,15 @@ window.eth = { }, } -window.eth._callbacks = {} -window.eth._onCallbacks = {} var Filter = function(options) { this.options = options; }; - Filter.prototype.changed = function(callback) { + // Register the watched:. Qml will call the appropriate event if anything + // interesting happens in the land of Go. eth.on("watched:"+this.number, callback) } - Filter.prototype.getMessages = function(cb) { return eth.getMessages(this.options, cb) } diff --git a/ethereal/assets/ext/test.html b/ethereal/assets/ext/test.html index b605e8dbd..0d6b710fa 100644 --- a/ethereal/assets/ext/test.html +++ b/ethereal/assets/ext/test.html @@ -22,6 +22,9 @@ function test() { console.log("getMessages", messages) }); + eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(a, b) { + console.log(a,b) + }) } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index b0f50c8f9..5faf50e91 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -193,7 +193,7 @@ ApplicationWindow { break - case "getStateKeyVals": + case "getEachStorage": require(1); var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) postData(data._seed,stateObject) diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 627f9e9ca..37e9676ff 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -44,7 +44,7 @@ type ExtApplication struct { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ - ethpub.NewPEthereum(lib.eth), + ethpub.New(lib.eth), lib.eth, make(chan ethreact.Event, 100), make(chan ethreact.Event, 100), diff --git a/ethereal/gui.go b/ethereal/gui.go index 276c1a348..c0584936d 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -57,7 +57,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden panic(err) } - pub := ethpub.NewPEthereum(ethereum) + pub := ethpub.New(ethereum) return &Gui{eth: ethereum, txDb: db, pub: pub, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config} } diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index 158fc93cf..d384c5048 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -50,7 +50,7 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { re := &JSRE{ ethereum, otto.New(), - ethpub.NewPEthereum(ethereum), + ethpub.New(ethereum), make(chan ethreact.Event, 10), make(chan ethreact.Event, 10), make(chan bool), diff --git a/utils/cmd.go b/utils/cmd.go index 0eeb1049e..d34348ad8 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -1,8 +1,17 @@ package utils import ( - "bitbucket.org/kardianos/osext" "fmt" + "io" + "log" + "os" + "os/signal" + "path" + "path/filepath" + "runtime" + "time" + + "bitbucket.org/kardianos/osext" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethdb" @@ -12,14 +21,6 @@ import ( "github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "io" - "log" - "os" - "os/signal" - "path" - "path/filepath" - "runtime" - "time" ) var logger = ethlog.NewLogger("CLI") @@ -227,7 +228,7 @@ func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, Se func StartRpc(ethereum *eth.Ethereum, RpcPort int) { var err error - ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort) + ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.New(ethereum), RpcPort) if err != nil { logger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err) } else { From a6c4543c575b52b943ff54f74c9d650d73175fe0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 13:16:07 +0200 Subject: [PATCH 34/51] Moving over to ethpipe --- ethereal/assets/ext/ethereum.js | 10 +++++ ethereal/assets/ext/test.html | 4 ++ ethereal/assets/qml/views/info.qml | 2 +- ethereal/assets/qml/views/transaction.qml | 2 +- ethereal/assets/qml/webapp.qml | 8 ++-- ethereal/debugger.go | 9 +--- ethereal/ext_app.go | 27 +++--------- ethereal/gui.go | 52 +++++++++-------------- ethereal/html_container.go | 8 ---- ethereal/ui_lib.go | 4 +- javascript/types.go | 32 +------------- utils/cmd.go | 14 ++++++ 12 files changed, 65 insertions(+), 107 deletions(-) diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index 03b25179b..fb8bd23a7 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -4,6 +4,16 @@ window.eth = { _callbacks: {}, _onCallbacks: {}, + test: function() { + var t = undefined; + navigator.qt.onmessage = function(d) { t = d; } + for(;;) { + if(t !== undefined) { + return t + } + } + }, + mutan: function(code) { }, diff --git a/ethereal/assets/ext/test.html b/ethereal/assets/ext/test.html index 0d6b710fa..980001f90 100644 --- a/ethereal/assets/ext/test.html +++ b/ethereal/assets/ext/test.html @@ -25,6 +25,10 @@ function test() { eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(a, b) { console.log(a,b) }) + + eth.getBlock("f70097659f329a09642a27f11338d9269de64f1d4485786e36bfc410832148cd", function(block) { + console.log(block) + }) } diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index fcddd46e2..3335a306a 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -28,7 +28,7 @@ Rectangle { text: "Address" } TextField { - text: pub.getKey().address + text: eth.getKey().address width: 500 } diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index 80e1670f8..ac38ebe0c 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -169,7 +169,7 @@ Rectangle { onClicked: { var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros; var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros; - var res = gui.create(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text) + var res = gui.transact(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/webapp.qml b/ethereal/assets/qml/webapp.qml index 5faf50e91..ac48e32a1 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -165,13 +165,13 @@ ApplicationWindow { break case "getBlockByNumber": - var block = eth.getBlock(data.args[0]) + var block = eth.getBlockByNumber(data.args[0]) postData(data._seed, block) break case "getBlockByHash": - var block = eth.getBlock(data.args[0]) + var block = eth.getBlockByHash(data.args[0]) postData(data._seed, block) break @@ -195,8 +195,8 @@ ApplicationWindow { case "getEachStorage": require(1); - var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true) - postData(data._seed,stateObject) + var storage = eth.getEachStorage(data.args[0]) + postData(data._seed, storage) break diff --git a/ethereal/debugger.go b/ethereal/debugger.go index 1cf5e0b66..56f0b47fc 100644 --- a/ethereal/debugger.go +++ b/ethereal/debugger.go @@ -103,14 +103,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data } }() - data := ethutil.StringToByteFunc(dataStr, func(s string) (ret []byte) { - slice := strings.Split(dataStr, "\n") - for _, dataItem := range slice { - d := ethutil.FormatData(dataItem) - ret = append(ret, d...) - } - return - }) + data := utils.FormatTransactionData(dataStr) var err error script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 37e9676ff..ad826eaa6 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -4,7 +4,7 @@ import ( "encoding/json" "github.com/ethereum/eth-go/ethchain" - "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/javascript" @@ -19,42 +19,36 @@ type AppContainer interface { Engine() *qml.Engine NewBlock(*ethchain.Block) - ObjectChanged(*ethstate.StateObject) - StorageChanged(*ethstate.StorageState) NewWatcher(chan bool) Messages(ethstate.Messages, string) } type ExtApplication struct { - *ethpub.PEthereum + *ethpipe.JSPipe eth ethchain.EthManager blockChan chan ethreact.Event - changeChan chan ethreact.Event messageChan chan ethreact.Event quitChan chan bool watcherQuitChan chan bool filters map[string]*ethchain.Filter - container AppContainer - lib *UiLib - registeredEvents []string + container AppContainer + lib *UiLib } func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ - ethpub.New(lib.eth), + ethpipe.NewJSPipe(lib.eth), lib.eth, make(chan ethreact.Event, 100), make(chan ethreact.Event, 100), - make(chan ethreact.Event, 100), make(chan bool), make(chan bool), make(map[string]*ethchain.Filter), container, lib, - nil, } return app @@ -93,9 +87,6 @@ func (app *ExtApplication) stop() { // Clean up reactor := app.lib.eth.Reactor() reactor.Unsubscribe("newBlock", app.blockChan) - for _, event := range app.registeredEvents { - reactor.Unsubscribe(event, app.changeChan) - } // Kill the main loop app.quitChan <- true @@ -103,7 +94,6 @@ func (app *ExtApplication) stop() { close(app.blockChan) close(app.quitChan) - close(app.changeChan) app.container.Destroy() } @@ -118,13 +108,6 @@ out: if block, ok := block.Resource.(*ethchain.Block); ok { app.container.NewBlock(block) } - case object := <-app.changeChan: - if stateObject, ok := object.Resource.(*ethstate.StateObject); ok { - app.container.ObjectChanged(stateObject) - } else if storageObject, ok := object.Resource.(*ethstate.StorageState); ok { - app.container.StorageChanged(storageObject) - } - case msg := <-app.messageChan: if messages, ok := msg.Resource.(ethstate.Messages); ok { for id, filter := range app.filters { diff --git a/ethereal/gui.go b/ethereal/gui.go index c0584936d..5cc305977 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -14,7 +14,6 @@ import ( "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" "github.com/ethereum/eth-go/ethwire" @@ -39,10 +38,11 @@ type Gui struct { txDb *ethdb.LDBDatabase - pub *ethpub.PEthereum logLevel ethlog.LogLevel open bool + pipe *ethpipe.JSPipe + Session string clientIdentity *ethwire.SimpleClientIdentity config *ethutil.ConfigManager @@ -57,9 +57,9 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden panic(err) } - pub := ethpub.New(ethereum) + pipe := ethpipe.NewJSPipe(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, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config} } func (gui *Gui) Start(assetPath string) { @@ -68,13 +68,12 @@ func (gui *Gui) Start(assetPath string) { // Register ethereum functions qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{ - Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" }, + Init: func(p *ethpipe.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" }, }, { - Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" }, + Init: func(p *ethpipe.JSTransaction, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" }, }, { - Init: func(p *ethpub.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" }, + Init: func(p *ethpipe.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" }, }}) - // Create a new QML engine gui.engine = qml.NewEngine() context := gui.engine.Context() @@ -82,7 +81,6 @@ func (gui *Gui) Start(assetPath string) { // Expose the eth library and the ui library to QML context.SetVar("gui", gui) - context.SetVar("pub", gui.pub) context.SetVar("eth", gui.uiLib) // Load the main QML interface @@ -231,7 +229,7 @@ func (gui *Gui) loadAddressBook() { view := gui.getObjectByName("infoView") view.Call("clearAddress") - nameReg := ethpub.EthereumConfig(gui.eth.StateManager()).NameReg() + nameReg := gui.pipe.World().Config().Get("NameReg") if nameReg != nil { nameReg.EachStorage(func(name string, value *ethutil.Value) { if name[0] != 0 { @@ -255,7 +253,7 @@ func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) { } var ( - ptx = ethpub.NewPTx(tx) + ptx = ethpipe.NewJSTx(tx) send = nameReg.Storage(tx.Sender()) rec = nameReg.Storage(tx.Recipient) s, r string @@ -301,8 +299,9 @@ func (gui *Gui) readPreviousTransactions() { } func (gui *Gui) processBlock(block *ethchain.Block, initial bool) { - name := ethpub.FindNameInNameReg(gui.eth.StateManager(), block.Coinbase) - b := ethpub.NewPBlock(block) + //name := ethpub.FindNameInNameReg(gui.eth.StateManager(), block.Coinbase) + name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00") + b := ethpipe.NewJSBlock(block) b.Name = name gui.getObjectByName("chainView").Call("addBlock", b, initial) @@ -391,12 +390,12 @@ func (gui *Gui) update() { if bytes.Compare(tx.Sender(), gui.address()) == 0 { object.SubAmount(tx.Value) - gui.getObjectByName("transactionView").Call("addTx", ethpub.NewPTx(tx), "send") + gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "send") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } else if bytes.Compare(tx.Recipient, gui.address()) == 0 { object.AddAmount(tx.Value) - gui.getObjectByName("transactionView").Call("addTx", ethpub.NewPTx(tx), "recv") + gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "recv") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } @@ -442,10 +441,9 @@ func (gui *Gui) update() { reactor.Subscribe("miner:start", miningChan) reactor.Subscribe("miner:stop", miningChan) - nameReg := ethpub.EthereumConfig(gui.eth.StateManager()).NameReg() - if nameReg != nil { - reactor.Subscribe("object:"+string(nameReg.Address()), objectChan) - } + nameReg := gui.pipe.World().Config().Get("NameReg") + reactor.Subscribe("object:"+string(nameReg.Address()), objectChan) + reactor.Subscribe("peerList", peerChan) } @@ -453,7 +451,7 @@ func (gui *Gui) setPeerInfo() { gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers)) gui.win.Root().Call("resetPeers") - for _, peer := range gui.pub.GetPeers() { + for _, peer := range gui.pipe.GetPeers() { gui.win.Root().Call("addPeer", peer) } } @@ -466,18 +464,10 @@ func (gui *Gui) address() []byte { return gui.eth.KeyManager().Address() } -func (gui *Gui) RegisterName(name string) { - name = fmt.Sprintf("\"register\"\n\"%s\"", name) +func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) { + data := ethutil.Bytes2Hex(utils.FormatTransactionData(d)) - gui.pub.Transact(gui.privateKey(), "NameReg", "", "10000", "10000000000000", name) -} - -func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) { - return gui.pub.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) -} - -func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) { - return gui.pub.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) + return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) } func (gui *Gui) SetCustomIdentifier(customIdentifier string) { diff --git a/ethereal/html_container.go b/ethereal/html_container.go index 7deee487d..bbf77402e 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -125,14 +125,6 @@ func (app *HtmlApplication) NewBlock(block *ethchain.Block) { app.webView.Call("onNewBlockCb", b) } -func (app *HtmlApplication) ObjectChanged(stateObject *ethstate.StateObject) { - app.webView.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject)) -} - -func (app *HtmlApplication) StorageChanged(storageObject *ethstate.StorageState) { - app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject)) -} - func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) { var msgs []javascript.JSMessage for _, m := range messages { diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index f900fcaee..c48a06547 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" "github.com/go-qml/qml" @@ -18,6 +19,7 @@ type memAddr struct { // UI Library that has some basic functionality exposed type UiLib struct { + *ethpipe.JSPipe engine *qml.Engine eth *eth.Ethereum connected bool @@ -31,7 +33,7 @@ type UiLib struct { } func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { - return &UiLib{engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth)} + return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth)} } func (self *UiLib) ImportTx(rlpTx string) { diff --git a/javascript/types.go b/javascript/types.go index fb1e54ae7..375e7b24c 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -128,37 +128,7 @@ func (self *JSEthereum) toVal(v interface{}) otto.Value { } 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.AddTo(ethutil.Hex2Bytes(object["to"].(string))) - } - if object["from"] != nil { - filter.AddFrom(ethutil.Hex2Bytes(object["from"].(string))) - } - if object["max"] != nil { - filter.SetMax(object["max"].(int)) - } - if object["skip"] != nil { - filter.SetSkip(object["skip"].(int)) - } + filter := ethchain.NewFilterFromMap(object, self.ethereum) messages := filter.Find() var msgs []JSMessage diff --git a/utils/cmd.go b/utils/cmd.go index d34348ad8..58e3eed1e 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -8,6 +8,7 @@ import ( "os/signal" "path" "path/filepath" + "regexp" "runtime" "time" @@ -267,6 +268,19 @@ func StartMining(ethereum *eth.Ethereum) bool { return false } +func FormatTransactionData(data string) []byte { + d := ethutil.StringToByteFunc(data, func(s string) (ret []byte) { + slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000) + for _, dataItem := range slice { + d := ethutil.FormatData(dataItem) + ret = append(ret, d...) + } + return + }) + + return d +} + func StopMining(ethereum *eth.Ethereum) bool { if ethereum.Mining && miner != nil { miner.Stop() From 9f4886839fbd3cf4872d721e5be1c1d61bbb64e7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 13:27:43 +0200 Subject: [PATCH 35/51] Moved over to hardcoded v1 of Go QML --- ethereal/debugger.go | 2 +- ethereal/ext_app.go | 2 +- ethereal/gui.go | 2 +- ethereal/html_container.go | 2 +- ethereal/main.go | 41 +++++++++++++++++++++++--------------- ethereal/qml_container.go | 2 +- ethereal/ui_lib.go | 2 +- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/ethereal/debugger.go b/ethereal/debugger.go index 56f0b47fc..b7a7c8c02 100644 --- a/ethereal/debugger.go +++ b/ethereal/debugger.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethvm" "github.com/ethereum/go-ethereum/utils" - "github.com/go-qml/qml" + "gopkg.in/qml.v1" ) type DebuggerWindow struct { diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index ad826eaa6..5543b0083 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/javascript" - "github.com/go-qml/qml" + "gopkg.in/qml.v1" ) type AppContainer interface { diff --git a/ethereal/gui.go b/ethereal/gui.go index 5cc305977..066db48b9 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -18,7 +18,7 @@ import ( "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "github.com/ethereum/go-ethereum/utils" - "github.com/go-qml/qml" + "gopkg.in/qml.v1" ) var logger = ethlog.NewLogger("GUI") diff --git a/ethereal/html_container.go b/ethereal/html_container.go index bbf77402e..172fff08e 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -14,8 +14,8 @@ import ( "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" - "github.com/go-qml/qml" "github.com/howeyc/fsnotify" + "gopkg.in/qml.v1" ) type HtmlApplication struct { diff --git a/ethereal/main.go b/ethereal/main.go index 431307c6f..393308d00 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -4,9 +4,10 @@ import ( "os" "runtime" + "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/utils" - "github.com/go-qml/qml" + "gopkg.in/qml.v1" ) const ( @@ -14,21 +15,9 @@ const ( Version = "0.6.1" ) -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) - - // This is a bit of a cheat, but ey! - os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999") - - qml.Init(nil) - - var interrupted = false - utils.RegisterInterrupt(func(os.Signal) { - interrupted = true - }) - - utils.HandleInterrupt() +var ethereum *eth.Ethereum +func run() error { // precedence: code-internal flag default < config file < environment variables < command line Init() // parsing command line @@ -47,7 +36,7 @@ func main() { clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier) - ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer) + ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer) if ShowGenesis { utils.ShowGenesis(ethereum) @@ -65,6 +54,26 @@ func main() { utils.StartEthereum(ethereum, UseSeed) // gui blocks the main thread gui.Start(AssetPath) + + return nil +} + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + + // This is a bit of a cheat, but ey! + os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999") + + //qml.Init(nil) + qml.Run(run) + + var interrupted = false + utils.RegisterInterrupt(func(os.Signal) { + interrupted = true + }) + + utils.HandleInterrupt() + // we need to run the interrupt callbacks in case gui is closed // this skips if we got here by actual interrupt stopping the GUI if !interrupted { diff --git a/ethereal/qml_container.go b/ethereal/qml_container.go index 45a6c0327..babb9ceab 100644 --- a/ethereal/qml_container.go +++ b/ethereal/qml_container.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" - "github.com/go-qml/qml" + "gopkg.in/qml.v1" ) type QmlApplication struct { diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index c48a06547..9220581cd 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" - "github.com/go-qml/qml" + "gopkg.in/qml.v1" ) type memAddr struct { From 203c4b99a0c5e3b826f5a131af2d7ec035b73107 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 13:45:34 +0200 Subject: [PATCH 36/51] LookupDomain method to uilib --- ethereal/assets/qml/webapp.qml | 2 +- ethereal/main.go | 2 +- ethereal/ui_lib.go | 28 ++++++++++++++++++++++++++++ ethereum/main.go | 2 +- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index ac48e32a1..7bcdc1610 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -103,7 +103,7 @@ ApplicationWindow { uri.replace(reg, function(match, pre, domain, path) { uri = pre; - var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); + var lookup = ui.lookupDomain(domain.substring(0, domain.length - 4)); var ip = []; for(var i = 0, l = lookup.length; i < l; i++) { ip.push(lookup.charCodeAt(i)) diff --git a/ethereal/main.go b/ethereal/main.go index 393308d00..47f6144fa 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -12,7 +12,7 @@ import ( const ( ClientIdentifier = "Ethereal" - Version = "0.6.1" + Version = "0.6.2" ) var ethereum *eth.Ethereum diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 9220581cd..ade9bf381 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -1,11 +1,15 @@ package main import ( + "bytes" "fmt" "path" + "strconv" + "strings" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" @@ -36,6 +40,30 @@ func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth)} } +func (self *UiLib) LookupDomain(domain string) string { + world := self.World() + + if len(domain) > 32 { + domain = string(ethcrypto.Sha3Bin([]byte(domain))) + } + data := world.Config().Get("DnsReg").StorageString(domain).Bytes() + + // Left padded = A record, Right padded = CNAME + if data[0] == 0 { + data = bytes.TrimLeft(data, "\x00") + var ipSlice []string + for _, d := range data { + ipSlice = append(ipSlice, strconv.Itoa(int(d))) + } + + return strings.Join(ipSlice, ".") + } else { + data = bytes.TrimRight(data, "\x00") + + return string(data) + } +} + func (self *UiLib) ImportTx(rlpTx string) { tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx)) self.eth.TxPool().QueueTransaction(tx) diff --git a/ethereum/main.go b/ethereum/main.go index 17838997c..6c5c71bf3 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -13,7 +13,7 @@ import ( const ( ClientIdentifier = "Ethereum(G)" - Version = "0.6.1" + Version = "0.6.2" ) var logger = ethlog.NewLogger("CLI") From c2c8757bd1e6c03254ca5888077b25a8902cfa0e Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 16:19:30 +0200 Subject: [PATCH 37/51] Added message to closure --- ethereal/debugger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereal/debugger.go b/ethereal/debugger.go index b7a7c8c02..7bc544377 100644 --- a/ethereal/debugger.go +++ b/ethereal/debugger.go @@ -134,7 +134,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data block := self.lib.eth.BlockChain().CurrentBlock - callerClosure := ethvm.NewClosure(account, contract, script, gas, gasPrice) + callerClosure := ethvm.NewClosure(ðstate.Message{}, account, contract, script, gas, gasPrice) env := utils.NewEnv(state, block, account.Address(), value) vm := ethvm.New(env) vm.Verbose = true From 8f1b461228a8e1cf81762c81037f879300f4989e Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 15 Aug 2014 16:26:49 +0200 Subject: [PATCH 38/51] Corrected getEachStorageAt --- ethereal/assets/ext/test.html | 7 +++++-- ethereal/assets/qml/webapp.qml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ethereal/assets/ext/test.html b/ethereal/assets/ext/test.html index 980001f90..629e98377 100644 --- a/ethereal/assets/ext/test.html +++ b/ethereal/assets/ext/test.html @@ -12,6 +12,7 @@ function test() { var filter = eth.watch({ latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead", + altered: ["aabb", {id: "eeff", "at": "aabb"}], }); filter.changed(function(messages) { @@ -22,8 +23,10 @@ function test() { console.log("getMessages", messages) }); - eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(a, b) { - console.log(a,b) + eth.getEachStorageAt("9ef0f0d81e040012600b0c1abdef7c48f720f88a", function(entries) { + for(var i = 0; i < entries.length; i++) { + console.log(entries[i].key, " : ", entries[i].value) + } }) eth.getBlock("f70097659f329a09642a27f11338d9269de64f1d4485786e36bfc410832148cd", function(block) { diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 7bcdc1610..e6d0f5c14 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -195,7 +195,7 @@ ApplicationWindow { case "getEachStorage": require(1); - var storage = eth.getEachStorage(data.args[0]) + var storage = JSON.parse(eth.getEachStorage(data.args[0])) postData(data._seed, storage) break From 2eab964a00b998068f49b088949730f4896e256c Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 16 Aug 2014 01:38:24 +0200 Subject: [PATCH 39/51] Switched over to ethpipe --- ethereal/html_container.go | 4 ++-- ethereal/qml_container.go | 12 ++---------- javascript/javascript_runtime.go | 20 ++++---------------- javascript/types.go | 28 ++++++++++++---------------- utils/cmd.go | 4 ++-- 5 files changed, 22 insertions(+), 46 deletions(-) diff --git a/ethereal/html_container.go b/ethereal/html_container.go index 172fff08e..e682dc3eb 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -10,7 +10,7 @@ import ( "path/filepath" "github.com/ethereum/eth-go/ethchain" - "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" @@ -121,7 +121,7 @@ func (app *HtmlApplication) Window() *qml.Window { } func (app *HtmlApplication) NewBlock(block *ethchain.Block) { - b := ðpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} + b := ðpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} app.webView.Call("onNewBlockCb", b) } diff --git a/ethereal/qml_container.go b/ethereal/qml_container.go index babb9ceab..8c9ea0a8f 100644 --- a/ethereal/qml_container.go +++ b/ethereal/qml_container.go @@ -5,7 +5,7 @@ import ( "runtime" "github.com/ethereum/eth-go/ethchain" - "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "gopkg.in/qml.v1" @@ -49,18 +49,10 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) { // Events func (app *QmlApplication) NewBlock(block *ethchain.Block) { - pblock := ðpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} + pblock := ðpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} app.win.Call("onNewBlockCb", pblock) } -func (app *QmlApplication) ObjectChanged(stateObject *ethstate.StateObject) { - app.win.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject)) -} - -func (app *QmlApplication) StorageChanged(storageObject *ethstate.StorageState) { - app.win.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject)) -} - func (self *QmlApplication) Messages(msgs ethstate.Messages, id string) { fmt.Println("IMPLEMENT QML APPLICATION MESSAGES METHOD") } diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index d384c5048..c794c32a8 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethlog" - "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" @@ -23,7 +23,7 @@ var jsrelogger = ethlog.NewLogger("JSRE") type JSRE struct { ethereum *eth.Ethereum Vm *otto.Otto - lib *ethpub.PEthereum + pipe *ethpipe.JSPipe blockChan chan ethreact.Event changeChan chan ethreact.Event @@ -50,7 +50,7 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { re := &JSRE{ ethereum, otto.New(), - ethpub.New(ethereum), + ethpipe.NewJSPipe(ethereum), make(chan ethreact.Event, 10), make(chan ethreact.Event, 10), make(chan bool), @@ -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, ethereum}) + re.Bind("eth", &JSEthereum{re.pipe, re.Vm, ethereum}) re.initStdFuncs() @@ -123,18 +123,6 @@ out: case block := <-self.blockChan: if _, ok := block.Resource.(*ethchain.Block); ok { } - 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)) - 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)) - cb.Call(cb, val) - } - } } } } diff --git a/javascript/types.go b/javascript/types.go index 375e7b24c..3a11e644b 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -5,20 +5,20 @@ import ( "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" - "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/otto" ) type JSStateObject struct { - *ethpub.PStateObject + *ethpipe.JSObject eth *JSEthereum } func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value { cb := call.Argument(0) - self.PStateObject.EachStorage(func(key string, value *ethutil.Value) { + self.JSObject.EachStorage(func(key string, value *ethutil.Value) { value.Decode() cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes()))) @@ -30,12 +30,12 @@ func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value { // The JSEthereum object attempts to wrap the PEthereum object and returns // meaningful javascript objects type JSBlock struct { - *ethpub.PBlock + *ethpipe.JSBlock eth *JSEthereum } func (self *JSBlock) GetTransaction(hash string) otto.Value { - return self.eth.toVal(self.PBlock.GetTransaction(hash)) + return self.eth.toVal(self.JSBlock.GetTransaction(hash)) } type JSMessage struct { @@ -67,33 +67,29 @@ func NewJSMessage(message *ethstate.Message) JSMessage { } type JSEthereum struct { - *ethpub.PEthereum + *ethpipe.JSPipe vm *otto.Otto ethereum *eth.Ethereum } func (self *JSEthereum) GetBlock(hash string) otto.Value { - return self.toVal(&JSBlock{self.PEthereum.GetBlock(hash), self}) + return self.toVal(&JSBlock{self.JSPipe.GetBlockByHash(hash), self}) } func (self *JSEthereum) GetPeers() otto.Value { - return self.toVal(self.PEthereum.GetPeers()) + return self.toVal(self.JSPipe.GetPeers()) } func (self *JSEthereum) GetKey() otto.Value { - return self.toVal(self.PEthereum.GetKey()) + return self.toVal(self.JSPipe.GetKey()) } func (self *JSEthereum) GetStateObject(addr string) otto.Value { - return self.toVal(&JSStateObject{self.PEthereum.GetStateObject(addr), self}) -} - -func (self *JSEthereum) GetStateKeyVals(addr string) otto.Value { - return self.toVal(self.PEthereum.GetStateObject(addr).StateKeyVal(false)) + return self.toVal(&JSStateObject{ethpipe.NewJSObject(self.JSPipe.World().SafeGet(ethutil.Hex2Bytes(addr))), self}) } func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { - r, err := self.PEthereum.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) + r, err := self.JSPipe.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) if err != nil { fmt.Println(err) @@ -104,7 +100,7 @@ func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, } func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, scriptStr string) otto.Value { - r, err := self.PEthereum.Create(key, valueStr, gasStr, gasPriceStr, scriptStr) + r, err := self.JSPipe.Transact(key, "", valueStr, gasStr, gasPriceStr, scriptStr) if err != nil { fmt.Println(err) diff --git a/utils/cmd.go b/utils/cmd.go index d417d51ca..cda735c27 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -18,7 +18,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/ethpub" + "github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" @@ -228,7 +228,7 @@ func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, Se func StartRpc(ethereum *eth.Ethereum, RpcPort int) { var err error - ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.New(ethereum), RpcPort) + ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpipe.NewJSPipe(ethereum), RpcPort) if err != nil { logger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err) } else { From 27735bbdfc4e32e2e5ca11f48591d62b766aa3f5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 17 Aug 2014 12:41:23 +0200 Subject: [PATCH 40/51] State dumps from gui --- ethereal/assets/ext/ethereum.js | 16 +++++++-- ethereal/assets/ext/test.html | 4 +++ ethereal/assets/qml/views/chain.qml | 54 +++++++++++++++++++++++++++-- ethereal/assets/qml/wallet.qml | 46 +++++++++++++++--------- ethereal/assets/qml/webapp.qml | 18 ++++++++-- ethereal/ext_app.go | 1 + ethereal/gui.go | 35 +++++++++++++++++++ ethereal/html_container.go | 6 ++++ ethereal/qml_container.go | 2 ++ ethereum/main.go | 2 +- 10 files changed, 158 insertions(+), 26 deletions(-) diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index fb8bd23a7..697a404a3 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -6,7 +6,8 @@ window.eth = { test: function() { var t = undefined; - navigator.qt.onmessage = function(d) { t = d; } + postData({call: "test"}) + navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; } for(;;) { if(t !== undefined) { return t @@ -14,7 +15,8 @@ window.eth = { } }, - mutan: function(code) { + mutan: function(code, cb) { + postData({call: "mutan", args: [code]}, cb) }, toHex: function(str) { @@ -281,3 +283,13 @@ navigator.qt.onmessage = function(ev) { } } } + +eth.on("chain:changed", function() { +}) + +eth.on("messages", { /* filters */}, function(messages){ +}) + +eth.on("pending:changed", function() { +}) + diff --git a/ethereal/assets/ext/test.html b/ethereal/assets/ext/test.html index 629e98377..4bac7d36f 100644 --- a/ethereal/assets/ext/test.html +++ b/ethereal/assets/ext/test.html @@ -32,6 +32,10 @@ function test() { eth.getBlock("f70097659f329a09642a27f11338d9269de64f1d4485786e36bfc410832148cd", function(block) { console.log(block) }) + + eth.mutan("var a = 10", function(code) { + console.log("code", code) + }); } diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index 2b968d56c..0f5604a9f 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -25,9 +25,57 @@ Rectangle { model: blockModel - onDoubleClicked: { - popup.visible = true - popup.setDetails(blockModel.get(row)) + itemDelegate: Item { + Text { + anchors { + left: parent.left + right: parent.right + leftMargin: 10 + verticalCenter: parent.verticalCenter + } + color: styleData.textColor + elide: styleData.elideMode + text: styleData.value + font.pixelSize: 11 + MouseArea { + acceptedButtons: Qt.RightButton + propagateComposedEvents: true + anchors.fill: parent + onClicked: { + blockTable.selection.clear() + blockTable.selection.select(styleData.row) + + contextMenu.row = styleData.row; + contextMenu.popup() + } + } + } + + } + + Menu { + id: contextMenu + property var row; + MenuItem { + text: "Details" + onTriggered: { + popup.visible = true + popup.setDetails(blockModel.get(this.row)) + } + } + + MenuSeparator{} + + MenuItem { + text: "Dump State" + onTriggered: { + generalFileDialog.show(false, function(path) { + var hash = blockModel.get(this.row).hash; + + gui.dumpState(hash, path); + }); + } + } } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index b35500209..30e1071f7 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -49,8 +49,7 @@ ApplicationWindow { text: "Import App" shortcut: "Ctrl+o" onTriggered: { - generalFileDialog.callback = importApp; - generalFileDialog.open() + generalFileDialog.show(true, importApp) } } @@ -62,10 +61,9 @@ ApplicationWindow { MenuItem { text: "Add plugin" onTriggered: { - generalFileDialog.callback = function(path) { + generalFileDialog.show(true, function(path) { addPlugin(path, {canClose: true}) - } - generalFileDialog.open() + }) } } @@ -75,10 +73,9 @@ ApplicationWindow { text: "Import key" shortcut: "Ctrl+i" onTriggered: { - generalFileDialog.callback = function(path) { - ui.importKey(path) - } - generalFileDialog.open() + generalFileDialog.show(true, function(path) { + gui.importKey(path) + }) } } @@ -86,9 +83,8 @@ ApplicationWindow { text: "Export keys" shortcut: "Ctrl+e" onTriggered: { - generalFileDialog.callback = function(path) { - } - generalFileDialog.open() + generalFileDialog.show(false, function(path) { + }) } } } @@ -111,10 +107,19 @@ ApplicationWindow { MenuItem { text: "Run JS file" onTriggered: { - generalFileDialog.callback = function(path) { + generalFileDialog.show(true, function(path) { eth.evalJavascriptFile(path) - } - generalFileDialog.open() + }) + } + } + + MenuItem { + text: "Dump state" + onTriggered: { + generalFileDialog.show(false, function(path) { + // Empty hash for latest + gui.dumpState("", path) + }) } } } @@ -396,8 +401,15 @@ ApplicationWindow { id: generalFileDialog property var callback; onAccepted: { - var path = this.fileUrl.toString() - callback.call(this, path) + var path = this.fileUrl.toString(); + callback.call(this, path); + } + + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; + + this.open(); } } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index e6d0f5c14..22e404cc8 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -78,6 +78,7 @@ ApplicationWindow { } } + WebView { objectName: "webView" id: webview @@ -127,6 +128,8 @@ ApplicationWindow { this.cleanPath = false; } } + + experimental.preferences.javascriptEnabled: true experimental.preferences.navigatorQtObjectEnabled: true experimental.preferences.developerExtrasEnabled: true @@ -251,9 +254,13 @@ ApplicationWindow { break - case "debug": - console.log(data.args[0]); - break; + case "mutan": + require(1) + + var code = eth.compileMutan(data.args[0]) + postData(data._seed, "0x"+code) + + break; } } catch(e) { console.log(data.call + ": " + e) @@ -262,6 +269,11 @@ ApplicationWindow { } } + function post(seed, data) { + console.log("data", data) + postData(data._seed, data) + } + function require(args, num) { if(args.length < num) { throw("required argument count of "+num+" got "+args.length); diff --git a/ethereal/ext_app.go b/ethereal/ext_app.go index 5543b0083..514084c97 100644 --- a/ethereal/ext_app.go +++ b/ethereal/ext_app.go @@ -21,6 +21,7 @@ type AppContainer interface { NewBlock(*ethchain.Block) NewWatcher(chan bool) Messages(ethstate.Messages, string) + Post(string, int) } type ExtApplication struct { diff --git a/ethereal/gui.go b/ethereal/gui.go index 066db48b9..6149b39b7 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "math/big" + "os" "strconv" "strings" "time" @@ -155,6 +156,40 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { return gui.win, nil } +func (self *Gui) DumpState(hash, path string) { + var stateDump []byte + + if len(hash) == 0 { + stateDump = self.eth.StateManager().CurrentState().Dump() + } else { + var block *ethchain.Block + if hash[0] == '#' { + i, _ := strconv.Atoi(hash[1:]) + block = self.eth.BlockChain().GetBlockByNumber(uint64(i)) + } else { + block = self.eth.BlockChain().GetBlock(ethutil.Hex2Bytes(hash)) + } + + if block == nil { + logger.Infof("block err: not found %s\n", hash) + return + } + + stateDump = block.State().Dump() + } + + file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm) + if err != nil { + logger.Infoln("dump err: ", err) + return + } + defer file.Close() + + logger.Infof("dumped state (%s) to %s\n", hash, path) + + file.Write(stateDump) +} + // The done handler will be called by QML when all views have been loaded func (gui *Gui) Done() { gui.qmlDone = true diff --git a/ethereal/html_container.go b/ethereal/html_container.go index e682dc3eb..69edea570 100644 --- a/ethereal/html_container.go +++ b/ethereal/html_container.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "errors" + "fmt" "io/ioutil" "net/url" "os" @@ -139,3 +140,8 @@ func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) { func (app *HtmlApplication) Destroy() { app.engine.Destroy() } + +func (app *HtmlApplication) Post(data string, seed int) { + fmt.Println("about to call 'post'") + app.webView.Call("post", seed, data) +} diff --git a/ethereal/qml_container.go b/ethereal/qml_container.go index 8c9ea0a8f..85bd7c699 100644 --- a/ethereal/qml_container.go +++ b/ethereal/qml_container.go @@ -64,3 +64,5 @@ func (app *QmlApplication) Engine() *qml.Engine { func (app *QmlApplication) Window() *qml.Window { return app.win } + +func (app *QmlApplication) Post(data string, s int) {} diff --git a/ethereum/main.go b/ethereum/main.go index 6c5c71bf3..070d3c201 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -71,7 +71,7 @@ func main() { } // Leave the Println. This needs clean output for piping - fmt.Println(block.State().Dump()) + fmt.Printf("%s\n", block.State().Dump()) os.Exit(0) } From e12abfd43b99e05a05957922f01e4f5460d286fb Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 17 Aug 2014 13:05:43 +0200 Subject: [PATCH 41/51] Double click --- ethereal/assets/qml/views/chain.qml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index 0f5604a9f..270018eb2 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -38,15 +38,22 @@ Rectangle { text: styleData.value font.pixelSize: 11 MouseArea { - acceptedButtons: Qt.RightButton + acceptedButtons: Qt.LeftButton | Qt.RightButton propagateComposedEvents: true anchors.fill: parent onClicked: { blockTable.selection.clear() blockTable.selection.select(styleData.row) - contextMenu.row = styleData.row; - contextMenu.popup() + if(mouse.button == Qt.RightButton) { + contextMenu.row = styleData.row; + contextMenu.popup() + } + } + + onDoubleClicked: { + popup.visible = true + popup.setDetails(blockModel.get(styleData.row)) } } } From 88655439a017ea74041d022a9f23a919d27f51e2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 17 Aug 2014 13:49:46 +0200 Subject: [PATCH 42/51] Copy to clipboard hax --- ethereal/assets/qml/views/chain.qml | 7 +++++ ethereal/assets/qml/views/info.qml | 41 +++++++++++++++++++++++++++++ ethereal/assets/qml/wallet.qml | 11 ++++++++ ethereal/gui.go | 5 ++++ 4 files changed, 64 insertions(+) diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index 270018eb2..9fbc02954 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -73,6 +73,13 @@ Rectangle { MenuSeparator{} + MenuItem { + text: "Copy" + onTriggered: { + copyToClipboard(blockModel.get(this.row).hash) + } + } + MenuItem { text: "Dump State" onTriggered: { diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index 3335a306a..60b6a62ec 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -57,6 +57,47 @@ Rectangle { TableViewColumn{ role: "address"; title: "address"; width: 300} model: addressModel + itemDelegate: Item { + Text { + anchors { + left: parent.left + right: parent.right + leftMargin: 10 + verticalCenter: parent.verticalCenter + } + color: styleData.textColor + elide: styleData.elideMode + text: styleData.value + font.pixelSize: 11 + MouseArea { + acceptedButtons: Qt.LeftButton | Qt.RightButton + propagateComposedEvents: true + anchors.fill: parent + onClicked: { + addressView.selection.clear() + addressView.selection.select(styleData.row) + + if(mouse.button == Qt.RightButton) { + contextMenu.row = styleData.row; + contextMenu.popup() + } + } + } + } + + } + + Menu { + id: contextMenu + property var row; + + MenuItem { + text: "Copy" + onTriggered: { + copyToClipboard(addressModel.get(this.row).address) + } + } + } } property var logModel: ListModel { diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 30e1071f7..45514f7c1 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -17,6 +17,17 @@ ApplicationWindow { title: "Ethereal" + TextField { + id: copyElementHax + visible: false + } + + function copyToClipboard(text) { + copyElementHax.text = text + copyElementHax.selectAll() + copyElementHax.copy() + } + // Takes care of loading all default plugins Component.onCompleted: { var historyView = addPlugin("./views/history.qml", {title: "History"}) diff --git a/ethereal/gui.go b/ethereal/gui.go index 6149b39b7..7a36a8b02 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -482,6 +482,11 @@ func (gui *Gui) update() { reactor.Subscribe("peerList", peerChan) } +func (gui *Gui) CopyToClipboard(data string) { + //clipboard.WriteAll("test") + fmt.Println("COPY currently BUGGED. Here are the contents:\n", data) +} + func (gui *Gui) setPeerInfo() { gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers)) From 5ae3deea86a4916eee07b30195da280957e2fd2f Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 17 Aug 2014 21:07:09 +0200 Subject: [PATCH 43/51] New menu --- ethereal/assets/qml/views/chain.qml | 7 + ethereal/assets/qml/views/history.qml | 1 + ethereal/assets/qml/views/info.qml | 1 + ethereal/assets/qml/views/javascript.qml | 2 +- ethereal/assets/qml/views/pending_tx.qml | 1 + ethereal/assets/qml/views/transaction.qml | 1 + ethereal/assets/qml/wallet.qml | 224 +++++++++++++++------- 7 files changed, 163 insertions(+), 74 deletions(-) diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index 9fbc02954..b94d9edca 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -7,8 +7,11 @@ import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 Rectangle { + id: root property var title: "Network" property var iconFile: "../net.png" + property var secondary: "Hi" + property var menuItem objectName: "chainView" visible: false @@ -93,6 +96,8 @@ Rectangle { } } + + function addBlock(block, initial) { var txs = JSON.parse(block.transactions); var amount = 0 @@ -109,6 +114,8 @@ Rectangle { } 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)}) } + + //root.secondary.text = "#" + block.number; } Window { diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml index a73b7367d..94ea29e61 100644 --- a/ethereal/assets/qml/views/history.qml +++ b/ethereal/assets/qml/views/history.qml @@ -9,6 +9,7 @@ import Ethereum 1.0 Rectangle { property var iconFile: "../tx.png" property var title: "Transactions" + property var menuItem property var txModel: ListModel { id: txModel diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index 60b6a62ec..c12977cbe 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -9,6 +9,7 @@ import Ethereum 1.0 Rectangle { property var title: "Information" property var iconFile: "../heart.png" + property var menuItem objectName: "infoView" visible: false diff --git a/ethereal/assets/qml/views/javascript.qml b/ethereal/assets/qml/views/javascript.qml index 376397130..aa5f93547 100644 --- a/ethereal/assets/qml/views/javascript.qml +++ b/ethereal/assets/qml/views/javascript.qml @@ -9,6 +9,7 @@ import Ethereum 1.0 Rectangle { property var title: "JavaScript" property var iconFile: "../tx.png" + property var menuItem objectName: "javascriptView" visible: false @@ -33,7 +34,6 @@ Rectangle { TextArea { id: output - verticalAlignment: TextEdit.AlignBottom text: "> JSRE Ready..." anchors { top: parent.top diff --git a/ethereal/assets/qml/views/pending_tx.qml b/ethereal/assets/qml/views/pending_tx.qml index 5c5c496d6..8e3690cb9 100644 --- a/ethereal/assets/qml/views/pending_tx.qml +++ b/ethereal/assets/qml/views/pending_tx.qml @@ -9,6 +9,7 @@ import Ethereum 1.0 Rectangle { property var title: "Pending Transactions" property var iconFile: "../tx.png" + property var menuItem objectName: "pendingTxView" anchors.fill: parent diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index ac38ebe0c..640e02a4e 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -9,6 +9,7 @@ import Ethereum 1.0 Rectangle { property var iconFile: "../new.png" property var title: "New transaction" + property var menuItem objectName: "newTxView" visible: false diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 45514f7c1..d1039518b 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -30,12 +30,12 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { - 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}) - var pendingTxView = addPlugin("./views/javascript.qml", {title: "JavaScript", canClose: true}) + var historyView = addPlugin("./views/history.qml", {default: true}) + var newTxView = addPlugin("./views/transaction.qml", {default: true}) + var chainView = addPlugin("./views/chain.qml", {default: true}) + var infoView = addPlugin("./views/info.qml", {default: true}) + var pendingTxView = addPlugin("./views/pending_tx.qml", {default: true}) + var pendingTxView = addPlugin("./views/javascript.qml", {default: true}) // Call the ready handler gui.done() @@ -250,12 +250,17 @@ ApplicationWindow { anchors.fill: parent resizing: false - function setView(view) { + function setView(view, menu) { for(var i = 0; i < views.length; i++) { - views[i].visible = false - } + views[i][0].visible = false + views[i][1].border.color = "#00000000" + views[i][1].color = "#00000000" + } view.visible = true + + menu.border.color = "#CCCCCC" + menu.color = "#FFFFFFFF" } function addComponent(component, options) { @@ -265,8 +270,9 @@ ApplicationWindow { return; } - menu.createMenuItem(view.iconFile, view, options); - mainSplit.views.push(view); + var menuItem = menu.createMenuItem(view.iconFile, view, options); + + mainSplit.views.push([view, menuItem]); return view } @@ -276,96 +282,91 @@ ApplicationWindow { ********************/ Rectangle { id: menu - Layout.minimumWidth: 80 - Layout.maximumWidth: 80 + Layout.minimumWidth: 180 + Layout.maximumWidth: 180 anchors.top: parent.top - color: "#252525" + color: "#ececec" Component { id: menuItemTemplate - Image { + Rectangle { + id: menuItem property var view; - anchors.horizontalCenter: parent.horizontalCenter - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view) - } - } - } - } - /* - Component { - id: menuItemTemplate + property alias title: label.text + property alias secondary: secondary.text - RowLayout { - property var view; - property alias source: icon.source; - property alias title: title.text - height: 25 - - id: tab + width: 180 + height: 28 + border.color: "#00000000" + border.width: 1 + radius: 5 + color: "#00000000" anchors { left: parent.left - right: parent.right + leftMargin: 4 } Image { id: icon - //anchors.horizontalCenter: parent.horizontalCenter + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + source: "../pick.png" + } + + Text { + id: label + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + } + + text: "Chain" + font.bold: true + color: "#0D0A01" + font.pixelSize: 12 + } + + Text { + id: secondary + anchors { + right: parent.right + rightMargin: 8 + verticalCenter: parent.verticalCenter + } + color: "#AEADBE" + font.pixelSize: 12 } MouseArea { anchors.fill: parent onClicked: { - mainSplit.setView(view) - } - } - - Rectangle { - color: "#bbbbbb" - Label { - id: title - y: parent.height / 2 - this.height / 2 - //x: 5 - font.pixelSize: 10 - } - - - 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") - } - } + mainSplit.setView(view, menuItem) } } } } - */ function createMenuItem(icon, view, options) { if(options === undefined) { options = {}; } - var comp = menuItemTemplate.createObject(menuColumn) + if(options.default) { + var comp = menuItemTemplate.createObject(menuDefault) + } + comp.view = view - comp.source = icon - //comp.title = options.title + comp.title = view.title + if(view.secondary !== undefined) { + comp.secondary = view.secondary + } + + return comp + /* if(options.canClose) { //comp.closeButton.visible = options.canClose @@ -375,10 +376,87 @@ ApplicationWindow { ColumnLayout { id: menuColumn - y: 50 + y: 30 + width: parent.width anchors.left: parent.left anchors.right: parent.right - spacing: 10 + spacing: 3 + + Text { + text: "ETHEREUM" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuDefault + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + Text { + text: "APPS" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + /* + Rectangle { + width: 180 + height: 28 + border.color: "#CCCCCC" + border.width: 1 + radius: 5 + color: "#FFFFFF" + + anchors { + left: parent.left + leftMargin: 4 + } + + Image { + id: icon + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + source: "../pick.png" + } + + Text { + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + } + + text: "Wallet" + font.bold: true + color: "#0D0A01" + } + + Text { + anchors { + right: parent.right + rightMargin: 8 + verticalCenter: parent.verticalCenter + } + color: "#AEADBE" + text: "12e15 Ξ" + font.pixelSize: 12 + } + } + */ } } From a8409b0a8bfa7f8434ede495094fd8d892c28c91 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 18 Aug 2014 01:35:42 +0200 Subject: [PATCH 44/51] Implementing new wallet views --- ethereal/assets/qml/views/history.qml | 7 ++- ethereal/assets/qml/views/info.qml | 2 +- ethereal/assets/qml/views/wallet.qml | 63 +++++++++++++++++++++ ethereal/assets/qml/wallet.qml | 78 ++++++++++++++++++++------ ethereal/assets/qml/webapp.qml | 26 ++++----- ethereal/assets/wallet.png | Bin 0 -> 1114 bytes ethereal/gui.go | 3 +- javascript/types.go | 6 +- 8 files changed, 147 insertions(+), 38 deletions(-) create mode 100644 ethereal/assets/qml/views/wallet.qml create mode 100644 ethereal/assets/wallet.png diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml index 94ea29e61..d8c932f8f 100644 --- a/ethereal/assets/qml/views/history.qml +++ b/ethereal/assets/qml/views/history.qml @@ -11,14 +11,15 @@ Rectangle { property var title: "Transactions" property var menuItem - property var txModel: ListModel { - id: txModel - } id: historyView + visible: false anchors.fill: parent objectName: "transactionView" + property var txModel: ListModel { + id: txModel + } TableView { id: txTableView anchors.fill: parent diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index c12977cbe..c4486aa6c 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -29,7 +29,7 @@ Rectangle { text: "Address" } TextField { - text: eth.getKey().address + text: eth.key().address width: 500 } diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml new file mode 100644 index 000000000..22b09640b --- /dev/null +++ b/ethereal/assets/qml/views/wallet.qml @@ -0,0 +1,63 @@ +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 { + id: root + property var title: "Wallet" + property var iconFile: "../wallet.png" + property var menuItem + + objectName: "walletView" + anchors.fill: parent + + function onReady() { + menuItem.secondary = eth.numberToHuman(eth.balanceAt(eth.key().address)) + + } + + ColumnLayout { + spacing: 10 + y: 40 + anchors { + left: parent.left + right: parent.right + } + + Text { + text: "Balance: " + eth.numberToHuman(eth.balanceAt(eth.key().address)) + font.pixelSize: 24 + anchors { + horizontalCenter: parent.horizontalCenter + } + } + + TableView { + id: txTableView + anchors { + left: parent.left + right: parent.right + } + TableViewColumn{ role: "num" ; title: "#" ; width: 30 } + TableViewColumn{ role: "from" ; title: "From" ; width: 280 } + TableViewColumn{ role: "to" ; title: "To" ; width: 280 } + TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 } + + model: ListModel { + id: txModel + Component.onCompleted: { + var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"})) + for(var i = 0; i < messages.length; i++) { + var message = messages[i]; + this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)}) + } + } + } + } + + } +} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index d1039518b..89126f600 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -6,6 +6,7 @@ import QtQuick.Window 2.1; import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 + ApplicationWindow { id: root @@ -30,12 +31,14 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { - var historyView = addPlugin("./views/history.qml", {default: true}) - var newTxView = addPlugin("./views/transaction.qml", {default: true}) - var chainView = addPlugin("./views/chain.qml", {default: true}) - var infoView = addPlugin("./views/info.qml", {default: true}) - var pendingTxView = addPlugin("./views/pending_tx.qml", {default: true}) - var pendingTxView = addPlugin("./views/javascript.qml", {default: true}) + var walletView = addPlugin("./views/wallet.qml", {section: "ethereum"}) + + var historyView = addPlugin("./views/history.qml", {section: "legacy"}) + var newTxView = addPlugin("./views/transaction.qml", {section: "legacy"}) + var chainView = addPlugin("./views/chain.qml", {section: "legacy"}) + var infoView = addPlugin("./views/info.qml", {section: "legacy"}) + var pendingTxView = addPlugin("./views/pending_tx.qml", {section: "legacy"}) + var pendingTxView = addPlugin("./views/javascript.qml", {section: "legacy"}) // Call the ready handler gui.done() @@ -252,10 +255,10 @@ ApplicationWindow { function setView(view, menu) { for(var i = 0; i < views.length; i++) { - views[i][0].visible = false + views[i].view.visible = false - views[i][1].border.color = "#00000000" - views[i][1].color = "#00000000" + views[i].menuItem.border.color = "#00000000" + views[i].menuItem.color = "#00000000" } view.visible = true @@ -265,14 +268,21 @@ ApplicationWindow { 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; } var menuItem = menu.createMenuItem(view.iconFile, view, options); + if(view.hasOwnProperty("menuItem")) { + view.menuItem = menuItem; + } + mainSplit.views.push({view: view, menuItem: menuItem}); - mainSplit.views.push([view, menuItem]); + if(view.hasOwnProperty("onReady")) { + view.onReady.call(view) + } return view } @@ -294,6 +304,7 @@ ApplicationWindow { property var view; property alias title: label.text + property alias icon: icon.source property alias secondary: secondary.text width: 180 @@ -310,11 +321,13 @@ ApplicationWindow { Image { id: icon + height: 20 + width: 20 anchors { left: parent.left verticalCenter: parent.verticalCenter + leftMargin: 3 } - source: "../pick.png" } Text { @@ -322,10 +335,10 @@ ApplicationWindow { anchors { left: icon.right verticalCenter: parent.verticalCenter + leftMargin: 3 } - text: "Chain" - font.bold: true + //font.bold: true color: "#0D0A01" font.pixelSize: 12 } @@ -355,15 +368,29 @@ ApplicationWindow { options = {}; } - if(options.default) { - var comp = menuItemTemplate.createObject(menuDefault) + var section; + switch(options.section) { + case "ethereum": + section = menuDefault; + break; + case "legacy": + section = menuLegacy; + break; + default: + section = menuApps; + break; } + + var comp = menuItemTemplate.createObject(section) comp.view = view comp.title = view.title + comp.icon = view.iconFile + /* if(view.secondary !== undefined) { comp.secondary = view.secondary } + */ return comp @@ -376,7 +403,7 @@ ApplicationWindow { ColumnLayout { id: menuColumn - y: 30 + y: 10 width: parent.width anchors.left: parent.left anchors.right: parent.right @@ -401,6 +428,25 @@ ApplicationWindow { } } + Text { + text: "LEGACY" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuLegacy + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + Text { text: "APPS" font.bold: true diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 22e404cc8..ca6860036 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -142,39 +142,39 @@ ApplicationWindow { try { switch(data.call) { case "getCoinBase": - postData(data._seed, eth.getCoinBase()) + postData(data._seed, eth.coinBase()) break case "getIsListening": - postData(data._seed, eth.getIsListening()) + postData(data._seed, eth.isListening()) break case "getIsMining": - postData(data._seed, eth.getIsMining()) + postData(data._seed, eth.isMining()) break case "getPeerCount": - postData(data._seed, eth.getPeerCount()) + postData(data._seed, eth.peerCount()) break case "getTxCountAt": require(1) - postData(data._seed, eth.getTxCountAt(data.args[0])) + postData(data._seed, eth.txCountAt(data.args[0])) break case "getBlockByNumber": - var block = eth.getBlockByNumber(data.args[0]) + var block = eth.blockByNumber(data.args[0]) postData(data._seed, block) break case "getBlockByHash": - var block = eth.getBlockByHash(data.args[0]) + var block = eth.blockByHash(data.args[0]) postData(data._seed, block) break @@ -190,22 +190,22 @@ ApplicationWindow { case "getStorage": require(2); - var stateObject = eth.getStateObject(data.args[0]) - var storage = stateObject.getStorage(data.args[1]) + var stateObject = eth.stateObject(data.args[0]) + var storage = stateObject.storageAt(data.args[1]) postData(data._seed, storage) break case "getEachStorage": require(1); - var storage = JSON.parse(eth.getEachStorage(data.args[0])) + var storage = JSON.parse(eth.eachStorage(data.args[0])) postData(data._seed, storage) break case "getTransactionsFor": require(1); - var txs = eth.getTransactionsFor(data.args[0], true) + var txs = eth.transactionsFor(data.args[0], true) postData(data._seed, txs) break @@ -213,12 +213,12 @@ ApplicationWindow { case "getBalance": require(1); - postData(data._seed, eth.getStateObject(data.args[0]).value()); + postData(data._seed, eth.stateObject(data.args[0]).value()); break case "getKey": - var key = eth.getKey().privateKey; + var key = eth.key().privateKey; postData(data._seed, key) break diff --git a/ethereal/assets/wallet.png b/ethereal/assets/wallet.png new file mode 100644 index 0000000000000000000000000000000000000000..92c401e52c7a6cd0715d15a3db781754c2502b21 GIT binary patch literal 1114 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9GG z!XV7ZFl&wkP(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@WME*L<>}%W62Wu3QbEqBqxd}F1es25UtI9 zI^n5*+PnVO)|SXD=ypj*S=6s(Vd%eHaVnTTO#F;9qqCZXbI5%U#zZ1_54{qgVy3wQXJk5i)8|WRa@AaYVr%K{XzP*3^(Zhjw3>;{US?Vfa3$vJ79ch@TUZfbXKB|~Jhy;}JG zYu*8uA9JkeyQwAjza`G=Rch)VPaBQKP0KiPJ5;tZExgzLWkcqy>xUnH=@&XO7Q|$`f=@_*dwg}r>H7hw^|)NAbi=q qEZ$;S!;C6!@qLqjUWtEx>xcMkfi0_;l5d>>Wq40lKbLh*2~7ZcbjkYw literal 0 HcmV?d00001 diff --git a/ethereal/gui.go b/ethereal/gui.go index 7a36a8b02..1765c3fb2 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -334,7 +334,6 @@ func (gui *Gui) readPreviousTransactions() { } func (gui *Gui) processBlock(block *ethchain.Block, initial bool) { - //name := ethpub.FindNameInNameReg(gui.eth.StateManager(), block.Coinbase) name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00") b := ethpipe.NewJSBlock(block) b.Name = name @@ -491,7 +490,7 @@ func (gui *Gui) setPeerInfo() { gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers)) gui.win.Root().Call("resetPeers") - for _, peer := range gui.pipe.GetPeers() { + for _, peer := range gui.pipe.Peers() { gui.win.Root().Call("addPeer", peer) } } diff --git a/javascript/types.go b/javascript/types.go index 3a11e644b..afa0a41c6 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -73,15 +73,15 @@ type JSEthereum struct { } func (self *JSEthereum) GetBlock(hash string) otto.Value { - return self.toVal(&JSBlock{self.JSPipe.GetBlockByHash(hash), self}) + return self.toVal(&JSBlock{self.JSPipe.BlockByHash(hash), self}) } func (self *JSEthereum) GetPeers() otto.Value { - return self.toVal(self.JSPipe.GetPeers()) + return self.toVal(self.JSPipe.Peers()) } func (self *JSEthereum) GetKey() otto.Value { - return self.toVal(self.JSPipe.GetKey()) + return self.toVal(self.JSPipe.Key()) } func (self *JSEthereum) GetStateObject(addr string) otto.Value { From ecc2c609d4dfe210c66c9316cf5b060f9b3a4ff0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 20 Aug 2014 10:00:02 +0200 Subject: [PATCH 45/51] Implemented QML message filtering --- ethereal/assets/ext/filter.js | 23 + ethereal/assets/qml/views/wallet.qml | 136 ++++- ethereal/assets/qml/wallet.qml | 836 ++++++++++++++------------- ethereal/gui.go | 48 +- ethereal/ui_lib.go | 34 +- 5 files changed, 643 insertions(+), 434 deletions(-) create mode 100644 ethereal/assets/ext/filter.js diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js new file mode 100644 index 000000000..20f0b36a3 --- /dev/null +++ b/ethereal/assets/ext/filter.js @@ -0,0 +1,23 @@ +var Filter = function(eth, options) { + this.callbacks = {}; + this.seed = Math.floor(Math.random() * 1000000); + this.eth = eth; + + eth.registerFilter(options, this.seed); +}; + +Filter.prototype.changed = function(callback) { + var cbseed = Math.floor(Math.random() * 1000000); + this.eth.registerFilterCallback(this.seed, cbseed); + + var self = this; + message.connect(function(messages, seed, callbackSeed) { + if(seed == self.seed && callbackSeed == cbseed) { + callback.call(self, messages); + } + }); +}; + +Filter.prototype.uninstall = function() { + eth.uninstallFilter(this.seed) +} diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index 22b09640b..9afb1f89e 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -17,43 +17,145 @@ Rectangle { function onReady() { menuItem.secondary = eth.numberToHuman(eth.balanceAt(eth.key().address)) + } + 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" } } ColumnLayout { spacing: 10 y: 40 - anchors { - left: parent.left - right: parent.right - } + anchors.fill: parent Text { + id: balance text: "Balance: " + eth.numberToHuman(eth.balanceAt(eth.key().address)) font.pixelSize: 24 anchors { horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: 20 } } - TableView { - id: txTableView + Rectangle { + id: newTxPane + color: "#ececec" + border.color: "#cccccc" + border.width: 1 + anchors { + top: balance.bottom + topMargin: 10 + left: parent.left + leftMargin: 5 + right: parent.right + rightMargin: 5 + } + height: 100 + + RowLayout { + id: amountFields + spacing: 10 + anchors { + top: parent.top + topMargin: 20 + left: parent.left + leftMargin: 20 + } + + Text { + text: "Ξ " + } + + // There's something off with the row layout where textfields won't listen to the width setting + Rectangle { + width: 50 + height: 20 + TextField { + id: txValue + width: parent.width + placeholderText: "0.00" + } + } + + ComboBox { + id: valueDenom + currentIndex: 6 + model: denomModel + } + + } + + RowLayout { + id: toFields + spacing: 10 + anchors { + top: amountFields.bottom + topMargin: 5 + left: parent.left + leftMargin: 20 + } + + Text { + text: "To" + } + + Rectangle { + width: 200 + height: 20 + TextField { + id: txTo + width: parent.width + placeholderText: "Address or name" + } + } + + Button { + text: "Send" + onClicked: { + var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros; + var gasPrice = "10000000000000" + var res = eth.transact(eth.key().privateKey, txTo.text, value, "500", gasPrice, "") + console.log(res) + } + } + } + } + + Rectangle { anchors { left: parent.left right: parent.right + top: newTxPane.bottom + topMargin: 10 + bottom: parent.bottom } - TableViewColumn{ role: "num" ; title: "#" ; width: 30 } - TableViewColumn{ role: "from" ; title: "From" ; width: 280 } - TableViewColumn{ role: "to" ; title: "To" ; width: 280 } - TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 } + TableView { + id: txTableView + anchors.fill : parent + TableViewColumn{ role: "num" ; title: "#" ; width: 30 } + TableViewColumn{ role: "from" ; title: "From" ; width: 280 } + TableViewColumn{ role: "to" ; title: "To" ; width: 280 } + TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 } - model: ListModel { - id: txModel - Component.onCompleted: { - var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"})) - for(var i = 0; i < messages.length; i++) { - var message = messages[i]; - this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)}) + model: ListModel { + id: txModel + Component.onCompleted: { + var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"})) + for(var i = 0; i < messages.length; i++) { + var message = messages[i]; + this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)}) + } } } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 89126f600..122ee049e 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -6,17 +6,28 @@ import QtQuick.Window 2.1; import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 +import "../ext/filter.js" as Eth ApplicationWindow { id: root property alias miningButtonText: miningButton.text + width: 900 height: 600 minimumHeight: 300 - title: "Ethereal" + title: "Ether browser" + + // This signal is used by the filter API. The filter API connects using this signal handler from + // the different QML files and plugins. + signal message(var callback, int seed, int seedCallback); + function invokeFilterCallback(data, receiverSeed, callbackSeed) { + var messages = JSON.parse(data) + // Signal handler + message(data, receiverSeed, callbackSeed); + } TextField { id: copyElementHax @@ -31,17 +42,17 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { - var walletView = addPlugin("./views/wallet.qml", {section: "ethereum"}) - - var historyView = addPlugin("./views/history.qml", {section: "legacy"}) - var newTxView = addPlugin("./views/transaction.qml", {section: "legacy"}) - var chainView = addPlugin("./views/chain.qml", {section: "legacy"}) - var infoView = addPlugin("./views/info.qml", {section: "legacy"}) - var pendingTxView = addPlugin("./views/pending_tx.qml", {section: "legacy"}) - var pendingTxView = addPlugin("./views/javascript.qml", {section: "legacy"}) + var walletView = addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}) + var historyView = addPlugin("./views/history.qml", {noAdd: true, section: "legacy"}) + var newTxView = addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}) + var chainView = addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}) + var infoView = addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}) + var pendingTxView = addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}) + var pendingTxView = addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}) // Call the ready handler gui.done() + } function addPlugin(path, options) { @@ -50,10 +61,20 @@ ApplicationWindow { if(component.status == Component.Error) { console.debug("Error:"+ component.errorString()); } + return } - return mainSplit.addComponent(component, options) + var views = mainSplit.addComponent(component, options) + views.menuItem.path = path + + mainSplit.views.push(views); + + if(!options.noAdd) { + gui.addPlugin(path) + } + + return views.view } MenuBar { @@ -76,7 +97,7 @@ ApplicationWindow { text: "Add plugin" onTriggered: { generalFileDialog.show(true, function(path) { - addPlugin(path, {canClose: true}) + addPlugin(path, {canClose: true, section: "apps"}) }) } } @@ -268,454 +289,441 @@ ApplicationWindow { function addComponent(component, options) { var view = mainView.createView(component, options) + view.visible = false + view.anchors.fill = mainView - if(!view.hasOwnProperty("iconFile")) { + if( !view.hasOwnProperty("iconFile") ) { console.log("Could not load plugin. Property 'iconFile' not found on view."); return; } var menuItem = menu.createMenuItem(view.iconFile, view, options); - if(view.hasOwnProperty("menuItem")) { + if( view.hasOwnProperty("menuItem") ) { view.menuItem = menuItem; } - mainSplit.views.push({view: view, menuItem: menuItem}); - if(view.hasOwnProperty("onReady")) { + if( view.hasOwnProperty("onReady") ) { view.onReady.call(view) } - return view + if( options.active ) { + setView(view, menuItem) + } + + + return {view: view, menuItem: menuItem} } /********************* * Main menu. ********************/ - Rectangle { - id: menu - Layout.minimumWidth: 180 - Layout.maximumWidth: 180 - anchors.top: parent.top - color: "#ececec" + Rectangle { + id: menu + Layout.minimumWidth: 180 + Layout.maximumWidth: 180 + anchors.top: parent.top + color: "#ececec" - Component { - id: menuItemTemplate - Rectangle { - id: menuItem - property var view; + Component { + id: menuItemTemplate + Rectangle { + id: menuItem + property var view; + property var path; - property alias title: label.text - property alias icon: icon.source - property alias secondary: secondary.text + property alias title: label.text + property alias icon: icon.source + property alias secondary: secondary.text - width: 180 - height: 28 - border.color: "#00000000" - border.width: 1 - radius: 5 - color: "#00000000" + width: 180 + height: 28 + border.color: "#00000000" + border.width: 1 + radius: 5 + color: "#00000000" - anchors { - left: parent.left - leftMargin: 4 - } + anchors { + left: parent.left + leftMargin: 4 + } - Image { - id: icon - height: 20 - width: 20 - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - } + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view, menuItem) + } + } - Text { - id: label - anchors { - left: icon.right - verticalCenter: parent.verticalCenter - leftMargin: 3 - } + Image { + id: icon + height: 20 + width: 20 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + MouseArea { + anchors.fill: parent + onClicked: { + menuItem.closeApp() + } + } + } - //font.bold: true - color: "#0D0A01" - font.pixelSize: 12 - } + Text { + id: label + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + leftMargin: 3 + } - Text { - id: secondary - anchors { - right: parent.right - rightMargin: 8 - verticalCenter: parent.verticalCenter - } - color: "#AEADBE" - font.pixelSize: 12 - } + color: "#0D0A01" + font.pixelSize: 12 + } - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view, menuItem) - } - } - } - } - - function createMenuItem(icon, view, options) { - if(options === undefined) { - options = {}; - } - - var section; - switch(options.section) { - case "ethereum": - section = menuDefault; - break; - case "legacy": - section = menuLegacy; - break; - default: - section = menuApps; - break; - } - - var comp = menuItemTemplate.createObject(section) - - comp.view = view - comp.title = view.title - comp.icon = view.iconFile - /* - if(view.secondary !== undefined) { - comp.secondary = view.secondary - } - */ - - return comp - - /* - if(options.canClose) { - //comp.closeButton.visible = options.canClose - } - */ - } - - ColumnLayout { - id: menuColumn - y: 10 - width: parent.width - anchors.left: parent.left - anchors.right: parent.right - spacing: 3 - - Text { - text: "ETHEREUM" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - ColumnLayout { - id: menuDefault - spacing: 3 - anchors { - left: parent.left - right: parent.right - } - } - - Text { - text: "LEGACY" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - ColumnLayout { - id: menuLegacy - spacing: 3 - anchors { - left: parent.left - right: parent.right - } - } - - Text { - text: "APPS" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - /* - Rectangle { - width: 180 - height: 28 - border.color: "#CCCCCC" - border.width: 1 - radius: 5 - color: "#FFFFFF" - - anchors { - left: parent.left - leftMargin: 4 - } - - Image { - id: icon - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - source: "../pick.png" - } - - Text { - anchors { - left: icon.right - verticalCenter: parent.verticalCenter - } - - text: "Wallet" - font.bold: true - color: "#0D0A01" - } - - Text { - anchors { - right: parent.right - rightMargin: 8 - verticalCenter: parent.verticalCenter - } - color: "#AEADBE" - text: "12e15 Ξ" - font.pixelSize: 12 - } - } - */ - } - } - - /********************* - * 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; - } - } + Text { + id: secondary + anchors { + right: parent.right + rightMargin: 8 + verticalCenter: parent.verticalCenter + } + color: "#AEADBE" + font.pixelSize: 12 + } - } + function closeApp() { + if(this.view.hasOwnProperty("onDestroy")) { + this.view.onDestroy.call(this.view) + } + + this.view.destroy() + this.destroy() + gui.removePlugin(this.path) + } + } + } + + function createMenuItem(icon, view, options) { + if(options === undefined) { + options = {}; + } + + var section; + switch(options.section) { + case "ethereum": + section = menuDefault; + break; + case "legacy": + section = menuLegacy; + break; + default: + section = menuApps; + break; + } + + var comp = menuItemTemplate.createObject(section) + + comp.view = view + comp.title = view.title + comp.icon = view.iconFile + /* + if(view.secondary !== undefined) { + comp.secondary = view.secondary + } + */ + + return comp + + /* + if(options.canClose) { + //comp.closeButton.visible = options.canClose + } + */ + } + + ColumnLayout { + id: menuColumn + y: 10 + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + spacing: 3 + + Text { + text: "ETHEREUM" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuDefault + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } - /****************** - * Dialogs - *****************/ - FileDialog { - id: generalFileDialog - property var callback; - onAccepted: { - var path = this.fileUrl.toString(); - callback.call(this, path); - } + Text { + text: "APPS" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } - function show(selectExisting, callback) { - generalFileDialog.callback = callback; - generalFileDialog.selectExisting = selectExisting; + ColumnLayout { + id: menuApps + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } - this.open(); - } - } + Text { + text: "DEBUG" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuLegacy + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + } + } + + /********************* + * 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; + } + } - /****************** - * Wallet functions - *****************/ - function importApp(path) { - var ext = path.split('.').pop() - if(ext == "html" || ext == "htm") { - eth.openHtml(path) - }else if(ext == "qml"){ - eth.openQml(path) - } - } + } - function setWalletValue(value) { - walletValueLabel.text = value - } - function loadPlugin(name) { - console.log("Loading plugin" + name) - mainView.addPlugin(name) - } + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString(); + callback.call(this, path); + } - function setPeers(text) { - peerLabel.text = text - } + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; - 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}) - } + this.open(); + } + } - function resetPeers(){ - peerModel.clear() - } - function timeAgo(unixTs){ - var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 - return (lapsed + " seconds ago") - } + /****************** + * Wallet functions + *****************/ + function importApp(path) { + var ext = path.split('.').pop() + if(ext == "html" || ext == "htm") { + eth.openHtml(path) + }else if(ext == "qml"){ + addPlugin(path, {canClose: true, section: "apps"}) + } + } - 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" } - } - } - } + function setWalletValue(value) { + walletValueLabel.text = value + } - Window { - id: aboutWin - visible: false - title: "About" - minimumWidth: 350 - maximumWidth: 350 - maximumHeight: 200 - minimumHeight: 200 + function loadPlugin(name) { + console.log("Loading plugin" + name) + var view = mainView.addPlugin(name) + } - Image { - id: aboutIcon - height: 150 - width: 150 - fillMode: Image.PreserveAspectFit - smooth: true - source: "../facet.png" - x: 10 - y: 10 - } + function setPeers(text) { + peerLabel.text = text + } - Text { - anchors.left: aboutIcon.right - anchors.leftMargin: 10 - font.pointSize: 12 - text: "

Ethereal - Adrastea


Development

Jeffrey Wilcke
Maran Hidskes
Viktor Trón
" - } - } + 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}) + } - 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 - } - } + function resetPeers(){ + peerModel.clear() + } - Window { - id: addPeerWin - visible: false - minimumWidth: 230 - maximumWidth: 230 - maximumHeight: 50 - minimumHeight: 50 + function timeAgo(unixTs){ + var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 + return (lapsed + " seconds ago") + } - TextField { - id: addrField - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 10 - placeholderText: "address:port" - onAccepted: { - eth.connectToPeer(addrField.text) - addPeerWin.visible = false - } - } - Button { - anchors.left: addrField.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 5 - text: "Add" - onClicked: { - eth.connectToPeer(addrField.text) - addPeerWin.visible = false - } - } - Component.onCompleted: { - addrField.focus = true - } - } -} + 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 { + 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: addPeerWin + visible: false + minimumWidth: 230 + maximumWidth: 230 + maximumHeight: 50 + minimumHeight: 50 + + TextField { + id: addrField + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 10 + placeholderText: "address:port" + onAccepted: { + eth.connectToPeer(addrField.text) + addPeerWin.visible = false + } + } + Button { + anchors.left: addrField.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 5 + text: "Add" + onClicked: { + eth.connectToPeer(addrField.text) + addPeerWin.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } + } diff --git a/ethereal/gui.go b/ethereal/gui.go index 1765c3fb2..3f989fe51 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/json" "fmt" "math/big" "os" @@ -24,6 +25,11 @@ import ( var logger = ethlog.NewLogger("GUI") +type plugin struct { + Name string `json:"name"` + Path string `json:"path"` +} + type Gui struct { // The main application window win *qml.Window @@ -48,6 +54,8 @@ type Gui struct { clientIdentity *ethwire.SimpleClientIdentity config *ethutil.ConfigManager + plugins map[string]plugin + miner *ethminer.Miner } @@ -59,8 +67,16 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden } pipe := ethpipe.NewJSPipe(ethereum) + gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)} + data, err := ethutil.ReadAllFile(ethutil.Config.ExecPath + "/plugins.json") + if err != nil { + fmt.Println(err) + } + fmt.Println(string(data)) - return &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config} + json.Unmarshal([]byte(data), &gui.plugins) + + return gui } func (gui *Gui) Start(assetPath string) { @@ -193,6 +209,7 @@ func (self *Gui) DumpState(hash, path string) { // 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) { @@ -375,6 +392,10 @@ func (gui *Gui) update() { gui.readPreviousTransactions() }() + for _, plugin := range gui.plugins { + gui.win.Root().Call("addPlugin", plugin.Path, "") + } + var ( blockChan = make(chan ethreact.Event, 100) txChan = make(chan ethreact.Event, 100) @@ -504,7 +525,16 @@ func (gui *Gui) address() []byte { } func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) { - data := ethutil.Bytes2Hex(utils.FormatTransactionData(d)) + var data string + if len(recipient) == 0 { + code, err := ethutil.Compile(d, false) + if err != nil { + return nil, err + } + data = ethutil.Bytes2Hex(code) + } else { + data = ethutil.Bytes2Hex(utils.FormatTransactionData(d)) + } return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) } @@ -528,6 +558,20 @@ func (gui *Gui) GetLogLevel() ethlog.LogLevel { return gui.logLevel } +func (self *Gui) AddPlugin(pluginPath string) { + self.plugins[pluginPath] = plugin{Name: "SomeName", Path: pluginPath} + + json, _ := json.MarshalIndent(self.plugins, "", " ") + ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json) +} + +func (self *Gui) RemovePlugin(pluginPath string) { + delete(self.plugins, pluginPath) + + json, _ := json.MarshalIndent(self.plugins, "", " ") + ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json) +} + // this extra function needed to give int typecast value to gui widget // that sets initial loglevel to default func (gui *Gui) GetLogLevelInt() int { diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index ade9bf381..7b2627c49 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethpipe" + "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/javascript" "gopkg.in/qml.v1" @@ -34,10 +35,13 @@ type UiLib struct { DbWindow *DebuggerWindow jsEngine *javascript.JSRE + + filterCallbacks map[int][]int + filters map[int]*GuiFilter } func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { - return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth)} + return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int), filters: make(map[int]*GuiFilter)} } func (self *UiLib) LookupDomain(domain string) string { @@ -155,3 +159,31 @@ func (self *UiLib) StartDebugger() { dbWindow.Show() } + +func (self *UiLib) RegisterFilter(object map[string]interface{}, seed int) { + filter := &GuiFilter{ethpipe.NewJSFilterFromMap(object, self.eth), seed} + self.filters[seed] = filter + + filter.MessageCallback = func(messages ethstate.Messages) { + for _, callbackSeed := range self.filterCallbacks[seed] { + self.win.Root().Call("invokeFilterCallback", filter.MessagesToJson(messages), seed, callbackSeed) + } + } +} + +func (self *UiLib) RegisterFilterCallback(seed, cbSeed int) { + self.filterCallbacks[seed] = append(self.filterCallbacks[seed], cbSeed) +} + +func (self *UiLib) UninstallFilter(seed int) { + filter := self.filters[seed] + if filter != nil { + filter.Uninstall() + delete(self.filters, seed) + } +} + +type GuiFilter struct { + *ethpipe.JSFilter + seed int +} From 0eb1db0d179e07e9133e5d2d2df5efcbb68d9884 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 20 Aug 2014 10:41:20 +0200 Subject: [PATCH 46/51] Added messages method --- ethereal/assets/ext/filter.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js index 20f0b36a3..7af6f56e7 100644 --- a/ethereal/assets/ext/filter.js +++ b/ethereal/assets/ext/filter.js @@ -1,14 +1,14 @@ -var Filter = function(eth, options) { +var Filter = function(options) { this.callbacks = {}; this.seed = Math.floor(Math.random() * 1000000); - this.eth = eth; + this.options = options; eth.registerFilter(options, this.seed); }; Filter.prototype.changed = function(callback) { var cbseed = Math.floor(Math.random() * 1000000); - this.eth.registerFilterCallback(this.seed, cbseed); + eth.registerFilterCallback(this.seed, cbseed); var self = this; message.connect(function(messages, seed, callbackSeed) { @@ -21,3 +21,7 @@ Filter.prototype.changed = function(callback) { Filter.prototype.uninstall = function() { eth.uninstallFilter(this.seed) } + +Filter.prototype.messages = function() { + return JSON.parse(eth.messages(this.options)) +} From fb49e5565ae7fe1d44180158aa982a96b7720fb5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 20 Aug 2014 12:42:49 +0200 Subject: [PATCH 47/51] Fixed minor issues with filtering --- ethereal/assets/ext/filter.js | 6 +++- ethereal/gui.go | 2 +- ethereal/ui_lib.go | 62 +++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js index 7af6f56e7..5c1c03aad 100644 --- a/ethereal/assets/ext/filter.js +++ b/ethereal/assets/ext/filter.js @@ -3,7 +3,11 @@ var Filter = function(options) { this.seed = Math.floor(Math.random() * 1000000); this.options = options; - eth.registerFilter(options, this.seed); + if(options == "chain") { + eth.registerFilterString(options, this.seed); + } else if(typeof options === "object") { + eth.registerFilter(options, this.seed); + } }; Filter.prototype.changed = function(callback) { diff --git a/ethereal/gui.go b/ethereal/gui.go index 3f989fe51..f450acde6 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -72,7 +72,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden if err != nil { fmt.Println(err) } - fmt.Println(string(data)) + fmt.Println("plugins:", string(data)) json.Unmarshal([]byte(data), &gui.plugins) diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 7b2627c49..4b8210da6 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -169,6 +169,20 @@ func (self *UiLib) RegisterFilter(object map[string]interface{}, seed int) { self.win.Root().Call("invokeFilterCallback", filter.MessagesToJson(messages), seed, callbackSeed) } } + +} + +func (self *UiLib) RegisterFilterString(typ string, seed int) { + filter := &GuiFilter{ethpipe.NewJSFilterFromMap(nil, self.eth), seed} + self.filters[seed] = filter + + if typ == "chain" { + filter.BlockCallback = func(block *ethchain.Block) { + for _, callbackSeed := range self.filterCallbacks[seed] { + self.win.Root().Call("invokeFilterCallback", "{}", seed, callbackSeed) + } + } + } } func (self *UiLib) RegisterFilterCallback(seed, cbSeed int) { @@ -187,3 +201,51 @@ type GuiFilter struct { *ethpipe.JSFilter seed int } + +func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, error) { + // Default values + if object["from"] == nil { + object["from"] = "" + } + if object["to"] == nil { + object["to"] = "" + } + if object["value"] == nil { + object["value"] = "" + } + if object["gas"] == nil { + object["gas"] = "" + } + if object["gasPrice"] == nil { + object["gasPrice"] = "" + } + + var dataStr string + var data []string + if list, ok := object["data"].(*qml.List); ok { + list.Convert(&data) + } + + for _, str := range data { + if ethutil.IsHex(str) { + str = str[2:] + + if len(str) != 64 { + str = ethutil.LeftPadString(str, 64) + } + } else { + str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32)) + } + + dataStr += str + } + + return self.JSPipe.Transact( + object["from"].(string), + object["to"].(string), + object["value"].(string), + object["gas"].(string), + object["gasPrice"].(string), + dataStr, + ) +} From 245ffb1123b58bc63ee8eae6b5250001c4a427c8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 20 Aug 2014 13:36:32 +0200 Subject: [PATCH 48/51] Fixed transact --- ethereal/assets/qml/views/wallet.qml | 2 +- ethereal/assets/qml/wallet.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index 9afb1f89e..b626ff678 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -125,7 +125,7 @@ Rectangle { onClicked: { var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros; var gasPrice = "10000000000000" - var res = eth.transact(eth.key().privateKey, txTo.text, value, "500", gasPrice, "") + var res = eth.transact({from: eth.key().privateKey, to: txTo.text, value: value, gas: "500", gasPrice: gasPrice}) console.log(res) } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 122ee049e..d732f2ec8 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -26,7 +26,7 @@ ApplicationWindow { function invokeFilterCallback(data, receiverSeed, callbackSeed) { var messages = JSON.parse(data) // Signal handler - message(data, receiverSeed, callbackSeed); + message(messages, receiverSeed, callbackSeed); } TextField { From 7c65560f20d31f4b56cce3d3f8aee918c03dba14 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 20 Aug 2014 23:52:33 +0200 Subject: [PATCH 49/51] Changed icon source --- ethereal/assets/qml/views/chain.qml | 2 +- ethereal/assets/qml/views/history.qml | 2 +- ethereal/assets/qml/views/info.qml | 2 +- ethereal/assets/qml/views/javascript.qml | 2 +- ethereal/assets/qml/views/pending_tx.qml | 2 +- ethereal/assets/qml/views/transaction.qml | 2 +- ethereal/assets/qml/views/wallet.qml | 4 ++-- ethereal/assets/qml/wallet.qml | 10 +++++----- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index b94d9edca..9eaa49db1 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -9,7 +9,7 @@ import Ethereum 1.0 Rectangle { id: root property var title: "Network" - property var iconFile: "../net.png" + property var iconSource: "../net.png" property var secondary: "Hi" property var menuItem diff --git a/ethereal/assets/qml/views/history.qml b/ethereal/assets/qml/views/history.qml index d8c932f8f..9eee883e3 100644 --- a/ethereal/assets/qml/views/history.qml +++ b/ethereal/assets/qml/views/history.qml @@ -7,7 +7,7 @@ import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 Rectangle { - property var iconFile: "../tx.png" + property var iconSource: "../tx.png" property var title: "Transactions" property var menuItem diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index c4486aa6c..ca6ca077e 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -8,7 +8,7 @@ import Ethereum 1.0 Rectangle { property var title: "Information" - property var iconFile: "../heart.png" + property var iconSource: "../heart.png" property var menuItem objectName: "infoView" diff --git a/ethereal/assets/qml/views/javascript.qml b/ethereal/assets/qml/views/javascript.qml index aa5f93547..ea05c4148 100644 --- a/ethereal/assets/qml/views/javascript.qml +++ b/ethereal/assets/qml/views/javascript.qml @@ -8,7 +8,7 @@ import Ethereum 1.0 Rectangle { property var title: "JavaScript" - property var iconFile: "../tx.png" + property var iconSource: "../tx.png" property var menuItem objectName: "javascriptView" diff --git a/ethereal/assets/qml/views/pending_tx.qml b/ethereal/assets/qml/views/pending_tx.qml index 8e3690cb9..abfa25790 100644 --- a/ethereal/assets/qml/views/pending_tx.qml +++ b/ethereal/assets/qml/views/pending_tx.qml @@ -8,7 +8,7 @@ import Ethereum 1.0 Rectangle { property var title: "Pending Transactions" - property var iconFile: "../tx.png" + property var iconSource: "../tx.png" property var menuItem objectName: "pendingTxView" diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index 640e02a4e..fb8ba8a6d 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -7,7 +7,7 @@ import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 Rectangle { - property var iconFile: "../new.png" + property var iconSource: "../new.png" property var title: "New transaction" property var menuItem diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index b626ff678..5e10a7022 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -9,14 +9,14 @@ import Ethereum 1.0 Rectangle { id: root property var title: "Wallet" - property var iconFile: "../wallet.png" + property var iconSource: "../wallet.png" property var menuItem objectName: "walletView" anchors.fill: parent function onReady() { - menuItem.secondary = eth.numberToHuman(eth.balanceAt(eth.key().address)) + menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.key().address)) } ListModel { diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index d732f2ec8..094349bab 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -292,12 +292,12 @@ ApplicationWindow { view.visible = false view.anchors.fill = mainView - if( !view.hasOwnProperty("iconFile") ) { - console.log("Could not load plugin. Property 'iconFile' not found on view."); + if( !view.hasOwnProperty("iconSource") ) { + console.log("Could not load plugin. Property 'iconSourc' not found on view."); return; } - var menuItem = menu.createMenuItem(view.iconFile, view, options); + var menuItem = menu.createMenuItem(view.iconSource, view, options); if( view.hasOwnProperty("menuItem") ) { view.menuItem = menuItem; } @@ -333,7 +333,7 @@ ApplicationWindow { property alias title: label.text property alias icon: icon.source - property alias secondary: secondary.text + property alias secondaryTitle: secondary.text width: 180 height: 28 @@ -429,7 +429,7 @@ ApplicationWindow { comp.view = view comp.title = view.title - comp.icon = view.iconFile + comp.icon = view.iconSource /* if(view.secondary !== undefined) { comp.secondary = view.secondary From 38cf52b62c6da4812cbef27c8a97dc839e480967 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 21 Aug 2014 15:22:24 +0200 Subject: [PATCH 50/51] Bumped version --- README.md | 2 +- ethereal/main.go | 2 +- ethereum/main.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 186c979bc..da75e4d9e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ether Ethereum Go Client © 2014 Jeffrey Wilcke. -Current state: Proof of Concept 0.6.0. +Current state: Proof of Concept 0.6.3. For the development package please see the [eth-go package](https://github.com/ethereum/eth-go). diff --git a/ethereal/main.go b/ethereal/main.go index 47f6144fa..4101efbca 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -12,7 +12,7 @@ import ( const ( ClientIdentifier = "Ethereal" - Version = "0.6.2" + Version = "0.6.3" ) var ethereum *eth.Ethereum diff --git a/ethereum/main.go b/ethereum/main.go index 070d3c201..a8e60dec7 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -13,7 +13,7 @@ import ( const ( ClientIdentifier = "Ethereum(G)" - Version = "0.6.2" + Version = "0.6.3" ) var logger = ethlog.NewLogger("CLI") From be9912fae218f12ed9087628be55c0898e161910 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 21 Aug 2014 15:42:59 +0200 Subject: [PATCH 51/51] MIT -> GPL --- LICENSE | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/LICENSE b/LICENSE index b77f7909a..78efdaabe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,16 @@ -The MIT License (MIT) +Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -Copyright (c) 2013 Jeffrey Wilcke +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +You should have received a copy of the GNU General Public License +along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301 USA